rettiwt-api 3.1.0 → 4.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 (64) hide show
  1. package/.github/FUNDING.yml +4 -0
  2. package/.github/workflows/documentation.yml +5 -0
  3. package/.github/workflows/publish.yml +3 -0
  4. package/.yarnrc.yml +1 -0
  5. package/README.md +50 -10
  6. package/dist/collections/Extractors.d.ts +4 -2
  7. package/dist/collections/Extractors.js +3 -3
  8. package/dist/collections/Extractors.js.map +1 -1
  9. package/dist/collections/Groups.js +3 -1
  10. package/dist/collections/Groups.js.map +1 -1
  11. package/dist/collections/Requests.js +3 -1
  12. package/dist/collections/Requests.js.map +1 -1
  13. package/dist/commands/Tweet.js +73 -41
  14. package/dist/commands/Tweet.js.map +1 -1
  15. package/dist/commands/User.js +3 -4
  16. package/dist/commands/User.js.map +1 -1
  17. package/dist/enums/Resource.d.ts +3 -1
  18. package/dist/enums/Resource.js +3 -1
  19. package/dist/enums/Resource.js.map +1 -1
  20. package/dist/helper/CliUtils.d.ts +2 -0
  21. package/dist/helper/CliUtils.js +2 -0
  22. package/dist/helper/CliUtils.js.map +1 -1
  23. package/dist/helper/JsonUtils.d.ts +2 -0
  24. package/dist/helper/JsonUtils.js +2 -0
  25. package/dist/helper/JsonUtils.js.map +1 -1
  26. package/dist/index.d.ts +2 -2
  27. package/dist/models/args/FetchArgs.d.ts +0 -2
  28. package/dist/models/args/FetchArgs.js +126 -10
  29. package/dist/models/args/FetchArgs.js.map +1 -1
  30. package/dist/models/args/PostArgs.d.ts +3 -1
  31. package/dist/models/args/PostArgs.js +62 -24
  32. package/dist/models/args/PostArgs.js.map +1 -1
  33. package/dist/models/data/Tweet.d.ts +3 -3
  34. package/dist/models/data/Tweet.js +1 -1
  35. package/dist/models/data/Tweet.js.map +1 -1
  36. package/dist/services/public/AuthService.d.ts +21 -0
  37. package/dist/services/public/AuthService.js +44 -1
  38. package/dist/services/public/AuthService.js.map +1 -1
  39. package/dist/services/public/FetcherService.d.ts +2 -2
  40. package/dist/services/public/FetcherService.js +5 -6
  41. package/dist/services/public/FetcherService.js.map +1 -1
  42. package/dist/services/public/TweetService.d.ts +55 -32
  43. package/dist/services/public/TweetService.js +96 -55
  44. package/dist/services/public/TweetService.js.map +1 -1
  45. package/dist/services/public/UserService.d.ts +4 -5
  46. package/dist/services/public/UserService.js +5 -6
  47. package/dist/services/public/UserService.js.map +1 -1
  48. package/package.json +3 -2
  49. package/src/collections/Extractors.ts +6 -3
  50. package/src/collections/Groups.ts +3 -1
  51. package/src/collections/Requests.ts +3 -1
  52. package/src/commands/Tweet.ts +43 -18
  53. package/src/commands/User.ts +3 -4
  54. package/src/enums/Resource.ts +3 -1
  55. package/src/helper/CliUtils.ts +2 -0
  56. package/src/helper/JsonUtils.ts +2 -0
  57. package/src/index.ts +5 -1
  58. package/src/models/args/FetchArgs.ts +128 -11
  59. package/src/models/args/PostArgs.ts +65 -24
  60. package/src/models/data/Tweet.ts +4 -4
  61. package/src/services/public/AuthService.ts +51 -1
  62. package/src/services/public/FetcherService.ts +9 -8
  63. package/src/services/public/TweetService.ts +103 -60
  64. package/src/services/public/UserService.ts +5 -6
