rettiwt-api 4.2.0-alpha.0 → 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.
- package/.eslintrc.js +35 -0
- package/dist/Rettiwt.d.ts +3 -0
- package/dist/Rettiwt.js +2 -0
- package/dist/Rettiwt.js.map +1 -1
- package/dist/cli.js +2 -0
- package/dist/cli.js.map +1 -1
- package/dist/collections/Extractors.d.ts +2 -1
- package/dist/collections/Extractors.js +3 -0
- package/dist/collections/Extractors.js.map +1 -1
- package/dist/collections/Groups.js +1 -0
- package/dist/collections/Groups.js.map +1 -1
- package/dist/collections/Requests.js +2 -1
- package/dist/collections/Requests.js.map +1 -1
- package/dist/commands/List.d.ts +10 -0
- package/dist/commands/List.js +104 -0
- package/dist/commands/List.js.map +1 -0
- package/dist/commands/Tweet.js +3 -1
- package/dist/commands/Tweet.js.map +1 -1
- package/dist/enums/Resource.d.ts +1 -0
- package/dist/enums/Resource.js +1 -0
- package/dist/enums/Resource.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/models/args/FetchArgs.d.ts +12 -1
- package/dist/models/args/FetchArgs.js +36 -0
- package/dist/models/args/FetchArgs.js.map +1 -1
- package/dist/models/data/Tweet.d.ts +18 -0
- package/dist/models/data/Tweet.js +45 -5
- package/dist/models/data/Tweet.js.map +1 -1
- package/dist/services/public/FetcherService.d.ts +5 -5
- package/dist/services/public/FetcherService.js +12 -12
- package/dist/services/public/FetcherService.js.map +1 -1
- package/dist/services/public/ListService.d.ts +71 -0
- package/dist/services/public/ListService.js +169 -0
- package/dist/services/public/ListService.js.map +1 -0
- package/dist/services/public/TweetService.d.ts +3 -2
- package/dist/services/public/TweetService.js +3 -1
- package/dist/services/public/TweetService.js.map +1 -1
- package/package.json +2 -2
- package/src/Rettiwt.ts +5 -0
- package/src/cli.ts +2 -0
- package/src/collections/Extractors.ts +3 -0
- package/src/collections/Groups.ts +1 -0
- package/src/collections/Requests.ts +2 -1
- package/src/commands/List.ts +49 -0
- package/src/commands/Tweet.ts +5 -1
- package/src/enums/Resource.ts +1 -0
- package/src/index.ts +4 -0
- package/src/models/args/FetchArgs.ts +46 -1
- package/src/models/data/Tweet.ts +52 -5
- package/src/services/public/FetcherService.ts +17 -17
- package/src/services/public/ListService.ts +112 -0
- package/src/services/public/TweetService.ts +9 -1
package/src/Rettiwt.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { AuthService } from './services/public/AuthService';
|
|
2
|
+
import { ListService } from './services/public/ListService';
|
|
2
3
|
import { TweetService } from './services/public/TweetService';
|
|
3
4
|
import { UserService } from './services/public/UserService';
|
|
4
5
|
import { IRettiwtConfig } from './types/RettiwtConfig';
|
|
@@ -48,6 +49,9 @@ export class Rettiwt {
|
|
|
48
49
|
/** The instance used to authenticate. */
|
|
49
50
|
public auth: AuthService;
|
|
50
51
|
|
|
52
|
+
/** The instance used to fetch data related to lists. */
|
|
53
|
+
public list: ListService;
|
|
54
|
+
|
|
51
55
|
/** The instance used to fetch data related to tweets. */
|
|
52
56
|
public tweet: TweetService;
|
|
53
57
|
|
|
@@ -61,6 +65,7 @@ export class Rettiwt {
|
|
|
61
65
|
*/
|
|
62
66
|
public constructor(config?: IRettiwtConfig) {
|
|
63
67
|
this.auth = new AuthService(config);
|
|
68
|
+
this.list = new ListService(config);
|
|
64
69
|
this.tweet = new TweetService(config);
|
|
65
70
|
this.user = new UserService(config);
|
|
66
71
|
}
|
package/src/cli.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { createCommand } from 'commander';
|
|
4
4
|
|
|
5
5
|
import auth from './commands/Auth';
|
|
6
|
+
import list from './commands/List';
|
|
6
7
|
import tweet from './commands/Tweet';
|
|
7
8
|
import user from './commands/User';
|
|
8
9
|
import { Rettiwt } from './Rettiwt';
|
|
@@ -32,6 +33,7 @@ const rettiwt: Rettiwt = new Rettiwt({
|
|
|
32
33
|
});
|
|
33
34
|
|
|
34
35
|
// Adding sub-commands
|
|
36
|
+
program.addCommand(list(rettiwt));
|
|
35
37
|
program.addCommand(tweet(rettiwt));
|
|
36
38
|
program.addCommand(user(rettiwt));
|
|
37
39
|
program.addCommand(auth(rettiwt));
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
IInitializeMediaUploadResponse,
|
|
3
|
+
IListMembersResponse,
|
|
3
4
|
IListTweetsResponse,
|
|
4
5
|
ITweetDetailsResponse,
|
|
5
6
|
ITweetLikeResponse,
|
|
@@ -42,6 +43,8 @@ import { User } from '../models/data/User';
|
|
|
42
43
|
*/
|
|
43
44
|
export const extractors = {
|
|
44
45
|
/* eslint-disable @typescript-eslint/naming-convention */
|
|
46
|
+
LIST_MEMBERS: (response: IListMembersResponse): CursoredData<User> =>
|
|
47
|
+
new CursoredData<User>(response, EBaseType.USER),
|
|
45
48
|
LIST_TWEETS: (response: IListTweetsResponse): CursoredData<Tweet> =>
|
|
46
49
|
new CursoredData<Tweet>(response, EBaseType.TWEET),
|
|
47
50
|
|
|
@@ -19,6 +19,7 @@ const request = new Request();
|
|
|
19
19
|
*/
|
|
20
20
|
export const requests: { [key in keyof typeof EResourceType]: (args: FetchArgs | PostArgs) => AxiosRequestConfig } = {
|
|
21
21
|
/* eslint-disable @typescript-eslint/naming-convention */
|
|
22
|
+
LIST_MEMBERS: (args: FetchArgs) => request.list.members(args.id!, args.count, args.cursor),
|
|
22
23
|
LIST_TWEETS: (args: FetchArgs) => request.list.tweets(args.id!, args.count, args.cursor),
|
|
23
24
|
|
|
24
25
|
MEDIA_UPLOAD_APPEND: (args: PostArgs) => request.media.appendUpload(args.upload!.id!, args.upload!.media!),
|
|
@@ -32,7 +33,7 @@ export const requests: { [key in keyof typeof EResourceType]: (args: FetchArgs |
|
|
|
32
33
|
TWEET_RETWEET: (args: PostArgs) => request.tweet.retweet(args.id!),
|
|
33
34
|
TWEET_RETWEETERS: (args: FetchArgs) => request.tweet.retweeters(args.id!, args.count, args.cursor),
|
|
34
35
|
TWEET_SCHEDULE: (args: PostArgs) => request.tweet.schedule(args.tweet!, args.tweet!.scheduleFor!),
|
|
35
|
-
TWEET_SEARCH: (args: FetchArgs) => request.tweet.search(args.filter!, args.count, args.cursor),
|
|
36
|
+
TWEET_SEARCH: (args: FetchArgs) => request.tweet.search(args.filter!, args.count, args.cursor, args.results),
|
|
36
37
|
TWEET_UNLIKE: (args: PostArgs) => request.tweet.unlike(args.id!),
|
|
37
38
|
TWEET_UNPOST: (args: PostArgs) => request.tweet.unpost(args.id!),
|
|
38
39
|
TWEET_UNRETWEET: (args: PostArgs) => request.tweet.unretweet(args.id!),
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { Command, createCommand } from 'commander';
|
|
2
|
+
|
|
3
|
+
import { output } from '../helper/CliUtils';
|
|
4
|
+
import { Rettiwt } from '../Rettiwt';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Creates a new 'list' command which uses the given Rettiwt instance.
|
|
8
|
+
*
|
|
9
|
+
* @param rettiwt - The Rettiwt instance to use.
|
|
10
|
+
* @returns The created 'list' command.
|
|
11
|
+
*/
|
|
12
|
+
function createListCommand(rettiwt: Rettiwt): Command {
|
|
13
|
+
// Creating the 'list' command
|
|
14
|
+
const list = createCommand('list').description('Access resources releated to lists');
|
|
15
|
+
|
|
16
|
+
// Members
|
|
17
|
+
list.command('members')
|
|
18
|
+
.description('Fetch the list of members of the given tweet list')
|
|
19
|
+
.argument('<id>', 'The id of the tweet list')
|
|
20
|
+
.argument('[count]', 'The number of members to fetch')
|
|
21
|
+
.argument('[cursor]', 'The cursor to the batch of members to fetch')
|
|
22
|
+
.action(async (id: string, count?: string, cursor?: string) => {
|
|
23
|
+
try {
|
|
24
|
+
const members = await rettiwt.list.members(id, count ? parseInt(count) : undefined, cursor);
|
|
25
|
+
output(members);
|
|
26
|
+
} catch (error) {
|
|
27
|
+
output(error);
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
// Tweets
|
|
32
|
+
list.command('tweets')
|
|
33
|
+
.description('Fetch the list of tweets in the tweet list with the given id')
|
|
34
|
+
.argument('<id>', 'The id of the tweet list')
|
|
35
|
+
.argument('[count]', 'The number of tweets to fetch')
|
|
36
|
+
.argument('[cursor]', 'The cursor to the batch of tweets to fetch')
|
|
37
|
+
.action(async (id: string, count?: string, cursor?: string) => {
|
|
38
|
+
try {
|
|
39
|
+
const tweets = await rettiwt.list.tweets(id, count ? parseInt(count) : undefined, cursor);
|
|
40
|
+
output(tweets);
|
|
41
|
+
} catch (error) {
|
|
42
|
+
output(error);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
return list;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export default createListCommand;
|
package/src/commands/Tweet.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Command, createCommand } from 'commander';
|
|
2
|
-
import { TweetFilter } from 'rettiwt-core';
|
|
2
|
+
import { ESearchResultType, TweetFilter } from 'rettiwt-core';
|
|
3
3
|
|
|
4
4
|
import { output } from '../helper/CliUtils';
|
|
5
5
|
import { Rettiwt } from '../Rettiwt';
|
|
@@ -172,6 +172,7 @@ function createTweetCommand(rettiwt: Rettiwt): Command {
|
|
|
172
172
|
.option('--exclude-replies', 'Matches the tweets that are not replies')
|
|
173
173
|
.option('-s, --start <string>', 'Matches the tweets made since the given date (valid date/time string)')
|
|
174
174
|
.option('-e, --end <string>', 'Matches the tweets made upto the given date (valid date/time string)')
|
|
175
|
+
.option('--top', 'Matches top tweets instead of latest')
|
|
175
176
|
.option('--stream', 'Stream the filtered tweets in pseudo-realtime')
|
|
176
177
|
.option('-i, --interval <number>', 'The polling interval (in ms) to use for streaming. Default is 60000')
|
|
177
178
|
.action(async (count?: string, cursor?: string, options?: TweetSearchOptions) => {
|
|
@@ -191,6 +192,7 @@ function createTweetCommand(rettiwt: Rettiwt): Command {
|
|
|
191
192
|
new TweetSearchOptions(options).toTweetFilter(),
|
|
192
193
|
count ? parseInt(count) : undefined,
|
|
193
194
|
cursor,
|
|
195
|
+
options?.top ? ESearchResultType.TOP : ESearchResultType.LATEST,
|
|
194
196
|
);
|
|
195
197
|
output(tweets);
|
|
196
198
|
}
|
|
@@ -296,6 +298,7 @@ class TweetSearchOptions {
|
|
|
296
298
|
public start?: string;
|
|
297
299
|
public stream?: boolean;
|
|
298
300
|
public to?: string;
|
|
301
|
+
public top?: boolean;
|
|
299
302
|
public words?: string;
|
|
300
303
|
|
|
301
304
|
/**
|
|
@@ -323,6 +326,7 @@ class TweetSearchOptions {
|
|
|
323
326
|
this.end = options?.end;
|
|
324
327
|
this.stream = options?.stream;
|
|
325
328
|
this.interval = options?.interval;
|
|
329
|
+
this.top = options?.top;
|
|
326
330
|
}
|
|
327
331
|
|
|
328
332
|
/**
|
package/src/enums/Resource.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -5,6 +5,7 @@ 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';
|
|
8
9
|
|
|
9
10
|
// ARG MODELS
|
|
10
11
|
export * from './models/args/FetchArgs';
|
|
@@ -37,6 +38,7 @@ export * from './types/ErrorHandler';
|
|
|
37
38
|
// RAW TYPES
|
|
38
39
|
export {
|
|
39
40
|
IInitializeMediaUploadResponse,
|
|
41
|
+
IListMembersResponse,
|
|
40
42
|
IListTweetsResponse,
|
|
41
43
|
ITweetDetailsResponse,
|
|
42
44
|
ITweetLikeResponse,
|
|
@@ -52,6 +54,7 @@ export {
|
|
|
52
54
|
ITweetUnretweetResponse,
|
|
53
55
|
} from 'rettiwt-core';
|
|
54
56
|
export {
|
|
57
|
+
IUserBookmarksResponse,
|
|
55
58
|
IUserDetailsResponse,
|
|
56
59
|
IUserFollowedResponse,
|
|
57
60
|
IUserFollowersResponse,
|
|
@@ -60,6 +63,7 @@ export {
|
|
|
60
63
|
IUserHighlightsResponse,
|
|
61
64
|
IUserLikesResponse,
|
|
62
65
|
IUserMediaResponse,
|
|
66
|
+
IUserNotifications as IUserNotificationsResponse,
|
|
63
67
|
IUserRecommendedResponse,
|
|
64
68
|
IUserSubscriptionsResponse,
|
|
65
69
|
IUserTweetsAndRepliesResponse,
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
validateSync,
|
|
15
15
|
} from 'class-validator';
|
|
16
16
|
|
|
17
|
-
import { TweetFilter as TweetFilterCore } from 'rettiwt-core';
|
|
17
|
+
import { ESearchResultType, TweetFilter as TweetFilterCore } from 'rettiwt-core';
|
|
18
18
|
|
|
19
19
|
import { EResourceType } from '../../enums/Resource';
|
|
20
20
|
import { DataValidationError } from '../errors/DataValidationError';
|
|
@@ -52,6 +52,7 @@ export class FetchArgs {
|
|
|
52
52
|
})
|
|
53
53
|
@IsOptional({
|
|
54
54
|
groups: [
|
|
55
|
+
EResourceType.LIST_MEMBERS,
|
|
55
56
|
EResourceType.LIST_TWEETS,
|
|
56
57
|
EResourceType.TWEET_RETWEETERS,
|
|
57
58
|
EResourceType.TWEET_SEARCH,
|
|
@@ -68,6 +69,7 @@ export class FetchArgs {
|
|
|
68
69
|
})
|
|
69
70
|
@Min(1, {
|
|
70
71
|
groups: [
|
|
72
|
+
EResourceType.LIST_MEMBERS,
|
|
71
73
|
EResourceType.LIST_TWEETS,
|
|
72
74
|
EResourceType.TWEET_RETWEETERS,
|
|
73
75
|
EResourceType.TWEET_SEARCH,
|
|
@@ -84,6 +86,7 @@ export class FetchArgs {
|
|
|
84
86
|
})
|
|
85
87
|
@Max(100, {
|
|
86
88
|
groups: [
|
|
89
|
+
EResourceType.LIST_MEMBERS,
|
|
87
90
|
EResourceType.LIST_TWEETS,
|
|
88
91
|
EResourceType.TWEET_RETWEETERS,
|
|
89
92
|
EResourceType.USER_FOLLOWERS,
|
|
@@ -119,6 +122,7 @@ export class FetchArgs {
|
|
|
119
122
|
})
|
|
120
123
|
@IsOptional({
|
|
121
124
|
groups: [
|
|
125
|
+
EResourceType.LIST_MEMBERS,
|
|
122
126
|
EResourceType.LIST_TWEETS,
|
|
123
127
|
EResourceType.TWEET_RETWEETERS,
|
|
124
128
|
EResourceType.TWEET_SEARCH,
|
|
@@ -137,6 +141,7 @@ export class FetchArgs {
|
|
|
137
141
|
})
|
|
138
142
|
@IsString({
|
|
139
143
|
groups: [
|
|
144
|
+
EResourceType.LIST_MEMBERS,
|
|
140
145
|
EResourceType.LIST_TWEETS,
|
|
141
146
|
EResourceType.TWEET_RETWEETERS,
|
|
142
147
|
EResourceType.TWEET_SEARCH,
|
|
@@ -163,6 +168,7 @@ export class FetchArgs {
|
|
|
163
168
|
*/
|
|
164
169
|
@IsEmpty({
|
|
165
170
|
groups: [
|
|
171
|
+
EResourceType.LIST_MEMBERS,
|
|
166
172
|
EResourceType.LIST_TWEETS,
|
|
167
173
|
EResourceType.TWEET_DETAILS,
|
|
168
174
|
EResourceType.TWEET_DETAILS_ALT,
|
|
@@ -202,6 +208,7 @@ export class FetchArgs {
|
|
|
202
208
|
})
|
|
203
209
|
@IsNotEmpty({
|
|
204
210
|
groups: [
|
|
211
|
+
EResourceType.LIST_MEMBERS,
|
|
205
212
|
EResourceType.LIST_TWEETS,
|
|
206
213
|
EResourceType.TWEET_DETAILS,
|
|
207
214
|
EResourceType.TWEET_DETAILS_ALT,
|
|
@@ -220,6 +227,7 @@ export class FetchArgs {
|
|
|
220
227
|
})
|
|
221
228
|
@IsString({
|
|
222
229
|
groups: [
|
|
230
|
+
EResourceType.LIST_MEMBERS,
|
|
223
231
|
EResourceType.LIST_TWEETS,
|
|
224
232
|
EResourceType.TWEET_DETAILS,
|
|
225
233
|
EResourceType.TWEET_DETAILS_ALT,
|
|
@@ -238,6 +246,7 @@ export class FetchArgs {
|
|
|
238
246
|
})
|
|
239
247
|
@IsNumberString(undefined, {
|
|
240
248
|
groups: [
|
|
249
|
+
EResourceType.LIST_MEMBERS,
|
|
241
250
|
EResourceType.LIST_TWEETS,
|
|
242
251
|
EResourceType.TWEET_DETAILS,
|
|
243
252
|
EResourceType.TWEET_DETAILS_ALT,
|
|
@@ -255,6 +264,41 @@ export class FetchArgs {
|
|
|
255
264
|
})
|
|
256
265
|
public id?: string;
|
|
257
266
|
|
|
267
|
+
/**
|
|
268
|
+
* The type of search results to fetch. Can be one of:
|
|
269
|
+
* - {@link EResourceType.LATEST}, for latest search results.
|
|
270
|
+
* - {@link EResourceType.TOP}, for top search results.
|
|
271
|
+
*
|
|
272
|
+
* @defaultValue {@link ESearchResultType.LATEST}.
|
|
273
|
+
*
|
|
274
|
+
* @remarks
|
|
275
|
+
* - Applicable only for {@link EResourceType.TWEET_SEARCH}.
|
|
276
|
+
*/
|
|
277
|
+
@IsEmpty({
|
|
278
|
+
groups: [
|
|
279
|
+
EResourceType.LIST_MEMBERS,
|
|
280
|
+
EResourceType.LIST_TWEETS,
|
|
281
|
+
EResourceType.TWEET_DETAILS,
|
|
282
|
+
EResourceType.TWEET_DETAILS_ALT,
|
|
283
|
+
EResourceType.TWEET_RETWEETERS,
|
|
284
|
+
EResourceType.USER_DETAILS_BY_USERNAME,
|
|
285
|
+
EResourceType.USER_DETAILS_BY_ID,
|
|
286
|
+
EResourceType.USER_FEED_FOLLOWED,
|
|
287
|
+
EResourceType.USER_FEED_RECOMMENDED,
|
|
288
|
+
EResourceType.USER_FOLLOWING,
|
|
289
|
+
EResourceType.USER_FOLLOWERS,
|
|
290
|
+
EResourceType.USER_HIGHLIGHTS,
|
|
291
|
+
EResourceType.USER_LIKES,
|
|
292
|
+
EResourceType.USER_MEDIA,
|
|
293
|
+
EResourceType.USER_NOTIFICATIONS,
|
|
294
|
+
EResourceType.USER_SUBSCRIPTIONS,
|
|
295
|
+
EResourceType.USER_TIMELINE,
|
|
296
|
+
EResourceType.USER_TIMELINE_AND_REPLIES,
|
|
297
|
+
],
|
|
298
|
+
})
|
|
299
|
+
@IsOptional({ groups: [EResourceType.TWEET_SEARCH] })
|
|
300
|
+
public results?: ESearchResultType;
|
|
301
|
+
|
|
258
302
|
/**
|
|
259
303
|
* @param resource - The resource to be fetched.
|
|
260
304
|
* @param args - Additional user-defined arguments for fetching the resource.
|
|
@@ -264,6 +308,7 @@ export class FetchArgs {
|
|
|
264
308
|
this.count = args.count;
|
|
265
309
|
this.cursor = args.cursor;
|
|
266
310
|
this.filter = args.filter ? new TweetFilter(args.filter) : undefined;
|
|
311
|
+
this.results = args.results;
|
|
267
312
|
|
|
268
313
|
// Validating this object
|
|
269
314
|
const validationResult = validateSync(this, { groups: [resource] });
|
package/src/models/data/Tweet.ts
CHANGED
|
@@ -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 =
|
|
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
|
|
91
|
-
|
|
92
|
-
|
|
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
|
|
29
|
+
private readonly _apiKey?: string;
|
|
30
30
|
|
|
31
31
|
/** The service used to handle HTTP and API errors */
|
|
32
|
-
private readonly
|
|
32
|
+
private readonly _errorHandler: IErrorHandler;
|
|
33
33
|
|
|
34
34
|
/** The guest key to use for authenticating against Twitter API as guest. */
|
|
35
|
-
private readonly
|
|
35
|
+
private readonly _guestKey?: string;
|
|
36
36
|
|
|
37
37
|
/** The URL To the proxy server to use for all others. */
|
|
38
|
-
private readonly
|
|
38
|
+
private readonly _proxyUrl?: URL;
|
|
39
39
|
|
|
40
40
|
/** The max wait time for a response. */
|
|
41
|
-
private readonly
|
|
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.
|
|
55
|
-
this.
|
|
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.
|
|
59
|
-
this.
|
|
60
|
-
this.
|
|
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.
|
|
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.
|
|
91
|
-
} else if (this.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { statSync } from 'fs';
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
|
+
ESearchResultType,
|
|
4
5
|
IInitializeMediaUploadResponse,
|
|
5
6
|
IListTweetsResponse,
|
|
6
7
|
ITweetDetailsResponse,
|
|
@@ -398,6 +399,7 @@ export class TweetService extends FetcherService {
|
|
|
398
399
|
* @param filter - The filter to be used for searching the tweets.
|
|
399
400
|
* @param count - The number of tweets to fetch, must be \<= 20.
|
|
400
401
|
* @param cursor - The cursor to the batch of tweets to fetch.
|
|
402
|
+
* @param results - The type of search results to fetch. Default is {@link ESearchResultType.LATEST}.
|
|
401
403
|
*
|
|
402
404
|
* @returns The list of tweets that match the given filter.
|
|
403
405
|
*
|
|
@@ -420,7 +422,12 @@ export class TweetService extends FetcherService {
|
|
|
420
422
|
*
|
|
421
423
|
* @remarks For details about available filters, refer to {@link TweetFilter}
|
|
422
424
|
*/
|
|
423
|
-
public async search(
|
|
425
|
+
public async search(
|
|
426
|
+
filter: TweetFilter,
|
|
427
|
+
count?: number,
|
|
428
|
+
cursor?: string,
|
|
429
|
+
results?: ESearchResultType,
|
|
430
|
+
): Promise<CursoredData<Tweet>> {
|
|
424
431
|
const resource = EResourceType.TWEET_SEARCH;
|
|
425
432
|
|
|
426
433
|
// Fetching raw list of filtered tweets
|
|
@@ -428,6 +435,7 @@ export class TweetService extends FetcherService {
|
|
|
428
435
|
filter: filter,
|
|
429
436
|
count: count,
|
|
430
437
|
cursor: cursor,
|
|
438
|
+
results: results,
|
|
431
439
|
});
|
|
432
440
|
|
|
433
441
|
// Deserializing response
|