rettiwt-api 6.3.0-alpha.1 → 7.0.1

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 (138) hide show
  1. package/README.md +81 -31
  2. package/dist/Rettiwt.d.ts +6 -2
  3. package/dist/Rettiwt.js +7 -3
  4. package/dist/Rettiwt.js.map +1 -1
  5. package/dist/cli.js +3 -1
  6. package/dist/cli.js.map +1 -1
  7. package/dist/collections/Extractors.d.ts +15 -2
  8. package/dist/collections/Extractors.js +12 -1
  9. package/dist/collections/Extractors.js.map +1 -1
  10. package/dist/collections/Groups.js +8 -0
  11. package/dist/collections/Groups.js.map +1 -1
  12. package/dist/collections/Requests.js +8 -0
  13. package/dist/collections/Requests.js.map +1 -1
  14. package/dist/commands/Space.d.ts +10 -0
  15. package/dist/commands/Space.js +38 -0
  16. package/dist/commands/Space.js.map +1 -0
  17. package/dist/commands/User.js +139 -0
  18. package/dist/commands/User.js.map +1 -1
  19. package/dist/enums/Resource.d.ts +8 -1
  20. package/dist/enums/Resource.js +8 -0
  21. package/dist/enums/Resource.js.map +1 -1
  22. package/dist/index.d.ts +11 -1
  23. package/dist/index.js +6 -0
  24. package/dist/index.js.map +1 -1
  25. package/dist/models/RettiwtConfig.d.ts +26 -3
  26. package/dist/models/RettiwtConfig.js +68 -3
  27. package/dist/models/RettiwtConfig.js.map +1 -1
  28. package/dist/models/args/FetchArgs.d.ts +3 -0
  29. package/dist/models/args/FetchArgs.js +6 -0
  30. package/dist/models/args/FetchArgs.js.map +1 -1
  31. package/dist/models/args/PostArgs.d.ts +24 -1
  32. package/dist/models/args/PostArgs.js +52 -1
  33. package/dist/models/args/PostArgs.js.map +1 -1
  34. package/dist/models/data/Space.d.ts +70 -0
  35. package/dist/models/data/Space.js +177 -0
  36. package/dist/models/data/Space.js.map +1 -0
  37. package/dist/models/data/User.d.ts +1 -1
  38. package/dist/models/data/User.js +3 -3
  39. package/dist/models/data/User.js.map +1 -1
  40. package/dist/models/data/UserAbout.d.ts +44 -0
  41. package/dist/models/data/UserAbout.js +129 -0
  42. package/dist/models/data/UserAbout.js.map +1 -0
  43. package/dist/requests/Space.d.ts +15 -0
  44. package/dist/requests/Space.js +74 -0
  45. package/dist/requests/Space.js.map +1 -0
  46. package/dist/requests/Tweet.d.ts +4 -0
  47. package/dist/requests/Tweet.js +57 -0
  48. package/dist/requests/Tweet.js.map +1 -1
  49. package/dist/requests/User.d.ts +21 -0
  50. package/dist/requests/User.js +64 -0
  51. package/dist/requests/User.js.map +1 -1
  52. package/dist/services/internal/AuthService.d.ts +25 -0
  53. package/dist/services/internal/AuthService.js +121 -0
  54. package/dist/services/internal/AuthService.js.map +1 -1
  55. package/dist/services/public/DirectMessageService.js +3 -3
  56. package/dist/services/public/DirectMessageService.js.map +1 -1
  57. package/dist/services/public/FetcherService.d.ts +4 -3
  58. package/dist/services/public/FetcherService.js +22 -16
  59. package/dist/services/public/FetcherService.js.map +1 -1
  60. package/dist/services/public/ListService.js +5 -5
  61. package/dist/services/public/ListService.js.map +1 -1
  62. package/dist/services/public/SpaceService.d.ts +42 -0
  63. package/dist/services/public/SpaceService.js +60 -0
  64. package/dist/services/public/SpaceService.js.map +1 -0
  65. package/dist/services/public/TweetService.js +26 -23
  66. package/dist/services/public/TweetService.js.map +1 -1
  67. package/dist/services/public/UserService.d.ts +79 -0
  68. package/dist/services/public/UserService.js +203 -23
  69. package/dist/services/public/UserService.js.map +1 -1
  70. package/dist/types/RettiwtConfig.d.ts +33 -3
  71. package/dist/types/args/FetchArgs.d.ts +35 -1
  72. package/dist/types/args/PostArgs.d.ts +44 -1
  73. package/dist/types/data/Space.d.ts +89 -0
  74. package/dist/types/data/Space.js +3 -0
  75. package/dist/types/data/Space.js.map +1 -0
  76. package/dist/types/data/User.d.ts +2 -2
  77. package/dist/types/data/UserAbout.d.ts +68 -0
  78. package/dist/types/data/UserAbout.js +3 -0
  79. package/dist/types/data/UserAbout.js.map +1 -0
  80. package/dist/types/raw/base/Space.d.ts +43 -22
  81. package/dist/types/raw/base/User.d.ts +1 -1
  82. package/dist/types/raw/space/AudioSpaceById.d.ts +50 -0
  83. package/dist/types/raw/space/AudioSpaceById.js +4 -0
  84. package/dist/types/raw/space/AudioSpaceById.js.map +1 -0
  85. package/dist/types/raw/space/Details.d.ts +2 -309
  86. package/dist/types/raw/tweet/Post.d.ts +16 -1
  87. package/dist/types/raw/user/About.d.ts +65 -0
  88. package/dist/types/raw/user/About.js +4 -0
  89. package/dist/types/raw/user/About.js.map +1 -0
  90. package/dist/types/raw/user/ChangePassword.d.ts +8 -0
  91. package/dist/types/raw/user/ChangePassword.js +3 -0
  92. package/dist/types/raw/user/ChangePassword.js.map +1 -0
  93. package/dist/types/raw/user/ProfileUpdate.d.ts +1 -0
  94. package/dist/types/raw/user/Settings.d.ts +21 -0
  95. package/dist/types/raw/user/Settings.js +4 -0
  96. package/dist/types/raw/user/Settings.js.map +1 -0
  97. package/package.json +5 -3
  98. package/src/Rettiwt.ts +10 -3
  99. package/src/cli.ts +3 -1
  100. package/src/collections/Extractors.ts +22 -3
  101. package/src/collections/Groups.ts +8 -0
  102. package/src/collections/Requests.ts +11 -0
  103. package/src/commands/Space.ts +46 -0
  104. package/src/commands/User.ts +159 -0
  105. package/src/enums/Resource.ts +9 -0
  106. package/src/index.ts +11 -1
  107. package/src/models/RettiwtConfig.ts +81 -6
  108. package/src/models/args/FetchArgs.ts +6 -0
  109. package/src/models/args/PostArgs.ts +58 -1
  110. package/src/models/data/Space.ts +201 -0
  111. package/src/models/data/User.ts +3 -3
  112. package/src/models/data/UserAbout.ts +161 -0
  113. package/src/requests/Space.ts +76 -0
  114. package/src/requests/Tweet.ts +59 -0
  115. package/src/requests/User.ts +69 -0
  116. package/src/services/internal/AuthService.ts +149 -1
  117. package/src/services/public/DirectMessageService.ts +3 -3
  118. package/src/services/public/FetcherService.ts +25 -18
  119. package/src/services/public/ListService.ts +5 -5
  120. package/src/services/public/SpaceService.ts +65 -0
  121. package/src/services/public/TweetService.ts +27 -24
  122. package/src/services/public/UserService.ts +247 -23
  123. package/src/types/RettiwtConfig.ts +35 -3
  124. package/src/types/args/FetchArgs.ts +41 -1
  125. package/src/types/args/PostArgs.ts +50 -1
  126. package/src/types/data/Space.ts +122 -0
  127. package/src/types/data/User.ts +2 -2
  128. package/src/types/data/UserAbout.ts +87 -0
  129. package/src/types/raw/base/Space.ts +42 -22
  130. package/src/types/raw/base/User.ts +1 -1
  131. package/src/types/raw/space/AudioSpaceById.ts +57 -0
  132. package/src/types/raw/space/Details.ts +3 -352
  133. package/src/types/raw/tweet/Post.ts +19 -1
  134. package/src/types/raw/user/About.ts +77 -0
  135. package/src/types/raw/user/ChangePassword.ts +8 -0
  136. package/src/types/raw/user/ProfileUpdate.ts +1 -0
  137. package/src/types/raw/user/Settings.ts +23 -0
  138. package/tsconfig.json +2 -2
