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
@@ -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 = Buffer.from(apiKey).toString('base64');
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.isAuthenticated = config?.apiKey ? true : false;
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.isAuthenticated });
72
+ LogService.log(ELogActions.AUTHORIZATION, { authenticated: this.userId != undefined });
71
73
 
72
74
  // Checking authorization status
73
- if (!allowGuestAuthentication.includes(resource) && this.isAuthenticated == false) {
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
- const cookies = Buffer.from(this.apiKey, 'base64').toString('ascii').split(';');
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' });
@@ -5,14 +5,16 @@ import {
5
5
  IListTweetsResponse,
6
6
  ITweetDetailsResponse,
7
7
  ITweetLikeResponse,
8
- ITweetLikersResponse,
9
8
  ITweetPostResponse,
9
+ ITweetRepliesResponse,
10
10
  ITweetRetweetersResponse,
11
11
  ITweetRetweetResponse,
12
+ ITweetScheduleResponse,
12
13
  ITweetSearchResponse,
13
14
  ITweetUnlikeResponse,
14
15
  ITweetUnpostResponse,
15
16
  ITweetUnretweetResponse,
17
+ ITweetUnscheduleResponse,
16
18
  TweetFilter,
17
19
  } from 'rettiwt-core';
18
20
 
@@ -68,15 +70,32 @@ export class TweetService extends FetcherService {
68
70
  * ```
69
71
  */
70
72
  public async details(id: string): Promise<Tweet | undefined> {
71
- const resource = EResourceType.TWEET_DETAILS;
73
+ let resource: EResourceType;
72
74
 
73
- // Fetching raw tweet details
74
- const response = await this.request<ITweetDetailsResponse>(resource, { id: id });
75
+ // If user is authenticated
76
+ if (this.userId != undefined) {
77
+ resource = EResourceType.TWEET_DETAILS_ALT;
75
78
 
76
- // Deserializing response
77
- const data = extractors[resource](response, id);
79
+ // Fetching raw tweet details
80
+ const response = await this.request<ITweetRepliesResponse>(resource, { id: id });
78
81
 
79
- return data;
82
+ // Deserializing response
83
+ const data = extractors[resource](response, id);
84
+
85
+ return data;
86
+ }
87
+ // If user is not authenticated
88
+ else {
89
+ resource = EResourceType.TWEET_DETAILS;
90
+
91
+ // Fetching raw tweet details
92
+ const response = await this.request<ITweetDetailsResponse>(resource, { id: id });
93
+
94
+ // Deserializing response
95
+ const data = extractors[resource](response, id);
96
+
97
+ return data;
98
+ }
80
99
  }
81
100
 
82
101
  /**
@@ -117,58 +136,6 @@ export class TweetService extends FetcherService {
117
136
  return data;
118
137
  }
119
138
 
120
- /**
121
- * @deprecated
122
- * The method will be removed in the next release following the removal of the ability to see tweet likers by Twitter.
123
- * Currently, the method does not work.
124
- *
125
- * Get the list of users who liked a tweet.
126
- *
127
- * @param id - The id of the target tweet.
128
- * @param count - The number of likers to fetch, must be \<= 100.
129
- * @param cursor - The cursor to the batch of likers to fetch.
130
- *
131
- * @returns The list of users who liked the given tweet.
132
- *
133
- * @example
134
- * ```
135
- * import { Rettiwt } from 'rettiwt-api';
136
- *
137
- * // Creating a new Rettiwt instance using the given 'API_KEY'
138
- * const rettiwt = new Rettiwt({ apiKey: API_KEY });
139
- *
140
- * // Fetching the most recent 100 likers of the Tweet with id '1234567890'
141
- * rettiwt.tweet.likers('1234567890')
142
- * .then(res => {
143
- * console.log(res);
144
- * })
145
- * .catch(err => {
146
- * console.log(err);
147
- * });
148
- * ```
149
- */
150
- public async likers(id: string, count?: number, cursor?: string): Promise<CursoredData<User>> {
151
- // Deprecation warning
152
- console.log(`
153
- This method has been deprecated following the removal of the ability to see tweet liksers by Twitter.
154
- Currently, the method does not work.
155
- `);
156
-
157
- const resource = EResourceType.TWEET_LIKERS;
158
-
159
- // Fetching raw likers
160
- const response = await this.request<ITweetLikersResponse>(resource, {
161
- id: id,
162
- count: count,
163
- cursor: cursor,
164
- });
165
-
166
- // Deserializing response
167
- const data = extractors[resource](response);
168
-
169
- return data;
170
- }
171
-
172
139
  /**
173
140
  * Get the list of tweets from a tweet list.
174
141
  *
@@ -221,7 +188,7 @@ export class TweetService extends FetcherService {
221
188
  *
222
189
  * @param options - The options describing the tweet to be posted. Check {@link TweetArgs} for available options.
223
190
  *
224
- * @returns Whether posting was successful or not.
191
+ * @returns The id of the posted tweet.
225
192
  *
226
193
  * @example
227
194
  * Posting a simple text
@@ -385,6 +352,46 @@ export class TweetService extends FetcherService {
385
352
  return data;
386
353
  }
387
354
 
355
+ /**
356
+ * Schedule a tweet.
357
+ *
358
+ * @param options - The options describing the tweet to be posted. Check {@link TweetArgs} for available options.
359
+ *
360
+ * @returns The id of the schedule.
361
+ *
362
+ * @example
363
+ * Scheduling a simple text
364
+ * ```
365
+ * import { Rettiwt } from 'rettiwt-api';
366
+ *
367
+ * // Creating a new Rettiwt instance using the given 'API_KEY'
368
+ * const rettiwt = new Rettiwt({ apiKey: API_KEY });
369
+ *
370
+ * // Scheduling a tweet to posted at 19th of August, 2024, at 11:59:00 AM, in local time
371
+ * rettiwt.tweet.schedule({ text: 'Hello World!', scheduleFor: new Date('2024-08-19 23:59:00') })
372
+ * .then(res => {
373
+ * console.log(res);
374
+ * })
375
+ * .catch(err => {
376
+ * console.log(err);
377
+ * });
378
+ * ```
379
+ *
380
+ * @remarks
381
+ * Scheduling a tweet is similar to {@link post}ing, except that an extra parameter called `scheduleFor` is used.
382
+ */
383
+ public async schedule(options: TweetArgs): Promise<string | undefined> {
384
+ const resource = EResourceType.TWEET_SCHEDULE;
385
+
386
+ // Scheduling the tweet
387
+ const response = await this.request<ITweetScheduleResponse>(resource, { tweet: options });
388
+
389
+ // Deserializing response
390
+ const data = extractors[resource](response);
391
+
392
+ return data;
393
+ }
394
+
388
395
  /**
389
396
  * Search for tweets using a filter.
390
397
  *
@@ -604,6 +611,42 @@ export class TweetService extends FetcherService {
604
611
  return data;
605
612
  }
606
613
 
614
+ /**
615
+ * Unschedule a tweet.
616
+ *
617
+ * @param id - The id of the scheduled tweet.
618
+ *
619
+ * @returns Whether unscheduling was successful or not.
620
+ *
621
+ * @example
622
+ * ```
623
+ * import { Rettiwt } from 'rettiwt-api';
624
+ *
625
+ * // Creating a new Rettiwt instance using the given 'API_KEY'
626
+ * const rettiwt = new Rettiwt({ apiKey: API_KEY });
627
+ *
628
+ * // Unscheduling the Tweet with id '1234567890'
629
+ * rettiwt.tweet.unschedule('1234567890')
630
+ * .then(res => {
631
+ * console.log(res);
632
+ * })
633
+ * .catch(err => {
634
+ * console.log(err);
635
+ * });
636
+ * ```
637
+ */
638
+ public async unschedule(id: string): Promise<boolean> {
639
+ const resource = EResourceType.TWEET_UNSCHEDULE;
640
+
641
+ // Unscheduling the tweet
642
+ const response = await this.request<ITweetUnscheduleResponse>(resource, { id: id });
643
+
644
+ // Deserializing the response
645
+ const data = extractors[resource](response) ?? false;
646
+
647
+ return data;
648
+ }
649
+
607
650
  /**
608
651
  * Upload a media file to Twitter.
609
652
  *
@@ -309,9 +309,8 @@ export class UserService extends FetcherService {
309
309
  }
310
310
 
311
311
  /**
312
- * Get the list of tweets liked by a user.
312
+ * Get the list of tweets liked by the logged in user.
313
313
  *
314
- * @param id - The id of the target user.
315
314
  * @param count - The number of likes to fetch, must be \<= 100.
316
315
  * @param cursor - The cursor to the batch of likes to fetch.
317
316
  *
@@ -324,8 +323,8 @@ export class UserService extends FetcherService {
324
323
  * // Creating a new Rettiwt instance using the given 'API_KEY'
325
324
  * const rettiwt = new Rettiwt({ apiKey: API_KEY });
326
325
  *
327
- * // Fetching the most recent 100 liked Tweets of the User with id '1234567890'
328
- * rettiwt.user.likes('1234567890')
326
+ * // Fetching the most recent 100 liked Tweets of the logged in User
327
+ * rettiwt.user.likes()
329
328
  * .then(res => {
330
329
  * console.log(res);
331
330
  * })
@@ -334,12 +333,12 @@ export class UserService extends FetcherService {
334
333
  * });
335
334
  * ```
336
335
  */
337
- public async likes(id: string, count?: number, cursor?: string): Promise<CursoredData<Tweet>> {
336
+ public async likes(count?: number, cursor?: string): Promise<CursoredData<Tweet>> {
338
337
  const resource = EResourceType.USER_LIKES;
339
338
 
340
339
  // Fetching raw list of likes
341
340
  const response = await this.request<IUserLikesResponse>(resource, {
342
- id: id,
341
+ id: this.userId,
343
342
  count: count,
344
343
  cursor: cursor,
345
344
  });