rettiwt-api 2.7.1 → 3.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 (139) hide show
  1. package/.eslintrc.js +73 -5
  2. package/.tool-versions +1 -0
  3. package/README.md +87 -20
  4. package/dist/Rettiwt.js +0 -1
  5. package/dist/Rettiwt.js.map +1 -1
  6. package/dist/cli.js +2 -4
  7. package/dist/cli.js.map +1 -1
  8. package/dist/collections/Extractors.d.ts +37 -0
  9. package/dist/collections/Extractors.js +67 -0
  10. package/dist/collections/Extractors.js.map +1 -0
  11. package/dist/collections/Groups.d.ts +19 -0
  12. package/dist/collections/Groups.js +55 -0
  13. package/dist/collections/Groups.js.map +1 -0
  14. package/dist/collections/Requests.d.ts +12 -0
  15. package/dist/collections/Requests.js +46 -0
  16. package/dist/collections/Requests.js.map +1 -0
  17. package/dist/commands/Auth.d.ts +6 -0
  18. package/dist/commands/Auth.js +26 -8
  19. package/dist/commands/Auth.js.map +1 -1
  20. package/dist/commands/Tweet.js +237 -82
  21. package/dist/commands/Tweet.js.map +1 -1
  22. package/dist/commands/User.js +197 -36
  23. package/dist/commands/User.js.map +1 -1
  24. package/dist/enums/Api.d.ts +30 -0
  25. package/dist/enums/Api.js +32 -1
  26. package/dist/enums/Api.js.map +1 -1
  27. package/dist/enums/Data.d.ts +9 -0
  28. package/dist/enums/Data.js +14 -0
  29. package/dist/enums/Data.js.map +1 -0
  30. package/dist/enums/Http.d.ts +1 -1
  31. package/dist/enums/Http.js +1 -1
  32. package/dist/enums/Logging.d.ts +6 -5
  33. package/dist/enums/Logging.js +6 -5
  34. package/dist/enums/Logging.js.map +1 -1
  35. package/dist/enums/Resource.d.ts +33 -0
  36. package/dist/enums/Resource.js +42 -0
  37. package/dist/enums/Resource.js.map +1 -0
  38. package/dist/helper/CliUtils.d.ts +1 -1
  39. package/dist/helper/CliUtils.js.map +1 -1
  40. package/dist/index.d.ts +11 -9
  41. package/dist/index.js +11 -14
  42. package/dist/index.js.map +1 -1
  43. package/dist/models/args/FetchArgs.d.ts +129 -0
  44. package/dist/models/args/FetchArgs.js +263 -0
  45. package/dist/models/args/FetchArgs.js.map +1 -0
  46. package/dist/models/args/PostArgs.d.ts +116 -0
  47. package/dist/models/args/PostArgs.js +232 -0
  48. package/dist/models/args/PostArgs.js.map +1 -0
  49. package/dist/models/data/CursoredData.d.ts +11 -11
  50. package/dist/models/data/CursoredData.js +21 -16
  51. package/dist/models/data/CursoredData.js.map +1 -1
  52. package/dist/models/data/List.d.ts +8 -10
  53. package/dist/models/data/List.js +2 -4
  54. package/dist/models/data/List.js.map +1 -1
  55. package/dist/models/data/Tweet.d.ts +44 -29
  56. package/dist/models/data/Tweet.js +74 -15
  57. package/dist/models/data/Tweet.js.map +1 -1
  58. package/dist/models/data/User.d.ts +38 -20
  59. package/dist/models/data/User.js +71 -7
  60. package/dist/models/data/User.js.map +1 -1
  61. package/dist/models/errors/ApiError.d.ts +1 -3
  62. package/dist/models/errors/ApiError.js +1 -4
  63. package/dist/models/errors/ApiError.js.map +1 -1
  64. package/dist/models/errors/DataValidationError.d.ts +30 -0
  65. package/dist/models/errors/DataValidationError.js +34 -0
  66. package/dist/models/errors/DataValidationError.js.map +1 -0
  67. package/dist/models/errors/HttpError.d.ts +1 -3
  68. package/dist/models/errors/HttpError.js +1 -4
  69. package/dist/models/errors/HttpError.js.map +1 -1
  70. package/dist/models/errors/TimeoutError.d.ts +2 -4
  71. package/dist/models/errors/TimeoutError.js +2 -5
  72. package/dist/models/errors/TimeoutError.js.map +1 -1
  73. package/dist/services/internal/ErrorService.d.ts +45 -35
  74. package/dist/services/internal/ErrorService.js +70 -68
  75. package/dist/services/internal/ErrorService.js.map +1 -1
  76. package/dist/services/internal/LogService.d.ts +7 -5
  77. package/dist/services/internal/LogService.js +28 -9
  78. package/dist/services/internal/LogService.js.map +1 -1
  79. package/dist/services/public/AuthService.d.ts +24 -20
  80. package/dist/services/public/AuthService.js +38 -36
  81. package/dist/services/public/AuthService.js.map +1 -1
  82. package/dist/services/public/FetcherService.d.ts +89 -0
  83. package/dist/services/public/FetcherService.js +240 -0
  84. package/dist/services/public/FetcherService.js.map +1 -0
  85. package/dist/services/public/TweetService.d.ts +213 -94
  86. package/dist/services/public/TweetService.js +409 -209
  87. package/dist/services/public/TweetService.js.map +1 -1
  88. package/dist/services/public/UserService.d.ts +185 -52
  89. package/dist/services/public/UserService.js +338 -103
  90. package/dist/services/public/UserService.js.map +1 -1
  91. package/dist/types/ReturnTypes.d.ts +21 -0
  92. package/dist/types/ReturnTypes.js +3 -0
  93. package/dist/types/ReturnTypes.js.map +1 -0
  94. package/package.json +4 -2
  95. package/src/Rettiwt.ts +0 -3
  96. package/src/cli.ts +2 -4
  97. package/src/collections/Extractors.ts +84 -0
  98. package/src/collections/Groups.ts +54 -0
  99. package/src/collections/Requests.ts +52 -0
  100. package/src/commands/Auth.ts +19 -7
  101. package/src/commands/Tweet.ts +179 -91
  102. package/src/commands/User.ts +118 -25
  103. package/src/enums/Api.ts +31 -0
  104. package/src/enums/Data.ts +9 -0
  105. package/src/enums/Http.ts +1 -1
  106. package/src/enums/Logging.ts +6 -5
  107. package/src/enums/Resource.ts +40 -0
  108. package/src/helper/CliUtils.ts +1 -1
  109. package/src/index.ts +41 -14
  110. package/src/models/args/FetchArgs.ts +296 -0
  111. package/src/models/args/PostArgs.ts +263 -0
  112. package/src/models/data/CursoredData.ts +23 -15
  113. package/src/models/data/List.ts +12 -15
  114. package/src/models/data/Tweet.ts +108 -39
  115. package/src/models/data/User.ts +99 -30
  116. package/src/models/errors/ApiError.ts +1 -4
  117. package/src/models/errors/DataValidationError.ts +44 -0
  118. package/src/models/errors/HttpError.ts +1 -4
  119. package/src/models/errors/TimeoutError.ts +2 -5
  120. package/src/services/internal/ErrorService.ts +76 -75
  121. package/src/services/internal/LogService.ts +20 -10
  122. package/src/services/public/AuthService.ts +39 -38
  123. package/src/services/public/FetcherService.ts +209 -0
  124. package/src/services/public/TweetService.ts +384 -179
  125. package/src/services/public/UserService.ts +319 -86
  126. package/src/types/RettiwtConfig.ts +0 -1
  127. package/src/types/ReturnTypes.ts +24 -0
  128. package/dist/models/args/TweetArgs.d.ts +0 -44
  129. package/dist/models/args/TweetArgs.js +0 -82
  130. package/dist/models/args/TweetArgs.js.map +0 -1
  131. package/dist/models/data/Media.d.ts +0 -14
  132. package/dist/models/data/Media.js +0 -19
  133. package/dist/models/data/Media.js.map +0 -1
  134. package/dist/services/internal/FetcherService.d.ts +0 -106
  135. package/dist/services/internal/FetcherService.js +0 -365
  136. package/dist/services/internal/FetcherService.js.map +0 -1
  137. package/src/models/args/TweetArgs.ts +0 -98
  138. package/src/models/data/Media.ts +0 -19
  139. package/src/services/internal/FetcherService.ts +0 -365
