emusks 0.0.3 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -277
- package/build/graphql.js +24 -0
- package/build/v1.1.js +28 -0
- package/build/v2.js +28 -0
- package/package.json +16 -8
- package/src/clients.js +62 -0
- package/src/cycletls.js +26 -0
- package/src/flow.js +399 -0
- package/src/graphql.js +70 -0
- package/src/helpers/account.js +271 -0
- package/src/helpers/bookmarks.js +120 -0
- package/src/helpers/communities.js +271 -0
- package/src/helpers/dms.js +131 -0
- package/src/helpers/index.js +32 -0
- package/src/helpers/lists.js +229 -0
- package/src/helpers/media.js +274 -0
- package/src/helpers/notifications.js +49 -0
- package/src/helpers/search.js +129 -0
- package/src/helpers/spaces.js +55 -0
- package/src/helpers/syndication.js +33 -0
- package/src/helpers/timelines.js +84 -0
- package/src/helpers/topics.js +99 -0
- package/src/helpers/trends.js +86 -0
- package/src/helpers/tweets.js +403 -0
- package/src/helpers/users.js +321 -0
- package/src/index.js +125 -55
- package/src/parsers/timeline.js +105 -0
- package/src/static/graphql.js +1 -0
- package/src/static/v1.1.js +1 -0
- package/src/static/v2.js +1 -0
- package/src/v1.1.js +64 -0
- package/src/v2.js +64 -0
- package/src/methods/bookmarks.js +0 -91
- package/src/methods/follow.js +0 -88
- package/src/methods/like.js +0 -87
- package/src/methods/notifications.js +0 -121
- package/src/methods/retweet.js +0 -91
- package/src/methods/search.js +0 -124
- package/src/methods/timeline.js +0 -215
- package/src/methods/tweet.js +0 -396
- package/src/methods/users.js +0 -209
- package/src/utils/headers.js +0 -23
package/README.md
CHANGED
|
@@ -1,279 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
<img src="https://emusks.tiago.zip/logo.svg" width="120">
|
|
2
|
+
<h1>emusks: Reverse-engineered Twitter API client</h1>
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
Log in and interact with the unofficial X API using any client identity — web, Android, iOS, or TweetDeck
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
import emusks from "emusks";
|
|
7
|
-
|
|
8
|
-
const client = new emusks({
|
|
9
|
-
authToken: "your_auth_token_here",
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
await client.login();
|
|
13
|
-
|
|
14
|
-
console.log(`logged in as ${client.user.username}`);
|
|
15
|
-
|
|
16
|
-
const timeline = await client.getTimeline({
|
|
17
|
-
count: 20,
|
|
18
|
-
type: "foryou", // or "following"
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
console.log(`timeline has ${timeline.tweets.length} tweets`);
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
## features
|
|
25
|
-
|
|
26
|
-
- **timeline**: get "For You" and "Following" timelines with full parsing
|
|
27
|
-
- **tweets**: create, delete, and fetch tweets with polls and media
|
|
28
|
-
- **interactions**: like, retweet, follow, bookmark functionality
|
|
29
|
-
- **search**: search for tweets and users
|
|
30
|
-
- **user data**: get detailed user profiles and statistics
|
|
31
|
-
- **conversations**: handle threaded conversations and replies
|
|
32
|
-
- **pagination**: cursor-based pagination for all timeline endpoints
|
|
33
|
-
|
|
34
|
-
## api reference
|
|
35
|
-
|
|
36
|
-
### timeline
|
|
37
|
-
|
|
38
|
-
get tweets from your timeline with support for both "For You" and "Following" feeds:
|
|
39
|
-
|
|
40
|
-
```javascript
|
|
41
|
-
const timeline = await client.getTimeline({
|
|
42
|
-
count: 20, // tweets to fetch
|
|
43
|
-
type: "foryou", // "foryou" or "following"
|
|
44
|
-
cursor: undefined, // pagination cursor for next page
|
|
45
|
-
includeAds: false, // include promoted content
|
|
46
|
-
seenTweetIds: [], // array of tweet ids
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
// response structure:
|
|
50
|
-
{
|
|
51
|
-
tweets: [...], // array of parsed tweet objects
|
|
52
|
-
conversations: [...], // array of conversation threads
|
|
53
|
-
users: [...], // array of suggested users
|
|
54
|
-
cursors: {
|
|
55
|
-
top: "...", // cursor for newer tweets
|
|
56
|
-
bottom: "..." // cursor for older tweets
|
|
57
|
-
},
|
|
58
|
-
metadata: {
|
|
59
|
-
scribeConfig: {...} // Timeline metadata
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
### Tweets
|
|
65
|
-
|
|
66
|
-
```javascript
|
|
67
|
-
// tweet something
|
|
68
|
-
|
|
69
|
-
const result = await client.tweet({
|
|
70
|
-
text: "Hello world!",
|
|
71
|
-
reply: "1234567890", // (optional)
|
|
72
|
-
poll: {
|
|
73
|
-
// (optional) add a poll
|
|
74
|
-
choices: ["Option 1", "Option 2"],
|
|
75
|
-
duration_minutes: 1440,
|
|
76
|
-
},
|
|
77
|
-
|
|
78
|
-
// TODO: media upload & quote tweets
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
// delete a tweet
|
|
82
|
-
await client.deleteTweet("1234567890");
|
|
83
|
-
|
|
84
|
-
// get a single tweet (no auth required)
|
|
85
|
-
const tweet = await client.getTweet("1234567890");
|
|
86
|
-
|
|
87
|
-
// get full tweet with replies (auth required)
|
|
88
|
-
const fullTweet = await client.getFullTweet("1234567890");
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
### Interactions
|
|
92
|
-
|
|
93
|
-
```javascript
|
|
94
|
-
// like/unlike a tweet
|
|
95
|
-
await client.likeTweet("1234567890");
|
|
96
|
-
await client.unlikeTweet("1234567890");
|
|
97
|
-
|
|
98
|
-
// retweet/unretweet
|
|
99
|
-
await client.retweet("1234567890");
|
|
100
|
-
await client.unretweet("1234567890");
|
|
101
|
-
|
|
102
|
-
// follow/unfollow user
|
|
103
|
-
await client.followUser("username");
|
|
104
|
-
await client.unfollowUser("username");
|
|
105
|
-
|
|
106
|
-
// bookmark/unbookmark
|
|
107
|
-
await client.bookmarkTweet("1234567890");
|
|
108
|
-
await client.unbookmarkTweet("1234567890");
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
### notifications
|
|
112
|
-
|
|
113
|
-
```javascript
|
|
114
|
-
const notifications = await client.getNotifications({
|
|
115
|
-
type: "All", // "All", "Verified", "Mentions"
|
|
116
|
-
limit: 20, // number of notifications to fetch
|
|
117
|
-
cursor: undefined, // pagination cursor for next page
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
// response structure:
|
|
121
|
-
{
|
|
122
|
-
entries: [...], // array of notification entries
|
|
123
|
-
cursors: {
|
|
124
|
-
top: "...", // cursor for newer notifications
|
|
125
|
-
bottom: "..." // cursor for older notifications
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// paginate through notifications
|
|
130
|
-
const nextPage = await client.getNotifications({
|
|
131
|
-
type: "All",
|
|
132
|
-
limit: 20,
|
|
133
|
-
cursor: notifications.cursors.bottom
|
|
134
|
-
});
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
### user tweets
|
|
138
|
-
|
|
139
|
-
get tweets from a specific user with pagination:
|
|
140
|
-
|
|
141
|
-
```javascript
|
|
142
|
-
const userTweets = await client.getUserTweets("1234567890", {
|
|
143
|
-
count: 20, // number of tweets to fetch
|
|
144
|
-
includePromotedContent: false, // include promoted content
|
|
145
|
-
cursor: undefined // pagination cursor
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
// response structure:
|
|
149
|
-
{
|
|
150
|
-
tweets: [...], // array of parsed tweet objects
|
|
151
|
-
cursors: {
|
|
152
|
-
top: "...", // cursor for newer tweets
|
|
153
|
-
bottom: "..." // cursor for older tweets
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// or:
|
|
158
|
-
const user = await client.getUser("username");
|
|
159
|
-
const tweets = await user.getTweets({
|
|
160
|
-
count: 20,
|
|
161
|
-
cursor: undefined
|
|
162
|
-
});
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
### tweet replies
|
|
166
|
-
|
|
167
|
-
get replies to a specific tweet with pagination:
|
|
168
|
-
|
|
169
|
-
```javascript
|
|
170
|
-
// Get full tweet conversation with replies
|
|
171
|
-
const fullTweet = await client.getFullTweet("1234567890", {
|
|
172
|
-
rankingMode: "Relevance", // "Relevance" or "Recency"
|
|
173
|
-
includeAds: false,
|
|
174
|
-
cursor: undefined // pagination cursor for more replies
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
// response structure:
|
|
178
|
-
{
|
|
179
|
-
tweets: [...], // array including main tweet and replies
|
|
180
|
-
cursors: {
|
|
181
|
-
top: "...", // cursor for newer replies
|
|
182
|
-
bottom: "..." // cursor for older replies
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// response structure:
|
|
187
|
-
{
|
|
188
|
-
mainTweet: {...}, // original tweet
|
|
189
|
-
replies: [...], // array of reply tweets
|
|
190
|
-
cursors: {
|
|
191
|
-
top: "...",
|
|
192
|
-
bottom: "..."
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
// if you just need replies:
|
|
197
|
-
const replies = await client.getTweetReplies("1234567890", {
|
|
198
|
-
rankingMode: "Relevance",
|
|
199
|
-
cursor: undefined
|
|
200
|
-
});
|
|
201
|
-
```
|
|
202
|
-
|
|
203
|
-
### search
|
|
204
|
-
|
|
205
|
-
search for tweets:
|
|
206
|
-
|
|
207
|
-
```javascript
|
|
208
|
-
const searchResults = await client.search("bun javascript", {
|
|
209
|
-
resultsCount: 20, // number of results to fetch
|
|
210
|
-
product: "Latest",
|
|
211
|
-
cursor: undefined // pagination cursor
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
// response structure:
|
|
215
|
-
{
|
|
216
|
-
entries: [...], // array of search result entries
|
|
217
|
-
cursors: {
|
|
218
|
-
top: "...", // cursor for newer results
|
|
219
|
-
bottom: "..." // cursor for older results
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// paginate through search results
|
|
224
|
-
const nextResults = await client.search("bun javascript", {
|
|
225
|
-
resultsCount: 20,
|
|
226
|
-
cursor: searchResults.cursors.bottom
|
|
227
|
-
});
|
|
228
|
-
```
|
|
229
|
-
|
|
230
|
-
## requirements
|
|
231
|
-
|
|
232
|
-
- twitter account auth cookie (not required for some endpoints)
|
|
233
|
-
|
|
234
|
-
## installation
|
|
235
|
-
|
|
236
|
-
- **bun:** `bun add emusks`
|
|
237
|
-
- **yarn:** `yarn add emusks`
|
|
238
|
-
- **pnpm:** `pnpm add emusks`
|
|
239
|
-
- **npm:** `npm install emusks`
|
|
240
|
-
|
|
241
|
-
### getting the auth cookie
|
|
242
|
-
|
|
243
|
-
1. log in to https://x.com
|
|
244
|
-
2. open devtools (F12)
|
|
245
|
-
3. go to the "Application" tab
|
|
246
|
-
4. find the "Cookies" section in the left sidebar
|
|
247
|
-
5. find the "x.com" domain
|
|
248
|
-
6. find the "auth_token" cookie
|
|
249
|
-
7. copy the value of the "auth_token" cookie
|
|
250
|
-
|
|
251
|
-
## limitations
|
|
252
|
-
|
|
253
|
-
- the twitter API might change at any time, but this is unlikely to happen
|
|
254
|
-
|
|
255
|
-
- emusks can **not** be used to login without an auth token. twitter has spent a lot of time and effort making it so that this is extremely hard to due to CAPTCHAs, complex flows, etc.
|
|
256
|
-
|
|
257
|
-
- emusks does **not** support all possible endpoints.
|
|
258
|
-
|
|
259
|
-
## faq
|
|
260
|
-
|
|
261
|
-
### why?
|
|
262
|
-
|
|
263
|
-
current ones are outdated, are hard to use, badly documented and such overall.
|
|
264
|
-
|
|
265
|
-
### no, like why did you name it emusks?
|
|
266
|
-
|
|
267
|
-
because elon is responsable for the awful state of twitter's horrible api and twitter overall.
|
|
268
|
-
|
|
269
|
-
### how?
|
|
270
|
-
|
|
271
|
-
endpoints are reverse-engineered from Tweetdeck Web (also known as X Pro), which uses separate endpoints that are less protected than the normal endpoints.
|
|
272
|
-
|
|
273
|
-
unlike the actual X Pro, elon forgot to restrict these endpoints to premium users, so they can be used by anyone.
|
|
274
|
-
|
|
275
|
-
other endpoints (mostly read ones) are reverse-engineered from other parts of twitter, such as embeds.
|
|
276
|
-
|
|
277
|
-
### why not the official API?
|
|
278
|
-
|
|
279
|
-
it's heavily ratelimited, hard to use, and overall very badly documented.
|
|
6
|
+
[Learn more →](https://emusks.tiago.zip)
|
package/build/graphql.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const URL = `https://raw.githubusercontent.com/fa0311/TwitterInternalAPIDocument/refs/heads/develop/docs/json/API.json`;
|
|
2
|
+
|
|
3
|
+
const json = await fetch(URL).then((r) => r.json());
|
|
4
|
+
const graphql = json.graphql;
|
|
5
|
+
|
|
6
|
+
const buildUrl = (base, features) => {
|
|
7
|
+
const params = new URLSearchParams();
|
|
8
|
+
|
|
9
|
+
if (features && Object.keys(features).length) {
|
|
10
|
+
params.set(`features`, JSON.stringify(features));
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const query = params.toString();
|
|
14
|
+
return query ? `${base}?${query}` : base;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const transformed = Object.fromEntries(
|
|
18
|
+
Object.entries(graphql).map(([name, data]) => {
|
|
19
|
+
const constructedUrl = buildUrl(data.url, data.features);
|
|
20
|
+
return [name, [data.method, constructedUrl]];
|
|
21
|
+
}),
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
await Bun.write(`./src/static/graphql.js`, `export default ${JSON.stringify(transformed)};`);
|
package/build/v1.1.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
const URL = `https://raw.githubusercontent.com/fa0311/TwitterInternalAPIDocument/refs/heads/develop/docs/json/v1.1.json`;
|
|
2
|
+
|
|
3
|
+
const json = await fetch(URL).then((r) => r.json());
|
|
4
|
+
|
|
5
|
+
const seen = new Set();
|
|
6
|
+
const duplicates = new Set();
|
|
7
|
+
|
|
8
|
+
for (const entry of json) {
|
|
9
|
+
const queryId = entry.queryId.replace(/^\//, "");
|
|
10
|
+
if (seen.has(queryId)) {
|
|
11
|
+
duplicates.add(queryId);
|
|
12
|
+
}
|
|
13
|
+
seen.add(queryId);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const transformed = {};
|
|
17
|
+
|
|
18
|
+
for (const entry of json) {
|
|
19
|
+
const queryId = entry.queryId.replace(/^\//, "");
|
|
20
|
+
const method = entry.dispatch[0];
|
|
21
|
+
const urlTemplate = entry.dispatch[2];
|
|
22
|
+
const url = urlTemplate.replace("{queryId}", queryId);
|
|
23
|
+
|
|
24
|
+
const key = duplicates.has(queryId) ? `${method}:${queryId}` : queryId;
|
|
25
|
+
transformed[key] = [method, url];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
await Bun.write(`./src/static/v1.1.js`, `export default ${JSON.stringify(transformed)};`);
|
package/build/v2.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
const URL = `https://raw.githubusercontent.com/fa0311/TwitterInternalAPIDocument/refs/heads/develop/docs/json/v2.json`;
|
|
2
|
+
|
|
3
|
+
const json = await fetch(URL).then((r) => r.json());
|
|
4
|
+
|
|
5
|
+
const seen = new Set();
|
|
6
|
+
const duplicates = new Set();
|
|
7
|
+
|
|
8
|
+
for (const entry of json) {
|
|
9
|
+
const queryId = entry.queryId.replace(/^\//, "");
|
|
10
|
+
if (seen.has(queryId)) {
|
|
11
|
+
duplicates.add(queryId);
|
|
12
|
+
}
|
|
13
|
+
seen.add(queryId);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const transformed = {};
|
|
17
|
+
|
|
18
|
+
for (const entry of json) {
|
|
19
|
+
const queryId = entry.queryId.replace(/^\//, "");
|
|
20
|
+
const method = entry.dispatch[0];
|
|
21
|
+
const urlTemplate = entry.dispatch[2];
|
|
22
|
+
const url = urlTemplate.replace("{queryId}", queryId);
|
|
23
|
+
|
|
24
|
+
const key = duplicates.has(queryId) ? `${method}:${queryId}` : queryId;
|
|
25
|
+
transformed[key] = [method, url];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
await Bun.write(`./src/static/v2.js`, `export default ${JSON.stringify(transformed)};`);
|
package/package.json
CHANGED
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "emusks",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "Reverse-engineered X
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
"license": "
|
|
3
|
+
"version": "2.0.1",
|
|
4
|
+
"description": "Reverse-engineered Twitter API client. Log in and interact with the unofficial X API using any client identity — web, Android, iOS, or TweetDeck",
|
|
5
|
+
"keywords": ["twitter", "twitter-api", "x", "x-api", "reverse-engineering", "client"],
|
|
6
|
+
"homepage": "https://github.com/tiagozip/cap",
|
|
7
|
+
"bugs": {
|
|
8
|
+
"url": "https://github.com/tiagozip/cap/issues"
|
|
9
|
+
},
|
|
10
|
+
"license": "AGPL-3.0-only",
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "https://github.com/tiagozip/cap.git"
|
|
14
|
+
},
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"cycletls": "^2.0.5",
|
|
17
|
+
"x-client-transaction-id": "^0.1.9"
|
|
18
|
+
}
|
|
11
19
|
}
|
package/src/clients.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
const chrome_fingerprint = {
|
|
2
|
+
"user-agent":
|
|
3
|
+
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36",
|
|
4
|
+
ja3: "771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,35-5-27-16-0-10-13-23-45-65037-17613-18-65281-51-43-11,4588-29-23-24,0",
|
|
5
|
+
ja4r: "t13d1516h2_002f,0035,009c,009d,1301,1302,1303,c013,c014,c02b,c02c,c02f,c030,cca8,cca9_0005,000a,000b,000d,0012,0017,001b,0023,002b,002d,0033,44cd,fe0d,ff01_0403,0804,0401,0503,0805,0501,0806,0601",
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
const android_fingerprint = {
|
|
9
|
+
ja3: "771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,23-0-16-11-17613-27-43-35-13-65037-51-18-65281-45-5-10-41,4588-29-23-24,0",
|
|
10
|
+
ja4r: "t13d1517h2_002f,0035,009c,009d,1301,1302,1303,c013,c014,c02b,c02c,c02f,c030,cca8,cca9_0005,000a,000b,000d,0012,0017,001b,0023,0029,002b,002d,0033,44cd,fe0d,ff01_0403,0804,0401,0503,0805,0501,0806,0601",
|
|
11
|
+
|
|
12
|
+
userAgent: "TwitterAndroid/10.93.0-release.00 (Android 14; Google Pixel 7 Pro)",
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const iphone_fingerprint = {
|
|
16
|
+
ja3: "771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,23-0-16-11-17613-27-43-35-13-65037-51-18-65281-45-5-10-41,4588-29-23-24,0",
|
|
17
|
+
ja4r: "t13d1517h2_002f,0035,009c,009d,1301,1302,1303,c013,c014,c02b,c02c,c02f,c030,cca8,cca9_0005,000a,000b,000d,0012,0017,001b,0023,0029,002b,002d,0033,44cd,fe0d,ff01_0403,0804,0401,0503,0805,0501,0806,0601",
|
|
18
|
+
|
|
19
|
+
userAgent: "Twitter-iPhone/10.93.0-release.00 (iPhone; iOS 16.5.1; Scale/3.00)",
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export default {
|
|
23
|
+
android: {
|
|
24
|
+
// beware! this might get your account locked when tweeting
|
|
25
|
+
bearer:
|
|
26
|
+
"AAAAAAAAAAAAAAAAAAAAAFXzAwAAAAAAMHCxpeSDG1gLNLghVe8d74hl6k4%3DRUMF4xAQLsbeBhTSRrCiQpJtxoGWeyHrDb5te2jpGskWDFW82F",
|
|
27
|
+
fingerprints: android_fingerprint,
|
|
28
|
+
},
|
|
29
|
+
iphone: {
|
|
30
|
+
bearer:
|
|
31
|
+
"AAAAAAAAAAAAAAAAAAAAAAj4AQAAAAAAPraK64zCZ9CSzdLesbE7LB%2Bw4uE%3DVJQREvQNCZJNiz3rHO7lOXlkVOQkzzdsgu6wWgcazdMUaGoUGm",
|
|
32
|
+
fingerprints: iphone_fingerprint,
|
|
33
|
+
},
|
|
34
|
+
ipad: {
|
|
35
|
+
bearer:
|
|
36
|
+
"AAAAAAAAAAAAAAAAAAAAAGHtAgAAAAAA%2Bx7ILXNILCqkSGIzy6faIHZ9s3Q%3DQy97w6SIrzE7lQwPJEYQBsArEE2fC25caFwRBvAGi456G09vGR",
|
|
37
|
+
fingerprints: iphone_fingerprint,
|
|
38
|
+
},
|
|
39
|
+
mac: {
|
|
40
|
+
bearer:
|
|
41
|
+
"AAAAAAAAAAAAAAAAAAAAAIWCCAAAAAAA2C25AxqI%2BYCS7pdfJKRH8Xh19zA%3D8vpDZzPHaEJhd20MKVWp3UR38YoPpuTX7UD2cVYo3YNikubuxd",
|
|
42
|
+
fingerprints: iphone_fingerprint,
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
old: {
|
|
46
|
+
// doesn't seem to work
|
|
47
|
+
bearer:
|
|
48
|
+
"AAAAAAAAAAAAAAAAAAAAAPYXBAAAAAAACLXUNDekMxqa8h%2F40K4moUkGsoc%3DTYfbDKbT3jJPCEVnMYqilB28NHfOPqkca3qaAxGfsyKCs0wRbw",
|
|
49
|
+
fingerprints: chrome_fingerprint,
|
|
50
|
+
},
|
|
51
|
+
web: {
|
|
52
|
+
bearer:
|
|
53
|
+
"AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA",
|
|
54
|
+
fingerprints: chrome_fingerprint,
|
|
55
|
+
},
|
|
56
|
+
tweetdeck: {
|
|
57
|
+
// requires premium
|
|
58
|
+
bearer:
|
|
59
|
+
"AAAAAAAAAAAAAAAAAAAAAFQODgEAAAAAVHTp76lzh3rFzcHbmHVvQxYYpTw%3DckAlMINMjmCwxUcaXbAN4XqJVdgMJaHqNOFgPMK0zN1qLqLQCF",
|
|
60
|
+
fingerprints: chrome_fingerprint,
|
|
61
|
+
},
|
|
62
|
+
};
|
package/src/cycletls.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import initCycleTLS from "cycletls";
|
|
2
|
+
|
|
3
|
+
let cycleTLS;
|
|
4
|
+
|
|
5
|
+
initCycleTLS().then((c) => {
|
|
6
|
+
cycleTLS = c;
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
export default async function getCycleTLS() {
|
|
10
|
+
if (!cycleTLS) {
|
|
11
|
+
await new Promise((r) => {
|
|
12
|
+
const c = setInterval(() => {
|
|
13
|
+
if (cycleTLS) {
|
|
14
|
+
clearInterval(c);
|
|
15
|
+
r();
|
|
16
|
+
}
|
|
17
|
+
}, 10);
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return cycleTLS;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
process.on("exit", () => {
|
|
25
|
+
if (cycleTLS) cycleTLS.exit();
|
|
26
|
+
});
|