Workaround GraphQL endpoint quirk

This commit is contained in:
dangered wolf 2023-08-16 14:56:23 -04:00
parent bc7e680a0b
commit 2932ab388f
No known key found for this signature in database
GPG key ID: 41E4D37680ED8B58
2 changed files with 171 additions and 0 deletions

View file

@ -18,6 +18,12 @@ const populateTweetProperties = async (
): Promise<APITweet> => {
const apiTweet = {} as APITweet;
if (typeof tweet.core === 'undefined' && typeof tweet.result !== 'undefined') {
tweet = tweet.result;
} else {
console.log('tweet core exists');
}
/* With v2 conversation API we re-add the user object ot the tweet because
Twitter stores it separately in the conversation API. This is to consolidate
it in case a user appears multiple times in a thread. */

View file

@ -304,3 +304,168 @@ type GraphQLUser = {
};
};
};
type GraphQLTweet = {
// Workaround
result: GraphQLTweet;
__typename: 'Tweet';
rest_id: string; // "1674824189176590336",
has_birdwatch_notes: false,
core: {
user_results: {
result: GraphQLUser;
}
}
edit_control: unknown,
edit_perspective: unknown,
is_translatable: false,
views: {
count: string; // "562"
state: string; // "EnabledWithCount"
}
source: string; // "<a href=\"https://mobile.twitter.com\" rel=\"nofollow\">Twitter Web App</a>"
quoted_status_result?: GraphQLTweet;
legacy: {
created_at: string; // "Tue Sep 14 20:00:00 +0000 2021"
conversation_id_str: string; // "1674824189176590336"
bookmark_count: number; // 0
bookmarked: boolean; // false
favorite_count: number; // 28
full_text: string; // "This is a test tweet"
in_reply_to_screen_name: string; // "username"
in_reply_to_status_id_str: string; // "1674824189176590336"
in_reply_to_user_id_str: string; // "783214"
is_quote_status: boolean; // false
quote_count: number; // 39
quoted_status_id_str: string; // "1674824189176590336"
quoted_status_permalink: {
url: string; // "https://t.co/aBcDeFgHiJ"
expanded: string; // "https://twitter.com/username/status/1674824189176590336"
display: string; // "twitter.com/username/statu…"
};
reply_count: number; // 1
retweet_count: number; // 4
lang: string; // "en"
possibly_sensitive: boolean; // false
possibly_sensitive_editable: boolean; // false
entities: {
media: {
display_url: string; // "pic.twitter.com/1X2X3X4X5X"
expanded_url: string; // "https://twitter.com/username/status/1674824189176590336/photo/1" "https://twitter.com/username/status/1674824189176590336/video/1"
id_str: string; // "1674824189176590336"
indices: [number, number]; // [number, number]
media_url_https: string; // "https://pbs.twimg.com/media/FAKESCREENSHOT.jpg" With videos appears to be the thumbnail
type: string; // "photo" Seems to be photo even with videos
}[]
user_mentions: unknown[];
urls: TcoExpansion[];
hashtags: unknown[];
symbols: unknown[];
}
extended_entities: {
media: TweetMedia[]
}
}
card: {
rest_id: string; // "card://1674824189176590336",
legacy: {
binding_values: {
key: `choice${1|2|3|4}_label`|'counts_are_final'|`choice${1|2|3|4}_count`|'last_updated_datetime_utc'|'duration_minutes'|'api'|'card_url'
value: {
string_value: string; // "Option text"
type: 'STRING'
}|{
boolean_value: boolean; // true
type: 'BOOLEAN'
}
}[]
}
}
}
type TweetTombstone = {
__typename: 'TweetTombstone';
tombstone: {
__typename: 'TextTombstone';
text: {
rtl: boolean; // false;
text: string; // "Youre unable to view this Tweet because this account owner limits who can view their Tweets. Learn more"
entities: unknown[];
}
}
}
type GraphQLTimelineTweetEntry = {
/** The entryID contains the tweet ID */
entryId: `tweet-${number}`; // "tweet-1674824189176590336"
sortIndex: string;
content: {
entryType: 'TimelineTimelineItem',
__typename: 'TimelineTimelineItem',
itemContent: {
item: 'TimelineTweet',
__typename: 'TimelineTweet',
tweet_results: {
result: GraphQLTweet|TweetTombstone;
}
}
}
}
type GraphQLConversationThread = {
entryId: `conversationthread-${number}`; // "conversationthread-1674824189176590336"
sortIndex: string;
}
type GraphQLTimelineEntry = GraphQLTimelineTweetEntry|GraphQLConversationThread|unknown;
type V2ThreadInstruction = TimeLineAddEntriesInstruction | TimeLineTerminateTimelineInstruction;
type TimeLineAddEntriesInstruction = {
type: 'TimelineAddEntries';
entries: GraphQLTimelineEntry[];
}
type TimeLineTerminateTimelineInstruction = {
type: 'TimelineTerminateTimeline';
direction: 'Top';
}
type GraphQLTweetNotFoundResponse = {
errors: [{
message: string; // "_Missing: No status found with that ID"
locations: unknown[];
path: string[]; // ["threaded_conversation_with_injections_v2"]
extensions: {
name: string; // "GenericError"
source: string; // "Server"
code: number; // 144
kind: string; // "NonFatal"
tracing: {
trace_id: string; // "2e39ff747de237db"
}
}
code: number; // 144
kind: string; // "NonFatal"
name: string; // "GenericError"
source: string; // "Server"
tracing: {
trace_id: string; // "2e39ff747de237db"
}
}]
data: Record<string, never>;
}
type GraphQLTweetFoundResponse = {
data: {
threaded_conversation_with_injections_v2: {
instructions: V2ThreadInstruction[]
}
}
}
type TweetResultsByRestIdResult = {
errors?: unknown[];
data?: {
tweetResult?: {
result?: {
__typename: 'TweetUnavailable';
reason: 'NsfwLoggedOut'|'Protected';
}|GraphQLTweet
}
}
}