diff --git a/.npmrc b/.npmrc index 74538ab74e5d..58ad2d1e59da 100644 --- a/.npmrc +++ b/.npmrc @@ -1,2 +1,3 @@ package-lock=true package-manager-strict=false +@jsr:registry=https://npm.jsr.io diff --git a/lib/routes/twitter/api/web-api/client-transaction.ts b/lib/routes/twitter/api/web-api/client-transaction.ts new file mode 100644 index 000000000000..8f82e14303fe --- /dev/null +++ b/lib/routes/twitter/api/web-api/client-transaction.ts @@ -0,0 +1,19 @@ +import { ClientTransaction, handleXMigration } from '@lami/x-client-transaction-id'; + +let clientTransactionPromise: Promise | undefined; + +const buildClientTransaction = async () => ClientTransaction.create(await handleXMigration()); + +const getClientTransaction = () => { + clientTransactionPromise ??= buildClientTransaction(); + return clientTransactionPromise; +}; + +export const getClientTransactionId = async (method: string, path: string) => { + try { + const clientTransaction = await getClientTransaction(); + return await clientTransaction.generateTransactionId(method, path); + } catch { + clientTransactionPromise = undefined; + } +}; diff --git a/lib/routes/twitter/api/web-api/utils.ts b/lib/routes/twitter/api/web-api/utils.ts index 560211aabce4..8f21ace1d6f8 100644 --- a/lib/routes/twitter/api/web-api/utils.ts +++ b/lib/routes/twitter/api/web-api/utils.ts @@ -10,6 +10,7 @@ import logger from '@/utils/logger'; import ofetch from '@/utils/ofetch'; import proxy from '@/utils/proxy'; +import { getClientTransactionId } from './client-transaction'; import { baseUrl, bearerToken, gqlFeatures, gqlMap, thirdPartySupportedAPI } from './constants'; import login from './login'; @@ -80,6 +81,7 @@ export const twitterGot = async ( params, options?: { allowNoAuth?: boolean; + headers?: Record; } ) => { const auth = await getAuth(30); @@ -171,6 +173,7 @@ export const twitterGot = async ( : { 'x-guest-token': jsonCookie.gt, }), + ...options?.headers, }, dispatcher: dispatchers?.agent, }); @@ -263,7 +266,14 @@ export const paginationTweets = async (endpoint: string, userId: number | undefi }); return data; } - const { data } = await twitterGot(baseUrl + gqlMap[endpoint], params); + const transactionId = endpoint === 'UserTweetsAndReplies' ? await getClientTransactionId('GET', new URL(baseUrl + gqlMap[endpoint]).pathname) : undefined; + const { data } = await twitterGot(baseUrl + gqlMap[endpoint], params, { + headers: transactionId + ? { + 'x-client-transaction-id': transactionId, + } + : undefined, + }); return data; }; diff --git a/package.json b/package.json index 7ca1169d6cb6..87ba635a8f0b 100644 --- a/package.json +++ b/package.json @@ -63,6 +63,7 @@ "@hono/node-server": "1.19.14", "@hono/zod-openapi": "1.3.0", "@jocmp/mercury-parser": "3.0.7", + "@lami/x-client-transaction-id": "npm:@jsr/lami__x-client-transaction-id@^0.2.0", "@notionhq/client": "5.18.0", "@opentelemetry/api": "1.9.1", "@opentelemetry/exporter-prometheus": "0.214.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2b65d613486f..e3076a9c4767 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -52,6 +52,9 @@ importers: '@jocmp/mercury-parser': specifier: 3.0.7 version: 3.0.7 + '@lami/x-client-transaction-id': + specifier: npm:@jsr/lami__x-client-transaction-id@^0.2.0 + version: '@jsr/lami__x-client-transaction-id@0.2.0' '@notionhq/client': specifier: 5.18.0 version: 5.18.0 @@ -1578,6 +1581,12 @@ packages: peerDependencies: jsep: ^0.4.0||^1.0.0 + '@jsr/lami__x-client-transaction-id@0.2.0': + resolution: {integrity: sha512-7JGbF627yVMaZONoBxLAtrTkNmmxVGtaV+sX3Kh3dS3yTyf5qtHpfAHxWInfDNpNTVzcdxoAEr5s0uu6HFyo4g==, tarball: https://npm.jsr.io/~/11/@jsr/lami__x-client-transaction-id/0.2.0.tgz} + + '@jsr/std__encoding@1.0.10': + resolution: {integrity: sha512-WK2njnDTyKefroRNk2Ooq7GStp6Y0ccAvr4To+Z/zecRAGe7+OSvH9DbiaHpAKwEi2KQbmpWMOYsdNt+TsdmSw==, tarball: https://npm.jsr.io/~/11/@jsr/std__encoding/1.0.10.tgz} + '@keyv/serialize@1.1.1': resolution: {integrity: sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==} @@ -3531,6 +3540,9 @@ packages: resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==} engines: {node: '>= 6'} + cssom@0.5.0: + resolution: {integrity: sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==} + currency-symbol-map@5.1.0: resolution: {integrity: sha512-LO/lzYRw134LMDVnLyAf1dHE5tyO6axEFkR3TXjQIOmMkAM9YL6QsiUwuXzZAmFnuDJcs4hayOgyIYtViXFrLw==} @@ -4307,6 +4319,9 @@ packages: html-escaper@2.0.2: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + html-escaper@3.0.3: + resolution: {integrity: sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==} + html-to-text@9.0.5: resolution: {integrity: sha512-qY60FjREgVZL03vJU6IfMV4GDjGBIoOyvuFdpBDIX9yTlDw0TjxVBQp+P8NvpdIXNJvfWBTNul7fsAQJq2FNpg==} engines: {node: '>=14'} @@ -4695,6 +4710,15 @@ packages: lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + linkedom@0.18.12: + resolution: {integrity: sha512-jalJsOwIKuQJSeTvsgzPe9iJzyfVaEJiEXl+25EkKevsULHvMJzpNqwvj1jOESWdmgKDiXObyjOYwlUqG7wo1Q==} + engines: {node: '>=16'} + peerDependencies: + canvas: '>= 2' + peerDependenciesMeta: + canvas: + optional: true + linkify-it@5.0.0: resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==} @@ -6054,6 +6078,9 @@ packages: ufo@1.6.3: resolution: {integrity: sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==} + uhyphen@0.2.0: + resolution: {integrity: sha512-qz3o9CHXmJJPGBdqzab7qAYuW8kQGKNEuoHFYrBwV6hWIMcpAmxDLXojcHfFr9US1Pe6zUswEIJIbLI610fuqA==} + uint8array-extras@1.5.0: resolution: {integrity: sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==} engines: {node: '>=18'} @@ -7329,6 +7356,15 @@ snapshots: dependencies: jsep: 1.4.0 + '@jsr/lami__x-client-transaction-id@0.2.0': + dependencies: + '@jsr/std__encoding': 1.0.10 + linkedom: 0.18.12 + transitivePeerDependencies: + - canvas + + '@jsr/std__encoding@1.0.10': {} + '@keyv/serialize@1.1.1': {} '@lifeomic/attempt@3.1.0': {} @@ -9158,6 +9194,8 @@ snapshots: css-what@6.2.2: {} + cssom@0.5.0: {} + currency-symbol-map@5.1.0: {} d@1.0.2: @@ -10098,6 +10136,8 @@ snapshots: html-escaper@2.0.2: {} + html-escaper@3.0.3: {} + html-to-text@9.0.5: dependencies: '@selderee/plugin-htmlparser2': 0.11.0 @@ -10532,6 +10572,14 @@ snapshots: lines-and-columns@1.2.4: {} + linkedom@0.18.12: + dependencies: + css-select: 5.2.2 + cssom: 0.5.0 + html-escaper: 3.0.3 + htmlparser2: 10.1.0 + uhyphen: 0.2.0 + linkify-it@5.0.0: dependencies: uc.micro: 2.1.0 @@ -12174,6 +12222,8 @@ snapshots: ufo@1.6.3: {} + uhyphen@0.2.0: {} + uint8array-extras@1.5.0: {} ultron@1.0.2: {}