@@ -9,13 +9,16 @@ import { List } from '../../models/data/List';
9
9
  import { Notification } from '../../models/data/Notification';
10
10
  import { Tweet } from '../../models/data/Tweet';
11
11
  import { User } from '../../models/data/User';
12
+ import { UserAbout } from '../../models/data/UserAbout';
12
13
  import { RettiwtConfig } from '../../models/RettiwtConfig';
13
14
  import { IProfileUpdateOptions } from '../../types/args/ProfileArgs';
15
+ import { IUserAboutResponse } from '../../types/raw/user/About';
14
16
  import { IUserAffiliatesResponse } from '../../types/raw/user/Affiliates';
15
17
  import { IUserAnalyticsResponse } from '../../types/raw/user/Analytics';
16
18
  import { IUserBookmarkFoldersResponse } from '../../types/raw/user/BookmarkFolders';
17
19
  import { IUserBookmarkFolderTweetsResponse } from '../../types/raw/user/BookmarkFolderTweets';
18
20
  import { IUserBookmarksResponse } from '../../types/raw/user/Bookmarks';
21
+ import { IUserChangePasswordResponse } from '../../types/raw/user/ChangePassword';
19
22
  import { IUserDetailsResponse } from '../../types/raw/user/Details';
20
23
  import { IUserDetailsBulkResponse } from '../../types/raw/user/DetailsBulk';
