rettiwt-api 1.0.6 → 1.0.8
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.
- package/dist/{queries → graphql/queries}/RootQuery.d.ts +0 -0
- package/dist/{queries → graphql/queries}/RootQuery.js +4 -3
- package/dist/graphql/queries/RootQuery.js.map +1 -0
- package/dist/{resolvers → graphql/resolvers}/AccountResolver.d.ts +1 -1
- package/dist/{resolvers → graphql/resolvers}/AccountResolver.js +0 -0
- package/dist/graphql/resolvers/AccountResolver.js.map +1 -0
- package/dist/{resolvers → graphql/resolvers}/ResolverBase.d.ts +1 -1
- package/dist/{resolvers → graphql/resolvers}/ResolverBase.js +0 -0
- package/dist/graphql/resolvers/ResolverBase.js.map +1 -0
- package/dist/{resolvers → graphql/resolvers}/TweetResolver.d.ts +10 -18
- package/dist/{resolvers → graphql/resolvers}/TweetResolver.js +51 -83
- package/dist/graphql/resolvers/TweetResolver.js.map +1 -0
- package/dist/{resolvers → graphql/resolvers}/UserResolver.d.ts +5 -4
- package/dist/{resolvers → graphql/resolvers}/UserResolver.js +51 -35
- package/dist/graphql/resolvers/UserResolver.js.map +1 -0
- package/dist/{models/graphql → graphql/types}/Global.d.ts +0 -0
- package/dist/{models/graphql → graphql/types}/Global.js +0 -0
- package/dist/graphql/types/Global.js.map +1 -0
- package/dist/graphql/types/TweetTypes.d.ts +4 -0
- package/dist/{models/graphql → graphql/types}/TweetTypes.js +17 -13
- package/dist/graphql/types/TweetTypes.js.map +1 -0
- package/dist/graphql/types/UserTypes.d.ts +3 -0
- package/dist/{models/graphql → graphql/types}/UserTypes.js +9 -11
- package/dist/graphql/types/UserTypes.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/server.js +3 -3
- package/dist/server.js.map +1 -1
- package/dist/services/AuthService.js +2 -2
- package/dist/services/AuthService.js.map +1 -1
- package/dist/services/CacheService.d.ts +3 -3
- package/dist/services/CacheService.js +22 -75
- package/dist/services/CacheService.js.map +1 -1
- package/dist/services/FetcherService.d.ts +3 -2
- package/dist/services/FetcherService.js +11 -29
- package/dist/services/FetcherService.js.map +1 -1
- package/dist/services/data/TweetService.d.ts +15 -16
- package/dist/services/data/TweetService.js +71 -50
- package/dist/services/data/TweetService.js.map +1 -1
- package/dist/services/data/UserAccountService.d.ts +3 -3
- package/dist/services/data/UserAccountService.js +22 -8
- package/dist/services/data/UserAccountService.js.map +1 -1
- package/dist/services/data/UserService.d.ts +42 -0
- package/dist/services/data/UserService.js +255 -0
- package/dist/services/data/UserService.js.map +1 -0
- package/dist/services/helper/Extractors.js +1 -1
- package/dist/services/helper/Extractors.js.map +1 -1
- package/dist/services/helper/Parser.d.ts +1 -1
- package/dist/services/helper/Parser.js +4 -3
- package/dist/services/helper/Parser.js.map +1 -1
- package/dist/services/helper/Urls.d.ts +2 -2
- package/dist/services/helper/Urls.js +19 -3
- package/dist/services/helper/Urls.js.map +1 -1
- package/dist/services/helper/deserializers/Tweets.d.ts +12 -0
- package/dist/services/helper/deserializers/Tweets.js +92 -0
- package/dist/services/helper/deserializers/Tweets.js.map +1 -0
- package/dist/services/{accounting/Flows.d.ts → helper/deserializers/User.d.ts} +0 -0
- package/dist/services/helper/deserializers/User.js +2 -0
- package/dist/services/helper/deserializers/User.js.map +1 -0
- package/dist/services/helper/deserializers/Users.d.ts +7 -0
- package/dist/services/helper/deserializers/Users.js +27 -0
- package/dist/services/helper/deserializers/Users.js.map +1 -0
- package/dist/services/helper/extractors/TweetExtractors.d.ts +0 -0
- package/dist/services/helper/extractors/TweetExtractors.js +2 -0
- package/dist/services/helper/extractors/TweetExtractors.js.map +1 -0
- package/dist/services/helper/extractors/Tweets.d.ts +34 -0
- package/dist/services/helper/extractors/Tweets.js +266 -0
- package/dist/services/helper/extractors/Tweets.js.map +1 -0
- package/dist/services/helper/extractors/UserExtractors.d.ts +45 -0
- package/dist/services/helper/extractors/UserExtractors.js +176 -0
- package/dist/services/helper/extractors/UserExtractors.js.map +1 -0
- package/dist/services/helper/extractors/Users.d.ts +23 -0
- package/dist/services/helper/extractors/Users.js +154 -0
- package/dist/services/helper/extractors/Users.js.map +1 -0
- package/dist/services/helper/urls/Authentication.d.ts +4 -0
- package/dist/services/helper/urls/Authentication.js +11 -0
- package/dist/services/helper/urls/Authentication.js.map +1 -0
- package/dist/services/helper/urls/Tweets.d.ts +32 -0
- package/dist/services/helper/urls/Tweets.js +51 -0
- package/dist/services/helper/urls/Tweets.js.map +1 -0
- package/dist/services/helper/urls/Urls.d.ts +4 -0
- package/dist/services/helper/urls/Urls.js +11 -0
- package/dist/services/helper/urls/Urls.js.map +1 -0
- package/dist/services/helper/urls/Users.d.ts +31 -0
- package/dist/services/helper/urls/Users.js +66 -0
- package/dist/services/helper/urls/Users.js.map +1 -0
- package/dist/types/{graphql/Errors.d.ts → Errors.d.ts} +0 -0
- package/dist/types/{graphql/Errors.js → Errors.js} +0 -0
- package/dist/types/Errors.js.map +1 -0
- package/dist/types/Resolvers.d.ts +9 -0
- package/dist/types/Resolvers.js +3 -0
- package/dist/types/Resolvers.js.map +1 -0
- package/dist/types/Tweet.d.ts +1 -0
- package/dist/types/Tweet.js.map +1 -1
- package/dist/types/User.d.ts +19 -0
- package/dist/types/User.js +4 -0
- package/dist/types/User.js.map +1 -0
- package/dist/types/data/Errors.d.ts +27 -0
- package/dist/types/data/Errors.js +35 -0
- package/dist/types/data/Errors.js.map +1 -0
- package/dist/types/data/Service.d.ts +29 -0
- package/dist/types/data/Service.js +19 -0
- package/dist/types/data/Service.js.map +1 -0
- package/dist/types/data/Tweet.d.ts +41 -0
- package/dist/types/data/Tweet.js +5 -0
- package/dist/types/data/Tweet.js.map +1 -0
- package/dist/types/data/User.d.ts +19 -0
- package/dist/types/data/User.js +4 -0
- package/dist/types/data/User.js.map +1 -0
- package/dist/types/raw/http/Error.d.ts +34 -0
- package/dist/types/raw/{user/Users.js → http/Error.js} +1 -1
- package/dist/types/raw/http/Error.js.map +1 -0
- package/dist/types/raw/http/Response.d.ts +34 -0
- package/dist/types/raw/http/Response.js +3 -0
- package/dist/types/raw/http/Response.js.map +1 -0
- package/package.json +1 -1
- package/src/{queries → graphql/queries}/RootQuery.ts +5 -4
- package/src/{resolvers → graphql/resolvers}/AccountResolver.ts +21 -21
- package/src/{resolvers → graphql/resolvers}/ResolverBase.ts +1 -1
- package/src/{resolvers → graphql/resolvers}/TweetResolver.ts +48 -30
- package/src/{resolvers → graphql/resolvers}/UserResolver.ts +41 -27
- package/src/{models/graphql → graphql/types}/Global.ts +0 -0
- package/src/{models/graphql → graphql/types}/TweetTypes.ts +16 -12
- package/src/{models/graphql → graphql/types}/UserTypes.ts +13 -15
- package/src/index.ts +2 -2
- package/src/server.ts +3 -3
- package/src/services/AuthService.ts +1 -1
- package/src/services/CacheService.ts +6 -8
- package/src/services/FetcherService.ts +11 -14
- package/src/services/data/TweetService.ts +58 -37
- package/src/services/data/UserService.ts +187 -0
- package/src/services/helper/Parser.ts +6 -4
- package/src/services/helper/{Deserializers.ts → deserializers/Tweets.ts} +3 -28
- package/src/services/helper/deserializers/Users.ts +26 -0
- package/src/services/helper/extractors/Tweets.ts +252 -0
- package/src/services/helper/extractors/Users.ts +137 -0
- package/src/services/helper/urls/Authentication.ts +6 -0
- package/src/services/helper/urls/Tweets.ts +46 -0
- package/src/services/helper/urls/Users.ts +62 -0
- package/src/types/Resolvers.ts +9 -0
- package/src/types/data/Errors.ts +28 -0
- package/src/types/{Service.ts → data/Service.ts} +4 -5
- package/src/types/{Tweet.ts → data/Tweet.ts} +1 -0
- package/src/types/{UserAccount.ts → data/User.ts} +0 -0
- package/tsconfig.json +2 -2
- package/dist/models/graphql/Global.js.map +0 -1
- package/dist/models/graphql/TweetTypes.d.ts +0 -6
- package/dist/models/graphql/TweetTypes.js.map +0 -1
- package/dist/models/graphql/UserTypes.d.ts +0 -3
- package/dist/models/graphql/UserTypes.js.map +0 -1
- package/dist/queries/RootQuery.js.map +0 -1
- package/dist/resolvers/AccountResolver.js.map +0 -1
- package/dist/resolvers/ResolverBase.js.map +0 -1
- package/dist/resolvers/TweetResolver.js.map +0 -1
- package/dist/resolvers/UserResolver.js.map +0 -1
- package/dist/services/AccountsService.d.ts +0 -17
- package/dist/services/AccountsService.js +0 -171
- package/dist/services/AccountsService.js.map +0 -1
- package/dist/services/accounting/AccountsService.d.ts +0 -20
- package/dist/services/accounting/AccountsService.js +0 -147
- package/dist/services/accounting/AccountsService.js.map +0 -1
- package/dist/services/accounting/Flows.js +0 -2
- package/dist/services/accounting/Flows.js.map +0 -1
- package/dist/services/accounting/LoginFlows.d.ts +0 -20
- package/dist/services/accounting/LoginFlows.js +0 -70
- package/dist/services/accounting/LoginFlows.js.map +0 -1
- package/dist/test/Test.js +0 -2
- package/dist/test/Test.js.map +0 -1
- package/dist/types/graphql/Errors.js.map +0 -1
- package/dist/types/raw/user/Users.d.ts +0 -120
- package/dist/types/raw/user/Users.js.map +0 -1
- package/src/services/data/UserAccountService.ts +0 -176
- package/src/services/helper/Extractors.ts +0 -455
- package/src/services/helper/Urls.ts +0 -109
- package/src/types/graphql/Errors.ts +0 -16
- package/src/types/raw/user/Tweets.ts +0 -2847
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
// TYPES
|
|
2
|
+
import { DataExtract } from '../../../types/Resolvers'
|
|
3
|
+
import { DataErrors } from '../../../types/data/Errors';
|
|
4
|
+
import RawUser from '../../../types/raw/user/User';
|
|
5
|
+
import RawUserFollowers from '../../../types/raw/user/Followers';
|
|
6
|
+
import RawUserFollowing from '../../../types/raw/user/Following';
|
|
7
|
+
import RawUserLikes from '../../../types/raw/user/Likes';
|
|
8
|
+
|
|
9
|
+
// PARSERS
|
|
10
|
+
import * as Parsers from '../Parser';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @returns The raw user account data formatted and sorted into required and additional data
|
|
14
|
+
* @param res The raw response received from Twitter
|
|
15
|
+
*/
|
|
16
|
+
export function extractUserDetails(res: RawUser): 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
|
+
// If user not found or account suspended
|
|
23
|
+
if (res.data?.user?.result?.__typename !== 'User') {
|
|
24
|
+
throw new Error(DataErrors.UserNotFound);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Destructuring user account data
|
|
28
|
+
required.push(res.data.user.result);
|
|
29
|
+
users.push(res.data.user.result);
|
|
30
|
+
|
|
31
|
+
// Returning the data
|
|
32
|
+
return {
|
|
33
|
+
required: required,
|
|
34
|
+
cursor: cursor,
|
|
35
|
+
users: users,
|
|
36
|
+
tweets: tweets
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* @returns The raw user following/followers data formatted and sorted into required and additional data
|
|
42
|
+
* @param res The raw response received from TwitterAPI
|
|
43
|
+
*/
|
|
44
|
+
export function extractUserFollow(res: RawUserFollowers | RawUserFollowing): DataExtract {
|
|
45
|
+
let required: any[] = []; // To store the reqruied raw data
|
|
46
|
+
let cursor: string = ''; // To store the cursor to next batch
|
|
47
|
+
let users: any[] = []; // To store additional user data
|
|
48
|
+
let tweets: any[] = []; // To store additional tweet data
|
|
49
|
+
|
|
50
|
+
// If user does not exist
|
|
51
|
+
if (Parsers.isJSONEmpty(res.data.user)) {
|
|
52
|
+
throw new Error(DataErrors.UserNotFound);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Extracting the raw list
|
|
56
|
+
res.data.user.result.timeline.timeline.instructions.forEach(item => {
|
|
57
|
+
if (item.type === 'TimelineAddEntries') {
|
|
58
|
+
// If no follow found
|
|
59
|
+
if (item.entries?.length == 2) {
|
|
60
|
+
// Returning the data
|
|
61
|
+
return {
|
|
62
|
+
required: required,
|
|
63
|
+
cursor: cursor,
|
|
64
|
+
users: users,
|
|
65
|
+
tweets: tweets
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Destructuring data
|
|
70
|
+
item.entries?.forEach(entry => {
|
|
71
|
+
// If entry is of type user and user account exists
|
|
72
|
+
if (entry.entryId.indexOf('user') != -1 && entry.content.itemContent?.user_results.result.__typename === 'User') {
|
|
73
|
+
required.push(entry.content.itemContent.user_results.result);
|
|
74
|
+
users.push(entry.content.itemContent.user_results.result);
|
|
75
|
+
}
|
|
76
|
+
// If entry is of type cursor
|
|
77
|
+
else if (entry.entryId.indexOf('cursor-bottom') != -1) {
|
|
78
|
+
cursor = entry.content.value ?? '';
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// Returning the data
|
|
85
|
+
return {
|
|
86
|
+
required: required,
|
|
87
|
+
cursor: cursor,
|
|
88
|
+
users: users,
|
|
89
|
+
tweets: tweets
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* @returns The raw user likes data formatted and sorted into required and additional data
|
|
95
|
+
* @param res The raw response received from TwitterAPI
|
|
96
|
+
*/
|
|
97
|
+
export function extractUserLikes(res: RawUserLikes): DataExtract {
|
|
98
|
+
let required: any[] = []; // To store the reqruied raw data
|
|
99
|
+
let cursor: string = ''; // To store the cursor to next batch
|
|
100
|
+
let users: any[] = []; // To store additional user data
|
|
101
|
+
let tweets: any[] = []; // To store additional tweet data
|
|
102
|
+
|
|
103
|
+
// If user does not exist
|
|
104
|
+
if (Parsers.isJSONEmpty(res.data.user)) {
|
|
105
|
+
throw new Error(DataErrors.UserNotFound);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// If user likes found
|
|
109
|
+
if (res.data.user.result.timeline_v2.timeline.instructions.length) {
|
|
110
|
+
// Extracting the raw list
|
|
111
|
+
res.data.user.result.timeline_v2.timeline.instructions.forEach(item => {
|
|
112
|
+
if (item.type === 'TimelineAddEntries') {
|
|
113
|
+
// Destructuring data
|
|
114
|
+
item.entries.forEach(entry => {
|
|
115
|
+
// If entry is of type tweet and tweet exists
|
|
116
|
+
if (entry.entryId.indexOf('tweet') != -1 && entry.content.itemContent?.tweet_results.result.__typename === 'Tweet') {
|
|
117
|
+
required.push(entry.content.itemContent.tweet_results.result);
|
|
118
|
+
users.push(entry.content.itemContent.tweet_results.result.core.user_results.result);
|
|
119
|
+
tweets.push(entry.content.itemContent.tweet_results.result);
|
|
120
|
+
}
|
|
121
|
+
// If entry is of type cursor
|
|
122
|
+
else if (entry.entryId.indexOf('cursor-bottom') != -1) {
|
|
123
|
+
cursor = entry.content.value ?? '';
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Returning the data
|
|
131
|
+
return {
|
|
132
|
+
required: required,
|
|
133
|
+
cursor: cursor,
|
|
134
|
+
users: users,
|
|
135
|
+
tweets: tweets
|
|
136
|
+
};
|
|
137
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @returns The url for fetching the list of tweets matching the given filter
|
|
3
|
+
* @param query The query to be used for searching tweets
|
|
4
|
+
* @param count The number of tweets to fetch
|
|
5
|
+
* @param cursor The cusor to next batch *
|
|
6
|
+
*/
|
|
7
|
+
export function tweetsUrl(query: string, count: number, cursor: string): string {
|
|
8
|
+
return `https://api.twitter.com/2/search/adaptive.json?include_want_retweets=1&include_quote_count=true&include_reply_count=1&tweet_mode=extended&include_entities=true&include_user_entities=true&simple_quoted_tweet=true&q=${encodeURIComponent(query)}&tweet_search_mode=live&count=${count}&query_source=typed_query&cursor=${encodeURIComponent(cursor)}`;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @returns The url for fetching the details of a given tweet
|
|
13
|
+
* @param tweetId The rest id of the target tweet
|
|
14
|
+
*/
|
|
15
|
+
export function tweetDetailsUrl(tweetId: string): string {
|
|
16
|
+
return `https://api.twitter.com/graphql/lXI2kaM2hgmbf7h42kpxuA/TweetDetail?variables=%7B%22focalTweetId%22%3A%22${tweetId}%22%2C%22referrer%22%3A%22profile%22%2C%22with_rux_injections%22%3Afalse%2C%22includePromotedContent%22%3Atrue%2C%22withCommunity%22%3Atrue%2C%22withQuickPromoteEligibilityTweetFields%22%3Atrue%2C%22withBirdwatchNotes%22%3Atrue%2C%22withSuperFollowsUserFields%22%3Atrue%2C%22withDownvotePerspective%22%3Afalse%2C%22withReactionsMetadata%22%3Afalse%2C%22withReactionsPerspective%22%3Afalse%2C%22withSuperFollowsTweetFields%22%3Atrue%2C%22withVoice%22%3Atrue%2C%22withV2Timeline%22%3Atrue%7D&features=%7B%22responsive_web_twitter_blue_verified_badge_is_enabled%22%3Atrue%2C%22verified_phone_label_enabled%22%3Atrue%2C%22responsive_web_graphql_timeline_navigation_enabled%22%3Atrue%2C%22view_counts_public_visibility_enabled%22%3Atrue%2C%22longform_notetweets_consumption_enabled%22%3Afalse%2C%22tweetypie_unmention_optimization_enabled%22%3Atrue%2C%22responsive_web_uc_gql_enabled%22%3Atrue%2C%22vibe_api_enabled%22%3Atrue%2C%22responsive_web_edit_tweet_api_enabled%22%3Atrue%2C%22graphql_is_translatable_rweb_tweet_is_translatable_enabled%22%3Atrue%2C%22view_counts_everywhere_api_enabled%22%3Atrue%2C%22standardized_nudges_misinfo%22%3Atrue%2C%22tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled%22%3Afalse%2C%22interactive_text_enabled%22%3Atrue%2C%22responsive_web_text_conversations_enabled%22%3Afalse%2C%22responsive_web_enhance_cards_enabled%22%3Afalse%7D`;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @returns The url for fetching the list of replies to a given tweet
|
|
21
|
+
* @param tweetId The rest of the target tweet
|
|
22
|
+
* @param cursor The curor to next batch
|
|
23
|
+
*/
|
|
24
|
+
export function tweetRepliesUrl(tweetId: string, cursor: string): string {
|
|
25
|
+
return `https://api.twitter.com/graphql/lXI2kaM2hgmbf7h42kpxuA/TweetDetail?variables=%7B%22focalTweetId%22%3A%22${tweetId}%22%2C%22cursor%22%3A%22${encodeURIComponent(cursor)}%22%2C%22referrer%22%3A%22tweet%22%2C%22with_rux_injections%22%3Afalse%2C%22includePromotedContent%22%3Atrue%2C%22withCommunity%22%3Atrue%2C%22withQuickPromoteEligibilityTweetFields%22%3Atrue%2C%22withBirdwatchNotes%22%3Atrue%2C%22withSuperFollowsUserFields%22%3Atrue%2C%22withDownvotePerspective%22%3Afalse%2C%22withReactionsMetadata%22%3Afalse%2C%22withReactionsPerspective%22%3Afalse%2C%22withSuperFollowsTweetFields%22%3Atrue%2C%22withVoice%22%3Atrue%2C%22withV2Timeline%22%3Atrue%7D&features=%7B%22responsive_web_twitter_blue_verified_badge_is_enabled%22%3Atrue%2C%22verified_phone_label_enabled%22%3Atrue%2C%22responsive_web_graphql_timeline_navigation_enabled%22%3Atrue%2C%22view_counts_public_visibility_enabled%22%3Atrue%2C%22longform_notetweets_consumption_enabled%22%3Afalse%2C%22tweetypie_unmention_optimization_enabled%22%3Atrue%2C%22responsive_web_uc_gql_enabled%22%3Atrue%2C%22vibe_api_enabled%22%3Atrue%2C%22responsive_web_edit_tweet_api_enabled%22%3Atrue%2C%22graphql_is_translatable_rweb_tweet_is_translatable_enabled%22%3Atrue%2C%22view_counts_everywhere_api_enabled%22%3Atrue%2C%22standardized_nudges_misinfo%22%3Atrue%2C%22tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled%22%3Afalse%2C%22interactive_text_enabled%22%3Atrue%2C%22responsive_web_text_conversations_enabled%22%3Afalse%2C%22responsive_web_enhance_cards_enabled%22%3Afalse%7D`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @returns The url for fetching the list of users who liked a given tweet
|
|
30
|
+
* @param tweetId The rest id of the target tweet
|
|
31
|
+
* @param count The batch size of the list of users
|
|
32
|
+
* @param cursor The curor to next batch
|
|
33
|
+
*/
|
|
34
|
+
export function tweetLikesUrl(tweetId: string, count: number, cursor: string): string {
|
|
35
|
+
return `https://api.twitter.com/graphql/56ZwFC3Vui31fF8IYX8EGA/Favoriters?variables=%7B%22tweetId%22%3A%22${tweetId}%22%2C%22count%22%3A${count}%2C%22cursor%22%3A%22${encodeURIComponent(cursor)}%22%2C%22includePromotedContent%22%3Atrue%2C%22withSuperFollowsUserFields%22%3Atrue%2C%22withDownvotePerspective%22%3Afalse%2C%22withReactionsMetadata%22%3Afalse%2C%22withReactionsPerspective%22%3Afalse%2C%22withSuperFollowsTweetFields%22%3Atrue%7D&features=%7B%22responsive_web_twitter_blue_verified_badge_is_enabled%22%3Atrue%2C%22verified_phone_label_enabled%22%3Atrue%2C%22responsive_web_graphql_timeline_navigation_enabled%22%3Atrue%2C%22view_counts_public_visibility_enabled%22%3Atrue%2C%22longform_notetweets_consumption_enabled%22%3Afalse%2C%22tweetypie_unmention_optimization_enabled%22%3Atrue%2C%22responsive_web_uc_gql_enabled%22%3Atrue%2C%22vibe_api_enabled%22%3Atrue%2C%22responsive_web_edit_tweet_api_enabled%22%3Atrue%2C%22graphql_is_translatable_rweb_tweet_is_translatable_enabled%22%3Atrue%2C%22view_counts_everywhere_api_enabled%22%3Atrue%2C%22standardized_nudges_misinfo%22%3Atrue%2C%22tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled%22%3Afalse%2C%22interactive_text_enabled%22%3Atrue%2C%22responsive_web_text_conversations_enabled%22%3Afalse%2C%22responsive_web_enhance_cards_enabled%22%3Afalse%7D`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @returns The url for fetching the list of user who retweeted the given tweet
|
|
40
|
+
* @param tweetId The rest id of the target tweet
|
|
41
|
+
* @param count The batch size of the list of users
|
|
42
|
+
* @param cursor The curor to next batch
|
|
43
|
+
*/
|
|
44
|
+
export function tweetRetweetUrl(tweetId: string, count: number, cursor: string): string {
|
|
45
|
+
return `https://api.twitter.com/graphql/Wd7DVeLqMj_JQiTL0tjJwQ/Retweeters?variables=%7B%22tweetId%22%3A%22${tweetId}%22%2C%22count%22%3A${count}%2C%22cursor%22%3A%22${encodeURIComponent(cursor)}%22%2C%22includePromotedContent%22%3Atrue%2C%22withSuperFollowsUserFields%22%3Atrue%2C%22withDownvotePerspective%22%3Afalse%2C%22withReactionsMetadata%22%3Afalse%2C%22withReactionsPerspective%22%3Afalse%2C%22withSuperFollowsTweetFields%22%3Atrue%7D&features=%7B%22responsive_web_twitter_blue_verified_badge_is_enabled%22%3Atrue%2C%22verified_phone_label_enabled%22%3Atrue%2C%22responsive_web_graphql_timeline_navigation_enabled%22%3Atrue%2C%22view_counts_public_visibility_enabled%22%3Atrue%2C%22longform_notetweets_consumption_enabled%22%3Afalse%2C%22tweetypie_unmention_optimization_enabled%22%3Atrue%2C%22responsive_web_uc_gql_enabled%22%3Atrue%2C%22vibe_api_enabled%22%3Atrue%2C%22responsive_web_edit_tweet_api_enabled%22%3Atrue%2C%22graphql_is_translatable_rweb_tweet_is_translatable_enabled%22%3Atrue%2C%22view_counts_everywhere_api_enabled%22%3Atrue%2C%22standardized_nudges_misinfo%22%3Atrue%2C%22tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled%22%3Afalse%2C%22interactive_text_enabled%22%3Atrue%2C%22responsive_web_text_conversations_enabled%22%3Afalse%2C%22responsive_web_enhance_cards_enabled%22%3Afalse%7D`;
|
|
46
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @returns The url for fetching user account details.
|
|
3
|
+
* @param screenName The screen name of the target user
|
|
4
|
+
*/
|
|
5
|
+
export function userDetailsUrl(screenName: string): string {
|
|
6
|
+
return `https://api.twitter.com/graphql/hVhfo_TquFTmgL7gYwf91Q/UserByScreenName?variables=%7B%22screen_name%22%3A%22${screenName}%22%2C%22withSafetyModeUserFields%22%3Atrue%2C%22withSuperFollowsUserFields%22%3Atrue%7D&features=%7B%22responsive_web_twitter_blue_verified_badge_is_enabled%22%3Atrue%2C%22verified_phone_label_enabled%22%3Atrue%2C%22responsive_web_graphql_timeline_navigation_enabled%22%3Atrue%7D`
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @returns The url for fetching user account details.
|
|
11
|
+
* @param restid The restId of the target user
|
|
12
|
+
*/
|
|
13
|
+
export function userDetailsByIdUrl(restId: string): string {
|
|
14
|
+
return `https://api.twitter.com/graphql/mi_IjXgFyr41N9zkszPz9w/UserByRestId?variables=%7B%22userId%22%3A%22${restId}%22%2C%22withSafetyModeUserFields%22%3Atrue%2C%22withSuperFollowsUserFields%22%3Atrue%7D&features=%7B%22responsive_web_twitter_blue_verified_badge_is_enabled%22%3Atrue%2C%22verified_phone_label_enabled%22%3Atrue%2C%22responsive_web_graphql_timeline_navigation_enabled%22%3Atrue%7D`;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @returns The url for fetching the list of users followed bu target user.
|
|
19
|
+
* @param userId The rest id of the target user
|
|
20
|
+
* @param count The batch size of the list of following, should be >= 40 and <=100
|
|
21
|
+
* @param cursor The cursor to next batch
|
|
22
|
+
*/
|
|
23
|
+
export function userFollowingUrl(userId: string, count: number, cursor: string): string {
|
|
24
|
+
/**
|
|
25
|
+
* Twitter has a ver odd behaviour here.
|
|
26
|
+
* If no cursor is provided, the number of followings fetched is slightly more the given count.
|
|
27
|
+
* If a cursor if provided, the number of followings is sometimes less than the provided count.
|
|
28
|
+
* NO SOLUTION EXISTS AS OF NOW!
|
|
29
|
+
*/
|
|
30
|
+
return `https://api.twitter.com/graphql/mSnjZc5CTm2Z5Lu_i4XsPQ/Following?variables=%7B%22userId%22%3A%22${userId}%22%2C%22count%22%3A${count}%2C%22cursor%22%3A%22${encodeURIComponent(cursor)}%22%2C%22includePromotedContent%22%3Afalse%2C%22withSuperFollowsUserFields%22%3Atrue%2C%22withDownvotePerspective%22%3Afalse%2C%22withReactionsMetadata%22%3Afalse%2C%22withReactionsPerspective%22%3Afalse%2C%22withSuperFollowsTweetFields%22%3Atrue%7D&features=%7B%22responsive_web_twitter_blue_verified_badge_is_enabled%22%3Atrue%2C%22verified_phone_label_enabled%22%3Atrue%2C%22responsive_web_graphql_timeline_navigation_enabled%22%3Atrue%2C%22view_counts_public_visibility_enabled%22%3Atrue%2C%22longform_notetweets_consumption_enabled%22%3Afalse%2C%22tweetypie_unmention_optimization_enabled%22%3Atrue%2C%22responsive_web_uc_gql_enabled%22%3Atrue%2C%22vibe_api_enabled%22%3Atrue%2C%22responsive_web_edit_tweet_api_enabled%22%3Atrue%2C%22graphql_is_translatable_rweb_tweet_is_translatable_enabled%22%3Atrue%2C%22view_counts_everywhere_api_enabled%22%3Atrue%2C%22standardized_nudges_misinfo%22%3Atrue%2C%22tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled%22%3Afalse%2C%22interactive_text_enabled%22%3Atrue%2C%22responsive_web_text_conversations_enabled%22%3Afalse%2C%22responsive_web_enhance_cards_enabled%22%3Afalse%7D`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @return The url for fetching the list of followers of the target user
|
|
35
|
+
* @param userId The rest id of the target user
|
|
36
|
+
* @param count The batch size for the list of followers, should be >= 40 and <=100
|
|
37
|
+
* @param cursor The cusor to next batch
|
|
38
|
+
*/
|
|
39
|
+
export function userFollowersUrl(userId: string, count: number, cursor: string): string {
|
|
40
|
+
/**
|
|
41
|
+
* Twitter has a very odd behaviour here.
|
|
42
|
+
* If no cursor is provided, the number of followers fetched is equal to count + 20.
|
|
43
|
+
* If a cursor is provided, the number of followers fetched is equal to count.
|
|
44
|
+
* The solution is to check accordingly, if a cursor if provided or not and manipulate the count
|
|
45
|
+
*/
|
|
46
|
+
// If no cursor if provided
|
|
47
|
+
if (!cursor) {
|
|
48
|
+
count = count - 20;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return `https://api.twitter.com/graphql/nwlAnaw7oKXcVLi91ehy7Q/Followers?variables=%7B%22userId%22%3A%22${userId}%22%2C%22count%22%3A${count}%2C%22cursor%22%3A%22${encodeURIComponent(cursor)}%22%2C%22includePromotedContent%22%3Afalse%2C%22withSuperFollowsUserFields%22%3Atrue%2C%22withDownvotePerspective%22%3Afalse%2C%22withReactionsMetadata%22%3Afalse%2C%22withReactionsPerspective%22%3Afalse%2C%22withSuperFollowsTweetFields%22%3Atrue%7D&features=%7B%22responsive_web_twitter_blue_verified_badge_is_enabled%22%3Atrue%2C%22verified_phone_label_enabled%22%3Atrue%2C%22responsive_web_graphql_timeline_navigation_enabled%22%3Atrue%2C%22view_counts_public_visibility_enabled%22%3Atrue%2C%22longform_notetweets_consumption_enabled%22%3Afalse%2C%22tweetypie_unmention_optimization_enabled%22%3Atrue%2C%22responsive_web_uc_gql_enabled%22%3Atrue%2C%22vibe_api_enabled%22%3Atrue%2C%22responsive_web_edit_tweet_api_enabled%22%3Atrue%2C%22graphql_is_translatable_rweb_tweet_is_translatable_enabled%22%3Atrue%2C%22view_counts_everywhere_api_enabled%22%3Atrue%2C%22standardized_nudges_misinfo%22%3Atrue%2C%22tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled%22%3Afalse%2C%22interactive_text_enabled%22%3Atrue%2C%22responsive_web_text_conversations_enabled%22%3Afalse%2C%22responsive_web_enhance_cards_enabled%22%3Afalse%7D`;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @returns The url for fetching the list of tweets liked by the target user
|
|
56
|
+
* @param userId The rest id of the target user
|
|
57
|
+
* @param count The batch size for the list of tweets
|
|
58
|
+
* @param cursor The cusor to next batch
|
|
59
|
+
*/
|
|
60
|
+
export function userLikesUrl(userId: string, count: number, cursor: string): string {
|
|
61
|
+
return `https://api.twitter.com/graphql/gP4ZKghLd4tpILgS6VudAQ/Likes?variables=%7B%22userId%22%3A%22${userId}%22%2C%22count%22%3A${count}%2C%22cursor%22%3A%22${encodeURIComponent(cursor)}%22%2C%22includePromotedContent%22%3Afalse%2C%22withSuperFollowsUserFields%22%3Atrue%2C%22withDownvotePerspective%22%3Afalse%2C%22withReactionsMetadata%22%3Afalse%2C%22withReactionsPerspective%22%3Afalse%2C%22withSuperFollowsTweetFields%22%3Atrue%2C%22withClientEventToken%22%3Afalse%2C%22withBirdwatchNotes%22%3Afalse%2C%22withVoice%22%3Atrue%2C%22withV2Timeline%22%3Atrue%7D&features=%7B%22responsive_web_twitter_blue_verified_badge_is_enabled%22%3Atrue%2C%22verified_phone_label_enabled%22%3Atrue%2C%22responsive_web_graphql_timeline_navigation_enabled%22%3Atrue%2C%22view_counts_public_visibility_enabled%22%3Atrue%2C%22longform_notetweets_consumption_enabled%22%3Afalse%2C%22tweetypie_unmention_optimization_enabled%22%3Atrue%2C%22responsive_web_uc_gql_enabled%22%3Atrue%2C%22vibe_api_enabled%22%3Atrue%2C%22responsive_web_edit_tweet_api_enabled%22%3Atrue%2C%22graphql_is_translatable_rweb_tweet_is_translatable_enabled%22%3Atrue%2C%22view_counts_everywhere_api_enabled%22%3Atrue%2C%22standardized_nudges_misinfo%22%3Atrue%2C%22tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled%22%3Afalse%2C%22interactive_text_enabled%22%3Atrue%2C%22responsive_web_text_conversations_enabled%22%3Afalse%2C%22responsive_web_enhance_cards_enabled%22%3Afalse%7D`;
|
|
62
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @summary Stores the data returned from extractors
|
|
3
|
+
*/
|
|
4
|
+
export interface DataExtract {
|
|
5
|
+
required: any; // To store the required data extracted
|
|
6
|
+
cursor: string; // To store the cursor extracted
|
|
7
|
+
users: any; // To store all user details extracted
|
|
8
|
+
tweets: any; // To store all tweets exracted
|
|
9
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @summary Stores different types of error messages related to authentication
|
|
3
|
+
*/
|
|
4
|
+
export enum AuthenticationErrors {
|
|
5
|
+
NotAuthenticated = "Cannot fetch this data without authentication",
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @summary Stores different types error messages for validation errors
|
|
10
|
+
*/
|
|
11
|
+
export enum ValidationErrors {
|
|
12
|
+
InvalidTweetFilter = "Atleast one of fromUsers/toUsers/mentions/hashtags/words argument is required",
|
|
13
|
+
NoUserIdentification = "Either userName or id must be given",
|
|
14
|
+
InvalidCount = "Invalid count provided"
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @summary Stores all the different type of error messages that are returned by services
|
|
19
|
+
*/
|
|
20
|
+
export enum DataErrors {
|
|
21
|
+
UserNotFound = "An account with given username/id was not found",
|
|
22
|
+
TweetNotFound = "A tweet with the given id was not found",
|
|
23
|
+
NoTweetsFound = "No tweets matching the given criteria found",
|
|
24
|
+
NoLikersFound = "No likers found for the tweet with the given id",
|
|
25
|
+
NoRetweetersFound = "No retweeters found for the tweet with the given id",
|
|
26
|
+
NoFollowsFound = "No follow details were found for the user with the given id",
|
|
27
|
+
NoLikedTweetsFound = "No liked tweets were found for the user with the given id"
|
|
28
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// SERVICES
|
|
2
|
-
import { AccountService } from "
|
|
3
|
-
import { TweetService } from "
|
|
4
|
-
import {
|
|
2
|
+
import { AccountService } from "../../services/accounts/AccountService";
|
|
3
|
+
import { TweetService } from "../../services/data/TweetService";
|
|
4
|
+
import { UserService } from "../../services/data/UserService";
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* @summary Stores the cursor to the batch of data
|
|
@@ -26,14 +26,13 @@ export class Cursor {
|
|
|
26
26
|
export interface CursoredData<Type> {
|
|
27
27
|
list?: Type[]; // To store the list data
|
|
28
28
|
next?: Cursor; // To store the information about cursor to the next batch
|
|
29
|
-
error?: Error; // To store the error message, if any
|
|
30
29
|
}
|
|
31
30
|
|
|
32
31
|
/**
|
|
33
32
|
* @summary Stores the data context from where data is to be fetched
|
|
34
33
|
*/
|
|
35
34
|
export interface DataContext {
|
|
36
|
-
users:
|
|
35
|
+
users: UserService, // To store the source for fetching user data
|
|
37
36
|
tweets: TweetService, // To store the source for fetching tweet data
|
|
38
37
|
account: AccountService // To store the source for account related operations
|
|
39
38
|
}
|
|
@@ -11,6 +11,7 @@ export interface TweetFilter {
|
|
|
11
11
|
mentions?: string[]; // To store the list of mentioned users
|
|
12
12
|
startDate?: string; // To store the beginning date to search tweets
|
|
13
13
|
endDate?: string; // To store the ending date to search tweets
|
|
14
|
+
sinceId?: string; // To store the first id (exclusive) from which to search tweets
|
|
14
15
|
quoted?: string; // To store the id of the tweet which is quoted
|
|
15
16
|
links?: boolean; // To store whether to fetch link tweets or not
|
|
16
17
|
};
|
|
File without changes
|
package/tsconfig.json
CHANGED
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
|
|
23
23
|
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
|
|
24
24
|
/* Modules */
|
|
25
|
-
// "module": "commonjs",
|
|
26
|
-
|
|
25
|
+
// "module": "commonjs", /* Specify what module code is generated. */
|
|
26
|
+
"rootDir": "./src", /* Specify the root folder within your source files. */
|
|
27
27
|
// "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
|
|
28
28
|
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
|
29
29
|
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Global.js","sourceRoot":"","sources":["../../../src/models/graphql/Global.ts"],"names":[],"mappings":";;;AAAA,UAAU;AACV,mCAA4D;AAE/C,QAAA,MAAM,GAAG,IAAI,2BAAiB,CAAC;IACxC,IAAI,EAAE,QAAQ;IACd,WAAW,EAAE,6CAA6C;IAC1D,MAAM,EAAE,cAAM,OAAA,CAAC;QACX,KAAK,EAAE,EAAE,IAAI,EAAE,uBAAa,EAAE;KACjC,CAAC,EAFY,CAEZ;CACL,CAAC,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"TweetTypes.js","sourceRoot":"","sources":["../../../src/models/graphql/TweetTypes.ts"],"names":[],"mappings":"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"}
|