fix(twitter): handle unavailable quoted tweets
This commit is contained in:
parent
8f1e11203d
commit
7a7453d2d2
|
|
@ -298,6 +298,26 @@ export const paginationTweets = async (endpoint: string, userId: number | undefi
|
|||
return gridEntries || moduleItems || entries || [];
|
||||
};
|
||||
|
||||
const getLegacyUser = (tweet: any) => tweet.core?.user_result?.result?.legacy || tweet.core?.user_results?.result?.legacy;
|
||||
|
||||
const getCoreUser = (tweet: any) => tweet.core?.user_result?.result?.core || tweet.core?.user_results?.result?.core;
|
||||
|
||||
const hydrateLegacyUser = (legacy: any, tweet: any) => {
|
||||
legacy.user = getLegacyUser(tweet);
|
||||
|
||||
const coreUser = getCoreUser(tweet);
|
||||
if (!legacy.user || !coreUser) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (coreUser.name) {
|
||||
legacy.user.name = coreUser.name;
|
||||
}
|
||||
if (coreUser.screen_name) {
|
||||
legacy.user.screen_name = coreUser.screen_name;
|
||||
}
|
||||
};
|
||||
|
||||
export function gatherLegacyFromData(entries: any[], filterNested?: string[], userId?: number | string) {
|
||||
const tweets: any[] = [];
|
||||
const filteredEntries: any[] = [];
|
||||
|
|
@ -327,32 +347,12 @@ export function gatherLegacyFromData(entries: any[], filterNested?: string[], us
|
|||
if (!t?.legacy) {
|
||||
continue;
|
||||
}
|
||||
t.legacy.user = t.core?.user_result?.result?.legacy || t.core?.user_results?.result?.legacy;
|
||||
// Add name and screen_name from core to maintain compatibility
|
||||
if (t.legacy.user && t.core?.user_results?.result?.core) {
|
||||
const coreUser = t.core.user_results.result.core;
|
||||
if (coreUser.name) {
|
||||
t.legacy.user.name = coreUser.name;
|
||||
}
|
||||
if (coreUser.screen_name) {
|
||||
t.legacy.user.screen_name = coreUser.screen_name;
|
||||
}
|
||||
}
|
||||
hydrateLegacyUser(t.legacy, t);
|
||||
t.legacy.id_str = t.rest_id; // avoid falling back to conversation_id_str elsewhere
|
||||
const quote = t.quoted_status_result?.result?.tweet || t.quoted_status_result?.result;
|
||||
if (quote) {
|
||||
if (quote?.legacy) {
|
||||
t.legacy.quoted_status = quote.legacy;
|
||||
t.legacy.quoted_status.user = quote.core.user_result?.result?.legacy || quote.core.user_results?.result?.legacy;
|
||||
// Add name and screen_name from core for quoted status user
|
||||
if (t.legacy.quoted_status.user && quote.core?.user_results?.result?.core) {
|
||||
const quoteCoreUser = quote.core.user_results.result.core;
|
||||
if (quoteCoreUser.name) {
|
||||
t.legacy.quoted_status.user.name = quoteCoreUser.name;
|
||||
}
|
||||
if (quoteCoreUser.screen_name) {
|
||||
t.legacy.quoted_status.user.screen_name = quoteCoreUser.screen_name;
|
||||
}
|
||||
}
|
||||
hydrateLegacyUser(t.legacy.quoted_status, quote);
|
||||
}
|
||||
if (t.note_tweet) {
|
||||
const tmp = t.note_tweet.note_tweet_results.result;
|
||||
|
|
|
|||
|
|
@ -210,7 +210,7 @@ const ProcessFeed = (ctx, { data = [] }, params = {}) => {
|
|||
if (item.is_quote_status) {
|
||||
const quoteData = item.quoted_status;
|
||||
|
||||
if (quoteData) {
|
||||
if (quoteData?.user) {
|
||||
quoteData.full_text = quoteData.full_text || quoteData.text;
|
||||
const author = quoteData.user;
|
||||
quote += '<div class="rsshub-quote">';
|
||||
|
|
@ -416,14 +416,15 @@ const ProcessFeed = (ctx, { data = [] }, params = {}) => {
|
|||
},
|
||||
],
|
||||
}) ||
|
||||
(item.is_quote_status && {
|
||||
links: [
|
||||
{
|
||||
url: `https://x.com/${item.quoted_status?.user?.screen_name}/status/${item.quoted_status?.id_str || item.quoted_status?.conversation_id_str}`,
|
||||
type: 'quote',
|
||||
},
|
||||
],
|
||||
}) ||
|
||||
(item.is_quote_status &&
|
||||
item.quoted_status?.user && {
|
||||
links: [
|
||||
{
|
||||
url: `https://x.com/${item.quoted_status?.user?.screen_name}/status/${item.quoted_status?.id_str || item.quoted_status?.conversation_id_str}`,
|
||||
type: 'quote',
|
||||
},
|
||||
],
|
||||
}) ||
|
||||
(item.in_reply_to_screen_name &&
|
||||
item.in_reply_to_status_id_str && {
|
||||
links: [
|
||||
|
|
|
|||
|
|
@ -0,0 +1,83 @@
|
|||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { gatherLegacyFromData } from './routes/twitter/api/web-api/utils';
|
||||
|
||||
const buildUser = (name: string, screenName: string) => ({
|
||||
core: {
|
||||
name,
|
||||
screen_name: screenName,
|
||||
},
|
||||
legacy: {
|
||||
name: `${name} legacy`,
|
||||
profile_image_url_https: `https://example.com/${screenName}.jpg`,
|
||||
screen_name: `${screenName}_legacy`,
|
||||
},
|
||||
});
|
||||
|
||||
const buildTweetEntry = (id: string, quotedResult?: Record<string, any>) => ({
|
||||
entryId: `tweet-${id}`,
|
||||
content: {
|
||||
itemContent: {
|
||||
tweet_results: {
|
||||
result: {
|
||||
__typename: 'Tweet',
|
||||
core: {
|
||||
user_results: {
|
||||
result: buildUser('Author', 'author'),
|
||||
},
|
||||
},
|
||||
is_quote_status: Boolean(quotedResult),
|
||||
legacy: {
|
||||
conversation_id_str: id,
|
||||
created_at: 'Sun Apr 26 14:01:56 +0000 2026',
|
||||
entities: {
|
||||
urls: [],
|
||||
},
|
||||
full_text: `tweet ${id}`,
|
||||
is_quote_status: Boolean(quotedResult),
|
||||
},
|
||||
quoted_status_result: quotedResult && {
|
||||
result: quotedResult,
|
||||
},
|
||||
rest_id: id,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
describe('gatherLegacyFromData', () => {
|
||||
it('skips quoted tombstones without dropping the timeline', () => {
|
||||
const normalQuote = {
|
||||
__typename: 'Tweet',
|
||||
core: {
|
||||
user_results: {
|
||||
result: buildUser('Quoted', 'quoted'),
|
||||
},
|
||||
},
|
||||
legacy: {
|
||||
conversation_id_str: '10',
|
||||
created_at: 'Sun Apr 26 13:00:00 +0000 2026',
|
||||
entities: {
|
||||
urls: [],
|
||||
},
|
||||
full_text: 'quoted tweet',
|
||||
},
|
||||
rest_id: '10',
|
||||
};
|
||||
const tombstoneQuote = {
|
||||
__typename: 'TweetTombstone',
|
||||
tombstone: {
|
||||
text: {
|
||||
text: 'This post is unavailable.',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const tweets = gatherLegacyFromData([buildTweetEntry('1', normalQuote), buildTweetEntry('2', tombstoneQuote)]);
|
||||
|
||||
expect(tweets).toHaveLength(2);
|
||||
expect(tweets[0].quoted_status.user.screen_name).toBe('quoted');
|
||||
expect(tweets[1].quoted_status).toBeUndefined();
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue