rettiwt-api 2.2.2 → 2.3.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 (79) hide show
  1. package/.eslintrc.js +1 -0
  2. package/README.md +39 -20
  3. package/dist/Rettiwt.d.ts +5 -5
  4. package/dist/Rettiwt.js +6 -7
  5. package/dist/Rettiwt.js.map +1 -1
  6. package/dist/enums/Logging.d.ts +12 -0
  7. package/dist/enums/Logging.js +17 -0
  8. package/dist/enums/Logging.js.map +1 -0
  9. package/dist/index.d.ts +15 -11
  10. package/dist/index.js +15 -11
  11. package/dist/index.js.map +1 -1
  12. package/dist/models/internal/RettiwtConfig.d.ts +20 -0
  13. package/dist/models/internal/RettiwtConfig.js +23 -0
  14. package/dist/models/internal/RettiwtConfig.js.map +1 -0
  15. package/dist/models/{CursoredData.d.ts → public/CursoredData.d.ts} +2 -3
  16. package/dist/models/{CursoredData.js → public/CursoredData.js} +1 -16
  17. package/dist/models/public/CursoredData.js.map +1 -0
  18. package/dist/models/{List.d.ts → public/List.d.ts} +1 -1
  19. package/dist/models/public/List.js.map +1 -0
  20. package/dist/models/{Tweet.d.ts → public/Tweet.d.ts} +1 -1
  21. package/dist/models/{Tweet.js → public/Tweet.js} +1 -1
  22. package/dist/models/public/Tweet.js.map +1 -0
  23. package/dist/models/{User.d.ts → public/User.d.ts} +1 -1
  24. package/dist/models/public/User.js.map +1 -0
  25. package/dist/services/{FetcherService.d.ts → internal/FetcherService.d.ts} +28 -9
  26. package/dist/services/{FetcherService.js → internal/FetcherService.js} +100 -22
  27. package/dist/services/internal/FetcherService.js.map +1 -0
  28. package/dist/services/internal/LogService.d.ts +22 -0
  29. package/dist/services/internal/LogService.js +35 -0
  30. package/dist/services/internal/LogService.js.map +1 -0
  31. package/dist/services/{TweetService.d.ts → public/TweetService.d.ts} +7 -7
  32. package/dist/services/{TweetService.js → public/TweetService.js} +4 -5
  33. package/dist/services/public/TweetService.js.map +1 -0
  34. package/dist/services/{UserService.d.ts → public/UserService.d.ts} +20 -7
  35. package/dist/services/{UserService.js → public/UserService.js} +35 -5
  36. package/dist/services/public/UserService.js.map +1 -0
  37. package/dist/types/internal/RettiwtConfig.d.ts +13 -0
  38. package/dist/types/internal/RettiwtConfig.js +3 -0
  39. package/dist/types/internal/RettiwtConfig.js.map +1 -0
  40. package/dist/types/public/CursoredData.js.map +1 -0
  41. package/dist/types/{List.js.map → public/List.js.map} +1 -1
  42. package/dist/types/{Tweet.js.map → public/Tweet.js.map} +1 -1
  43. package/dist/types/{User.js.map → public/User.js.map} +1 -1
  44. package/package.json +2 -2
  45. package/src/Rettiwt.ts +11 -9
  46. package/src/enums/Logging.ts +12 -0
  47. package/src/index.ts +15 -11
  48. package/src/models/internal/RettiwtConfig.ts +29 -0
  49. package/src/models/{CursoredData.ts → public/CursoredData.ts} +7 -22
  50. package/src/models/{List.ts → public/List.ts} +9 -9
  51. package/src/models/{Tweet.ts → public/Tweet.ts} +25 -25
  52. package/src/models/{User.ts → public/User.ts} +16 -16
  53. package/src/services/{FetcherService.ts → internal/FetcherService.ts} +116 -21
  54. package/src/services/internal/LogService.ts +39 -0
  55. package/src/services/{TweetService.ts → public/TweetService.ts} +16 -16
  56. package/src/services/{UserService.ts → public/UserService.ts} +39 -13
  57. package/src/types/internal/RettiwtConfig.ts +15 -0
  58. package/dist/models/CursoredData.js.map +0 -1
  59. package/dist/models/List.js.map +0 -1
  60. package/dist/models/Tweet.js.map +0 -1
  61. package/dist/models/User.js.map +0 -1
  62. package/dist/services/FetcherService.js.map +0 -1
  63. package/dist/services/TweetService.js.map +0 -1
  64. package/dist/services/UserService.js.map +0 -1
  65. package/dist/types/CursoredData.js.map +0 -1
  66. /package/dist/models/{List.js → public/List.js} +0 -0
  67. /package/dist/models/{User.js → public/User.js} +0 -0
  68. /package/dist/types/{CursoredData.d.ts → public/CursoredData.d.ts} +0 -0
  69. /package/dist/types/{CursoredData.js → public/CursoredData.js} +0 -0
  70. /package/dist/types/{List.d.ts → public/List.d.ts} +0 -0
  71. /package/dist/types/{List.js → public/List.js} +0 -0
  72. /package/dist/types/{Tweet.d.ts → public/Tweet.d.ts} +0 -0
  73. /package/dist/types/{Tweet.js → public/Tweet.js} +0 -0
  74. /package/dist/types/{User.d.ts → public/User.d.ts} +0 -0
  75. /package/dist/types/{User.js → public/User.js} +0 -0
  76. /package/src/types/{CursoredData.ts → public/CursoredData.ts} +0 -0
  77. /package/src/types/{List.ts → public/List.ts} +0 -0
  78. /package/src/types/{Tweet.ts → public/Tweet.ts} +0 -0
  79. /package/src/types/{User.ts → public/User.ts} +0 -0
