rettiwt-api 5.1.0-alpha.0 → 6.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 (150) hide show
  1. package/.eslintrc.js +13 -8
  2. package/README.md +8 -1
  3. package/dist/cli.js +17 -14
  4. package/dist/cli.js.map +1 -1
  5. package/dist/collections/Extractors.d.ts +1 -1
  6. package/dist/collections/Extractors.js +21 -21
  7. package/dist/collections/Extractors.js.map +1 -1
  8. package/dist/collections/Groups.d.ts +4 -4
  9. package/dist/collections/Groups.js +45 -45
  10. package/dist/collections/Groups.js.map +1 -1
  11. package/dist/collections/Requests.d.ts +3 -3
  12. package/dist/collections/Requests.js +3 -3
  13. package/dist/collections/Requests.js.map +1 -1
  14. package/dist/collections/Tweet.d.ts +4 -4
  15. package/dist/collections/Tweet.js +5 -5
  16. package/dist/collections/Tweet.js.map +1 -1
  17. package/dist/commands/Tweet.js +32 -4
  18. package/dist/commands/Tweet.js.map +1 -1
  19. package/dist/enums/Api.d.ts +1 -1
  20. package/dist/enums/Api.js +7 -7
  21. package/dist/enums/Api.js.map +1 -1
  22. package/dist/enums/Authentication.d.ts +1 -1
  23. package/dist/enums/Authentication.js +7 -7
  24. package/dist/enums/Authentication.js.map +1 -1
  25. package/dist/enums/Data.d.ts +1 -1
  26. package/dist/enums/Data.js +7 -7
  27. package/dist/enums/Data.js.map +1 -1
  28. package/dist/enums/Logging.d.ts +1 -1
  29. package/dist/enums/Logging.js +11 -11
  30. package/dist/enums/Logging.js.map +1 -1
  31. package/dist/enums/Media.d.ts +1 -1
  32. package/dist/enums/Media.js +7 -7
  33. package/dist/enums/Media.js.map +1 -1
  34. package/dist/enums/Notification.d.ts +1 -1
  35. package/dist/enums/Notification.js +9 -9
  36. package/dist/enums/Notification.js.map +1 -1
  37. package/dist/enums/Resource.d.ts +1 -1
  38. package/dist/enums/Resource.js +42 -42
  39. package/dist/enums/Resource.js.map +1 -1
  40. package/dist/enums/Tweet.d.ts +1 -1
  41. package/dist/enums/Tweet.js +7 -7
  42. package/dist/enums/Tweet.js.map +1 -1
  43. package/dist/enums/raw/Analytics.d.ts +2 -2
  44. package/dist/enums/raw/Analytics.js +21 -21
  45. package/dist/enums/raw/Analytics.js.map +1 -1
  46. package/dist/enums/raw/Media.d.ts +1 -1
  47. package/dist/enums/raw/Media.js +7 -7
  48. package/dist/enums/raw/Media.js.map +1 -1
  49. package/dist/enums/raw/Notification.d.ts +1 -1
  50. package/dist/enums/raw/Notification.js +8 -8
  51. package/dist/enums/raw/Notification.js.map +1 -1
  52. package/dist/enums/raw/Tweet.d.ts +2 -2
  53. package/dist/enums/raw/Tweet.js +12 -12
  54. package/dist/enums/raw/Tweet.js.map +1 -1
  55. package/dist/models/RettiwtConfig.d.ts +3 -2
  56. package/dist/models/RettiwtConfig.js +7 -5
  57. package/dist/models/RettiwtConfig.js.map +1 -1
  58. package/dist/models/args/FetchArgs.d.ts +3 -3
  59. package/dist/models/args/FetchArgs.js +3 -3
  60. package/dist/models/args/FetchArgs.js.map +1 -1
  61. package/dist/models/auth/AuthCredential.d.ts +2 -2
  62. package/dist/models/auth/AuthCredential.js +3 -3
  63. package/dist/models/auth/AuthCredential.js.map +1 -1
  64. package/dist/models/data/CursoredData.d.ts +2 -2
  65. package/dist/models/data/CursoredData.js +3 -3
  66. package/dist/models/data/CursoredData.js.map +1 -1
  67. package/dist/models/data/Notification.d.ts +2 -2
  68. package/dist/models/data/Notification.js +3 -3
  69. package/dist/models/data/Notification.js.map +1 -1
  70. package/dist/models/data/Tweet.d.ts +4 -4
  71. package/dist/models/data/Tweet.js +18 -18
  72. package/dist/models/data/Tweet.js.map +1 -1
  73. package/dist/models/data/User.js +9 -9
  74. package/dist/models/data/User.js.map +1 -1
  75. package/dist/requests/Tweet.d.ts +2 -2
  76. package/dist/requests/Tweet.js +2 -2
  77. package/dist/requests/Tweet.js.map +1 -1
  78. package/dist/requests/User.d.ts +2 -2
  79. package/dist/requests/User.js.map +1 -1
  80. package/dist/services/internal/AuthService.js +1 -1
  81. package/dist/services/internal/AuthService.js.map +1 -1
  82. package/dist/services/internal/ErrorService.d.ts +2 -2
  83. package/dist/services/internal/ErrorService.js +4 -4
  84. package/dist/services/internal/ErrorService.js.map +1 -1
  85. package/dist/services/internal/LogService.d.ts +2 -2
  86. package/dist/services/internal/LogService.js.map +1 -1
  87. package/dist/services/internal/TidService.d.ts +4 -4
  88. package/dist/services/internal/TidService.js +10 -10
  89. package/dist/services/internal/TidService.js.map +1 -1
  90. package/dist/services/public/FetcherService.d.ts +7 -7
  91. package/dist/services/public/FetcherService.js +79 -35
  92. package/dist/services/public/FetcherService.js.map +1 -1
  93. package/dist/services/public/ListService.js +4 -4
  94. package/dist/services/public/ListService.js.map +1 -1
  95. package/dist/services/public/TweetService.d.ts +3 -3
  96. package/dist/services/public/TweetService.js +35 -35
  97. package/dist/services/public/TweetService.js.map +1 -1
  98. package/dist/services/public/UserService.js +37 -37
  99. package/dist/services/public/UserService.js.map +1 -1
  100. package/dist/types/RettiwtConfig.d.ts +6 -0
  101. package/dist/types/args/FetchArgs.d.ts +2 -2
  102. package/dist/types/auth/AuthCredential.d.ts +2 -2
  103. package/dist/types/data/Notification.d.ts +2 -2
  104. package/dist/types/data/Tweet.d.ts +2 -2
  105. package/dist/types/raw/base/Media.d.ts +2 -2
  106. package/dist/types/raw/base/Notification.d.ts +2 -2
  107. package/package.json +18 -18
  108. package/src/cli.ts +20 -14
  109. package/src/collections/Extractors.ts +21 -21
  110. package/src/collections/Groups.ts +45 -45
  111. package/src/collections/Requests.ts +4 -4
  112. package/src/collections/Tweet.ts +6 -6
  113. package/src/commands/Tweet.ts +33 -4
  114. package/src/enums/Api.ts +1 -1
  115. package/src/enums/Authentication.ts +1 -1
  116. package/src/enums/Data.ts +1 -1
  117. package/src/enums/Logging.ts +1 -1
  118. package/src/enums/Media.ts +1 -1
  119. package/src/enums/Notification.ts +1 -1
  120. package/src/enums/Resource.ts +1 -1
  121. package/src/enums/Tweet.ts +1 -1
  122. package/src/enums/raw/Analytics.ts +2 -2
  123. package/src/enums/raw/Media.ts +1 -1
  124. package/src/enums/raw/Notification.ts +1 -1
  125. package/src/enums/raw/Tweet.ts +2 -2
  126. package/src/models/RettiwtConfig.ts +7 -5
  127. package/src/models/args/FetchArgs.ts +5 -5
  128. package/src/models/auth/AuthCredential.ts +5 -5
  129. package/src/models/data/CursoredData.ts +5 -5
  130. package/src/models/data/Notification.ts +6 -6
  131. package/src/models/data/Tweet.ts +22 -22
  132. package/src/models/data/User.ts +10 -10
  133. package/src/requests/Tweet.ts +4 -4
  134. package/src/requests/User.ts +3 -3
  135. package/src/services/internal/AuthService.ts +2 -2
  136. package/src/services/internal/ErrorService.ts +4 -4
  137. package/src/services/internal/LogService.ts +2 -2
  138. package/src/services/internal/TidService.ts +11 -11
  139. package/src/services/public/FetcherService.ts +54 -38
  140. package/src/services/public/ListService.ts +6 -6
  141. package/src/services/public/TweetService.ts +39 -39
  142. package/src/services/public/UserService.ts +40 -40
  143. package/src/types/RettiwtConfig.ts +7 -0
  144. package/src/types/args/FetchArgs.ts +2 -2
  145. package/src/types/auth/AuthCredential.ts +2 -2
  146. package/src/types/data/Notification.ts +2 -2
  147. package/src/types/data/Tweet.ts +2 -2
  148. package/src/types/raw/base/Media.ts +2 -2
  149. package/src/types/raw/base/Notification.ts +2 -2
  150. package/.tool-versions +0 -1
