@glagan/rettiwt-api 7.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 (165) hide show
  1. package/.eslintrc.js +166 -0
  2. package/.gitattributes +3 -0
  3. package/.github/FUNDING.yml +4 -0
  4. package/.github/ISSUE_TEMPLATE/bug-report.yml +57 -0
  5. package/.github/ISSUE_TEMPLATE/feature-request.yml +20 -0
  6. package/.github/ISSUE_TEMPLATE/question.yml +15 -0
  7. package/.github/PULL_REQUEST_TEMPLATE.md +32 -0
  8. package/.github/workflows/ci.yml +32 -0
  9. package/.github/workflows/publish.yml +23 -0
  10. package/.nvmrc +1 -0
  11. package/.prettierignore +3 -0
  12. package/.prettierrc +13 -0
  13. package/LICENSE +21 -0
  14. package/README.md +566 -0
  15. package/dist/cli.js +43 -0
  16. package/eslint.config.mjs +17 -0
  17. package/package.json +50 -0
  18. package/src/Rettiwt.ts +97 -0
  19. package/src/cli.ts +48 -0
  20. package/src/collections/Extractors.ts +155 -0
  21. package/src/collections/Groups.ts +81 -0
  22. package/src/collections/Requests.ts +89 -0
  23. package/src/collections/Tweet.ts +17 -0
  24. package/src/commands/DirectMessage.ts +62 -0
  25. package/src/commands/List.ts +90 -0
  26. package/src/commands/Tweet.ts +437 -0
  27. package/src/commands/User.ts +367 -0
  28. package/src/enums/Api.ts +10 -0
  29. package/src/enums/Authentication.ts +10 -0
  30. package/src/enums/Data.ts +13 -0
  31. package/src/enums/Logging.ts +14 -0
  32. package/src/enums/Media.ts +10 -0
  33. package/src/enums/Notification.ts +12 -0
  34. package/src/enums/Resource.ts +69 -0
  35. package/src/enums/Tweet.ts +8 -0
  36. package/src/enums/raw/Analytics.ts +32 -0
  37. package/src/enums/raw/Media.ts +10 -0
  38. package/src/enums/raw/Notification.ts +11 -0
  39. package/src/enums/raw/Tweet.ts +20 -0
  40. package/src/helper/CliUtils.ts +17 -0
  41. package/src/helper/JsonUtils.ts +70 -0
  42. package/src/index.ts +128 -0
  43. package/src/models/RettiwtConfig.ts +101 -0
  44. package/src/models/args/FetchArgs.ts +169 -0
  45. package/src/models/args/PostArgs.ts +93 -0
  46. package/src/models/args/ProfileArgs.ts +68 -0
  47. package/src/models/auth/AuthCookie.ts +58 -0
  48. package/src/models/auth/AuthCredential.ts +83 -0
  49. package/src/models/data/Analytics.ts +97 -0
  50. package/src/models/data/BookmarkFolder.ts +73 -0
  51. package/src/models/data/Conversation.ts +344 -0
  52. package/src/models/data/CursoredData.ts +64 -0
  53. package/src/models/data/DirectMessage.ts +335 -0
  54. package/src/models/data/Inbox.ts +124 -0
  55. package/src/models/data/List.ts +113 -0
  56. package/src/models/data/Notification.ts +84 -0
  57. package/src/models/data/Tweet.ts +388 -0
  58. package/src/models/data/User.ts +187 -0
  59. package/src/models/errors/TwitterError.ts +65 -0
  60. package/src/models/params/Variables.ts +62 -0
  61. package/src/requests/DirectMessage.ts +229 -0
  62. package/src/requests/List.ts +203 -0
  63. package/src/requests/Media.ts +67 -0
  64. package/src/requests/Tweet.ts +607 -0
  65. package/src/requests/User.ts +1191 -0
  66. package/src/services/internal/AuthService.ts +115 -0
  67. package/src/services/internal/ErrorService.ts +41 -0
  68. package/src/services/internal/LogService.ts +34 -0
  69. package/src/services/public/DirectMessageService.ts +159 -0
  70. package/src/services/public/FetcherService.ts +366 -0
  71. package/src/services/public/ListService.ts +241 -0
  72. package/src/services/public/TweetService.ts +886 -0
  73. package/src/services/public/UserService.ts +1154 -0
  74. package/src/types/ErrorHandler.ts +13 -0
  75. package/src/types/Fetch.ts +3 -0
  76. package/src/types/RettiwtConfig.ts +48 -0
  77. package/src/types/args/FetchArgs.ts +233 -0
  78. package/src/types/args/PostArgs.ts +142 -0
  79. package/src/types/args/ProfileArgs.ts +33 -0
  80. package/src/types/auth/AuthCookie.ts +22 -0
  81. package/src/types/auth/AuthCredential.ts +28 -0
  82. package/src/types/auth/TransactionHeader.ts +8 -0
  83. package/src/types/data/Analytics.ts +58 -0
  84. package/src/types/data/BookmarkFolder.ts +12 -0
  85. package/src/types/data/Conversation.ts +44 -0
  86. package/src/types/data/CursoredData.ts +24 -0
  87. package/src/types/data/DirectMessage.ts +33 -0
  88. package/src/types/data/Inbox.ts +23 -0
  89. package/src/types/data/List.ts +33 -0
  90. package/src/types/data/Notification.ts +26 -0
  91. package/src/types/data/Tweet.ts +99 -0
  92. package/src/types/data/User.ts +54 -0
  93. package/src/types/errors/TwitterError.ts +37 -0
  94. package/src/types/params/Variables.ts +41 -0
  95. package/src/types/raw/base/Analytic.ts +32 -0
  96. package/src/types/raw/base/BookmarkFolder.ts +12 -0
  97. package/src/types/raw/base/Cursor.ts +13 -0
  98. package/src/types/raw/base/Error.ts +38 -0
  99. package/src/types/raw/base/LimitedVisibilityTweet.ts +40 -0
  100. package/src/types/raw/base/List.ts +50 -0
  101. package/src/types/raw/base/Media.ts +53 -0
  102. package/src/types/raw/base/Message.ts +22 -0
  103. package/src/types/raw/base/Notification.ts +66 -0
  104. package/src/types/raw/base/Space.ts +35 -0
  105. package/src/types/raw/base/Tweet.ts +139 -0
  106. package/src/types/raw/base/User.ts +182 -0
  107. package/src/types/raw/composite/DataResult.ts +8 -0
  108. package/src/types/raw/composite/TimelineList.ts +10 -0
  109. package/src/types/raw/composite/TimelineTweet.ts +14 -0
  110. package/src/types/raw/composite/TimelineUser.ts +13 -0
  111. package/src/types/raw/dm/Conversation.ts +59 -0
  112. package/src/types/raw/dm/InboxInitial.ts +155 -0
  113. package/src/types/raw/dm/InboxTimeline.ts +301 -0
  114. package/src/types/raw/dm/UserUpdates.ts +46 -0
  115. package/src/types/raw/generic/Response.ts +10 -0
  116. package/src/types/raw/list/AddMember.ts +175 -0
  117. package/src/types/raw/list/Details.ts +176 -0
  118. package/src/types/raw/list/Members.ts +154 -0
  119. package/src/types/raw/list/RemoveMember.ts +174 -0
  120. package/src/types/raw/list/Tweets.ts +2296 -0
  121. package/src/types/raw/media/FinalizeUpload.ts +20 -0
  122. package/src/types/raw/media/InitalizeUpload.ts +12 -0
  123. package/src/types/raw/media/LiveVideoStream.ts +21 -0
  124. package/src/types/raw/space/Details.ts +359 -0
  125. package/src/types/raw/tweet/Bookmark.ts +14 -0
  126. package/src/types/raw/tweet/Details.ts +210 -0
  127. package/src/types/raw/tweet/DetailsBulk.ts +338 -0
  128. package/src/types/raw/tweet/Like.ts +14 -0
  129. package/src/types/raw/tweet/Likers.ts +200 -0
  130. package/src/types/raw/tweet/Post.ts +150 -0
  131. package/src/types/raw/tweet/Replies.ts +539 -0
  132. package/src/types/raw/tweet/Retweet.ts +31 -0
  133. package/src/types/raw/tweet/Retweeters.ts +208 -0
  134. package/src/types/raw/tweet/Schedule.ts +18 -0
  135. package/src/types/raw/tweet/Search.ts +597 -0
  136. package/src/types/raw/tweet/Unbookmark.ts +14 -0
  137. package/src/types/raw/tweet/Unlike.ts +14 -0
  138. package/src/types/raw/tweet/Unpost.ts +20 -0
  139. package/src/types/raw/tweet/Unretweet.ts +31 -0
  140. package/src/types/raw/tweet/Unschedule.ts +14 -0
  141. package/src/types/raw/user/Affiliates.ts +179 -0
  142. package/src/types/raw/user/Analytics.ts +23 -0
  143. package/src/types/raw/user/BookmarkFolderTweets.ts +53 -0
  144. package/src/types/raw/user/BookmarkFolders.ts +41 -0
  145. package/src/types/raw/user/Bookmarks.ts +637 -0
  146. package/src/types/raw/user/Details.ts +185 -0
  147. package/src/types/raw/user/DetailsBulk.ts +104 -0
  148. package/src/types/raw/user/Follow.ts +280 -0
  149. package/src/types/raw/user/Followed.ts +1942 -0
  150. package/src/types/raw/user/Followers.ts +215 -0
  151. package/src/types/raw/user/Following.ts +215 -0
  152. package/src/types/raw/user/Highlights.ts +1287 -0
  153. package/src/types/raw/user/Likes.ts +1254 -0
  154. package/src/types/raw/user/Lists.ts +378 -0
  155. package/src/types/raw/user/Media.ts +1738 -0
  156. package/src/types/raw/user/Notifications.ts +499 -0
  157. package/src/types/raw/user/ProfileUpdate.ts +80 -0
  158. package/src/types/raw/user/Recommended.ts +2319 -0
  159. package/src/types/raw/user/Scheduled.ts +37 -0
  160. package/src/types/raw/user/Search.ts +230 -0
  161. package/src/types/raw/user/Subscriptions.ts +176 -0
  162. package/src/types/raw/user/Tweets.ts +1254 -0
  163. package/src/types/raw/user/TweetsAndReplies.ts +1254 -0
  164. package/src/types/raw/user/Unfollow.ts +280 -0
  165. package/tsconfig.json +97 -0