21
24
  import { IUserFollowResponse } from '../../types/raw/user/Follow';
@@ -30,11 +33,14 @@ import { IUserNotificationsResponse } from '../../types/raw/user/Notifications';
30
33
  import { IUserProfileUpdateResponse } from '../../types/raw/user/ProfileUpdate';
31
34
  import { IUserRecommendedResponse } from '../../types/raw/user/Recommended';
32
35
  import { IUserSearchResponse } from '../../types/raw/user/Search';
36
+ import { IUserSettingsResponse } from '../../types/raw/user/Settings';
33
37
  import { IUserSubscriptionsResponse } from '../../types/raw/user/Subscriptions';
34
38
  import { IUserTweetsResponse } from '../../types/raw/user/Tweets';
35
39
  import { IUserTweetsAndRepliesResponse } from '../../types/raw/user/TweetsAndReplies';
36
40
  import { IUserUnfollowResponse } from '../../types/raw/user/Unfollow';
37
41
 
42
+ import { AuthService } from '../internal/AuthService';
43
+
38
44
  import { FetcherService } from './FetcherService';
39
45
 
40
46
  /**
@@ -52,6 +58,97 @@ export class UserService extends FetcherService {
52
58
  super(config);
53
59
  }
54
60
 
61
+ /**
62
+ * Gets the size in bytes of a base64 string.
63
+ *
64
+ * @param base64Data - The base64 data show size is required.
65
+ *
66
+ * @returns The size in bytes of the data.
67
+ */
68
+ private _base64ByteSize(base64Data: string): number {
69
+ const paddingMatch = base64Data.match(/=+$/);
70
+ const paddingLength = paddingMatch ? paddingMatch[0].length : 0;
71
+
72
+ return (base64Data.length * 3) / 4 - paddingLength;
73
+ }
74
+
75
+ /**
76
+ * Normalizes base64 data into just the raw base64 string.
77
+ *
78
+ * @param payload - The data to normalize.
79
+ *
80
+ * @returns The raw base64 part of the data.
81
+ */
82
+ private _normalizeBase64(payload: string): string {
83
+ const trimmedPayload = payload.trim();
84
+ const lowerCasePayload = trimmedPayload.toLowerCase();
85
+ const base64Marker = ';base64,';
86
+
87
+ if (lowerCasePayload.startsWith('data:')) {
88
+ const markerIndex = lowerCasePayload.indexOf(base64Marker);
89
+ if (markerIndex !== -1) {
90
+ return trimmedPayload.slice(markerIndex + base64Marker.length).trim();
91
+ }
92
+ }
93
+
94
+ return trimmedPayload;
95
+ }
96
+
97
+ private _validateBase64Payload(payload: string, fieldName: string): string {
98
+ const normalizedPayload = this._normalizeBase64(payload).replace(/\s+/g, '');
99
+
100
+ if (normalizedPayload.length === 0) {
101
+ throw new Error(`${fieldName} cannot be empty`);
102
+ }
103
+
104
+ if (!/^[A-Za-z0-9+/]*={0,2}$/.test(normalizedPayload)) {
105
+ throw new Error(`${fieldName} must be valid base64`);
106
+ }
107
+
108
+ return normalizedPayload;
109
+ }
110
+
111
+ /**
112
+ * Get the about profile of a user.
113
+ *
114
+ * @param userName - The username/screenname of the target user.
115
+ *
116
+ * @returns The about profile of the user.
117
+ *
118
+ * @example
119
+ *
120
+ * ```ts
121
+ * import { Rettiwt } from 'rettiwt-api';
122
+ *
123
+ * // Creating a new Rettiwt instance using the given 'API_KEY'
124
+ * const rettiwt = new Rettiwt({ apiKey: API_KEY });
125
+ *
126
+ * // Fetching the about profile of the User with username 'user1' or '@user1'
127
+ * rettiwt.user.about('user1') // or @user1
128
+ * .then(res => {
129
+ * console.log(res);
130
+ * })
131
+ * .catch(err => {
132
+ * console.log(err);
133
+ * });
134
+ * ```
135
+ */
136
+ public async about(userName: string): Promise<UserAbout | undefined> {
137
+ const resource = ResourceType.USER_ABOUT_BY_USERNAME;
138
+
139
+ if (userName.startsWith('@')) {
140
+ userName = userName.slice(1);
141
+ }
142
+
143
+ // Fetching raw about profile
144
+ const response = await this.request<IUserAboutResponse>(resource, { id: userName });
145
+
146
+ // Deserializing response
147
+ const data = Extractors[resource](response.data);
148
+
149
+ return data;
150
+ }
151
+
55
152
  /**
56
153
  * Get the list affiliates of a user.
57
154
  *
@@ -90,7 +187,7 @@ export class UserService extends FetcherService {
90
187
  });
91
188
 
92
189
  // Deserializing response
93
- const data = Extractors[resource](response);
190
+ const data = Extractors[resource](response.data);
94
191
 
95
192
  return data;
96
193
  }
@@ -148,7 +245,7 @@ export class UserService extends FetcherService {
148
245
  showVerifiedFollowers,
149
246
  });
150
247
 
151
- const data = Extractors[resource](response);
248
+ const data = Extractors[resource](response.data);
152
249
 
153
250
  return data;
154
251
  }
@@ -191,7 +288,7 @@ export class UserService extends FetcherService {
191
288
  });
192
289
 
193
290
  // Deserializing response
194
- const data = Extractors[resource](response);
291
+ const data = Extractors[resource](response.data);
195
292
 
196
293
  return data;
197
294
  }
@@ -230,7 +327,7 @@ export class UserService extends FetcherService {
230
327
  });
231
328
 
232
329
  // Deserializing response
233
- const data = Extractors[resource](response);
330
+ const data = Extractors[resource](response.data);
234
331
 
235
332
  return data;
236
333
  }
@@ -271,11 +368,86 @@ export class UserService extends FetcherService {
271
368
  });
272
369
 
273
370
  // Deserializing response
274
- const data = Extractors[resource](response);
371
+ const data = Extractors[resource](response.data);
372
+
373
+ return data;
374
+ }
375
+
376
+ /**
377
+ * Changes the password of the authenticated user.
378
+ *
379
+ * @param currentPassword - The current account password.
380
+ * @param newPassword - The new password to set.
381
+ * @returns Whether the password was changed successfully.
382
+ *
383
+ * @remarks
384
+ * After a successful password change, this method attempts to rotate the current
385
+ * `apiKey` using cookies returned by Twitter. If rotation is not possible, you
386
+ * must re-authenticate and obtain a new `apiKey` to continue making authenticated
387
+ * requests.
388
+ */
389
+ public async changePassword(currentPassword: string, newPassword: string): Promise<boolean> {
390
+ const resource = ResourceType.USER_PASSWORD_CHANGE;
391
+
392
+ // Changing the password
393
+ const response = await this.request<IUserChangePasswordResponse>(resource, {
394
+ changePassword: { currentPassword, newPassword },
395
+ });
396
+
397
+ // Getting if password change was successful or not
398
+ const data = Extractors[resource](response.data) ?? false;
399
+
400
+ // If password change was successful
401
+ if (data === true) {
402
+ // Getting the new API key
403
+ const newApiKey = AuthService.getApiKeyFromReponse(response);
404
+
405
+ // If new API key is generated, update current API key
406
+ if (newApiKey !== undefined) {
407
+ this.config.apiKey = newApiKey;
408
+ }
409
+
410
+ // Getting the new CSRF token and updating current API key
411
+ await AuthService.refreshCsrfToken(this.config);
412
+ }
275
413
 
276
414
  return data;
277
415
  }
