rettiwt-api 1.0.6 → 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.
- 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 -9
- package/dist/{resolvers → graphql/resolvers}/TweetResolver.js +51 -35
- 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 +5 -7
- 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 +6 -6
- package/dist/services/data/TweetService.js +44 -27
- 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 +32 -0
- package/dist/services/helper/extractors/Tweets.js +264 -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 +20 -0
- package/dist/services/helper/extractors/Users.js +151 -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 +26 -0
- package/dist/types/data/Errors.js +36 -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 +43 -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 +9 -11
- 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 +53 -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,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
|
+
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
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 => '
|
|
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 => '
|
|
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 ? '
|
|
109
|
+
.join(' ') + (!filter.links ? ' -filter:links' : '');
|
|
108
110
|
}
|
|
@@ -1,34 +1,9 @@
|
|
|
1
1
|
// TYPES
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
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 '
|
|
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
|
+
}
|
|
@@ -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
|
+
}
|