@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
|
@@ -812,13 +812,16 @@ class ApiRequest {
|
|
|
812
812
|
toRequestUrl() {
|
|
813
813
|
const params = new URLSearchParams();
|
|
814
814
|
if (this.variables) {
|
|
815
|
-
|
|
815
|
+
const variablesStr = stringify(this.variables);
|
|
816
|
+
if (variablesStr) params.set("variables", variablesStr);
|
|
816
817
|
}
|
|
817
818
|
if (this.features) {
|
|
818
|
-
|
|
819
|
+
const featuresStr = stringify(this.features);
|
|
820
|
+
if (featuresStr) params.set("features", featuresStr);
|
|
819
821
|
}
|
|
820
822
|
if (this.fieldToggles) {
|
|
821
|
-
|
|
823
|
+
const fieldTogglesStr = stringify(this.fieldToggles);
|
|
824
|
+
if (fieldTogglesStr) params.set("fieldToggles", fieldTogglesStr);
|
|
822
825
|
}
|
|
823
826
|
return `${this.url}?${params.toString()}`;
|
|
824
827
|
}
|
|
@@ -1544,9 +1547,12 @@ async function getSearchTimeline(query, maxItems, searchMode, auth, cursor) {
|
|
|
1544
1547
|
break;
|
|
1545
1548
|
}
|
|
1546
1549
|
const params = new URLSearchParams();
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
+
const featuresStr = stringify(features);
|
|
1551
|
+
const fieldTogglesStr = stringify(fieldToggles);
|
|
1552
|
+
const variablesStr = stringify(variables);
|
|
1553
|
+
if (featuresStr) params.set("features", featuresStr);
|
|
1554
|
+
if (fieldTogglesStr) params.set("fieldToggles", fieldTogglesStr);
|
|
1555
|
+
if (variablesStr) params.set("variables", variablesStr);
|
|
1550
1556
|
const res = await requestApi(
|
|
1551
1557
|
`https://api.x.com/graphql/gkjsKepM6gl_HmFWoWKfgg/SearchTimeline?${params.toString()}`,
|
|
1552
1558
|
auth
|
|
@@ -1660,8 +1666,10 @@ async function getFollowingTimeline(userId, maxItems, auth, cursor) {
|
|
|
1660
1666
|
variables["cursor"] = cursor;
|
|
1661
1667
|
}
|
|
1662
1668
|
const params = new URLSearchParams();
|
|
1663
|
-
|
|
1664
|
-
|
|
1669
|
+
const featuresStr = stringify(features);
|
|
1670
|
+
const variablesStr = stringify(variables);
|
|
1671
|
+
if (featuresStr) params.set("features", featuresStr);
|
|
1672
|
+
if (variablesStr) params.set("variables", variablesStr);
|
|
1665
1673
|
const res = await requestApi(
|
|
1666
1674
|
`https://x.com/i/api/graphql/iSicc7LrzWGBgDPL0tM_TQ/Following?${params.toString()}`,
|
|
1667
1675
|
auth
|
|
@@ -1695,8 +1703,10 @@ async function getFollowersTimeline(userId, maxItems, auth, cursor) {
|
|
|
1695
1703
|
variables["cursor"] = cursor;
|
|
1696
1704
|
}
|
|
1697
1705
|
const params = new URLSearchParams();
|
|
1698
|
-
|
|
1699
|
-
|
|
1706
|
+
const featuresStr = stringify(features);
|
|
1707
|
+
const variablesStr = stringify(variables);
|
|
1708
|
+
if (featuresStr) params.set("features", featuresStr);
|
|
1709
|
+
if (variablesStr) params.set("variables", variablesStr);
|
|
1700
1710
|
const res = await requestApi(
|
|
1701
1711
|
`https://x.com/i/api/graphql/rRXFSG5vR6drKr5M37YOTw/Followers?${params.toString()}`,
|
|
1702
1712
|
auth
|
|
@@ -1972,6 +1982,155 @@ async function getTweetAnonymous(id, auth) {
|
|
|
1972
1982
|
return parseTimelineEntryItemContentRaw(res.value.data, id);
|
|
1973
1983
|
}
|
|
1974
1984
|
|
|
1985
|
+
async function* getDmConversationMessagesGenerator(conversationId, maxMessages, initialCursor, fetchFunc) {
|
|
1986
|
+
let nMessages = 0;
|
|
1987
|
+
let cursor = initialCursor;
|
|
1988
|
+
while (nMessages < maxMessages) {
|
|
1989
|
+
const batch = await fetchFunc(
|
|
1990
|
+
conversationId,
|
|
1991
|
+
maxMessages,
|
|
1992
|
+
cursor
|
|
1993
|
+
);
|
|
1994
|
+
const { conversation, next } = batch;
|
|
1995
|
+
if (!conversation?.entries || conversation?.entries?.length === 0) {
|
|
1996
|
+
break;
|
|
1997
|
+
}
|
|
1998
|
+
for (const entry of conversation.entries) {
|
|
1999
|
+
if (nMessages < maxMessages) {
|
|
2000
|
+
yield entry;
|
|
2001
|
+
nMessages++;
|
|
2002
|
+
} else {
|
|
2003
|
+
break;
|
|
2004
|
+
}
|
|
2005
|
+
}
|
|
2006
|
+
cursor = next;
|
|
2007
|
+
if (conversation.status === "AT_END" || !next) {
|
|
2008
|
+
break;
|
|
2009
|
+
}
|
|
2010
|
+
await jitter(1e3);
|
|
2011
|
+
}
|
|
2012
|
+
}
|
|
2013
|
+
|
|
2014
|
+
async function fetchDmInbox(auth) {
|
|
2015
|
+
if (!await auth.isLoggedIn()) {
|
|
2016
|
+
throw new AuthenticationError(
|
|
2017
|
+
"Scraper is not logged-in for fetching direct messages."
|
|
2018
|
+
);
|
|
2019
|
+
}
|
|
2020
|
+
const params = new URLSearchParams();
|
|
2021
|
+
addApiParams(params, false);
|
|
2022
|
+
params.set("nsfw_filtering_enabled", "false");
|
|
2023
|
+
params.set("filter_low_quality", "true");
|
|
2024
|
+
params.set("include_quality", "all");
|
|
2025
|
+
params.set("include_ext_profile_image_shape", "1");
|
|
2026
|
+
params.set("dm_secret_conversations_enabled", "false");
|
|
2027
|
+
params.set("krs_registration_enabled", "false");
|
|
2028
|
+
params.set("include_ext_limited_action_results", "true");
|
|
2029
|
+
params.set("dm_users", "true");
|
|
2030
|
+
params.set("include_groups", "true");
|
|
2031
|
+
params.set("include_inbox_timelines", "true");
|
|
2032
|
+
params.set("supports_reactions", "true");
|
|
2033
|
+
params.set("supports_edit", "true");
|
|
2034
|
+
params.set("include_ext_edit_control", "true");
|
|
2035
|
+
params.set("include_ext_business_affiliations_label", "true");
|
|
2036
|
+
params.set("include_ext_parody_commentary_fan_label", "true");
|
|
2037
|
+
params.set(
|
|
2038
|
+
"ext",
|
|
2039
|
+
"mediaColor,altText,mediaStats,highlightedLabel,parodyCommentaryFanLabel,voiceInfo,birdwatchPivot,superFollowMetadata,unmentionInfo,editControl,article"
|
|
2040
|
+
);
|
|
2041
|
+
const res = await requestApi(
|
|
2042
|
+
`https://x.com/i/api/1.1/dm/inbox_initial_state.json?${params.toString()}`,
|
|
2043
|
+
auth
|
|
2044
|
+
);
|
|
2045
|
+
if (!res.success) {
|
|
2046
|
+
throw res.err;
|
|
2047
|
+
}
|
|
2048
|
+
return parseDmInbox(res.value);
|
|
2049
|
+
}
|
|
2050
|
+
async function parseDmInbox(inbox) {
|
|
2051
|
+
return inbox.inbox_initial_state;
|
|
2052
|
+
}
|
|
2053
|
+
async function getDmInbox(auth) {
|
|
2054
|
+
return await fetchDmInbox(auth);
|
|
2055
|
+
}
|
|
2056
|
+
async function fetchDmConversation(conversationId, cursor, auth) {
|
|
2057
|
+
if (!await auth.isLoggedIn()) {
|
|
2058
|
+
throw new AuthenticationError(
|
|
2059
|
+
"Scraper is not logged-in for fetching direct messages."
|
|
2060
|
+
);
|
|
2061
|
+
}
|
|
2062
|
+
const params = new URLSearchParams();
|
|
2063
|
+
addApiParams(params, false);
|
|
2064
|
+
params.set("context", "FETCH_DM_CONVERSATION_HISTORY");
|
|
2065
|
+
params.set("include_ext_profile_image_shape", "1");
|
|
2066
|
+
params.set("dm_secret_conversations_enabled", "false");
|
|
2067
|
+
params.set("krs_registration_enabled", "false");
|
|
2068
|
+
params.set("include_ext_limited_action_results", "true");
|
|
2069
|
+
params.set("dm_users", "true");
|
|
2070
|
+
params.set("include_groups", "true");
|
|
2071
|
+
params.set("include_inbox_timelines", "true");
|
|
2072
|
+
params.set("supports_reactions", "true");
|
|
2073
|
+
params.set("supports_edit", "true");
|
|
2074
|
+
params.set("include_conversation_info", "true");
|
|
2075
|
+
params.set(
|
|
2076
|
+
"ext",
|
|
2077
|
+
"mediaColor,altText,mediaStats,highlightedLabel,parodyCommentaryFanLabel,voiceInfo,birdwatchPivot,superFollowMetadata,unmentionInfo,editControl,article"
|
|
2078
|
+
);
|
|
2079
|
+
if (cursor) {
|
|
2080
|
+
if (cursor.maxId) {
|
|
2081
|
+
params.set("max_id", cursor.maxId);
|
|
2082
|
+
}
|
|
2083
|
+
if (cursor.minId) {
|
|
2084
|
+
params.set("min_id", cursor.minId);
|
|
2085
|
+
}
|
|
2086
|
+
}
|
|
2087
|
+
const url = `https://x.com/i/api/1.1/dm/conversation/${conversationId}.json?${params.toString()}`;
|
|
2088
|
+
const res = await requestApi(url, auth);
|
|
2089
|
+
if (!res.success) {
|
|
2090
|
+
throw res.err;
|
|
2091
|
+
}
|
|
2092
|
+
return parseDmConversation(res.value);
|
|
2093
|
+
}
|
|
2094
|
+
async function parseDmConversation(conversation) {
|
|
2095
|
+
return conversation.conversation_timeline;
|
|
2096
|
+
}
|
|
2097
|
+
async function getDmConversation(conversationId, cursor, auth) {
|
|
2098
|
+
return await fetchDmConversation(conversationId, cursor, auth);
|
|
2099
|
+
}
|
|
2100
|
+
function getDmMessages(conversationId, maxMessages, cursor, auth) {
|
|
2101
|
+
return getDmConversationMessagesGenerator(
|
|
2102
|
+
conversationId,
|
|
2103
|
+
maxMessages,
|
|
2104
|
+
cursor,
|
|
2105
|
+
async (id, _max, cursor2) => {
|
|
2106
|
+
const conversation = await fetchDmConversation(id, cursor2, auth);
|
|
2107
|
+
let next = void 0;
|
|
2108
|
+
if (cursor2?.minId && conversation.max_entry_id) {
|
|
2109
|
+
next = { minId: conversation.max_entry_id };
|
|
2110
|
+
} else if (conversation.min_entry_id) {
|
|
2111
|
+
next = { maxId: conversation.min_entry_id };
|
|
2112
|
+
}
|
|
2113
|
+
return {
|
|
2114
|
+
conversation,
|
|
2115
|
+
next
|
|
2116
|
+
};
|
|
2117
|
+
}
|
|
2118
|
+
);
|
|
2119
|
+
}
|
|
2120
|
+
function findDmConversationsByUserId(inbox, userId) {
|
|
2121
|
+
const conversations = [];
|
|
2122
|
+
for (const conversationId in inbox.conversations) {
|
|
2123
|
+
const conversation = inbox.conversations[conversationId];
|
|
2124
|
+
const hasUser = conversation.participants.some(
|
|
2125
|
+
(participant) => participant.user_id === userId
|
|
2126
|
+
);
|
|
2127
|
+
if (hasUser) {
|
|
2128
|
+
conversations.push(conversation);
|
|
2129
|
+
}
|
|
2130
|
+
}
|
|
2131
|
+
return conversations;
|
|
2132
|
+
}
|
|
2133
|
+
|
|
1975
2134
|
const twUrl = "https://x.com";
|
|
1976
2135
|
class Scraper {
|
|
1977
2136
|
/**
|
|
@@ -2236,6 +2395,45 @@ class Scraper {
|
|
|
2236
2395
|
return getTweetAnonymous(id, this.auth);
|
|
2237
2396
|
}
|
|
2238
2397
|
}
|
|
2398
|
+
/**
|
|
2399
|
+
* Retrieves the direct message inbox for the authenticated user.
|
|
2400
|
+
*
|
|
2401
|
+
* @return A promise that resolves to an object representing the direct message inbox.
|
|
2402
|
+
*/
|
|
2403
|
+
getDmInbox() {
|
|
2404
|
+
return getDmInbox(this.auth);
|
|
2405
|
+
}
|
|
2406
|
+
/**
|
|
2407
|
+
* Retrieves the direct message conversation for the specified conversation ID.
|
|
2408
|
+
*
|
|
2409
|
+
* @param conversationId - The unique identifier of the DM conversation to retrieve.
|
|
2410
|
+
* @param cursor - Use `maxId` to get messages before a message ID (older messages), or `minId` to get messages after a message ID (newer messages).
|
|
2411
|
+
* @return A promise that resolves to the timeline of the DM conversation.
|
|
2412
|
+
*/
|
|
2413
|
+
getDmConversation(conversationId, cursor) {
|
|
2414
|
+
return getDmConversation(conversationId, cursor, this.auth);
|
|
2415
|
+
}
|
|
2416
|
+
/**
|
|
2417
|
+
* Retrieves direct messages from a specific conversation.
|
|
2418
|
+
*
|
|
2419
|
+
* @param conversationId - The unique identifier of the conversation to fetch messages from.
|
|
2420
|
+
* @param [maxMessages=20] - The maximum number of messages to retrieve per request.
|
|
2421
|
+
* @param cursor - Use `maxId` to get messages before a message ID (older messages), or `minId` to get messages after a message ID (newer messages).
|
|
2422
|
+
* @returns An {@link AsyncGenerator} of messages from the provided conversation.
|
|
2423
|
+
*/
|
|
2424
|
+
getDmMessages(conversationId, maxMessages = 20, cursor) {
|
|
2425
|
+
return getDmMessages(conversationId, maxMessages, cursor, this.auth);
|
|
2426
|
+
}
|
|
2427
|
+
/**
|
|
2428
|
+
* Retrieves a list of direct message conversations for a specific user based on their user ID.
|
|
2429
|
+
*
|
|
2430
|
+
* @param inbox - The DM inbox containing all available conversations.
|
|
2431
|
+
* @param userId - The unique identifier of the user whose DM conversations are to be retrieved.
|
|
2432
|
+
* @return An array of DM conversations associated with the specified user ID.
|
|
2433
|
+
*/
|
|
2434
|
+
findDmConversationsByUserId(inbox, userId) {
|
|
2435
|
+
return findDmConversationsByUserId(inbox, userId);
|
|
2436
|
+
}
|
|
2239
2437
|
/**
|
|
2240
2438
|
* Returns if the scraper has a guest token. The token may not be valid.
|
|
2241
2439
|
* @returns `true` if the scraper has a guest token; otherwise `false`.
|