@@ -1,20 +1,33 @@
1
- // PACKAGES
2
- import { EResourceType, MediaArgs, TweetFilter } from 'rettiwt-core';
3
-
4
- // SERVICES
5
- import { FetcherService } from '../internal/FetcherService';
6
-
7
- // TYPES
8
- import { IRettiwtConfig } from '../../types/RettiwtConfig';
9
-
10
- // MODELS
1
+ import { statSync } from 'fs';
2
+
3
+ import {
4
+ IInitializeMediaUploadResponse,
5
+ IListTweetsResponse,
6
+ ITweetDetailsResponse,
7
+ ITweetLikeResponse,
8
+ ITweetLikersResponse,
9
+ ITweetPostResponse,
10
+ ITweetRetweetersResponse,
11
+ ITweetRetweetResponse,
12
+ ITweetSearchResponse,
13
+ ITweetUnlikeResponse,
14
+ ITweetUnpostResponse,
15
+ ITweetUnretweetResponse,
16
+ TweetFilter,
17
+ } from 'rettiwt-core';
18
+
19
+ import { extractors } from '../../collections/Extractors';
20
+ import { EResourceType } from '../../enums/Resource';
21
+ import { TweetArgs } from '../../models/args/PostArgs';
22
+ import { CursoredData } from '../../models/data/CursoredData';
11
23
  import { Tweet } from '../../models/data/Tweet';
12
24
  import { User } from '../../models/data/User';
13
- import { CursoredData } from '../../models/data/CursoredData';
14
- import { TweetArgs, TweetMediaArgs } from '../../models/args/TweetArgs';
25
+ import { IRettiwtConfig } from '../../types/RettiwtConfig';
26
+
27
+ import { FetcherService } from './FetcherService';
15
28
 
16
29
  /**
17
- * Handles fetching of data related to tweets.
30
+ * Handles interacting with resources related to tweets.
18
31
  *
19
32
  * @public
20
33
  */
