paginate bookmarks and folders
This commit is contained in:
committed by
Peter Steinberger
parent
f048c3d968
commit
7548fd5ed1
@@ -81,28 +81,33 @@ export function withTimelines<TBase extends AbstractConstructor<TwitterClientBas
|
|||||||
return { success: false, error: userResult.error ?? 'Could not determine current user' };
|
return { success: false, error: userResult.error ?? 'Could not determine current user' };
|
||||||
}
|
}
|
||||||
|
|
||||||
const variables = {
|
const userId = userResult.user.id;
|
||||||
userId: userResult.user.id,
|
const pageSize = 20;
|
||||||
count,
|
const seen = new Set<string>();
|
||||||
includePromotedContent: false,
|
const tweets: TweetData[] = [];
|
||||||
withClientEventToken: false,
|
let cursor: string | undefined;
|
||||||
withBirdwatchNotes: false,
|
|
||||||
withVoice: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
const features = buildLikesFeatures();
|
const features = buildLikesFeatures();
|
||||||
|
|
||||||
const params = new URLSearchParams({
|
const fetchPage = async (pageCount: number, pageCursor?: string) => {
|
||||||
variables: JSON.stringify(variables),
|
|
||||||
features: JSON.stringify(features),
|
|
||||||
});
|
|
||||||
|
|
||||||
const tryOnce = async () => {
|
|
||||||
let lastError: string | undefined;
|
let lastError: string | undefined;
|
||||||
let had404 = false;
|
let had404 = false;
|
||||||
const queryIds = await this.getLikesQueryIds();
|
const queryIds = await this.getLikesQueryIds();
|
||||||
|
|
||||||
for (const queryId of queryIds) {
|
for (const queryId of queryIds) {
|
||||||
|
const variables = {
|
||||||
|
userId,
|
||||||
|
count: pageCount,
|
||||||
|
includePromotedContent: false,
|
||||||
|
withClientEventToken: false,
|
||||||
|
withBirdwatchNotes: false,
|
||||||
|
withVoice: true,
|
||||||
|
...(pageCursor ? { cursor: pageCursor } : {}),
|
||||||
|
};
|
||||||
|
|
||||||
|
const params = new URLSearchParams({
|
||||||
|
variables: JSON.stringify(variables),
|
||||||
|
features: JSON.stringify(features),
|
||||||
|
});
|
||||||
const url = `${TWITTER_API_BASE}/${queryId}/Likes?${params.toString()}`;
|
const url = `${TWITTER_API_BASE}/${queryId}/Likes?${params.toString()}`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -147,14 +152,21 @@ export function withTimelines<TBase extends AbstractConstructor<TwitterClientBas
|
|||||||
errors?: Array<{ message: string }>;
|
errors?: Array<{ message: string }>;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (data.errors && data.errors.length > 0) {
|
|
||||||
return { success: false as const, error: data.errors.map((e) => e.message).join(', '), had404 };
|
|
||||||
}
|
|
||||||
|
|
||||||
const instructions = data.data?.user?.result?.timeline?.timeline?.instructions;
|
const instructions = data.data?.user?.result?.timeline?.timeline?.instructions;
|
||||||
const tweets = parseTweetsFromInstructions(instructions, { quoteDepth: this.quoteDepth, includeRaw });
|
if (data.errors && data.errors.length > 0) {
|
||||||
|
const message = data.errors.map((e) => e.message).join(', ');
|
||||||
|
if (!instructions) {
|
||||||
|
if (message.includes('Query: Unspecified')) {
|
||||||
|
lastError = message;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return { success: false as const, error: message, had404 };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const pageTweets = parseTweetsFromInstructions(instructions, { quoteDepth: this.quoteDepth, includeRaw });
|
||||||
|
const nextCursor = extractCursorFromInstructions(instructions);
|
||||||
|
|
||||||
return { success: true as const, tweets, had404 };
|
return { success: true as const, tweets: pageTweets, cursor: nextCursor, had404 };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
lastError = error instanceof Error ? error.message : String(error);
|
lastError = error instanceof Error ? error.message : String(error);
|
||||||
}
|
}
|
||||||
@@ -163,21 +175,51 @@ export function withTimelines<TBase extends AbstractConstructor<TwitterClientBas
|
|||||||
return { success: false as const, error: lastError ?? 'Unknown error fetching likes', had404 };
|
return { success: false as const, error: lastError ?? 'Unknown error fetching likes', had404 };
|
||||||
};
|
};
|
||||||
|
|
||||||
const firstAttempt = await tryOnce();
|
const fetchWithRefresh = async (pageCount: number, pageCursor?: string) => {
|
||||||
|
const firstAttempt = await fetchPage(pageCount, pageCursor);
|
||||||
if (firstAttempt.success) {
|
if (firstAttempt.success) {
|
||||||
return { success: true, tweets: firstAttempt.tweets };
|
return firstAttempt;
|
||||||
}
|
}
|
||||||
|
const shouldRefresh =
|
||||||
if (firstAttempt.had404) {
|
firstAttempt.had404 || (typeof firstAttempt.error === 'string' && firstAttempt.error.includes('Query: Unspecified'));
|
||||||
|
if (shouldRefresh) {
|
||||||
await this.refreshQueryIds();
|
await this.refreshQueryIds();
|
||||||
const secondAttempt = await tryOnce();
|
const secondAttempt = await fetchPage(pageCount, pageCursor);
|
||||||
if (secondAttempt.success) {
|
if (secondAttempt.success) {
|
||||||
return { success: true, tweets: secondAttempt.tweets };
|
return secondAttempt;
|
||||||
}
|
}
|
||||||
return { success: false, error: secondAttempt.error };
|
return { success: false as const, error: secondAttempt.error };
|
||||||
|
}
|
||||||
|
return { success: false as const, error: firstAttempt.error };
|
||||||
|
};
|
||||||
|
|
||||||
|
while (tweets.length < count) {
|
||||||
|
const pageCount = Math.min(pageSize, count - tweets.length);
|
||||||
|
const page = await fetchWithRefresh(pageCount, cursor);
|
||||||
|
if (!page.success) {
|
||||||
|
return { success: false, error: page.error };
|
||||||
}
|
}
|
||||||
|
|
||||||
return { success: false, error: firstAttempt.error };
|
let added = 0;
|
||||||
|
for (const tweet of page.tweets) {
|
||||||
|
if (seen.has(tweet.id)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
seen.add(tweet.id);
|
||||||
|
tweets.push(tweet);
|
||||||
|
added += 1;
|
||||||
|
if (tweets.length >= count) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!page.cursor || page.cursor === cursor || page.tweets.length === 0 || added === 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
cursor = page.cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { success: true, tweets };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -329,19 +371,21 @@ export function withTimelines<TBase extends AbstractConstructor<TwitterClientBas
|
|||||||
}
|
}
|
||||||
pagesFetched += 1;
|
pagesFetched += 1;
|
||||||
|
|
||||||
|
let added = 0;
|
||||||
for (const tweet of page.tweets) {
|
for (const tweet of page.tweets) {
|
||||||
if (seen.has(tweet.id)) {
|
if (seen.has(tweet.id)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
seen.add(tweet.id);
|
seen.add(tweet.id);
|
||||||
tweets.push(tweet);
|
tweets.push(tweet);
|
||||||
|
added += 1;
|
||||||
if (!unlimited && tweets.length >= limit) {
|
if (!unlimited && tweets.length >= limit) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const pageCursor = page.cursor;
|
const pageCursor = page.cursor;
|
||||||
if (!pageCursor || pageCursor === cursor || page.tweets.length === 0) {
|
if (!pageCursor || pageCursor === cursor || page.tweets.length === 0 || added === 0) {
|
||||||
nextCursor = undefined;
|
nextCursor = undefined;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -510,19 +554,21 @@ export function withTimelines<TBase extends AbstractConstructor<TwitterClientBas
|
|||||||
}
|
}
|
||||||
pagesFetched += 1;
|
pagesFetched += 1;
|
||||||
|
|
||||||
|
let added = 0;
|
||||||
for (const tweet of page.tweets) {
|
for (const tweet of page.tweets) {
|
||||||
if (seen.has(tweet.id)) {
|
if (seen.has(tweet.id)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
seen.add(tweet.id);
|
seen.add(tweet.id);
|
||||||
tweets.push(tweet);
|
tweets.push(tweet);
|
||||||
|
added += 1;
|
||||||
if (!unlimited && tweets.length >= limit) {
|
if (!unlimited && tweets.length >= limit) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const pageCursor = page.cursor;
|
const pageCursor = page.cursor;
|
||||||
if (!pageCursor || pageCursor === cursor || page.tweets.length === 0) {
|
if (!pageCursor || pageCursor === cursor || page.tweets.length === 0 || added === 0) {
|
||||||
nextCursor = undefined;
|
nextCursor = undefined;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user