rettiwt-api 6.1.7 → 6.2.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 (89) hide show
  1. package/README.md +34 -0
  2. package/dist/collections/Extractors.d.ts +7 -0
  3. package/dist/collections/Extractors.js +3 -0
  4. package/dist/collections/Extractors.js.map +1 -1
  5. package/dist/collections/Groups.js +3 -0
  6. package/dist/collections/Groups.js.map +1 -1
  7. package/dist/collections/Requests.js +3 -0
  8. package/dist/collections/Requests.js.map +1 -1
  9. package/dist/commands/User.js +47 -0
  10. package/dist/commands/User.js.map +1 -1
  11. package/dist/enums/Data.d.ts +2 -1
  12. package/dist/enums/Data.js +1 -0
  13. package/dist/enums/Data.js.map +1 -1
  14. package/dist/enums/Resource.d.ts +4 -1
  15. package/dist/enums/Resource.js +3 -0
  16. package/dist/enums/Resource.js.map +1 -1
  17. package/dist/index.d.ts +8 -0
  18. package/dist/index.js +4 -0
  19. package/dist/index.js.map +1 -1
  20. package/dist/models/args/PostArgs.d.ts +3 -0
  21. package/dist/models/args/PostArgs.js +5 -0
  22. package/dist/models/args/PostArgs.js.map +1 -1
  23. package/dist/models/args/ProfileArgs.d.ts +16 -0
  24. package/dist/models/args/ProfileArgs.js +63 -0
  25. package/dist/models/args/ProfileArgs.js.map +1 -0
  26. package/dist/models/data/BookmarkFolder.d.ts +31 -0
  27. package/dist/models/data/BookmarkFolder.js +64 -0
  28. package/dist/models/data/BookmarkFolder.js.map +1 -0
  29. package/dist/models/data/CursoredData.d.ts +2 -1
  30. package/dist/models/data/CursoredData.js +7 -0
  31. package/dist/models/data/CursoredData.js.map +1 -1
  32. package/dist/models/data/Tweet.d.ts +4 -2
  33. package/dist/models/data/Tweet.js +4 -0
  34. package/dist/models/data/Tweet.js.map +1 -1
  35. package/dist/requests/User.d.ts +19 -0
  36. package/dist/requests/User.js +135 -5
  37. package/dist/requests/User.js.map +1 -1
  38. package/dist/services/public/UserService.d.ts +107 -0
  39. package/dist/services/public/UserService.js +135 -0
  40. package/dist/services/public/UserService.js.map +1 -1
  41. package/dist/types/args/PostArgs.d.ts +8 -0
  42. package/dist/types/args/ProfileArgs.d.ts +30 -0
  43. package/dist/types/args/ProfileArgs.js +3 -0
  44. package/dist/types/args/ProfileArgs.js.map +1 -0
  45. package/dist/types/data/BookmarkFolder.d.ts +11 -0
  46. package/dist/types/data/BookmarkFolder.js +3 -0
  47. package/dist/types/data/BookmarkFolder.js.map +1 -0
  48. package/dist/types/data/CursoredData.d.ts +2 -1
  49. package/dist/types/data/Tweet.d.ts +2 -0
  50. package/dist/types/raw/base/BookmarkFolder.d.ts +11 -0
  51. package/dist/types/raw/base/BookmarkFolder.js +3 -0
  52. package/dist/types/raw/base/BookmarkFolder.js.map +1 -0
  53. package/dist/types/raw/user/BookmarkFolderTweets.d.ts +44 -0
  54. package/dist/types/raw/user/BookmarkFolderTweets.js +4 -0
  55. package/dist/types/raw/user/BookmarkFolderTweets.js.map +1 -0
  56. package/dist/types/raw/user/BookmarkFolders.d.ts +33 -0
  57. package/dist/types/raw/user/BookmarkFolders.js +4 -0
  58. package/dist/types/raw/user/BookmarkFolders.js.map +1 -0
  59. package/dist/types/raw/user/ProfileUpdate.d.ts +75 -0
  60. package/dist/types/raw/user/ProfileUpdate.js +4 -0
  61. package/dist/types/raw/user/ProfileUpdate.js.map +1 -0
  62. package/package.json +3 -7
  63. package/playground/.env.example +1 -1
  64. package/playground/README.md +1 -1
  65. package/playground/index.js +1 -1
  66. package/playground/package.json +1 -1
  67. package/src/collections/Extractors.ts +9 -0
  68. package/src/collections/Groups.ts +3 -0
  69. package/src/collections/Requests.ts +4 -0
  70. package/src/commands/User.ts +61 -0
  71. package/src/enums/Data.ts +1 -0
  72. package/src/enums/Resource.ts +3 -0
  73. package/src/index.ts +8 -0
  74. package/src/models/args/PostArgs.ts +6 -0
  75. package/src/models/args/ProfileArgs.ts +68 -0
  76. package/src/models/data/BookmarkFolder.ts +73 -0
  77. package/src/models/data/CursoredData.ts +8 -2
  78. package/src/models/data/Tweet.ts +8 -2
  79. package/src/requests/User.ts +139 -5
  80. package/src/services/public/UserService.ts +153 -0
  81. package/src/types/args/PostArgs.ts +10 -0
  82. package/src/types/args/ProfileArgs.ts +33 -0
  83. package/src/types/data/BookmarkFolder.ts +12 -0
  84. package/src/types/data/CursoredData.ts +4 -1
  85. package/src/types/data/Tweet.ts +3 -0
  86. package/src/types/raw/base/BookmarkFolder.ts +12 -0
  87. package/src/types/raw/user/BookmarkFolderTweets.ts +53 -0
  88. package/src/types/raw/user/BookmarkFolders.ts +41 -0
  89. package/src/types/raw/user/ProfileUpdate.ts +80 -0
