- bird follow <username-or-id>: follow a user by handle or ID - bird unfollow <username-or-id>: unfollow a user by handle or ID Uses REST API /1.1/friendships/create.json and /destroy.json with GraphQL fallback. Accepts @handles, bare usernames, or numeric user IDs.
396 lines
8.2 KiB
TypeScript
396 lines
8.2 KiB
TypeScript
// Raw media entity from Twitter API
|
|
export interface GraphqlMediaEntity {
|
|
id_str?: string;
|
|
media_url_https?: string;
|
|
type?: 'photo' | 'video' | 'animated_gif';
|
|
url?: string;
|
|
expanded_url?: string;
|
|
sizes?: {
|
|
thumb?: { w: number; h: number; resize: string };
|
|
small?: { w: number; h: number; resize: string };
|
|
medium?: { w: number; h: number; resize: string };
|
|
large?: { w: number; h: number; resize: string };
|
|
};
|
|
video_info?: {
|
|
duration_millis?: number;
|
|
variants?: Array<{
|
|
bitrate?: number;
|
|
content_type?: string;
|
|
url?: string;
|
|
}>;
|
|
};
|
|
}
|
|
|
|
export type GraphqlTweetResult = {
|
|
__typename?: string;
|
|
rest_id?: string;
|
|
legacy?: {
|
|
full_text?: string;
|
|
created_at?: string;
|
|
reply_count?: number;
|
|
retweet_count?: number;
|
|
favorite_count?: number;
|
|
conversation_id_str?: string;
|
|
in_reply_to_status_id_str?: string | null;
|
|
entities?: {
|
|
media?: GraphqlMediaEntity[];
|
|
};
|
|
extended_entities?: {
|
|
media?: GraphqlMediaEntity[];
|
|
};
|
|
};
|
|
core?: {
|
|
user_results?: {
|
|
result?: {
|
|
rest_id?: string;
|
|
id?: string;
|
|
legacy?: {
|
|
screen_name?: string;
|
|
name?: string;
|
|
};
|
|
core?: {
|
|
screen_name?: string;
|
|
name?: string;
|
|
};
|
|
};
|
|
};
|
|
};
|
|
note_tweet?: {
|
|
note_tweet_results?: {
|
|
result?: {
|
|
text?: string;
|
|
richtext?: {
|
|
text?: string;
|
|
};
|
|
rich_text?: {
|
|
text?: string;
|
|
};
|
|
content?: {
|
|
text?: string;
|
|
richtext?: {
|
|
text?: string;
|
|
};
|
|
rich_text?: {
|
|
text?: string;
|
|
};
|
|
};
|
|
};
|
|
};
|
|
};
|
|
article?: {
|
|
title?: string;
|
|
preview_text?: string;
|
|
article_results?: {
|
|
result?: {
|
|
title?: string;
|
|
preview_text?: string;
|
|
plain_text?: string;
|
|
text?: string;
|
|
richtext?: {
|
|
text?: string;
|
|
};
|
|
rich_text?: {
|
|
text?: string;
|
|
};
|
|
body?: {
|
|
text?: string;
|
|
richtext?: {
|
|
text?: string;
|
|
};
|
|
rich_text?: {
|
|
text?: string;
|
|
};
|
|
};
|
|
content?: {
|
|
text?: string;
|
|
richtext?: {
|
|
text?: string;
|
|
};
|
|
rich_text?: {
|
|
text?: string;
|
|
};
|
|
items?: Array<{
|
|
text?: string;
|
|
content?: {
|
|
text?: string;
|
|
richtext?: { text?: string };
|
|
rich_text?: { text?: string };
|
|
};
|
|
}>;
|
|
};
|
|
sections?: Array<{
|
|
items?: Array<{
|
|
text?: string;
|
|
content?: {
|
|
text?: string;
|
|
richtext?: { text?: string };
|
|
rich_text?: { text?: string };
|
|
};
|
|
}>;
|
|
}>;
|
|
/** Draft.js content state for rich article content */
|
|
content_state?: {
|
|
blocks: Array<{
|
|
key: string;
|
|
type: string;
|
|
text: string;
|
|
data?: Record<string, unknown>;
|
|
entityRanges?: Array<{ key: number; offset: number; length: number }>;
|
|
inlineStyleRanges?: Array<{ offset: number; length: number; style: string }>;
|
|
}>;
|
|
entityMap?:
|
|
| Array<{
|
|
key: string;
|
|
value: {
|
|
type: string;
|
|
mutability: string;
|
|
data: Record<string, unknown>;
|
|
};
|
|
}>
|
|
| Record<
|
|
string,
|
|
{
|
|
type: string;
|
|
mutability: string;
|
|
data: Record<string, unknown>;
|
|
}
|
|
>;
|
|
};
|
|
};
|
|
};
|
|
plain_text?: string;
|
|
text?: string;
|
|
richtext?: {
|
|
text?: string;
|
|
};
|
|
rich_text?: {
|
|
text?: string;
|
|
};
|
|
body?: {
|
|
text?: string;
|
|
richtext?: {
|
|
text?: string;
|
|
};
|
|
rich_text?: {
|
|
text?: string;
|
|
};
|
|
};
|
|
content?: {
|
|
text?: string;
|
|
richtext?: {
|
|
text?: string;
|
|
};
|
|
rich_text?: {
|
|
text?: string;
|
|
};
|
|
items?: Array<{
|
|
text?: string;
|
|
content?: {
|
|
text?: string;
|
|
richtext?: { text?: string };
|
|
rich_text?: { text?: string };
|
|
};
|
|
}>;
|
|
};
|
|
sections?: Array<{
|
|
items?: Array<{
|
|
text?: string;
|
|
content?: {
|
|
text?: string;
|
|
richtext?: { text?: string };
|
|
rich_text?: { text?: string };
|
|
};
|
|
}>;
|
|
}>;
|
|
};
|
|
tweet?: GraphqlTweetResult;
|
|
quoted_status_result?: {
|
|
result?: GraphqlTweetResult;
|
|
};
|
|
};
|
|
|
|
export type TweetResult =
|
|
| {
|
|
success: true;
|
|
tweetId: string;
|
|
}
|
|
| {
|
|
success: false;
|
|
error: string;
|
|
};
|
|
|
|
export type BookmarkMutationResult =
|
|
| {
|
|
success: true;
|
|
}
|
|
| {
|
|
success: false;
|
|
error: string;
|
|
};
|
|
|
|
export type FollowMutationResult =
|
|
| {
|
|
success: true;
|
|
userId?: string;
|
|
username?: string;
|
|
}
|
|
| {
|
|
success: false;
|
|
error: string;
|
|
};
|
|
|
|
export interface UploadMediaResult {
|
|
success: boolean;
|
|
mediaId?: string;
|
|
error?: string;
|
|
}
|
|
|
|
// Parsed media item for output
|
|
export interface TweetMedia {
|
|
type: 'photo' | 'video' | 'animated_gif';
|
|
url: string;
|
|
previewUrl?: string;
|
|
width?: number;
|
|
height?: number;
|
|
// For video/animated_gif: best quality video URL
|
|
videoUrl?: string;
|
|
durationMs?: number;
|
|
}
|
|
|
|
export interface TweetData {
|
|
id: string;
|
|
text: string;
|
|
author: {
|
|
username: string;
|
|
name: string;
|
|
};
|
|
authorId?: string;
|
|
createdAt?: string;
|
|
replyCount?: number;
|
|
retweetCount?: number;
|
|
likeCount?: number;
|
|
conversationId?: string;
|
|
inReplyToStatusId?: string;
|
|
// Optional quoted tweet; depth controlled by quoteDepth (default: 1).
|
|
quotedTweet?: TweetData;
|
|
// Media attachments (photos, videos, GIFs)
|
|
media?: TweetMedia[];
|
|
// Article metadata (for Twitter Articles / long-form posts)
|
|
article?: {
|
|
title: string;
|
|
previewText?: string;
|
|
};
|
|
// Raw GraphQL tweet result (only when includeRaw is enabled)
|
|
_raw?: GraphqlTweetResult;
|
|
}
|
|
|
|
export interface GetTweetResult {
|
|
success: boolean;
|
|
tweet?: TweetData;
|
|
error?: string;
|
|
}
|
|
|
|
export type SearchResult =
|
|
| {
|
|
success: true;
|
|
tweets: TweetData[];
|
|
/** Cursor for fetching the next page of results */
|
|
nextCursor?: string;
|
|
}
|
|
| {
|
|
success: false;
|
|
error: string;
|
|
tweets?: TweetData[];
|
|
/** Cursor for fetching the next page of results */
|
|
nextCursor?: string;
|
|
};
|
|
|
|
export interface CurrentUserResult {
|
|
success: boolean;
|
|
user?: {
|
|
id: string;
|
|
username: string;
|
|
name: string;
|
|
};
|
|
error?: string;
|
|
}
|
|
|
|
export interface TwitterUser {
|
|
id: string;
|
|
username: string;
|
|
name: string;
|
|
description?: string;
|
|
followersCount?: number;
|
|
followingCount?: number;
|
|
isBlueVerified?: boolean;
|
|
profileImageUrl?: string;
|
|
createdAt?: string;
|
|
}
|
|
|
|
export interface FollowingResult {
|
|
success: boolean;
|
|
users?: TwitterUser[];
|
|
error?: string;
|
|
/** Cursor for fetching the next page of results */
|
|
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 {
|
|
relayBaseUrl?: string;
|
|
profileName?: string;
|
|
timeoutMs?: number;
|
|
// Max depth for quoted tweets (0 disables). Defaults to 1.
|
|
quoteDepth?: number;
|
|
}
|
|
|
|
export interface TwitterList {
|
|
id: string;
|
|
name: string;
|
|
description?: string;
|
|
memberCount?: number;
|
|
subscriberCount?: number;
|
|
isPrivate?: boolean;
|
|
createdAt?: string;
|
|
owner?: {
|
|
id: string;
|
|
username: string;
|
|
name: string;
|
|
};
|
|
}
|
|
|
|
export interface ListsResult {
|
|
success: boolean;
|
|
lists?: TwitterList[];
|
|
error?: string;
|
|
}
|
|
|
|
export interface CreateTweetResponse {
|
|
data?: {
|
|
create_tweet?: {
|
|
tweet_results?: {
|
|
result?: {
|
|
rest_id?: string;
|
|
legacy?: {
|
|
full_text?: string;
|
|
};
|
|
};
|
|
};
|
|
};
|
|
};
|
|
errors?: Array<{ message: string; code?: number }>;
|
|
}
|