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