feat: add quote depth option for quoted tweets

This commit is contained in:
Peter Steinberger
2026-01-01 10:45:15 +01:00
parent c90d3f9ab4
commit 071953e9f4
2 changed files with 59 additions and 39 deletions
+26 -30
View File
@@ -35,7 +35,14 @@ const QUERY_IDS: Record<OperationName, string> = {
const TARGET_QUERY_ID_OPERATIONS = Object.keys(FALLBACK_QUERY_IDS) as Array<OperationName>;
function normalizeQuoteDepth(value?: number): number {
if (value === undefined || value === null) return 1;
if (!Number.isFinite(value)) return 1;
return Math.max(0, Math.floor(value));
}
type GraphqlTweetResult = {
__typename?: string;
rest_id?: string;
legacy?: {
full_text?: string;
@@ -180,6 +187,7 @@ type GraphqlTweetResult = {
}>;
}>;
};
tweet?: GraphqlTweetResult;
quoted_status_result?: {
result?: GraphqlTweetResult;
};
@@ -215,15 +223,8 @@ export interface TweetData {
likeCount?: number;
conversationId?: string;
inReplyToStatusId?: string;
quotedTweet?: {
id: string;
text: string;
author: {
username: string;
name: string;
};
authorId?: string;
};
// Optional quoted tweet; depth controlled by quoteDepth (default: 1).
quotedTweet?: TweetData;
}
export interface GetTweetResult {
@@ -252,6 +253,8 @@ export interface TwitterClientOptions {
cookies: TwitterCookies;
userAgent?: string;
timeoutMs?: number;
// Max depth for quoted tweets (0 disables). Defaults to 1.
quoteDepth?: number;
}
interface CreateTweetResponse {
@@ -276,6 +279,7 @@ export class TwitterClient {
private cookieHeader: string;
private userAgent: string;
private timeoutMs?: number;
private quoteDepth: number;
private clientUuid: string;
private clientDeviceId: string;
private clientUserId?: string;
@@ -291,6 +295,7 @@ export class TwitterClient {
options.userAgent ||
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36';
this.timeoutMs = options.timeoutMs;
this.quoteDepth = normalizeQuoteDepth(options.quoteDepth);
this.clientUuid = randomUUID();
this.clientDeviceId = randomUUID();
}
@@ -686,7 +691,13 @@ export class TwitterClient {
);
}
private mapTweetResult(result: GraphqlTweetResult | undefined): TweetData | undefined {
private unwrapTweetResult(result: GraphqlTweetResult | undefined): GraphqlTweetResult | undefined {
if (!result) return undefined;
if (result.tweet) return result.tweet;
return result;
}
private mapTweetResult(result: GraphqlTweetResult | undefined, quoteDepth = this.quoteDepth): TweetData | undefined {
const userResult = result?.core?.user_results?.result;
const userLegacy = userResult?.legacy;
const userCore = userResult?.core;
@@ -698,26 +709,11 @@ export class TwitterClient {
const text = this.extractTweetText(result);
if (!text) return undefined;
// Extract quoted tweet if present
let quotedTweet: TweetData['quotedTweet'] = undefined;
const quotedResult = result.quoted_status_result?.result;
if (quotedResult?.rest_id) {
const quotedUserResult = quotedResult?.core?.user_results?.result;
const quotedUserLegacy = quotedUserResult?.legacy;
const quotedUserCore = quotedUserResult?.core;
const quotedUsername = quotedUserLegacy?.screen_name ?? quotedUserCore?.screen_name;
const quotedName = quotedUserLegacy?.name ?? quotedUserCore?.name ?? quotedUsername;
const quotedText = this.extractTweetText(quotedResult);
if (quotedUsername && quotedText) {
quotedTweet = {
id: quotedResult.rest_id,
text: quotedText,
author: {
username: quotedUsername,
name: quotedName || quotedUsername,
},
authorId: quotedUserResult?.rest_id,
};
let quotedTweet: TweetData | undefined;
if (quoteDepth > 0) {
const quotedResult = this.unwrapTweetResult(result.quoted_status_result?.result);
if (quotedResult) {
quotedTweet = this.mapTweetResult(quotedResult, quoteDepth - 1);
}
}