Merge pull request #438 from FixTweet/thread-api-updated

Merge main bugfixes into thread-api branch
This commit is contained in:
dangered wolf 2023-10-22 14:22:51 -04:00 committed by GitHub
commit e38aae51fd
Signed by: DevComp
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 105 additions and 52 deletions

View file

@ -27,7 +27,7 @@
### Change `x.com` to `fixupx.com` in your link
### For Twitter links on Discord, send a Twitter link and type `s/e/p` to make `twittpr.com`.
### For `twitter.com` links on Discord, send a link and type `s/e/p` to make `twittpr.com`.
### Note: Some extra features described may currently broken due to recent Twitter/X API changes. [Tracking thread for the API changes](https://github.com/FixTweet/FixTweet/issues/333)

95
package-lock.json generated
View file

@ -13,15 +13,15 @@
"toucan-js": "^3.3.0"
},
"devDependencies": {
"@cloudflare/workers-types": "^4.20231010.0",
"@cloudflare/workers-types": "^4.20231016.0",
"@microsoft/eslint-formatter-sarif": "^3.0.0",
"@sentry/esbuild-plugin": "^2.8.0",
"@sentry/integrations": "^7.74.0",
"@types/jest": "^29.5.5",
"@sentry/integrations": "^7.74.1",
"@types/jest": "^29.5.6",
"@typescript-eslint/eslint-plugin": "^6.8.0",
"@typescript-eslint/parser": "^6.8.0",
"dotenv": "^16.3.1",
"eslint": "^8.51.0",
"eslint": "^8.52.0",
"eslint-config-prettier": "^9.0.0",
"eslint-config-typescript": "^3.0.0",
"eslint-plugin-optimize-regex": "^1.2.1",
@ -32,7 +32,7 @@
"ts-jest": "^29.1.1",
"ts-loader": "^9.5.0",
"typescript": "^5.2.2",
"wrangler": "^3.13.1"
"wrangler": "^3.14.0"
}
},
"node_modules/@aashutoshrathi/word-wrap": {
@ -1223,9 +1223,9 @@
}
},
"node_modules/@eslint/js": {
"version": "8.51.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.51.0.tgz",
"integrity": "sha512-HxjQ8Qn+4SI3/AFv6sOrDB+g6PpUTDwSJiQqOrnneEk8L71161srI9gjzzZvYVbzHiVg/BvcH95+cK/zfIt4pg==",
"version": "8.52.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.52.0.tgz",
"integrity": "sha512-mjZVbpaeMZludF2fsWLD0Z9gCref1Tk4i9+wddjRvpUNqqcndPkBD09N/Mapey0b3jaXbLm2kICwFv2E64QinA==",
"dev": true,
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
@ -1241,12 +1241,12 @@
}
},
"node_modules/@humanwhocodes/config-array": {
"version": "0.11.11",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.11.tgz",
"integrity": "sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==",
"version": "0.11.13",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz",
"integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==",
"dev": true,
"dependencies": {
"@humanwhocodes/object-schema": "^1.2.1",
"@humanwhocodes/object-schema": "^2.0.1",
"debug": "^4.1.1",
"minimatch": "^3.0.5"
},
@ -1268,9 +1268,9 @@
}
},
"node_modules/@humanwhocodes/object-schema": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz",
"integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz",
"integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==",
"dev": true
},
"node_modules/@iarna/toml": {
@ -2135,6 +2135,42 @@
"node": ">=8"
}
},
"node_modules/@sentry/integrations/node_modules/@sentry/core": {
"version": "7.74.1",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.74.1.tgz",
"integrity": "sha512-LvEhOSfdIvwkr+PdlrT/aA/iOLhkXrSkvjqAQyogE4ddCWeYfS0NoirxNt1EaxMBAWKhYZRqzkA7WA4LDLbzlA==",
"dev": true,
"dependencies": {
"@sentry/types": "7.74.1",
"@sentry/utils": "7.74.1",
"tslib": "^2.4.1 || ^1.9.3"
},
"engines": {
"node": ">=8"
}
},
"node_modules/@sentry/integrations/node_modules/@sentry/types": {
"version": "7.74.1",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.74.1.tgz",
"integrity": "sha512-2jIuPc+YKvXqZETwr2E8VYnsH1zsSUR/wkIvg1uTVeVNyoowJv+YsOtCdeGyL2AwiotUBSPKu7O1Lz0kq5rMOQ==",
"dev": true,
"engines": {
"node": ">=8"
}
},
"node_modules/@sentry/integrations/node_modules/@sentry/utils": {
"version": "7.74.1",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.74.1.tgz",
"integrity": "sha512-qUsqufuHYcy5gFhLZslLxA5kcEOkkODITXW3c7D+x+8iP/AJqa8v8CeUCVNS7RetHCuIeWAbbTClC4c411EwQg==",
"dev": true,
"dependencies": {
"@sentry/types": "7.74.1",
"tslib": "^2.4.1 || ^1.9.3"
},
"engines": {
"node": ">=8"
}
},
"node_modules/@sentry/node": {
"version": "7.74.1",
"resolved": "https://registry.npmjs.org/@sentry/node/-/node-7.74.1.tgz",
@ -2313,9 +2349,9 @@
}
},
"node_modules/@types/jest": {
"version": "29.5.5",
"resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.5.tgz",
"integrity": "sha512-ebylz2hnsWR9mYvmBFbXJXr+33UPc4+ZdxyDXh5w0FlPBTfCVN3wPL+kuOiQt3xvrK419v7XWeAs+AeOksafXg==",
"version": "29.5.6",
"resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.6.tgz",
"integrity": "sha512-/t9NnzkOpXb4Nfvg17ieHE6EeSjDS2SGSpNYfoLbUAeL/EOueU/RSdOWFpfQTXBEM7BguYW1XQ0EbM+6RlIh6w==",
"dev": true,
"dependencies": {
"expect": "^29.0.0",
@ -2553,6 +2589,12 @@
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@ungap/structured-clone": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
"integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==",
"dev": true
},
"node_modules/@webassemblyjs/ast": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz",
@ -3605,18 +3647,19 @@
}
},
"node_modules/eslint": {
"version": "8.51.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.51.0.tgz",
"integrity": "sha512-2WuxRZBrlwnXi+/vFSJyjMqrNjtJqiasMzehF0shoLaW7DzS3/9Yvrmq5JiT66+pNjiX4UBnLDiKHcWAr/OInA==",
"version": "8.52.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.52.0.tgz",
"integrity": "sha512-zh/JHnaixqHZsolRB/w9/02akBk9EPrOs9JwcTP2ek7yL5bVvXuRariiaAjjoJ5DvuwQ1WAE/HsMz+w17YgBCg==",
"dev": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.6.1",
"@eslint/eslintrc": "^2.1.2",
"@eslint/js": "8.51.0",
"@humanwhocodes/config-array": "^0.11.11",
"@eslint/js": "8.52.0",
"@humanwhocodes/config-array": "^0.11.13",
"@humanwhocodes/module-importer": "^1.0.1",
"@nodelib/fs.walk": "^1.2.8",
"@ungap/structured-clone": "^1.2.0",
"ajv": "^6.12.4",
"chalk": "^4.0.0",
"cross-spawn": "^7.0.2",
@ -7320,9 +7363,9 @@
}
},
"node_modules/wrangler": {
"version": "3.13.2",
"resolved": "https://registry.npmjs.org/wrangler/-/wrangler-3.13.2.tgz",
"integrity": "sha512-Z/ZrAL2mJc7E4ialOV9c3wGry0qagp9DfPl5XVd67vtlFPeTSR+1pemtZ5+qI2BXi59kP3OGHBKrrIyG0d9csg==",
"version": "3.14.0",
"resolved": "https://registry.npmjs.org/wrangler/-/wrangler-3.14.0.tgz",
"integrity": "sha512-4vzw11yG1/KXpYKbumvRJ61Iyhm/yKXb/ayOw/2xiIRdKdpsfN9/796d2l525+CDaGwZWswpLENe6ZMS0p/Ghg==",
"dev": true,
"dependencies": {
"@cloudflare/kv-asset-handler": "^0.2.0",

View file

@ -16,15 +16,15 @@
"author": "dangered wolf",
"license": "MIT",
"devDependencies": {
"@cloudflare/workers-types": "^4.20231010.0",
"@cloudflare/workers-types": "^4.20231016.0",
"@microsoft/eslint-formatter-sarif": "^3.0.0",
"@sentry/esbuild-plugin": "^2.8.0",
"@sentry/integrations": "^7.74.0",
"@types/jest": "^29.5.5",
"@sentry/integrations": "^7.74.1",
"@types/jest": "^29.5.6",
"@typescript-eslint/eslint-plugin": "^6.8.0",
"@typescript-eslint/parser": "^6.8.0",
"dotenv": "^16.3.1",
"eslint": "^8.51.0",
"eslint": "^8.52.0",
"eslint-config-prettier": "^9.0.0",
"eslint-config-typescript": "^3.0.0",
"eslint-plugin-optimize-regex": "^1.2.1",
@ -35,7 +35,7 @@
"ts-jest": "^29.1.1",
"ts-loader": "^9.5.0",
"typescript": "^5.2.2",
"wrangler": "^3.13.1"
"wrangler": "^3.14.0"
},
"dependencies": {
"itty-router": "^4.0.23",

View file

@ -300,9 +300,9 @@ export const statusAPI = async (
const quoteTweet = tweet.quoted_status_result;
if (quoteTweet) {
apiTweet.quote = (await populateTweetProperties(quoteTweet, res, language)) as APITweet;
/* Only override the embed_card if it's a basic tweet, since media always takes precedence */
if (apiTweet.embed_card === 'tweet') {
apiTweet.embed_card = apiTweet.quote.embed_card;
/* Only override the twitter_card if it's a basic tweet, since media always takes precedence */
if (apiTweet.twitter_card === 'tweet' && apiTweet.quote !== null) {
apiTweet.twitter_card = apiTweet.quote.twitter_card;
}
}

View file

@ -36,6 +36,16 @@ export const handleStatus = async (
const api = await statusAPI(status, language, event as FetchEvent, flags);
const tweet = api?.tweet as APITweet;
/* Catch this request if it's an API response */
if (flags?.api) {
return {
response: new Response(JSON.stringify(api), {
headers: { ...Constants.RESPONSE_HEADERS, ...Constants.API_RESPONSE_HEADERS },
status: api.code
})
};
}
/* If there was any errors fetching the Tweet, we'll return it */
switch (api.code) {
case 401:
@ -66,16 +76,6 @@ export const handleStatus = async (
let ivbody = '';
/* Catch this request if it's an API response */
if (flags?.api) {
return {
response: new Response(JSON.stringify(api), {
headers: { ...Constants.RESPONSE_HEADERS, ...Constants.API_RESPONSE_HEADERS },
status: api.code
})
};
}
let overrideMedia: APIMedia | undefined;
// Check if mediaNumber exists, and if that media exists in tweet.media.all. If it does, we'll store overrideMedia variable

View file

@ -1,16 +1,26 @@
import { Constants } from '../constants';
const getDomain = (twitterId: string): string | null => {
const mosaicDomains = Constants.MOSAIC_DOMAIN_LIST;
if (mosaicDomains.length === 0) {
return null;
}
let hash = 0;
for (let i = 0; i < twitterId.length; i++) {
const char = twitterId.charCodeAt(i);
hash = (hash << 5) - hash + char;
}
return mosaicDomains[Math.abs(hash) % mosaicDomains.length];
}
/* Handler for mosaic (multi-image combiner) */
export const handleMosaic = async (
mediaList: APIPhoto[],
id: string
): Promise<APIMosaicPhoto | null> => {
const mosaicDomains = Constants.MOSAIC_DOMAIN_LIST;
let selectedDomain: string | null = null;
while (selectedDomain === null && mosaicDomains.length > 0) {
const domain = mosaicDomains[Math.floor(Math.random() * mosaicDomains.length)];
selectedDomain = domain;
}
const selectedDomain: string | null = getDomain(id);
/* Fallback if there are no Mosaic servers */
if (selectedDomain === null) {

View file

@ -153,7 +153,7 @@ const statusRequest = async (request: IRequest, event: FetchEvent, flags: InputF
Since we obviously have no media to give the user, we'll just redirect to the Tweet.
Embeds will return as usual to bots as if direct media was never specified. */
if (!isBotUA) {
if (!isBotUA && !flags.api) {
const baseUrl = getBaseRedirectUrl(request);
/* Do not cache if using a custom redirect */
const cacheControl = baseUrl !== Constants.TWITTER_ROOT ? 'max-age=0' : undefined;