@the-convocation/twitter-scraper 0.17.2 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/default/cjs/index.js +208 -10
- package/dist/default/cjs/index.js.map +1 -1
- package/dist/default/esm/index.mjs +208 -10
- package/dist/default/esm/index.mjs.map +1 -1
- package/dist/node/cjs/index.cjs +208 -10
- package/dist/node/cjs/index.cjs.map +1 -1
- package/dist/node/esm/index.mjs +208 -10
- package/dist/node/esm/index.mjs.map +1 -1
- package/dist/types/index.d.ts +185 -1
- package/package.json +1 -1
package/dist/node/esm/index.mjs
CHANGED
|
@@ -790,13 +790,16 @@ class ApiRequest {
|
|
|
790
790
|
toRequestUrl() {
|
|
791
791
|
const params = new URLSearchParams();
|
|
792
792
|
if (this.variables) {
|
|
793
|
-
|
|
793
|
+
const variablesStr = stringify(this.variables);
|
|
794
|
+
if (variablesStr) params.set("variables", variablesStr);
|
|
794
795
|
}
|
|
795
796
|
if (this.features) {
|
|
796
|
-
|
|
797
|
+
const featuresStr = stringify(this.features);
|
|
798
|
+
if (featuresStr) params.set("features", featuresStr);
|
|
797
799
|
}
|
|
798
800
|
if (this.fieldToggles) {
|
|
799
|
-
|
|
801
|
+
const fieldTogglesStr = stringify(this.fieldToggles);
|
|
802
|
+
if (fieldTogglesStr) params.set("fieldToggles", fieldTogglesStr);
|
|
800
803
|
}
|
|
801
804
|
return `${this.url}?${params.toString()}`;
|
|
802
805
|
}
|
|
@@ -1522,9 +1525,12 @@ async function getSearchTimeline(query, maxItems, searchMode, auth, cursor) {
|
|
|
1522
1525
|
break;
|
|
1523
1526
|
}
|
|
1524
1527
|
const params = new URLSearchParams();
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
+
const featuresStr = stringify(features);
|
|
1529
|
+
const fieldTogglesStr = stringify(fieldToggles);
|
|
1530
|
+
const variablesStr = stringify(variables);
|
|
1531
|
+
if (featuresStr) params.set("features", featuresStr);
|
|
1532
|
+
if (fieldTogglesStr) params.set("fieldToggles", fieldTogglesStr);
|
|
1533
|
+
if (variablesStr) params.set("variables", variablesStr);
|
|
1528
1534
|
const res = await requestApi(
|
|
1529
1535
|
`https://api.x.com/graphql/gkjsKepM6gl_HmFWoWKfgg/SearchTimeline?${params.toString()}`,
|
|
1530
1536
|
auth
|
|
@@ -1638,8 +1644,10 @@ async function getFollowingTimeline(userId, maxItems, auth, cursor) {
|
|
|
1638
1644
|
variables["cursor"] = cursor;
|
|
1639
1645
|
}
|
|
1640
1646
|
const params = new URLSearchParams();
|
|
1641
|
-
|
|
1642
|
-
|
|
1647
|
+
const featuresStr = stringify(features);
|
|
1648
|
+
const variablesStr = stringify(variables);
|
|
1649
|
+
if (featuresStr) params.set("features", featuresStr);
|
|
1650
|
+
if (variablesStr) params.set("variables", variablesStr);
|
|
1643
1651
|
const res = await requestApi(
|
|
1644
1652
|
`https://x.com/i/api/graphql/iSicc7LrzWGBgDPL0tM_TQ/Following?${params.toString()}`,
|
|
1645
1653
|
auth
|
|
@@ -1673,8 +1681,10 @@ async function getFollowersTimeline(userId, maxItems, auth, cursor) {
|
|
|
1673
1681
|
variables["cursor"] = cursor;
|
|
1674
1682
|
}
|
|
1675
1683
|
const params = new URLSearchParams();
|
|
1676
|
-
|
|
1677
|
-
|
|
1684
|
+
const featuresStr = stringify(features);
|
|
1685
|
+
const variablesStr = stringify(variables);
|
|
1686
|
+
if (featuresStr) params.set("features", featuresStr);
|
|
1687
|
+
if (variablesStr) params.set("variables", variablesStr);
|
|
1678
1688
|
const res = await requestApi(
|
|
1679
1689
|
`https://x.com/i/api/graphql/rRXFSG5vR6drKr5M37YOTw/Followers?${params.toString()}`,
|
|
1680
1690
|
auth
|
|
@@ -1950,6 +1960,155 @@ async function getTweetAnonymous(id, auth) {
|
|
|
1950
1960
|
return parseTimelineEntryItemContentRaw(res.value.data, id);
|
|
1951
1961
|
}
|
|
1952
1962
|
|
|
1963
|
+
async function* getDmConversationMessagesGenerator(conversationId, maxMessages, initialCursor, fetchFunc) {
|
|
1964
|
+
let nMessages = 0;
|
|
1965
|
+
let cursor = initialCursor;
|
|
1966
|
+
while (nMessages < maxMessages) {
|
|
1967
|
+
const batch = await fetchFunc(
|
|
1968
|
+
conversationId,
|
|
1969
|
+
maxMessages,
|
|
1970
|
+
cursor
|
|
1971
|
+
);
|
|
1972
|
+
const { conversation, next } = batch;
|
|
1973
|
+
if (!conversation?.entries || conversation?.entries?.length === 0) {
|
|
1974
|
+
break;
|
|
1975
|
+
}
|
|
1976
|
+
for (const entry of conversation.entries) {
|
|
1977
|
+
if (nMessages < maxMessages) {
|
|
1978
|
+
yield entry;
|
|
1979
|
+
nMessages++;
|
|
1980
|
+
} else {
|
|
1981
|
+
break;
|
|
1982
|
+
}
|
|
1983
|
+
}
|
|
1984
|
+
cursor = next;
|
|
1985
|
+
if (conversation.status === "AT_END" || !next) {
|
|
1986
|
+
break;
|
|
1987
|
+
}
|
|
1988
|
+
await jitter(1e3);
|
|
1989
|
+
}
|
|
1990
|
+
}
|
|
1991
|
+
|
|
1992
|
+
async function fetchDmInbox(auth) {
|
|
1993
|
+
if (!await auth.isLoggedIn()) {
|
|
1994
|
+
throw new AuthenticationError(
|
|
1995
|
+
"Scraper is not logged-in for fetching direct messages."
|
|
1996
|
+
);
|
|
1997
|
+
}
|
|
1998
|
+
const params = new URLSearchParams();
|
|
1999
|
+
addApiParams(params, false);
|
|
2000
|
+
params.set("nsfw_filtering_enabled", "false");
|
|
2001
|
+
params.set("filter_low_quality", "true");
|
|
2002
|
+
params.set("include_quality", "all");
|
|
2003
|
+
params.set("include_ext_profile_image_shape", "1");
|
|
2004
|
+
params.set("dm_secret_conversations_enabled", "false");
|
|
2005
|
+
params.set("krs_registration_enabled", "false");
|
|
2006
|
+
params.set("include_ext_limited_action_results", "true");
|
|
2007
|
+
params.set("dm_users", "true");
|
|
2008
|
+
params.set("include_groups", "true");
|
|
2009
|
+
params.set("include_inbox_timelines", "true");
|
|
2010
|
+
params.set("supports_reactions", "true");
|
|
2011
|
+
params.set("supports_edit", "true");
|
|
2012
|
+
params.set("include_ext_edit_control", "true");
|
|
2013
|
+
params.set("include_ext_business_affiliations_label", "true");
|
|
2014
|
+
params.set("include_ext_parody_commentary_fan_label", "true");
|
|
2015
|
+
params.set(
|
|
2016
|
+
"ext",
|
|
2017
|
+
"mediaColor,altText,mediaStats,highlightedLabel,parodyCommentaryFanLabel,voiceInfo,birdwatchPivot,superFollowMetadata,unmentionInfo,editControl,article"
|
|
2018
|
+
);
|
|
2019
|
+
const res = await requestApi(
|
|
2020
|
+
`https://x.com/i/api/1.1/dm/inbox_initial_state.json?${params.toString()}`,
|
|
2021
|
+
auth
|
|
2022
|
+
);
|
|
2023
|
+
if (!res.success) {
|
|
2024
|
+
throw res.err;
|
|
2025
|
+
}
|
|
2026
|
+
return parseDmInbox(res.value);
|
|
2027
|
+
}
|
|
2028
|
+
async function parseDmInbox(inbox) {
|
|
2029
|
+
return inbox.inbox_initial_state;
|
|
2030
|
+
}
|
|
2031
|
+
async function getDmInbox(auth) {
|
|
2032
|
+
return await fetchDmInbox(auth);
|
|
2033
|
+
}
|
|
2034
|
+
async function fetchDmConversation(conversationId, cursor, auth) {
|
|
2035
|
+
if (!await auth.isLoggedIn()) {
|
|
2036
|
+
throw new AuthenticationError(
|
|
2037
|
+
"Scraper is not logged-in for fetching direct messages."
|
|
2038
|
+
);
|
|
2039
|
+
}
|
|
2040
|
+
const params = new URLSearchParams();
|
|
2041
|
+
addApiParams(params, false);
|
|
2042
|
+
params.set("context", "FETCH_DM_CONVERSATION_HISTORY");
|
|
2043
|
+
params.set("include_ext_profile_image_shape", "1");
|
|
2044
|
+
params.set("dm_secret_conversations_enabled", "false");
|
|
2045
|
+
params.set("krs_registration_enabled", "false");
|
|
2046
|
+
params.set("include_ext_limited_action_results", "true");
|
|
2047
|
+
params.set("dm_users", "true");
|
|
2048
|
+
params.set("include_groups", "true");
|
|
2049
|
+
params.set("include_inbox_timelines", "true");
|
|
2050
|
+
params.set("supports_reactions", "true");
|
|
2051
|
+
params.set("supports_edit", "true");
|
|
2052
|
+
params.set("include_conversation_info", "true");
|
|
2053
|
+
params.set(
|
|
2054
|
+
"ext",
|
|
2055
|
+
"mediaColor,altText,mediaStats,highlightedLabel,parodyCommentaryFanLabel,voiceInfo,birdwatchPivot,superFollowMetadata,unmentionInfo,editControl,article"
|
|
2056
|
+
);
|
|
2057
|
+
if (cursor) {
|
|
2058
|
+
if (cursor.maxId) {
|
|
2059
|
+
params.set("max_id", cursor.maxId);
|
|
2060
|
+
}
|
|
2061
|
+
if (cursor.minId) {
|
|
2062
|
+
params.set("min_id", cursor.minId);
|
|
2063
|
+
}
|
|
2064
|
+
}
|
|
2065
|
+
const url = `https://x.com/i/api/1.1/dm/conversation/${conversationId}.json?${params.toString()}`;
|
|
2066
|
+
const res = await requestApi(url, auth);
|
|
2067
|
+
if (!res.success) {
|
|
2068
|
+
throw res.err;
|
|
2069
|
+
}
|
|
2070
|
+
return parseDmConversation(res.value);
|
|
2071
|
+
}
|
|
2072
|
+
async function parseDmConversation(conversation) {
|
|
2073
|
+
return conversation.conversation_timeline;
|
|
2074
|
+
}
|
|
2075
|
+
async function getDmConversation(conversationId, cursor, auth) {
|
|
2076
|
+
return await fetchDmConversation(conversationId, cursor, auth);
|
|
2077
|
+
}
|
|
2078
|
+
function getDmMessages(conversationId, maxMessages, cursor, auth) {
|
|
2079
|
+
return getDmConversationMessagesGenerator(
|
|
2080
|
+
conversationId,
|
|
2081
|
+
maxMessages,
|
|
2082
|
+
cursor,
|
|
2083
|
+
async (id, _max, cursor2) => {
|
|
2084
|
+
const conversation = await fetchDmConversation(id, cursor2, auth);
|
|
2085
|
+
let next = void 0;
|
|
2086
|
+
if (cursor2?.minId && conversation.max_entry_id) {
|
|
2087
|
+
next = { minId: conversation.max_entry_id };
|
|
2088
|
+
} else if (conversation.min_entry_id) {
|
|
2089
|
+
next = { maxId: conversation.min_entry_id };
|
|
2090
|
+
}
|
|
2091
|
+
return {
|
|
2092
|
+
conversation,
|
|
2093
|
+
next
|
|
2094
|
+
};
|
|
2095
|
+
}
|
|
2096
|
+
);
|
|
2097
|
+
}
|
|
2098
|
+
function findDmConversationsByUserId(inbox, userId) {
|
|
2099
|
+
const conversations = [];
|
|
2100
|
+
for (const conversationId in inbox.conversations) {
|
|
2101
|
+
const conversation = inbox.conversations[conversationId];
|
|
2102
|
+
const hasUser = conversation.participants.some(
|
|
2103
|
+
(participant) => participant.user_id === userId
|
|
2104
|
+
);
|
|
2105
|
+
if (hasUser) {
|
|
2106
|
+
conversations.push(conversation);
|
|
2107
|
+
}
|
|
2108
|
+
}
|
|
2109
|
+
return conversations;
|
|
2110
|
+
}
|
|
2111
|
+
|
|
1953
2112
|
const twUrl = "https://x.com";
|
|
1954
2113
|
class Scraper {
|
|
1955
2114
|
/**
|
|
@@ -2214,6 +2373,45 @@ class Scraper {
|
|
|
2214
2373
|
return getTweetAnonymous(id, this.auth);
|
|
2215
2374
|
}
|
|
2216
2375
|
}
|
|
2376
|
+
/**
|
|
2377
|
+
* Retrieves the direct message inbox for the authenticated user.
|
|
2378
|
+
*
|
|
2379
|
+
* @return A promise that resolves to an object representing the direct message inbox.
|
|
2380
|
+
*/
|
|
2381
|
+
getDmInbox() {
|
|
2382
|
+
return getDmInbox(this.auth);
|
|
2383
|
+
}
|
|
2384
|
+
/**
|
|
2385
|
+
* Retrieves the direct message conversation for the specified conversation ID.
|
|
2386
|
+
*
|
|
2387
|
+
* @param conversationId - The unique identifier of the DM conversation to retrieve.
|
|
2388
|
+
* @param cursor - Use `maxId` to get messages before a message ID (older messages), or `minId` to get messages after a message ID (newer messages).
|
|
2389
|
+
* @return A promise that resolves to the timeline of the DM conversation.
|
|
2390
|
+
*/
|
|
2391
|
+
getDmConversation(conversationId, cursor) {
|
|
2392
|
+
return getDmConversation(conversationId, cursor, this.auth);
|
|
2393
|
+
}
|
|
2394
|
+
/**
|
|
2395
|
+
* Retrieves direct messages from a specific conversation.
|
|
2396
|
+
*
|
|
2397
|
+
* @param conversationId - The unique identifier of the conversation to fetch messages from.
|
|
2398
|
+
* @param [maxMessages=20] - The maximum number of messages to retrieve per request.
|
|
2399
|
+
* @param cursor - Use `maxId` to get messages before a message ID (older messages), or `minId` to get messages after a message ID (newer messages).
|
|
2400
|
+
* @returns An {@link AsyncGenerator} of messages from the provided conversation.
|
|
2401
|
+
*/
|
|
2402
|
+
getDmMessages(conversationId, maxMessages = 20, cursor) {
|
|
2403
|
+
return getDmMessages(conversationId, maxMessages, cursor, this.auth);
|
|
2404
|
+
}
|
|
2405
|
+
/**
|
|
2406
|
+
* Retrieves a list of direct message conversations for a specific user based on their user ID.
|
|
2407
|
+
*
|
|
2408
|
+
* @param inbox - The DM inbox containing all available conversations.
|
|
2409
|
+
* @param userId - The unique identifier of the user whose DM conversations are to be retrieved.
|
|
2410
|
+
* @return An array of DM conversations associated with the specified user ID.
|
|
2411
|
+
*/
|
|
2412
|
+
findDmConversationsByUserId(inbox, userId) {
|
|
2413
|
+
return findDmConversationsByUserId(inbox, userId);
|
|
2414
|
+
}
|
|
2217
2415
|
/**
|
|
2218
2416
|
* Returns if the scraper has a guest token. The token may not be valid.
|
|
2219
2417
|
* @returns `true` if the scraper has a guest token; otherwise `false`.
|