@@ -23,7 +23,7 @@ This playground is intended for developers to test and experiment with features
23
23
  2. **Environment Variables**
24
24
  Create a `.env` file in the `playground` directory with your API credentials:
25
25
  ```env
26
- ACCESS_TOKEN=your_access_token_here
26
+ API_KEY=your_api_key_here
27
27
  ```
28
28
 
29
29
  ### Usage
@@ -1,7 +1,7 @@
1
1
  import { Rettiwt } from 'rettiwt-api';
2
2
  import 'dotenv/config';
3
3
 
4
- const rettiwt = new Rettiwt({ apiKey: process.env.ACCESS_TOKEN });
4
+ const rettiwt = new Rettiwt({ apiKey: process.env.API_KEY });
5
5
 
6
6
  async function userDetails() {
7
7
  try {
@@ -10,6 +10,6 @@
10
10
  },
11
11
  "dependencies": {
12
12
  "dotenv": "^17.2.0",
13
- "rettiwt-api": "workspace"
13
+ "rettiwt-api": "file:../"
14
14
  }
15
15
  }
@@ -1,5 +1,6 @@
1
1
  import { BaseType } from '../enums/Data';
2
2
  import { Analytics } from '../models/data/Analytics';
3
+ import { BookmarkFolder } from '../models/data/BookmarkFolder';
3
4
  import { Conversation } from '../models/data/Conversation';
4
5
  import { CursoredData } from '../models/data/CursoredData';
5
6
  import { Inbox } from '../models/data/Inbox';
@@ -34,6 +35,8 @@ import { ITweetUnretweetResponse } from '../types/raw/tweet/Unretweet';
34
35
  import { ITweetUnscheduleResponse } from '../types/raw/tweet/Unschedule';
35
36
  import { IUserAffiliatesResponse } from '../types/raw/user/Affiliates';
36
37
  import { IUserAnalyticsResponse } from '../types/raw/user/Analytics';