@@ -0,0 +1,29 @@
1
+ // TYPES
2
+ import { IRettiwtConfig } from '../../types/internal/RettiwtConfig';
3
+
4
+ /**
5
+ * The configuration for initializing a new Rettiwt instance.
6
+ *
7
+ * @internal
8
+ */
9
+ export class RettiwtConfig implements IRettiwtConfig {
10
+ /** The apiKey (cookie) to use for authenticating Rettiwt against Twitter API. */
11
+ public apiKey?: string;
12
+
13
+ /** Optional URL with proxy configuration to use for requests to Twitter API. */
14
+ public proxyUrl?: URL;
15
+
16
+ /** Whether to write logs to console or not. */
17
+ public logging?: boolean;
18
+
19
+ /**
20
+ * Initializes a new configuration object from the given config.
21
+ *
22
+ * @param config - The configuration object.
23
+ */
24
+ public constructor(config: RettiwtConfig) {
25
+ this.apiKey = config.apiKey;
26
+ this.proxyUrl = config.proxyUrl;
27
+ this.logging = config.logging;
28
+ }
29
+ }
@@ -1,12 +1,9 @@
1
- // PACKAGES
2
- import { ITweet as IRawTweet, IUser as IRawUser } from 'rettiwt-core';
3
-
4
1
  // MODELS
5
2
  import { Tweet } from './Tweet';
6
3
  import { User } from './User';
7
4
 
8
5
  // TYPES
9
- import { ICursor, ICursoredData } from '../types/CursoredData';
6
+ import { ICursor, ICursoredData } from '../../types/public/CursoredData';
10
7
 
11
8
  /**
12
9
  * The data that us fetched batch-wise along with a cursor.
@@ -17,29 +14,17 @@ import { ICursor, ICursoredData } from '../types/CursoredData';
17
14
  */