278
416
 
417
+ /**
418
+ * Changes the username (screen_name) of the authenticated user.
419
+ *
420
+ * @param newUsername - The new username (with or without `@`).
421
+ * @returns Whether the username was changed successfully.
422
+ */
423
+ public async changeUsername(newUsername: string): Promise<boolean> {
424
+ const resource = ResourceType.USER_USERNAME_CHANGE;
425
+
426
+ // Strip @ prefix if present
427
+ const username = newUsername.startsWith('@') ? newUsername.slice(1) : newUsername;
428
+
429
+ // Username validation
430
+ if (username.length < 4) {
431
+ throw new Error('Username must be at least 4 characters long');
432
+ }
433
+ if (username.length > 15) {
434
+ throw new Error('Username cannot exceed 15 characters');
435
+ }
436
+ if (!/^[A-Za-z0-9_]+$/.test(username)) {
437
+ throw new Error('Username can only contain letters, numbers, and underscores');
438
+ }
439
+
440
+ // Changing the username
441
+ const response = await this.request<IUserSettingsResponse>(resource, {
442
+ username,
443
+ });
444
+
445
+ // Getting the updated username
446
+ const updatedUsername = Extractors[resource](response.data);
447
+
448
+ return updatedUsername?.toLowerCase() === username.toLowerCase();
449
+ }
450
+
279
451
  /**
280
452
  * Get the details of the logged in user.
281
453
  *
@@ -386,7 +558,7 @@ export class UserService extends FetcherService {
386
558
  const response = await this.request<IUserDetailsBulkResponse>(resource, { ids: id });
387
559
 
388
560
  // Deserializing response
389
- const data = Extractors[resource](response, id);
561
+ const data = Extractors[resource](response.data, id);
390
562
 
391
563
  return data;
392
564
  }
@@ -413,7 +585,7 @@ export class UserService extends FetcherService {
413
585
  const response = await this.request<IUserDetailsResponse>(resource, { id: id ?? this.config.userId });
414
586
 
415
587
  // Deserializing response
416
- const data = Extractors[resource](response);
588
+ const data = Extractors[resource](response.data);
417
589
 
418
590
  return data;
419
591
  }
@@ -453,7 +625,7 @@ export class UserService extends FetcherService {
453
625
  const response = await this.request<IUserFollowResponse>(ResourceType.USER_FOLLOW, { id: id });
454
626
 
455
627
  // Deserializing the response
456
- const data = Extractors[resource](response) ?? false;
628
+ const data = Extractors[resource](response.data) ?? false;
457
629
 
458
630
  return data;
459
631
  }
@@ -494,7 +666,7 @@ export class UserService extends FetcherService {
494
666
  });
495
667
 
496
668
  // Deserializing response
497
- const data = Extractors[resource](response);
669
+ const data = Extractors[resource](response.data);
498
670
 
499
671
  return data;
500
672
  }
@@ -537,7 +709,7 @@ export class UserService extends FetcherService {
537
709
  });
538
710
 
539
711
  // Deserializing response
540
- const data = Extractors[resource](response);
712
+ const data = Extractors[resource](response.data);
541
713
 
542
714
  return data;
543
715
  }
@@ -580,7 +752,7 @@ export class UserService extends FetcherService {
580
752
  });
581
753
 
582
754
  // Deserializing response
583
- const data = Extractors[resource](response);
755
+ const data = Extractors[resource](response.data);
584
756
 
585
757
  return data;
586
758
  }
@@ -623,7 +795,7 @@ export class UserService extends FetcherService {
623
795
  });
624
796
 
625
797
  // Deserializing response
626
- const data = Extractors[resource](response);
798
+ const data = Extractors[resource](response.data);
627
799
 
628
800
  return data;
629
801
  }
@@ -665,7 +837,7 @@ export class UserService extends FetcherService {
665
837
  });
666
838
 
667
839
  // Deserializing response
668
- const data = Extractors[resource](response);
840
+ const data = Extractors[resource](response.data);
669
841
 
670
842
  return data;
671
843
  }
@@ -707,7 +879,7 @@ export class UserService extends FetcherService {
707
879
  });
708
880
 
709
881
  // Deserializing response
710
- const data = Extractors[resource](response);
882
+ const data = Extractors[resource](response.data);
711
883
 
712
884
  return data;
713
885
  }
@@ -750,7 +922,7 @@ export class UserService extends FetcherService {
750
922
  });
751
923
 
752
924
  // Deserializing response
753
- const data = Extractors[resource](response);
925
+ const data = Extractors[resource](response.data);
754
926
 
755
927
  return data;
756
928
  }
@@ -807,7 +979,7 @@ export class UserService extends FetcherService {
807
979
  });
808
980
 
809
981
  // Deserializing response
810
- const notifications = Extractors[resource](response);
982
+ const notifications = Extractors[resource](response.data);
811
983
 
812
984
  // Sorting the notifications by time, from oldest to recent
813
985
  notifications.list.sort((a, b) => new Date(a.receivedAt).valueOf() - new Date(b.receivedAt).valueOf());
@@ -864,7 +1036,7 @@ export class UserService extends FetcherService {
864
1036
  });
865
1037
 
866
1038
  // Deserializing response
867
- const data = Extractors[resource](response);
1039
+ const data = Extractors[resource](response.data);
868
1040
 
869
1041
  return data;
870
1042
  }
@@ -911,7 +1083,7 @@ export class UserService extends FetcherService {
911
1083
  });
912
1084
 
913
1085
  // Deserializing response
914
- const data = Extractors[resource](response);
1086
+ const data = Extractors[resource](response.data);
915
1087
 
916
1088
  return data;
917
1089
  }
@@ -954,7 +1126,7 @@ export class UserService extends FetcherService {
954
1126
  });
955
1127
 
956
1128
  // Deserializing response
957
- const data = Extractors[resource](response);
1129
+ const data = Extractors[resource](response.data);
958
1130
 
959
1131
  return data;
960
1132
  }
@@ -997,7 +1169,7 @@ export class UserService extends FetcherService {
997
1169
  });
998
1170
 
999
1171
  // Deserializing response
1000
- const data = Extractors[resource](response);
1172
+ const data = Extractors[resource](response.data);
1001
1173
 
1002
1174
  return data;
1003
1175
  }
@@ -1045,7 +1217,7 @@ export class UserService extends FetcherService {
1045
1217
  });
1046
1218
 
1047
1219
  // Deserializing response
1048
- const data = Extractors[resource](response);
1220
+ const data = Extractors[resource](response.data);
1049
1221
 
1050
1222
  return data;
1051
1223
  }
@@ -1082,7 +1254,7 @@ export class UserService extends FetcherService {
1082
1254
  const response = await this.request<IUserUnfollowResponse>(ResourceType.USER_UNFOLLOW, { id: id });
1083
1255
 
1084
1256
  // Deserializing the response
1085
- const data = Extractors[resource](response) ?? false;
1257
+ const data = Extractors[resource](response.data) ?? false;
1086
1258
 
1087
1259
  return data;
1088
1260
  }
@@ -1147,7 +1319,59 @@ export class UserService extends FetcherService {
1147
1319
  const response = await this.request<IUserProfileUpdateResponse>(resource, { profileOptions: validatedOptions });
1148
1320
 
1149
1321
  // Deserializing the response
1150
- const data = Extractors[resource](response) ?? false;
1322
+ const data = Extractors[resource](response.data) ?? false;
1323
+
1324
+ return data;
1325
+ }
1326
+
1327
+ /**
1328
+ * Updates the profile banner of the authenticated user.
1329
+ *
1330
+ * @param bannerBase64 - The base64-encoded banner image data.
1331
+ * @returns Whether the profile banner was updated successfully.
1332
+ */
1333
+ public async updateProfileBanner(bannerBase64: string): Promise<boolean> {
1334
+ const resource = ResourceType.USER_PROFILE_BANNER_UPDATE;
1335
+
1336
+ const validatedBanner = this._validateBase64Payload(bannerBase64, 'Profile banner');
1337
+
1338
+ // Banner size validation (max 5 MB)
1339
+ const bannerSizeBytes = this._base64ByteSize(validatedBanner);
1340
+ if (bannerSizeBytes > 5 * 1024 * 1024) {
1341
+ throw new Error('Profile banner cannot exceed 5 MB');
1342
+ }
1343
+
1344
+ const response = await this.request<IUserProfileUpdateResponse>(resource, {
1345
+ profileBanner: validatedBanner,
1346
+ });
1347
+
1348
+ const data = Extractors[resource](response.data) ?? false;
1349
+
1350
+ return data;
1351
+ }
1352
+
1353
+ /**
1354
+ * Updates the profile image of the authenticated user.
1355
+ *
1356
+ * @param imageBase64 - The base64-encoded image data.
1357
+ * @returns Whether the profile image was updated successfully.
1358
+ */
1359
+ public async updateProfileImage(imageBase64: string): Promise<boolean> {
1360
+ const resource = ResourceType.USER_PROFILE_IMAGE_UPDATE;
1361
+
1362
+ const validatedImage = this._validateBase64Payload(imageBase64, 'Profile image');
1363
+
1364
+ // Image size validation (max 2 MB)
1365
+ const imageSizeBytes = this._base64ByteSize(validatedImage);
1366
+ if (imageSizeBytes > 2 * 1024 * 1024) {
1367
+ throw new Error('Profile image cannot exceed 2 MB');
1368
+ }
1369
+
1370
+ const response = await this.request<IUserProfileUpdateResponse>(resource, {
1371
+ profileImage: validatedImage,
1372
+ });
1373
+
1374
+ const data = Extractors[resource](response.data) ?? false;
1151
1375
 
1152
1376
  return data;
1153
1377
  }
