rettiwt-api 4.2.0-alpha.1 → 4.2.0-alpha.2

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 (44) hide show
  1. package/.eslintrc.js +35 -0
  2. package/dist/Rettiwt.d.ts +3 -0
  3. package/dist/Rettiwt.js +2 -0
  4. package/dist/Rettiwt.js.map +1 -1
  5. package/dist/cli.js +2 -0
  6. package/dist/cli.js.map +1 -1
  7. package/dist/collections/Extractors.d.ts +2 -1
  8. package/dist/collections/Extractors.js +3 -0
  9. package/dist/collections/Extractors.js.map +1 -1
  10. package/dist/collections/Groups.js +1 -0
  11. package/dist/collections/Groups.js.map +1 -1
  12. package/dist/collections/Requests.js +1 -0
  13. package/dist/collections/Requests.js.map +1 -1
  14. package/dist/commands/List.d.ts +10 -0
  15. package/dist/commands/List.js +104 -0
  16. package/dist/commands/List.js.map +1 -0
  17. package/dist/enums/Resource.d.ts +1 -0
  18. package/dist/enums/Resource.js +1 -0
  19. package/dist/enums/Resource.js.map +1 -1
  20. package/dist/index.d.ts +2 -2
  21. package/dist/models/args/FetchArgs.js +11 -2
  22. package/dist/models/args/FetchArgs.js.map +1 -1
  23. package/dist/models/data/Tweet.d.ts +18 -0
  24. package/dist/models/data/Tweet.js +45 -5
  25. package/dist/models/data/Tweet.js.map +1 -1
  26. package/dist/services/public/FetcherService.d.ts +5 -5
  27. package/dist/services/public/FetcherService.js +12 -12
  28. package/dist/services/public/FetcherService.js.map +1 -1
  29. package/dist/services/public/ListService.d.ts +71 -0
  30. package/dist/services/public/ListService.js +169 -0
  31. package/dist/services/public/ListService.js.map +1 -0
  32. package/package.json +2 -2
  33. package/src/Rettiwt.ts +5 -0
  34. package/src/cli.ts +2 -0
  35. package/src/collections/Extractors.ts +3 -0
  36. package/src/collections/Groups.ts +1 -0
  37. package/src/collections/Requests.ts +1 -0
  38. package/src/commands/List.ts +49 -0
  39. package/src/enums/Resource.ts +1 -0
  40. package/src/index.ts +3 -0
  41. package/src/models/args/FetchArgs.ts +11 -1
  42. package/src/models/data/Tweet.ts +52 -5
  43. package/src/services/public/FetcherService.ts +17 -17
  44. package/src/services/public/ListService.ts +112 -0
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  EMediaType,
3
+ ILimitedVisibilityTweet,
3
4
  IExtendedMedia as IRawExtendedMedia,
4
5
  ITweet as IRawTweet,
5
6
  IEntities as IRawTweetEntities,
