rettiwt-api 4.2.0-alpha.2 → 5.0.0-alpha.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 (50) hide show
  1. package/.github/workflows/documentation.yml +1 -4
  2. package/.github/workflows/publish-alpha.yml +1 -4
  3. package/.github/workflows/publish.yml +1 -4
  4. package/.tool-versions +1 -1
  5. package/README.md +14 -8
  6. package/dist/collections/Extractors.d.ts +2 -1
  7. package/dist/collections/Extractors.js +3 -0
  8. package/dist/collections/Extractors.js.map +1 -1
  9. package/dist/collections/Groups.js +1 -0
  10. package/dist/collections/Groups.js.map +1 -1
  11. package/dist/collections/Requests.js +2 -1
  12. package/dist/collections/Requests.js.map +1 -1
  13. package/dist/commands/Tweet.js +2 -1
  14. package/dist/commands/Tweet.js.map +1 -1
  15. package/dist/commands/User.js +62 -39
  16. package/dist/commands/User.js.map +1 -1
  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 +1 -2
  21. package/dist/index.js +0 -3
  22. package/dist/index.js.map +1 -1
  23. package/dist/models/args/FetchArgs.d.ts +6 -0
  24. package/dist/models/args/FetchArgs.js +13 -0
  25. package/dist/models/args/FetchArgs.js.map +1 -1
  26. package/dist/models/data/Tweet.d.ts +6 -2
  27. package/dist/models/data/Tweet.js +48 -19
  28. package/dist/models/data/Tweet.js.map +1 -1
  29. package/dist/models/data/User.js +1 -1
  30. package/dist/models/data/User.js.map +1 -1
  31. package/dist/services/public/AuthService.js +1 -1
  32. package/dist/services/public/AuthService.js.map +1 -1
  33. package/dist/services/public/UserService.d.ts +26 -0
  34. package/dist/services/public/UserService.js +44 -0
  35. package/dist/services/public/UserService.js.map +1 -1
  36. package/package.json +3 -4
  37. package/src/collections/Extractors.ts +4 -1
  38. package/src/collections/Groups.ts +1 -0
  39. package/src/collections/Requests.ts +2 -1
  40. package/src/commands/Tweet.ts +2 -2
  41. package/src/commands/User.ts +13 -0
  42. package/src/enums/Resource.ts +1 -0
  43. package/src/index.ts +1 -2
  44. package/src/models/args/FetchArgs.ts +17 -0
  45. package/src/models/data/Notification.ts +1 -1
  46. package/src/models/data/Tweet.ts +56 -20
  47. package/src/models/data/User.ts +1 -1
  48. package/src/services/public/AuthService.ts +3 -1
  49. package/src/services/public/UserService.ts +42 -1
  50. package/.yarnrc.yml +0 -1
@@ -1,5 +1,5 @@
1
1
  import { Command, createCommand } from 'commander';
2
- import { ESearchResultType, TweetFilter } from 'rettiwt-core';
2
+ import { TweetFilter } from 'rettiwt-core';
3
3
 
4
4
  import { output } from '../helper/CliUtils';
5
5
  import { Rettiwt } from '../Rettiwt';
@@ -192,7 +192,6 @@ function createTweetCommand(rettiwt: Rettiwt): Command {
192
192
  new TweetSearchOptions(options).toTweetFilter(),
193
193
  count ? parseInt(count) : undefined,
194
194
  cursor,
195
- options?.top ? ESearchResultType.TOP : ESearchResultType.LATEST,
196
195
  );
197
196
  output(tweets);
198
197
  }
@@ -352,6 +351,7 @@ class TweetSearchOptions {
352
351
  links: !this.excludeLinks,
353
352
  replies: !this.excludeReplies,
354
353
  startDate: this.start ? new Date(this.start) : undefined,
354
+ top: this.top,
355
355
  endDate: this.end ? new Date(this.end) : undefined,
356
356
  });
357
357
  }