@@ -1,3 +1,5 @@
1
+ import { AxiosProxyConfig, AxiosResponse } from 'axios';
2
+
1
3
  import { IErrorHandler } from './ErrorHandler';
2
4
 
3
5
  /**
@@ -10,11 +12,34 @@ export interface IRettiwtConfig {
10
12
  apiKey?: string;
11
13
 
12
14
  /**
13
- * Optional URL to proxy server to use for requests to Twitter API.
15
+ * The proxy to use.
16
+ *
17
+ * @remarks
18
+ * <br>
19
+ * - If set to anything besides `undefined`, disables Axios' built-in environment variable-set proxy.
20
+ *
21
+ * @example
22
+ * ```
23
+ * // Use custom proxy config via config object
24
+ * {
25
+ * proxy: {
26
+ * host: '127.0.0.1',
27
+ * port: 8080
28
+ * }
29
+ * }
30
+ *
31
+ * // Use custom proxy config via URL
32
+ * {
33
+ * proxy: 'https://127.0.0.1:8080'
34
+ * }
14
35
  *
15
- * @remarks When deploying to cloud platforms, if setting {@link IRettiwtConfig.authProxyUrl} does not resolve Error 429, then this might be required.
36
+ * // Use Axios environment variable for proxy
37
+ * {
38
+ * proxy: undefined
39
+ * }
40
+ * ```
16
41
  */