@@ -1,6 +1,6 @@
1
- import { ELogActions } from '../../enums/Logging';
2
- import { EMediaType } from '../../enums/Media';
3
- import { ERawMediaType } from '../../enums/raw/Media';
1
+ import { LogActions } from '../../enums/Logging';
2
+ import { MediaType } from '../../enums/Media';
3
+ import { RawMediaType } from '../../enums/raw/Media';
4
4
  import { findByFilter } from '../../helper/JsonUtils';
5
5
 
6
6
  import { LogService } from '../../services/internal/LogService';
@@ -52,7 +52,7 @@ export class Tweet implements ITweet {
52
52
  this.tweetBy = new User(tweet.core.user_results.result);
53
53
  this.entities = new TweetEntities(tweet.legacy.entities);
54
54
  this.media = tweet.legacy.extended_entities?.media?.map((media) => new TweetMedia(media));
55
- this.quoted = this.getQuotedTweet(tweet);
55
+ this.quoted = this._getQuotedTweet(tweet);
56
56
  this.fullText = tweet.note_tweet ? tweet.note_tweet.note_tweet_results.result.text : tweet.legacy.full_text;
57
57
  this.replyTo = tweet.legacy.in_reply_to_status_id_str;
58
58
  this.lang = tweet.legacy.lang;
@@ -62,7 +62,7 @@ export class Tweet implements ITweet {
62
62
  this.likeCount = tweet.legacy.favorite_count;
63
63
  this.viewCount = tweet.views.count ? parseInt(tweet.views.count) : 0;
64
64
  this.bookmarkCount = tweet.legacy.bookmark_count;
65
- this.retweetedTweet = this.getRetweetedTweet(tweet);
65
+ this.retweetedTweet = this._getRetweetedTweet(tweet);
66
66
  this.url = `https://x.com/${this.tweetBy.userName}/status/${this.id}`;
67
67
  }
68
68
 
@@ -78,7 +78,7 @@ export class Tweet implements ITweet {
78
78
  *
79
79
  * @returns - The deserialized original quoted tweet.
80
80
  */
81
- private getQuotedTweet(tweet: IRawTweet): Tweet | undefined {
81
+ private _getQuotedTweet(tweet: IRawTweet): Tweet | undefined {
82
82
  // If tweet with limited visibility
83
83
  if (
84
84
  tweet.quoted_status_result &&
@@ -104,7 +104,7 @@ export class Tweet implements ITweet {
104
104
  *
105
105
  * @returns - The deserialized original retweeted tweet.
106
106
  */
107
- private getRetweetedTweet(tweet: IRawTweet): Tweet | undefined {
107
+ private _getRetweetedTweet(tweet: IRawTweet): Tweet | undefined {
108
108
  // If retweet with limited visibility
109
109
  if (
110
110
  tweet.legacy?.retweeted_status_result &&
@@ -141,13 +141,13 @@ export class Tweet implements ITweet {
141
141
  for (const item of extract) {
142
142
  if (item.legacy) {
143
143
  // Logging
144
- LogService.log(ELogActions.DESERIALIZE, { id: item.rest_id });
144
+ LogService.log(LogActions.DESERIALIZE, { id: item.rest_id });
145
145
 
146
146
  tweets.push(new Tweet(item));
147
147
  } else {
148
148
  // Logging
149
- LogService.log(ELogActions.WARNING, {
150
- action: ELogActions.DESERIALIZE,
149
+ LogService.log(LogActions.WARNING, {
150
+ action: LogActions.DESERIALIZE,
151
151
  message: `Tweet not found, skipping`,
152
152
  });
153
153
  }
@@ -179,13 +179,13 @@ export class Tweet implements ITweet {
179
179
  for (const item of extract) {
180
180
  if (item.legacy) {
181
181
  // Logging
182
- LogService.log(ELogActions.DESERIALIZE, { id: item.rest_id });
182
+ LogService.log(LogActions.DESERIALIZE, { id: item.rest_id });
183
183
 
184
184
  tweets.push(new Tweet(item));
185
185
  } else {
186
186
  // Logging
187
- LogService.log(ELogActions.WARNING, {
188
- action: ELogActions.DESERIALIZE,
187
+ LogService.log(LogActions.WARNING, {
188
+ action: LogActions.DESERIALIZE,
189
189
  message: `Tweet not found, skipping`,
190
190
  });
191
191
  }
@@ -221,15 +221,15 @@ export class Tweet implements ITweet {
221
221
  // If normal tweet
222
222
  else if ((item.tweet_results?.result as IRawTweet)?.legacy) {
223
223
  // Logging
224
- LogService.log(ELogActions.DESERIALIZE, { id: (item.tweet_results.result as IRawTweet).rest_id });
224
+ LogService.log(LogActions.DESERIALIZE, { id: (item.tweet_results.result as IRawTweet).rest_id });
225
225
 
226
226
  tweets.push(new Tweet(item.tweet_results.result as IRawTweet));
227
227
  }
228
228
  // If invalid/unrecognized tweet
229
229
  else {
230
230
  // Logging
231
- LogService.log(ELogActions.WARNING, {
232
- action: ELogActions.DESERIALIZE,
231
+ LogService.log(LogActions.WARNING, {
232
+ action: LogActions.DESERIALIZE,
233
233
  message: `Tweet not found, skipping`,
234
234
  });
235
235
  }
@@ -328,7 +328,7 @@ export class TweetMedia {
328
328
  public thumbnailUrl?: string;
329
329
 
330
330
  /** The type of media. */
331
- public type: EMediaType;
331
+ public type: MediaType;
332
332
 
333
333
  /** The direct URL to the media. */
334
334
  public url = '';
@@ -338,18 +338,18 @@ export class TweetMedia {
338
338
  */
339
339
  public constructor(media: IRawExtendedMedia) {
340
340
  // If the media is a photo
341
- if (media.type == ERawMediaType.PHOTO) {
342
- this.type = EMediaType.PHOTO;
341
+ if (media.type == RawMediaType.PHOTO) {
342
+ this.type = MediaType.PHOTO;
343
343
  this.url = media.media_url_https;
344
344
  }
345
345
  // If the media is a gif
346
- else if (media.type == ERawMediaType.GIF) {
347
- this.type = EMediaType.GIF;
346
+ else if (media.type == RawMediaType.GIF) {
347
+ this.type = MediaType.GIF;
348
348
  this.url = media.video_info?.variants[0].url as string;
349
349
  }
350
350
  // If the media is a video
351
351
  else {
352
- this.type = EMediaType.VIDEO;
352
+ this.type = MediaType.VIDEO;
353
353
  this.thumbnailUrl = media.media_url_https;
354
354
 
355
355
  /** The highest bitrate of all variants. */
@@ -1,4 +1,4 @@
1
- import { ELogActions } from '../../enums/Logging';
1
+ import { LogActions } from '../../enums/Logging';
2
2
  import { findByFilter } from '../../helper/JsonUtils';
3
3
  import { LogService } from '../../services/internal/LogService';
4
4
  import { IUser } from '../../types/data/User';
@@ -73,13 +73,13 @@ export class User implements IUser {
73
73
  for (const item of extract) {
74
74
  if (item.legacy && item.legacy.created_at) {
75
75
  // Logging
76
- LogService.log(ELogActions.DESERIALIZE, { id: item.rest_id });
76
+ LogService.log(LogActions.DESERIALIZE, { id: item.rest_id });
77
77
 
78
78
  users.push(new User(item));
79
79
  } else {
80
80
  // Logging
81
- LogService.log(ELogActions.WARNING, {
82
- action: ELogActions.DESERIALIZE,
81
+ LogService.log(LogActions.WARNING, {
82
+ action: LogActions.DESERIALIZE,
83
83
  message: `User not found, skipping`,
84
84
  });
85
85
  }
@@ -110,13 +110,13 @@ export class User implements IUser {
110
110
  for (const item of extract) {
111
111
  if (item.legacy && item.legacy.created_at) {
112
112
  // Logging
113
- LogService.log(ELogActions.DESERIALIZE, { id: item.rest_id });
113
+ LogService.log(LogActions.DESERIALIZE, { id: item.rest_id });
114
114
 
115
115
  users.push(new User(item));
116
116
  } else {
117
117
  // Logging
118
- LogService.log(ELogActions.WARNING, {
119
- action: ELogActions.DESERIALIZE,
118
+ LogService.log(LogActions.WARNING, {
119
+ action: LogActions.DESERIALIZE,
120
120
  message: `User not found, skipping`,
121
121
  });
122
122
  }
@@ -142,13 +142,13 @@ export class User implements IUser {
142
142
  for (const item of extract) {
143
143
  if (item.user_results?.result?.legacy) {
144
144
  // Logging
145
- LogService.log(ELogActions.DESERIALIZE, { id: item.user_results.result.rest_id });
145
+ LogService.log(LogActions.DESERIALIZE, { id: item.user_results.result.rest_id });
146
146
 
147
147
  users.push(new User(item.user_results.result));
148
148
  } else {
149
149
  // Logging
150
- LogService.log(ELogActions.WARNING, {
151
- action: ELogActions.DESERIALIZE,
150
+ LogService.log(LogActions.WARNING, {
151
+ action: LogActions.DESERIALIZE,
152
152
  message: `User not found, skipping`,
153
153
  });
154
154
  }
@@ -1,6 +1,6 @@
1
1
  import { AxiosRequestConfig } from 'axios';
2
2
 
3
- import { ERawTweetRepliesSortType, ERawTweetSearchResultType } from '../enums/raw/Tweet';
3
+ import { RawTweetRepliesSortType, RawTweetSearchResultType } from '../enums/raw/Tweet';
4
4
  import { TweetFilter } from '../models/args/FetchArgs';
5
5
  import { NewTweet } from '../models/args/PostArgs';
6
6
  import { MediaVariable, ReplyVariable } from '../models/params/Variables';
@@ -266,7 +266,7 @@ export class TweetRequests {
266
266
  * @param id - The id of the tweet whose replies are to be fetched.
267
267
  * @param cursor - The cursor to the batch of replies to fetch.
268
268
  */
269
- public static replies(id: string, cursor?: string, sortBy?: ERawTweetRepliesSortType): AxiosRequestConfig {
269
+ public static replies(id: string, cursor?: string, sortBy?: RawTweetRepliesSortType): AxiosRequestConfig {
270
270
  return {
271
271
  method: 'get',
272
272
  url: 'https://x.com/i/api/graphql/_8aYOgEDz35BrBcBal1-_w/TweetDetail',
@@ -277,7 +277,7 @@ export class TweetRequests {
277
277
  cursor: cursor,
278
278
  referrer: 'tweet',
279
279
  with_rux_injections: false,
280
- rankingMode: sortBy ?? ERawTweetRepliesSortType.RELEVACE,
280
+ rankingMode: sortBy ?? RawTweetRepliesSortType.RELEVACE,
281
281
  includePromotedContent: true,
282
282
  withCommunity: true,
283
283
  withQuickPromoteEligibilityTweetFields: true,
@@ -451,7 +451,7 @@ export class TweetRequests {
451
451
  count: count,
452
452
  cursor: cursor,
453
453
  querySource: 'typed_query',
454
- product: parsedFilter.top ? ERawTweetSearchResultType.TOP : ERawTweetSearchResultType.LATEST,
454
+ product: parsedFilter.top ? RawTweetSearchResultType.TOP : RawTweetSearchResultType.LATEST,
455
455
  withAuxiliaryUserLabels: false,
456
456
  withArticleRichContentState: false,
457
457
  withArticlePlainText: false,
@@ -2,7 +2,7 @@ import qs from 'querystring';
2
2
 
3
3
  import { AxiosRequestConfig } from 'axios';
4
4
 
5
- import { ERawAnalyticsGranularity, ERawAnalyticsMetric } from '../enums/raw/Analytics';
5
+ import { RawAnalyticsGranularity, RawAnalyticsMetric } from '../enums/raw/Analytics';
6
6
 
7
7
  /**
8
8
  * Collection of requests related to users.
@@ -77,8 +77,8 @@ export class UserRequests {
77
77
  public static analytics(
78
78
  fromTime: Date,
79
79
  toTime: Date,
80
- granularity: ERawAnalyticsGranularity,
81
- requestedMetrics: ERawAnalyticsMetric[],
80
+ granularity: RawAnalyticsGranularity,
81
+ requestedMetrics: RawAnalyticsMetric[],
82
82
  ): AxiosRequestConfig {
83
83
  return {
84
84
  method: 'get',
@@ -1,6 +1,6 @@
1
1
  import axios from 'axios';
2
2
 
3
- import { EApiErrors } from '../../enums/Api';
3
+ import { ApiErrors } from '../../enums/Api';
4
4
  import { AuthCredential } from '../../models/auth/AuthCredential';
5
5
  import { RettiwtConfig } from '../../models/RettiwtConfig';
6
6
 
@@ -67,7 +67,7 @@ export class AuthService {
67
67
  }
68
68
  // If user id was not found
69
69
  else {
70
- throw new Error(EApiErrors.BAD_AUTHENTICATION);
70
+ throw new Error(ApiErrors.BAD_AUTHENTICATION);
71
71
  }
72
72
  }
73
73
 
@@ -15,14 +15,14 @@ export class ErrorService implements IErrorHandler {
15
15
  *
16
16
  * @param error - The error response received from Twitter.
17
17
  */
18
- private handleAxiosError(error: AxiosError<IRawErrorData | IRawErrorDetails>): void {
18
+ private _handleAxiosError(error: AxiosError<IRawErrorData | IRawErrorDetails>): void {
19
19
  throw new TwitterError(error);
20
20
  }
21
21
 
22
22
  /**
23
23
  * Handle unknown error.
24
24
  */
25
- private handleUnknownError(): void {
25
+ private _handleUnknownError(): void {
26
26
  throw new Error('Unknown error');
27
27
  }
28
28
 
@@ -33,9 +33,9 @@ export class ErrorService implements IErrorHandler {
33
33
  */
34
34
  public handle(error: unknown): void {
35
35
  if (isAxiosError(error)) {
36
- this.handleAxiosError(error as AxiosError<IRawErrorData | IRawErrorDetails>);
36
+ this._handleAxiosError(error as AxiosError<IRawErrorData | IRawErrorDetails>);
37
37
  } else {
38
- this.handleUnknownError();
38
+ this._handleUnknownError();
39
39
  }
40
40
  }
41
41
  }
@@ -1,4 +1,4 @@
1
- import { ELogActions } from '../../enums/Logging';
1
+ import { LogActions } from '../../enums/Logging';
2
2
 
3
3
  /**
4
4
  * Handles logging of data for debug purpose.
@@ -16,7 +16,7 @@ export class LogService {
16
16
  *
17
17
  * @param data - The data to be logged.
18
18
  */
19
- public static log(action: ELogActions, data: NonNullable<unknown>): void {
19
+ public static log(action: LogActions, data: NonNullable<unknown>): void {
20
20
  // Proceed to log only if logging is enabled
21
21
  if (this.enabled) {
22
22
  // Preparing the log message
@@ -1,7 +1,7 @@
1
1
  import axios from 'axios';
2
2
  import * as htmlParser from 'node-html-parser';
3
3
 
4
- import { ELogActions } from '../../enums/Logging';
4
+ import { LogActions } from '../../enums/Logging';
5
5
 
6
6
  import { calculateClientTransactionIdHeader } from '../../helper/TidUtils';
7
7
 
@@ -34,16 +34,16 @@ export class TidService implements ITidProvider {
34
34
  *
35
35
  * @returns The new dynamic args.
36
36
  */
37
- private async getDynamicArgs(): Promise<ITidDynamicArgs> {
38
- const html = await this.getHomepageHtml();
37
+ private async _getDynamicArgs(): Promise<ITidDynamicArgs> {
38
+ const html = await this._getHomepageHtml();
39
39
  const root = htmlParser.parse(html);
40
40
  const keyElement = root.querySelector("[name='twitter-site-verification']");
41
41
  const frameElements = root.querySelectorAll("[id^='loading-x-anim']");
42
42
 
43
43
  return {
44
44
  verificationKey: keyElement?.getAttribute('content') ?? '',
45
- frames: frameElements.map((el) => this.parseFrameElement(el)),
46
- indices: await this.getKeyBytesIndices(html),
45
+ frames: frameElements.map((el) => this._parseFrameElement(el)),
46
+ indices: await this._getKeyBytesIndices(html),
47
47
  };
48
48
  }
49
49
 
@@ -52,7 +52,7 @@ export class TidService implements ITidProvider {
52
52
  *
53
53
  * @returns The stringified HTML content of the homepage.
54
54
  */
55
- private async getHomepageHtml(): Promise<string> {
55
+ private async _getHomepageHtml(): Promise<string> {
56
56
  const response = await axios.get<string>('https://x.com', {
57
57
  headers: this._config.headers,
58
58
  httpAgent: this._config.httpsAgent,
@@ -62,10 +62,10 @@ export class TidService implements ITidProvider {
62
62
  return response.data;
63
63
  }
64
64
 
65
- private async getKeyBytesIndices(html: string): Promise<number[]> {
65
+ private async _getKeyBytesIndices(html: string): Promise<number[]> {
66
66
  const ondemandFileMatch = html.match(/ondemand\.s":"([^"]+)"/);
67
67
  if (!ondemandFileMatch || !ondemandFileMatch[1]) {
68
- LogService.log(ELogActions.WARNING, { message: 'ondemand.s file not found' });
68
+ LogService.log(LogActions.WARNING, { message: 'ondemand.s file not found' });
69
69
 
70
70
  return [0, 0, 0, 0];
71
71
  }
@@ -80,7 +80,7 @@ export class TidService implements ITidProvider {
80
80
  return Array.from(match).map((m) => Number(m[2]));
81
81
  }
82
82
 
83
- private parseFrameElement(element: htmlParser.HTMLElement): number[][] {
83
+ private _parseFrameElement(element: htmlParser.HTMLElement): number[][] {
84
84
  const pathElement = element.children[0].children[1];
85
85
  const value = pathElement.getAttribute('d');
86
86
  if (!value) {
@@ -122,7 +122,7 @@ export class TidService implements ITidProvider {
122
122
  extraByte: 3,
123
123
  });
124
124
  } catch (err) {
125
- LogService.log(ELogActions.WARNING, {
125
+ LogService.log(LogActions.WARNING, {
126
126
  message: 'Failed to generated transaction token. Request may or may not work',
127
127
  error: err,
128
128
  });
@@ -135,6 +135,6 @@ export class TidService implements ITidProvider {
135
135
  * Refreshes the dynamic args from the homepage.
136
136
  */
137
137
  public async refreshDynamicArgs(): Promise<void> {
138
- this._dynamicArgs = await this.getDynamicArgs();
138
+ this._dynamicArgs = await this._getDynamicArgs();
139
139
  }
140
140
  }
@@ -1,11 +1,11 @@
1
- import axios from 'axios';
1
+ import axios, { isAxiosError } from 'axios';
2
2
  import { Cookie } from 'cookiejar';
3
3
 
4
- import { allowGuestAuthentication, fetchResources, postResources } from '../../collections/Groups';
5
- import { requests } from '../../collections/Requests';
6
- import { EApiErrors } from '../../enums/Api';
7
- import { ELogActions } from '../../enums/Logging';
8
- import { EResourceType } from '../../enums/Resource';
4
+ import { AllowGuestAuthenticationGroup, FetchResourcesGroup, PostResourcesGroup } from '../../collections/Groups';
5
+ import { Requests } from '../../collections/Requests';
6
+ import { ApiErrors } from '../../enums/Api';
7
+ import { LogActions } from '../../enums/Logging';
8
+ import { ResourceType } from '../../enums/Resource';
9
9
  import { FetchArgs } from '../../models/args/FetchArgs';
10
10
  import { PostArgs } from '../../models/args/PostArgs';
11
11
  import { AuthCredential } from '../../models/auth/AuthCredential';
@@ -65,13 +65,13 @@ export class FetcherService {
65
65
  *
66
66
  * @throws An error if not authorized to access the requested resource.
67
67
  */
68
- private checkAuthorization(resource: EResourceType): void {
68
+ private _checkAuthorization(resource: ResourceType): void {
69
69
  // Logging
70
- LogService.log(ELogActions.AUTHORIZATION, { authenticated: this.config.userId != undefined });
70
+ LogService.log(LogActions.AUTHORIZATION, { authenticated: this.config.userId != undefined });
71
71
 
72
72
  // Checking authorization status
73
- if (!allowGuestAuthentication.includes(resource) && this.config.userId == undefined) {
74
- throw new Error(EApiErrors.RESOURCE_NOT_ALLOWED);
73
+ if (!AllowGuestAuthenticationGroup.includes(resource) && this.config.userId == undefined) {
74
+ throw new Error(ApiErrors.RESOURCE_NOT_ALLOWED);
75
75
  }
76
76
  }
77
77
 
@@ -80,10 +80,10 @@ export class FetcherService {
80
80
  *
81
81
  * @returns The generated AuthCredential
82
82
  */
83
- private async getCredential(): Promise<AuthCredential> {
83
+ private async _getCredential(): Promise<AuthCredential> {
84
84
  if (this.config.apiKey) {
85
85
  // Logging
86
- LogService.log(ELogActions.GET, { target: 'USER_CREDENTIAL' });
86
+ LogService.log(LogActions.GET, { target: 'USER_CREDENTIAL' });
87
87
 
88
88
  return new AuthCredential(
89
89
  AuthService.decodeCookie(this.config.apiKey)
@@ -92,7 +92,7 @@ export class FetcherService {
92
92
  );
93
93
  } else {
94
94
  // Logging
95
- LogService.log(ELogActions.GET, { target: 'NEW_GUEST_CREDENTIAL' });
95
+ LogService.log(LogActions.GET, { target: 'NEW_GUEST_CREDENTIAL' });
96
96
 
97
97
  return this._auth.guest();
98
98
  }
@@ -106,7 +106,7 @@ export class FetcherService {
106
106
  *
107
107
  * @returns The header containing the transaction ID.
108
108
  */
109
- private async getTransactionHeader(method: string, url: string): Promise<ITidHeader | undefined> {
109
+ private async _getTransactionHeader(method: string, url: string): Promise<ITidHeader | undefined> {
110
110
  // Getting the URL path excluding all params
111
111
  const path = new URL(url).pathname.split('?')[0].trim();
112
112
 
@@ -132,15 +132,15 @@ export class FetcherService {
132
132
  *
133
133
  * @returns The validated args.
134
134
  */
135
- private validateArgs(resource: EResourceType, args: IFetchArgs | IPostArgs): FetchArgs | PostArgs | undefined {
136
- if (fetchResources.includes(resource)) {
135
+ private _validateArgs(resource: ResourceType, args: IFetchArgs | IPostArgs): FetchArgs | PostArgs | undefined {
136
+ if (FetchResourcesGroup.includes(resource)) {
137
137
  // Logging
138
- LogService.log(ELogActions.VALIDATE, { target: 'FETCH_ARGS' });
138
+ LogService.log(LogActions.VALIDATE, { target: 'FETCH_ARGS' });
139
139
 
140
140
  return new FetchArgs(args);
141
- } else if (postResources.includes(resource)) {
141
+ } else if (PostResourcesGroup.includes(resource)) {
142
142
  // Logging
143
- LogService.log(ELogActions.VALIDATE, { target: 'POST_ARGS' });
143
+ LogService.log(LogActions.VALIDATE, { target: 'POST_ARGS' });
144
144
 
145
145
  return new PostArgs(args);
146
146
  }
@@ -149,7 +149,7 @@ export class FetcherService {
149
149
  /**
150
150
  * Introduces a delay using the configured delay/delay function.
151
151
  */
152
- private async wait(): Promise<void> {
152
+ private async _wait(): Promise<void> {
153
153
  // If no delay is set, skip
154
154
  if (this._delay == undefined) {
155
155
  return;
@@ -198,44 +198,60 @@ export class FetcherService {
198
198
  * });
199
199
  * ```
200
200
  */
201
- public async request<T = unknown>(resource: EResourceType, args: IFetchArgs | IPostArgs): Promise<T> {
201
+ public async request<T = unknown>(resource: ResourceType, args: IFetchArgs | IPostArgs): Promise<T> {
202
+ /** The error, if any. */
203
+ let error: unknown = undefined;
204
+
202
205
  // Logging
203
- LogService.log(ELogActions.REQUEST, { resource: resource, args: args });
206
+ LogService.log(LogActions.REQUEST, { resource: resource, args: args });
204
207
 
205
208
  // Checking authorization for the requested resource
206
- this.checkAuthorization(resource);
209
+ this._checkAuthorization(resource);
207
210
 
208
211
  // Validating args
209
- args = this.validateArgs(resource, args)!;
212
+ args = this._validateArgs(resource, args)!;
210
213
 
211
214
  // Getting credentials from key
212
- const cred: AuthCredential = await this.getCredential();
215
+ const cred: AuthCredential = await this._getCredential();
213
216
 
214
217
  // Getting request configuration
215
- const config = requests[resource](args);
218
+ const config = Requests[resource](args);
216
219
 
217
220
  // Setting additional request parameters
218
221
  config.headers = {
219
222
  ...config.headers,
220
223
  ...cred.toHeader(),
221
- ...(await this.getTransactionHeader(config.method ?? '', config.url ?? '')),
224
+ ...(await this._getTransactionHeader(config.method ?? '', config.url ?? '')),
222
225
  ...this.config.headers,
223
226
  };
224
227
  config.httpAgent = this.config.httpsAgent;
225
228
  config.httpsAgent = this.config.httpsAgent;
226
229
  config.timeout = this._timeout;
227
230
 
228
- // Sending the request
229
- try {
230
- // Introducing a delay
231
- await this.wait();
232
-
233
- // Returning the reponse body
234
- return (await axios<T>(config)).data;
235
- } catch (error) {
236
- // If error, delegate handling to error handler
237
- this._errorHandler.handle(error);
238
- throw error;
231
+ // Using retries for error 404
232
+ for (let retry = 1; retry <= this.config.maxRetries; retry++) {
233
+ // Sending the request
234
+ try {
235
+ // Introducing a delay
236
+ await this._wait();
237
+
238
+ // Returning the reponse body
239
+ return (await axios<T>(config)).data;
240
+ } catch (err) {
241
+ // If it's an error 404, retry
242
+ if (isAxiosError(err) && err.status === 404) {
243
+ error = err;
244
+ continue;
245
+ }
246
+ // Else, delegate error handling
247
+ else {
248
+ this._errorHandler.handle(err);
249
+ throw err;
250
+ }
251
+ }
239
252
  }
253
+
254
+ /** If request not successful even after retries, throw the error */
255
+ throw error;
240
256
  }
241
257
  }
@@ -1,5 +1,5 @@
1
- import { extractors } from '../../collections/Extractors';
2
- import { EResourceType } from '../../enums/Resource';
1
+ import { Extractors } from '../../collections/Extractors';
2
+ import { ResourceType } from '../../enums/Resource';
3
3
  import { CursoredData } from '../../models/data/CursoredData';
4
4
  import { Tweet } from '../../models/data/Tweet';
5
5
  import { User } from '../../models/data/User';
@@ -49,7 +49,7 @@ export class ListService extends FetcherService {
49
49
  * @remarks Due a bug in Twitter API, the count is ignored when no cursor is provided and defaults to 100.
50
50
  */
51
51
  public async members(id: string, count?: number, cursor?: string): Promise<CursoredData<User>> {
52
- const resource: EResourceType = EResourceType.LIST_MEMBERS;
52
+ const resource: ResourceType = ResourceType.LIST_MEMBERS;
53
53
 
54
54
  // Fetching the raw list of members
55
55
  const response = await this.request<IListMembersResponse>(resource, {
@@ -59,7 +59,7 @@ export class ListService extends FetcherService {
59
59
  });
60
60
 
61
61
  // Deserializing response
62
- const data = extractors[resource](response);
62
+ const data = Extractors[resource](response);
63
63
 
64
64
  return data;
65
65
  }
@@ -94,7 +94,7 @@ export class ListService extends FetcherService {
94
94
  * @remarks Due a bug in Twitter API, the count is ignored when no cursor is provided and defaults to 100.
95
95
  */
96
96
  public async tweets(id: string, count?: number, cursor?: string): Promise<CursoredData<Tweet>> {
97
- const resource = EResourceType.LIST_TWEETS;
97
+ const resource = ResourceType.LIST_TWEETS;
98
98
 
99
99
  // Fetching raw list tweets
100
100
  const response = await this.request<IListTweetsResponse>(resource, {
@@ -104,7 +104,7 @@ export class ListService extends FetcherService {
104
104
  });
105
105
 
106
106
  // Deserializing response
107
- const data = extractors[resource](response);
107
+ const data = Extractors[resource](response);
108
108
 
109
109
  // Sorting the tweets by date, from recent to oldest
110
110
  data.list.sort((a, b) => new Date(b.createdAt).valueOf() - new Date(a.createdAt).valueOf());