rettiwt-api 3.1.1 → 4.1.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.
- package/.github/FUNDING.yml +4 -0
- package/.github/workflows/documentation.yml +5 -0
- package/.github/workflows/publish-alpha.yml +29 -0
- package/.github/workflows/publish.yml +3 -0
- package/.yarnrc.yml +1 -0
- package/README.md +50 -10
- package/dist/collections/Extractors.d.ts +6 -2
- package/dist/collections/Extractors.js +6 -3
- package/dist/collections/Extractors.js.map +1 -1
- package/dist/collections/Groups.js +4 -1
- package/dist/collections/Groups.js.map +1 -1
- package/dist/collections/Requests.js +4 -1
- package/dist/collections/Requests.js.map +1 -1
- package/dist/commands/Tweet.js +73 -41
- package/dist/commands/Tweet.js.map +1 -1
- package/dist/commands/User.js +42 -19
- package/dist/commands/User.js.map +1 -1
- package/dist/enums/Data.d.ts +1 -0
- package/dist/enums/Data.js +1 -0
- package/dist/enums/Data.js.map +1 -1
- package/dist/enums/Resource.d.ts +4 -1
- package/dist/enums/Resource.js +4 -1
- package/dist/enums/Resource.js.map +1 -1
- package/dist/helper/CliUtils.d.ts +2 -0
- package/dist/helper/CliUtils.js +2 -0
- package/dist/helper/CliUtils.js.map +1 -1
- package/dist/helper/JsonUtils.d.ts +2 -0
- package/dist/helper/JsonUtils.js +3 -1
- package/dist/helper/JsonUtils.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/models/args/FetchArgs.d.ts +0 -2
- package/dist/models/args/FetchArgs.js +138 -10
- package/dist/models/args/FetchArgs.js.map +1 -1
- package/dist/models/args/PostArgs.d.ts +3 -1
- package/dist/models/args/PostArgs.js +62 -24
- package/dist/models/args/PostArgs.js.map +1 -1
- package/dist/models/data/CursoredData.d.ts +3 -3
- package/dist/models/data/CursoredData.js +5 -1
- package/dist/models/data/CursoredData.js.map +1 -1
- package/dist/models/data/Notification.d.ts +46 -0
- package/dist/models/data/Notification.js +69 -0
- package/dist/models/data/Notification.js.map +1 -0
- package/dist/models/data/Tweet.d.ts +6 -6
- package/dist/models/data/Tweet.js +1 -1
- package/dist/models/data/Tweet.js.map +1 -1
- package/dist/models/data/User.d.ts +3 -3
- package/dist/models/data/User.js.map +1 -1
- package/dist/services/public/AuthService.d.ts +21 -0
- package/dist/services/public/AuthService.js +44 -1
- package/dist/services/public/AuthService.js.map +1 -1
- package/dist/services/public/FetcherService.d.ts +2 -2
- package/dist/services/public/FetcherService.js +5 -6
- package/dist/services/public/FetcherService.js.map +1 -1
- package/dist/services/public/TweetService.d.ts +55 -32
- package/dist/services/public/TweetService.js +96 -55
- package/dist/services/public/TweetService.js.map +1 -1
- package/dist/services/public/UserService.d.ts +32 -6
- package/dist/services/public/UserService.js +52 -7
- package/dist/services/public/UserService.js.map +1 -1
- package/package.json +3 -2
- package/src/collections/Extractors.ts +10 -3
- package/src/collections/Groups.ts +4 -1
- package/src/collections/Requests.ts +4 -1
- package/src/commands/Tweet.ts +43 -18
- package/src/commands/User.ts +17 -4
- package/src/enums/Data.ts +1 -0
- package/src/enums/Resource.ts +4 -1
- package/src/helper/CliUtils.ts +2 -0
- package/src/helper/JsonUtils.ts +3 -1
- package/src/index.ts +5 -1
- package/src/models/args/FetchArgs.ts +140 -11
- package/src/models/args/PostArgs.ts +65 -24
- package/src/models/data/CursoredData.ts +7 -4
- package/src/models/data/Notification.ts +91 -0
- package/src/models/data/Tweet.ts +6 -7
- package/src/models/data/User.ts +3 -3
- package/src/services/public/AuthService.ts +51 -1
- package/src/services/public/FetcherService.ts +9 -8
- package/src/services/public/TweetService.ts +103 -60
- package/src/services/public/UserService.ts +51 -7
|
@@ -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
|
-
@
|
|
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
|
-
@
|
|
70
|
-
|
|
71
|
-
|
|
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
|
-
@
|
|
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
|
-
@
|
|
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
|
-
@
|
|
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
|
-
@
|
|
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;
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { ICursor
|
|
1
|
+
import { ICursor } from 'rettiwt-core';
|
|
2
2
|
|
|
3
3
|
import { EBaseType } from '../../enums/Data';
|
|
4
4
|
|
|
5
5
|
import { findByFilter } from '../../helper/JsonUtils';
|
|
6
6
|
|
|
7
|
+
import { Notification } from './Notification';
|
|
7
8
|
import { Tweet } from './Tweet';
|
|
8
9
|
import { User } from './User';
|
|
9
10
|
|
|
@@ -14,7 +15,7 @@ import { User } from './User';
|
|
|
14
15
|
*
|
|
15
16
|
* @public
|
|
16
17
|
*/
|
|
17
|
-
export class CursoredData<T extends Tweet | User> {
|
|
18
|
+
export class CursoredData<T extends Notification | Tweet | User> {
|
|
18
19
|
/** The batch of data of the given type. */
|
|
19
20
|
public list: T[] = [];
|
|
20
21
|
|
|
@@ -25,11 +26,13 @@ export class CursoredData<T extends Tweet | User> {
|
|
|
25
26
|
* @param response - The raw response.
|
|
26
27
|
* @param type - The base type of the data included in the batch.
|
|
27
28
|
*/
|
|
28
|
-
public constructor(response:
|
|
29
|
+
public constructor(response: NonNullable<unknown>, type: EBaseType) {
|
|
29
30
|
if (type == EBaseType.TWEET) {
|
|
30
31
|
this.list = Tweet.list(response) as T[];
|
|
31
|
-
} else {
|
|
32
|
+
} else if (type == EBaseType.USER) {
|
|
32
33
|
this.list = User.list(response) as T[];
|
|
34
|
+
} else if (type == EBaseType.NOTIFICATION) {
|
|
35
|
+
this.list = Notification.list(response) as T[];
|
|
33
36
|
}
|
|
34
37
|
|
|
35
38
|
this.next = new Cursor(findByFilter<ICursor>(response, 'cursorType', 'Bottom')[0]?.value ?? '');
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ENotificationType as ENotificationTypeOriginal,
|
|
3
|
+
INotification,
|
|
4
|
+
IUserNotifications as IUserNotificationsResponse,
|
|
5
|
+
} from 'rettiwt-core';
|
|
6
|
+
|
|
7
|
+
import { findKeyByValue } from '../../helper/JsonUtils';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* The different types of notifications.
|
|
11
|
+
*
|
|
12
|
+
* @public
|
|
13
|
+
*/
|
|
14
|
+
export enum ENotificationType {
|
|
15
|
+
RECOMMENDATION = 'RECOMMENDATION',
|
|
16
|
+
INFORMATION = 'INFORMATION',
|
|
17
|
+
LIVE = 'LIVE',
|
|
18
|
+
ALERT = 'ALERT',
|
|
19
|
+
UNDEFINED = 'UNDEFINED',
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* The details of a single notification.
|
|
24
|
+
*
|
|
25
|
+
* @public
|
|
26
|
+
*/
|
|
27
|
+
export class Notification {
|
|
28
|
+
/** The list of id of the users from whom the notification was received. */
|
|
29
|
+
public from: string[];
|
|
30
|
+
|
|
31
|
+
/** The id of the notification. */
|
|
32
|
+
public id: string;
|
|
33
|
+
|
|
34
|
+
/** The text contents of the notification. */
|
|
35
|
+
public message: string;
|
|
36
|
+
|
|
37
|
+
/** The date/time at which the notification was received. */
|
|
38
|
+
public receivedAt: Date;
|
|
39
|
+
|
|
40
|
+
/** The list of id of the target tweet(s) of the notification. */
|
|
41
|
+
public target: string[];
|
|
42
|
+
|
|
43
|
+
/** The type of notification. */
|
|
44
|
+
public type?: ENotificationType;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* @param notification - The raw notification details.
|
|
48
|
+
*/
|
|
49
|
+
public constructor(notification: INotification) {
|
|
50
|
+
// Getting the original notification type
|
|
51
|
+
const notificationType: string | undefined = findKeyByValue(ENotificationTypeOriginal, notification.icon.id);
|
|
52
|
+
|
|
53
|
+
this.from = notification.template?.aggregateUserActionsV1?.fromUsers
|
|
54
|
+
? notification.template.aggregateUserActionsV1.fromUsers.map((item) => item.user.id)
|
|
55
|
+
: [];
|
|
56
|
+
this.id = notification.id;
|
|
57
|
+
this.message = notification.message.text;
|
|
58
|
+
this.receivedAt = new Date(Number(notification.timestampMs));
|
|
59
|
+
this.target = notification.template?.aggregateUserActionsV1?.targetObjects
|
|
60
|
+
? notification.template.aggregateUserActionsV1.targetObjects.map((item) => item.tweet.id)
|
|
61
|
+
: [];
|
|
62
|
+
this.type = notificationType
|
|
63
|
+
? ENotificationType[notificationType as keyof typeof ENotificationType]
|
|
64
|
+
: ENotificationType.UNDEFINED;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Extracts and deserializes the list of notifications from the given raw response data.
|
|
69
|
+
*
|
|
70
|
+
* @param response - The raw response data.
|
|
71
|
+
*
|
|
72
|
+
* @returns The deserialized list of notifications.
|
|
73
|
+
*
|
|
74
|
+
* @internal
|
|
75
|
+
*/
|
|
76
|
+
public static list(response: NonNullable<unknown>): Notification[] {
|
|
77
|
+
const notifications: Notification[] = [];
|
|
78
|
+
|
|
79
|
+
// Extracting notifications
|
|
80
|
+
if ((response as IUserNotificationsResponse).globalObjects.notifications) {
|
|
81
|
+
// Iterating over the raw list of notifications
|
|
82
|
+
for (const [, value] of Object.entries(
|
|
83
|
+
(response as IUserNotificationsResponse).globalObjects.notifications,
|
|
84
|
+
)) {
|
|
85
|
+
notifications.push(new Notification(value as INotification));
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return notifications;
|
|
90
|
+
}
|
|
91
|
+
}
|
package/src/models/data/Tweet.ts
CHANGED
|
@@ -3,7 +3,6 @@ import {
|
|
|
3
3
|
IExtendedMedia as IRawExtendedMedia,
|
|
4
4
|
ITweet as IRawTweet,
|
|
5
5
|
IEntities as IRawTweetEntities,
|
|
6
|
-
IResponse,
|
|
7
6
|
ITimelineTweet,
|
|
8
7
|
ITweet,
|
|
9
8
|
} from 'rettiwt-core';
|
|
@@ -48,8 +47,8 @@ export class Tweet {
|
|
|
48
47
|
/** The number of quotes of the tweet. */
|
|
49
48
|
public quoteCount: number;
|
|
50
49
|
|
|
51
|
-
/** The
|
|
52
|
-
public quoted?:
|
|
50
|
+
/** The tweet which is quoted in the tweet. */
|
|
51
|
+
public quoted?: Tweet;
|
|
53
52
|
|
|
54
53
|
/** The number of replies to the tweet. */
|
|
55
54
|
public replyCount: number;
|
|
@@ -60,7 +59,7 @@ export class Tweet {
|
|
|
60
59
|
/** The number of retweets of the tweet. */
|
|
61
60
|
public retweetCount: number;
|
|
62
61
|
|
|
63
|
-
/** The tweet which
|
|
62
|
+
/** The tweet which is retweeted in this tweet (if any). */
|
|
64
63
|
public retweetedTweet?: Tweet;
|
|
65
64
|
|
|
66
65
|
/** The details of the user who made the tweet. */
|
|
@@ -78,7 +77,7 @@ export class Tweet {
|
|
|
78
77
|
this.tweetBy = new User(tweet.core.user_results.result);
|
|
79
78
|
this.entities = new TweetEntities(tweet.legacy.entities);
|
|
80
79
|
this.media = tweet.legacy.extended_entities?.media?.map((media) => new TweetMedia(media));
|
|
81
|
-
this.quoted = tweet.
|
|
80
|
+
this.quoted = tweet.quoted_status_result ? new Tweet(tweet.quoted_status_result.result) : undefined;
|
|
82
81
|
this.fullText = tweet.note_tweet ? tweet.note_tweet.note_tweet_results.result.text : tweet.legacy.full_text;
|
|
83
82
|
this.replyTo = tweet.legacy.in_reply_to_status_id_str;
|
|
84
83
|
this.lang = tweet.legacy.lang;
|
|
@@ -102,7 +101,7 @@ export class Tweet {
|
|
|
102
101
|
*
|
|
103
102
|
* @internal
|
|
104
103
|
*/
|
|
105
|
-
public static list(response:
|
|
104
|
+
public static list(response: NonNullable<unknown>): Tweet[] {
|
|
106
105
|
const tweets: Tweet[] = [];
|
|
107
106
|
|
|
108
107
|
// Extracting the matching data
|
|
@@ -137,7 +136,7 @@ export class Tweet {
|
|
|
137
136
|
*
|
|
138
137
|
* @internal
|
|
139
138
|
*/
|
|
140
|
-
public static single(response:
|
|
139
|
+
public static single(response: NonNullable<unknown>, id: string): Tweet | undefined {
|
|
141
140
|
const tweets: Tweet[] = [];
|
|
142
141
|
|
|
143
142
|
// Extracting the matching data
|
package/src/models/data/User.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { IUser as IRawUser,
|
|
1
|
+
import { IUser as IRawUser, ITimelineUser, IUser } from 'rettiwt-core';
|
|
2
2
|
|
|
3
3
|
import { ELogActions } from '../../enums/Logging';
|
|
4
4
|
import { findByFilter } from '../../helper/JsonUtils';
|
|
@@ -81,7 +81,7 @@ export class User {
|
|
|
81
81
|
*
|
|
82
82
|
* @internal
|
|
83
83
|
*/
|
|
84
|
-
public static list(response:
|
|
84
|
+
public static list(response: NonNullable<unknown>): User[] {
|
|
85
85
|
const users: User[] = [];
|
|
86
86
|
|
|
87
87
|
// Extracting the matching data
|
|
@@ -115,7 +115,7 @@ export class User {
|
|
|
115
115
|
*
|
|
116
116
|
* @internal
|
|
117
117
|
*/
|
|
118
|
-
public static single(response:
|
|
118
|
+
public static single(response: NonNullable<unknown>): User | undefined {
|
|
119
119
|
const users: User[] = [];
|
|
120
120
|
|
|
121
121
|
// Extracting the matching data
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Auth } from 'rettiwt-auth';
|
|
2
2
|
|
|
3
|
+
import { EApiErrors } from '../../enums/Api';
|
|
3
4
|
import { IRettiwtConfig } from '../../types/RettiwtConfig';
|
|
4
5
|
|
|
5
6
|
import { FetcherService } from './FetcherService';
|
|
@@ -19,6 +20,55 @@ export class AuthService extends FetcherService {
|
|
|
19
20
|
super(config);
|
|
20
21
|
}
|
|
21
22
|
|
|
23
|
+
/**
|
|
24
|
+
* Decodes the encoded cookie string.
|
|
25
|
+
*
|
|
26
|
+
* @param encodedCookies - The encoded cookie string to decode.
|
|
27
|
+
* @returns The decoded cookie string.
|
|
28
|
+
*/
|
|
29
|
+
public static decodeCookie(encodedCookies: string): string {
|
|
30
|
+
// Decoding the encoded cookie string
|
|
31
|
+
const decodedCookies: string = Buffer.from(encodedCookies, 'base64').toString('ascii');
|
|
32
|
+
|
|
33
|
+
return decodedCookies;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Encodes the given cookie string.
|
|
38
|
+
*
|
|
39
|
+
* @param cookieString - The cookie string to encode.
|
|
40
|
+
* @returns The encoded cookie string.
|
|
41
|
+
*/
|
|
42
|
+
public static encodeCookie(cookieString: string): string {
|
|
43
|
+
// Encoding the cookie string to base64
|
|
44
|
+
const encodedCookies: string = Buffer.from(cookieString).toString('base64');
|
|
45
|
+
|
|
46
|
+
return encodedCookies;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Gets the user's id from the given API key.
|
|
51
|
+
*
|
|
52
|
+
* @param apiKey - The API key.
|
|
53
|
+
* @returns The user id associated with the API key.
|
|
54
|
+
*/
|
|
55
|
+
public static getUserId(apiKey: string): string {
|
|
56
|
+
// Getting the cookie string from the API key
|
|
57
|
+
const cookieString: string = AuthService.decodeCookie(apiKey);
|
|
58
|
+
|
|
59
|
+
// Searching for the user id in the cookie string
|
|
60
|
+
const searchResults: string[] | null = cookieString.match(/((?<=twid="u=)(.*)(?="))|((?<=twid=u%3D)(.*)(?=;))/);
|
|
61
|
+
|
|
62
|
+
// If user id was found
|
|
63
|
+
if (searchResults) {
|
|
64
|
+
return searchResults[0];
|
|
65
|
+
}
|
|
66
|
+
// If user id was not found
|
|
67
|
+
else {
|
|
68
|
+
throw new Error(EApiErrors.BAD_AUTHENTICATION);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
22
72
|
/**
|
|
23
73
|
* Login to twitter as guest.
|
|
24
74
|
*
|
|
@@ -91,7 +141,7 @@ export class AuthService extends FetcherService {
|
|
|
91
141
|
).toHeader().cookie as string) ?? '';
|
|
92
142
|
|
|
93
143
|
// Converting the credentials to base64 string
|
|
94
|
-
apiKey =
|
|
144
|
+
apiKey = AuthService.encodeCookie(apiKey);
|
|
95
145
|
|
|
96
146
|
return apiKey;
|
|
97
147
|
}
|
|
@@ -17,6 +17,8 @@ import { IRettiwtConfig } from '../../types/RettiwtConfig';
|
|
|
17
17
|
import { ErrorService } from '../internal/ErrorService';
|
|
18
18
|
import { LogService } from '../internal/LogService';
|
|
19
19
|
|
|
20
|
+
import { AuthService } from './AuthService';
|
|
21
|
+
|
|
20
22
|
/**
|
|
21
23
|
* The base service that handles all HTTP requests.
|
|
22
24
|
*
|
|
@@ -32,9 +34,6 @@ export class FetcherService {
|
|
|
32
34
|
/** The guest key to use for authenticating against Twitter API as guest. */
|
|
33
35
|
private readonly guestKey?: string;
|
|
34
36
|
|
|
35
|
-
/** Whether the instance is authenticated or not. */
|
|
36
|
-
private readonly isAuthenticated: boolean;
|
|
37
|
-
|
|
38
37
|
/** The URL To the proxy server to use for all others. */
|
|
39
38
|
private readonly proxyUrl?: URL;
|
|
40
39
|
|
|
@@ -44,6 +43,9 @@ export class FetcherService {
|
|
|
44
43
|
/** The URL to the proxy server to use only for authentication. */
|
|
45
44
|
protected readonly authProxyUrl?: URL;
|
|
46
45
|
|
|
46
|
+
/** The id of the authenticated user (if any). */
|
|
47
|
+
protected readonly userId?: string;
|
|
48
|
+
|
|
47
49
|
/**
|
|
48
50
|
* @param config - The config object for configuring the Rettiwt instance.
|
|
49
51
|
*/
|
|
@@ -51,7 +53,7 @@ export class FetcherService {
|
|
|
51
53
|
LogService.enabled = config?.logging ?? false;
|
|
52
54
|
this.apiKey = config?.apiKey;
|
|
53
55
|
this.guestKey = config?.guestKey;
|
|
54
|
-
this.
|
|
56
|
+
this.userId = config?.apiKey ? AuthService.getUserId(config.apiKey) : undefined;
|
|
55
57
|
this.authProxyUrl = config?.authProxyUrl ?? config?.proxyUrl;
|
|
56
58
|
this.proxyUrl = config?.proxyUrl;
|
|
57
59
|
this.timeout = config?.timeout ?? 0;
|
|
@@ -67,10 +69,10 @@ export class FetcherService {
|
|
|
67
69
|
*/
|
|
68
70
|
private checkAuthorization(resource: EResourceType): void {
|
|
69
71
|
// Logging
|
|
70
|
-
LogService.log(ELogActions.AUTHORIZATION, { authenticated: this.
|
|
72
|
+
LogService.log(ELogActions.AUTHORIZATION, { authenticated: this.userId != undefined });
|
|
71
73
|
|
|
72
74
|
// Checking authorization status
|
|
73
|
-
if (!allowGuestAuthentication.includes(resource) && this.
|
|
75
|
+
if (!allowGuestAuthentication.includes(resource) && this.userId == undefined) {
|
|
74
76
|
throw new Error(EApiErrors.RESOURCE_NOT_ALLOWED);
|
|
75
77
|
}
|
|
76
78
|
}
|
|
@@ -85,8 +87,7 @@ export class FetcherService {
|
|
|
85
87
|
// Logging
|
|
86
88
|
LogService.log(ELogActions.GET, { target: 'USER_CREDENTIAL' });
|
|
87
89
|
|
|
88
|
-
|
|
89
|
-
return new AuthCredential(cookies);
|
|
90
|
+
return new AuthCredential(AuthService.decodeCookie(this.apiKey).split(';'));
|
|
90
91
|
} else if (this.guestKey) {
|
|
91
92
|
// Logging
|
|
92
93
|
LogService.log(ELogActions.GET, { target: 'GUEST_CREDENTIAL' });
|