emusks 0.0.3 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +4 -277
  2. package/build/graphql.js +24 -0
  3. package/build/v1.1.js +28 -0
  4. package/build/v2.js +28 -0
  5. package/package.json +16 -8
  6. package/src/clients.js +62 -0
  7. package/src/cycletls.js +26 -0
  8. package/src/flow.js +399 -0
  9. package/src/graphql.js +70 -0
  10. package/src/helpers/account.js +271 -0
  11. package/src/helpers/bookmarks.js +120 -0
  12. package/src/helpers/communities.js +271 -0
  13. package/src/helpers/dms.js +131 -0
  14. package/src/helpers/index.js +32 -0
  15. package/src/helpers/lists.js +229 -0
  16. package/src/helpers/media.js +274 -0
  17. package/src/helpers/notifications.js +49 -0
  18. package/src/helpers/search.js +129 -0
  19. package/src/helpers/spaces.js +55 -0
  20. package/src/helpers/syndication.js +33 -0
  21. package/src/helpers/timelines.js +84 -0
  22. package/src/helpers/topics.js +99 -0
  23. package/src/helpers/trends.js +86 -0
  24. package/src/helpers/tweets.js +403 -0
  25. package/src/helpers/users.js +321 -0
  26. package/src/index.js +125 -55
  27. package/src/parsers/timeline.js +105 -0
  28. package/src/static/graphql.js +1 -0
  29. package/src/static/v1.1.js +1 -0
  30. package/src/static/v2.js +1 -0
  31. package/src/v1.1.js +64 -0
  32. package/src/v2.js +64 -0
  33. package/src/methods/bookmarks.js +0 -91
  34. package/src/methods/follow.js +0 -88
  35. package/src/methods/like.js +0 -87
  36. package/src/methods/notifications.js +0 -121
  37. package/src/methods/retweet.js +0 -91
  38. package/src/methods/search.js +0 -124
  39. package/src/methods/timeline.js +0 -215
  40. package/src/methods/tweet.js +0 -396
  41. package/src/methods/users.js +0 -209
  42. package/src/utils/headers.js +0 -23