17
- proxyUrl?: URL;
42
+ proxy?: AxiosProxyConfig | string | null;
18
43
 
19
44
  /** The max wait time (in milli-seconds) for a response; if not set, Twitter server timeout is used. */
20
45
  timeout?: number;
@@ -22,6 +47,13 @@ export interface IRettiwtConfig {
22
47
  /** Whether to write logs to console or not. */
23
48
  logging?: boolean;
24
49
 
50
+ /**
51
+ * Optional response middleware to be executed on obtaining a successful response.
52
+ *
53
+ * @param response - The raw `AxiosReponse` object.
54
+ */
55
+ responseMiddleware?: (response: AxiosResponse) => void | Promise<void>;
56
+
25
57
  /** Optional custom error handler to define error conditions and process API/HTTP errors in responses. */
26
58
  errorHandler?: IErrorHandler;
27
59
 
@@ -71,7 +71,7 @@ export interface IFetchArgs {
71
71
  *
72
72
  * @remarks
73
73
  * - Required for all resources except {@link ResourceType.TWEET_SEARCH} and {@link ResourceType.USER_TIMELINE_RECOMMENDED}.
74
- * - For {@link ResourceType.USER_DETAILS_BY_USERNAME} and {@link ResourceType.USER_SEARCH}, can be alphanumeric, while for others, is strictly numeric.
74
+ * - For {@link ResourceType.USER_DETAILS_BY_USERNAME}, {@link ResourceType.USER_ABOUT_BY_USERNAME}, and {@link ResourceType.USER_SEARCH}, can be alphanumeric, while for others, is strictly numeric.
75
75
  */
76
76
  id?: string;
77
77
 
@@ -83,6 +83,30 @@ export interface IFetchArgs {
83
83
  */
84
84
  ids?: string[];
85
85
 
86
+ /**
87
+ * Whether to include replay information when fetching space details.
88
+ *
89
+ * @remarks
90
+ * - Only works for {@link ResourceType.SPACE_DETAILS}.
91
+ */
92
+ withReplays?: boolean;
93
+
94
+ /**
95
+ * Whether to include listeners information when fetching space details.
96
+ *
97
+ * @remarks
98
+ * - Only works for {@link ResourceType.SPACE_DETAILS}.
99
+ */
100
+ withListeners?: boolean;
101
+
102
+ /**
103
+ * Whether to request metatags for space details.
104
+ *
105
+ * @remarks
106
+ * - Only works for {@link ResourceType.SPACE_DETAILS}.
107
+ */
108
+ isMetatagsQuery?: boolean;
109
+
86
110
  /**
87
111
  * The sorting to use for tweet results.
88
112
  *
@@ -132,6 +156,22 @@ export interface IFetchArgs {
132
156
  showVerifiedFollowers?: boolean;
133
157
  }
134
158
 
159
+ /**
160
+ * Options specifying the data that is to be fetched for space details.
161
+ *
162
+ * @public
163
+ */
164
+ export interface ISpaceDetailsOptions {
165
+ /** Whether to include replay information. */
166
+ withReplays?: boolean;
167
+
168
+ /** Whether to include listeners information. */
169
+ withListeners?: boolean;
170
+
171
+ /** Whether the request is a metatags query. */
172
+ isMetatagsQuery?: boolean;
173
+ }
174
+
135
175
  /**
136
176
  * The filter to be used for searching tweets.
137
177
  *
@@ -20,9 +20,20 @@ export interface IPostArgs {
20
20
  * - {@link ResourceType.TWEET_UNRETWEET}
21
21
  * - {@link ResourceType.USER_FOLLOW}
22
22
  * - {@link ResourceType.USER_UNFOLLOW}
23
+ *
24
+ * For {@link ResourceType.USER_USERNAME_CHANGE}, use {@link IPostArgs.username}.
25
+ * `id` is still accepted for backward compatibility.
23
26
  */
24
27
  id?: string;
25
28
 
29
+ /**
30
+ * The new username to set.
31
+ *
32
+ * @remarks
33
+ * Required only when changing username using {@link ResourceType.USER_USERNAME_CHANGE}.
34
+ */
35
+ username?: string;
36
+
26
37
  /**
27
38
  * The tweet that is to be posted.
28
39
  *
@@ -67,6 +78,30 @@ export interface IPostArgs {
67
78
  * Required only when updating user profile using {@link ResourceType.USER_PROFILE_UPDATE}
68
79
  */
69
80
  profileOptions?: IProfileUpdateOptions;
81
+
82
+ /**
83
+ * Base64-encoded profile image data.
84
+ *
85
+ * @remarks
86
+ * Required only when updating profile image using {@link ResourceType.USER_PROFILE_IMAGE_UPDATE}.
87
+ */
88
+ profileImage?: string;
89
+
90
+ /**
91
+ * Base64-encoded profile banner data.
92
+ *
93
+ * @remarks
94
+ * Required only when updating profile banner using {@link ResourceType.USER_PROFILE_BANNER_UPDATE}.
95
+ */
96
+ profileBanner?: string;
97
+
98
+ /**
99
+ * Password change arguments.
100
+ *
101
+ * @remarks
102
+ * Required only when changing password using {@link ResourceType.USER_PASSWORD_CHANGE}.
103
+ */
104
+ changePassword?: IChangePasswordArgs;
70
105
  }
71
106
 
72
107
  /**
@@ -98,7 +133,8 @@ export interface INewTweet {
98
133
  * The text for the tweet to be created.
99
134
  *
100
135
  * @remarks
101
- * Length of the tweet must be \<= 280 characters.
136
+ * Length of the tweet must be \<= 280 characters for non-premium accounts.
137
+ * X Premium (Blue) accounts can post longer tweets (up to 25,000 characters).
102
138
  */
103
139
  text?: string;
104
140
  }
@@ -140,3 +176,16 @@ export interface IUploadArgs {
140
176
  */
141
177
  size?: number;
142
178
  }
179
+
180
+ /**
181
+ * Arguments for changing the account password.
182
+ *
183
+ * @public
184
+ */
185
+ export interface IChangePasswordArgs {
186
+ /** The current account password. */
187
+ currentPassword: string;
188
+
189
+ /** The new password to set. */
190
+ newPassword: string;
191
+ }