rettiwt-api 6.3.0-alpha.1 → 7.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 (130) 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/UserAbout.d.ts +44 -0
  38. package/dist/models/data/UserAbout.js +129 -0
  39. package/dist/models/data/UserAbout.js.map +1 -0
  40. package/dist/requests/Space.d.ts +15 -0
  41. package/dist/requests/Space.js +74 -0
  42. package/dist/requests/Space.js.map +1 -0
  43. package/dist/requests/Tweet.d.ts +4 -0
  44. package/dist/requests/Tweet.js +57 -0
  45. package/dist/requests/Tweet.js.map +1 -1
  46. package/dist/requests/User.d.ts +21 -0
  47. package/dist/requests/User.js +64 -0
  48. package/dist/requests/User.js.map +1 -1
  49. package/dist/services/internal/AuthService.d.ts +25 -0
  50. package/dist/services/internal/AuthService.js +121 -0
  51. package/dist/services/internal/AuthService.js.map +1 -1
  52. package/dist/services/public/DirectMessageService.js +3 -3
  53. package/dist/services/public/DirectMessageService.js.map +1 -1
  54. package/dist/services/public/FetcherService.d.ts +4 -3
  55. package/dist/services/public/FetcherService.js +22 -16
  56. package/dist/services/public/FetcherService.js.map +1 -1
  57. package/dist/services/public/ListService.js +5 -5
  58. package/dist/services/public/ListService.js.map +1 -1
  59. package/dist/services/public/SpaceService.d.ts +42 -0
  60. package/dist/services/public/SpaceService.js +60 -0
  61. package/dist/services/public/SpaceService.js.map +1 -0
  62. package/dist/services/public/TweetService.js +26 -23
  63. package/dist/services/public/TweetService.js.map +1 -1
  64. package/dist/services/public/UserService.d.ts +79 -0
  65. package/dist/services/public/UserService.js +203 -23
  66. package/dist/services/public/UserService.js.map +1 -1
  67. package/dist/types/RettiwtConfig.d.ts +33 -3
  68. package/dist/types/args/FetchArgs.d.ts +35 -1
  69. package/dist/types/args/PostArgs.d.ts +44 -1
  70. package/dist/types/data/Space.d.ts +89 -0
  71. package/dist/types/data/Space.js +3 -0
  72. package/dist/types/data/Space.js.map +1 -0
  73. package/dist/types/data/UserAbout.d.ts +68 -0
  74. package/dist/types/data/UserAbout.js +3 -0
  75. package/dist/types/data/UserAbout.js.map +1 -0
  76. package/dist/types/raw/base/Space.d.ts +43 -22
  77. package/dist/types/raw/space/AudioSpaceById.d.ts +50 -0
  78. package/dist/types/raw/space/AudioSpaceById.js +4 -0
  79. package/dist/types/raw/space/AudioSpaceById.js.map +1 -0
  80. package/dist/types/raw/space/Details.d.ts +2 -309
  81. package/dist/types/raw/tweet/Post.d.ts +16 -1
  82. package/dist/types/raw/user/About.d.ts +65 -0
  83. package/dist/types/raw/user/About.js +4 -0
  84. package/dist/types/raw/user/About.js.map +1 -0
  85. package/dist/types/raw/user/ChangePassword.d.ts +8 -0
  86. package/dist/types/raw/user/ChangePassword.js +3 -0
  87. package/dist/types/raw/user/ChangePassword.js.map +1 -0
  88. package/dist/types/raw/user/ProfileUpdate.d.ts +1 -0
  89. package/dist/types/raw/user/Settings.d.ts +21 -0
  90. package/dist/types/raw/user/Settings.js +4 -0
  91. package/dist/types/raw/user/Settings.js.map +1 -0
  92. package/package.json +5 -3
  93. package/src/Rettiwt.ts +10 -3
  94. package/src/cli.ts +3 -1
  95. package/src/collections/Extractors.ts +22 -3
  96. package/src/collections/Groups.ts +8 -0
  97. package/src/collections/Requests.ts +11 -0
  98. package/src/commands/Space.ts +46 -0
  99. package/src/commands/User.ts +159 -0
  100. package/src/enums/Resource.ts +9 -0
  101. package/src/index.ts +11 -1
  102. package/src/models/RettiwtConfig.ts +81 -6
  103. package/src/models/args/FetchArgs.ts +6 -0
  104. package/src/models/args/PostArgs.ts +58 -1
  105. package/src/models/data/Space.ts +201 -0
  106. package/src/models/data/UserAbout.ts +161 -0
  107. package/src/requests/Space.ts +76 -0
  108. package/src/requests/Tweet.ts +59 -0
  109. package/src/requests/User.ts +69 -0
  110. package/src/services/internal/AuthService.ts +149 -1
  111. package/src/services/public/DirectMessageService.ts +3 -3
  112. package/src/services/public/FetcherService.ts +25 -18
  113. package/src/services/public/ListService.ts +5 -5
  114. package/src/services/public/SpaceService.ts +65 -0
  115. package/src/services/public/TweetService.ts +27 -24
  116. package/src/services/public/UserService.ts +247 -23
  117. package/src/types/RettiwtConfig.ts +35 -3
  118. package/src/types/args/FetchArgs.ts +41 -1
  119. package/src/types/args/PostArgs.ts +50 -1
  120. package/src/types/data/Space.ts +122 -0
  121. package/src/types/data/UserAbout.ts +87 -0
  122. package/src/types/raw/base/Space.ts +42 -22
  123. package/src/types/raw/space/AudioSpaceById.ts +57 -0
  124. package/src/types/raw/space/Details.ts +3 -352
  125. package/src/types/raw/tweet/Post.ts +19 -1
  126. package/src/types/raw/user/About.ts +77 -0
  127. package/src/types/raw/user/ChangePassword.ts +8 -0
  128. package/src/types/raw/user/ProfileUpdate.ts +1 -0
  129. package/src/types/raw/user/Settings.ts +23 -0
  130. package/tsconfig.json +2 -2