@@ -3,14 +3,16 @@ import {
3
3
  IListTweetsResponse,
4
4
  ITweetDetailsResponse,
5
5
  ITweetLikeResponse,
6
- ITweetLikersResponse,
7
6
  ITweetPostResponse,
7
+ ITweetRepliesResponse,
8
8
  ITweetRetweetersResponse,
9
9
  ITweetRetweetResponse,
10
+ ITweetScheduleResponse,
10
11
  ITweetSearchResponse,
11
12
  ITweetUnlikeResponse,
12
13
  ITweetUnpostResponse,
13
14
  ITweetUnretweetResponse,
15
+ ITweetUnscheduleResponse,
14
16
  IUserDetailsResponse,
15
17
  IUserFollowedResponse,
16
18
  IUserFollowersResponse,
@@ -47,20 +49,21 @@ export const extractors = {
47
49
  response.media_id_string ?? undefined,
48
50
 
49
51
  TWEET_DETAILS: (response: ITweetDetailsResponse, id: string): Tweet | undefined => Tweet.single(response, id),
52
+ TWEET_DETAILS_ALT: (response: ITweetRepliesResponse, id: string): Tweet | undefined => Tweet.single(response, id),
50
53
  TWEET_LIKE: (response: ITweetLikeResponse): boolean => (response?.data?.favorite_tweet ? true : false),
51
- TWEET_LIKERS: (response: ITweetLikersResponse): CursoredData<User> =>
52
- new CursoredData<User>(response, EBaseType.USER),
53
54
  TWEET_POST: (response: ITweetPostResponse): string =>
54
55
  response?.data?.create_tweet?.tweet_results?.result?.rest_id ?? undefined,
55
56
  TWEET_RETWEET: (response: ITweetRetweetResponse): boolean => (response?.data?.create_retweet ? true : false),
56
57
  TWEET_RETWEETERS: (response: ITweetRetweetersResponse): CursoredData<User> =>
57
58
  new CursoredData<User>(response, EBaseType.USER),
59
+ TWEET_SCHEDULE: (response: ITweetScheduleResponse): string => response?.data?.tweet?.rest_id ?? undefined,
58
60
  TWEET_SEARCH: (response: ITweetSearchResponse): CursoredData<Tweet> =>
59
61
  new CursoredData<Tweet>(response, EBaseType.TWEET),
60
62
  TWEET_UNLIKE: (response: ITweetUnlikeResponse): boolean => (response?.data?.unfavorite_tweet ? true : false),
61
63
  TWEET_UNPOST: (response: ITweetUnpostResponse): boolean => (response?.data?.delete_tweet ? true : false),
62
64
  TWEET_UNRETWEET: (response: ITweetUnretweetResponse): boolean =>
63
65
  response?.data?.unretweet?.source_tweet_results?.result ? true : false,
66
+ TWEET_UNSCHEDULE: (response: ITweetUnscheduleResponse): boolean => response?.data?.scheduledtweet_delete == 'Done',
64
67
 
65
68
  USER_DETAILS_BY_USERNAME: (response: IUserDetailsResponse): User | undefined => User.single(response),
66
69
  USER_DETAILS_BY_ID: (response: IUserDetailsResponse): User | undefined => User.single(response),
@@ -19,7 +19,7 @@ export const allowGuestAuthentication = [
19
19
  export const fetchResources = [
20
20
  EResourceType.LIST_TWEETS,
21
21
  EResourceType.TWEET_DETAILS,
22
- EResourceType.TWEET_LIKERS,
22
+ EResourceType.TWEET_DETAILS_ALT,
23
23
  EResourceType.TWEET_RETWEETERS,
24
24
  EResourceType.TWEET_SEARCH,
25
25
  EResourceType.USER_DETAILS_BY_USERNAME,
@@ -48,9 +48,11 @@ export const postResources = [
48
48
  EResourceType.TWEET_LIKE,
49
49
  EResourceType.TWEET_POST,
50
50
  EResourceType.TWEET_RETWEET,
51
+ EResourceType.TWEET_SCHEDULE,
51
52
  EResourceType.TWEET_UNLIKE,
52
53
  EResourceType.TWEET_UNPOST,
53
54
  EResourceType.TWEET_UNRETWEET,
55
+ EResourceType.TWEET_UNSCHEDULE,
54
56
  EResourceType.USER_FOLLOW,
55
57
  EResourceType.USER_UNFOLLOW,
56
58
  ];
@@ -26,15 +26,17 @@ export const requests: { [key in keyof typeof EResourceType]: (args: FetchArgs |
26
26
  MEDIA_UPLOAD_INITIALIZE: (args: PostArgs) => request.media.initializeUpload(args.upload!.size!),
27
27
 
28
28
  TWEET_DETAILS: (args: FetchArgs) => request.tweet.details(args.id!),
29
+ TWEET_DETAILS_ALT: (args: FetchArgs) => request.tweet.replies(args.id!),
29
30
  TWEET_LIKE: (args: PostArgs) => request.tweet.like(args.id!),
30
- TWEET_LIKERS: (args: FetchArgs) => request.tweet.likers(args.id!, args.count, args.cursor),
31
31
  TWEET_POST: (args: PostArgs) => request.tweet.post(args.tweet!),
32
32
  TWEET_RETWEET: (args: PostArgs) => request.tweet.retweet(args.id!),
33
33
  TWEET_RETWEETERS: (args: FetchArgs) => request.tweet.retweeters(args.id!, args.count, args.cursor),
34
+ TWEET_SCHEDULE: (args: PostArgs) => request.tweet.schedule(args.tweet!, args.tweet!.scheduleFor!),
34
35
  TWEET_SEARCH: (args: FetchArgs) => request.tweet.search(args.filter!, args.count, args.cursor),
35
36
  TWEET_UNLIKE: (args: PostArgs) => request.tweet.unlike(args.id!),
36
37
  TWEET_UNPOST: (args: PostArgs) => request.tweet.unpost(args.id!),
37
38
  TWEET_UNRETWEET: (args: PostArgs) => request.tweet.unretweet(args.id!),
39
+ TWEET_UNSCHEDULE: (args: PostArgs) => request.tweet.unschedule(args.id!),
38
40
 
39
41
  USER_DETAILS_BY_USERNAME: (args: FetchArgs) => request.user.detailsByUsername(args.id!),
40
42
  USER_DETAILS_BY_ID: (args: FetchArgs) => request.user.detailsById(args.id!),
@@ -42,22 +42,6 @@ function createTweetCommand(rettiwt: Rettiwt): Command {
42
42
  }
43
43
  });
44
44
 
45
- // Likers
46
- tweet
47
- .command('likers')
48
- .description('Fetch the list of users who liked the given tweets')
49
- .argument('<id>', 'The id of the tweet')
50
- .argument('[count]', 'The number of likers to fetch')
51
- .argument('[cursor]', 'The cursor to the batch of likers to fetch')
52
- .action(async (id: string, count?: string, cursor?: string) => {
53
- try {
54
- const tweets = await rettiwt.tweet.likers(id, count ? parseInt(count) : undefined, cursor);
55
- output(tweets);
56
- } catch (error) {
57
- output(error);
58
- }
59
- });
60
-
61
45
  // List
62
46
  tweet
63
47
  .command('list')
@@ -129,6 +113,33 @@ function createTweetCommand(rettiwt: Rettiwt): Command {
129
113
  }
130
114
  });
131
115
 
116
+ // Schedule
117
+ tweet
118
+ .command('schedule')
119
+ .description('Schedule a tweet to be posted at a given date/time')
120
+ .argument('<text>', 'The text to post as a tweet')
121
+ .argument('<time>', 'The date/time at which the tweet is to be scheduled (valid date/time string)')
122
+ .option('-m, --media [string]', 'Comma-separated list of ids of the media item(s) to be posted')
123
+ .option('-q, --quote [string]', 'The id of the tweet to quote in the tweet to be posted')
124
+ .option(
125
+ '-r, --reply [string]',
126
+ 'The id of the tweet to which the reply is to be made, if the tweet is to be a reply',
127
+ )
128
+ .action(async (text: string, time: string, options?: { media?: string; quote?: string; reply?: string }) => {
129
+ try {
130
+ const result = await rettiwt.tweet.schedule({
131
+ text: text,
132
+ media: options?.media ? options?.media.split(',').map((item) => ({ id: item })) : undefined,
133
+ quote: options?.quote,
134
+ replyTo: options?.reply,
135
+ scheduleFor: new Date(time),
136
+ });
137
+ output(result);
138
+ } catch (error) {
139
+ output(error);
140
+ }
141
+ });
142
+
132
143
  // Search
133
144
  tweet
134
145
  .command('search')
@@ -158,8 +169,8 @@ function createTweetCommand(rettiwt: Rettiwt): Command {
158
169
  .option('-q, --quoted <string>', 'Matches the tweets that quote the tweet with the given id')
159
170
  .option('--exclude-links', 'Matches tweets that do not contain links')
160
171
  .option('--exclude-replies', 'Matches the tweets that are not replies')
161
- .option('-s, --start <string>', 'Matches the tweets made since the given date (valid date string)')
162
- .option('-e, --end <string>', 'Matches the tweets made upto the given date (valid date string)')
172
+ .option('-s, --start <string>', 'Matches the tweets made since the given date (valid date/time string)')
173
+ .option('-e, --end <string>', 'Matches the tweets made upto the given date (valid date/time string)')
163
174
  .option('--stream', 'Stream the filtered tweets in pseudo-realtime')
164
175
  .option('-i, --interval <number>', 'The polling interval (in ms) to use for streaming. Default is 60000')
165
176
  .action(async (count?: string, cursor?: string, options?: TweetSearchOptions) => {
@@ -229,6 +240,20 @@ function createTweetCommand(rettiwt: Rettiwt): Command {
229
240
  }
230
241
  });
231
242
 
243
+ // Unschedule
244
+ tweet
245
+ .command('unschedule')
246
+ .description('Unschedule a tweet')
247
+ .argument('<id>', 'The id of the tweet')
248
+ .action(async (id: string) => {
249
+ try {
250
+ const result = await rettiwt.tweet.unschedule(id);
251
+ output(result);
252
+ } catch (error) {
253
+ output(error);
254
+ }
255
+ });
256
+
232
257
  // Upload
233
258
  tweet
234
259
  .command('upload')
@@ -99,13 +99,12 @@ function createUserCommand(rettiwt: Rettiwt): Command {
99
99
 
100
100
  // Likes
101
101
  user.command('likes')
102
- .description('Fetch the list of tweets liked by the given user')
103
- .argument('<id>', 'The id of the user')
102
+ .description('Fetch your list of liked tweet')
104
103
  .argument('[count]', 'The number of liked tweets to fetch')
105
104
  .argument('[cursor]', 'The cursor to the batch of liked tweets to fetch')
106
- .action(async (id: string, count?: string, cursor?: string) => {
105
+ .action(async (count?: string, cursor?: string) => {
107
106
  try {
108
- const tweets = await rettiwt.user.likes(id, count ? parseInt(count) : undefined, cursor);
107
+ const tweets = await rettiwt.user.likes(count ? parseInt(count) : undefined, cursor);
109
108
  output(tweets);
110
109
  } catch (error) {
111
110
  output(error);
@@ -14,15 +14,17 @@ export enum EResourceType {
14
14
 
15
15
  // TWEET
16
16
  TWEET_DETAILS = 'TWEET_DETAILS',
17
+ TWEET_DETAILS_ALT = 'TWEET_DETAILS_ALT',
17
18
  TWEET_LIKE = 'TWEET_LIKE',
18
- TWEET_LIKERS = 'TWEET_LIKERS',
19
19
  TWEET_POST = 'TWEET_POST',
20
20
  TWEET_RETWEET = 'TWEET_RETWEET',
21
21
  TWEET_RETWEETERS = 'TWEET_RETWEETERS',
22
+ TWEET_SCHEDULE = 'TWEET_SCHEDULE',
22
23
  TWEET_SEARCH = 'TWEET_SEARCH',
23
24
  TWEET_UNLIKE = 'TWEET_UNLIKE',
24
25
  TWEET_UNPOST = 'TWEET_UNPOST',
25
26
  TWEET_UNRETWEET = 'TWEET_UNRETWEET',
27
+ TWEET_UNSCHEDULE = 'TWEET_UNSCHEDULE',
26
28
 
27
29
  // USER
28
30
  USER_DETAILS_BY_USERNAME = 'USER_DETAILS_BY_USERNAME',
@@ -2,6 +2,8 @@
2
2
  * Outputs the given JSON data.
3
3
  *
4
4
  * @param data - The data to be output.
5
+ *
6
+ * @internal
5
7
  */
6
8
  export function output(data: unknown): void {
7
9
  // If data is string, output as is
@@ -52,6 +52,8 @@ export function findByFilter<T>(data: NonNullable<unknown>, key: string, value:
52
52
  * @param data - The data on which search is to be performed.
53
53
  * @param value - The value to search.
54
54
  * @returns The key with the given value.
55
+ *
56
+ * @internal
55
57
  */
56
58
  export function findKeyByValue(data: NonNullable<unknown>, value: string): string | undefined {
57
59
  // Finding the key-value pairs that have the given value
package/src/index.ts CHANGED
@@ -39,23 +39,27 @@ export {
39
39
  IListTweetsResponse,
40
40
  ITweetDetailsResponse,
41
41
  ITweetLikeResponse,
42
- ITweetLikersResponse,
43
42
  ITweetPostResponse,
43
+ ITweetRepliesResponse,
44
44
  ITweetRetweetersResponse,
45
45
  ITweetRetweetResponse,
46
+ ITweetScheduleResponse,
46
47
  ITweetSearchResponse,
47
48
  ITweetUnlikeResponse,
48
49
  ITweetUnpostResponse,
50
+ ITweetUnscheduleResponse,
49
51
  ITweetUnretweetResponse,
50
52
  } from 'rettiwt-core';
51
53
  export {
52
54
  IUserDetailsResponse,
55
+ IUserFollowedResponse,
53
56
  IUserFollowersResponse,
54
57
  IUserFollowingResponse,
55
58
  IUserFollowResponse,
56
59
  IUserHighlightsResponse,
57
60
  IUserLikesResponse,
58
61
  IUserMediaResponse,
62
+ IUserRecommendedResponse,
59
63
  IUserSubscriptionsResponse,
60
64
  IUserTweetsAndRepliesResponse,
61
65
  IUserTweetsResponse,
@@ -2,6 +2,7 @@ import {
2
2
  IsArray,
3
3
  IsBoolean,
4
4
  IsDate,
5
+ IsEmpty,
5
6
  IsNotEmpty,
6
7
  IsNumber,
7
8
  IsNumberString,
@@ -9,6 +10,7 @@ import {
9
10
  IsOptional,
10
11
  IsString,
11
12
  Max,
13
+ Min,
12
14
  validateSync,
13
15
  } from 'class-validator';
14
16
 
@@ -37,14 +39,50 @@ export class FetchArgs {
37
39
  * - Has not effect for:
38
40
  * - {@link EResourceType.USER_FEED_FOLLOWED}
39
41
  * - {@link EResourceType.USER_FEED_RECOMMENDED}
40
- *
41
- * @defaultValue 20
42
42
  */
43
- @IsOptional({ groups: [EResourceType.USER_FEED_FOLLOWED, EResourceType.USER_FEED_RECOMMENDED] })
43
+ @IsEmpty({
44
+ groups: [
45
+ EResourceType.TWEET_DETAILS,
46
+ EResourceType.TWEET_DETAILS_ALT,
47
+ EResourceType.USER_DETAILS_BY_ID,
48
+ EResourceType.USER_DETAILS_BY_USERNAME,
49
+ EResourceType.USER_FEED_FOLLOWED,
50
+ EResourceType.USER_FEED_RECOMMENDED,
51
+ ],
52
+ })
53
+ @IsOptional({
54
+ groups: [
55
+ EResourceType.LIST_TWEETS,
56
+ EResourceType.TWEET_RETWEETERS,
57
+ EResourceType.TWEET_SEARCH,
58
+ EResourceType.USER_FOLLOWERS,
59
+ EResourceType.USER_FOLLOWING,
60
+ EResourceType.USER_HIGHLIGHTS,
61
+ EResourceType.USER_LIKES,
62
+ EResourceType.USER_MEDIA,
63
+ EResourceType.USER_SUBSCRIPTIONS,
64
+ EResourceType.USER_TIMELINE,
65
+ EResourceType.USER_TIMELINE_AND_REPLIES,
66
+ ],
67
+ })
68
+ @Min(1, {
69
+ groups: [
70
+ EResourceType.LIST_TWEETS,
71
+ EResourceType.TWEET_RETWEETERS,
72
+ EResourceType.TWEET_SEARCH,
73
+ EResourceType.USER_FOLLOWERS,
74
+ EResourceType.USER_FOLLOWING,
75
+ EResourceType.USER_HIGHLIGHTS,
76
+ EResourceType.USER_LIKES,
77
+ EResourceType.USER_MEDIA,
78
+ EResourceType.USER_SUBSCRIPTIONS,
79
+ EResourceType.USER_TIMELINE,
80
+ EResourceType.USER_TIMELINE_AND_REPLIES,
81
+ ],
82
+ })
44
83
  @Max(100, {
45
84
  groups: [
46
85
  EResourceType.LIST_TWEETS,
47
- EResourceType.TWEET_LIKERS,
48
86
  EResourceType.TWEET_RETWEETERS,
49
87
  EResourceType.USER_FOLLOWERS,
50
88
  EResourceType.USER_FOLLOWING,
@@ -66,8 +104,48 @@ export class FetchArgs {
66
104
  * - May be used for cursored resources.
67
105
  * - Has no effect for other resources.
68
106
  */
69
- @IsOptional()
70
- @IsString()
107
+ @IsEmpty({
108
+ groups: [
109
+ EResourceType.TWEET_DETAILS,
110
+ EResourceType.TWEET_DETAILS_ALT,
111
+ EResourceType.USER_DETAILS_BY_ID,
112
+ EResourceType.USER_DETAILS_BY_USERNAME,
113
+ ],
114
+ })
115
+ @IsOptional({
116
+ groups: [
117
+ EResourceType.LIST_TWEETS,
118
+ EResourceType.TWEET_RETWEETERS,
119
+ EResourceType.TWEET_SEARCH,
120
+ EResourceType.USER_FEED_FOLLOWED,
121
+ EResourceType.USER_FEED_RECOMMENDED,
122
+ EResourceType.USER_FOLLOWING,
123
+ EResourceType.USER_FOLLOWERS,
124
+ EResourceType.USER_HIGHLIGHTS,
125
+ EResourceType.USER_LIKES,
126
+ EResourceType.USER_MEDIA,
127
+ EResourceType.USER_SUBSCRIPTIONS,
128
+ EResourceType.USER_TIMELINE,
129
+ EResourceType.USER_TIMELINE_AND_REPLIES,
130
+ ],
131
+ })
132
+ @IsString({
133
+ groups: [
134
+ EResourceType.LIST_TWEETS,
135
+ EResourceType.TWEET_RETWEETERS,
136
+ EResourceType.TWEET_SEARCH,
137
+ EResourceType.USER_FEED_FOLLOWED,
138
+ EResourceType.USER_FEED_RECOMMENDED,
139
+ EResourceType.USER_FOLLOWING,
140
+ EResourceType.USER_FOLLOWERS,
141
+ EResourceType.USER_HIGHLIGHTS,
142
+ EResourceType.USER_LIKES,
143
+ EResourceType.USER_MEDIA,
144
+ EResourceType.USER_SUBSCRIPTIONS,
145
+ EResourceType.USER_TIMELINE,
146
+ EResourceType.USER_TIMELINE_AND_REPLIES,
147
+ ],
148
+ })
71
149
  public cursor?: string;
72
150
 
73
151
  /**
@@ -76,7 +154,26 @@ export class FetchArgs {
76
154
  * @remarks
77
155
  * Required when searching for tweets using {@link EResourceType.TWEET_SEARCH}.
78
156
  */
79
- @IsOptional()
157
+ @IsEmpty({
158
+ groups: [
159
+ EResourceType.LIST_TWEETS,
160
+ EResourceType.TWEET_DETAILS,
161
+ EResourceType.TWEET_DETAILS_ALT,
162
+ EResourceType.TWEET_RETWEETERS,
163
+ EResourceType.USER_DETAILS_BY_USERNAME,
164
+ EResourceType.USER_DETAILS_BY_ID,
165
+ EResourceType.USER_FEED_FOLLOWED,
166
+ EResourceType.USER_FEED_RECOMMENDED,
167
+ EResourceType.USER_FOLLOWING,
168
+ EResourceType.USER_FOLLOWERS,
169
+ EResourceType.USER_HIGHLIGHTS,
170
+ EResourceType.USER_LIKES,
171
+ EResourceType.USER_MEDIA,
172
+ EResourceType.USER_SUBSCRIPTIONS,
173
+ EResourceType.USER_TIMELINE,
174
+ EResourceType.USER_TIMELINE_AND_REPLIES,
175
+ ],
176
+ })
80
177
  @IsNotEmpty({ groups: [EResourceType.TWEET_SEARCH] })
81
178
  @IsObject({ groups: [EResourceType.TWEET_SEARCH] })
82
179
  public filter?: TweetFilter;
@@ -88,12 +185,14 @@ export class FetchArgs {
88
185
  * - Required for all resources except {@link EResourceType.TWEET_SEARCH} and {@link EResourceType.USER_TIMELINE_RECOMMENDED}.
89
186
  * - For {@link EResourceType.USER_DETAILS_BY_USERNAME}, can be alphanumeric, while for others, is strictly numeric.
90
187
  */
91
- @IsOptional()
188
+ @IsEmpty({
189
+ groups: [EResourceType.USER_FEED_FOLLOWED, EResourceType.USER_FEED_RECOMMENDED],
190
+ })
92
191
  @IsNotEmpty({
93
192
  groups: [
94
193
  EResourceType.LIST_TWEETS,
95
194
  EResourceType.TWEET_DETAILS,
96
- EResourceType.TWEET_LIKERS,
195
+ EResourceType.TWEET_DETAILS_ALT,
97
196
  EResourceType.TWEET_RETWEETERS,
98
197
  EResourceType.USER_DETAILS_BY_USERNAME,
99
198
  EResourceType.USER_DETAILS_BY_ID,
@@ -107,11 +206,29 @@ export class FetchArgs {
107
206
  EResourceType.USER_TIMELINE_AND_REPLIES,
108
207
  ],
109
208
  })
209
+ @IsString({
210
+ groups: [
211
+ EResourceType.LIST_TWEETS,
212
+ EResourceType.TWEET_DETAILS,
213
+ EResourceType.TWEET_DETAILS_ALT,
214
+ EResourceType.TWEET_RETWEETERS,
215
+ EResourceType.USER_DETAILS_BY_USERNAME,
216
+ EResourceType.USER_DETAILS_BY_ID,
217
+ EResourceType.USER_FOLLOWING,
218
+ EResourceType.USER_FOLLOWERS,
219
+ EResourceType.USER_HIGHLIGHTS,
220
+ EResourceType.USER_LIKES,
221
+ EResourceType.USER_MEDIA,
222
+ EResourceType.USER_SUBSCRIPTIONS,
223
+ EResourceType.USER_TIMELINE,
224
+ EResourceType.USER_TIMELINE_AND_REPLIES,
225
+ ],
226
+ })
110
227
  @IsNumberString(undefined, {
111
228
  groups: [
112
229
  EResourceType.LIST_TWEETS,
113
230
  EResourceType.TWEET_DETAILS,
114
- EResourceType.TWEET_LIKERS,
231
+ EResourceType.TWEET_DETAILS_ALT,
115
232
  EResourceType.TWEET_RETWEETERS,
116
233
  EResourceType.USER_DETAILS_BY_ID,
117
234
  EResourceType.USER_FOLLOWERS,
@@ -132,7 +249,7 @@ export class FetchArgs {
132
249
  */
133
250
  public constructor(resource: EResourceType, args: FetchArgs) {
134
251
  this.id = args.id;
135
- this.count = args.count ?? 20;
252
+ this.count = args.count;
136
253
  this.cursor = args.cursor;
137
254
  this.filter = args.filter ? new TweetFilter(args.filter) : undefined;
138
255
 
@@ -1,6 +1,8 @@
1
1
  import {
2
2
  ArrayMaxSize,
3
3
  IsArray,
4
+ IsDate,
5
+ IsEmpty,
4
6
  IsNotEmpty,
5
7
  IsNumberString,
6
8
  IsObject,
@@ -8,6 +10,7 @@ import {
8
10
  IsString,
9
11
  Max,
10
12
  MaxLength,
13
+ MinDate,
11
14
  validateSync,
12
15
  } from 'class-validator';
13
16
 
@@ -35,7 +38,15 @@ export class PostArgs {
35
38
  * - {@link EResourceType.USER_FOLLOW}
36
39
  * - {@link EResourceType.USER_UNFOLLOW}
37
40
  */
38
- @IsOptional()
41
+ @IsEmpty({
42
+ groups: [
43
+ EResourceType.MEDIA_UPLOAD_APPEND,
44
+ EResourceType.MEDIA_UPLOAD_FINALIZE,
45
+ EResourceType.MEDIA_UPLOAD_INITIALIZE,
46
+ EResourceType.TWEET_POST,
47
+ EResourceType.TWEET_SCHEDULE,
48
+ ],
49
+ })
39
50
  @IsNotEmpty({
40
51
  groups: [
41
52
  EResourceType.TWEET_LIKE,
@@ -43,6 +54,7 @@ export class PostArgs {
43
54
  EResourceType.TWEET_UNLIKE,
44
55
  EResourceType.TWEET_UNPOST,
45
56
  EResourceType.TWEET_UNRETWEET,
57
+ EResourceType.TWEET_UNSCHEDULE,
46
58
  EResourceType.USER_FOLLOW,
47
59
  EResourceType.USER_UNFOLLOW,
48
60
  ],
@@ -54,6 +66,7 @@ export class PostArgs {
54
66
  EResourceType.TWEET_UNLIKE,
55
67
  EResourceType.TWEET_UNPOST,
56
68
  EResourceType.TWEET_UNRETWEET,
69
+ EResourceType.TWEET_UNSCHEDULE,
57
70
  EResourceType.USER_FOLLOW,
58
71
  EResourceType.USER_UNFOLLOW,
59
72
  ],
@@ -66,9 +79,23 @@ export class PostArgs {
66
79
  * @remarks
67
80
  * Required only when posting a tweet using {@link EResourceType.TWEET_POST}
68
81
  */
69
- @IsOptional()
70
- @IsNotEmpty({ groups: [EResourceType.TWEET_POST] })
71
- @IsObject({ groups: [EResourceType.TWEET_POST] })
82
+ @IsEmpty({
83
+ groups: [
84
+ EResourceType.MEDIA_UPLOAD_APPEND,
85
+ EResourceType.MEDIA_UPLOAD_FINALIZE,
86
+ EResourceType.MEDIA_UPLOAD_INITIALIZE,
87
+ EResourceType.TWEET_LIKE,
88
+ EResourceType.TWEET_RETWEET,
89
+ EResourceType.TWEET_UNLIKE,
90
+ EResourceType.TWEET_UNPOST,
91
+ EResourceType.TWEET_UNRETWEET,
92
+ EResourceType.TWEET_UNSCHEDULE,
93
+ EResourceType.USER_FOLLOW,
94
+ EResourceType.USER_UNFOLLOW,
95
+ ],
96
+ })
97
+ @IsNotEmpty({ groups: [EResourceType.TWEET_POST, EResourceType.TWEET_SCHEDULE] })
98
+ @IsObject({ groups: [EResourceType.TWEET_POST, EResourceType.TWEET_SCHEDULE] })
72
99
  public tweet?: TweetArgs;
73
100
 
74
101
  /**
@@ -80,7 +107,13 @@ export class PostArgs {
80
107
  * - {@link EResourceType.MEDIA_UPLOAD_FINALIZE}
81
108
  * - {@link EResourceType.MEDIA_UPLOAD_INITIALIZE}
82
109
  */
83
- @IsOptional()
110
+ @IsEmpty({
111
+ groups: [
112
+ EResourceType.MEDIA_UPLOAD_APPEND,
113
+ EResourceType.MEDIA_UPLOAD_FINALIZE,
114
+ EResourceType.MEDIA_UPLOAD_INITIALIZE,
115
+ ],
116
+ })
84
117
  @IsNotEmpty({
85
118
  groups: [
86
119
  EResourceType.MEDIA_UPLOAD_INITIALIZE,
@@ -103,7 +136,7 @@ export class PostArgs {
103
136
  */
104
137
  public constructor(resource: EResourceType, args: PostArgs) {
105
138
  this.id = args.id;
106
- this.tweet = args.tweet ? new TweetArgs(args.tweet) : undefined;
139
+ this.tweet = args.tweet ? new TweetArgs(resource, args.tweet) : undefined;
107
140
  this.upload = args.upload ? new UploadArgs(resource, args.upload) : undefined;
108
141
 
109
142
  // Validating this object
@@ -128,45 +161,53 @@ export class TweetArgs extends NewTweet {
128
161
  * @remarks
129
162
  * Maximum number of media items that can be posted is 4.
130
163
  */
131
- @IsOptional()
132
- @IsArray()
133
- @ArrayMaxSize(4)
134
- @IsObject({ each: true })
164
+ @IsOptional({ groups: [EResourceType.TWEET_POST, EResourceType.TWEET_SCHEDULE] })
165
+ @IsArray({ groups: [EResourceType.TWEET_POST, EResourceType.TWEET_SCHEDULE] })
166
+ @ArrayMaxSize(4, { groups: [EResourceType.TWEET_POST, EResourceType.TWEET_SCHEDULE] })
167
+ @IsObject({ each: true, groups: [EResourceType.TWEET_POST, EResourceType.TWEET_SCHEDULE] })
135
168
  public media?: TweetMediaArgs[];
136
169
 
137
170
  /** The id of the tweet to quote. */
138
- @IsOptional()
139
- @IsNumberString()
171
+ @IsOptional({ groups: [EResourceType.TWEET_POST, EResourceType.TWEET_SCHEDULE] })
172
+ @IsNumberString(undefined, { groups: [EResourceType.TWEET_POST, EResourceType.TWEET_SCHEDULE] })
140
173
  public quote?: string;
141
174
 
142
175
  /** The id of the tweet to which the given tweet must be a reply. */
143
- @IsOptional()
144
- @IsNumberString()
176
+ @IsOptional({ groups: [EResourceType.TWEET_POST, EResourceType.TWEET_SCHEDULE] })
177
+ @IsNumberString(undefined, { groups: [EResourceType.TWEET_POST, EResourceType.TWEET_SCHEDULE] })
145
178
  public replyTo?: string;
146
179
 
180
+ /** The date/time at which the tweet must be scheduled to be posted. */
181
+ @IsEmpty({ groups: [EResourceType.TWEET_POST] })
182
+ @IsNotEmpty({ groups: [EResourceType.TWEET_SCHEDULE] })
183
+ @IsDate({ groups: [EResourceType.TWEET_SCHEDULE] })
184
+ @MinDate(() => new Date(), { groups: [EResourceType.TWEET_SCHEDULE] })
185
+ public scheduleFor?: Date;
186
+
147
187
  /**
148
188
  * The text for the tweet to be created.
149
189
  *
150
190
  * @remarks
151
191
  * Length of the tweet must be \<= 280 characters.
152
192
  */
153
- @IsNotEmpty()
154
- @IsString()
155
- @MaxLength(280)
193
+ @IsNotEmpty({ groups: [EResourceType.TWEET_POST, EResourceType.TWEET_SCHEDULE] })
194
+ @IsString({ groups: [EResourceType.TWEET_POST, EResourceType.TWEET_SCHEDULE] })
195
+ @MaxLength(280, { groups: [EResourceType.TWEET_POST, EResourceType.TWEET_SCHEDULE] })
156
196
  public text: string;
157
197
 
158
198
  /**
159
199
  * @param args - Arguments specifying the tweet to be posted.
160
200
  */
161
- public constructor(args: TweetArgs) {
201
+ public constructor(resource: EResourceType, args: TweetArgs) {
162
202
  super();
163
- this.text = args.text;
164
- this.quote = args.quote;
165
203
  this.media = args.media ? args.media.map((item) => new TweetMediaArgs(item)) : undefined;
204
+ this.quote = args.quote;
166
205
  this.replyTo = args.replyTo;
206
+ this.scheduleFor = args.scheduleFor;
207
+ this.text = args.text;
167
208
 
168
209
  // Validating this object
169
- const validationResult = validateSync(this);
210
+ const validationResult = validateSync(this, { groups: [resource] });
170
211
 
171
212
  // If valiation error occured
172
213
  if (validationResult.length) {
@@ -223,13 +264,13 @@ export class TweetMediaArgs extends NewTweetMedia {
223
264
  */
224
265
  export class UploadArgs {
225
266
  /** The id allocated to the media file to be uploaded. */
226
- @IsOptional()
267
+ @IsEmpty({ groups: [EResourceType.MEDIA_UPLOAD_INITIALIZE] })
227
268
  @IsNotEmpty({ groups: [EResourceType.MEDIA_UPLOAD_APPEND, EResourceType.MEDIA_UPLOAD_FINALIZE] })
228
269
  @IsNumberString(undefined, { groups: [EResourceType.MEDIA_UPLOAD_APPEND, EResourceType.MEDIA_UPLOAD_FINALIZE] })
229
270
  public id?: string;
230
271
 
231
272
  /** The media file to be uploaded. */
232
- @IsOptional()
273
+ @IsEmpty({ groups: [EResourceType.MEDIA_UPLOAD_FINALIZE, EResourceType.MEDIA_UPLOAD_INITIALIZE] })
233
274
  @IsNotEmpty({ groups: [EResourceType.MEDIA_UPLOAD_APPEND] })
234
275
  public media?: string | ArrayBuffer;
235
276
 
@@ -238,7 +279,7 @@ export class UploadArgs {
238
279
  *
239
280
  * @remarks The size must be \<= 5242880 bytes.
240
281
  */
241
- @IsOptional()
282
+ @IsEmpty({ groups: [EResourceType.MEDIA_UPLOAD_APPEND, EResourceType.MEDIA_UPLOAD_FINALIZE] })
242
283
  @IsNotEmpty({ groups: [EResourceType.MEDIA_UPLOAD_INITIALIZE] })
243
284
  @Max(5242880, { groups: [EResourceType.MEDIA_UPLOAD_INITIALIZE] })
244
285
  public size?: number;
@@ -48,8 +48,8 @@ export class Tweet {
48
48
  /** The number of quotes of the tweet. */
49
49
  public quoteCount: number;
50
50
 
51
- /** The rest id of the tweet which is quoted in the tweet. */
52
- public quoted?: string;
51
+ /** The tweet which is quoted in the tweet. */
52
+ public quoted?: Tweet;
53
53
 
54
54
  /** The number of replies to the tweet. */
55
55
  public replyCount: number;
@@ -60,7 +60,7 @@ export class Tweet {
60
60
  /** The number of retweets of the tweet. */
61
61
  public retweetCount: number;
62
62
 
63
- /** The tweet which was retweeted in this tweet (if any). */
63
+ /** The tweet which is retweeted in this tweet (if any). */
64
64
  public retweetedTweet?: Tweet;
65
65
 
66
66
  /** The details of the user who made the tweet. */
@@ -78,7 +78,7 @@ export class Tweet {
78
78
  this.tweetBy = new User(tweet.core.user_results.result);
79
79
  this.entities = new TweetEntities(tweet.legacy.entities);
80
80
  this.media = tweet.legacy.extended_entities?.media?.map((media) => new TweetMedia(media));
81
- this.quoted = tweet.legacy.quoted_status_id_str;
81
+ this.quoted = tweet.quoted_status_result ? new Tweet(tweet.quoted_status_result.result) : undefined;
82
82
  this.fullText = tweet.note_tweet ? tweet.note_tweet.note_tweet_results.result.text : tweet.legacy.full_text;
83
83
  this.replyTo = tweet.legacy.in_reply_to_status_id_str;
84
84
  this.lang = tweet.legacy.lang;