emusks 0.0.2 → 2.0.0

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 (43) 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 +290 -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 +405 -0
  25. package/src/helpers/users.js +321 -0
  26. package/src/index.js +125 -55
  27. package/src/parsers/timeline.js +141 -0
  28. package/src/parsers/tweet.js +9 -2
  29. package/src/static/graphql.js +1 -0
  30. package/src/static/v1.1.js +1 -0
  31. package/src/static/v2.js +1 -0
  32. package/src/v1.1.js +64 -0
  33. package/src/v2.js +64 -0
  34. package/src/methods/bookmarks.js +0 -91
  35. package/src/methods/follow.js +0 -88
  36. package/src/methods/like.js +0 -87
  37. package/src/methods/notifications.js +0 -121
  38. package/src/methods/retweet.js +0 -91
  39. package/src/methods/search.js +0 -124
  40. package/src/methods/timeline.js +0 -215
  41. package/src/methods/tweet.js +0 -396
  42. package/src/methods/users.js +0 -209
  43. package/src/utils/headers.js +0 -23
@@ -0,0 +1,405 @@
1
+ import getCycleTLS from "../cycletls.js";
2
+ import parseTweet from "../parsers/tweet.js";
3
+
4
+ async function createPollCard(client, poll) {
5
+ if (!poll.choices || poll.choices.length < 2)
6
+ throw new Error("a poll must have at least 2 choices");
7
+ if (poll.choices.length > 4)
8
+ throw new Error("a poll must not have more than 4 choices");
9
+
10
+ const hasImages = poll.choices.some((c) => typeof c === "object" && c.image);
11
+ const allImages = poll.choices.every((c) => typeof c === "object" && c.image);
12
+ if (hasImages && !allImages)
13
+ throw new Error(
14
+ "either all poll choices must have images, or none of them",
15
+ );
16
+
17
+ const labels = poll.choices.map((c) => (typeof c === "object" ? c.label : c));
18
+
19
+ for (const label of labels) {
20
+ if (typeof label !== "string" || label.length === 0)
21
+ throw new Error("each poll choice must have a non-empty label");
22
+ if (label.length > 25)
23
+ throw new Error(`poll choice "${label}" exceeds the 25-character limit`);
24
+ }
25
+
26
+ const duration = poll.duration_minutes ?? 1440;
27
+ if (duration < 5 || duration > 10080)
28
+ throw new Error(
29
+ "poll duration must be between 5 and 10 080 minutes (7 days)",
30
+ );
31
+
32
+ const cycleTLS = await getCycleTLS();
33
+
34
+ const cardObj = {
35
+ "twitter:api:api:endpoint": "1",
36
+ "twitter:long:duration_minutes": duration,
37
+ };
38
+
39
+ if (hasImages) {
40
+ // media poll — uses a different card type and includes image media IDs
41
+ cardObj["twitter:card"] = `poll_choice_images`;
42
+ for (let i = 0; i < poll.choices.length; i++) {
43
+ cardObj[`twitter:string:choice${i + 1}_label`] = labels[i];
44
+ cardObj[`twitter:image:choice${i + 1}_image:src:id`] =
45
+ `mis://${poll.choices[i].image}`;
46
+ }
47
+ } else {
48
+ // text-only poll
49
+ cardObj["twitter:card"] = `poll${poll.choices.length}choice_text_only`;
50
+ for (let i = 0; i < labels.length; i++) {
51
+ cardObj[`twitter:string:choice${i + 1}_label`] = labels[i];
52
+ }
53
+ }
54
+
55
+ const cardData = JSON.stringify(cardObj);
56
+
57
+ const res = await cycleTLS(
58
+ "https://caps.x.com/v2/cards/create.json",
59
+ {
60
+ headers: {
61
+ accept: "*/*",
62
+ "accept-language": "en-US,en;q=0.9",
63
+ authorization: `Bearer ${client.auth.client.bearer}`,
64
+ "content-type": "application/x-www-form-urlencoded",
65
+ "x-csrf-token": client.auth.csrfToken,
66
+ "x-twitter-active-user": "yes",
67
+ "x-twitter-auth-type": "OAuth2Session",
68
+ "x-twitter-client-language": "en",
69
+ priority: "u=1, i",
70
+ "sec-ch-ua": '"Not(A:Brand";v="8", "Chromium";v="144"',
71
+ "sec-ch-ua-mobile": "?0",
72
+ "sec-ch-ua-platform": '"macOS"',
73
+ "sec-fetch-dest": "empty",
74
+ "sec-fetch-mode": "cors",
75
+ "sec-fetch-site": "same-site",
76
+ "sec-gpc": "1",
77
+ cookie:
78
+ client.auth.client.headers.cookie +
79
+ (client.elevatedCookies ? `; ${client.elevatedCookies}` : ""),
80
+ },
81
+ body: `card_data=${encodeURIComponent(cardData)}`,
82
+ userAgent:
83
+ client.auth.client.fingerprints?.userAgent ||
84
+ client.auth.client.fingerprints?.["user-agent"],
85
+ ja3: client.auth.client.fingerprints?.ja3,
86
+ ja4r: client.auth.client.fingerprints?.ja4r,
87
+ proxy: client.proxy || undefined,
88
+ referrer: "https://x.com/",
89
+ },
90
+ "post",
91
+ );
92
+
93
+ const data = await res.json();
94
+ if (!data?.card_uri) {
95
+ throw new Error(`failed to create poll card: ${JSON.stringify(data)}`);
96
+ }
97
+ return data.card_uri;
98
+ }
99
+
100
+ export default (client) => ({
101
+ async create(text, opts = {}) {
102
+ let cardUri = opts.cardUri;
103
+
104
+ if (opts.poll) {
105
+ if (cardUri)
106
+ throw new Error("a tweet can't have both a poll and a cardUri");
107
+ cardUri = await createPollCard(client, opts.poll);
108
+ }
109
+
110
+ const res = await client.graphql("CreateTweet", {
111
+ body: {
112
+ variables: {
113
+ tweet_text: text,
114
+ dark_request: false,
115
+ card_uri: cardUri || undefined,
116
+ media: {
117
+ media_entities:
118
+ opts.mediaIds?.map((id) => ({
119
+ media_id: id,
120
+ tagged_users: [],
121
+ })) || [],
122
+ possibly_sensitive: opts.sensitive || false,
123
+ },
124
+ semantic_annotation_ids: [],
125
+ ...(opts.replyTo
126
+ ? {
127
+ reply: {
128
+ in_reply_to_tweet_id: opts.replyTo,
129
+ exclude_reply_user_ids: [],
130
+ },
131
+ }
132
+ : {}),
133
+ ...(opts.quoteTweetId
134
+ ? { attachment_url: `https://x.com/i/status/${opts.quoteTweetId}` }
135
+ : {}),
136
+ ...(opts.conversationControl
137
+ ? { conversation_control: { mode: opts.conversationControl } }
138
+ : {}),
139
+ ...opts.variables,
140
+ },
141
+ },
142
+ });
143
+ const tweet = res?.data?.create_tweet?.tweet_results?.result;
144
+ return tweet ? parseTweet(tweet) : res;
145
+ },
146
+
147
+ async createNote(text, opts = {}) {
148
+ const res = await client.graphql("CreateNoteTweet", {
149
+ body: {
150
+ variables: {
151
+ tweet_text: text,
152
+ dark_request: false,
153
+ media: {
154
+ media_entities:
155
+ opts.mediaIds?.map((id) => ({
156
+ media_id: id,
157
+ tagged_users: [],
158
+ })) || [],
159
+ possibly_sensitive: opts.sensitive || false,
160
+ },
161
+ semantic_annotation_ids: [],
162
+ richtext_options: { richtext_tags: opts.richtext_tags || [] },
163
+ ...(opts.replyTo
164
+ ? {
165
+ reply: {
166
+ in_reply_to_tweet_id: opts.replyTo,
167
+ exclude_reply_user_ids: [],
168
+ },
169
+ }
170
+ : {}),
171
+ ...opts.variables,
172
+ },
173
+ },
174
+ });
175
+ const tweet = res?.data?.notetweet_create?.tweet_results?.result;
176
+ return tweet ? parseTweet(tweet) : res;
177
+ },
178
+
179
+ async delete(tweetId) {
180
+ return await client.graphql("DeleteTweet", {
181
+ body: { variables: { tweet_id: tweetId, dark_request: false } },
182
+ });
183
+ },
184
+
185
+ async like(tweetId) {
186
+ return await client.graphql("FavoriteTweet", {
187
+ body: { variables: { tweet_id: tweetId } },
188
+ });
189
+ },
190
+
191
+ async unlike(tweetId) {
192
+ return await client.graphql("UnfavoriteTweet", {
193
+ body: { variables: { tweet_id: tweetId } },
194
+ });
195
+ },
196
+
197
+ async retweet(tweetId) {
198
+ return await client.graphql("CreateRetweet", {
199
+ body: { variables: { tweet_id: tweetId, dark_request: false } },
200
+ });
201
+ },
202
+
203
+ async unretweet(tweetId) {
204
+ return await client.graphql("DeleteRetweet", {
205
+ body: { variables: { source_tweet_id: tweetId, dark_request: false } },
206
+ });
207
+ },
208
+
209
+ async pin(tweetId) {
210
+ return await client.graphql("PinTweet", {
211
+ body: { variables: { tweet_id: tweetId } },
212
+ });
213
+ },
214
+
215
+ async unpin(tweetId) {
216
+ return await client.graphql("UnpinTweet", {
217
+ body: { variables: { tweet_id: tweetId } },
218
+ });
219
+ },
220
+
221
+ async get(tweetId) {
222
+ const res = await client.graphql("TweetResultByRestId", {
223
+ variables: {
224
+ tweetId,
225
+ withCommunity: false,
226
+ includePromotedContent: false,
227
+ withVoice: false,
228
+ },
229
+ fieldToggles: {
230
+ withArticlePlainText: false,
231
+ withArticleRichContentState: false,
232
+ withAuxiliaryUserLabels: false,
233
+ },
234
+ });
235
+ const tweet = res?.data?.tweetResult?.result;
236
+ return tweet ? parseTweet(tweet) : res;
237
+ },
238
+
239
+ async getMany(tweetIds) {
240
+ const res = await client.graphql("TweetResultsByRestIds", {
241
+ variables: {
242
+ tweetIds,
243
+ withCommunity: false,
244
+ includePromotedContent: false,
245
+ withVoice: false,
246
+ },
247
+ fieldToggles: {
248
+ withArticlePlainText: false,
249
+ withArticleRichContentState: false,
250
+ withAuxiliaryUserLabels: false,
251
+ },
252
+ });
253
+ const results = res?.data?.tweetResult || [];
254
+ return Array.isArray(results)
255
+ ? results.map((r) => (r?.result ? parseTweet(r.result) : r))
256
+ : res;
257
+ },
258
+
259
+ async detail(tweetId, opts = {}) {
260
+ return await client.graphql("TweetDetail", {
261
+ variables: {
262
+ focalTweetId: tweetId,
263
+ with_rux_injections: false,
264
+ rankingMode: "Relevance",
265
+ includePromotedContent: true,
266
+ withCommunity: true,
267
+ withQuickPromoteEligibilityTweetFields: true,
268
+ withBirdwatchNotes: true,
269
+ withVoice: true,
270
+ ...opts.variables,
271
+ },
272
+ fieldToggles: {
273
+ withArticlePlainText: false,
274
+ withArticleRichContentState: false,
275
+ withAuxiliaryUserLabels: false,
276
+ },
277
+ });
278
+ },
279
+
280
+ async editHistory(tweetId) {
281
+ return await client.graphql("TweetEditHistory", {
282
+ variables: { tweetId, withQuickPromoteEligibilityTweetFields: true },
283
+ });
284
+ },
285
+
286
+ async retweeters(tweetId, opts = {}) {
287
+ return await client.graphql("Retweeters", {
288
+ variables: {
289
+ tweetId,
290
+ count: opts.count || 20,
291
+ cursor: opts.cursor,
292
+ includePromotedContent: false,
293
+ },
294
+ });
295
+ },
296
+
297
+ async highlight(tweetId) {
298
+ return await client.graphql("CreateHighlight", {
299
+ body: { variables: { tweet_id: tweetId } },
300
+ });
301
+ },
302
+
303
+ async unhighlight(tweetId) {
304
+ return await client.graphql("DeleteHighlight", {
305
+ body: { variables: { tweet_id: tweetId } },
306
+ });
307
+ },
308
+
309
+ async schedule(text, scheduledAt, opts = {}) {
310
+ return await client.graphql("CreateScheduledTweet", {
311
+ body: {
312
+ variables: {
313
+ post_tweet_request: {
314
+ status: text,
315
+ ...(opts.mediaIds ? { media_ids: opts.mediaIds } : {}),
316
+ ...(opts.replyTo ? { in_reply_to_status_id: opts.replyTo } : {}),
317
+ auto_populate_reply_metadata: true,
318
+ },
319
+ execute_at: Math.floor(new Date(scheduledAt).getTime() / 1000),
320
+ },
321
+ },
322
+ });
323
+ },
324
+
325
+ async deleteScheduled(scheduledTweetId) {
326
+ return await client.graphql("DeleteScheduledTweet", {
327
+ body: { variables: { scheduled_tweet_id: scheduledTweetId } },
328
+ });
329
+ },
330
+
331
+ async getScheduled() {
332
+ return await client.graphql("FetchScheduledTweets", {
333
+ variables: {},
334
+ });
335
+ },
336
+
337
+ async moderate(tweetId) {
338
+ return await client.graphql("ModerateTweet", {
339
+ body: { variables: { tweet_id: tweetId } },
340
+ });
341
+ },
342
+
343
+ async unmoderate(tweetId) {
344
+ return await client.graphql("UnmoderateTweet", {
345
+ body: { variables: { tweet_id: tweetId } },
346
+ });
347
+ },
348
+
349
+ async pinReply(tweetId) {
350
+ return await client.graphql("PinReply", {
351
+ body: { variables: { tweet_id: tweetId } },
352
+ });
353
+ },
354
+
355
+ async unpinReply(tweetId) {
356
+ return await client.graphql("UnpinReply", {
357
+ body: { variables: { tweet_id: tweetId } },
358
+ });
359
+ },
360
+
361
+ async setConversationControl(tweetId, mode) {
362
+ return await client.graphql("ConversationControlChange", {
363
+ body: { variables: { tweet_id: tweetId, mode } },
364
+ });
365
+ },
366
+
367
+ async removeConversationControl(tweetId) {
368
+ return await client.graphql("ConversationControlDelete", {
369
+ body: { variables: { tweet_id: tweetId } },
370
+ });
371
+ },
372
+
373
+ async unmention(tweetId) {
374
+ return await client.graphql("UnmentionUserFromConversation", {
375
+ body: { variables: { tweet_id: tweetId } },
376
+ });
377
+ },
378
+
379
+ async createThread(items) {
380
+ if (!Array.isArray(items) || items.length < 2)
381
+ throw new Error("a thread must have at least 2 tweets");
382
+
383
+ const tweets = [];
384
+ let lastId = null;
385
+
386
+ for (const item of items) {
387
+ const opts = typeof item === "string" ? {} : { ...item };
388
+ const text = typeof item === "string" ? item : item.text;
389
+
390
+ if (lastId) opts.replyTo = lastId;
391
+
392
+ const tweet = await this.create(text, opts);
393
+ tweets.push(tweet);
394
+ lastId = tweet.id;
395
+ }
396
+
397
+ return tweets;
398
+ },
399
+
400
+ async similar(tweetId) {
401
+ return await client.graphql("SimilarPosts", {
402
+ variables: { tweet_id: tweetId },
403
+ });
404
+ },
405
+ });
@@ -0,0 +1,321 @@
1
+ import parseTimeline from "../parsers/timeline.js";
2
+ import parseUser from "../parsers/user.js";
3
+
4
+ export default (client) => ({
5
+ async get(userId) {
6
+ const res = await client.graphql("UserByRestId", {
7
+ variables: { userId, withSafetyModeUserFields: true },
8
+ fieldToggles: { withAuxiliaryUserLabels: false },
9
+ });
10
+ const user = res?.data?.user?.result;
11
+ return user ? parseUser(user) : res;
12
+ },
13
+
14
+ async getByUsername(username) {
15
+ const res = await client.graphql("UserByScreenName", {
16
+ variables: { screen_name: username, withSafetyModeUserFields: true },
17
+ fieldToggles: { withAuxiliaryUserLabels: false },
18
+ });
19
+ const user = res?.data?.user?.result;
20
+ return user ? parseUser(user) : res;
21
+ },
22
+
23
+ async getMany(userIds) {
24
+ const res = await client.graphql("UsersByRestIds", {
25
+ variables: { userIds, withSafetyModeUserFields: true },
26
+ fieldToggles: { withAuxiliaryUserLabels: false },
27
+ });
28
+ const users = res?.data?.users || [];
29
+ return Array.isArray(users)
30
+ ? users.map((u) => (u?.result ? parseUser(u.result) : u))
31
+ : res;
32
+ },
33
+
34
+ async getManyByUsername(screenNames) {
35
+ const res = await client.graphql("UsersByScreenNames", {
36
+ variables: { screen_names: screenNames, withSafetyModeUserFields: true },
37
+ fieldToggles: { withAuxiliaryUserLabels: false },
38
+ });
39
+ const users = res?.data?.users || [];
40
+ return Array.isArray(users)
41
+ ? users.map((u) => (u?.result ? parseUser(u.result) : u))
42
+ : res;
43
+ },
44
+
45
+ async tweets(userId, opts = {}) {
46
+ const raw = await client.graphql("UserTweets", {
47
+ variables: {
48
+ userId,
49
+ count: opts.count || 20,
50
+ cursor: opts.cursor,
51
+ includePromotedContent: true,
52
+ withQuickPromoteEligibilityTweetFields: true,
53
+ withVoice: true,
54
+ withV2Timeline: true,
55
+ ...opts.variables,
56
+ },
57
+ fieldToggles: {
58
+ withArticlePlainText: false,
59
+ withArticleRichContentState: false,
60
+ withAuxiliaryUserLabels: false,
61
+ },
62
+ });
63
+ return parseTimeline(raw);
64
+ },
65
+
66
+ async replies(userId, opts = {}) {
67
+ const raw = await client.graphql("UserTweetsAndReplies", {
68
+ variables: {
69
+ userId,
70
+ count: opts.count || 20,
71
+ cursor: opts.cursor,
72
+ includePromotedContent: true,
73
+ withCommunity: true,
74
+ withVoice: true,
75
+ withV2Timeline: true,
76
+ ...opts.variables,
77
+ },
78
+ fieldToggles: {
79
+ withArticlePlainText: false,
80
+ withArticleRichContentState: false,
81
+ withAuxiliaryUserLabels: false,
82
+ },
83
+ });
84
+ return parseTimeline(raw);
85
+ },
86
+
87
+ async media(userId, opts = {}) {
88
+ const raw = await client.graphql("UserMedia", {
89
+ variables: {
90
+ userId,
91
+ count: opts.count || 20,
92
+ cursor: opts.cursor,
93
+ includePromotedContent: false,
94
+ withClientEventToken: false,
95
+ withBirdwatchNotes: false,
96
+ withVoice: true,
97
+ withV2Timeline: true,
98
+ ...opts.variables,
99
+ },
100
+ fieldToggles: {
101
+ withArticlePlainText: false,
102
+ withArticleRichContentState: false,
103
+ withAuxiliaryUserLabels: false,
104
+ },
105
+ });
106
+ return parseTimeline(raw);
107
+ },
108
+
109
+ async highlights(userId, opts = {}) {
110
+ const raw = await client.graphql("UserHighlightsTweets", {
111
+ variables: {
112
+ userId,
113
+ count: opts.count || 20,
114
+ cursor: opts.cursor,
115
+ includePromotedContent: true,
116
+ withVoice: true,
117
+ ...opts.variables,
118
+ },
119
+ fieldToggles: {
120
+ withArticlePlainText: false,
121
+ withArticleRichContentState: false,
122
+ withAuxiliaryUserLabels: false,
123
+ },
124
+ });
125
+ return parseTimeline(raw);
126
+ },
127
+
128
+ async followers(userId, opts = {}) {
129
+ const raw = await client.graphql("Followers", {
130
+ variables: {
131
+ userId,
132
+ count: opts.count || 20,
133
+ cursor: opts.cursor,
134
+ includePromotedContent: false,
135
+ },
136
+ });
137
+ return parseTimeline(raw);
138
+ },
139
+
140
+ async following(userId, opts = {}) {
141
+ const raw = await client.graphql("Following", {
142
+ variables: {
143
+ userId,
144
+ count: opts.count || 20,
145
+ cursor: opts.cursor,
146
+ includePromotedContent: false,
147
+ },
148
+ });
149
+ return parseTimeline(raw);
150
+ },
151
+
152
+ async verifiedFollowers(userId, opts = {}) {
153
+ const raw = await client.graphql("BlueVerifiedFollowers", {
154
+ variables: {
155
+ userId,
156
+ count: opts.count || 20,
157
+ cursor: opts.cursor,
158
+ includePromotedContent: false,
159
+ },
160
+ });
161
+ return parseTimeline(raw);
162
+ },
163
+
164
+ async followersYouKnow(userId, opts = {}) {
165
+ const raw = await client.graphql("FollowersYouKnow", {
166
+ variables: {
167
+ userId,
168
+ count: opts.count || 20,
169
+ cursor: opts.cursor,
170
+ includePromotedContent: false,
171
+ },
172
+ });
173
+ return parseTimeline(raw);
174
+ },
175
+
176
+ async follow(userId) {
177
+ const res = await client.v1_1("friendships/create", {
178
+ body: JSON.stringify({ user_id: userId }),
179
+ });
180
+ const json = await res.json();
181
+ return json ? parseUser(json) : res;
182
+ },
183
+
184
+ async unfollow(userId) {
185
+ const res = await client.v1_1("friendships/destroy", {
186
+ body: JSON.stringify({ user_id: userId }),
187
+ });
188
+ const json = await res.json();
189
+ return json ? parseUser(json) : res;
190
+ },
191
+
192
+ async block(userId) {
193
+ const res = await client.v1_1("blocks/create", {
194
+ body: JSON.stringify({ user_id: userId }),
195
+ });
196
+ const json = await res.json();
197
+ return json ? parseUser(json) : res;
198
+ },
199
+
200
+ async unblock(userId) {
201
+ const res = await client.v1_1("blocks/destroy", {
202
+ body: JSON.stringify({ user_id: userId }),
203
+ });
204
+ const json = await res.json();
205
+ return json ? parseUser(json) : res;
206
+ },
207
+
208
+ async mute(userId) {
209
+ const res = await client.v1_1("mutes/users/create", {
210
+ body: JSON.stringify({ user_id: userId }),
211
+ });
212
+ const json = await res.json();
213
+ return json ? parseUser(json) : res;
214
+ },
215
+
216
+ async unmute(userId) {
217
+ const res = await client.v1_1("mutes/users/destroy", {
218
+ body: JSON.stringify({ user_id: userId }),
219
+ });
220
+ const json = await res.json();
221
+ return json ? parseUser(json) : res;
222
+ },
223
+
224
+ async removeFollower(userId) {
225
+ return await client.graphql("RemoveFollower", {
226
+ body: { variables: { target_user_id: userId } },
227
+ });
228
+ },
229
+
230
+ async blocked(opts = {}) {
231
+ const raw = await client.graphql("BlockedAccountsAll", {
232
+ variables: {
233
+ count: opts.count || 20,
234
+ cursor: opts.cursor,
235
+ includePromotedContent: false,
236
+ },
237
+ });
238
+ return parseTimeline(raw);
239
+ },
240
+
241
+ async muted(opts = {}) {
242
+ const raw = await client.graphql("MutedAccounts", {
243
+ variables: {
244
+ count: opts.count || 20,
245
+ cursor: opts.cursor,
246
+ includePromotedContent: false,
247
+ },
248
+ });
249
+ return parseTimeline(raw);
250
+ },
251
+
252
+ async lookup(params = {}) {
253
+ const res = await client.v1_1("users/lookup", { params });
254
+ return await res.json();
255
+ },
256
+
257
+ async updateProfile(params = {}) {
258
+ const res = await client.v1_1("account/update_profile", {
259
+ body: JSON.stringify(params),
260
+ });
261
+ const json = await res.json();
262
+ return json ? parseUser(json) : res;
263
+ },
264
+
265
+ async updateProfileImage(imageData) {
266
+ const res = await client.v1_1("account/update_profile_image", {
267
+ body: JSON.stringify({ image: imageData }),
268
+ });
269
+ return await res.json();
270
+ },
271
+
272
+ async updateProfileBanner(bannerData) {
273
+ const res = await client.v1_1("account/update_profile_banner", {
274
+ body: JSON.stringify({ banner: bannerData }),
275
+ });
276
+ return await res.json();
277
+ },
278
+
279
+ async removeProfileBanner() {
280
+ const res = await client.v1_1("account/remove_profile_banner", {});
281
+ return await res.json();
282
+ },
283
+
284
+ async subscriptions(userId, opts = {}) {
285
+ const raw = await client.graphql("UserCreatorSubscriptions", {
286
+ variables: {
287
+ userId,
288
+ count: opts.count || 20,
289
+ cursor: opts.cursor,
290
+ },
291
+ });
292
+ return parseTimeline(raw);
293
+ },
294
+
295
+ async subscribers(userId, opts = {}) {
296
+ const raw = await client.graphql("UserCreatorSubscribers", {
297
+ variables: {
298
+ userId,
299
+ count: opts.count || 20,
300
+ cursor: opts.cursor,
301
+ },
302
+ });
303
+ return parseTimeline(raw);
304
+ },
305
+
306
+ async superFollowers(opts = {}) {
307
+ const raw = await client.graphql("SuperFollowers", {
308
+ variables: {
309
+ count: opts.count || 20,
310
+ cursor: opts.cursor,
311
+ includePromotedContent: false,
312
+ },
313
+ });
314
+ return parseTimeline(raw);
315
+ },
316
+
317
+ async recommendations(params = {}) {
318
+ const res = await client.v1_1("users/recommendations", { params });
319
+ return await res.json();
320
+ },
321
+ });