@@ -77,7 +78,7 @@ export class Tweet {
77
78
  this.tweetBy = new User(tweet.core.user_results.result);
78
79
  this.entities = new TweetEntities(tweet.legacy.entities);
79
80
  this.media = tweet.legacy.extended_entities?.media?.map((media) => new TweetMedia(media));
80
- this.quoted = tweet.quoted_status_result ? new Tweet(tweet.quoted_status_result.result) : undefined;
81
+ this.quoted = this.getQuotedTweet(tweet);
81
82
  this.fullText = tweet.note_tweet ? tweet.note_tweet.note_tweet_results.result.text : tweet.legacy.full_text;
82
83
  this.replyTo = tweet.legacy.in_reply_to_status_id_str;
83
84
  this.lang = tweet.legacy.lang;
@@ -87,9 +88,51 @@ export class Tweet {
87
88
  this.likeCount = tweet.legacy.favorite_count;
88
89
  this.viewCount = tweet.views.count ? parseInt(tweet.views.count) : 0;
89
90
  this.bookmarkCount = tweet.legacy.bookmark_count;
90
- this.retweetedTweet = tweet.legacy.retweeted_status_result?.result?.rest_id
91
- ? new Tweet(tweet.legacy.retweeted_status_result.result)
92
- : undefined;
91
+ this.retweetedTweet = this.getRetweetedTweet(tweet);
92
+ }
93
+
94
+ /**
95
+ * Extract and deserialize the original quoted tweet from the given raw tweet.
96
+ *
97
+ * @param tweet - The raw tweet.
98
+ *
99
+ * @returns - The deserialized original quoted tweet.
100
+ */
101
+ private getQuotedTweet(tweet: IRawTweet): Tweet | undefined {
102
+ // If tweet with limited visibility
103
+ if (
104
+ tweet.quoted_status_result &&
105
+ Object.entries(tweet.quoted_status_result).length &&
106
+ tweet.quoted_status_result.result.__typename == 'TweetWithVisibilityResults'
107
+ ) {
108
+ return new Tweet((tweet.quoted_status_result.result as ILimitedVisibilityTweet).tweet);
109
+ }
110
+ // If normal tweet
111
+ else if (tweet.quoted_status_result && Object.entries(tweet.quoted_status_result).length) {
112
+ return new Tweet(tweet.quoted_status_result.result as ITweet);
113
+ }
114
+ // Else, skip
115
+ else {
116
+ return undefined;
117
+ }
118
+ }
119
+
120
+ /**
121
+ * Extract and deserialize the original retweeted tweet from the given raw tweet.
122
+ *
123
+ * @param tweet - The raw tweet.
124
+ *
125
+ * @returns - The deserialized original retweeted tweet.
126
+ */
127
+ private getRetweetedTweet(tweet: IRawTweet): Tweet | undefined {
128
+ // If valid retweeted tweet
129
+ if (tweet.legacy.retweeted_status_result?.result?.rest_id) {
130
+ return new Tweet(tweet.legacy.retweeted_status_result.result);
131
+ }
132
+ // Else, skip
133
+ else {
134
+ return undefined;
135
+ }
93
136
  }
94
137
 
95
138
  /**
@@ -112,7 +155,6 @@ export class Tweet {
112
155
  if (item.tweet_results?.result?.legacy) {
113
156
  // Logging
114
157
  LogService.log(ELogActions.DESERIALIZE, { id: item.tweet_results.result.rest_id });
115
-
116
158
  tweets.push(new Tweet(item.tweet_results.result));
117
159
  } else {
118
160
  // Logging
@@ -210,6 +252,9 @@ export class TweetEntities {
210
252
  * @public
211
253
  */
212
254
  export class TweetMedia {
255
+ /** The thumbnail URL for the video content of the tweet. */
256
+ public thumbnailUrl?: string;
257
+
213
258
  /** The type of media. */
214
259
  public type: EMediaType;
215
260
 
@@ -232,6 +277,8 @@ export class TweetMedia {
232
277
  }
233
278
  // If the media is a video
234
279
  else {
280
+ this.thumbnailUrl = media.media_url_https;
281
+
235
282
  /** The highest bitrate of all variants. */
236
283
  let highestRate: number = 0;
237
284
 
@@ -26,19 +26,19 @@ import { AuthService } from './AuthService';
26
26
  */
27
27
  export class FetcherService {
28
28
  /** The api key to use for authenticating against Twitter API as user. */
29
- private readonly apiKey?: string;
29
+ private readonly _apiKey?: string;
30
30
 
31
31
  /** The service used to handle HTTP and API errors */
32
- private readonly errorHandler: IErrorHandler;
32
+ private readonly _errorHandler: IErrorHandler;
33
33
 
34
34
  /** The guest key to use for authenticating against Twitter API as guest. */
35
- private readonly guestKey?: string;
35
+ private readonly _guestKey?: string;
36
36
 
37
37
  /** The URL To the proxy server to use for all others. */
38
- private readonly proxyUrl?: URL;
38
+ private readonly _proxyUrl?: URL;
39
39
 
40
40
  /** The max wait time for a response. */
41
- private readonly timeout: number;
41
+ private readonly _timeout: number;
42
42
 
43
43
  /** The URL to the proxy server to use only for authentication. */
44
44
  protected readonly authProxyUrl?: URL;
@@ -51,13 +51,13 @@ export class FetcherService {
51
51
  */
52
52
  public constructor(config?: IRettiwtConfig) {
53
53
  LogService.enabled = config?.logging ?? false;
54
- this.apiKey = config?.apiKey;
55
- this.guestKey = config?.guestKey;
54
+ this._apiKey = config?.apiKey;
55
+ this._guestKey = config?.guestKey;
56
56
  this.userId = config?.apiKey ? AuthService.getUserId(config.apiKey) : undefined;
57
57
  this.authProxyUrl = config?.authProxyUrl ?? config?.proxyUrl;
58
- this.proxyUrl = config?.proxyUrl;
59
- this.timeout = config?.timeout ?? 0;
60
- this.errorHandler = config?.errorHandler ?? new ErrorService();
58
+ this._proxyUrl = config?.proxyUrl;
59
+ this._timeout = config?.timeout ?? 0;
60
+ this._errorHandler = config?.errorHandler ?? new ErrorService();
61
61
  }
62
62
 
63
63
  /**
@@ -83,16 +83,16 @@ export class FetcherService {
83
83
  * @returns The generated AuthCredential
84
84
  */
85
85
  private async getCredential(): Promise<AuthCredential> {
86
- if (this.apiKey) {
86
+ if (this._apiKey) {
87
87
  // Logging
88
88
  LogService.log(ELogActions.GET, { target: 'USER_CREDENTIAL' });
89
89
 
90
- return new AuthCredential(AuthService.decodeCookie(this.apiKey).split(';'));
91
- } else if (this.guestKey) {
90
+ return new AuthCredential(AuthService.decodeCookie(this._apiKey).split(';'));
91
+ } else if (this._guestKey) {
92
92
  // Logging
93
93
  LogService.log(ELogActions.GET, { target: 'GUEST_CREDENTIAL' });
94
94
 
95
- return new AuthCredential(undefined, this.guestKey);
95
+ return new AuthCredential(undefined, this._guestKey);
96
96
  } else {
97
97
  // Logging
98
98
  LogService.log(ELogActions.GET, { target: 'NEW_GUEST_CREDENTIAL' });
@@ -183,7 +183,7 @@ export class FetcherService {
183
183
  args = this.validateArgs(resource, args)!;
184
184
 
185
185
  // Getting HTTPS agent
186
- const httpsAgent: Agent = this.getHttpsAgent(this.proxyUrl);
186
+ const httpsAgent: Agent = this.getHttpsAgent(this._proxyUrl);
187
187
 
188
188
  // Getting credentials from key
189
189
  const cred: AuthCredential = await this.getCredential();
@@ -195,7 +195,7 @@ export class FetcherService {
195
195
  config.headers = { ...config.headers, ...cred.toHeader() };
196
196
  config.httpAgent = httpsAgent;
197
197
  config.httpsAgent = httpsAgent;
198
- config.timeout = this.timeout;
198
+ config.timeout = this._timeout;
199
199
 
200
200
  // Sending the request
201
201
  try {
@@ -203,7 +203,7 @@ export class FetcherService {
203
203
  return (await axios<T>(config)).data;
204
204
  } catch (error) {
205
205
  // If error, delegate handling to error handler
206
- this.errorHandler.handle(error);
206
+ this._errorHandler.handle(error);
207
207
  throw error;
208
208
  }
209
209
  }
@@ -0,0 +1,112 @@
1
+ import { IListMembersResponse, IListTweetsResponse } from 'rettiwt-core';
2
+
3
+ import { extractors } from '../../collections/Extractors';
4
+ import { EResourceType } from '../../enums/Resource';
5
+ import { CursoredData } from '../../models/data/CursoredData';
6
+ import { Tweet } from '../../models/data/Tweet';
7
+ import { User } from '../../models/data/User';
8
+ import { IRettiwtConfig } from '../../types/RettiwtConfig';
9
+
10
+ import { FetcherService } from './FetcherService';
11
+
12
+ export class ListService extends FetcherService {
13
+ /**
14
+ * @param config - The config object for configuring the Rettiwt instance.
15
+ *
16
+ * @internal
17
+ */
18
+ public constructor(config?: IRettiwtConfig) {
19
+ super(config);
20
+ }
21
+
22
+ /**
23
+ * Get the list of members of a tweet list.
24
+ *
25
+ * @param id - The id of target list.
26
+ * @param count - The number of members to fetch, must be \<= 100.
27
+ * @param cursor - The cursor to the batch of members to fetch.
28
+ *
29
+ * @returns The list tweets in the given list.
30
+ *
31
+ * @example
32
+ * ```
33
+ * import { Rettiwt } from 'rettiwt-api';
34
+ *
35
+ * // Creating a new Rettiwt instance using the given 'API_KEY'
36
+ * const rettiwt = new Rettiwt({ apiKey: API_KEY });
37
+ *
38
+ * // Fetching the first 100 members of the Twitter list with id '1234567890'
39
+ * rettiwt.list.members('1234567890')
40
+ * .then(res => {
41
+ * console.log(res);
42
+ * })
43
+ * .catch(err => {
44
+ * console.log(err);
45
+ * });
46
+ * ```
47
+ *
48
+ * @remarks Due a bug in Twitter API, the count is ignored when no cursor is provided and defaults to 100.
49
+ */
50
+ public async members(id: string, count?: number, cursor?: string): Promise<CursoredData<User>> {
51
+ const resource: EResourceType = EResourceType.LIST_MEMBERS;
52
+
53
+ // Fetching the raw list of members
54
+ const response = await this.request<IListMembersResponse>(resource, {
55
+ id: id,
56
+ count: count,
57
+ cursor: cursor,
58
+ });
59
+
60
+ // Deserializing response
61
+ const data = extractors[resource](response);
62
+
63
+ return data;
64
+ }
65
+
66
+ /**
67
+ * Get the list of tweets from a tweet list.
68
+ *
69
+ * @param id - The id of target list.
70
+ * @param count - The number of tweets to fetch, must be \<= 100.
71
+ * @param cursor - The cursor to the batch of tweets to fetch.
72
+ *
73
+ * @returns The list tweets in the given list.
74
+ *
75
+ * @example
76
+ * ```
77
+ * import { Rettiwt } from 'rettiwt-api';
78
+ *
79
+ * // Creating a new Rettiwt instance using the given 'API_KEY'
80
+ * const rettiwt = new Rettiwt({ apiKey: API_KEY });
81
+ *
82
+ * // Fetching the most recent 100 tweets of the Twitter list with id '1234567890'
83
+ * rettiwt.list.tweets('1234567890')
84
+ * .then(res => {
85
+ * console.log(res);
86
+ * })
87
+ * .catch(err => {
88
+ * console.log(err);
89
+ * });
90
+ * ```
91
+ *
92
+ * @remarks Due a bug in Twitter API, the count is ignored when no cursor is provided and defaults to 100.
93
+ */
94
+ public async tweets(id: string, count?: number, cursor?: string): Promise<CursoredData<Tweet>> {
95
+ const resource = EResourceType.LIST_TWEETS;
96
+
97
+ // Fetching raw list tweets
98
+ const response = await this.request<IListTweetsResponse>(resource, {
99
+ id: id,
100
+ count: count,
101
+ cursor: cursor,
102
+ });
103
+
104
+ // Deserializing response
105
+ const data = extractors[resource](response);
106
+
107
+ // Sorting the tweets by date, from recent to oldest
108
+ data.list.sort((a, b) => new Date(b.createdAt).valueOf() - new Date(a.createdAt).valueOf());
109
+
110
+ return data;
111
+ }
112
+ }