diff --git a/src/lib/twitter-client-users.ts b/src/lib/twitter-client-users.ts index 758cbe2..8515548 100644 --- a/src/lib/twitter-client-users.ts +++ b/src/lib/twitter-client-users.ts @@ -65,7 +65,7 @@ export function withUsers>( .filter((u) => u !== null); } - private async getFollowersViaRest(userId: string, count: number): Promise { + private async getFollowersViaRest(userId: string, count: number, cursor?: string): Promise { const params = new URLSearchParams({ user_id: userId, count: String(count), @@ -73,6 +73,10 @@ export function withUsers>( include_user_entities: 'false', }); + if (cursor) { + params.set('cursor', cursor); + } + const urls = [ `https://x.com/i/api/1.1/followers/list.json?${params.toString()}`, `https://api.twitter.com/1.1/followers/list.json?${params.toString()}`, @@ -95,11 +99,13 @@ export function withUsers>( const data = (await response.json()) as { users?: RestUser[]; + next_cursor_str?: string; }; const users = this.parseUsersFromRestResponse(data.users); + const nextCursor = data.next_cursor_str && data.next_cursor_str !== '0' ? data.next_cursor_str : undefined; - return { success: true, users }; + return { success: true, users, nextCursor }; } catch (error) { lastError = error instanceof Error ? error.message : String(error); } @@ -108,7 +114,7 @@ export function withUsers>( return { success: false, error: lastError ?? 'Unknown error fetching followers' }; } - private async getFollowingViaRest(userId: string, count: number): Promise { + private async getFollowingViaRest(userId: string, count: number, cursor?: string): Promise { const params = new URLSearchParams({ user_id: userId, count: String(count), @@ -116,6 +122,10 @@ export function withUsers>( include_user_entities: 'false', }); + if (cursor) { + params.set('cursor', cursor); + } + const urls = [ `https://x.com/i/api/1.1/friends/list.json?${params.toString()}`, `https://api.twitter.com/1.1/friends/list.json?${params.toString()}`, @@ -138,11 +148,13 @@ export function withUsers>( const data = (await response.json()) as { users?: RestUser[]; + next_cursor_str?: string; }; const users = this.parseUsersFromRestResponse(data.users); + const nextCursor = data.next_cursor_str && data.next_cursor_str !== '0' ? data.next_cursor_str : undefined; - return { success: true, users }; + return { success: true, users, nextCursor }; } catch (error) { lastError = error instanceof Error ? error.message : String(error); } @@ -333,8 +345,7 @@ export function withUsers>( // GraphQL Following can also return 404 (queryId churn / endpoint flakiness). // Fallback to the internal v1.1 REST endpoint used by the web client through the relay. - // Note: REST fallback does not support cursor pagination. - const restAttempt = await this.getFollowingViaRest(userId, count); + const restAttempt = await this.getFollowingViaRest(userId, count, cursor); if (restAttempt.success) { return restAttempt; } @@ -439,8 +450,7 @@ export function withUsers>( // GraphQL Followers regularly returns 404 (queryId churn / endpoint flakiness). // Fallback to the internal v1.1 REST endpoint used by the web client through the relay. - // Note: REST fallback does not support cursor pagination. - const restAttempt = await this.getFollowersViaRest(userId, count); + const restAttempt = await this.getFollowersViaRest(userId, count, cursor); if (restAttempt.success) { return restAttempt; }