rettiwt-api 1.0.5 → 1.0.7

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 (184) hide show
  1. package/README.md +2 -2
  2. package/dist/{queries → graphql/queries}/RootQuery.d.ts +0 -0
  3. package/dist/{queries → graphql/queries}/RootQuery.js +15 -3
  4. package/dist/graphql/queries/RootQuery.js.map +1 -0
  5. package/dist/graphql/resolvers/AccountResolver.d.ts +12 -0
  6. package/dist/graphql/resolvers/AccountResolver.js +84 -0
  7. package/dist/graphql/resolvers/AccountResolver.js.map +1 -0
  8. package/dist/{resolvers → graphql/resolvers}/ResolverBase.d.ts +1 -1
  9. package/dist/{resolvers → graphql/resolvers}/ResolverBase.js +0 -0
  10. package/dist/graphql/resolvers/ResolverBase.js.map +1 -0
  11. package/dist/{resolvers → graphql/resolvers}/TweetResolver.d.ts +10 -9
  12. package/dist/{resolvers → graphql/resolvers}/TweetResolver.js +51 -35
  13. package/dist/graphql/resolvers/TweetResolver.js.map +1 -0
  14. package/dist/{resolvers → graphql/resolvers}/UserResolver.d.ts +5 -4
  15. package/dist/{resolvers → graphql/resolvers}/UserResolver.js +51 -35
  16. package/dist/graphql/resolvers/UserResolver.js.map +1 -0
  17. package/dist/{models/graphql → graphql/types}/Global.d.ts +0 -0
  18. package/dist/{models/graphql → graphql/types}/Global.js +0 -0
  19. package/dist/graphql/types/Global.js.map +1 -0
  20. package/dist/graphql/types/TweetTypes.d.ts +4 -0
  21. package/dist/{models/graphql → graphql/types}/TweetTypes.js +5 -7
  22. package/dist/graphql/types/TweetTypes.js.map +1 -0
  23. package/dist/graphql/types/UserTypes.d.ts +3 -0
  24. package/dist/{models/graphql → graphql/types}/UserTypes.js +9 -11
  25. package/dist/graphql/types/UserTypes.js.map +1 -0
  26. package/dist/index.d.ts +4 -2
  27. package/dist/index.js +5 -3
  28. package/dist/index.js.map +1 -1
  29. package/dist/server.js +6 -4
  30. package/dist/server.js.map +1 -1
  31. package/dist/services/AuthService.js +2 -2
  32. package/dist/services/AuthService.js.map +1 -1
  33. package/dist/services/CacheService.d.ts +3 -3
  34. package/dist/services/CacheService.js +22 -75
  35. package/dist/services/CacheService.js.map +1 -1
  36. package/dist/services/FetcherService.d.ts +14 -4
  37. package/dist/services/FetcherService.js +58 -52
  38. package/dist/services/FetcherService.js.map +1 -1
  39. package/dist/services/accounts/AccountService.d.ts +42 -0
  40. package/dist/services/accounts/AccountService.js +291 -0
  41. package/dist/services/accounts/AccountService.js.map +1 -0
  42. package/dist/services/accounts/LoginFlows.d.ts +77 -0
  43. package/dist/services/accounts/LoginFlows.js +92 -0
  44. package/dist/services/accounts/LoginFlows.js.map +1 -0
  45. package/dist/services/data/TweetService.d.ts +6 -6
  46. package/dist/services/data/TweetService.js +44 -27
  47. package/dist/services/data/TweetService.js.map +1 -1
  48. package/dist/services/data/UserAccountService.d.ts +3 -3
  49. package/dist/services/data/UserAccountService.js +22 -8
  50. package/dist/services/data/UserAccountService.js.map +1 -1
  51. package/dist/services/data/UserService.d.ts +42 -0
  52. package/dist/services/data/UserService.js +255 -0
  53. package/dist/services/data/UserService.js.map +1 -0
  54. package/dist/services/helper/Extractors.js +1 -1
  55. package/dist/services/helper/Extractors.js.map +1 -1
  56. package/dist/services/helper/Headers.d.ts +11 -1
  57. package/dist/services/helper/Headers.js +28 -2
  58. package/dist/services/helper/Headers.js.map +1 -1
  59. package/dist/services/helper/Parser.d.ts +1 -1
  60. package/dist/services/helper/Parser.js +4 -3
  61. package/dist/services/helper/Parser.js.map +1 -1
  62. package/dist/services/helper/Urls.d.ts +2 -2
  63. package/dist/services/helper/Urls.js +19 -3
  64. package/dist/services/helper/Urls.js.map +1 -1
  65. package/dist/services/helper/deserializers/Tweets.d.ts +12 -0
  66. package/dist/services/helper/deserializers/Tweets.js +92 -0
  67. package/dist/services/helper/deserializers/Tweets.js.map +1 -0
  68. package/dist/services/helper/deserializers/User.d.ts +0 -0
  69. package/dist/services/helper/deserializers/User.js +2 -0
  70. package/dist/services/helper/deserializers/User.js.map +1 -0
  71. package/dist/services/helper/deserializers/Users.d.ts +7 -0
  72. package/dist/services/helper/deserializers/Users.js +27 -0
  73. package/dist/services/helper/deserializers/Users.js.map +1 -0
  74. package/dist/services/helper/extractors/TweetExtractors.d.ts +0 -0
  75. package/dist/services/helper/extractors/TweetExtractors.js +2 -0
  76. package/dist/services/helper/extractors/TweetExtractors.js.map +1 -0
  77. package/dist/services/helper/extractors/Tweets.d.ts +32 -0
  78. package/dist/services/helper/extractors/Tweets.js +264 -0
  79. package/dist/services/helper/extractors/Tweets.js.map +1 -0
  80. package/dist/services/helper/extractors/UserExtractors.d.ts +45 -0
  81. package/dist/services/helper/extractors/UserExtractors.js +176 -0
  82. package/dist/services/helper/extractors/UserExtractors.js.map +1 -0
  83. package/dist/services/helper/extractors/Users.d.ts +20 -0
  84. package/dist/services/helper/extractors/Users.js +151 -0
  85. package/dist/services/helper/extractors/Users.js.map +1 -0
  86. package/dist/services/helper/urls/Authentication.d.ts +4 -0
  87. package/dist/services/helper/urls/Authentication.js +11 -0
  88. package/dist/services/helper/urls/Authentication.js.map +1 -0
  89. package/dist/services/helper/urls/Tweets.d.ts +32 -0
  90. package/dist/services/helper/urls/Tweets.js +51 -0
  91. package/dist/services/helper/urls/Tweets.js.map +1 -0
  92. package/dist/services/helper/urls/Urls.d.ts +4 -0
  93. package/dist/services/helper/urls/Urls.js +11 -0
  94. package/dist/services/helper/urls/Urls.js.map +1 -0
  95. package/dist/services/helper/urls/Users.d.ts +31 -0
  96. package/dist/services/helper/urls/Users.js +66 -0
  97. package/dist/services/helper/urls/Users.js.map +1 -0
  98. package/dist/types/{graphql/Errors.d.ts → Errors.d.ts} +0 -0
  99. package/dist/types/{graphql/Errors.js → Errors.js} +0 -0
  100. package/dist/types/Errors.js.map +1 -0
  101. package/dist/types/HTTP.d.ts +0 -7
  102. package/dist/types/HTTP.js +1 -10
  103. package/dist/types/HTTP.js.map +1 -1
  104. package/dist/types/Resolvers.d.ts +9 -0
  105. package/dist/types/Resolvers.js +3 -0
  106. package/dist/types/Resolvers.js.map +1 -0
  107. package/dist/types/Service.d.ts +2 -0
  108. package/dist/types/Service.js.map +1 -1
  109. package/dist/types/Tweet.d.ts +1 -0
  110. package/dist/types/Tweet.js.map +1 -1
  111. package/dist/types/User.d.ts +19 -0
  112. package/dist/types/User.js +4 -0
  113. package/dist/types/User.js.map +1 -0
  114. package/dist/types/data/Errors.d.ts +26 -0
  115. package/dist/types/data/Errors.js +36 -0
  116. package/dist/types/data/Errors.js.map +1 -0
  117. package/dist/types/data/Service.d.ts +29 -0
  118. package/dist/types/data/Service.js +19 -0
  119. package/dist/types/data/Service.js.map +1 -0
  120. package/dist/types/data/Tweet.d.ts +41 -0
  121. package/dist/types/data/Tweet.js +5 -0
  122. package/dist/types/data/Tweet.js.map +1 -0
  123. package/dist/types/data/User.d.ts +19 -0
  124. package/dist/types/data/User.js +4 -0
  125. package/dist/types/data/User.js.map +1 -0
  126. package/dist/types/raw/http/Error.d.ts +34 -0
  127. package/dist/types/raw/{user/Users.js → http/Error.js} +1 -1
  128. package/dist/types/raw/http/Error.js.map +1 -0
  129. package/dist/types/raw/http/Response.d.ts +34 -0
  130. package/dist/types/raw/http/Response.js +3 -0
  131. package/dist/types/raw/http/Response.js.map +1 -0
  132. package/package.json +4 -2
  133. package/src/{queries → graphql/queries}/RootQuery.ts +16 -4
  134. package/src/graphql/resolvers/AccountResolver.ts +22 -0
  135. package/src/{resolvers → graphql/resolvers}/ResolverBase.ts +1 -1
  136. package/src/{resolvers → graphql/resolvers}/TweetResolver.ts +43 -30
  137. package/src/{resolvers → graphql/resolvers}/UserResolver.ts +41 -27
  138. package/src/{models/graphql → graphql/types}/Global.ts +0 -0
  139. package/src/{models/graphql → graphql/types}/TweetTypes.ts +9 -11
  140. package/src/{models/graphql → graphql/types}/UserTypes.ts +13 -15
  141. package/src/index.ts +5 -3
  142. package/src/server.ts +6 -4
  143. package/src/services/AuthService.ts +1 -1
  144. package/src/services/CacheService.ts +6 -8
  145. package/src/services/FetcherService.ts +37 -22
  146. package/src/services/accounts/AccountService.ts +156 -0
  147. package/src/services/accounts/LoginFlows.ts +90 -0
  148. package/src/services/data/TweetService.ts +53 -37
  149. package/src/services/data/UserService.ts +187 -0
  150. package/src/services/helper/Headers.ts +27 -1
  151. package/src/services/helper/Parser.ts +6 -4
  152. package/src/services/helper/{Deserializers.ts → deserializers/Tweets.ts} +3 -28
  153. package/src/services/helper/deserializers/Users.ts +26 -0
  154. package/src/services/helper/extractors/Tweets.ts +252 -0
  155. package/src/services/helper/extractors/Users.ts +137 -0
  156. package/src/services/helper/urls/Authentication.ts +6 -0
  157. package/src/services/helper/urls/Tweets.ts +46 -0
  158. package/src/services/helper/urls/Users.ts +62 -0
  159. package/src/types/HTTP.ts +0 -8
  160. package/src/types/Resolvers.ts +9 -0
  161. package/src/types/data/Errors.ts +28 -0
  162. package/src/types/{Service.ts → data/Service.ts} +6 -5
  163. package/src/types/{Tweet.ts → data/Tweet.ts} +1 -0
  164. package/src/types/{UserAccount.ts → data/User.ts} +0 -0
  165. package/tsconfig.json +2 -2
  166. package/dist/models/graphql/Global.js.map +0 -1
  167. package/dist/models/graphql/TweetTypes.d.ts +0 -6
  168. package/dist/models/graphql/TweetTypes.js.map +0 -1
  169. package/dist/models/graphql/UserTypes.d.ts +0 -3
  170. package/dist/models/graphql/UserTypes.js.map +0 -1
  171. package/dist/queries/RootQuery.js.map +0 -1
  172. package/dist/resolvers/ResolverBase.js.map +0 -1
  173. package/dist/resolvers/TweetResolver.js.map +0 -1
  174. package/dist/resolvers/UserResolver.js.map +0 -1
  175. package/dist/test/Test.js +0 -2
  176. package/dist/test/Test.js.map +0 -1
  177. package/dist/types/graphql/Errors.js.map +0 -1
  178. package/dist/types/raw/user/Users.d.ts +0 -120
  179. package/dist/types/raw/user/Users.js.map +0 -1
  180. package/src/services/data/UserAccountService.ts +0 -176
  181. package/src/services/helper/Extractors.ts +0 -455
  182. package/src/services/helper/Urls.ts +0 -109
  183. package/src/types/graphql/Errors.ts +0 -16
  184. package/src/types/raw/user/Tweets.ts +0 -2847