@@ -13,6 +13,19 @@ function createUserCommand(rettiwt: Rettiwt): Command {
13
13
  // Creating the 'user' command
14
14
  const user = createCommand('user').description('Access resources releated to users');
15
15
 
16
+ user.command('bookmarks')
17
+ .description('Fetch your list of bookmarks')
18
+ .argument('[count]', 'The number of bookmarks to fetch')
19
+ .argument('[cursor]', 'The cursor to the batch of bookmarks to fetch')
20
+ .action(async (count?: string, cursor?: string) => {
21
+ try {
22
+ const bookmarks = await rettiwt.user.bookmarks(count ? parseInt(count) : undefined, cursor);
23
+ output(bookmarks);
24
+ } catch (error) {
25
+ output(error);
26
+ }
27
+ });
28
+
16
29
  // Details
17
30
  user.command('details')
18
31
  .description('Fetch the details of the user with the given id/username')
@@ -28,6 +28,7 @@ export enum EResourceType {
28
28
  TWEET_UNSCHEDULE = 'TWEET_UNSCHEDULE',
29
29
 
30
30
  // USER
31
+ USER_BOOKMARKS = 'USER_BOOKMARKS',
31
32
  USER_DETAILS_BY_USERNAME = 'USER_DETAILS_BY_USERNAME',
32
33
  USER_DETAILS_BY_ID = 'USER_DETAILS_BY_ID',
33
34
  USER_FEED_FOLLOWED = 'USER_FEED_FOLLOWED',
package/src/index.ts CHANGED
@@ -5,7 +5,6 @@ export * from './Rettiwt';
5
5
  export * from './enums/Api';
6
6
  export * from './enums/Http';
7
7
  export * from './enums/Resource';
8
- export { ESearchResultType } from 'rettiwt-core';
9
8
 
10
9
  // ARG MODELS
11
10
  export * from './models/args/FetchArgs';
@@ -63,7 +62,7 @@ export {
63
62
  IUserHighlightsResponse,
64
63
  IUserLikesResponse,
65
64
  IUserMediaResponse,
66
- IUserNotifications as IUserNotificationsResponse,
65
+ IUserNotificationsResponse,
67
66
  IUserRecommendedResponse,
68
67
  IUserSubscriptionsResponse,
69
68
  IUserTweetsAndRepliesResponse,
@@ -56,6 +56,7 @@ export class FetchArgs {
56
56
  EResourceType.LIST_TWEETS,
57
57
  EResourceType.TWEET_RETWEETERS,
58
58
  EResourceType.TWEET_SEARCH,
59
+ EResourceType.USER_BOOKMARKS,
59
60
  EResourceType.USER_FOLLOWERS,
60
61
  EResourceType.USER_FOLLOWING,
61
62
  EResourceType.USER_HIGHLIGHTS,
@@ -73,6 +74,7 @@ export class FetchArgs {
73
74
  EResourceType.LIST_TWEETS,
74
75
  EResourceType.TWEET_RETWEETERS,
75
76
  EResourceType.TWEET_SEARCH,
77
+ EResourceType.USER_BOOKMARKS,
76
78
  EResourceType.USER_FOLLOWERS,
77
79
  EResourceType.USER_FOLLOWING,
78
80
  EResourceType.USER_HIGHLIGHTS,
@@ -89,6 +91,7 @@ export class FetchArgs {
89
91
  EResourceType.LIST_MEMBERS,
90
92
  EResourceType.LIST_TWEETS,
91
93
  EResourceType.TWEET_RETWEETERS,
94
+ EResourceType.USER_BOOKMARKS,
92
95
  EResourceType.USER_FOLLOWERS,
93
96
  EResourceType.USER_FOLLOWING,
94
97
  EResourceType.USER_HIGHLIGHTS,
@@ -126,6 +129,7 @@ export class FetchArgs {
126
129
  EResourceType.LIST_TWEETS,
127
130
  EResourceType.TWEET_RETWEETERS,
128
131
  EResourceType.TWEET_SEARCH,
132
+ EResourceType.USER_BOOKMARKS,
129
133
  EResourceType.USER_FEED_FOLLOWED,
130
134
  EResourceType.USER_FEED_RECOMMENDED,
131
135
  EResourceType.USER_FOLLOWING,
@@ -145,6 +149,7 @@ export class FetchArgs {
145
149
  EResourceType.LIST_TWEETS,
146
150
  EResourceType.TWEET_RETWEETERS,
147
151
  EResourceType.TWEET_SEARCH,
152
+ EResourceType.USER_BOOKMARKS,
148
153
  EResourceType.USER_FEED_FOLLOWED,
149
154
  EResourceType.USER_FEED_RECOMMENDED,
150
155
  EResourceType.USER_FOLLOWING,
@@ -173,6 +178,7 @@ export class FetchArgs {
173
178
  EResourceType.TWEET_DETAILS,
174
179
  EResourceType.TWEET_DETAILS_ALT,
175
180
  EResourceType.TWEET_RETWEETERS,
181
+ EResourceType.USER_BOOKMARKS,
176
182
  EResourceType.USER_DETAILS_BY_USERNAME,
177
183
  EResourceType.USER_DETAILS_BY_ID,
178
184
  EResourceType.USER_FEED_FOLLOWED,
@@ -201,6 +207,7 @@ export class FetchArgs {
201
207
  */
202
208
  @IsEmpty({
203
209
  groups: [
210
+ EResourceType.USER_BOOKMARKS,
204
211
  EResourceType.USER_FEED_FOLLOWED,
205
212
  EResourceType.USER_FEED_RECOMMENDED,
206
213
  EResourceType.USER_NOTIFICATIONS,
@@ -281,6 +288,7 @@ export class FetchArgs {
281
288
  EResourceType.TWEET_DETAILS,
282
289
  EResourceType.TWEET_DETAILS_ALT,
283
290
  EResourceType.TWEET_RETWEETERS,
291
+ EResourceType.USER_BOOKMARKS,
284
292
  EResourceType.USER_DETAILS_BY_USERNAME,
285
293
  EResourceType.USER_DETAILS_BY_ID,
286
294
  EResourceType.USER_FEED_FOLLOWED,
@@ -461,6 +469,15 @@ export class TweetFilter extends TweetFilterCore {
461
469
  @IsString({ each: true })
462
470
  public toUsers?: string[];
463
471
 
472
+ /**
473
+ * Whether to fetch top or not.
474
+ *
475
+ * @defaultValue true
476
+ */
477
+ @IsOptional()
478
+ @IsBoolean()
479
+ public top?: boolean;
480
+
464
481
  /**
465
482
  * @param filter - The filter to use for searching tweets.
466
483
  */
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  ENotificationType as ENotificationTypeOriginal,
3
3
  INotification,
4
- IUserNotifications as IUserNotificationsResponse,
4
+ IUserNotificationsResponse,
5
5
  } from 'rettiwt-core';
6
6
 
7
7
  import { findKeyByValue } from '../../helper/JsonUtils';
@@ -55,7 +55,7 @@ export class Tweet {
55
55
  public replyCount: number;
56
56
 
57
57
  /** The rest id of the tweet to which the tweet is a reply. */
58
- public replyTo?: string;
58
+ public replyTo?: Tweet;
59
59
 
60
60
  /** The number of retweets of the tweet. */
61
61
  public retweetCount: number;
@@ -71,16 +71,17 @@ export class Tweet {
71
71
 
72
72
  /**
73
73
  * @param tweet - The raw tweet details.
74
+ * @param response - The raw response
74
75
  */
75
- public constructor(tweet: IRawTweet) {
76
+ public constructor(tweet: IRawTweet, response: NonNullable<unknown>) {
76
77
  this.id = tweet.rest_id;
77
78
  this.createdAt = tweet.legacy.created_at;
78
79
  this.tweetBy = new User(tweet.core.user_results.result);
79
80
  this.entities = new TweetEntities(tweet.legacy.entities);
80
81
  this.media = tweet.legacy.extended_entities?.media?.map((media) => new TweetMedia(media));
81
- this.quoted = this.getQuotedTweet(tweet);
82
+ this.quoted = this.getQuotedTweet(tweet, response);
82
83
  this.fullText = tweet.note_tweet ? tweet.note_tweet.note_tweet_results.result.text : tweet.legacy.full_text;
83
- this.replyTo = tweet.legacy.in_reply_to_status_id_str;
84
+ this.replyTo = this.getParentTweet(tweet, response);
84
85
  this.lang = tweet.legacy.lang;
85
86
  this.quoteCount = tweet.legacy.quote_count;
86
87
  this.replyCount = tweet.legacy.reply_count;
@@ -88,28 +89,42 @@ export class Tweet {
88
89
  this.likeCount = tweet.legacy.favorite_count;
89
90
  this.viewCount = tweet.views.count ? parseInt(tweet.views.count) : 0;
90
91
  this.bookmarkCount = tweet.legacy.bookmark_count;
91
- this.retweetedTweet = this.getRetweetedTweet(tweet);
92
+ this.retweetedTweet = this.getRetweetedTweet(tweet, response);
93
+ }
94
+
95
+ private getParentTweet(tweet: IRawTweet, response: NonNullable<unknown>): Tweet | undefined {
96
+ // Getting parent tweet ID, if any
97
+ const parentTweetId: string = tweet.legacy?.in_reply_to_status_id_str ?? '';
98
+
99
+ // If no parent tweet
100
+ if (parentTweetId.length == 0) {
101
+ return;
102
+ }
103
+
104
+ // Getting the details of parent tweet
105
+ return Tweet.single(response, parentTweetId);
92
106
  }
93
107
 
94
108
  /**
95
109
  * Extract and deserialize the original quoted tweet from the given raw tweet.
96
110
  *
97
111
  * @param tweet - The raw tweet.
112
+ * @param response - The raw response
98
113
  *
99
114
  * @returns - The deserialized original quoted tweet.
100
115
  */
101
- private getQuotedTweet(tweet: IRawTweet): Tweet | undefined {
116
+ private getQuotedTweet(tweet: IRawTweet, response: NonNullable<unknown>): Tweet | undefined {
102
117
  // If tweet with limited visibility
103
118
  if (
104
119
  tweet.quoted_status_result &&
105
- Object.entries(tweet.quoted_status_result).length &&
106
- tweet.quoted_status_result.result.__typename == 'TweetWithVisibilityResults'
120
+ tweet.quoted_status_result?.result?.__typename == 'TweetWithVisibilityResults' &&
121
+ (tweet.quoted_status_result.result as ILimitedVisibilityTweet)?.tweet?.legacy
107
122
  ) {
108
- return new Tweet((tweet.quoted_status_result.result as ILimitedVisibilityTweet).tweet);
123
+ return new Tweet((tweet.quoted_status_result.result as ILimitedVisibilityTweet).tweet, response);
109
124
  }
110
125
  // 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);
126
+ else if ((tweet.quoted_status_result?.result as ITweet)?.rest_id) {
127
+ return new Tweet(tweet.quoted_status_result.result as ITweet, response);
113
128
  }
114
129
  // Else, skip
115
130
  else {
@@ -121,13 +136,22 @@ export class Tweet {
121
136
  * Extract and deserialize the original retweeted tweet from the given raw tweet.
122
137
  *
123
138
  * @param tweet - The raw tweet.
139
+ * @param response - The raw response
124
140
  *
125
141
  * @returns - The deserialized original retweeted tweet.
126
142
  */
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);
143
+ private getRetweetedTweet(tweet: IRawTweet, response: NonNullable<unknown>): Tweet | undefined {
144
+ // If retweet with limited visibility
145
+ if (
146
+ tweet.legacy?.retweeted_status_result &&
147
+ tweet.legacy?.retweeted_status_result?.result?.__typename == 'TweetWithVisibilityResults' &&
148
+ (tweet.legacy?.retweeted_status_result?.result as ILimitedVisibilityTweet)?.tweet?.legacy
149
+ ) {
150
+ return new Tweet((tweet.legacy.retweeted_status_result.result as ILimitedVisibilityTweet).tweet, response);
151
+ }
152
+ // If normal tweet
153
+ else if ((tweet.legacy?.retweeted_status_result?.result as ITweet)?.rest_id) {
154
+ return new Tweet(tweet.legacy.retweeted_status_result.result as ITweet, response);
131
155
  }
132
156
  // Else, skip
133
157
  else {
@@ -152,11 +176,23 @@ export class Tweet {
152
176
 
153
177
  // Deserializing valid data
154
178
  for (const item of extract) {
155
- if (item.tweet_results?.result?.legacy) {
179
+ // If tweet with limited visibility
180
+ if (
181
+ item.tweet_results?.result &&
182
+ item.tweet_results?.result?.__typename == 'TweetWithVisibilityResults' &&
183
+ (item.tweet_results?.result as ILimitedVisibilityTweet)?.tweet?.legacy
184
+ ) {
185
+ tweets.push(new Tweet((item.tweet_results.result as ILimitedVisibilityTweet).tweet, response));
186
+ }
187
+ // If normal tweet
188
+ else if ((item.tweet_results?.result as ITweet)?.legacy) {
156
189
  // Logging
157
- LogService.log(ELogActions.DESERIALIZE, { id: item.tweet_results.result.rest_id });
158
- tweets.push(new Tweet(item.tweet_results.result));
159
- } else {
190
+ LogService.log(ELogActions.DESERIALIZE, { id: (item.tweet_results.result as ITweet).rest_id });
191
+
192
+ tweets.push(new Tweet(item.tweet_results.result as ITweet, response));
193
+ }
194
+ // If invalid/unrecognized tweet
195
+ else {
160
196
  // Logging
161
197
  LogService.log(ELogActions.WARNING, {
162
198
  action: ELogActions.DESERIALIZE,
@@ -190,7 +226,7 @@ export class Tweet {
190
226
  // Logging
191
227
  LogService.log(ELogActions.DESERIALIZE, { id: item.rest_id });
192
228
 
193
- tweets.push(new Tweet(item));
229
+ tweets.push(new Tweet(item, response));
194
230
  } else {
195
231
  // Logging
196
232
  LogService.log(ELogActions.WARNING, {
@@ -123,7 +123,7 @@ export class User {
123
123
 
124
124
  // Deserializing valid data
125
125
  for (const item of extract) {
126
- if (item.legacy) {
126
+ if (item.legacy && item.legacy.created_at) {
127
127
  // Logging
128
128
  LogService.log(ELogActions.DESERIALIZE, { id: item.rest_id });
129
129
 
@@ -57,7 +57,9 @@ export class AuthService extends FetcherService {
57
57
  const cookieString: string = AuthService.decodeCookie(apiKey);
58
58
 
59
59
  // Searching for the user id in the cookie string
60
- const searchResults: string[] | null = cookieString.match(/((?<=twid="u=)(.*)(?="))|((?<=twid=u%3D)(.*)(?=;))/);
60
+ const searchResults: string[] | null = cookieString.match(
61
+ /((?<=twid="u=)(\d+)(?="))|((?<=twid=u%3D)(\d+)(?=;))/,
62
+ );
61
63
 
62
64
  // If user id was found
63
65
  if (searchResults) {
@@ -1,4 +1,5 @@
1
1
  import {
2
+ IUserBookmarksResponse,
2
3
  IUserDetailsResponse,
3
4
  IUserFollowedResponse,
4
5
  IUserFollowersResponse,
@@ -7,7 +8,7 @@ import {
7
8
  IUserHighlightsResponse,
8
9
  IUserLikesResponse,
9
10
  IUserMediaResponse,
10
- IUserNotifications as IUserNotificationsResponse,
11
+ IUserNotificationsResponse,
11
12
  IUserRecommendedResponse,
12
13
  IUserSubscriptionsResponse,
13
14
  IUserTweetsAndRepliesResponse,
@@ -40,6 +41,46 @@ export class UserService extends FetcherService {
40
41
  super(config);
41
42
  }
42
43
 
44
+ /**
45
+ * Get the list of bookmarks of the logged in user.
46
+ *
47
+ * @param count - The number of bookmakrs to fetch, must be \<= 100.
48
+ * @param cursor - The cursor to the batch of bookmarks to fetch.
49
+ *
50
+ * @returns The list of tweets bookmarked by the target user.
51
+ *
52
+ * @example
53
+ * ```
54
+ * import { Rettiwt } from 'rettiwt-api';
55
+ *
56
+ * // Creating a new Rettiwt instance using the given 'API_KEY'
57
+ * const rettiwt = new Rettiwt({ apiKey: API_KEY });
58
+ *
59
+ * // Fetching the most recent 100 liked Tweets of the logged in User
60
+ * rettiwt.user.bookmarks()
61
+ * .then(res => {
62
+ * console.log(res);
63
+ * })
64
+ * .catch(err => {
65
+ * console.log(err);
66
+ * });
67
+ * ```
68
+ */
69
+ public async bookmarks(count?: number, cursor?: string): Promise<CursoredData<Tweet>> {
70
+ const resource = EResourceType.USER_BOOKMARKS;
71
+
72
+ // Fetching raw list of likes
73
+ const response = await this.request<IUserBookmarksResponse>(resource, {
74
+ count: count,
75
+ cursor: cursor,
76
+ });
77
+
78
+ // Deserializing response
79
+ const data = extractors[resource](response);
80
+
81
+ return data;
82
+ }
83
+
43
84
  /**
44
85
  * Get the details of a user.
45
86
  *
package/.yarnrc.yml DELETED
@@ -1 +0,0 @@
1
- nodeLinker: node-modules