@@ -1,396 +0,0 @@
1
- import headers from "../utils/headers.js";
2
- import parseTweet from "../parsers/tweet.js";
3
- import parseUser from "../parsers/user.js";
4
-
5
- const createPoll = async function (poll, auth) {
6
- // to add a poll to a tweet, we need to first create a card
7
- // this for whatever reason isn't using the normal graphql api
8
-
9
- if (!poll.choices || poll.choices.length < 2)
10
- throw new Error("a poll must have at least 2 choices");
11
-
12
- if (!poll.choices || poll.choices.length > 4)
13
- throw new Error("a poll must not have more than 4 choices");
14
-
15
- const pollResponse = await (
16
- await fetch("https://caps.x.com/v2/cards/create.json", {
17
- headers: {
18
- Accept: "*/*",
19
- "accept-language": "en-US,en;q=0.9",
20
-
21
- authorization:
22
- "Bearer AAAAAAAAAAAAAAAAAAAAAFQODgEAAAAAVHTp76lzh3rFzcHbmHVvQxYYpTw%3DckAlMINMjmCwxUcaXbAN4XqJVdgMJaHqNOFgPMK0zN1qLqLQCF",
23
- "content-type": "application/x-www-form-urlencoded",
24
- "x-csrf-token": auth.csrfToken,
25
-
26
- "x-twitter-active-user": "yes",
27
- "x-twitter-auth-type": "OAuth2Session",
28
- "x-twitter-client-language": "en",
29
-
30
- cookie: auth.cookies,
31
- ...headers,
32
- },
33
- body: `card_data=${encodeURIComponent(
34
- JSON.stringify({
35
- "twitter:card": `poll${poll.choices.length}choice_text_only`,
36
- "twitter:api:api:endpoint": "1",
37
- "twitter:long:duration_minutes": poll.duration_minutes || 1440,
38
- ...Object.fromEntries(
39
- poll.choices.map((choice, i) => [
40
- `twitter:string:choice${i + 1}_label`,
41
- choice,
42
- ])
43
- ),
44
- })
45
- )}`,
46
- method: "POST",
47
- })
48
- ).json();
49
-
50
- if (!pollResponse?.card_uri)
51
- throw new Error(
52
- `failed to create poll card: ${JSON.stringify(pollResponse, null, 2)}`
53
- );
54
-
55
- return pollResponse?.card_uri;
56
- };
57
-
58
- export async function tweet(body = {}) {
59
- if (!this.auth.cookies) {
60
- throw new Error("you must be logged in to tweet");
61
- }
62
-
63
- if (body.poll) {
64
- if (body.card) throw new Error("a tweet can't have both a poll and a card");
65
-
66
- body.card = await createPoll(body.poll, this.auth);
67
- }
68
-
69
- const out = await (
70
- await fetch(
71
- `https://pro.x.com/i/api/graphql/IVdJU2Vjw2llhmJOAZy9Ow/CreateTweet`,
72
- {
73
- headers: {
74
- Accept: "*/*",
75
- "accept-language": "en-US,en;q=0.9",
76
-
77
- authorization:
78
- "Bearer AAAAAAAAAAAAAAAAAAAAAFQODgEAAAAAVHTp76lzh3rFzcHbmHVvQxYYpTw%3DckAlMINMjmCwxUcaXbAN4XqJVdgMJaHqNOFgPMK0zN1qLqLQCF",
79
- "content-type": "application/json",
80
-
81
- "x-client-transaction-id": Math.random().toString().replace("0.", ""),
82
- "x-csrf-token": this.auth.csrfToken,
83
-
84
- "x-twitter-active-user": "yes",
85
- "x-twitter-auth-type": "OAuth2Session",
86
- "x-twitter-client-language": "en",
87
-
88
- cookie: this.auth.cookies,
89
- ...headers,
90
- },
91
- method: "POST",
92
- body: JSON.stringify({
93
- variables: {
94
- tweet_text: body.text,
95
- reply: body.reply
96
- ? {
97
- in_reply_to_tweet_id: body.reply,
98
- exclude_reply_user_ids: [],
99
- }
100
- : undefined,
101
- dark_request: false,
102
- card_uri: body.card || undefined,
103
- media: body.media || {
104
- media_entities: [],
105
- possibly_sensitive: false,
106
- },
107
- semantic_annotation_ids: [],
108
- disallowed_reply_options: body.disallowed_reply_options || null,
109
- },
110
- features: {
111
- premium_content_api_read_enabled: false,
112
- communities_web_enable_tweet_community_results_fetch: true,
113
- c9s_tweet_anatomy_moderator_badge_enabled: true,
114
- responsive_web_grok_analyze_button_fetch_trends_enabled: false,
115
- responsive_web_grok_analyze_post_followups_enabled: true,
116
- responsive_web_jetfuel_frame: false,
117
- responsive_web_grok_share_attachment_enabled: true,
118
- responsive_web_edit_tweet_api_enabled: true,
119
- graphql_is_translatable_rweb_tweet_is_translatable_enabled: true,
120
- view_counts_everywhere_api_enabled: true,
121
- longform_notetweets_consumption_enabled: true,
122
- responsive_web_twitter_article_tweet_consumption_enabled: true,
123
- tweet_awards_web_tipping_enabled: false,
124
- responsive_web_grok_show_grok_translated_post: false,
125
- responsive_web_grok_analysis_button_from_backend: true,
126
- creator_subscriptions_quote_tweet_preview_enabled: false,
127
- longform_notetweets_rich_text_read_enabled: true,
128
- longform_notetweets_inline_media_enabled: true,
129
- profile_label_improvements_pcf_label_in_post_enabled: true,
130
- rweb_tipjar_consumption_enabled: true,
131
- responsive_web_graphql_exclude_directive_enabled: true,
132
- verified_phone_label_enabled: false,
133
- articles_preview_enabled: true,
134
- responsive_web_graphql_skip_user_profile_image_extensions_enabled: false,
135
- freedom_of_speech_not_reach_fetch_enabled: true,
136
- standardized_nudges_misinfo: true,
137
- tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled: true,
138
- responsive_web_grok_image_annotation_enabled: true,
139
- responsive_web_graphql_timeline_navigation_enabled: true,
140
- responsive_web_enhance_cards_enabled: false,
141
- includePromotedContent: false,
142
- },
143
- queryId: "IVdJU2Vjw2llhmJOAZy9Ow",
144
- }),
145
- }
146
- )
147
- ).json();
148
-
149
- if (!out?.data?.create_tweet) {
150
- throw new Error(`failed to tweet: ${JSON.stringify(out, null, 2)}`);
151
- }
152
-
153
- return {
154
- post_id: out?.data?.create_tweet?.tweet_results?.result?.legacy?.id_str,
155
- post_url: `https://x.com/i/status/${out?.data?.create_tweet?.tweet_results?.result?.legacy?.id_str}`,
156
- response: out,
157
- };
158
- }
159
-
160
- export async function deleteTweet(tweetId) {
161
- if (!this.auth.cookies) {
162
- throw new Error("you must be logged in to delete a tweet");
163
- }
164
-
165
- const out = await (
166
- await fetch(
167
- `https://pro.x.com/i/api/graphql/VaenaVgh5q5ih7kvyVjgtg/DeleteTweet`,
168
- {
169
- headers: {
170
- Accept: "*/*",
171
- "accept-language": "en-US,en;q=0.9",
172
-
173
- authorization:
174
- "Bearer AAAAAAAAAAAAAAAAAAAAAFQODgEAAAAAVHTp76lzh3rFzcHbmHVvQxYYpTw%3DckAlMINMjmCwxUcaXbAN4XqJVdgMJaHqNOFgPMK0zN1qLqLQCF",
175
- "content-type": "application/json",
176
-
177
- "x-client-transaction-id": Math.random().toString().replace("0.", ""),
178
- "x-csrf-token": this.auth.csrfToken,
179
-
180
- "x-twitter-active-user": "yes",
181
- "x-twitter-auth-type": "OAuth2Session",
182
- "x-twitter-client-language": "en",
183
-
184
- cookie: this.auth.cookies,
185
- ...headers,
186
- },
187
- method: "POST",
188
- body: JSON.stringify({
189
- variables: {
190
- dark_request: false,
191
- tweet_id: tweetId,
192
- },
193
- queryId: "VaenaVgh5q5ih7kvyVjgtg",
194
- }),
195
- }
196
- )
197
- ).json();
198
-
199
- if (!out?.data?.delete_tweet) {
200
- throw new Error(`failed to delete tweet: ${JSON.stringify(out, null, 2)}`);
201
- }
202
-
203
- return true;
204
- }
205
-
206
- export async function getTweet(tweetId) {
207
- const raw = await (
208
- await fetch(
209
- `https://cdn.syndication.twimg.com/tweet-result?id=${tweetId}&lang=en&token=${Math.random()
210
- .toString()
211
- .replace("0.", "")}`
212
- )
213
- ).json();
214
-
215
- const tweetObj = parseTweet(raw);
216
- if (tweetObj.user) {
217
- tweetObj.user = parseUser(tweetObj.user);
218
- }
219
- return tweetObj;
220
- }
221
-
222
- export async function getFullTweet(
223
- tweetId,
224
- { rankingMode = "Relevance", includeAds = false, cursor = undefined } = {}
225
- ) {
226
- if (!this.auth.cookies) {
227
- throw new Error(
228
- "you must be logged in to get a full tweet. consider using getTweet instead for a shorter yet still useful result without needing to log in"
229
- );
230
- }
231
-
232
- const variables = {
233
- focalTweetId: tweetId,
234
- referrer: "ntab",
235
- with_rux_injections: false,
236
- rankingMode: rankingMode,
237
- includePromotedContent: includeAds,
238
- withCommunity: true,
239
- withQuickPromoteEligibilityTweetFields: true,
240
- withBirdwatchNotes: true,
241
- withVoice: true,
242
- };
243
-
244
- if (cursor) {
245
- variables.cursor = cursor;
246
- }
247
-
248
- const out = await (
249
- await fetch(
250
- `https://pro.x.com/i/api/graphql/8IPrg-fiWPM4p735QRfGqA/TweetDetail?variables=${encodeURIComponent(
251
- JSON.stringify(variables)
252
- )}&features=${encodeURIComponent(
253
- JSON.stringify({
254
- rweb_video_screen_enabled: false,
255
- payments_enabled: false,
256
- profile_label_improvements_pcf_label_in_post_enabled: true,
257
- rweb_tipjar_consumption_enabled: true,
258
- verified_phone_label_enabled: false,
259
- creator_subscriptions_tweet_preview_api_enabled: true,
260
- responsive_web_graphql_timeline_navigation_enabled: true,
261
- responsive_web_graphql_skip_user_profile_image_extensions_enabled: false,
262
- premium_content_api_read_enabled: false,
263
- communities_web_enable_tweet_community_results_fetch: true,
264
- c9s_tweet_anatomy_moderator_badge_enabled: true,
265
- responsive_web_grok_analyze_button_fetch_trends_enabled: false,
266
- responsive_web_grok_analyze_post_followups_enabled: true,
267
- responsive_web_jetfuel_frame: false,
268
- responsive_web_grok_share_attachment_enabled: true,
269
- articles_preview_enabled: true,
270
- responsive_web_edit_tweet_api_enabled: true,
271
- graphql_is_translatable_rweb_tweet_is_translatable_enabled: true,
272
- view_counts_everywhere_api_enabled: true,
273
- longform_notetweets_consumption_enabled: true,
274
- responsive_web_twitter_article_tweet_consumption_enabled: true,
275
- tweet_awards_web_tipping_enabled: false,
276
- responsive_web_grok_show_grok_translated_post: false,
277
- responsive_web_grok_analysis_button_from_backend: false,
278
- creator_subscriptions_quote_tweet_preview_enabled: false,
279
- freedom_of_speech_not_reach_fetch_enabled: true,
280
- standardized_nudges_misinfo: true,
281
- tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled: true,
282
- longform_notetweets_rich_text_read_enabled: true,
283
- longform_notetweets_inline_media_enabled: true,
284
- responsive_web_grok_image_annotation_enabled: true,
285
- responsive_web_enhance_cards_enabled: false,
286
- })
287
- )}&fieldToggles=${encodeURIComponent(
288
- JSON.stringify({
289
- withArticleRichContentState: true,
290
- withArticlePlainText: false,
291
- withGrokAnalyze: false,
292
- withDisallowedReplyControls: true,
293
- })
294
- )}`,
295
- {
296
- headers: {
297
- authorization:
298
- "Bearer AAAAAAAAAAAAAAAAAAAAAFQODgEAAAAAVHTp76lzh3rFzcHbmHVvQxYYpTw%3DckAlMINMjmCwxUcaXbAN4XqJVdgMJaHqNOFgPMK0zN1qLqLQCF",
299
- "content-type": "application/json",
300
-
301
- "x-client-transaction-id": Math.random().toString().replace("0.", ""),
302
- "x-csrf-token": this.auth.csrfToken,
303
-
304
- "x-twitter-active-user": "yes",
305
- "x-twitter-auth-type": "OAuth2Session",
306
- "x-twitter-client-language": "en",
307
-
308
- cookie: this.auth.cookies,
309
- ...headers,
310
- },
311
- }
312
- )
313
- ).json();
314
-
315
- const entries =
316
- out?.data?.threaded_conversation_with_injections_v2?.instructions?.find(
317
- (e) => e.type === "TimelineAddEntries"
318
- )?.entries;
319
-
320
- if (!entries.length) {
321
- throw new Error("unable to find instructions");
322
- }
323
-
324
- const cursors = {
325
- top: null,
326
- bottom: null,
327
- };
328
-
329
- entries.forEach((entry) => {
330
- if (entry.entryId && entry.entryId.startsWith("cursor-")) {
331
- if (entry.entryId.includes("top")) {
332
- cursors.top = entry.content?.itemContent?.value;
333
- } else if (entry.entryId.includes("bottom")) {
334
- cursors.bottom = entry.content?.itemContent?.value;
335
- }
336
- }
337
- });
338
-
339
- const tweets = [];
340
-
341
- entries.forEach((entry) => {
342
- if (entry.content?.itemContent?.tweet_results?.result) {
343
- const rawTweet = entry.content.itemContent.tweet_results.result;
344
- const tweetObj = parseTweet(rawTweet);
345
- if (tweetObj.user) {
346
- tweetObj.user = parseUser(tweetObj.user);
347
- }
348
- tweets.push(tweetObj);
349
- } else if (
350
- entry.content?.entryType === "TimelineTimelineModule" &&
351
- entry.content?.items
352
- ) {
353
- entry.content.items.forEach((item) => {
354
- if (item.item?.itemContent?.tweet_results?.result) {
355
- const rawTweet = item.item.itemContent.tweet_results.result;
356
- const tweetObj = parseTweet(rawTweet);
357
- if (tweetObj.user) {
358
- tweetObj.user = parseUser(tweetObj.user);
359
- }
360
- tweets.push(tweetObj);
361
- }
362
- });
363
- }
364
- });
365
-
366
- return {
367
- tweets,
368
- cursors,
369
- };
370
- }
371
-
372
- export async function getTweetReplies(
373
- tweetId,
374
- { rankingMode = "Relevance", includeAds = false, cursor = undefined } = {}
375
- ) {
376
- const fullTweetData = await this.getFullTweet(tweetId, {
377
- rankingMode,
378
- includeAds,
379
- cursor,
380
- });
381
-
382
- const mainTweet = fullTweetData.tweets.find((tweet) => tweet.id === tweetId);
383
- const replies = fullTweetData.tweets.filter(
384
- (tweet) =>
385
- tweet.id !== tweetId &&
386
- (tweet.in_reply_to_status_id === tweetId ||
387
- tweet.conversation_id === tweetId)
388
- );
389
-
390
- return {
391
- mainTweet,
392
- replies,
393
- cursors: fullTweetData.cursors,
394
- fullTweetData,
395
- };
396
- }
@@ -1,209 +0,0 @@
1
- import parseTweet from "../parsers/tweet.js";
2
- import parseUser from "../parsers/user.js";
3
- import headers from "../utils/headers.js";
4
-
5
- export async function getUser(username) {
6
- const out = await (
7
- await fetch(
8
- `https://pro.x.com/i/api/graphql/jUKA--0QkqGIFhmfRZdWrQ/UserByScreenName?variables=${encodeURIComponent(
9
- JSON.stringify({
10
- screen_name: username,
11
- })
12
- )}&features=${encodeURIComponent(
13
- JSON.stringify({
14
- responsive_web_grok_bio_auto_translation_is_enabled: false,
15
- hidden_profile_subscriptions_enabled: false,
16
- payments_enabled: false,
17
- profile_label_improvements_pcf_label_in_post_enabled: true,
18
- rweb_tipjar_consumption_enabled: true,
19
- verified_phone_label_enabled: true,
20
- subscriptions_verification_info_is_identity_verified_enabled: true,
21
- subscriptions_verification_info_verified_since_enabled: true,
22
- highlights_tweets_tab_ui_enabled: true,
23
- responsive_web_twitter_article_notes_tab_enabled: true,
24
- subscriptions_feature_can_gift_premium: true,
25
- creator_subscriptions_tweet_preview_api_enabled: true,
26
- responsive_web_graphql_skip_user_profile_image_extensions_enabled: false,
27
- responsive_web_graphql_timeline_navigation_enabled: true,
28
- })
29
- )}&fieldToggles=${encodeURIComponent(
30
- JSON.stringify({
31
- withAuxiliaryUserLabels: true,
32
- })
33
- )}`,
34
- {
35
- headers: {
36
- Accept: "*/*",
37
- "accept-language": "en-US,en;q=0.9",
38
-
39
- authorization:
40
- "Bearer AAAAAAAAAAAAAAAAAAAAAFQODgEAAAAAVHTp76lzh3rFzcHbmHVvQxYYpTw%3DckAlMINMjmCwxUcaXbAN4XqJVdgMJaHqNOFgPMK0zN1qLqLQCF",
41
- "content-type": "application/json",
42
-
43
- "x-csrf-token": this.auth.csrfToken,
44
-
45
- "x-twitter-active-user": "yes",
46
- "x-twitter-auth-type": "OAuth2Session",
47
- "x-twitter-client-language": "en",
48
-
49
- cookie: this.auth.cookies,
50
- ...headers,
51
- },
52
- }
53
- )
54
- ).json();
55
-
56
- if (!out?.data?.user?.result)
57
- throw new Error(`failed to get user: ${JSON.stringify(out, null, 1)}`);
58
-
59
- const user = parseUser(out.data.user.result);
60
-
61
- user.follow = () => this.follow(out.data.user.result.rest_id);
62
- user.unfollow = () => this.unfollow(out.data.user.result.rest_id);
63
- user.getTweets = (options) =>
64
- this.getUserTweets(out.data.user.result.rest_id, options);
65
-
66
- return user;
67
- }
68
-
69
- export async function getUserTweets(
70
- userId,
71
- { count = 20, includePromotedContent = false, cursor = undefined } = {}
72
- ) {
73
- if (!this.auth.cookies) {
74
- throw new Error("you must be logged in to get user tweets");
75
- }
76
-
77
- const variables = {
78
- userId: userId,
79
- count: count,
80
- includePromotedContent: includePromotedContent,
81
- withQuickPromoteEligibilityTweetFields: true,
82
- withVoice: true,
83
- };
84
-
85
- if (cursor) {
86
- variables.cursor = cursor;
87
- }
88
-
89
- const out = await (
90
- await fetch(
91
- `https://pro.x.com/i/api/graphql/2ItQrd86P8C0pDU6td3Z7Q/UserTweets?variables=${encodeURIComponent(
92
- JSON.stringify(variables)
93
- )}&features=${encodeURIComponent(
94
- JSON.stringify({
95
- rweb_video_screen_enabled: false,
96
- payments_enabled: false,
97
- profile_label_improvements_pcf_label_in_post_enabled: true,
98
- rweb_tipjar_consumption_enabled: true,
99
- verified_phone_label_enabled: false,
100
- creator_subscriptions_tweet_preview_api_enabled: true,
101
- responsive_web_graphql_timeline_navigation_enabled: true,
102
- responsive_web_graphql_skip_user_profile_image_extensions_enabled: false,
103
- premium_content_api_read_enabled: false,
104
- communities_web_enable_tweet_community_results_fetch: true,
105
- c9s_tweet_anatomy_moderator_badge_enabled: true,
106
- responsive_web_grok_analyze_button_fetch_trends_enabled: false,
107
- responsive_web_grok_analyze_post_followups_enabled: true,
108
- responsive_web_jetfuel_frame: false,
109
- responsive_web_grok_share_attachment_enabled: true,
110
- articles_preview_enabled: true,
111
- responsive_web_edit_tweet_api_enabled: true,
112
- graphql_is_translatable_rweb_tweet_is_translatable_enabled: true,
113
- view_counts_everywhere_api_enabled: true,
114
- longform_notetweets_consumption_enabled: true,
115
- responsive_web_twitter_article_tweet_consumption_enabled: true,
116
- tweet_awards_web_tipping_enabled: false,
117
- responsive_web_grok_show_grok_translated_post: false,
118
- responsive_web_grok_analysis_button_from_backend: false,
119
- creator_subscriptions_quote_tweet_preview_enabled: false,
120
- freedom_of_speech_not_reach_fetch_enabled: true,
121
- standardized_nudges_misinfo: true,
122
- tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled: true,
123
- longform_notetweets_rich_text_read_enabled: true,
124
- longform_notetweets_inline_media_enabled: true,
125
- responsive_web_grok_image_annotation_enabled: true,
126
- responsive_web_enhance_cards_enabled: false,
127
- })
128
- )}&fieldToggles=${encodeURIComponent(
129
- JSON.stringify({
130
- withArticlePlainText: false,
131
- })
132
- )}`,
133
- {
134
- headers: {
135
- Accept: "*/*",
136
- "accept-language": "en-US,en;q=0.9",
137
- authorization:
138
- "Bearer AAAAAAAAAAAAAAAAAAAAAFQODgEAAAAAVHTp76lzh3rFzcHbmHVvQxYYpTw%3DckAlMINMjmCwxUcaXbAN4XqJVdgMJaHqNOFgPMK0zN1qLqLQCF",
139
- "content-type": "application/json",
140
- "x-csrf-token": this.auth.csrfToken,
141
- "x-twitter-active-user": "yes",
142
- "x-twitter-auth-type": "OAuth2Session",
143
- "x-twitter-client-language": "en",
144
- cookie: this.auth.cookies,
145
- ...headers,
146
- },
147
- }
148
- )
149
- ).json();
150
-
151
- if (!out?.data?.user?.result?.timeline?.timeline?.instructions) {
152
- throw new Error(
153
- `failed to get user tweets: ${JSON.stringify(out, null, 1)}`
154
- );
155
- }
156
-
157
- const instructions = out.data.user.result.timeline.timeline.instructions;
158
-
159
- const timelineInstruction = instructions.find(
160
- (instruction) =>
161
- instruction.type === "TimelineAddEntries" ||
162
- instruction.type === "TimelinePinEntry"
163
- );
164
-
165
- let entries = [];
166
- const cursors = {
167
- top: null,
168
- bottom: null,
169
- };
170
-
171
- if (!timelineInstruction) {
172
- instructions.forEach((instruction) => {
173
- if (instruction.entries) {
174
- entries.push(...instruction.entries);
175
- } else if (instruction.entry) {
176
- entries.push(instruction.entry);
177
- }
178
- });
179
-
180
- if (entries.length === 0) {
181
- return { tweets: [], cursors };
182
- }
183
- } else {
184
- entries = timelineInstruction.entries || [timelineInstruction.entry];
185
- }
186
-
187
- // Extract cursors for pagination
188
- entries.forEach((entry) => {
189
- if (entry.entryId && entry.entryId.startsWith("cursor-")) {
190
- if (entry.entryId.includes("top")) {
191
- cursors.top = entry.content?.value;
192
- } else if (entry.entryId.includes("bottom")) {
193
- cursors.bottom = entry.content?.value;
194
- }
195
- }
196
- });
197
-
198
- const tweets = entries
199
- .filter((entry) => entry.content?.itemContent?.tweet_results?.result)
200
- .map((entry) => {
201
- const tweet = entry.content.itemContent.tweet_results.result;
202
- return parseTweet(tweet);
203
- });
204
-
205
- return {
206
- tweets,
207
- cursors,
208
- };
209
- }
@@ -1,23 +0,0 @@
1
- export default {
2
- "accept-language": "en-US,en;q=0.9",
3
- "cache-control": "no-cache",
4
- priority: "u=0, i",
5
- pragma: "no-cache",
6
- "sec-ch-ua": '"Chromium";v="137", "Not/A)Brand";v="24"',
7
- "sec-ch-ua-arch": '"arm"',
8
- "sec-ch-ua-bitness": '"64"',
9
- "sec-ch-ua-full-version": '"137.0.7151.104"',
10
- "sec-ch-ua-full-version-list":
11
- '"Chromium";v="137.0.7151.104", "Not/A)Brand";v="24.0.0.0"',
12
- "sec-ch-ua-mobile": "?0",
13
- "sec-ch-ua-model": '""',
14
- "sec-ch-ua-platform": '"macOS"',
15
- "sec-ch-ua-platform-version": '"16.0.0"',
16
- "sec-fetch-dest": "document",
17
- "sec-fetch-mode": "navigate",
18
- "sec-fetch-site": "same-origin",
19
- "sec-fetch-user": "?1",
20
- "upgrade-insecure-requests": "1",
21
- "User-Agent":
22
- "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36",
23
- };