38
+ import { IUserBookmarkFoldersResponse } from '../types/raw/user/BookmarkFolders';
39
+ import { IUserBookmarkFolderTweetsResponse } from '../types/raw/user/BookmarkFolderTweets';
37
40
  import { IUserBookmarksResponse } from '../types/raw/user/Bookmarks';
38
41
  import { IUserDetailsResponse } from '../types/raw/user/Details';
39
42
  import { IUserDetailsBulkResponse } from '../types/raw/user/DetailsBulk';
@@ -46,6 +49,7 @@ import { IUserLikesResponse } from '../types/raw/user/Likes';
46
49
  import { IUserListsResponse } from '../types/raw/user/Lists';
47
50
  import { IUserMediaResponse } from '../types/raw/user/Media';
48
51
  import { IUserNotificationsResponse } from '../types/raw/user/Notifications';
52
+ import { IUserProfileUpdateResponse } from '../types/raw/user/ProfileUpdate';
49
53
  import { IUserRecommendedResponse } from '../types/raw/user/Recommended';
50
54
  import { IUserSubscriptionsResponse } from '../types/raw/user/Subscriptions';
51
55
  import { IUserTweetsResponse } from '../types/raw/user/Tweets';
@@ -110,6 +114,10 @@ export const Extractors = {
110
114
  new Analytics(response.data.viewer_v2.user_results.result),
111
115
  USER_BOOKMARKS: (response: IUserBookmarksResponse): CursoredData<Tweet> =>
112
116
  new CursoredData<Tweet>(response, BaseType.TWEET),
117
+ USER_BOOKMARK_FOLDERS: (response: IUserBookmarkFoldersResponse): CursoredData<BookmarkFolder> =>
118
+ new CursoredData<BookmarkFolder>(response, BaseType.BOOKMARK_FOLDER),
119
+ USER_BOOKMARK_FOLDER_TWEETS: (response: IUserBookmarkFolderTweetsResponse): CursoredData<Tweet> =>
120
+ new CursoredData<Tweet>(response, BaseType.TWEET),
113
121
  USER_DETAILS_BY_USERNAME: (response: IUserDetailsResponse): User | undefined => User.single(response),
114
122
  USER_DETAILS_BY_ID: (response: IUserDetailsResponse): User | undefined => User.single(response),
115
123
  USER_DETAILS_BY_IDS_BULK: (response: IUserDetailsBulkResponse, ids: string[]): User[] =>
@@ -139,6 +147,7 @@ export const Extractors = {
139
147
  USER_TIMELINE_AND_REPLIES: (response: IUserTweetsAndRepliesResponse): CursoredData<Tweet> =>
140
148
  new CursoredData<Tweet>(response, BaseType.TWEET),
141
149
  USER_UNFOLLOW: (response: IUserUnfollowResponse): boolean => (response?.id ? true : false),
150
+ USER_PROFILE_UPDATE: (response: IUserProfileUpdateResponse): boolean => (response?.name ? true : false),
142
151
 
143
152
  /* eslint-enable @typescript-eslint/naming-convention */
144
153
  };
@@ -33,6 +33,8 @@ export const FetchResourcesGroup = [
33
33
  ResourceType.USER_AFFILIATES,
34
34
  ResourceType.USER_ANALYTICS,
35
35
  ResourceType.USER_BOOKMARKS,
36
+ ResourceType.USER_BOOKMARK_FOLDERS,
37
+ ResourceType.USER_BOOKMARK_FOLDER_TWEETS,
36
38
  ResourceType.USER_DETAILS_BY_USERNAME,
37
39
  ResourceType.USER_DETAILS_BY_ID,
38
40
  ResourceType.USER_DETAILS_BY_IDS_BULK,
@@ -74,4 +76,5 @@ export const PostResourcesGroup = [
74
76
  ResourceType.TWEET_UNSCHEDULE,
75
77
  ResourceType.USER_FOLLOW,
76
78
  ResourceType.USER_UNFOLLOW,
79
+ ResourceType.USER_PROFILE_UPDATE,
77
80
  ];
@@ -63,6 +63,9 @@ export const Requests: { [key in keyof typeof ResourceType]: (args: IFetchArgs |
63
63
  args.showVerifiedFollowers!,
64
64
  ),
65
65
  USER_BOOKMARKS: (args: IFetchArgs) => UserRequests.bookmarks(args.count, args.cursor),
66
+ USER_BOOKMARK_FOLDERS: (args: IFetchArgs) => UserRequests.bookmarkFolders(args.cursor),
67
+ USER_BOOKMARK_FOLDER_TWEETS: (args: IFetchArgs) =>
68
+ UserRequests.bookmarkFolderTweets(args.id!, args.count, args.cursor),
66
69
  USER_DETAILS_BY_USERNAME: (args: IFetchArgs) => UserRequests.detailsByUsername(args.id!),
67
70
  USER_DETAILS_BY_ID: (args: IFetchArgs) => UserRequests.detailsById(args.id!),
68
71
  USER_DETAILS_BY_IDS_BULK: (args: IFetchArgs) => UserRequests.bulkDetailsByIds(args.ids!),
@@ -80,6 +83,7 @@ export const Requests: { [key in keyof typeof ResourceType]: (args: IFetchArgs |
80
83
  USER_TIMELINE: (args: IFetchArgs) => UserRequests.tweets(args.id!, args.count, args.cursor),
81
84
  USER_TIMELINE_AND_REPLIES: (args: IFetchArgs) => UserRequests.tweetsAndReplies(args.id!, args.count, args.cursor),
82
85
  USER_UNFOLLOW: (args: IPostArgs) => UserRequests.unfollow(args.id!),
86
+ USER_PROFILE_UPDATE: (args: IPostArgs) => UserRequests.updateProfile(args.profileOptions!),
83
87
 
84
88
  /* eslint-enable @typescript-eslint/naming-convention */
85
89
  };
@@ -80,6 +80,36 @@ function createUserCommand(rettiwt: Rettiwt): Command {
80
80
  }
81
81
  });
82
82
 
83
+ user.command('bookmark-folders')
84
+ .description('Fetch your list of bookmark folders')
85
+ .argument('[cursor]', 'The cursor to the batch of bookmark folders to fetch')
86
+ .action(async (cursor?: string) => {
87
+ try {
88
+ const folders = await rettiwt.user.bookmarkFolders(cursor);
89
+ output(folders);
90
+ } catch (error) {
91
+ output(error);
92
+ }
93
+ });
94
+
95
+ user.command('bookmark-folder-tweets')
96
+ .description('Fetch tweets from a specific bookmark folder')
97
+ .argument('<folderId>', 'The ID of the bookmark folder')
98
+ .argument('[count]', 'The number of tweets to fetch')
99
+ .argument('[cursor]', 'The cursor to the batch of tweets to fetch')
100
+ .action(async (folderId: string, count?: string, cursor?: string) => {
101
+ try {
102
+ const tweets = await rettiwt.user.bookmarkFolderTweets(
103
+ folderId,
104
+ count ? parseInt(count) : undefined,
105
+ cursor,
106
+ );
107
+ output(tweets);
108
+ } catch (error) {
109
+ output(error);
110
+ }
111
+ });
112
+
83
113
  // Details
84
114
  user.command('details')
85
115
  .description('Fetch the details of the user with the given id/username')
@@ -274,6 +304,27 @@ function createUserCommand(rettiwt: Rettiwt): Command {
274
304
  }
275
305
  });