@@ -0,0 +1,886 @@
1
+ import { statSync } from 'fs';
2
+
3
+ import { Extractors } from '../../collections/Extractors';
4
+ import { ResourceType } from '../../enums/Resource';
5
+ import { TweetRepliesSortType } from '../../enums/Tweet';
6
+ import { CursoredData } from '../../models/data/CursoredData';
7
+ import { Tweet } from '../../models/data/Tweet';
8
+ import { User } from '../../models/data/User';
9
+
10
+ import { RettiwtConfig } from '../../models/RettiwtConfig';
11
+ import { ITweetFilter } from '../../types/args/FetchArgs';
12
+ import { INewTweet } from '../../types/args/PostArgs';
13
+ import { IMediaInitializeUploadResponse } from '../../types/raw/media/InitalizeUpload';
14
+
15
+ import { ITweetBookmarkResponse } from '../../types/raw/tweet/Bookmark';
16
+ import { ITweetDetailsResponse } from '../../types/raw/tweet/Details';
17
+ import { ITweetDetailsBulkResponse } from '../../types/raw/tweet/DetailsBulk';
18
+ import { ITweetLikeResponse } from '../../types/raw/tweet/Like';
19
+ import { ITweetLikersResponse } from '../../types/raw/tweet/Likers';
20
+ import { ITweetPostResponse } from '../../types/raw/tweet/Post';
21
+ import { ITweetRepliesResponse } from '../../types/raw/tweet/Replies';
22
+ import { ITweetRetweetResponse } from '../../types/raw/tweet/Retweet';
23
+ import { ITweetRetweetersResponse } from '../../types/raw/tweet/Retweeters';
24
+ import { ITweetScheduleResponse } from '../../types/raw/tweet/Schedule';
25
+ import { ITweetSearchResponse } from '../../types/raw/tweet/Search';
26
+ import { ITweetUnbookmarkResponse } from '../../types/raw/tweet/Unbookmark';
27
+ import { ITweetUnlikeResponse } from '../../types/raw/tweet/Unlike';
28
+ import { ITweetUnpostResponse } from '../../types/raw/tweet/Unpost';
29
+ import { ITweetUnretweetResponse } from '../../types/raw/tweet/Unretweet';
30
+ import { ITweetUnscheduleResponse } from '../../types/raw/tweet/Unschedule';
31
+
32
+ import { FetcherService } from './FetcherService';
33
+
34
+ /**
35
+ * Handles interacting with resources related to tweets.
36
+ *
37
+ * @public
38
+ */
39
+ export class TweetService extends FetcherService {
40
+ /**
41
+ * @param config - The config object for configuring the Rettiwt instance.
42
+ *
43
+ * @internal
44
+ */
45
+ public constructor(config: RettiwtConfig) {
46
+ super(config);
47
+ }
48
+
49
+ /**
50
+ * Bookmark a tweet.
51
+ *
52
+ * @param id - The ID of the tweet to be bookmarked.
53
+ *
54
+ * @returns Whether bookmarking was successful or not.
55
+ *
56
+ * @example
57
+ *
58
+ * ```ts
59
+ * import { Rettiwt } from 'rettiwt-api';
60
+ *
61
+ * // Creating a new Rettiwt instance using the given 'API_KEY'
62
+ * const rettiwt = new Rettiwt({ apiKey: API_KEY });
63
+ *
64
+ * // Bookmarking the Tweet with id '1234567890'
65
+ * rettiwt.tweet.bookmark('1234567890')
66
+ * .then(res => {
67
+ * console.log(res);
68
+ * })
69
+ * .catch(err => {
70
+ * console.log(err);
71
+ * });
72
+ * ```
73
+ */
74
+ public async bookmark(id: string): Promise<boolean> {
75
+ const resource = ResourceType.TWEET_BOOKMARK;
76
+
77
+ // Favoriting the tweet
78
+ const response = await this.request<ITweetBookmarkResponse>(resource, {
79
+ id: id,
80
+ });
81
+
82
+ // Deserializing response
83
+ const data = Extractors[resource](response) ?? false;
84
+
85
+ return data;
86
+ }
87
+
88
+ /**
89
+ * Get the details of one or more tweets.
90
+ *
91
+ * @param id - The ID/IDs of the target tweet/tweets.
92
+ *
93
+ * @returns
94
+ * The details of the tweet with the given ID.
95
+ *
96
+ * If more than one ID is provided, returns a list.
97
+ *
98
+ * If no tweet/tweets matches the given ID/IDs, returns `undefined`/`[]`.
99
+ *
100
+ * @example
101
+ *
102
+ * #### Fetching the details of a single tweet
103
+ * ```ts
104
+ * import { Rettiwt } from 'rettiwt-api';
105
+ *
106
+ * // Creating a new Rettiwt instance using the given 'API_KEY'
107
+ * const rettiwt = new Rettiwt({ apiKey: API_KEY });
108
+ *
109
+ * // Fetching the details of the tweet with the id '1234567890'
110
+ * rettiwt.tweet.details('1234567890')
111
+ * .then(res => {
112
+ * console.log(res); # 'res' is a single tweet
113
+ * })
114
+ * .catch(err => {
115
+ * console.log(err);
116
+ * });
117
+ * ```
118
+ *
119
+ * @example
120
+ *
121
+ * #### Fetching the details of multiple tweets
122
+ * ```ts
123
+ * import { Rettiwt } from 'rettiwt-api';
124
+ *
125
+ * // Creating a new Rettiwt instance using the given 'API_KEY'
126
+ * const rettiwt = new Rettiwt({ apiKey: API_KEY });
127
+ *
128
+ * // Fetching the details of the tweets with IDs '123', '456', '789'
129
+ * rettiwt.tweet.details(['123', '456', '789'])
130
+ * .then(res => {
131
+ * console.log(res); # 'res' is an array of tweets
132
+ * })
133
+ * .catch(err => {
134
+ * console.log(err);
135
+ * });
136
+ * ```
137
+ */
138
+ public async details<T extends string | string[]>(id: T): Promise<T extends string ? Tweet | undefined : Tweet[]> {
139
+ let resource: ResourceType;
140
+
141
+ // If user is authenticated and details of single tweet required
142
+ if (this.config.userId != undefined && typeof id == 'string') {
143
+ resource = ResourceType.TWEET_DETAILS_ALT;
144
+
145
+ // Fetching raw tweet details
146
+ const response = await this.request<ITweetRepliesResponse>(resource, { id: id });
147
+
148
+ // Deserializing response
149
+ const data = Extractors[resource](response, id);
150
+
151
+ return data as T extends string ? Tweet | undefined : Tweet[];
152
+ }
153
+ // If user is authenticated and details of multiple tweets required
154
+ else if (this.config.userId != undefined && Array.isArray(id)) {
155
+ resource = ResourceType.TWEET_DETAILS_BULK;
156
+
157
+ // Fetching raw tweet details
158
+ const response = await this.request<ITweetDetailsBulkResponse>(resource, { ids: id });
159
+
160
+ // Deserializing response
161
+ const data = Extractors[resource](response, id);
162
+
163
+ return data as T extends string ? Tweet | undefined : Tweet[];
164
+ }
165
+ // If user is not authenticated
166
+ else {
167
+ resource = ResourceType.TWEET_DETAILS;
168
+
169
+ // Fetching raw tweet details
170
+ const response = await this.request<ITweetDetailsResponse>(resource, { id: String(id) });
171
+
172
+ // Deserializing response
173
+ const data = Extractors[resource](response, String(id));
174
+
175
+ return data as T extends string ? Tweet | undefined : Tweet[];
176
+ }
177
+ }
178
+
179
+ /**
180
+ * Like a tweet.
181
+ *
182
+ * @param id - The ID of the tweet to be liked.
183
+ *
184
+ * @returns Whether liking was successful or not.
185
+ *
186
+ * @example
187
+ *
188
+ * ```ts
189
+ * import { Rettiwt } from 'rettiwt-api';
190
+ *
191
+ * // Creating a new Rettiwt instance using the given 'API_KEY'
192
+ * const rettiwt = new Rettiwt({ apiKey: API_KEY });
193
+ *
194
+ * // Liking the Tweet with id '1234567890'
195
+ * rettiwt.tweet.like('1234567890')
196
+ * .then(res => {
197
+ * console.log(res);
198
+ * })
199
+ * .catch(err => {
200
+ * console.log(err);
201
+ * });
202
+ * ```
203
+ */
204
+ public async like(id: string): Promise<boolean> {
205
+ const resource = ResourceType.TWEET_LIKE;
206
+
207
+ // Favoriting the tweet
208
+ const response = await this.request<ITweetLikeResponse>(resource, {
209
+ id: id,
210
+ });
211
+
212
+ // Deserializing response
213
+ const data = Extractors[resource](response) ?? false;
214
+
215
+ return data;
216
+ }
217
+
218
+ /**
219
+ * Get the list of users who liked a tweet. Only works for your own tweets.
220
+ *
221
+ * @param id - The ID of the target tweet.
222
+ * @param count - The number of likers to fetch, must be \<= 100.
223
+ * @param cursor - The cursor to the batch of likers to fetch.
224
+ *
225
+ * @returns The list of users who liked the given tweet.
226
+ *
227
+ * @example
228
+ *
229
+ * ```ts
230
+ * import { Rettiwt } from 'rettiwt-api';
231
+ *
232
+ * // Creating a new Rettiwt instance using the given 'API_KEY'
233
+ * const rettiwt = new Rettiwt({ apiKey: API_KEY });
234
+ *
235
+ * // Fetching the most recent 100 likers of the Tweet with id '1234567890'
236
+ * rettiwt.tweet.likers('1234567890')
237
+ * .then(res => {
238
+ * console.log(res);
239
+ * })
240
+ * .catch(err => {
241
+ * console.log(err);
242
+ * });
243
+ * ```
244
+ */
245
+ public async likers(id: string, count?: number, cursor?: string): Promise<CursoredData<User>> {
246
+ const resource = ResourceType.TWEET_LIKERS;
247
+
248
+ // Fetching raw likers
249
+ const response = await this.request<ITweetLikersResponse>(resource, {
250
+ id: id,
251
+ count: count,
252
+ cursor: cursor,
253
+ });
254
+
255
+ // Deserializing response
256
+ const data = Extractors[resource](response);
257
+
258
+ return data;
259
+ }
260
+
261
+ /**
262
+ * Post a tweet.
263
+ *
264
+ * @param options - The options describing the tweet to be posted. Check {@link TweetArgs} for available options.
265
+ *
266
+ * @returns The ID of the posted tweet.
267
+ *
268
+ * @example
269
+ *
270
+ * #### Posting a simple text
271
+ * ```ts
272
+ * import { Rettiwt } from 'rettiwt-api';
273
+ *
274
+ * // Creating a new Rettiwt instance using the given 'API_KEY'
275
+ * const rettiwt = new Rettiwt({ apiKey: API_KEY });
276
+ *
277
+ * // Posting a tweet to twitter
278
+ * rettiwt.tweet.post({ text: 'Hello World!' })
279
+ * .then(res => {
280
+ * console.log(res);
281
+ * })
282
+ * .catch(err => {
283
+ * console.log(err);
284
+ * });
285
+ * ```
286
+ *
287
+ * @example
288
+ *
289
+ * #### Posting a tweet with an image that has been already uploaded
290
+ * ```ts
291
+ * import { Rettiwt } from 'rettiwt-api';
292
+ *
293
+ * // Creating a new Rettiwt instance using the given 'API_KEY'
294
+ * const rettiwt = new Rettiwt({ apiKey: API_KEY });
295
+ *
296
+ * // Posting a tweet, containing an image with ID '1234567890', to twitter
297
+ * rettiwt.tweet.post({ text: 'What a nice view!', media: [{ id: '1234567890' }] })
298
+ * .then(res => {
299
+ * console.log(res);
300
+ * })
301
+ * .catch(err => {
302
+ * console.log(err);
303
+ * });
304
+ * ```
305
+ *
306
+ * @example
307
+ *
308
+ * #### Posting a reply to a tweet
309
+ * ```ts
310
+ * import { Rettiwt } from 'rettiwt-api';
311
+ *
312
+ * // Creating a new Rettiwt instance using the given 'API_KEY'
313
+ * const rettiwt = new Rettiwt({ apiKey: API_KEY });
314
+ *
315
+ * // Posting a simple text reply, to a tweet with id "1234567890"
316
+ * rettiwt.tweet.post({ text: 'Hello!', replyTo: "1234567890" })
317
+ * .then(res => {
318
+ * console.log(res);
319
+ * })
320
+ * .catch(err => {
321
+ * console.log(err);
322
+ * });
323
+ * ```
324
+ *
325
+ * @example
326
+ *
327
+ * #### Posting a tweet that quotes another tweet
328
+ * ```ts
329
+ * import { Rettiwt } from 'rettiwt-api';
330
+ *
331
+ * // Creating a new Rettiwt instance using the given 'API_KEY'
332
+ * const rettiwt = new Rettiwt({ apiKey: API_KEY });
333
+ *
334
+ * // Posting a simple text tweet, quoting a tweet with id "1234567890"
335
+ * rettiwt.tweet.post({ text: 'Hello!', quote: "1234567890" })
336
+ * .then(res => {
337
+ * console.log(res);
338
+ * })
339
+ * .catch(err => {
340
+ * console.log(err);
341
+ * });
342
+ * ```
343
+ */
344
+ public async post(options: INewTweet): Promise<string | undefined> {
345
+ const resource = ResourceType.TWEET_POST;
346
+
347
+ // Posting the tweet
348
+ const response = await this.request<ITweetPostResponse>(resource, { tweet: options });
349
+
350
+ // Deserializing response
351
+ const data = Extractors[resource](response);
352
+
353
+ return data;
354
+ }
355
+
356
+ /**
357
+ * Get the list of replies to a tweet.
358
+ *
359
+ * If the target tweet is a thread,
360
+ * the first batch always contains all the tweets in the thread,
361
+ * if `sortBy` is set to {@link TweetRepliesSortType.RELEVANCE}.
362
+ *
363
+ * @param id - The ID of the target tweet.
364
+ * @param cursor - The cursor to the batch of replies to fetch.
365
+ * @param sortBy - The sorting order of the replies to fetch. Default is {@link TweetRepliesSortType.RECENT}.
366
+ *
367
+ * @returns The list of replies to the given tweet.
368
+ *
369
+ * @example
370
+ *
371
+ * ```ts
372
+ * import { Rettiwt } from 'rettiwt-api';
373
+ *
374
+ * // Creating a new Rettiwt instance using the given 'API_KEY'
375
+ * const rettiwt = new Rettiwt({ apiKey: API_KEY });
376
+ *
377
+ * // Fetching the first 100 replies to the Tweet with id '1234567890'
378
+ * rettiwt.tweet.replies('1234567890')
379
+ * .then(res => {
380
+ * console.log(res);
381
+ * })
382
+ * .catch(err => {
383
+ * console.log(err);
384
+ * });
385
+ * ```
386
+ *
387
+ * @remarks
388
+ *
389
+ * If the given tweet is the start of/part of a thread, the first batch always contains all the tweets in the thread.
390
+ */
391
+ public async replies(
392
+ id: string,
393
+ cursor?: string,
394
+ sortBy: TweetRepliesSortType = TweetRepliesSortType.LATEST,
395
+ ): Promise<CursoredData<Tweet>> {
396
+ const resource = ResourceType.TWEET_REPLIES;
397
+
398
+ // Fetching raw list of replies
399
+ const response = await this.request<ITweetDetailsResponse>(resource, {
400
+ id: id,
401
+ cursor: cursor,
402
+ sortBy: sortBy,
403
+ });
404
+
405
+ // Deserializing response
406
+ const data = Extractors[resource](response);
407
+
408
+ return data;
409
+ }
410
+
411
+ /**
412
+ * Retweet a tweet.
413
+ *
414
+ * @param id - The ID of the target tweet.
415
+ *
416
+ * @returns Whether retweeting was successful or not.
417
+ *
418
+ * @example
419
+ *
420
+ * ```ts
421
+ * import { Rettiwt } from 'rettiwt-api';
422
+ *
423
+ * // Creating a new Rettiwt instance using the given 'API_KEY'
424
+ * const rettiwt = new Rettiwt({ apiKey: API_KEY });
425
+ *
426
+ * // Retweeting the Tweet with id '1234567890'
427
+ * rettiwt.tweet.retweet('1234567890')
428
+ * .then(res => {
429
+ * console.log(res);
430
+ * })
431
+ * .catch(err => {
432
+ * console.log(err);
433
+ * });
434
+ * ```
435
+ */
436
+ public async retweet(id: string): Promise<boolean> {
437
+ const resource = ResourceType.TWEET_RETWEET;
438
+
439
+ // Retweeting the tweet
440
+ const response = await this.request<ITweetRetweetResponse>(resource, { id: id });
441
+
442
+ // Deserializing response
443
+ const data = Extractors[resource](response) ?? false;
444
+
445
+ return data;
446
+ }
447
+
448
+ /**
449
+ * Get the list of users who retweeted a tweet.
450
+ *
451
+ * @param id - The ID of the target tweet.
452
+ * @param count - The number of retweeters to fetch, must be \<= 100.
453
+ * @param cursor - The cursor to the batch of retweeters to fetch.
454
+ *
455
+ * @returns The list of users who retweeted the given tweet.
456
+ *
457
+ * @example
458
+ *
459
+ * ```ts
460
+ * import { Rettiwt } from 'rettiwt-api';
461
+ *
462
+ * // Creating a new Rettiwt instance using the given 'API_KEY'
463
+ * const rettiwt = new Rettiwt({ apiKey: API_KEY });
464
+ *
465
+ * // Fetching the most recent 100 retweeters of the Tweet with id '1234567890'
466
+ * rettiwt.tweet.retweeters('1234567890')
467
+ * .then(res => {
468
+ * console.log(res);
469
+ * })
470
+ * .catch(err => {
471
+ * console.log(err);
472
+ * });
473
+ * ```
474
+ */
475
+ public async retweeters(id: string, count?: number, cursor?: string): Promise<CursoredData<User>> {
476
+ const resource = ResourceType.TWEET_RETWEETERS;
477
+
478
+ // Fetching raw list of retweeters
479
+ const response = await this.request<ITweetRetweetersResponse>(resource, {
480
+ id: id,
481
+ count: count,
482
+ cursor: cursor,
483
+ });
484
+
485
+ // Deserializing response
486
+ const data = Extractors[resource](response);
487
+
488
+ return data;
489
+ }
490
+
491
+ /**
492
+ * Schedule a tweet.
493
+ *
494
+ * @param options - The options describing the tweet to be posted. Check {@link TweetArgs} for available options.
495
+ *
496
+ * @returns The ID of the schedule.
497
+ *
498
+ * @example
499
+ *
500
+ * #### Scheduling a simple text
501
+ * ```ts
502
+ * import { Rettiwt } from 'rettiwt-api';
503
+ *
504
+ * // Creating a new Rettiwt instance using the given 'API_KEY'
505
+ * const rettiwt = new Rettiwt({ apiKey: API_KEY });
506
+ *
507
+ * // Scheduling a tweet to posted at 19th of August, 2024, at 11:59:00 AM, in local time
508
+ * rettiwt.tweet.schedule({ text: 'Hello World!', scheduleFor: new Date('2024-08-19 23:59:00') })
509
+ * .then(res => {
510
+ * console.log(res);
511
+ * })
512
+ * .catch(err => {
513
+ * console.log(err);
514
+ * });
515
+ * ```
516
+ *
517
+ * @remarks
518
+ *
519
+ * Scheduling a tweet is similar to {@link post}ing, except that an extra parameter called `scheduleFor` is used.
520
+ */
521
+ public async schedule(options: INewTweet): Promise<string | undefined> {
522
+ const resource = ResourceType.TWEET_SCHEDULE;
523
+
524
+ // Scheduling the tweet
525
+ const response = await this.request<ITweetScheduleResponse>(resource, { tweet: options });
526
+
527
+ // Deserializing response
528
+ const data = Extractors[resource](response);
529
+
530
+ return data;
531
+ }
532
+
533
+ /**
534
+ * Search for tweets using a filter.
535
+ *
536
+ * @param filter - The filter to be used for searching the tweets.
537
+ * @param count - The number of tweets to fetch, must be \<= 20.
538
+ * @param cursor - The cursor to the batch of tweets to fetch.
539
+ * @param results - The type of search results to fetch. Default is {@link ESearchResultType.LATEST}.
540
+ *
541
+ * @returns The list of tweets that match the given filter.
542
+ *
543
+ * @example
544
+ *
545
+ * ```ts
546
+ * import { Rettiwt } from 'rettiwt-api';
547
+ *
548
+ * // Creating a new Rettiwt instance using the given 'API_KEY'
549
+ * const rettiwt = new Rettiwt({ apiKey: API_KEY });
550
+ *
551
+ * // Fetching the most recent 5 tweets from user 'user1'
552
+ * rettiwt.tweet.search({ fromUsers: ['user1'] }, 5)
553
+ * .then(res => {
554
+ * console.log(res);
555
+ * })
556
+ * .catch(err => {
557
+ * console.log(err);
558
+ * });
559
+ * ```
560
+ *
561
+ * @remarks
562
+ *
563
+ * For details about available filters, refer to {@link TweetFilter}
564
+ */
565
+ public async search(filter: ITweetFilter, count?: number, cursor?: string): Promise<CursoredData<Tweet>> {
566
+ const resource = ResourceType.TWEET_SEARCH;
567
+
568
+ // Fetching raw list of filtered tweets
569
+ const response = await this.request<ITweetSearchResponse>(resource, {
570
+ filter: filter,
571
+ count: count,
572
+ cursor: cursor,
573
+ });
574
+
575
+ // Deserializing response
576
+ const data = Extractors[resource](response);
577
+
578
+ // Sorting the tweets by date, from recent to oldest
579
+ data.list.sort((a, b) => new Date(b.createdAt).valueOf() - new Date(a.createdAt).valueOf());
580
+
581
+ return data;
582
+ }
583
+
584
+ /**
585
+ * Stream tweets in pseudo real-time using a filter.
586
+ *
587
+ * @param filter - The filter to be used for searching the tweets.
588
+ * @param pollingInterval - The interval in milliseconds to poll for new tweets. Default interval is 60000 ms.
589
+ *
590
+ * @returns An async generator that yields matching tweets as they are found.
591
+ *
592
+ * @example
593
+ *
594
+ * ```ts
595
+ * import { Rettiwt } from 'rettiwt-api';
596
+ *
597
+ * // Creating a new Rettiwt instance using the given 'API_KEY'
598
+ * const rettiwt = new Rettiwt({ apiKey: API_KEY });
599
+ *
600
+ * // Creating a function that streams all new tweets from the user 'user1'
601
+ * async function streamTweets() {
602
+ * try {
603
+ * // Awaiting for the tweets returned by the AsyncGenerator returned by the method
604
+ * for await (const tweet of rettiwt.tweet.stream({ fromUsers: ['user1'] }, 5000)) {
605
+ * console.log(tweet.fullText);
606
+ * }
607
+ * }
608
+ * catch (err) {
609
+ * console.log(err);
610
+ * }
611
+ * }
612
+ *
613
+ * // Calling the function
614
+ * streamTweets();
615
+ * ```
616
+ */
617
+ public async *stream(filter: ITweetFilter, pollingInterval = 60000): AsyncGenerator<Tweet> {
618
+ const startDate = new Date();
619
+
620
+ let cursor: string | undefined = undefined;
621
+ let sinceId: string | undefined = undefined;
622
+ let nextSinceId: string | undefined = undefined;
623
+
624
+ while (true) {
625
+ // Pause execution for the specified polling interval before proceeding to the next iteration
626
+ await new Promise((resolve) => setTimeout(resolve, pollingInterval));
627
+
628
+ // Search for tweets
629
+ const tweets = await this.search({ ...filter, startDate: startDate, sinceId: sinceId }, undefined, cursor);
630
+
631
+ // Yield the matching tweets
632
+ for (const tweet of tweets.list) {
633
+ yield tweet;
634
+ }
635
+
636
+ // Store the most recent tweet ID from this batch
637
+ if (tweets.list.length > 0 && cursor === undefined) {
638
+ nextSinceId = tweets.list[0].id;
639
+ }
640
+
641
+ // If there are more tweets to fetch, adjust the cursor value
642
+ if (tweets.list.length > 0 && tweets.next) {
643
+ cursor = tweets.next;
644
+ }
645
+ // Else, start the next iteration from this batch's most recent tweet
646
+ else {
647
+ sinceId = nextSinceId;
648
+ cursor = undefined;
649
+ }
650
+ }
651
+ }
652
+
653
+ /**
654
+ * Unbookmark a tweet.
655
+ *
656
+ * @param id - The ID of the target tweet.
657
+ *
658
+ * @returns Whether unbookmarking was successful or not.
659
+ *
660
+ * @example
661
+ *
662
+ * ```ts
663
+ * import { Rettiwt } from 'rettiwt-api';
664
+ *
665
+ * // Creating a new Rettiwt instance using the given 'API_KEY'
666
+ * const rettiwt = new Rettiwt({ apiKey: API_KEY });
667
+ *
668
+ * // Unbookmarking the tweet with id '1234567890'
669
+ * rettiwt.tweet.unbookmark('1234567890')
670
+ * .then(res => {
671
+ * console.log(res);
672
+ * })
673
+ * .catch(err => {
674
+ * console.log(err);
675
+ * });
676
+ * ```
677
+ */
678
+ public async unbookmark(id: string): Promise<boolean> {
679
+ const resource = ResourceType.TWEET_UNBOOKMARK;
680
+
681
+ // Unliking the tweet
682
+ const response = await this.request<ITweetUnbookmarkResponse>(resource, { id: id });
683
+
684
+ // Deserializing the response
685
+ const data = Extractors[resource](response) ?? false;
686
+
687
+ return data;
688
+ }
689
+
690
+ /**
691
+ * Unlike a tweet.
692
+ *
693
+ * @param id - The ID of the target tweet.
694
+ *
695
+ * @returns Whether unliking was successful or not.
696
+ *
697
+ * @example
698
+ *
699
+ * ```ts
700
+ * import { Rettiwt } from 'rettiwt-api';
701
+ *
702
+ * // Creating a new Rettiwt instance using the given 'API_KEY'
703
+ * const rettiwt = new Rettiwt({ apiKey: API_KEY });
704
+ *
705
+ * // Unliking the Tweet with id '1234567890'
706
+ * rettiwt.tweet.unlike('1234567890')
707
+ * .then(res => {
708
+ * console.log(res);
709
+ * })
710
+ * .catch(err => {
711
+ * console.log(err);
712
+ * });
713
+ * ```
714
+ */
715
+ public async unlike(id: string): Promise<boolean> {
716
+ const resource = ResourceType.TWEET_UNLIKE;
717
+
718
+ // Unliking the tweet
719
+ const response = await this.request<ITweetUnlikeResponse>(resource, { id: id });
720
+
721
+ // Deserializing the response
722
+ const data = Extractors[resource](response) ?? false;
723
+
724
+ return data;
725
+ }
726
+
727
+ /**
728
+ * Unpost a tweet.
729
+ *
730
+ * @param id - The ID of the target tweet.
731
+ *
732
+ * @returns Whether unposting was successful or not.
733
+ *
734
+ * @example
735
+ *
736
+ * ```ts
737
+ * import { Rettiwt } from 'rettiwt-api';
738
+ *
739
+ * // Creating a new Rettiwt instance using the given 'API_KEY'
740
+ * const rettiwt = new Rettiwt({ apiKey: API_KEY });
741
+ *
742
+ * // Unposting the Tweet with id '1234567890'
743
+ * rettiwt.tweet.unpost('1234567890')
744
+ * .then(res => {
745
+ * console.log(res);
746
+ * })
747
+ * .catch(err => {
748
+ * console.log(err);
749
+ * });
750
+ * ```
751
+ */
752
+ public async unpost(id: string): Promise<boolean> {
753
+ const resource = ResourceType.TWEET_UNPOST;
754
+
755
+ // Unposting the tweet
756
+ const response = await this.request<ITweetUnpostResponse>(resource, { id: id });
757
+
758
+ // Deserializing the response
759
+ const data = Extractors[resource](response) ?? false;
760
+
761
+ return data;
762
+ }
763
+
764
+ /**
765
+ * Unretweet a tweet.
766
+ *
767
+ * @param id - The ID of the target tweet.
768
+ *
769
+ * @returns Whether unretweeting was successful or not.
770
+ *
771
+ * @example
772
+ *
773
+ * ```ts
774
+ * import { Rettiwt } from 'rettiwt-api';
775
+ *
776
+ * // Creating a new Rettiwt instance using the given 'API_KEY'
777
+ * const rettiwt = new Rettiwt({ apiKey: API_KEY });
778
+ *
779
+ * // Unretweeting the Tweet with id '1234567890'
780
+ * rettiwt.tweet.unretweet('1234567890')
781
+ * .then(res => {
782
+ * console.log(res);
783
+ * })
784
+ * .catch(err => {
785
+ * console.log(err);
786
+ * });
787
+ * ```
788
+ */
789
+ public async unretweet(id: string): Promise<boolean> {
790
+ const resource = ResourceType.TWEET_UNRETWEET;
791
+
792
+ // Unretweeting the tweet
793
+ const response = await this.request<ITweetUnretweetResponse>(resource, { id: id });
794
+
795
+ // Deserializing the response
796
+ const data = Extractors[resource](response) ?? false;
797
+
798
+ return data;
799
+ }
800
+
801
+ /**
802
+ * Unschedule a tweet.
803
+ *
804
+ * @param id - The ID of the scheduled tweet.
805
+ *
806
+ * @returns Whether unscheduling was successful or not.
807
+ *
808
+ * @example
809
+ *
810
+ * ```ts
811
+ * import { Rettiwt } from 'rettiwt-api';
812
+ *
813
+ * // Creating a new Rettiwt instance using the given 'API_KEY'
814
+ * const rettiwt = new Rettiwt({ apiKey: API_KEY });
815
+ *
816
+ * // Unscheduling the Tweet with id '1234567890'
817
+ * rettiwt.tweet.unschedule('1234567890')
818
+ * .then(res => {
819
+ * console.log(res);
820
+ * })
821
+ * .catch(err => {
822
+ * console.log(err);
823
+ * });
824
+ * ```
825
+ */
826
+ public async unschedule(id: string): Promise<boolean> {
827
+ const resource = ResourceType.TWEET_UNSCHEDULE;
828
+
829
+ // Unscheduling the tweet
830
+ const response = await this.request<ITweetUnscheduleResponse>(resource, { id: id });
831
+
832
+ // Deserializing the response
833
+ const data = Extractors[resource](response) ?? false;
834
+
835
+ return data;
836
+ }
837
+
838
+ /**
839
+ * Upload a media file to Twitter.
840
+ *
841
+ * @param media - The path or ArrayBuffer to the media file to upload.
842
+ *
843
+ * @returns The ID of the uploaded media.
844
+ *
845
+ * @example
846
+ *
847
+ * ```ts
848
+ * import { Rettiwt } from 'rettiwt-api';
849
+ *
850
+ * // Creating a new Rettiwt instance using the given 'API_KEY'
851
+ * const rettiwt = new Rettiwt({ apiKey: API_KEY });
852
+ *
853
+ * // Uploading a file called mountains.jpg
854
+ * rettiwt.tweet.upload('mountains.jpg')
855
+ * .then(res => {
856
+ * console.log(res);
857
+ * })
858
+ * .catch(err => {
859
+ * console.log(err);
860
+ * });
861
+ * ```
862
+ *
863
+ * @remarks
864
+ *
865
+ * - The uploaded media exists for 24 hrs within which it can be included in a tweet to be posted.
866
+ * If not posted in a tweet within this period, the uploaded media is removed.
867
+ * - Instead of a path to the media, an ArrayBuffer containing the media can also be uploaded.
868
+ */
869
+ public async upload(media: string | ArrayBuffer): Promise<string> {
870
+ // INITIALIZE
871
+ const size = typeof media == 'string' ? statSync(media).size : media.byteLength;
872
+ const id: string = (
873
+ await this.request<IMediaInitializeUploadResponse>(ResourceType.MEDIA_UPLOAD_INITIALIZE, {
874
+ upload: { size: size },
875
+ })
876
+ ).media_id_string;
877
+
878
+ // APPEND
879
+ await this.request<unknown>(ResourceType.MEDIA_UPLOAD_APPEND, { upload: { id: id, media: media } });
880
+
881
+ // FINALIZE
882
+ await this.request<unknown>(ResourceType.MEDIA_UPLOAD_FINALIZE, { upload: { id: id } });
883
+
884
+ return id;
885
+ }
886
+ }