@@ -32,7 +45,10 @@ export class TweetService extends FetcherService {
32
45
  * Get the details of a tweet.
33
46
  *
34
47
  * @param id - The id of the target tweet.
35
- * @returns The details of a single tweet with the given tweet id.
48
+ *
49
+ * @returns
50
+ * The details of the tweet with the given id.
51
+ * If no tweet matches the given id, returns `undefined`.
36
52
  *
37
53
  * @example
38
54
  * ```
@@ -41,8 +57,8 @@ export class TweetService extends FetcherService {
41
57
  * // Creating a new Rettiwt instance using the given 'API_KEY'
42
58
  * const rettiwt = new Rettiwt({ apiKey: API_KEY });
43
59
  *
44
- * // Fetching the details of the tweet with the id '12345678'
45
- * rettiwt.tweet.details('12345678')
60
+ * // Fetching the details of the tweet with the id '1234567890'
61
+ * rettiwt.tweet.details('1234567890')
46
62
  * .then(res => {
47
63
  * console.log(res);
48
64
  * })
@@ -50,23 +66,25 @@ export class TweetService extends FetcherService {
50
66
  * console.log(err);
51
67
  * });
52
68
  * ```
53
- *
54
- * @public
55
69
  */
56
- public async details(id: string): Promise<Tweet> {
57
- // Fetching the requested data
58
- const data = await this.fetch<Tweet>(EResourceType.TWEET_DETAILS, { id: id });
70
+ public async details(id: string): Promise<Tweet | undefined> {
71
+ const resource = EResourceType.TWEET_DETAILS;
59
72
 
60
- return data.list[0];
73
+ // Fetching raw tweet details
74
+ const response = await this.request<ITweetDetailsResponse>(resource, { id: id });
75
+
76
+ // Deserializing response
77
+ const data = extractors[resource](response, id);
78
+
79
+ return data;
61
80
  }
62
81
 
63
82
  /**
64
- * Search for tweets using a query.
83
+ * Like a tweet.
65
84
  *
66
- * @param query - The query be used for searching the tweets.
67
- * @param count - The number of tweets to fetch, must be \<= 20.
68
- * @param cursor - The cursor to the batch of tweets to fetch.
69
- * @returns The list of tweets that match the given filter.
85
+ * @param id - The id of the tweet to be liked.
86
+ *
87
+ * @returns Whether liking was successful or not.
70
88
  *
71
89
  * @example
72
90
  * ```
@@ -75,8 +93,8 @@ export class TweetService extends FetcherService {
75
93
  * // Creating a new Rettiwt instance using the given 'API_KEY'
76
94
  * const rettiwt = new Rettiwt({ apiKey: API_KEY });
77
95
  *
78
- * // Fetching the most recent 5 tweets from user 'user1'
79
- * rettiwt.tweet.search({ fromUsers: ['user1'] }, 5)
96
+ * // Liking the Tweet with id '1234567890'
97
+ * rettiwt.tweet.like('1234567890')
80
98
  * .then(res => {
81
99
  * console.log(res);
82
100
  * })
@@ -84,31 +102,29 @@ export class TweetService extends FetcherService {
84
102
  * console.log(err);
85
103
  * });
86
104
  * ```
87
- *
88
- * @remarks For details about available filters, refer to {@link TweetFilter}
89
- *
90
- * @public
91
105
  */
92
- public async search(query: TweetFilter, count?: number, cursor?: string): Promise<CursoredData<Tweet>> {
93
- // Fetching the requested data
94
- const data = await this.fetch<Tweet>(EResourceType.TWEET_SEARCH, {
95
- filter: query,
96
- count: count,
97
- cursor: cursor,
106
+ public async like(id: string): Promise<boolean> {
107
+ const resource = EResourceType.TWEET_LIKE;
108
+
109
+ // Favoriting the tweet
110
+ const response = await this.request<ITweetLikeResponse>(resource, {
111
+ id: id,
98
112
  });
99
113
 
100
- // Sorting the tweets by date, from recent to oldest
101
- data.list.sort((a, b) => new Date(b.createdAt).valueOf() - new Date(a.createdAt).valueOf());
114
+ // Deserializing response
115
+ const data = extractors[resource](response) ?? false;
102
116
 
103
117
  return data;
104
118
  }
105
119
 
106
120
  /**
107
- * Stream tweets in pseudo real-time using a filter.
121
+ * Get the list of users who liked a tweet.
108
122
  *
109
- * @param filter - The filter to be used for searching the tweets.
110
- * @param pollingInterval - The interval in milliseconds to poll for new tweets. Default interval is 60000 ms.
111
- * @returns An async generator that yields matching tweets as they are found.
123
+ * @param id - The id of the target tweet.
124
+ * @param count - The number of likers to fetch, must be \<= 100.
125
+ * @param cursor - The cursor to the batch of likers to fetch.
126
+ *
127
+ * @returns The list of users who liked the given tweet.
112
128
  *
113
129
  * @example
114
130
  * ```
@@ -117,64 +133,40 @@ export class TweetService extends FetcherService {
117
133
  * // Creating a new Rettiwt instance using the given 'API_KEY'
118
134
  * const rettiwt = new Rettiwt({ apiKey: API_KEY });
119
135
  *
120
- * // Streaming all upcoming tweets from user 'user1'
121
- * (async () => {
122
- * try {
123
- * for await (const tweet of rettiwt.tweet.stream({ fromUsers: ['user1'] }, 1000)) {
124
- * console.log(tweet.fullText);
125
- * }
126
- * }
127
- * catch (err) {
128
- * console.log(err);
129
- * }
130
- * })();
136
+ * // Fetching the most recent 100 likers of the Tweet with id '1234567890'
137
+ * rettiwt.tweet.likers('1234567890')
138
+ * .then(res => {
139
+ * console.log(res);
140
+ * })
141
+ * .catch(err => {
142
+ * console.log(err);
143
+ * });
131
144
  * ```
132
- *
133
- * @public
134
145
  */
135
- public async *stream(filter: TweetFilter, pollingInterval: number = 60000): AsyncGenerator<Tweet> {
136
- const startDate = new Date();
146
+ public async likers(id: string, count?: number, cursor?: string): Promise<CursoredData<User>> {
147
+ const resource = EResourceType.TWEET_LIKERS;
137
148
 
138
- let cursor: string | undefined = undefined;
139
- let sinceId: string | undefined = undefined;
140
- let nextSinceId: string | undefined = undefined;
141
-
142
- while (true) {
143
- // Pause execution for the specified polling interval before proceeding to the next iteration
144
- await new Promise((resolve) => setTimeout(resolve, pollingInterval));
145
-
146
- // Search for tweets
147
- const tweets = await this.search({ ...filter, startDate: startDate, sinceId: sinceId }, undefined, cursor);
148
-
149
- // Yield the matching tweets
150
- for (const tweet of tweets.list) {
151
- yield tweet;
152
- }
149
+ // Fetching raw likers
150
+ const response = await this.request<ITweetLikersResponse>(resource, {
151
+ id: id,
152
+ count: count,
153
+ cursor: cursor,
154
+ });
153
155
 
154
- // Store the most recent tweet ID from this batch
155
- if (tweets.list.length > 0 && cursor === undefined) {
156
- nextSinceId = tweets.list[0].id;
157
- }
156
+ // Deserializing response
157
+ const data = extractors[resource](response);
158
158
 
159
- // If there are more tweets to fetch, adjust the cursor value
160
- if (tweets.list.length > 0 && tweets.next) {
161
- cursor = tweets.next.value;
162
- }
163
- // Else, start the next iteration from this batch's most recent tweet
164
- else {
165
- sinceId = nextSinceId;
166
- cursor = undefined;
167
- }
168
- }
159
+ return data;
169
160
  }
170
161
 
171
162
  /**
172
- * Get the tweets from the tweet list with the given id.
163
+ * Get the list of tweets from a tweet list.
173
164
  *
174
- * @param listId - The id of list from where the tweets are to be fetched.
165
+ * @param id - The id of target list.
175
166
  * @param count - The number of tweets to fetch, must be \<= 100.
176
167
  * @param cursor - The cursor to the batch of tweets to fetch.
177
- * @returns The list tweets present in the given list.
168
+ *
169
+ * @returns The list tweets in the given list.
178
170
  *
179
171
  * @example
180
172
  * ```
@@ -183,8 +175,8 @@ export class TweetService extends FetcherService {
183
175
  * // Creating a new Rettiwt instance using the given 'API_KEY'
184
176
  * const rettiwt = new Rettiwt({ apiKey: API_KEY });
185
177
  *
186
- * // Fetching the most recent 100 tweets of the Twitter list with id '12345678'
187
- * rettiwt.tweet.list('12345678')
178
+ * // Fetching the most recent 100 tweets of the Twitter list with id '1234567890'
179
+ * rettiwt.tweet.list('1234567890')
188
180
  * .then(res => {
189
181
  * console.log(res);
190
182
  * })
@@ -195,14 +187,19 @@ export class TweetService extends FetcherService {
195
187
  *
196
188
  * @remarks Due a bug in Twitter API, the count is ignored when no cursor is provided and defaults to 100.
197
189
  */
198
- public async list(listId: string, count?: number, cursor?: string): Promise<CursoredData<Tweet>> {
199
- // Fetching the requested data
200
- const data = await this.fetch<Tweet>(EResourceType.LIST_TWEETS, {
201
- id: listId,
190
+ public async list(id: string, count?: number, cursor?: string): Promise<CursoredData<Tweet>> {
191
+ const resource = EResourceType.LIST_TWEETS;
192
+
193
+ // Fetching raw list tweets
194
+ const response = await this.request<IListTweetsResponse>(resource, {
195
+ id: id,
202
196
  count: count,
203
197
  cursor: cursor,
204
198
  });
205
199
 
200
+ // Deserializing response
201
+ const data = extractors[resource](response);
202
+
206
203
  // Sorting the tweets by date, from recent to oldest
207
204
  data.list.sort((a, b) => new Date(b.createdAt).valueOf() - new Date(a.createdAt).valueOf());
208
205
 
@@ -210,22 +207,22 @@ export class TweetService extends FetcherService {
210
207
  }
211
208
 
212
209
  /**
213
- * Get the list of users who liked a tweet.
210
+ * Post a tweet.
214
211
  *
215
- * @param tweetId - The rest id of the target tweet.
216
- * @param count - The number of favoriters to fetch, must be \<= 100.
217
- * @param cursor - The cursor to the batch of favoriters to fetch.
218
- * @returns The list of users who liked the given tweet.
212
+ * @param options - The options describing the tweet to be posted. Check {@link TweetArgs} for available options.
213
+ *
214
+ * @returns Whether posting was successful or not.
219
215
  *
220
216
  * @example
217
+ * Posting a simple text
221
218
  * ```
222
219
  * import { Rettiwt } from 'rettiwt-api';
223
220
  *
224
221
  * // Creating a new Rettiwt instance using the given 'API_KEY'
225
222
  * const rettiwt = new Rettiwt({ apiKey: API_KEY });
226
223
  *
227
- * // Fetching the most recent 100 likers of the Tweet with id '12345678'
228
- * rettiwt.tweet.favoriters('12345678')
224
+ * // Posting a tweet to twitter
225
+ * rettiwt.tweet.post({ text: 'Hello World!' })
229
226
  * .then(res => {
230
227
  * console.log(res);
231
228
  * })
@@ -234,15 +231,104 @@ export class TweetService extends FetcherService {
234
231
  * });
235
232
  * ```
236
233
  *
237
- * @public
234
+ * @example
235
+ * Posting a tweet with an image that has been already uploaded
236
+ * ```
237
+ * import { Rettiwt } from 'rettiwt-api';
238
+ *
239
+ * // Creating a new Rettiwt instance using the given 'API_KEY'
240
+ * const rettiwt = new Rettiwt({ apiKey: API_KEY });
241
+ *
242
+ * // Posting a tweet, containing an image called 'mountains.jpg', to twitter
243
+ * rettiwt.tweet.post({ text: 'What a nice view!', media: [{ id: '1234567890' }] })
244
+ * .then(res => {
245
+ * console.log(res);
246
+ * })
247
+ * .catch(err => {
248
+ * console.log(err);
249
+ * });
250
+ * ```
251
+ *
252
+ * @example
253
+ * Posting a reply to a tweet
254
+ * ```
255
+ * import { Rettiwt } from 'rettiwt-api';
256
+ *
257
+ * // Creating a new Rettiwt instance using the given 'API_KEY'
258
+ * const rettiwt = new Rettiwt({ apiKey: API_KEY });
259
+ *
260
+ * // Posting a simple text reply, to a tweet with id "1234567890"
261
+ * rettiwt.tweet.post({ text: 'Hello!', replyTo: "1234567890" })
262
+ * .then(res => {
263
+ * console.log(res);
264
+ * })
265
+ * .catch(err => {
266
+ * console.log(err);
267
+ * });
268
+ * ```
269
+ *
270
+ * * @example
271
+ * Posting a tweet that quotes another tweet
272
+ * ```
273
+ * import { Rettiwt } from 'rettiwt-api';
274
+ *
275
+ * // Creating a new Rettiwt instance using the given 'API_KEY'
276
+ * const rettiwt = new Rettiwt({ apiKey: API_KEY });
277
+ *
278
+ * // Posting a simple text tweet, quoting a tweet with id "1234567890"
279
+ * rettiwt.tweet.post({ text: 'Hello!', quote: "1234567890" })
280
+ * .then(res => {
281
+ * console.log(res);
282
+ * })
283
+ * .catch(err => {
284
+ * console.log(err);
285
+ * });
286
+ * ```
238
287
  */
239
- public async favoriters(tweetId: string, count?: number, cursor?: string): Promise<CursoredData<User>> {
240
- // Fetching the requested data
241
- const data = await this.fetch<User>(EResourceType.TWEET_FAVORITERS, {
242
- id: tweetId,
243
- count: count,
244
- cursor: cursor,
245
- });
288
+ public async post(options: TweetArgs): Promise<string | undefined> {
289
+ const resource = EResourceType.TWEET_POST;
290
+
291
+ // Posting the tweet
292
+ const response = await this.request<ITweetPostResponse>(resource, { tweet: options });
293
+
294
+ // Deserializing response
295
+ const data = extractors[resource](response);
296
+
297
+ return data;
298
+ }
299
+
300
+ /**
301
+ * Retweet a tweet.
302
+ *
303
+ * @param id - The id of the target tweet.
304
+ *
305
+ * @returns Whether retweeting was successful or not.
306
+ *
307
+ * @example
308
+ * ```
309
+ * import { Rettiwt } from 'rettiwt-api';
310
+ *
311
+ * // Creating a new Rettiwt instance using the given 'API_KEY'
312
+ * const rettiwt = new Rettiwt({ apiKey: API_KEY });
313
+ *
314
+ * // Retweeting the Tweet with id '1234567890'
315
+ * rettiwt.tweet.retweet('1234567890')
316
+ * .then(res => {
317
+ * console.log(res);
318
+ * })
319
+ * .catch(err => {
320
+ * console.log(err);
321
+ * });
322
+ * ```
323
+ */
324
+ public async retweet(id: string): Promise<boolean> {
325
+ const resource = EResourceType.TWEET_RETWEET;
326
+
327
+ // Retweeting the tweet
328
+ const response = await this.request<ITweetRetweetResponse>(resource, { id: id });
329
+
330
+ // Deserializing response
331
+ const data = extractors[resource](response) ?? false;
246
332
 
247
333
  return data;
248
334
  }
@@ -250,9 +336,10 @@ export class TweetService extends FetcherService {
250
336
  /**
251
337
  * Get the list of users who retweeted a tweet.
252
338
  *
253
- * @param tweetId - The rest id of the target tweet.
339
+ * @param id - The id of the target tweet.
254
340
  * @param count - The number of retweeters to fetch, must be \<= 100.
255
341
  * @param cursor - The cursor to the batch of retweeters to fetch.
342
+ *
256
343
  * @returns The list of users who retweeted the given tweet.
257
344
  *
258
345
  * @example
@@ -262,8 +349,8 @@ export class TweetService extends FetcherService {
262
349
  * // Creating a new Rettiwt instance using the given 'API_KEY'
263
350
  * const rettiwt = new Rettiwt({ apiKey: API_KEY });
264
351
  *
265
- * // Fetching the most recent 100 retweeters of the Tweet with id '12345678'
266
- * rettiwt.tweet.retweeters('12345678')
352
+ * // Fetching the most recent 100 retweeters of the Tweet with id '1234567890'
353
+ * rettiwt.tweet.retweeters('1234567890')
267
354
  * .then(res => {
268
355
  * console.log(res);
269
356
  * })
@@ -271,37 +358,41 @@ export class TweetService extends FetcherService {
271
358
  * console.log(err);
272
359
  * });
273
360
  * ```
274
- *
275
- * @public
276
361
  */
277
- public async retweeters(tweetId: string, count?: number, cursor?: string): Promise<CursoredData<User>> {
278
- // Fetching the requested data
279
- const data = await this.fetch<User>(EResourceType.TWEET_RETWEETERS, {
280
- id: tweetId,
362
+ public async retweeters(id: string, count?: number, cursor?: string): Promise<CursoredData<User>> {
363
+ const resource = EResourceType.TWEET_RETWEETERS;
364
+
365
+ // Fetching raw list of retweeters
366
+ const response = await this.request<ITweetRetweetersResponse>(resource, {
367
+ id: id,
281
368
  count: count,
282
369
  cursor: cursor,
283
370
  });
284
371
 
372
+ // Deserializing response
373
+ const data = extractors[resource](response);
374
+
285
375
  return data;
286
376
  }
287
377
 
288
378
  /**
289
- * Post a tweet.
379
+ * Search for tweets using a filter.
290
380
  *
291
- * @param text - The text to be posted, length must be \<= 280 characters.
292
- * @param media - The list of media to post in the tweet, max number of media must be \<= 4.
293
- * @param replyTo - The id of the tweet to which the reply is to be made.
294
- * @returns Whether posting was successful or not.
381
+ * @param filter - The filter to be used for searching the tweets.
382
+ * @param count - The number of tweets to fetch, must be \<= 20.
383
+ * @param cursor - The cursor to the batch of tweets to fetch.
295
384
  *
296
- * @example Posting a simple text
385
+ * @returns The list of tweets that match the given filter.
386
+ *
387
+ * @example
297
388
  * ```
298
389
  * import { Rettiwt } from 'rettiwt-api';
299
390
  *
300
391
  * // Creating a new Rettiwt instance using the given 'API_KEY'
301
392
  * const rettiwt = new Rettiwt({ apiKey: API_KEY });
302
393
  *
303
- * // Posting a tweet to twitter
304
- * rettiwt.tweet.tweet('Hello World!')
394
+ * // Fetching the most recent 5 tweets from user 'user1'
395
+ * rettiwt.tweet.search({ fromUsers: ['user1'] }, 5)
305
396
  * .then(res => {
306
397
  * console.log(res);
307
398
  * })
@@ -310,15 +401,107 @@ export class TweetService extends FetcherService {
310
401
  * });
311
402
  * ```
312
403
  *
313
- * @example Posting a tweet with an image
404
+ * @remarks For details about available filters, refer to {@link TweetFilter}
405
+ */
406
+ public async search(filter: TweetFilter, count?: number, cursor?: string): Promise<CursoredData<Tweet>> {
407
+ const resource = EResourceType.TWEET_SEARCH;
408
+
409
+ // Fetching raw list of filtered tweets
410
+ const response = await this.request<ITweetSearchResponse>(resource, {
411
+ filter: filter,
412
+ count: count,
413
+ cursor: cursor,
414
+ });
415
+
416
+ // Deserializing response
417
+ const data = extractors[resource](response);
418
+
419
+ // Sorting the tweets by date, from recent to oldest
420
+ data.list.sort((a, b) => new Date(b.createdAt).valueOf() - new Date(a.createdAt).valueOf());
421
+
422
+ return data;
423
+ }
424
+
425
+ /**
426
+ * Stream tweets in pseudo real-time using a filter.
427
+ *
428
+ * @param filter - The filter to be used for searching the tweets.
429
+ * @param pollingInterval - The interval in milliseconds to poll for new tweets. Default interval is 60000 ms.
430
+ *
431
+ * @returns An async generator that yields matching tweets as they are found.
432
+ *
433
+ * @example
314
434
  * ```
315
435
  * import { Rettiwt } from 'rettiwt-api';
316
436
  *
317
437
  * // Creating a new Rettiwt instance using the given 'API_KEY'
318
438
  * const rettiwt = new Rettiwt({ apiKey: API_KEY });
319
439
  *
320
- * // Posting a tweet, containing an image called 'mountains.jpg', to twitter
321
- * rettiwt.tweet.tweet('What a nice view!', [{ path: 'mountains.jpg' }])
440
+ * // Streaming all upcoming tweets from user 'user1'
441
+ * (async () => {
442
+ * try {
443
+ * for await (const tweet of rettiwt.tweet.stream({ fromUsers: ['user1'] }, 1000)) {
444
+ * console.log(tweet.fullText);
445
+ * }
446
+ * }
447
+ * catch (err) {
448
+ * console.log(err);
449
+ * }
450
+ * })();
451
+ * ```
452
+ */
453
+ public async *stream(filter: TweetFilter, pollingInterval: number = 60000): AsyncGenerator<Tweet> {
454
+ const startDate = new Date();
455
+
456
+ let cursor: string | undefined = undefined;
457
+ let sinceId: string | undefined = undefined;
458
+ let nextSinceId: string | undefined = undefined;
459
+
460
+ while (true) {
461
+ // Pause execution for the specified polling interval before proceeding to the next iteration
462
+ await new Promise((resolve) => setTimeout(resolve, pollingInterval));
463
+
464
+ // Search for tweets
465
+ const tweets = await this.search({ ...filter, startDate: startDate, sinceId: sinceId }, undefined, cursor);
466
+
467
+ // Yield the matching tweets
468
+ for (const tweet of tweets.list) {
469
+ yield tweet;
470
+ }
471
+
472
+ // Store the most recent tweet ID from this batch
473
+ if (tweets.list.length > 0 && cursor === undefined) {
474
+ nextSinceId = tweets.list[0].id;
475
+ }
476
+
477
+ // If there are more tweets to fetch, adjust the cursor value
478
+ if (tweets.list.length > 0 && tweets.next) {
479
+ cursor = tweets.next.value;
480
+ }
481
+ // Else, start the next iteration from this batch's most recent tweet
482
+ else {
483
+ sinceId = nextSinceId;
484
+ cursor = undefined;
485
+ }
486
+ }
487
+ }
488
+
489
+ /**
490
+ * Unlike a tweet.
491
+ *
492
+ * @param id - The id of the target tweet.
493
+ *
494
+ * @returns Whether unliking was successful or not.
495
+ *
496
+ * @example
497
+ * ```
498
+ * import { Rettiwt } from 'rettiwt-api';
499
+ *
500
+ * // Creating a new Rettiwt instance using the given 'API_KEY'
501
+ * const rettiwt = new Rettiwt({ apiKey: API_KEY });
502
+ *
503
+ * // Unliking the Tweet with id '1234567890'
504
+ * rettiwt.tweet.unlike('1234567890')
322
505
  * .then(res => {
323
506
  * console.log(res);
324
507
  * })
@@ -326,16 +509,35 @@ export class TweetService extends FetcherService {
326
509
  * console.log(err);
327
510
  * });
328
511
  * ```
512
+ */
513
+ public async unlike(id: string): Promise<boolean> {
514
+ const resource = EResourceType.TWEET_UNLIKE;
515
+
516
+ // Unliking the tweet
517
+ const response = await this.request<ITweetUnlikeResponse>(resource, { id: id });
518
+
519
+ // Deserializing the response
520
+ const data = extractors[resource](response) ?? false;
521
+
522
+ return data;
523
+ }
524
+
525
+ /**
526
+ * Unpost a tweet.
329
527
  *
330
- * @example Posting a reply to a tweet
528
+ * @param id - The id of the target tweet.
529
+ *
530
+ * @returns Whether unposting was successful or not.
531
+ *
532
+ * @example
331
533
  * ```
332
534
  * import { Rettiwt } from 'rettiwt-api';
333
535
  *
334
536
  * // Creating a new Rettiwt instance using the given 'API_KEY'
335
537
  * const rettiwt = new Rettiwt({ apiKey: API_KEY });
336
538
  *
337
- * // Posting a simple text reply, to a tweet with id "1234567890"
338
- * rettiwt.tweet.tweet('Hello!', undefined, "1234567890")
539
+ * // Unposting the Tweet with id '1234567890'
540
+ * rettiwt.tweet.unpost('1234567890')
339
541
  * .then(res => {
340
542
  * console.log(res);
341
543
  * })
@@ -343,40 +545,25 @@ export class TweetService extends FetcherService {
343
545
  * console.log(err);
344
546
  * });
345
547
  * ```
346
- *
347
- * @public
348
548
  */
349
- public async tweet(text: string, media?: TweetMediaArgs[], replyTo?: string): Promise<boolean> {
350
- // Converting JSON args to object
351
- const tweet: TweetArgs = new TweetArgs({ text: text, media: media });
352
-
353
- /** Stores the list of media that has been uploaded */
354
- const uploadedMedia: MediaArgs[] = [];
549
+ public async unpost(id: string): Promise<boolean> {
550
+ const resource = EResourceType.TWEET_UNPOST;
355
551
 
356
- // If tweet includes media, upload the media items
357
- if (tweet.media) {
358
- for (const item of tweet.media) {
359
- // Uploading the media item and getting it's allocated id
360
- const id: string = await this.upload(item.path);
361
-
362
- // Storing the uploaded media item
363
- uploadedMedia.push(new MediaArgs({ id: id, tags: item.tags }));
364
- }
365
- }
552
+ // Unposting the tweet
553
+ const response = await this.request<ITweetUnpostResponse>(resource, { id: id });
366
554
 
367
- // Posting the tweet
368
- const data = await this.post(EResourceType.CREATE_TWEET, {
369
- tweet: { text: text, media: uploadedMedia, replyTo: replyTo },
370
- });
555
+ // Deserializing the response
556
+ const data = extractors[resource](response) ?? false;
371
557
 
372
558
  return data;
373
559
  }
374
560
 
375
561
  /**
376
- * Favorite the tweet with the given id.
562
+ * Unretweet a tweet.
563
+ *
564
+ * @param id - The id of the target tweet.
377
565
  *
378
- * @param tweetId - The id of the tweet to be favorited.
379
- * @returns Whether favoriting was successful or not.
566
+ * @returns Whether unretweeting was successful or not.
380
567
  *
381
568
  * @example
382
569
  * ```
@@ -385,8 +572,8 @@ export class TweetService extends FetcherService {
385
572
  * // Creating a new Rettiwt instance using the given 'API_KEY'
386
573
  * const rettiwt = new Rettiwt({ apiKey: API_KEY });
387
574
  *
388
- * // Liking the Tweet with id '12345678'
389
- * rettiwt.tweet.favorite('12345678')
575
+ * // Unretweeting the Tweet with id '1234567890'
576
+ * rettiwt.tweet.unretweet('1234567890')
390
577
  * .then(res => {
391
578
  * console.log(res);
392
579
  * })
@@ -394,21 +581,25 @@ export class TweetService extends FetcherService {
394
581
  * console.log(err);
395
582
  * });
396
583
  * ```
397
- *
398
- * @public
399
584
  */
400
- public async favorite(tweetId: string): Promise<boolean> {
401
- // Favoriting the tweet
402
- const data = await this.post(EResourceType.FAVORITE_TWEET, { id: tweetId });
585
+ public async unretweet(id: string): Promise<boolean> {
586
+ const resource = EResourceType.TWEET_UNRETWEET;
587
+
588
+ // Unretweeting the tweet
589
+ const response = await this.request<ITweetUnretweetResponse>(resource, { id: id });
590
+
591
+ // Deserializing the response
592
+ const data = extractors[resource](response) ?? false;
403
593
 
404
594
  return data;
405
595
  }
406
596
 
407
597
  /**
408
- * Retweet the tweet with the given id.
598
+ * Upload a media file to Twitter.
409
599
  *
410
- * @param tweetId - The id of the tweet with the given id.
411
- * @returns Whether retweeting was successful or not.
600
+ * @param media - The path or ArrayBuffer to the media file to upload.
601
+ *
602
+ * @returns The id of the uploaded media.
412
603
  *
413
604
  * @example
414
605
  * ```
@@ -417,8 +608,8 @@ export class TweetService extends FetcherService {
417
608
  * // Creating a new Rettiwt instance using the given 'API_KEY'
418
609
  * const rettiwt = new Rettiwt({ apiKey: API_KEY });
419
610
  *
420
- * // Retweeting the Tweet with id '12345678'
421
- * rettiwt.tweet.retweet('12345678')
611
+ * // Uploading a file called mountains.jpg
612
+ * rettiwt.tweet.upload('mountains.jpg')
422
613
  * .then(res => {
423
614
  * console.log(res);
424
615
  * })
@@ -427,12 +618,26 @@ export class TweetService extends FetcherService {
427
618
  * });
428
619
  * ```
429
620
  *
430
- * @public
621
+ * @remarks
622
+ * - The uploaded media exists for 24 hrs within which it can be included in a tweet to be posted.
623
+ * If not posted in a tweet within this period, the uploaded media is removed.
624
+ * - Instead of a path to the media, an ArrayBuffer containing the media can also be uploaded.
431
625
  */
432
- public async retweet(tweetId: string): Promise<boolean> {
433
- // Retweeting the tweet
434
- const data = await this.post(EResourceType.CREATE_RETWEET, { id: tweetId });
435
-
436
- return data;
626
+ public async upload(media: string | ArrayBuffer): Promise<string> {
627
+ // INITIALIZE
628
+ const size = typeof media == 'string' ? statSync(media).size : media.byteLength;
629
+ const id: string = (
630
+ await this.request<IInitializeMediaUploadResponse>(EResourceType.MEDIA_UPLOAD_INITIALIZE, {
631
+ upload: { size: size },
632
+ })
633
+ ).media_id_string;
634
+
635
+ // APPEND
636
+ await this.request<unknown>(EResourceType.MEDIA_UPLOAD_APPEND, { upload: { id: id, media: media } });
637
+
638
+ // FINALIZE
639
+ await this.request<unknown>(EResourceType.MEDIA_UPLOAD_FINALIZE, { upload: { id: id } });
640
+
641
+ return id;
437
642
  }
438
643
  }