agent-twitter-summary
Version:
A twitter client for agents
128 lines (114 loc) • 4.05 kB
text/typescript
import { Profile, parseProfile } from './profile';
import { QueryProfilesResponse, QueryTweetsResponse } from './timeline-v1';
import { SearchEntryRaw, parseLegacyTweet } from './timeline-v2';
import { Tweet } from './tweets';
export interface SearchTimeline {
data?: {
search_by_raw_query?: {
search_timeline?: {
timeline?: {
instructions?: {
entries?: SearchEntryRaw[];
entry?: SearchEntryRaw;
type?: string;
}[];
};
};
};
};
}
export function parseSearchTimelineTweets(
timeline: SearchTimeline,
): QueryTweetsResponse {
let bottomCursor: string | undefined;
let topCursor: string | undefined;
const tweets: Tweet[] = [];
const instructions =
timeline.data?.search_by_raw_query?.search_timeline?.timeline
?.instructions ?? [];
for (const instruction of instructions) {
if (
instruction.type === 'TimelineAddEntries' ||
instruction.type === 'TimelineReplaceEntry'
) {
if (instruction.entry?.content?.cursorType === 'Bottom') {
bottomCursor = instruction.entry.content.value;
continue;
} else if (instruction.entry?.content?.cursorType === 'Top') {
topCursor = instruction.entry.content.value;
continue;
}
const entries = instruction.entries ?? [];
for (const entry of entries) {
const itemContent = entry.content?.itemContent;
if (itemContent?.tweetDisplayType === 'Tweet') {
const tweetResultRaw = itemContent.tweet_results?.result;
const tweetResult = parseLegacyTweet(
tweetResultRaw?.core?.user_results?.result?.legacy,
tweetResultRaw?.legacy,
);
if (tweetResult.success) {
if (!tweetResult.tweet.views && tweetResultRaw?.views?.count) {
const views = parseInt(tweetResultRaw.views.count);
if (!isNaN(views)) {
tweetResult.tweet.views = views;
}
}
tweets.push(tweetResult.tweet);
}
} else if (entry.content?.cursorType === 'Bottom') {
bottomCursor = entry.content.value;
} else if (entry.content?.cursorType === 'Top') {
topCursor = entry.content.value;
}
}
}
}
return { tweets, next: bottomCursor, previous: topCursor };
}
export function parseSearchTimelineUsers(
timeline: SearchTimeline,
): QueryProfilesResponse {
let bottomCursor: string | undefined;
let topCursor: string | undefined;
const profiles: Profile[] = [];
const instructions =
timeline.data?.search_by_raw_query?.search_timeline?.timeline
?.instructions ?? [];
for (const instruction of instructions) {
if (
instruction.type === 'TimelineAddEntries' ||
instruction.type === 'TimelineReplaceEntry'
) {
if (instruction.entry?.content?.cursorType === 'Bottom') {
bottomCursor = instruction.entry.content.value;
continue;
} else if (instruction.entry?.content?.cursorType === 'Top') {
topCursor = instruction.entry.content.value;
continue;
}
const entries = instruction.entries ?? [];
for (const entry of entries) {
const itemContent = entry.content?.itemContent;
if (itemContent?.userDisplayType === 'User') {
const userResultRaw = itemContent.user_results?.result;
if (userResultRaw?.legacy) {
const profile = parseProfile(
userResultRaw.legacy,
userResultRaw.is_blue_verified,
);
if (!profile.userId) {
profile.userId = userResultRaw.rest_id;
}
profiles.push(profile);
}
} else if (entry.content?.cursorType === 'Bottom') {
bottomCursor = entry.content.value;
} else if (entry.content?.cursorType === 'Top') {
topCursor = entry.content.value;
}
}
}
}
return { profiles, next: bottomCursor, previous: topCursor };
}