feat: add AboutAccountQuery client support
This commit is contained in:
@@ -23,6 +23,7 @@ const TARGET_OPERATIONS = [
|
|||||||
'ExplorePage',
|
'ExplorePage',
|
||||||
'GenericTimelineById',
|
'GenericTimelineById',
|
||||||
'TrendHistory',
|
'TrendHistory',
|
||||||
|
'AboutAccountQuery',
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
type OperationName = (typeof TARGET_OPERATIONS)[number];
|
type OperationName = (typeof TARGET_OPERATIONS)[number];
|
||||||
|
|||||||
+6
-1
@@ -14,4 +14,9 @@ export type { ExploreTab, NewsFetchOptions, NewsItem, NewsResult } from './twitt
|
|||||||
export type { SearchFetchOptions } from './twitter-client-search.js';
|
export type { SearchFetchOptions } from './twitter-client-search.js';
|
||||||
export type { TimelineFetchOptions } from './twitter-client-timelines.js';
|
export type { TimelineFetchOptions } from './twitter-client-timelines.js';
|
||||||
export type { TweetFetchOptions } from './twitter-client-tweet-detail.js';
|
export type { TweetFetchOptions } from './twitter-client-tweet-detail.js';
|
||||||
export type { TweetResult, UploadMediaResult } from './twitter-client-types.js';
|
export type {
|
||||||
|
AboutAccountProfile,
|
||||||
|
AboutAccountResult,
|
||||||
|
TweetResult,
|
||||||
|
UploadMediaResult,
|
||||||
|
} from './twitter-client-types.js';
|
||||||
|
|||||||
@@ -13,5 +13,6 @@
|
|||||||
"ExploreSidebar": "lpSN4M6qpimkF4nRFPE3nQ",
|
"ExploreSidebar": "lpSN4M6qpimkF4nRFPE3nQ",
|
||||||
"ExplorePage": "kheAINB_4pzRDqkzG3K-ng",
|
"ExplorePage": "kheAINB_4pzRDqkzG3K-ng",
|
||||||
"GenericTimelineById": "uGSr7alSjR9v6QJAIaqSKQ",
|
"GenericTimelineById": "uGSr7alSjR9v6QJAIaqSKQ",
|
||||||
"TrendHistory": "Sj4T-jSB9pr0Mxtsc1UKZQ"
|
"TrendHistory": "Sj4T-jSB9pr0Mxtsc1UKZQ",
|
||||||
|
"AboutAccountQuery": "zs_jFPFT78rBpXv9Z3U2YQ"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ export const FALLBACK_QUERY_IDS = {
|
|||||||
ExplorePage: 'kheAINB_4pzRDqkzG3K-ng',
|
ExplorePage: 'kheAINB_4pzRDqkzG3K-ng',
|
||||||
GenericTimelineById: 'uGSr7alSjR9v6QJAIaqSKQ',
|
GenericTimelineById: 'uGSr7alSjR9v6QJAIaqSKQ',
|
||||||
TrendHistory: 'Sj4T-jSB9pr0Mxtsc1UKZQ',
|
TrendHistory: 'Sj4T-jSB9pr0Mxtsc1UKZQ',
|
||||||
|
AboutAccountQuery: 'zs_jFPFT78rBpXv9Z3U2YQ',
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export type OperationName = keyof typeof FALLBACK_QUERY_IDS;
|
export type OperationName = keyof typeof FALLBACK_QUERY_IDS;
|
||||||
|
|||||||
@@ -324,6 +324,20 @@ export interface FollowingResult {
|
|||||||
nextCursor?: string;
|
nextCursor?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface AboutAccountProfile {
|
||||||
|
accountBasedIn?: string;
|
||||||
|
source?: string;
|
||||||
|
createdCountryAccurate?: boolean;
|
||||||
|
locationAccurate?: boolean;
|
||||||
|
learnMoreUrl?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AboutAccountResult {
|
||||||
|
success: boolean;
|
||||||
|
aboutProfile?: AboutAccountProfile;
|
||||||
|
error?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface TwitterClientOptions {
|
export interface TwitterClientOptions {
|
||||||
relayBaseUrl?: string;
|
relayBaseUrl?: string;
|
||||||
profileName?: string;
|
profileName?: string;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { normalizeHandle } from './normalize-handle.js';
|
import { normalizeHandle } from './normalize-handle.js';
|
||||||
import type { AbstractConstructor, Mixin, TwitterClientBase } from './twitter-client-base.js';
|
import type { AbstractConstructor, Mixin, TwitterClientBase } from './twitter-client-base.js';
|
||||||
import { TWITTER_API_BASE } from './twitter-client-constants.js';
|
import { TWITTER_API_BASE } from './twitter-client-constants.js';
|
||||||
|
import type { AboutAccountResult } from './twitter-client-types.js';
|
||||||
|
|
||||||
/** Result of username to userId lookup */
|
/** Result of username to userId lookup */
|
||||||
export interface UserLookupResult {
|
export interface UserLookupResult {
|
||||||
@@ -13,6 +14,7 @@ export interface UserLookupResult {
|
|||||||
|
|
||||||
export interface TwitterClientUserLookupMethods {
|
export interface TwitterClientUserLookupMethods {
|
||||||
getUserIdByUsername(username: string): Promise<UserLookupResult>;
|
getUserIdByUsername(username: string): Promise<UserLookupResult>;
|
||||||
|
getUserAboutAccount(username: string): Promise<AboutAccountResult>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function withUserLookup<TBase extends AbstractConstructor<TwitterClientBase>>(
|
export function withUserLookup<TBase extends AbstractConstructor<TwitterClientBase>>(
|
||||||
@@ -205,6 +207,97 @@ export function withUserLookup<TBase extends AbstractConstructor<TwitterClientBa
|
|||||||
|
|
||||||
return { success: false, error: lastError ?? 'Unknown error looking up user' };
|
return { success: false, error: lastError ?? 'Unknown error looking up user' };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async getAboutAccountQueryIds(): Promise<string[]> {
|
||||||
|
const primary = await this.getQueryId('AboutAccountQuery');
|
||||||
|
return Array.from(new Set([primary, 'zs_jFPFT78rBpXv9Z3U2YQ']));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get account origin and location information for a user.
|
||||||
|
* Returns data from Twitter's "About this account" feature.
|
||||||
|
*/
|
||||||
|
async getUserAboutAccount(username: string): Promise<AboutAccountResult> {
|
||||||
|
const cleanUsername = normalizeHandle(username);
|
||||||
|
if (!cleanUsername) {
|
||||||
|
return { success: false, error: `Invalid username: ${username}` };
|
||||||
|
}
|
||||||
|
|
||||||
|
const variables = {
|
||||||
|
screenName: cleanUsername,
|
||||||
|
};
|
||||||
|
|
||||||
|
const params = new URLSearchParams({
|
||||||
|
variables: JSON.stringify(variables),
|
||||||
|
});
|
||||||
|
|
||||||
|
let lastError: string | undefined;
|
||||||
|
const queryIds = await this.getAboutAccountQueryIds();
|
||||||
|
|
||||||
|
for (const queryId of queryIds) {
|
||||||
|
const url = `${TWITTER_API_BASE}/${queryId}/AboutAccountQuery?${params.toString()}`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await this.fetchWithTimeout(url, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: this.getHeaders(),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const text = await response.text();
|
||||||
|
if (response.status === 404) {
|
||||||
|
lastError = `HTTP ${response.status}`;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
lastError = `HTTP ${response.status}: ${text.slice(0, 200)}`;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = (await response.json()) as {
|
||||||
|
data?: {
|
||||||
|
user_result_by_screen_name?: {
|
||||||
|
result?: {
|
||||||
|
about_profile?: {
|
||||||
|
account_based_in?: string;
|
||||||
|
source?: string;
|
||||||
|
created_country_accurate?: boolean;
|
||||||
|
location_accurate?: boolean;
|
||||||
|
learn_more_url?: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
errors?: Array<{ message: string }>;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (data.errors && data.errors.length > 0) {
|
||||||
|
lastError = data.errors.map((e) => e.message).join(', ');
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const aboutProfile = data.data?.user_result_by_screen_name?.result?.about_profile;
|
||||||
|
if (!aboutProfile) {
|
||||||
|
lastError = 'Missing about_profile in response';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
aboutProfile: {
|
||||||
|
accountBasedIn: aboutProfile.account_based_in,
|
||||||
|
source: aboutProfile.source,
|
||||||
|
createdCountryAccurate: aboutProfile.created_country_accurate,
|
||||||
|
locationAccurate: aboutProfile.location_accurate,
|
||||||
|
learnMoreUrl: aboutProfile.learn_more_url,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
lastError = error instanceof Error ? error.message : String(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { success: false, error: lastError ?? 'Unknown error fetching account details' };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return TwitterClientUserLookup;
|
return TwitterClientUserLookup;
|
||||||
|
|||||||
Reference in New Issue
Block a user