276
306
 
307
+ // Update Profile
308
+ user.command('update-profile')
309
+ .description('Update your profile information')
310
+ .option('-n, --name <string>', 'Display name (max 50 characters)')
311
+ .option('-u, --url <string>', 'Profile URL')
312
+ .option('-l, --location <string>', 'Location (max 30 characters)')
313
+ .option('-d, --description <string>', 'Description/bio (max 160 characters)')
314
+ .action(async (options?: UserProfileUpdateOptions) => {
315
+ try {
316
+ const result = await rettiwt.user.updateProfile({
317
+ name: options?.name,
318
+ url: options?.url,
319
+ location: options?.location,
320
+ description: options?.description,
321
+ });
322
+ output(result);
323
+ } catch (error) {
324
+ output(error);
325
+ }
326
+ });
327
+
277
328
  return user;
278
329
  }
279
330
 
@@ -288,4 +339,14 @@ type UserAnalyticsOptions = {
288
339
  verifiedFollowers?: boolean;
289
340
  };
290
341
 
342
+ /**
343
+ * The options for updating user profile.
344
+ */
345
+ type UserProfileUpdateOptions = {
346
+ name?: string;
347
+ url?: string;
348
+ location?: string;
349
+ description?: string;
350
+ };
351
+
291
352
  export default createUserCommand;
