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

View file

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

View file

@ -300,9 +300,9 @@ export const statusAPI = async (
const quoteTweet = tweet.quoted_status_result; const quoteTweet = tweet.quoted_status_result;
if (quoteTweet) { if (quoteTweet) {
apiTweet.quote = (await populateTweetProperties(quoteTweet, res, language)) as APITweet; 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 */ /* Only override the twitter_card if it's a basic tweet, since media always takes precedence */
if (apiTweet.embed_card === 'tweet') { if (apiTweet.twitter_card === 'tweet' && apiTweet.quote !== null) {
apiTweet.embed_card = apiTweet.quote.embed_card; 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 api = await statusAPI(status, language, event as FetchEvent, flags);
const tweet = api?.tweet as APITweet; 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 */ /* If there was any errors fetching the Tweet, we'll return it */
switch (api.code) { switch (api.code) {
case 401: case 401:
@ -66,16 +76,6 @@ export const handleStatus = async (
let ivbody = ''; 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; 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 // 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'; 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) */ /* Handler for mosaic (multi-image combiner) */
export const handleMosaic = async ( export const handleMosaic = async (
mediaList: APIPhoto[], mediaList: APIPhoto[],
id: string id: string
): Promise<APIMosaicPhoto | null> => { ): Promise<APIMosaicPhoto | null> => {
const mosaicDomains = Constants.MOSAIC_DOMAIN_LIST; const selectedDomain: string | null = getDomain(id);
let selectedDomain: string | null = null;
while (selectedDomain === null && mosaicDomains.length > 0) {
const domain = mosaicDomains[Math.floor(Math.random() * mosaicDomains.length)];
selectedDomain = domain;
}
/* Fallback if there are no Mosaic servers */ /* Fallback if there are no Mosaic servers */
if (selectedDomain === null) { 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. 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. */ Embeds will return as usual to bots as if direct media was never specified. */
if (!isBotUA) { if (!isBotUA && !flags.api) {
const baseUrl = getBaseRedirectUrl(request); const baseUrl = getBaseRedirectUrl(request);
/* Do not cache if using a custom redirect */ /* Do not cache if using a custom redirect */
const cacheControl = baseUrl !== Constants.TWITTER_ROOT ? 'max-age=0' : undefined; const cacheControl = baseUrl !== Constants.TWITTER_ROOT ? 'max-age=0' : undefined;