@@ -0,0 +1,187 @@
1
+ // SERVICES
2
+ import { FetcherService } from '../FetcherService';
3
+ import { AuthService } from '../AuthService';
4
+
5
+ // TYPES
6
+ import { User } from '../../types/data/User';
7
+ import { Tweet } from '../../types/data/Tweet';
8
+ import { CursoredData } from '../../types/data/Service';
9
+ import { Result as TweetData } from '../../types/raw/tweet/Tweet';
10
+ import RawUser, { Result as UserData } from '../../types/raw/user/User';
11
+ import RawUserFollowers from '../../types/raw/user/Followers';
12
+ import RawUserFollowing from '../../types/raw/user/Following';
13
+ import RawUserLikes from '../../types/raw/user/Likes';
14
+ import * as Errors from '../../types/data/Errors';
15
+
16
+ // URLS
17
+ import * as UserUrls from '../helper/urls/Users';
18
+
19
+ // EXTRACTORS
20
+ import * as UserExtractors from '../helper/extractors/Users';
21
+
22
+ // DESERIALIZERS
23
+ import * as UserDeserializers from '../helper/deserializers/Users';
24
+ import * as TweetDeserializers from '../helper/deserializers/Tweets';
25
+
26
+ /**
27
+ * A service that deals with fetching of data related to user account
28
+ */
29
+ export class UserService extends FetcherService {
30
+ // MEMBER METHODS
31
+ constructor(auth: AuthService) {
32
+ super(auth);
33
+ }
34
+
35
+ /**
36
+ * @returns The details of the given user
37
+ * @param screenName The screen name of the target user.
38
+ */
39
+ async getUserDetails(screenName: string): Promise<User> {
40
+ // Fetching the raw data
41
+ let res: RawUser = await this.request<RawUser>(UserUrls.userDetailsUrl(screenName), false).then(res => res.data);
42
+
43
+ // Extracting data
44
+ let data = UserExtractors.extractUserDetails(res);
45
+
46
+ // Caching data
47
+ this.cacheData(data);
48
+
49
+ // Parsing data
50
+ let user = UserDeserializers.toUser(data.required[0]);
51
+
52
+ return user;
53
+ }
54
+
55
+ /**
56
+ * @returns The details of the user with given rest id
57
+ * @param restId The screen name of the target user.
58
+ */
59
+ async getUserDetailsById(restId: string): Promise<User> {
60
+ // Getting data from cache
61
+ let cachedData = await this.readData(restId);
62
+
63
+ // If data exists in cache
64
+ if(cachedData) {
65
+ return cachedData;
66
+ }
67
+
68
+ // Fetchin the raw data
69
+ let res = await this.request<RawUser>(UserUrls.userDetailsByIdUrl(restId), false).then(res => res.data);
70
+
71
+ // Extracting data
72
+ let data = UserExtractors.extractUserDetails(res);
73
+
74
+ // Caching data
75
+ this.cacheData(data);
76
+
77
+ // Parsing data
78
+ let user = UserDeserializers.toUser(data.required[0]);
79
+
80
+ return user;
81
+ }
82
+
83
+ /**
84
+ * @returns The list of users followed by the target user
85
+ * @param userId The rest id of the target user
86
+ * @param count The number of following to fetch, should be >= 40 (when no cursor is provided) and <=100
87
+ * @param cursor The cursor to next batch. If blank, first batch is fetched
88
+ */
89
+ async getUserFollowing(userId: string, count: number, cursor: string): Promise<CursoredData<User>> {
90
+ // If user is not authenticated, abort
91
+ if(!this.isAuthenticated) {
92
+ throw new Error(Errors.AuthenticationErrors.NotAuthenticated);
93
+ }
94
+
95
+ // If invalid count provided
96
+ if (count < 40 && !cursor) {
97
+ throw new Error(Errors.ValidationErrors.InvalidCount);
98
+ }
99
+
100
+ // Fetchin the raw data
101
+ let res = await this.request<RawUserFollowing>(UserUrls.userFollowingUrl(userId, count, cursor)).then(res => res.data);
102
+
103
+ // Extracting data
104
+ let data = UserExtractors.extractUserFollow(res);
105
+
106
+ // Caching data
107
+ this.cacheData(data);
108
+
109
+ // Parsing data
110
+ let users = data.required.map((item: UserData) => UserDeserializers.toUser(item));
111
+
112
+ return {
113
+ list: users,
114
+ next: { value: data.cursor }
115
+ };
116
+ }
117
+
118
+ /**
119
+ * @returns The list of users following the target user
120
+ * @param userId The rest id of the target user
121
+ * @param count The number of followers to fetch, should be >= 40 (when no cursor is provided) and <=100
122
+ * @param cursor The cursor to next batch. If blank, first batch is fetched
123
+ */
124
+ async getUserFollowers(userId: string, count: number, cursor: string): Promise<CursoredData<User>> {
125
+ // If user is not authenticated, abort
126
+ if (!this.isAuthenticated) {
127
+ throw new Error(Errors.AuthenticationErrors.NotAuthenticated);
128
+ }
129
+
130
+ // If invalid count provided
131
+ if (count < 40 && !cursor) {
132
+ throw new Error(Errors.ValidationErrors.InvalidCount);
133
+ }
134
+
135
+ // Fetching the raw data
136
+ let res = await this.request<RawUserFollowers>(UserUrls.userFollowersUrl(userId, count, cursor)).then(res => res.data);
137
+
138
+ // Extracting data
139
+ let data = UserExtractors.extractUserFollow(res);
140
+
141
+ // Caching data
142
+ this.cacheData(data);
143
+
144
+ // Parsing data
145
+ let users = data.required.map((item: UserData) => UserDeserializers.toUser(item));
146
+
147
+ return {
148
+ list: users,
149
+ next: { value: data.cursor }
150
+ };
151
+ }
152
+
153
+ /**
154
+ * @returns The list of tweets liked by the target user
155
+ * @param userId The rest id of the target user
156
+ * @param count The number of likes to fetch, must be >= 40 (when no cursor is provided) and <= 100
157
+ * @param cursor The cursor to next batch. If blank, first batch is fetched
158
+ */
159
+ async getUserLikes(userId: string, count: number, cursor: string): Promise<CursoredData<Tweet>> {
160
+ // If user is not authenticated, abort
161
+ if (!this.isAuthenticated) {
162
+ throw new Error(Errors.AuthenticationErrors.NotAuthenticated);
163
+ }
164
+
165
+ // If invalid count provided
166
+ if (count < 40 && !cursor) {
167
+ throw new Error(Errors.ValidationErrors.InvalidCount);
168
+ }
169
+
170
+ // Fetching the raw data
171
+ let res = await this.request<RawUserLikes>(UserUrls.userLikesUrl(userId, count, cursor)).then(res => res.data);
172
+
173
+ // Extracting data
174
+ let data = UserExtractors.extractUserLikes(res);
175
+
176
+ // Caching data
177
+ this.cacheData(data);
178
+
179
+ // Parsing data
180
+ let tweets = data.required.map((item: TweetData) => TweetDeserializers.toTweet(item));
181
+
182
+ return {
183
+ list: tweets,
184
+ next: { value: data.cursor }
185
+ };
186
+ }
187
+ };
@@ -2,10 +2,10 @@
2
2
  import { GuestCredentials, AuthCredentials } from '../../types/Authentication';