@@ -0,0 +1,65 @@
1
+ /**
2
+ * The raw data received when fetching the about profile of the given user.
3
+ *
4
+ * @public
5
+ */
6
+ export interface IUserAboutResponse {
7
+ data: Data;
8
+ }
9
+ interface Data {
10
+ user_result_by_screen_name: UserResultByScreenName;
11
+ }
12
+ interface UserResultByScreenName {
13
+ result: IUserAboutResult;
14
+ id: string;
15
+ }
16
+ export interface IUserAboutResult {
17
+ __typename: string;
18
+ id: string;
19
+ rest_id: string;
20
+ avatar?: Avatar;
21
+ core?: Core;
22
+ profile_image_shape?: string;
23
+ verification?: Verification;
24
+ affiliates_highlighted_label?: unknown;
25
+ is_blue_verified?: boolean;
26
+ privacy?: Privacy;
27
+ about_profile?: AboutProfile;
28
+ verification_info?: VerificationInfo;
29
+ identity_profile_labels_highlighted_label?: unknown;
30
+ }
31
+ interface Avatar {
32
+ image_url: string;
33
+ }
34
+ interface Core {
35
+ created_at: string;
36
+ name: string;
37
+ screen_name: string;
38
+ }
39
+ interface Verification {
40
+ verified: boolean;
41
+ }
42
+ interface Privacy {
43
+ protected: boolean;
44
+ }
45
+ interface AboutProfile {
46
+ created_country_accurate?: boolean;
47
+ account_based_in?: string;
48
+ location_accurate?: boolean;
49
+ learn_more_url?: string;
50
+ source?: string;
51
+ username_changes?: UsernameChanges;
52
+ }
53
+ interface UsernameChanges {
54
+ count?: string;
55
+ last_changed_at_msec?: string;
56
+ }
57
+ interface VerificationInfo {
58
+ reason?: VerificationReason;
59
+ id?: string;
60
+ is_identity_verified?: boolean;
61
+ }
62
+ interface VerificationReason {
63
+ verified_since_msec?: string;
64
+ }
65
+ export {};
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ /* eslint-disable */
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ //# sourceMappingURL=About.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"About.js","sourceRoot":"","sources":["../../../../src/types/raw/user/About.ts"],"names":[],"mappings":";AAAA,oBAAoB"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * The raw data received when changing the account password.
3
+ *
4
+ * @public
5
+ */
6
+ export interface IUserChangePasswordResponse {
7
+ status: string;
8
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=ChangePassword.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChangePassword.js","sourceRoot":"","sources":["../../../../src/types/raw/user/ChangePassword.ts"],"names":[],"mappings":""}
@@ -37,6 +37,7 @@ export interface IUserProfileUpdateResponse {
37
37
  profile_image_url: string;
38
38
  profile_image_url_https: string;
39
39
  profile_banner_url: string;
40
+ profile_banner_url_https?: string;
40
41
  profile_link_color: string;
41
42
  profile_sidebar_border_color: string;
42
43
  profile_sidebar_fill_color: string;
@@ -0,0 +1,21 @@
1
+ /**
2
+ * The raw data received from the account settings endpoint.
3
+ *
4
+ * @public
5
+ */
6
+ export interface IUserSettingsResponse {
7
+ screen_name: string;
8
+ protected: boolean;
9
+ language: string;
10
+ geo_enabled: boolean;
11
+ discoverable_by_email: boolean;
12
+ discoverable_by_mobile_phone: boolean;
13
+ use_cookie_personalization: boolean;
14
+ sleep_time: {
15
+ enabled: boolean;
16
+ end_time: any;
17
+ start_time: any;
18
+ };
19
+ display_sensitive_media: boolean;
20
+ allow_media_tagging: string;
21
+ }
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ /* eslint-disable @typescript-eslint/naming-convention, @typescript-eslint/no-explicit-any */
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ //# sourceMappingURL=Settings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Settings.js","sourceRoot":"","sources":["../../../../src/types/raw/user/Settings.ts"],"names":[],"mappings":";AAAA,6FAA6F"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rettiwt-api",
3
- "version": "6.3.0-alpha.1",
3
+ "version": "7.0.0",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "description": "An API for fetching data from TwitterAPI, without any rate limits!",
@@ -40,7 +40,7 @@
40
40
  "@typescript-eslint/parser": "^8.24.0",
41
41
  "eslint": "^9.20.1",
42
42
  "eslint-plugin-import": "^2.31.0",
43
- "eslint-plugin-tsdoc": "^0.4.0",
43
+ "eslint-plugin-tsdoc": "^0.5.2",
44
44
  "nodemon": "^3.1.9",
45
45
  "prettier": "^3.5.1",
46
46
  "typedoc": "^0.27.7",
@@ -51,9 +51,11 @@
51
51
  "chalk": "^5.4.1",
52
52
  "commander": "^11.1.0",
53
53
  "cookiejar": "^2.1.4",
54
+ "http-proxy-agent": "^7.0.2",
54
55
  "https-proxy-agent": "^7.0.6",
55
- "jsdom": "^27.2.0",
56
+ "linkedom": "^0.18.12",
56
57
  "node-html-parser": "^7.0.1",
58
+ "socks-proxy-agent": "^8.0.5",
57
59
  "x-client-transaction-id": "^0.2.0"
58
60
  }
59
61
  }
