Simplify relay request headers
CI / test (pull_request) Has been cancelled

This commit is contained in:
2026-06-24 03:52:16 +09:00
parent 1f6e228272
commit 6503bb1b76
8 changed files with 23 additions and 68 deletions
+3 -41
View File
@@ -1,4 +1,3 @@
import { randomBytes, randomUUID } from 'node:crypto';
import { runtimeQueryIds } from './runtime-query-ids.js';
import { type OperationName, QUERY_IDS, TARGET_QUERY_ID_OPERATIONS } from './twitter-client-constants.js';
import type { CurrentUserResult, TwitterClientOptions } from './twitter-client-types.js';
@@ -16,22 +15,14 @@ export type Mixin<TBase extends AbstractConstructor<TwitterClientBase>, TAdded>
export abstract class TwitterClientBase {
protected relayBaseUrl: string;
protected userAgent: string;
protected timeoutMs?: number;
protected quoteDepth: number;
protected clientUuid: string;
protected clientDeviceId: string;
protected clientUserId?: string;
constructor(options: TwitterClientOptions) {
this.relayBaseUrl = this.normalizeRelayBaseUrl(options.relayBaseUrl ?? process.env.TWITTER_RELAY_BASE_URL);
this.userAgent =
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();
}
protected abstract getCurrentUser(): Promise<CurrentUserResult>;
@@ -82,11 +73,7 @@ export abstract class TwitterClientBase {
}
protected getHeaders(): Record<string, string> {
return this.getJsonHeaders();
}
protected createTransactionId(): string {
return randomBytes(16).toString('hex');
return {};
}
private normalizeRelayBaseUrl(value: string | undefined): string {
@@ -105,37 +92,12 @@ export abstract class TwitterClientBase {
return `${this.relayBaseUrl}${parsed.pathname}${parsed.search}`;
}
protected getBaseHeaders(): Record<string, string> {
const headers: Record<string, string> = {
accept: '*/*',
'accept-language': 'en-US,en;q=0.9',
'x-twitter-active-user': 'yes',
'x-twitter-client-language': 'en',
'x-client-uuid': this.clientUuid,
'x-twitter-client-deviceid': this.clientDeviceId,
'x-client-transaction-id': this.createTransactionId(),
'user-agent': this.userAgent,
origin: 'https://x.com',
referer: 'https://x.com/',
};
if (this.clientUserId) {
headers['x-twitter-client-user-id'] = this.clientUserId;
}
return headers;
}
protected getJsonHeaders(): Record<string, string> {
return {
...this.getBaseHeaders(),
'content-type': 'application/json',
};
return { 'content-type': 'application/json' };
}
protected getUploadHeaders(): Record<string, string> {
// Note: do not set content-type; URLSearchParams/FormData need to set it (incl boundary) themselves.
return this.getBaseHeaders();
return {};
}
protected async ensureClientUserId(): Promise<void> {
+3 -4
View File
@@ -22,7 +22,6 @@ export function withBookmarks<TBase extends AbstractConstructor<TwitterClientBas
let urlWithOperation = `${TWITTER_API_BASE}/${queryId}/DeleteBookmark`;
const buildBody = () => JSON.stringify({ variables, queryId });
const buildHeaders = () => ({ ...this.getHeaders(), referer: `https://x.com/i/status/${tweetId}` });
let body = buildBody();
const parseResponse = async (response: Response): Promise<BookmarkMutationResult> => {
@@ -42,7 +41,7 @@ export function withBookmarks<TBase extends AbstractConstructor<TwitterClientBas
try {
let response = await this.fetchWithTimeout(urlWithOperation, {
method: 'POST',
headers: buildHeaders(),
headers: this.getJsonHeaders(),
body,
});
@@ -54,14 +53,14 @@ export function withBookmarks<TBase extends AbstractConstructor<TwitterClientBas
response = await this.fetchWithTimeout(urlWithOperation, {
method: 'POST',
headers: buildHeaders(),
headers: this.getJsonHeaders(),
body,
});
if (response.status === 404) {
const retry = await this.fetchWithTimeout(TWITTER_GRAPHQL_POST_URL, {
method: 'POST',
headers: buildHeaders(),
headers: this.getJsonHeaders(),
body,
});
+5 -9
View File
@@ -71,7 +71,7 @@ export function withPosting<TBase extends AbstractConstructor<TwitterClientBase>
let body = buildBody();
try {
const headers = { ...this.getHeaders(), referer: 'https://x.com/compose/post' };
const headers = this.getJsonHeaders();
let response = await this.fetchWithTimeout(urlWithOperation, {
method: 'POST',
headers,
@@ -88,14 +88,14 @@ export function withPosting<TBase extends AbstractConstructor<TwitterClientBase>
response = await this.fetchWithTimeout(urlWithOperation, {
method: 'POST',
headers: { ...this.getHeaders(), referer: 'https://x.com/compose/post' },
headers,
body,
});
if (response.status === 404) {
const retry = await this.fetchWithTimeout(TWITTER_GRAPHQL_POST_URL, {
method: 'POST',
headers: { ...this.getHeaders(), referer: 'https://x.com/compose/post' },
headers,
body,
});
@@ -224,12 +224,8 @@ export function withPosting<TBase extends AbstractConstructor<TwitterClientBase>
try {
const response = await this.fetchWithTimeout(TWITTER_STATUS_UPDATE_URL, {
method: 'POST',
headers: {
...this.getBaseHeaders(),
'content-type': 'application/x-www-form-urlencoded',
referer: 'https://x.com/compose/post',
},
body: params.toString(),
headers: this.getHeaders(),
body: params,
});
if (!response.ok) {
+1 -1
View File
@@ -105,7 +105,7 @@ export function withSearch<TBase extends AbstractConstructor<TwitterClientBase>>
try {
const response = await this.fetchWithTimeout(url, {
method: 'POST',
headers: this.getHeaders(),
headers: this.getJsonHeaders(),
body: JSON.stringify({ features, queryId }),
});
+1 -1
View File
@@ -251,7 +251,7 @@ export function withTweetDetails<TBase extends AbstractConstructor<TwitterClient
const postResponse = await this.fetchWithTimeout(`${TWITTER_API_BASE}/${queryId}/TweetDetail`, {
method: 'POST',
headers: this.getHeaders(),
headers: this.getJsonHeaders(),
body: JSON.stringify({ variables, features, queryId }),
});
-1
View File
@@ -326,7 +326,6 @@ export interface FollowingResult {
export interface TwitterClientOptions {
relayBaseUrl?: string;
userAgent?: string;
timeoutMs?: number;
// Max depth for quoted tweets (0 disables). Defaults to 1.
quoteDepth?: number;