3
3
 
4
4
  /**
5
- * @returns The header required for making authorized HTTP requests
6
5
  * @param authToken The authentication token received from Twitter
7
6
  * @param csrfToken The csrf token received from Twitter
8
7
  * @param cookie The cookie associated with the logged in account
8
+ * @returns The header required for making authorized HTTP requests
9
9
  */
10
10
  export function authorizedHeader(authCred: AuthCredentials): any {
11
11
  return [
@@ -24,6 +24,10 @@ export function authorizedHeader(authCred: AuthCredentials): any {
24
24
  ];
25
25
  }
26
26
 
27
+ /**
28
+ * @param guestCred The guest credentials to use
29
+ * @returns The header requred for making guest HTTP requests
30
+ */
27
31
  export function guestHeader(guestCred: GuestCredentials): any {
28
32
  return [
29
33
  'Accept: */*',
@@ -31,4 +35,26 @@ export function guestHeader(guestCred: GuestCredentials): any {
31
35
  'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36 Edg/109.0.1518.70',
32
36
  `x-guest-token: ${guestCred.guestToken}`
33
37
  ];
38
+ }
39
+
40
+ /**
41
+ * @param guestCred The guest credentials to use for making the login requests
42
+ * @param cookie The cookie to be used
43
+ * @returns The header for making HTTP request for logging in
44
+ */
45
+ export function loginHeader(guestCred: GuestCredentials, cookie: string): any {
46
+ return [
47
+ `sec-ch-ua: "Not_A Brand";v="99", "Microsoft Edge";v="109", "Chromium";v="109"`,
48
+ `x-twitter-client-language: en`,
49
+ `sec-ch-ua-mobile: ?0`,
50
+ `authorization: ${guestCred.authToken}`,
51
+ `User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36 Edg/109.0.1518.78`,
52
+ `content-type: application/json`,
53
+ `x-guest-token: ${guestCred.guestToken}`,
54
+ `x-twitter-active-user: yes`,
55
+ `sec-ch-ua-platform: "Windows"`,
56
+ `Accept: */*`,
57
+ `host: api.twitter.com`,
58
+ `Cookie: ${cookie}`
59
+ ];
34
60
  }
@@ -1,4 +1,5 @@
1
- import { TweetFilter } from '../../types/Tweet';
1
+ // TYPES
2
+ import { TweetFilter } from '../../types/data/Tweet';
2
3
 
3
4
  /**
4
5
  * @returns Whether the given json object is empty or not
@@ -95,14 +96,15 @@ export function toQueryString(filter: TweetFilter): string {
95
96
  // Concatenating the input filter arguments to a URL query formatted string
96
97
  return [
97
98
  filter.words ? filter.words.join(' ') : '',
98
- filter.hashtags ? `(${filter.hashtags.map(hashtag => '%23' + hashtag).join(' OR ')})` : '',
99
+ filter.hashtags ? `(${filter.hashtags.map(hashtag => '#' + hashtag).join(' OR ')})` : '',
99
100
  filter.fromUsers ? `(${filter.fromUsers.map(user => `from:${user}`).join(' OR ')})` : '',
100
101
  filter.toUsers ? `(${filter.toUsers.map(user => `to:${user}`).join(' OR ')})` : '',
101
- filter.mentions ? `(${filter.mentions.map(mention => '%40' + mention).join(' OR ')})` : '',
102
+ filter.mentions ? `(${filter.mentions.map(mention => '@' + mention).join(' OR ')})` : '',
102
103
  filter.startDate ? `since:${filter.startDate}` : '',
103
104
  filter.endDate ? `until:${filter.endDate}` : '',
105
+ filter.sinceId ? `since_id:${filter.sinceId}` : '',
104
106
  filter.quoted ? `quoted_tweet_id:${filter.quoted}` : ''
105
107
  ]
106
108
  .filter(item => item !== '()' && item !== '')
107
- .join(' ') + (!filter.links ? '%20-filter%3Alinks' : '');
109
+ .join(' ') + (!filter.links ? ' -filter:links' : '');
108
110
  }
@@ -1,34 +1,9 @@
1
1
  // TYPES
2
- import { User } from '../../types/UserAccount';
3
- import { Tweet, TweetEntities } from '../../types/Tweet';
4
- import { Result as RawUser } from '../../types/raw/user/User';
5
- import { Result as RawTweet, Entities2 as RawTweetEntities } from '../../types/raw/tweet/Tweet';
2
+ import { Tweet, TweetEntities } from '../../../types/data/Tweet';
3
+ import { Result as RawTweet, Entities2 as RawTweetEntities } from '../../../types/raw/tweet/Tweet';
6
4
 
7
5
  // PARSERS
8
- import * as Parsers from './Parser';
9
-
10
- /**
11
- * @returns A User object containing the user details
12
- * @param data The raw user data from Twitter API
13
- */
14
- export function toUser(data: RawUser): User {
15
- return {
16
- id: data.rest_id,
17
- userName: data.legacy.screen_name,
18
- fullName: data.legacy.name,
19
- createdAt: data.legacy.created_at,
20
- description: data.legacy.description,
21
- isVerified: data.legacy.verified,
22
- favouritesCount: data.legacy.favourites_count,
23
- followersCount: data.legacy.followers_count,
24
- followingsCount: data.legacy.friends_count,
25
- statusesCount: data.legacy.statuses_count,
26
- location: data.legacy.location,
27
- pinnedTweet: data.legacy.pinned_tweet_ids_str[0],
28
- profileBanner: data.legacy.profile_banner_url,
29
- profileImage: data.legacy.profile_image_url_https
30
- };
31
- }
6
+ import * as Parsers from '../Parser';
32
7
 
33
8
  /**
34
9
  * @returns A TweetEntities object containing the various tweet entities
@@ -0,0 +1,26 @@
1
+ // TYPES
2
+ import { User } from '../../../types/data/User';
3
+ import { Result as RawUser } from '../../../types/raw/user/User';
4
+
5
+ /**
6
+ * @returns A User object containing the user details
7
+ * @param data The raw user data from Twitter API
8
+ */
9
+ export function toUser(data: RawUser): User {
10
+ return {
11
+ id: data.rest_id,
12
+ userName: data.legacy.screen_name,
13
+ fullName: data.legacy.name,
14
+ createdAt: data.legacy.created_at,
15
+ description: data.legacy.description,
16
+ isVerified: data.legacy.verified,
17
+ favouritesCount: data.legacy.favourites_count,
18
+ followersCount: data.legacy.followers_count,
19
+ followingsCount: data.legacy.friends_count,
20
+ statusesCount: data.legacy.statuses_count,
21
+ location: data.legacy.location,
22
+ pinnedTweet: data.legacy.pinned_tweet_ids_str[0],
23
+ profileBanner: data.legacy.profile_banner_url,
24
+ profileImage: data.legacy.profile_image_url_https
25
+ };
26
+ }
@@ -0,0 +1,252 @@
1
+ // TYPES
2
+ import { DataExtract } from '../../../types/Resolvers';
3
+ import { DataErrors } from '../../../types/data/Errors';
4
+ import RawTweet from '../../../types/raw/tweet/Tweet';
5
+ import RawTweets from '../../../types/raw/tweet/Tweets';
6
+ import RawRetweeters from '../../../types/raw/tweet/Retweeters';
7
+ import RawLikers from '../../../types/raw/tweet/Favouriters';
8
+
9
+ // PARSERS
10
+ import * as Parsers from '../Parser';
11
+
12
+ /**
13
+ * @returns The raw tweets data formatted and sorted into required and additional data
14
+ * @param res The raw response received from TwitterAPI
15
+ */
16
+ export function extractTweets(res: RawTweets): DataExtract {
17
+ let required: any[] = []; // To store the reqruied raw data
18
+ let cursor: string = ''; // To store the cursor to next batch
19
+ let users: any[] = []; // To store additional user data
20
+ let tweets: any[] = []; // To store additional tweet data
21
+
22
+ // Getting raw tweet list
23
+ let dataTweets = res.globalObjects.tweets;
24
+
25
+ // Getting raw users list
26
+ let dataUsers = res.globalObjects.users;
27
+
28
+ // If tweets found
29
+ if (!Parsers.isJSONEmpty(dataTweets)) {
30
+ // Destructuring the list of tweets
31
+ for (let key of Object.keys(dataTweets)) {
32
+ required.push({ rest_id: dataTweets[key].id_str, legacy: dataTweets[key] });
33
+ tweets.push({ rest_id: dataTweets[key].id_str, legacy: dataTweets[key] });
34
+ }
35
+
36
+ // Destructuring the list of users
37
+ for (let key of Object.keys(dataUsers)) {
38
+ users.push({ rest_id: dataUsers[key].id_str, legacy: dataUsers[key] });
39
+ }
40
+
41
+ // Getting the cursor to next batch
42
+ // If not first batch
43
+ if (res.timeline.instructions.length > 2) {
44
+ cursor = res.timeline.instructions[2]?.replaceEntry.entry.content.operation?.cursor.value ?? '';
45
+ }
46
+ // If first batch
47
+ else {
48
+ cursor = res.timeline.instructions[0].addEntries?.entries.filter(item => item.entryId.indexOf('cursor-bottom') != -1)[0].content.operation?.cursor.value ?? '';
49
+ }
50
+ }
51
+
52
+ // Returning the data
53
+ return {
54
+ required: required,
55
+ cursor: cursor,
56
+ users: users,
57
+ tweets: tweets
58
+ };
59
+ }
60
+
61
+ /**
62
+ * @returns The raw tweet data formatted and sorted into required and additional data
63
+ * @param res The raw response received from TwitterAPI
64
+ * @param tweetId The rest id of the tweet to fetch
65
+ */
66
+ export function extractTweet(res: RawTweet, tweetId: string): DataExtract {
67
+ let required: any[] = []; // To store the reqruied raw data
68
+ let cursor: string = ''; // To store the cursor to next batch
69
+ let users: any[] = []; // To store additional user data
70
+ let tweets: any[] = []; // To store additional tweet data
71
+
72
+ // If tweet does not exist
73
+ if (Parsers.isJSONEmpty(res.data)) {
74
+ throw new Error(DataErrors.TweetNotFound);
75
+ }
76
+
77
+ // Destructuring the received raw data
78
+ res.data.threaded_conversation_with_injections_v2.instructions.filter(item => item['type'] === 'TimelineAddEntries')[0].entries?.forEach(entry => {
79
+ // If entry is of type tweet and tweet exists
80
+ if (entry.entryId.indexOf('tweet') != -1 && entry.content.itemContent?.tweet_results?.result.__typename === 'Tweet') {
81
+ // If this is the required tweet
82
+ if (entry.entryId.indexOf(tweetId) != -1) {
83
+ required.push(entry.content.itemContent.tweet_results.result);
84
+ }
85
+ tweets.push(entry.content.itemContent.tweet_results.result);
86
+ users.push(entry.content.itemContent.tweet_results.result.core.user_results.result);
87
+ }
88
+ // If entry if of type conversation
89
+ else if (entry.entryId.indexOf('conversationthread') != -1) {
90
+ // Iterating over the conversation
91
+ entry.content.items?.forEach(item => {
92
+ // If item is of type tweet and tweet exists
93
+ if (item.entryId.indexOf('tweet') != -1 && item.item.itemContent.tweet_results?.result.__typename === 'Tweet') {
94
+ required.push(item.item.itemContent.tweet_results.result);
95
+ tweets.push(item.item.itemContent.tweet_results.result);
96
+ users.push(item.item.itemContent.tweet_results.result.core.user_results.result);
97
+ }
98
+ });
99
+ }
100
+ });
101
+
102
+ // Returning the data
103
+ return {
104
+ required: required,
105
+ cursor: cursor,
106
+ users: users,
107
+ tweets: tweets
108
+ };
109
+ }
110
+
111
+ /**
112
+ * @returns The raw tweet likers data formatted and sorted into required and additional data
113
+ * @param res The raw response received from TwitterAPI
114
+ */
115
+ export function extractTweetLikers(res: RawLikers): DataExtract {
116
+ let required: any[] = []; // To store the reqruied raw data
117
+ let cursor: string = ''; // To store the cursor to next batch
118
+ let users: any[] = []; // To store additional user data
119
+ let tweets: any[] = []; // To store additional tweet data
120
+
121
+ // If tweet does not exist
122
+ if (Parsers.isJSONEmpty(res.data.favoriters_timeline)) {
123
+ throw new Error(DataErrors.TweetNotFound);
124
+ }
125
+
126
+ // If likes found
127
+ if (res.data.favoriters_timeline.timeline.instructions.length) {
128
+ // Destructuring raw list of likers
129
+ res.data.favoriters_timeline.timeline.instructions.filter(item => item.type === 'TimelineAddEntries')[0].entries.forEach(entry => {
130
+ // If entry is of type user and user exists
131
+ if (entry.entryId.indexOf('user') != -1 && entry.content.itemContent?.user_results.result.__typename === 'User') {
132
+ required.push(entry.content.itemContent.user_results.result);
133
+ users.push(entry.content.itemContent.user_results.result);
134
+ }
135
+ // If entry is of type cursor
136
+ else if (entry.entryId.indexOf('cursor-bottom') != -1) {
137
+ cursor = entry.content.value ?? '';
138
+ }
139
+ });
140
+ }
141
+
142
+ // Returning the data
143
+ return {
144
+ required: required,
145
+ cursor: cursor,
146
+ users: users,
147
+ tweets: tweets
148
+ };
149
+ }
150
+
151
+ /**
152
+ * @returns The raw tweet retweeters data formatted and sorted into required and additional data
153
+ * @param res The raw response received from TwitterAPI
154
+ */
155
+ export function extractTweetRetweeters(res: RawRetweeters): DataExtract {
156
+ let required: any[] = []; // To store the reqruied raw data
157
+ let cursor: string = ''; // To store the cursor to next batch
158
+ let users: any[] = []; // To store additional user data
159
+ let tweets: any[] = []; // To store additional tweet data
160
+
161
+ // If tweet does not exist
162
+ if (Parsers.isJSONEmpty(res.data.retweeters_timeline)) {
163
+ throw new Error(DataErrors.TweetNotFound);
164
+ }
165
+
166
+ // If retweeters found
167
+ if (res.data.retweeters_timeline.timeline.instructions.length) {
168
+ // Destructuring raw list of retweeters
169
+ res.data.retweeters_timeline.timeline.instructions.filter(item => item.type === 'TimelineAddEntries')[0].entries.forEach(entry => {
170
+ // If entry is of type user and user exists
171
+ if (entry.entryId.indexOf('user') != -1 && entry.content.itemContent?.user_results.result.__typename === 'User') {
172
+ required.push(entry.content.itemContent.user_results.result);
173
+ users.push(entry.content.itemContent.user_results.result);
174
+ }
175
+ // If entry is of type cursor
176
+ else if (entry.entryId.indexOf('cursor-bottom') != -1) {
177
+ cursor = entry.content.value ?? '';
178
+ }
179
+ });
180
+ }
181
+
182
+ // Returning the data
183
+ return {
184
+ required: required,
185
+ cursor: cursor,
186
+ users: users,
187
+ tweets: tweets
188
+ };
189
+ }
190
+
191
+ /**
192
+ * @returns The raw tweet replies data formatted and sorted into required and additional data
193
+ * @param res The raw response received from TwitterAPI
194
+ * @param tweetId The id of the tweet whose replies must be extracted
195
+ */
196
+ export function extractTweetReplies(res: RawTweet, tweetId: string): DataExtract {
197
+ let required: any[] = []; // To store the reqruied raw data
198
+ let cursor: string = ''; // To store the cursor to next batch
199
+ let users: any[] = []; // To store additional user data
200
+ let tweets: any[] = []; // To store additional tweet data
201
+
202
+ // If tweet does not exist
203
+ if (Parsers.isJSONEmpty(res.data)) {
204
+ throw new Error(DataErrors.TweetNotFound);
205
+ }
206
+
207
+ // Destructuring the received raw data
208
+ res.data.threaded_conversation_with_injections_v2.instructions.filter(item => item.type === 'TimelineAddEntries')[0].entries?.map(entry => {
209
+ // If entry is of type tweet
210
+ if (entry.entryId.indexOf('tweet') != -1) {
211
+ // If tweet exists
212
+ if (entry.content.itemContent?.tweet_results?.result.__typename === 'Tweet') {
213
+ tweets.push(entry.content.itemContent.tweet_results.result);
214
+ users.push(entry.content.itemContent.tweet_results.result.core.user_results.result);
215
+ }
216
+ }
217
+ // If entry if of type conversation/reply
218
+ else if (entry.entryId.indexOf('conversationthread') != -1) {
219
+ // If tweet exists
220
+ if (entry.content.items?.at(0)?.item.itemContent.tweet_results?.result.__typename === 'Tweet') {
221
+ // Adding the 1st entry, which is a reply, to required list
222
+ required.push(entry.content.items[0].item.itemContent.tweet_results?.result);
223
+ tweets.push(entry.content.items[0].item.itemContent.tweet_results?.result);
224
+ users.push(entry.content.items[0].item.itemContent.tweet_results?.result.core.user_results.result);
225
+ }
226
+
227
+ // Iterating over the rest of the conversation
228
+ entry.content.items?.forEach(item => {
229
+ // If item is of type tweet
230
+ if (item.entryId.indexOf('tweet') != -1) {
231
+ // If tweet exists
232
+ if (item.item.itemContent.tweet_results?.result.__typename === 'Tweet') {
233
+ tweets.push(item.item.itemContent.tweet_results.result);
234
+ users.push(item.item.itemContent.tweet_results.result.core.user_results.result);
235
+ }
236
+ }
237
+ });
238
+ }
239
+ // If entry is of type bottom cursor
240
+ else if (entry.entryId.indexOf('cursor-bottom') != -1) {
241
+ cursor = entry.content.itemContent?.value ?? '';
242
+ }
243
+ });
244
+
245
+ // Returning the data
246
+ return {
247
+ required: required,
248
+ cursor: cursor,
249
+ users: users,
250
+ tweets: tweets
251
+ };
252
+ }