package/src/Rettiwt.ts CHANGED
@@ -1,6 +1,9 @@
1
+ import { AxiosProxyConfig } from 'axios';
2
+
1
3
  import { RettiwtConfig } from './models/RettiwtConfig';
2
4
  import { DirectMessageService } from './services/public/DirectMessageService';
3
5
  import { ListService } from './services/public/ListService';
6
+ import { SpaceService } from './services/public/SpaceService';
4
7
  import { TweetService } from './services/public/TweetService';
5
8
  import { UserService } from './services/public/UserService';
6
9
  import { IRettiwtConfig } from './types/RettiwtConfig';
@@ -56,6 +59,9 @@ export class Rettiwt {
56
59
  /** The instance used to fetch data related to lists. */
57
60
  public list: ListService;
58
61
 
62
+ /** The instance used to fetch data related to spaces. */
63
+ public space: SpaceService;
64
+
59
65
  /** The instance used to fetch data related to tweets. */
60
66
  public tweet: TweetService;
61
67
 
@@ -71,6 +77,7 @@ export class Rettiwt {
71
77
  this._config = new RettiwtConfig(config);
72
78
  this.dm = new DirectMessageService(this._config);
73
79
  this.list = new ListService(this._config);
80
+ this.space = new SpaceService(this._config);
74
81
  this.tweet = new TweetService(this._config);
75
82
  this.user = new UserService(this._config);
76
83
  }
@@ -90,8 +97,8 @@ export class Rettiwt {
90
97
  this._config.headers = headers;
91
98
  }
92
99
 
93
- /** Set the proxy URL for the current instance. */
94
- public set proxyUrl(proxyUrl: URL) {
95
- this._config.proxyUrl = proxyUrl;
100
+ /** Set the proxy for the current instance. */
101
+ public set proxy(proxy: AxiosProxyConfig | string | undefined | null) {
102
+ this._config.proxy = proxy;
96
103
  }
97
104
  }
package/src/cli.ts CHANGED
@@ -4,6 +4,7 @@ import { createCommand } from 'commander';
4
4
 
5
5
  import dm from './commands/DirectMessage';
6
6
  import list from './commands/List';
7
+ import space from './commands/Space';
7
8
  import tweet from './commands/Tweet';
8
9
  import user from './commands/User';
9
10
  import { Rettiwt } from './Rettiwt';
@@ -32,7 +33,7 @@ Program.parse();
32
33
  const RettiwtInstance = new Rettiwt({
33
34
  apiKey: process.env.API_KEY ?? (Program.opts().key as string),
34
35
  logging: Program.opts().log ? true : false,
35
- proxyUrl: Program.opts().proxy as URL,
36
+ proxy: Program.opts().proxy as string,
36
37
  timeout: Program.opts().timeout ? Number(Program.opts().timeout) : undefined,
37
38
  maxRetries: Program.opts().retries as number,
38
39
  delay: Program.opts().delay as number,
@@ -41,6 +42,7 @@ const RettiwtInstance = new Rettiwt({
41
42
  // Adding sub-commands
42
43
  Program.addCommand(dm(RettiwtInstance));
43
44
  Program.addCommand(list(RettiwtInstance));
45
+ Program.addCommand(space(RettiwtInstance));
44
46
  Program.addCommand(tweet(RettiwtInstance));
45
47
  Program.addCommand(user(RettiwtInstance));
46
48
 
@@ -6,8 +6,10 @@ import { CursoredData } from '../models/data/CursoredData';
6
6
  import { Inbox } from '../models/data/Inbox';
7
7
  import { List } from '../models/data/List';
8
8
  import { Notification } from '../models/data/Notification';
9
+ import { Space } from '../models/data/Space';
9
10
  import { Tweet } from '../models/data/Tweet';
10
11
  import { User } from '../models/data/User';
12
+ import { UserAbout } from '../models/data/UserAbout';
11
13
  import { IConversationTimelineResponse } from '../types/raw/dm/Conversation';
12
14
  import { IInboxInitialResponse } from '../types/raw/dm/InboxInitial';
13
15
  import { IInboxTimelineResponse } from '../types/raw/dm/InboxTimeline';
@@ -17,12 +19,13 @@ import { IListMembersResponse } from '../types/raw/list/Members';
17
19
  import { IListMemberRemoveResponse } from '../types/raw/list/RemoveMember';
18
20
  import { IListTweetsResponse } from '../types/raw/list/Tweets';
19
21
  import { IMediaInitializeUploadResponse } from '../types/raw/media/InitalizeUpload';
22
+ import { IAudioSpaceByIdResponse } from '../types/raw/space/AudioSpaceById';
20
23
  import { ITweetBookmarkResponse } from '../types/raw/tweet/Bookmark';
21
24
  import { ITweetDetailsResponse } from '../types/raw/tweet/Details';
22
25
  import { ITweetDetailsBulkResponse } from '../types/raw/tweet/DetailsBulk';
23
26
  import { ITweetLikeResponse } from '../types/raw/tweet/Like';
24
27
  import { ITweetLikersResponse } from '../types/raw/tweet/Likers';
25
- import { ITweetPostResponse } from '../types/raw/tweet/Post';
28
+ import { ITweetPostNoteResponse, ITweetPostResponse } from '../types/raw/tweet/Post';
26
29
  import { ITweetRepliesResponse } from '../types/raw/tweet/Replies';
27
30
  import { ITweetRetweetResponse } from '../types/raw/tweet/Retweet';
28
31
  import { ITweetRetweetersResponse } from '../types/raw/tweet/Retweeters';
@@ -33,11 +36,13 @@ import { ITweetUnlikeResponse } from '../types/raw/tweet/Unlike';
33
36
  import { ITweetUnpostResponse } from '../types/raw/tweet/Unpost';
34
37
  import { ITweetUnretweetResponse } from '../types/raw/tweet/Unretweet';
35
38
  import { ITweetUnscheduleResponse } from '../types/raw/tweet/Unschedule';
39
+ import { IUserAboutResponse } from '../types/raw/user/About';
36
40
  import { IUserAffiliatesResponse } from '../types/raw/user/Affiliates';
37
41
  import { IUserAnalyticsResponse } from '../types/raw/user/Analytics';
38
42
  import { IUserBookmarkFoldersResponse } from '../types/raw/user/BookmarkFolders';
39
43
  import { IUserBookmarkFolderTweetsResponse } from '../types/raw/user/BookmarkFolderTweets';
40
44
  import { IUserBookmarksResponse } from '../types/raw/user/Bookmarks';
45
+ import { IUserChangePasswordResponse } from '../types/raw/user/ChangePassword';
41
46
  import { IUserDetailsResponse } from '../types/raw/user/Details';
42
47
  import { IUserDetailsBulkResponse } from '../types/raw/user/DetailsBulk';
43
48
  import { IUserFollowResponse } from '../types/raw/user/Follow';
@@ -52,6 +57,7 @@ import { IUserNotificationsResponse } from '../types/raw/user/Notifications';
52
57
  import { IUserProfileUpdateResponse } from '../types/raw/user/ProfileUpdate';
53
58
  import { IUserRecommendedResponse } from '../types/raw/user/Recommended';
54
59
  import { IUserSearchResponse } from '../types/raw/user/Search';
60
+ import { IUserSettingsResponse } from '../types/raw/user/Settings';
55
61
  import { IUserSubscriptionsResponse } from '../types/raw/user/Subscriptions';
56
62
  import { IUserTweetsResponse } from '../types/raw/user/Tweets';
57
63
  import { IUserTweetsAndRepliesResponse } from '../types/raw/user/TweetsAndReplies';
@@ -85,6 +91,8 @@ export const Extractors = {
85
91
  DM_INBOX_INITIAL_STATE: (response: IInboxInitialResponse): Inbox => new Inbox(response),
86
92
  DM_INBOX_TIMELINE: (response: IInboxTimelineResponse): Inbox => new Inbox(response),
87
93
 
94
+ SPACE_DETAILS: (response: IAudioSpaceByIdResponse): Space | undefined => Space.single(response),
95
+
88
96
  TWEET_BOOKMARK: (response: ITweetBookmarkResponse): boolean => response?.data?.tweet_bookmark_put === 'Done',
89
97
  TWEET_DETAILS: (response: ITweetDetailsResponse, id: string): Tweet | undefined => Tweet.single(response, id),
90
98
  TWEET_DETAILS_ALT: (response: ITweetRepliesResponse, id: string): Tweet | undefined => Tweet.single(response, id),
@@ -92,8 +100,12 @@ export const Extractors = {
92
100
  TWEET_LIKE: (response: ITweetLikeResponse): boolean => (response?.data?.favorite_tweet ? true : false),
93
101
  TWEET_LIKERS: (response: ITweetLikersResponse): CursoredData<User> =>
94
102
  new CursoredData<User>(response, BaseType.USER),
95
- TWEET_POST: (response: ITweetPostResponse): string =>
96
- response?.data?.create_tweet?.tweet_results?.result?.rest_id ?? undefined,
103
+ TWEET_POST: (response: ITweetPostResponse): string | undefined =>
104
+ response?.data?.create_tweet?.tweet_results?.result?.rest_id ??
105
+ response?.data?.create_note_tweet?.tweet_results?.result?.rest_id ??
106
+ undefined,
107
+ TWEET_POST_NOTE: (response: ITweetPostNoteResponse): string | undefined =>
108
+ response?.data?.notetweet_create?.tweet_results?.result?.rest_id ?? undefined,
97
109
  TWEET_REPLIES: (response: ITweetDetailsResponse): CursoredData<Tweet> =>
98
110
  new CursoredData<Tweet>(response, BaseType.TWEET),
99
111
  TWEET_RETWEET: (response: ITweetRetweetResponse): boolean => (response?.data?.create_retweet ? true : false),
@@ -119,6 +131,7 @@ export const Extractors = {
119
131
  new CursoredData<BookmarkFolder>(response, BaseType.BOOKMARK_FOLDER),
120
132
  USER_BOOKMARK_FOLDER_TWEETS: (response: IUserBookmarkFolderTweetsResponse): CursoredData<Tweet> =>
121
133
  new CursoredData<Tweet>(response, BaseType.TWEET),
134
+ USER_ABOUT_BY_USERNAME: (response: IUserAboutResponse): UserAbout | undefined => UserAbout.single(response),
122
135
  USER_DETAILS_BY_USERNAME: (response: IUserDetailsResponse): User | undefined => User.single(response),
123
136
  USER_DETAILS_BY_ID: (response: IUserDetailsResponse): User | undefined => User.single(response),
124
137
  USER_DETAILS_BY_IDS_BULK: (response: IUserDetailsBulkResponse, ids: string[]): User[] =>
@@ -150,6 +163,12 @@ export const Extractors = {
150
163
  new CursoredData<Tweet>(response, BaseType.TWEET),
151
164
  USER_UNFOLLOW: (response: IUserUnfollowResponse): boolean => (response?.id ? true : false),
152
165
  USER_PROFILE_UPDATE: (response: IUserProfileUpdateResponse): boolean => (response?.name ? true : false),
166
+ USER_PROFILE_IMAGE_UPDATE: (response: IUserProfileUpdateResponse): boolean =>
167
+ response?.profile_image_url || response?.profile_image_url_https ? true : false,
168
+ USER_PROFILE_BANNER_UPDATE: (response: IUserProfileUpdateResponse): boolean =>
169
+ !response || response?.profile_banner_url || response?.profile_banner_url_https ? true : false,
170
+ USER_USERNAME_CHANGE: (response: IUserSettingsResponse): string | undefined => response?.screen_name ?? undefined,
171
+ USER_PASSWORD_CHANGE: (response: IUserChangePasswordResponse): boolean => response?.status === 'ok',
153
172
 
154
173
  /* eslint-enable @typescript-eslint/naming-convention */
155
174
  };
@@ -9,6 +9,7 @@ export const AllowGuestAuthenticationGroup = [
9
9
  ResourceType.TWEET_DETAILS,
10
10
  ResourceType.USER_DETAILS_BY_USERNAME,
11
11
  ResourceType.USER_TIMELINE,
12
+ ResourceType.SPACE_DETAILS,
12
13
  ];
13
14
 
14
15
  /**
@@ -23,6 +24,7 @@ export const FetchResourcesGroup = [
23
24
  ResourceType.DM_CONVERSATION,
24
25
  ResourceType.DM_INBOX_INITIAL_STATE,
25
26
  ResourceType.DM_INBOX_TIMELINE,
27
+ ResourceType.SPACE_DETAILS,
26
28
  ResourceType.TWEET_DETAILS,
27
29
  ResourceType.TWEET_DETAILS_ALT,
28
30
  ResourceType.TWEET_DETAILS_BULK,
@@ -35,6 +37,7 @@ export const FetchResourcesGroup = [
35
37
  ResourceType.USER_BOOKMARKS,
36
38
  ResourceType.USER_BOOKMARK_FOLDERS,
37
39
  ResourceType.USER_BOOKMARK_FOLDER_TWEETS,
40
+ ResourceType.USER_ABOUT_BY_USERNAME,
38
41
  ResourceType.USER_DETAILS_BY_USERNAME,
39
42
  ResourceType.USER_DETAILS_BY_ID,
40
43
  ResourceType.USER_DETAILS_BY_IDS_BULK,
@@ -68,6 +71,7 @@ export const PostResourcesGroup = [
68
71
  ResourceType.TWEET_BOOKMARK,
69
72
  ResourceType.TWEET_LIKE,
70
73
  ResourceType.TWEET_POST,
74
+ ResourceType.TWEET_POST_NOTE,
71
75
  ResourceType.TWEET_RETWEET,
72
76
  ResourceType.TWEET_SCHEDULE,
73
77
  ResourceType.TWEET_UNBOOKMARK,
@@ -78,4 +82,8 @@ export const PostResourcesGroup = [
78
82
  ResourceType.USER_FOLLOW,
79
83
  ResourceType.USER_UNFOLLOW,
80
84
  ResourceType.USER_PROFILE_UPDATE,
85
+ ResourceType.USER_PROFILE_IMAGE_UPDATE,
86
+ ResourceType.USER_PROFILE_BANNER_UPDATE,
87
+ ResourceType.USER_USERNAME_CHANGE,
88
+ ResourceType.USER_PASSWORD_CHANGE,
81
89
  ];
@@ -4,6 +4,7 @@ import { ResourceType } from '../enums/Resource';
4
4
  import { DMRequests } from '../requests/DirectMessage';
5
5
  import { ListRequests } from '../requests/List';
6
6
  import { MediaRequests } from '../requests/Media';
7
+ import { SpaceRequests } from '../requests/Space';
7
8
  import { TweetRequests } from '../requests/Tweet';
8
9
  import { UserRequests } from '../requests/User';
9
10
  import { IFetchArgs } from '../types/args/FetchArgs';
@@ -34,6 +35,9 @@ export const Requests: { [key in keyof typeof ResourceType]: (args: IFetchArgs |
34
35
  DM_INBOX_TIMELINE: (args: IFetchArgs) => DMRequests.inboxTimeline(args.maxId),
35
36
  DM_DELETE_CONVERSATION: (args: IPostArgs) => DMRequests.deleteConversation(args.conversationId!),
36
37
 
38
+ SPACE_DETAILS: (args: IFetchArgs) =>
39
+ SpaceRequests.details(args.id!, args.withReplays, args.withListeners, args.isMetatagsQuery),
40
+
37
41
  TWEET_BOOKMARK: (args: IPostArgs) => TweetRequests.bookmark(args.id!),
38
42
  TWEET_DETAILS: (args: IFetchArgs) => TweetRequests.details(args.id!),
39
43
  TWEET_DETAILS_ALT: (args: IFetchArgs) => TweetRequests.replies(args.id!),
@@ -41,6 +45,7 @@ export const Requests: { [key in keyof typeof ResourceType]: (args: IFetchArgs |
41
45
  TWEET_LIKE: (args: IPostArgs) => TweetRequests.like(args.id!),
42
46
  TWEET_LIKERS: (args: IFetchArgs) => TweetRequests.likers(args.id!, args.count, args.cursor),
43
47
  TWEET_POST: (args: IPostArgs) => TweetRequests.post(args.tweet!),
48
+ TWEET_POST_NOTE: (args: IPostArgs) => TweetRequests.postNote(args.tweet!),
44
49
  TWEET_REPLIES: (args: IFetchArgs) =>
45
50
  TweetRequests.replies(args.id!, args.cursor, args.sortBy ? TweetRepliesSortTypeMap[args.sortBy] : undefined),
46
51
  TWEET_RETWEET: (args: IPostArgs) => TweetRequests.retweet(args.id!),
@@ -66,6 +71,7 @@ export const Requests: { [key in keyof typeof ResourceType]: (args: IFetchArgs |
66
71
  USER_BOOKMARK_FOLDERS: (args: IFetchArgs) => UserRequests.bookmarkFolders(args.cursor),
67
72
  USER_BOOKMARK_FOLDER_TWEETS: (args: IFetchArgs) =>
68
73
  UserRequests.bookmarkFolderTweets(args.id!, args.count, args.cursor),
74
+ USER_ABOUT_BY_USERNAME: (args: IFetchArgs) => UserRequests.aboutByUsername(args.id!),
69
75
  USER_DETAILS_BY_USERNAME: (args: IFetchArgs) => UserRequests.detailsByUsername(args.id!),
70
76
  USER_DETAILS_BY_ID: (args: IFetchArgs) => UserRequests.detailsById(args.id!),
71
77
  USER_DETAILS_BY_IDS_BULK: (args: IFetchArgs) => UserRequests.bulkDetailsByIds(args.ids!),
@@ -85,6 +91,11 @@ export const Requests: { [key in keyof typeof ResourceType]: (args: IFetchArgs |
85
91
  USER_TIMELINE_AND_REPLIES: (args: IFetchArgs) => UserRequests.tweetsAndReplies(args.id!, args.count, args.cursor),
86
92
  USER_UNFOLLOW: (args: IPostArgs) => UserRequests.unfollow(args.id!),
87
93
  USER_PROFILE_UPDATE: (args: IPostArgs) => UserRequests.updateProfile(args.profileOptions!),
94
+ USER_PROFILE_IMAGE_UPDATE: (args: IPostArgs) => UserRequests.updateProfileImage(args.profileImage!),
95
+ USER_PROFILE_BANNER_UPDATE: (args: IPostArgs) => UserRequests.updateProfileBanner(args.profileBanner!),
96
+ USER_USERNAME_CHANGE: (args: IPostArgs) => UserRequests.changeUsername(args.username!),
97
+ USER_PASSWORD_CHANGE: (args: IPostArgs) =>
98
+ UserRequests.changePassword(args.changePassword!.currentPassword, args.changePassword!.newPassword),
88
99
 
89
100
  /* eslint-enable @typescript-eslint/naming-convention */
90
101
  };
@@ -0,0 +1,46 @@
1
+ import { Command, createCommand } from 'commander';
2
+
3
+ import { output } from '../helper/CliUtils';
4
+ import { Rettiwt } from '../Rettiwt';
5
+
6
+ interface ISpaceDetailsOptions {
7
+ withReplays?: boolean;
8
+ withListeners?: boolean;
9
+ metatags?: boolean;
10
+ }
11
+
12
+ /**
13
+ * Creates a new 'space' command which uses the given Rettiwt instance.
14
+ *
15
+ * @param rettiwt - The Rettiwt instance to use.
16
+ * @returns The created 'space' command.
17
+ */
18
+ function createSpaceCommand(rettiwt: Rettiwt): Command {
19
+ // Creating the 'space' command
20
+ const space = createCommand('space').description('Access resources related to spaces');
21
+
22
+ // Details
23
+ space
24
+ .command('details')
25
+ .description('Fetch the details of a space with the given id')
26
+ .argument('<id>', 'The id of the space')
27
+ .option('--with-replays', 'Include replay information')
28
+ .option('--with-listeners', 'Include listeners information')
29
+ .option('--metatags', 'Request metatags in the response')
30
+ .action(async (id: string, options?: ISpaceDetailsOptions) => {
31
+ try {
32
+ const details = await rettiwt.space.details(id, {
33
+ withReplays: options?.withReplays,
34
+ withListeners: options?.withListeners,
35
+ isMetatagsQuery: options?.metatags,
36
+ });
37
+ output(details);
38
+ } catch (error) {
39
+ output(error);
40
+ }
41
+ });
42
+
43
+ return space;
44
+ }
45
+
46
+ export default createSpaceCommand;
@@ -1,3 +1,7 @@
1
+ import { readFileSync } from 'fs';
2
+ import { createInterface } from 'readline/promises';
3
+ import { Writable } from 'stream';
4
+
1
5
  import { Command, createCommand } from 'commander';
2
6
 
3
7
  import { RawAnalyticsGranularity, RawAnalyticsMetric } from '../enums/raw/Analytics';
@@ -110,6 +114,19 @@ function createUserCommand(rettiwt: Rettiwt): Command {
110
114
  }
111
115
  });
112
116
 
117
+ // About
118
+ user.command('about')
119
+ .description('Fetch the about profile of the user with the given username')
120
+ .argument('<username>', 'The username of the user')
121
+ .action(async (username: string) => {
122
+ try {
123
+ const about = await rettiwt.user.about(username);
124
+ output(about);
125
+ } catch (error) {
126
+ output(error);
127
+ }
128
+ });
129
+
113
130
  // Details
114
131
  user.command('details')
115
132
  .description('Fetch the details of the user with the given id/username')
@@ -340,9 +357,144 @@ function createUserCommand(rettiwt: Rettiwt): Command {
340
357
  }
341
358
  });
342
359
 
360
+ // Change Password
361
+ user.command('change-password')
362
+ .description('Change your account password')
363
+ .option('--show-new-key', 'Include rotated apiKey in the output')
364
+ .action(async (options?: UserPasswordChangeOptions) => {
365
+ try {
366
+ const initialApiKey = rettiwt.apiKey;
367
+ const currentPassword = await promptHidden('Current password: ');
368
+ const newPassword = await promptHidden('New password: ');
369
+ const confirmPassword = await promptHidden('Confirm new password: ');
370
+
371
+ if (newPassword !== confirmPassword) {
372
+ throw new Error('New password confirmation does not match');
373
+ }
374
+ if (newPassword === currentPassword) {
375
+ throw new Error('New password must be different from current password');
376
+ }
377
+
378
+ const result = await rettiwt.user.changePassword(currentPassword, newPassword);
379
+ const apiKeyUpdated = initialApiKey !== rettiwt.apiKey;
380
+ const response = {
381
+ success: result,
382
+ apiKeyUpdated: result ? apiKeyUpdated : false,
383
+ ...(options?.showNewKey ? { apiKey: rettiwt.apiKey } : {}),
384
+ };
385
+
386
+ output(response);
387
+ } catch (error) {
388
+ output(error);
389
+ }
390
+ });
391
+
392
+ // Change Username
393
+ user.command('change-username')
394
+ .description('Change your username')
395
+ .argument('<username>', 'The new username (with or without @)')
396
+ .action(async (username: string) => {
397
+ try {
398
+ const result = await rettiwt.user.changeUsername(username);
399
+ output(result);
400
+ } catch (error) {
401
+ output(error);
402
+ }
403
+ });
404
+
405
+ // Update Profile Banner
406
+ user.command('update-profile-banner')
407
+ .description('Update your profile banner from an image file path')
408
+ .argument('<path>', 'The path to the banner image file')
409
+ .action(async (path: string) => {
410
+ try {
411
+ const result = await rettiwt.user.updateProfileBanner(fileToBase64(path));
412
+ output(result);
413
+ } catch (error) {
414
+ output(error);
415
+ }
416
+ });
417
+
418
+ // Update Profile Image
419
+ user.command('update-profile-image')
420
+ .description('Update your profile image from an image file path')
421
+ .argument('<path>', 'The path to the profile image file')
422
+ .action(async (path: string) => {
423
+ try {
424
+ const result = await rettiwt.user.updateProfileImage(fileToBase64(path));
425
+ output(result);
426
+ } catch (error) {
427
+ output(error);
428
+ }
429
+ });
430
+
343
431
  return user;
344
432
  }
345
433
 
434
+ /**
435
+ * Reads a file and returns its base64 representation.
436
+ *
437
+ * @param path - The path to the file.
438
+ * @returns The base64 representation of the file contents.
439
+ */
440
+ function fileToBase64(path: string): string {
441
+ if (path.trim().length === 0) {
442
+ throw new Error('File path cannot be empty');
443
+ }
444
+
445
+ try {
446
+ return readFileSync(path).toString('base64');
447
+ } catch (error) {
448
+ if (error instanceof Error) {
449
+ throw new Error(`Could not read file at '${path}': ${error.message}`);
450
+ }
451
+
452
+ throw new Error(`Could not read file at '${path}'`);
453
+ }
454
+ }
455
+
456
+ /**
457
+ * Prompts user for hidden input without echoing typed characters.
458
+ *
459
+ * @param query - The prompt text.
460
+ * @returns The provided value.
461
+ */
462
+ async function promptHidden(query: string): Promise<string> {
463
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
464
+ throw new Error('Password prompt requires an interactive terminal');
465
+ }
466
+
467
+ let queryShown = false;
468
+
469
+ const mutedOutput = new Writable({
470
+ write(chunk: Buffer | string, encoding: BufferEncoding, callback: (error?: Error | null) => void): void {
471
+ const text = chunk.toString();
472
+
473
+ if (!queryShown) {
474
+ process.stdout.write(text);
475
+ queryShown = text.includes(query);
476
+ }
477
+
478
+ callback();
479
+ },
480
+ });
481
+
482
+ const input = createInterface({
483
+ input: process.stdin,
484
+ output: mutedOutput,
485
+ terminal: true,
486
+ });
487
+
488
+ try {
489
+ const value = await input.question(query);
490
+ process.stdout.write('\n');
491
+
492
+ return value;
493
+ } finally {
494
+ input.close();
495
+ }
496
+ }
497
+
346
498
  /**
347
499
  * The options for fetching user analytics.
348
500
  */
@@ -364,4 +516,11 @@ type UserProfileUpdateOptions = {
364
516
  description?: string;
365
517
  };
366
518
 
519
+ /**
520
+ * The options for changing account password.
521
+ */
522
+ type UserPasswordChangeOptions = {
523
+ showNewKey?: boolean;
524
+ };
525
+
367
526
  export default createUserCommand;
@@ -22,6 +22,9 @@ export enum ResourceType {
22
22
  DM_INBOX_TIMELINE = 'DM_INBOX_TIMELINE',
23
23
  DM_DELETE_CONVERSATION = 'DM_DELETE_CONVERSATION',
24
24
 
25
+ // SPACE
26
+ SPACE_DETAILS = 'SPACE_DETAILS',
27
+
25
28
  // TWEET
26
29
  TWEET_BOOKMARK = 'TWEET_BOOKMARK',
27
30
  TWEET_DETAILS = 'TWEET_DETAILS',
@@ -30,6 +33,7 @@ export enum ResourceType {
30
33
  TWEET_LIKE = 'TWEET_LIKE',
31
34
  TWEET_LIKERS = 'TWEET_LIKERS',
32
35
  TWEET_POST = 'TWEET_POST',
36
+ TWEET_POST_NOTE = 'TWEET_POST_NOTE',
33
37
  TWEET_REPLIES = 'TWEET_REPLIES',
34
38
  TWEET_RETWEET = 'TWEET_RETWEET',
35
39
  TWEET_RETWEETERS = 'TWEET_RETWEETERS',
@@ -47,6 +51,7 @@ export enum ResourceType {
47
51
  USER_BOOKMARKS = 'USER_BOOKMARKS',
48
52
  USER_BOOKMARK_FOLDERS = 'USER_BOOKMARK_FOLDERS',
49
53
  USER_BOOKMARK_FOLDER_TWEETS = 'USER_BOOKMARK_FOLDER_TWEETS',
54
+ USER_ABOUT_BY_USERNAME = 'USER_ABOUT_BY_USERNAME',
50
55
  USER_DETAILS_BY_USERNAME = 'USER_DETAILS_BY_USERNAME',
51
56
  USER_DETAILS_BY_ID = 'USER_DETAILS_BY_ID',
52
57
  USER_DETAILS_BY_IDS_BULK = 'USER_DETAILS_BY_IDS_BULK',
@@ -66,4 +71,8 @@ export enum ResourceType {
66
71
  USER_TIMELINE_AND_REPLIES = 'USER_TIMELINE_AND_REPLIES',
67
72
  USER_UNFOLLOW = 'USER_UNFOLLOW',
68
73
  USER_PROFILE_UPDATE = 'USER_PROFILE_UPDATE',
74
+ USER_PROFILE_IMAGE_UPDATE = 'USER_PROFILE_IMAGE_UPDATE',
75
+ USER_PROFILE_BANNER_UPDATE = 'USER_PROFILE_BANNER_UPDATE',
76
+ USER_USERNAME_CHANGE = 'USER_USERNAME_CHANGE',
77
+ USER_PASSWORD_CHANGE = 'USER_PASSWORD_CHANGE',
69
78
  }