18
15
  export class CursoredData<T extends Tweet | User> implements ICursoredData<T> {
19
16
  /** The list of data of the given type. */
20
- list: T[] = [];
17
+ public list: T[] = [];
21
18
 
22
19
  /** The cursor to the next batch of data. */
23
- next: Cursor;
20
+ public next: Cursor;
24
21
 
25
22
  /**
26
23
  * @param list - The list of data item to store.
27
24
  * @param next - The cursor to the next batch of data.
28
25
  */
29
- constructor(list: (IRawTweet | IRawUser)[] = [], next: string = '') {
30
- // Deserializing the input raw data and storing it in the list
31
- for (const item of list) {
32
- // If the item is a valid raw tweet
33
- if (item && item.__typename == 'Tweet' && item.rest_id) {
34
- this.list.push(new Tweet(item as IRawTweet) as T);
35
- }
36
- // If the item is a valid raw user
37
- else if (item && item.__typename == 'User' && item.rest_id && (item as IRawUser).id) {
38
- this.list.push(new User(item as IRawUser) as T);
39
- }
40
- }
41
-
42
- // Initializing cursors
26
+ public constructor(list: T[] = [], next: string = '') {
27
+ this.list = list;
43
28
  this.next = new Cursor(next);
44
29
  }
45
30
  }
@@ -51,14 +36,14 @@ export class CursoredData<T extends Tweet | User> implements ICursoredData<T> {
51
36
  */
52
37
  export class Cursor implements ICursor {
53
38
  /** The cursor string. */
54
- value: string;
39
+ public value: string;
55
40
 
56
41
  /**
57
42
  * Initializes a new cursor from the given cursor string.
58
43
  *
59
44
  * @param cursorStr - The string representation of the cursor.
60
45
  */
61
- constructor(cursorStr: string) {
46
+ public constructor(cursorStr: string) {
62
47
  this.value = cursorStr;
63
48
  }
64
49
  }
@@ -2,7 +2,7 @@
2
2
  import { IList as IRawList } from 'rettiwt-core';
3
3
 
4
4
  // TYPES
5
- import { IList } from '../types/List';
5
+ import { IList } from '../../types/public/List';
6
6
 
7
7
  /**
8
8
  * The details of a single Twitter List.
@@ -11,32 +11,32 @@ import { IList } from '../types/List';
11
11
  */
12
12
  export class List implements IList {
13
13
  /** The rest id of the list. */
14
- id: string;
14
+ public id: string;
15
15
 
16
16
  /** The name of the list. */
17
- name: string;
17
+ public name: string;
18
18
 
19
19
  /** The date and time of creation of the list, int UTC string format. */
20
- createdAt: string;
20
+ public createdAt: string;
21
21
 
22
22
  /** The list description. */
23
- description: string;
23
+ public description: string;
24
24
 
25
25
  /** The number of memeber of the list. */
26
- memberCount: number;
26
+ public memberCount: number;
27
27
 
28
28
  /** The number of subscribers of the list. */
29
- subscriberCount: number;
29
+ public subscriberCount: number;
30
30
 
31
31
  /** The rest id of the user who created the list. */
32
- createdBy: string;
32
+ public createdBy: string;
33
33
 
34
34
  /**
35
35
  * Initializes a new Tweet List from the given raw list data.
36
36
  *
37
37
  * @param list - list The raw tweet list data.
38
38
  */
39
- constructor(list: IRawList) {
39
+ public constructor(list: IRawList) {
40
40
  this.id = list.id_str;
41
41
  this.name = list.name;
42
42
  this.createdAt = new Date(list.created_at).toISOString();
@@ -7,13 +7,13 @@ import {
7
7
  } from 'rettiwt-core';
8
8
 
9
9
  // TYPES
10
- import { ITweet, ITweetEntities } from '../types/Tweet';
10
+ import { ITweet, ITweetEntities } from '../../types/public/Tweet';
11
11
 
12
12
  // MODELS
13
13
  import { User } from './User';
14
14
 
15
15
  // PARSERS
16
- import { normalizeText } from '../helper/JsonUtils';
16
+ import { normalizeText } from '../../helper/JsonUtils';
17
17
 
18
18
  /**
19
19
  * The details of a single Tweet.
@@ -22,56 +22,56 @@ import { normalizeText } from '../helper/JsonUtils';
22
22
  */
23
23
  export class Tweet implements ITweet {
24
24
  /** The rest id of the tweet. */
25
- id: string;
25
+ public id: string;
26
26
 
27
27
  /** The details of the user who made the tweet. */
28
- tweetBy: User;
28
+ public tweetBy: User;
29
29
 
30
30
  /** The date and time of creation of the tweet, in UTC string format. */
31
- createdAt: string;
31
+ public createdAt: string;
32
32
 
33
33
  /** Additional tweet entities like urls, mentions, etc. */
34
- entities: TweetEntities;
34
+ public entities: TweetEntities;
35
35
 
36
36
  /** The urls of the media contents of the tweet (if any). */
37
- media: TweetMedia[];
37
+ public media: TweetMedia[];
38
38
 
39
39
  /** The rest id of the tweet which is quoted in the tweet. */
40
- quoted: string;
40
+ public quoted: string;
41
41
 
42
42
  /** The full text content of the tweet. */
43
- fullText: string;
43
+ public fullText: string;
44
44
 
45
45
  /** The rest id of the user to which the tweet is a reply. */
46
- replyTo: string;
46
+ public replyTo: string;
47
47
 
48
48
  /** The language in which the tweet is written. */
49
- lang: string;
49
+ public lang: string;
50
50
 
51
51
  /** The number of quotes of the tweet. */
52
- quoteCount: number;
52
+ public quoteCount: number;
53
53
 
54
54
  /** The number of replies to the tweet. */
55
- replyCount: number;
55
+ public replyCount: number;
56
56
 
57
57
  /** The number of retweets of the tweet. */
58
- retweetCount: number;
58
+ public retweetCount: number;
59
59
 
60
60
  /** The number of likes of the tweet. */
61
- likeCount: number;
61
+ public likeCount: number;
62
62
 
63
63
  /** The number of views of a tweet. */
64
- viewCount: number;
64
+ public viewCount: number;
65
65
 
66
66
  /** The number of bookmarks of a tweet. */
67
- bookmarkCount: number;
67
+ public bookmarkCount: number;
68
68
 
69
69
  /**
70
70
  * Initializes a new Tweet from the given raw tweet data.
71
71
  *
72
72
  * @param tweet - The raw tweet data.
73
73
  */
74
- constructor(tweet: IRawTweet) {
74
+ public constructor(tweet: IRawTweet) {
75
75
  this.id = tweet.rest_id;
76
76
  this.createdAt = tweet.legacy.created_at;
77
77
  this.tweetBy = new User(tweet.core.user_results.result);
@@ -97,20 +97,20 @@ export class Tweet implements ITweet {
97
97
  */
98
98
  export class TweetEntities implements ITweetEntities {
99
99
  /** The list of hashtags mentioned in the tweet. */
100
- hashtags: string[] = [];
100
+ public hashtags: string[] = [];
101
101
 
102
102
  /** The list of urls mentioned in the tweet. */
103
- urls: string[] = [];
103
+ public urls: string[] = [];
104
104
 
105
105
  /** The list of IDs of users mentioned in the tweet. */
106
- mentionedUsers: string[] = [];
106
+ public mentionedUsers: string[] = [];
107
107
 
108
108
  /**
109
109
  * Initializes the TweetEntities from the raw tweet entities.
110
110
  *
111
111
  * @param entities - The raw tweet entities.
112
112
  */
113
- constructor(entities: IRawTweetEntities) {
113
+ public constructor(entities: IRawTweetEntities) {
114
114
  // Extracting user mentions
115
115
  if (entities.user_mentions) {
116
116
  for (const user of entities.user_mentions) {
@@ -141,17 +141,17 @@ export class TweetEntities implements ITweetEntities {
141
141
  */
142
142
  export class TweetMedia {
143
143
  /** The type of media. */
144
- type: EMediaType;
144
+ public type: EMediaType;
145
145
 
146
146
  /** The direct URL to the media. */
147
- url: string = '';
147
+ public url: string = '';
148
148
 
149
149
  /**
150
150
  * Initializes the TweetMedia from the raw tweet media.
151
151
  *
152
152
  * @param media - The raw tweet media.
153
153
  */
154
- constructor(media: IRawExtendedMedia) {
154
+ public constructor(media: IRawExtendedMedia) {
155
155
  this.type = media.type;
156
156
 
157
157
  // If the media is a photo
@@ -2,7 +2,7 @@
2
2
  import { IUser as IRawUser } from 'rettiwt-core';
3
3
 
4
4
  // TYPES
5
- import { IUser } from '../types/User';
5
+ import { IUser } from '../../types/public/User';
6
6
 
7
7
  /**
8
8
  * The details of a single user.
@@ -11,53 +11,53 @@ import { IUser } from '../types/User';
11
11
  */
12
12
  export class User implements IUser {
13
13
  /** The rest id of the user. */
14
- id: string;
14
+ public id: string;
15
15
 
16
16
  /** The username/screenname of the user. */
17
- userName: string;
17
+ public userName: string;
18
18
 
19
19
  /** The full name of the user. */
20
- fullName: string;
20
+ public fullName: string;
21
21
 
22
22
  /** The creation date of user's account. */
23
- createdAt: string;
23
+ public createdAt: string;
24
24
 
25
25
  /** The user's description. */
26
- description: string;
26
+ public description: string;
27
27
 
28
28
  /** Whether the account is verified or not. */
29
- isVerified: boolean;
29
+ public isVerified: boolean;
30
30
 
31
31
  /** The number of tweets liked by the user. */
32
- favouritesCount: number;
32
+ public favouritesCount: number;
33
33
 
34
34
  /** The number of followers of the user. */
35
- followersCount: number;
35
+ public followersCount: number;
36
36
 
37
37
  /** The number of following of the user. */
38
- followingsCount: number;
38
+ public followingsCount: number;
39
39
 
40
40
  /** The number of tweets made by the user. */
41
- statusesCount: number;
41
+ public statusesCount: number;
42
42
 
43
43
  /** The location of user as provided by user. */
44
- location: string;
44
+ public location: string;
45
45
 
46
46
  /** The rest id of the tweet pinned in the user's profile. */
47
- pinnedTweet: string;
47
+ public pinnedTweet: string;
48
48
 
49
49
  /** The url of the profile banner image. */
50
- profileBanner: string;
50
+ public profileBanner: string;
51
51
 
52
52
  /** The url of the profile image. */
53
- profileImage: string;
53
+ public profileImage: string;
54
54
 
55
55
  /**
56
56
  * Initializes a new User from the given raw user data.
57
57
  *
58
58
  * @param user - The raw user data.
59
59
  */
60
- constructor(user: IRawUser) {
60
+ public constructor(user: IRawUser) {
61
61
  this.id = user.rest_id;
62
62
  this.userName = user.legacy.screen_name;
63
63
  this.fullName = user.legacy.name;
@@ -13,20 +13,25 @@ import {
13
13
  } from 'rettiwt-core';
14
14
  import axios, { AxiosRequestConfig, AxiosRequestHeaders, AxiosResponse } from 'axios';
15
15
  import https, { Agent } from 'https';
16
- import { AuthCredential } from 'rettiwt-auth';
16
+ import { AuthCredential, Auth } from 'rettiwt-auth';
17
17
  import { HttpsProxyAgent } from 'https-proxy-agent';
18
18
 
19
+ // SERVICES
20
+ import { LogService } from './LogService';
21
+
19
22
  // ENUMS
20
- import { EHttpStatus } from '../enums/HTTP';
21
- import { EApiErrors } from '../enums/ApiErrors';
23
+ import { EHttpStatus } from '../../enums/HTTP';
24
+ import { EApiErrors } from '../../enums/ApiErrors';
25
+ import { ELogActions } from '../../enums/Logging';
22
26
 
23
27
  // MODELS
24
- import { CursoredData } from '../models/CursoredData';
25
- import { Tweet } from '../models/Tweet';
26
- import { User } from '../models/User';
28
+ import { RettiwtConfig } from '../../models/internal/RettiwtConfig';
29
+ import { CursoredData } from '../../models/public/CursoredData';
30
+ import { Tweet } from '../../models/public/Tweet';
31
+ import { User } from '../../models/public/User';
27
32
 
28
33
  // HELPERS
29
- import { findByFilter, findKeyByValue } from '../helper/JsonUtils';
34
+ import { findByFilter, findKeyByValue } from '../../helper/JsonUtils';
30
35
 
31
36
  /**
32
37
  * The base service that handles all HTTP requests.
@@ -35,18 +40,25 @@ import { findByFilter, findKeyByValue } from '../helper/JsonUtils';
35
40
  */
36
41
  export class FetcherService {
37
42
  /** The credential to use for authenticating against Twitter API. */
38
- private cred: AuthCredential;
43
+ private cred?: AuthCredential;
44
+
45
+ /** Whether the instance is authenticated or not. */
46
+ private readonly isAuthenticated: boolean;
39
47
 
40
48
  /** The HTTPS Agent to use for requests to Twitter API. */
41
49
  private readonly httpsAgent: Agent;
42
50
 
51
+ /** The log service instance to use to logging. */
52
+ private readonly logger: LogService;
53
+
43
54
  /**
44
- * @param apiKey - The apiKey (cookie) to use for authenticating Rettiwt against Twitter API.
45
- * @param proxyUrl - Optional URL with proxy configuration to use for requests to Twitter API.
55
+ * @param config - The config object for configuring the Rettiwt instance.
46
56
  */
47
- constructor(apiKey: string, proxyUrl?: URL) {
48
- this.cred = this.getAuthCredential(apiKey);
49
- this.httpsAgent = this.getHttpsAgent(proxyUrl);
57
+ public constructor(config?: RettiwtConfig) {
58
+ this.cred = config?.apiKey ? this.getAuthCredential(config.apiKey) : undefined;
59
+ this.isAuthenticated = config?.apiKey ? true : false;
60
+ this.httpsAgent = this.getHttpsAgent(config?.proxyUrl);
61
+ this.logger = new LogService(config?.logging);
50
62
  }
51
63
 
52
64
  /**
@@ -59,6 +71,27 @@ export class FetcherService {
59
71
  return new AuthCredential(apiKey.split(';'));
60
72
  }
61
73
 
74
+ /**
75
+ * Checks the authorization status based on the requested resource.
76
+ *
77
+ * @param resourceType - The type of resource to fetch.
78
+ * @throws An error if not authorized to access the requested resource.
79
+ */
80
+ private checkAuthorization(resourceType: EResourceType): void {
81
+ // Logging
82
+ this.logger.log(ELogActions.AUTHORIZATION, { authenticated: this.isAuthenticated });
83
+
84
+ // Checking authorization status
85
+ if (
86
+ resourceType != EResourceType.TWEET_DETAILS &&
87
+ resourceType != EResourceType.USER_DETAILS &&
88
+ resourceType != EResourceType.USER_TWEETS &&
89
+ this.isAuthenticated == false
90
+ ) {
91
+ throw new Error(EApiErrors.RESOURCE_NOT_ALLOWED);
92
+ }
93
+ }
94
+
62
95
  /**
63
96
  * Gets the HttpsAgent based on whether a proxy is used or not.
64
97
  *
@@ -78,6 +111,7 @@ export class FetcherService {
78
111
  *
79
112
  * @param res - The response object received.
80
113
  * @returns The received response, if no HTTP errors are found.
114
+ * @throws An error if any HTTP-related error has occured.
81
115
  */
82
116
  private handleHttpError(res: AxiosResponse<IResponse<unknown>>): AxiosResponse<IResponse<unknown>> {
83
117
  /**
@@ -95,6 +129,7 @@ export class FetcherService {
95
129
  *
96
130
  * @param res - The response object received.
97
131
  * @returns The received response, if no API errors are found.
132
+ * @throws An error if any API-related error has occured.
98
133
  */
99
134
  private handleApiError(res: AxiosResponse<IResponse<unknown>>): AxiosResponse<IResponse<unknown>> {
100
135
  // If error exists
@@ -121,6 +156,12 @@ export class FetcherService {
121
156
  * @returns The response received.
122
157
  */
123
158
  private async request(config: Request): Promise<AxiosResponse<IResponse<unknown>>> {
159
+ // Checking authorization for the requested resource
160
+ this.checkAuthorization(config.endpoint);
161
+
162
+ // If not authenticated, use guest authentication
163
+ this.cred = this.cred ?? (await new Auth().getGuestCredential());
164
+
124
165
  /**
125
166
  * Creating axios request configuration from the input configuration.
126
167
  */
@@ -145,14 +186,18 @@ export class FetcherService {
145
186
  *
146
187
  * @param data - The data from which extraction is to be done.
147
188
  * @param type - The type of data to extract.
148
- * @typeParam BaseType - The base type of the raw data present in the input.
149
- * @typeParam DeserializedType - The type of data produced after deserialization of BaseType.
150
189
  * @returns The extracted data.
151
190
  */
152
- private extractData<DeserializedType extends Tweet | User>(
191
+ private extractData(
153
192
  data: NonNullable<unknown>,
154
193
  type: EResourceType,
155
- ): CursoredData<DeserializedType> {
194
+ ): {
195
+ /** The required extracted data. */
196
+ required: (IRawTweet | IRawUser)[];
197
+
198
+ /** The cursor string to the next batch of data. */
199
+ next: string;
200
+ } {
156
201
  /**
157
202
  * The required extracted data.
158
203
  */
@@ -166,7 +211,8 @@ export class FetcherService {
166
211
  type == EResourceType.TWEET_SEARCH ||
167
212
  type == EResourceType.USER_LIKES ||
168
213
  type == EResourceType.LIST_TWEETS ||
169
- type == EResourceType.USER_TWEETS
214
+ type == EResourceType.USER_TWEETS ||
215
+ type == EResourceType.USER_TWEETS_AND_REPLIES
170
216
  ) {
171
217
  required = findByFilter<ITimelineTweet>(data, '__typename', 'TimelineTweet').map(
172
218
  (item) => item.tweet_results.result,
@@ -182,7 +228,47 @@ export class FetcherService {
182
228
  );
183
229
  }
184
230
 
185
- return new CursoredData(required, findByFilter<IRawCursor>(data, 'cursorType', 'Bottom')[0]?.value);
231
+ return {
232
+ required: required,
233
+ next: findByFilter<IRawCursor>(data, 'cursorType', 'Bottom')[0]?.value,
234
+ };
235
+ }
236
+
237
+ /**
238
+ * Deserializes the extracted data into a cursored list.
239
+ *
240
+ * @param extractedData - The list of extracted data.
241
+ * @param next - The cursor to the next batch of data.
242
+ * @returns The cursored data object.
243
+ */
244
+ private deserializeData<OutType extends Tweet | User>(
245
+ extractedData: (IRawTweet | IRawUser)[] = [],
246
+ next: string = '',
247
+ ): CursoredData<OutType> {
248
+ /** The list of deserialized data. */
249
+ const deserializedList: OutType[] = [];
250
+
251
+ // Deserializing the extracted raw data and storing it in the list
252
+ for (const item of extractedData) {
253
+ // If the item is a valid raw tweet
254
+ if (item && item.__typename == 'Tweet' && item.rest_id) {
255
+ // Logging
256
+ this.logger.log(ELogActions.DESERIALIZE, { type: item.__typename, id: item.rest_id });
257
+
258
+ // Adding deserialized Tweet to list
259
+ deserializedList.push(new Tweet(item as IRawTweet) as OutType);
260
+ }
261
+ // If the item is a valid raw user
262
+ else if (item && item.__typename == 'User' && item.rest_id && (item as IRawUser).id) {
263
+ // Logging
264
+ this.logger.log(ELogActions.DESERIALIZE, { type: item.__typename, id: item.rest_id });
265
+
266
+ // Adding deserialized User to list
267
+ deserializedList.push(new User(item as IRawUser) as OutType);
268
+ }
269
+ }
270
+
271
+ return new CursoredData<OutType>(deserializedList, next);
186
272
  }
187
273
 
188
274
  /**
@@ -197,6 +283,9 @@ export class FetcherService {
197
283
  resourceType: EResourceType,
198
284
  args: Args,
199
285
  ): Promise<CursoredData<OutType>> {
286
+ // Logging
287
+ this.logger.log(ELogActions.FETCH, { resourceType: resourceType, args: args });
288
+
200
289
  // Preparing the HTTP request
201
290
  const request: Request = new Request(resourceType, args);
202
291
 
@@ -204,9 +293,12 @@ export class FetcherService {
204
293
  const res = await this.request(request).then((res) => res.data);
205
294
 
206
295
  // Extracting data
207
- const data = this.extractData<OutType>(res, resourceType);
296
+ const extractedData = this.extractData(res, resourceType);
297
+
298
+ // Deserializing data
299
+ const deserializedData = this.deserializeData<OutType>(extractedData.required, extractedData.next);
208
300
 
209
- return data;
301
+ return deserializedData;
210
302
  }
211
303
 
212
304
  /**
@@ -217,6 +309,9 @@ export class FetcherService {
217
309
  * @returns Whether posting was successful or not.
218
310
  */
219
311
  protected async post(resourceType: EResourceType, args: Args): Promise<boolean> {
312
+ // Logging
313
+ this.logger.log(ELogActions.POST, { resourceType: resourceType, args: args });
314
+
220
315
  // Preparing the HTTP request
221
316
  const request: Request = new Request(resourceType, args);
222
317
 
@@ -0,0 +1,39 @@
1
+ // ENUMS
2
+ import { ELogActions } from '../../enums/Logging';
3
+
4
+ /**
5
+ * Handles logging of data for debug purpose.
6
+ *
7
+ * @internal
8
+ */
9
+ export class LogService {
10
+ /** Whether logging is enabled or not. */
11
+ private readonly enabled: boolean;
12
+
13
+ /**
14
+ * Initializes a new LogService instance.
15
+ *
16
+ * @param enable - Whether to enable logging or not.
17
+ */
18
+ public constructor(enable?: boolean) {
19
+ this.enabled = enable ?? false;
20
+ }
21
+
22
+ /**
23
+ * Logs the given data.
24
+ *
25
+ * @param data - The data to be logged.
26
+ */
27
+ public log(action: ELogActions, data: NonNullable<unknown>): void {
28
+ // Proceed to log only if logging is enabled
29
+ if (this.enabled) {
30
+ // Preparing the log message
31
+ const logMessage: string = `[Rettiwt-API] [${action}] [${new Date().toISOString()}] ${JSON.stringify(
32
+ data,
33
+ )}`;
34
+
35
+ // Logging
36
+ console.log(logMessage);
37
+ }
38
+ }
39
+ }