package/src/enums/Data.ts CHANGED
@@ -9,4 +9,5 @@ export enum BaseType {
9
9
  TWEET = 'TWEET',
10
10
  USER = 'USER',
11
11
  LIST = 'LIST',
12
+ BOOKMARK_FOLDER = 'BOOKMARK_FOLDER',
12
13
  }
@@ -45,6 +45,8 @@ export enum ResourceType {
45
45
  USER_AFFILIATES = 'USER_AFFILIATES',
46
46
  USER_ANALYTICS = 'USER_ANALYTICS',
47
47
  USER_BOOKMARKS = 'USER_BOOKMARKS',
48
+ USER_BOOKMARK_FOLDERS = 'USER_BOOKMARK_FOLDERS',
49
+ USER_BOOKMARK_FOLDER_TWEETS = 'USER_BOOKMARK_FOLDER_TWEETS',
48
50
  USER_DETAILS_BY_USERNAME = 'USER_DETAILS_BY_USERNAME',
49
51
  USER_DETAILS_BY_ID = 'USER_DETAILS_BY_ID',
50
52
  USER_DETAILS_BY_IDS_BULK = 'USER_DETAILS_BY_IDS_BULK',
@@ -62,4 +64,5 @@ export enum ResourceType {
62
64
  USER_TIMELINE = 'USER_TIMELINE',
63
65
  USER_TIMELINE_AND_REPLIES = 'USER_TIMELINE_AND_REPLIES',
64
66
  USER_UNFOLLOW = 'USER_UNFOLLOW',
67
+ USER_PROFILE_UPDATE = 'USER_PROFILE_UPDATE',
65
68
  }
package/src/index.ts CHANGED
@@ -18,6 +18,8 @@ export * from './enums/Tweet';
18
18
  // MODELS
19
19
  export * from './models/args/FetchArgs';
20
20
  export * from './models/args/PostArgs';
21
+ export * from './models/args/ProfileArgs';
22
+ export * from './models/data/BookmarkFolder';
21
23
  export * from './models/data/Conversation';
22
24
  export * from './models/data/CursoredData';
23
25
  export * from './models/data/DirectMessage';
@@ -45,6 +47,8 @@ export * from './services/public/UserService';
45
47
  // TYPES
46
48
  export * from './types/args/FetchArgs';
47
49
  export * from './types/args/PostArgs';
50
+ export * from './types/args/ProfileArgs';
51
+ export * from './types/data/BookmarkFolder';
48
52
  export * from './types/data/Conversation';
49
53
  export * from './types/data/CursoredData';
50
54
  export * from './types/data/DirectMessage';
@@ -56,6 +60,7 @@ export * from './types/data/User';
56
60
  export * from './types/errors/TwitterError';
57
61
  export * from './types/params/Variables';
58
62
  export { IAnalytics as IRawAnalytics } from './types/raw/base/Analytic';
63
+ export { IBookmarkFolder as IRawBookmarkFolder } from './types/raw/base/BookmarkFolder';
59
64
  export { ICursor as IRawCursor } from './types/raw/base/Cursor';
60
65
  export { IErrorData as IRawErrorData, IErrorDetails as IRawErrorDetails } from './types/raw/base/Error';
61
66
  export { ILimitedVisibilityTweet as IRawLimitedVisibilityTweet } from './types/raw/base/LimitedVisibilityTweet';
@@ -94,6 +99,8 @@ export { ITweetUnretweetResponse as IRawTweetUnretweetResponse } from './types/r
94
99
  export { ITweetUnscheduleResponse as ITRawTweetUnscheduleResponse } from './types/raw/tweet/Unschedule';
95
100
  export { IUserAffiliatesResponse as IRawUserAffiliatesResponse } from './types/raw/user/Affiliates';
96
101
  export { IUserAnalyticsResponse as IRawUserAnalyticsResponse } from './types/raw/user/Analytics';
102
+ export { IUserBookmarkFoldersResponse as IRawUserBookmarkFoldersResponse } from './types/raw/user/BookmarkFolders';
103
+ export { IUserBookmarkFolderTweetsResponse as IRawUserBookmarkFolderTweetsResponse } from './types/raw/user/BookmarkFolderTweets';
97
104
  export { IUserBookmarksResponse as IRawUserBookmarksResponse } from './types/raw/user/Bookmarks';
98
105
  export { IUserDetailsResponse as IRawUserDetailsResponse } from './types/raw/user/Details';
99
106
  export { IUserDetailsBulkResponse as IRawUserDetailsBulkResponse } from './types/raw/user/DetailsBulk';
@@ -111,6 +118,7 @@ export { IUserSubscriptionsResponse as IRawUserSubscriptionsResponse } from './t
111
118
  export { IUserTweetsResponse as IRawUserTweetsResponse } from './types/raw/user/Tweets';
112
119
  export { IUserTweetsAndRepliesResponse as IRawUserTweetsAndRepliesResponse } from './types/raw/user/TweetsAndReplies';
113
120
  export { IUserUnfollowResponse as IRawUserUnfollowResponse } from './types/raw/user/Unfollow';
121
+ export { IUserProfileUpdateResponse as IRawUserProfileUpdateResponse } from './types/raw/user/ProfileUpdate';
114
122
  export * from './types/ErrorHandler';
115
123
  export * from './types/RettiwtConfig';
116
124
  export { IConversationTimelineResponse as IRawConversationTimelineResponse } from './types/raw/dm/Conversation';
@@ -1,12 +1,16 @@
1
1
  import { INewTweet, INewTweetMedia, IPostArgs, IUploadArgs } from '../../types/args/PostArgs';
2
2
 
3
+ import { ProfileUpdateOptions } from './ProfileArgs';
4
+
3
5
  /**
4
6
  * Options specifying the data that is to be posted.
5
7
  *
6
8
  * @public
7
9
  */
8
10
  export class PostArgs implements IPostArgs {
11
+ public conversationId?: string;
9
12
  public id?: string;
13
+ public profileOptions?: ProfileUpdateOptions;
10
14
  public tweet?: NewTweet;
11
15
  public upload?: UploadArgs;
12
16
  public userId?: string;
@@ -20,6 +24,8 @@ export class PostArgs implements IPostArgs {
20
24
  this.tweet = args.tweet ? new NewTweet(args.tweet) : undefined;
21
25
  this.upload = args.upload ? new UploadArgs(args.upload) : undefined;
22
26
  this.userId = args.userId;
27
+ this.conversationId = args.conversationId;
28
+ this.profileOptions = args.profileOptions ? new ProfileUpdateOptions(args.profileOptions) : undefined;
23
29
  }
24
30
  }
25
31
 
@@ -0,0 +1,68 @@
1
+ import { IProfileUpdateOptions } from '../../types/args/ProfileArgs';
2
+
3
+ /**
4
+ * Configuration for profile update.
5
+ *
6
+ * @public
7
+ */
8
+ export class ProfileUpdateOptions implements IProfileUpdateOptions {
9
+ public description?: string;
10
+ public location?: string;
11
+ public name?: string;
12
+ public url?: string;
13
+
14
+ /**
15
+ * @param options - The options specifying the profile fields to update.
16
+ */
17
+ public constructor(options: IProfileUpdateOptions) {
18
+ this.description = options.description;
19
+ this.location = options.location;
20
+ this.name = options.name;
21
+ this.url = options.url;
22
+
23
+ // At least one field must be provided
24
+ if (
25
+ this.name === undefined &&
26
+ this.url === undefined &&
27
+ this.location === undefined &&
28
+ this.description === undefined
29
+ ) {
30
+ throw new Error('At least one profile field must be provided');
31
+ }
32
+
33
+ // Name validation
34
+ if (this.name !== undefined) {
35
+ if (this.name.trim().length === 0) {
36
+ throw new Error('Name cannot be empty');
37
+ }
38
+ if (this.name.length > 50) {
39
+ throw new Error('Name cannot exceed 50 characters');
40
+ }
41
+ }
42
+
43
+ // URL validation (minimal - just check if not empty when provided)
44
+ if (this.url !== undefined && this.url.trim().length === 0) {
45
+ throw new Error('URL cannot be empty');
46
+ }
47
+
48
+ // Location validation
49
+ if (this.location !== undefined) {
50
+ if (this.location.trim().length === 0) {
51
+ throw new Error('Location cannot be empty');
52
+ }
53
+ if (this.location.length > 30) {
54
+ throw new Error('Location cannot exceed 30 characters');
55
+ }
56
+ }
57
+
58
+ // Description validation
59
+ if (this.description !== undefined) {
60
+ if (this.description.trim().length === 0) {
61
+ throw new Error('Description cannot be empty');
62
+ }
63
+ if (this.description.length > 160) {
64
+ throw new Error('Description cannot exceed 160 characters');
65
+ }
66
+ }
67
+ }
68
+ }
@@ -0,0 +1,73 @@
1
+ import { LogActions } from '../../enums/Logging';
2
+ import { LogService } from '../../services/internal/LogService';
3
+ import { IBookmarkFolder } from '../../types/data/BookmarkFolder';
4
+ import { IBookmarkFolder as IRawBookmarkFolder } from '../../types/raw/base/BookmarkFolder';
5
+ import { IUserBookmarkFoldersResponse } from '../../types/raw/user/BookmarkFolders';
6
+
7
+ /**
8
+ * The details of a single Bookmark Folder.
9
+ *
10
+ * @public
11
+ */
12
+ export class BookmarkFolder implements IBookmarkFolder {
13
+ /** The raw bookmark folder details. */
14
+ private readonly _raw: IRawBookmarkFolder;
15
+
16
+ public id: string;
17
+ public name: string;
18
+
19
+ /**
20
+ * @param folder - The raw bookmark folder details.
21
+ */
22
+ public constructor(folder: IRawBookmarkFolder) {
23
+ this._raw = { ...folder };
24
+ this.id = folder.id;
25
+ this.name = folder.name;
26
+ }
27
+
28
+ /** The raw bookmark folder details. */
29
+ public get raw(): IRawBookmarkFolder {
30
+ return { ...this._raw };
31
+ }
32
+
33
+ /**
34
+ * Extracts and deserializes bookmark folders from the given raw response data.
35
+ *
36
+ * @param response - The raw response data.
37
+ *
38
+ * @returns The deserialized list of bookmark folders.
39
+ */
40
+ public static list(response: NonNullable<unknown>): BookmarkFolder[] {
41
+ const folders: BookmarkFolder[] = [];
42
+
43
+ // Extract items from the response structure
44
+ const items = (response as IUserBookmarkFoldersResponse)?.data?.viewer?.user_results?.result
45
+ ?.bookmark_collections_slice?.items;
46
+
47
+ if (!items || !Array.isArray(items)) {
48
+ return folders;
49
+ }
50
+
51
+ // Deserialize valid folders
52
+ for (const item of items) {
53
+ if (item && item.id) {
54
+ // Logging
55
+ LogService.log(LogActions.DESERIALIZE, { id: item.id });
56
+
57
+ folders.push(new BookmarkFolder(item));
58
+ }
59
+ }
60
+
61
+ return folders;
62
+ }
63
+
64
+ /**
65
+ * @returns A serializable JSON representation of `this` object.
66
+ */
67
+ public toJSON(): IBookmarkFolder {
68
+ return {
69
+ id: this.id,
70
+ name: this.name,
71
+ };
72
+ }
73
+ }
@@ -4,9 +4,10 @@ import { findByFilter } from '../../helper/JsonUtils';
4
4
 
5
5
  import { ICursoredData } from '../../types/data/CursoredData';
6
6
  import { ICursor as IRawCursor } from '../../types/raw/base/Cursor';
7
+ import { IUserBookmarkFoldersResponse } from '../../types/raw/user/BookmarkFolders';
7
8
 
9
+ import { BookmarkFolder } from './BookmarkFolder';
8
10
  import { List } from './List';
9
-
10
11
  import { Notification } from './Notification';
11
12
  import { Tweet } from './Tweet';
12
13
  import { User } from './User';
@@ -18,7 +19,7 @@ import { User } from './User';
18
19
  *
19
20
  * @public
20
21
  */
21
- export class CursoredData<T extends Notification | Tweet | User | List> implements ICursoredData<T> {
22
+ export class CursoredData<T extends Notification | Tweet | User | List | BookmarkFolder> implements ICursoredData<T> {
22
23
  public list: T[];
23
24
  public next: string;
24
25
 
@@ -43,6 +44,11 @@ export class CursoredData<T extends Notification | Tweet | User | List> implemen
43
44
  } else if (type == BaseType.NOTIFICATION) {
44
45
  this.list = Notification.list(response) as T[];
45
46
  this.next = findByFilter<IRawCursor>(response, 'cursorType', 'Bottom')[0]?.value ?? '';
47
+ } else if (type == BaseType.BOOKMARK_FOLDER) {
48
+ this.list = BookmarkFolder.list(response) as T[];
49
+ const sliceInfo = (response as IUserBookmarkFoldersResponse)?.data?.viewer?.user_results?.result
50
+ ?.bookmark_collections_slice?.slice_info;
51
+ this.next = sliceInfo?.next_cursor ?? '';
46
52
  }
47
53
  }
48
54
 
@@ -272,7 +272,7 @@ export class Tweet implements ITweet {
272
272
  *
273
273
  * @public
274
274
  */
275
- export class TweetEntities {
275
+ export class TweetEntities implements ITweetEntities {
276
276
  /** The list of hashtags mentioned in the tweet. */
277
277
  public hashtags: string[] = [];
278
278
 
@@ -325,7 +325,10 @@ export class TweetEntities {
325
325
  *
326
326
  * @public
327
327
  */
328
- export class TweetMedia {
328
+ export class TweetMedia implements ITweetMedia {
329
+ /** The ID of the media. */
330
+ public id: string;
331
+
329
332
  /** The thumbnail URL for the video content of the tweet. */
330
333
  public thumbnailUrl?: string;
331
334
 
@@ -339,6 +342,8 @@ export class TweetMedia {
339
342
  * @param media - The raw media details.
340
343
  */
341
344
  public constructor(media: IRawExtendedMedia) {
345
+ this.id = media.id_str;
346
+
342
347
  // If the media is a photo
343
348
  if (media.type == RawMediaType.PHOTO) {
344
349
  this.type = MediaType.PHOTO;
@@ -374,6 +379,7 @@ export class TweetMedia {
374
379
  */
375
380
  public toJSON(): ITweetMedia {
376
381
  return {
382
+ id: this.id,
377
383
  thumbnailUrl: this.thumbnailUrl,
378
384
  type: this.type,
379
385
  url: this.url,