Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@the-convocation/twitter-scraper

Package Overview
Dependencies
Maintainers
4
Versions
36
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@the-convocation/twitter-scraper - npm Package Compare versions

Comparing version 0.4.0 to 0.5.0

11

dist/api.d.ts

@@ -60,2 +60,13 @@ import { TwitterAuth } from './auth';

responsive_web_enhance_cards_enabled: boolean;
subscriptions_verification_info_enabled: boolean;
subscriptions_verification_info_reason_enabled: boolean;
subscriptions_verification_info_verified_since_enabled: boolean;
super_follow_badge_privacy_enabled: boolean;
super_follow_exclusive_tweet_notifications_enabled: boolean;
super_follow_tweet_api_enabled: boolean;
super_follow_user_api_enabled: boolean;
android_graphql_skip_api_media_color_palette: boolean;
creator_subscriptions_subscription_count_enabled: boolean;
blue_business_profile_image_shape_enabled: boolean;
unified_cards_ad_metadata_container_dynamic_card_content_query_enabled: boolean;
};

@@ -62,0 +73,0 @@ export declare function addApiParams(params: URLSearchParams, includeTweetReplies: boolean): URLSearchParams;

@@ -92,2 +92,13 @@ "use strict";

responsive_web_enhance_cards_enabled: false,
subscriptions_verification_info_enabled: true,
subscriptions_verification_info_reason_enabled: true,
subscriptions_verification_info_verified_since_enabled: true,
super_follow_badge_privacy_enabled: false,
super_follow_exclusive_tweet_notifications_enabled: false,
super_follow_tweet_api_enabled: false,
super_follow_user_api_enabled: false,
android_graphql_skip_api_media_color_palette: false,
creator_subscriptions_subscription_count_enabled: false,
blue_business_profile_image_shape_enabled: false,
unified_cards_ad_metadata_container_dynamic_card_content_query_enabled: false,
};

@@ -94,0 +105,0 @@ }

22

dist/profile.d.ts

@@ -16,2 +16,4 @@ import { RequestApiResult } from './api';

friends_count?: number;
media_count?: number;
statuses_count?: number;
id_str?: string;

@@ -21,3 +23,5 @@ listed_count?: number;

location: string;
geo_enabled?: boolean;
pinned_tweet_ids_str?: string[];
profile_background_color?: string;
profile_banner_url?: string;

@@ -27,4 +31,6 @@ profile_image_url_https?: string;

screen_name?: string;
statuses_count?: number;
verified?: boolean;
has_custom_timelines?: boolean;
has_extended_profile?: boolean;
url?: string;
}

@@ -42,4 +48,7 @@ /**

friendsCount?: number;
mediaCount?: number;
statusesCount?: number;
isPrivate?: boolean;
isVerified?: boolean;
isBlueVerified?: boolean;
joined?: Date;

@@ -59,5 +68,8 @@ likesCount?: number;

data: {
user: {
rest_id?: string;
legacy: LegacyUserRaw;
user_result: {
result: {
rest_id?: string;
isBlueVerified: boolean;
legacy: LegacyUserRaw;
};
};

@@ -69,5 +81,5 @@ };

}
export declare function parseProfile(user: LegacyUserRaw): Profile;
export declare function parseProfile(user: LegacyUserRaw, isBlueVerified?: boolean): Profile;
export declare function getProfile(username: string, auth: TwitterAuth): Promise<RequestApiResult<Profile>>;
export declare function getUserIdByScreenName(screenName: string, auth: TwitterAuth): Promise<RequestApiResult<string>>;
//# sourceMappingURL=profile.d.ts.map
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getUserIdByScreenName = exports.getProfile = exports.parseProfile = void 0;
const json_stable_stringify_1 = __importDefault(require("json-stable-stringify"));
const api_1 = require("./api");
function parseProfile(user) {
function parseProfile(user, isBlueVerified) {
const profile = {

@@ -13,2 +17,3 @@ avatar: user.profile_image_url_https,

friendsCount: user.friends_count,
mediaCount: user.media_count,
isPrivate: user.protected,

@@ -25,2 +30,3 @@ isVerified: user.verified,

username: user.screen_name,
isBlueVerified: isBlueVerified ?? false,
};

@@ -39,7 +45,15 @@ if (user.created_at != null) {

const params = new URLSearchParams();
params.set('variables', JSON.stringify({
params.set('variables', (0, json_stable_stringify_1.default)({
screen_name: username,
withHighlightedLabel: true,
}));
const res = await (0, api_1.requestApi)(`https://api.twitter.com/graphql/4S2ihIKfF3xhp-ENxvUAfQ/UserByScreenName?${params}`, auth);
const features = (0, api_1.addApiFeatures)({
interactive_text_enabled: true,
longform_notetweets_inline_media_enabled: false,
responsive_web_text_conversations_enabled: false,
tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled: false,
vibe_api_enabled: false,
});
params.set('features', (0, json_stable_stringify_1.default)(features));
const res = await (0, api_1.requestApi)(`https://api.twitter.com/graphql/u7wQyGi6oExe8_TRWGMq4Q/UserResultByScreenNameQuery?${params.toString()}`, auth);
if (!res.success) {

@@ -56,3 +70,3 @@ return res;

}
const { user } = value.data;
const { result: user } = value.data.user_result;
const { legacy } = user;

@@ -74,3 +88,3 @@ if (user.rest_id == null || user.rest_id.length === 0) {

success: true,
value: parseProfile(user.legacy),
value: parseProfile(user.legacy, user.isBlueVerified),
};

@@ -77,0 +91,0 @@ }

@@ -6,3 +6,3 @@ import { Cookie } from 'tough-cookie';

import { QueryProfilesResponse, QueryTweetsResponse } from './timeline-v1';
import { Tweet } from './tweets';
import { Tweet, TweetQuery } from './tweets';
import fetch from 'cross-fetch';

@@ -108,2 +108,36 @@ export interface ScraperOptions {

/**
* Fetches the first tweet matching the given query.
*
* Example:
* ```js
* const timeline = scraper.getTweets('user', 200);
* const retweet = await scraper.getTweetWhere(timeline, { isRetweet: true });
* ```
* @param tweets The {@link AsyncIterable} of tweets to search through.
* @param query A query to test **all** tweets against. This may be either an
* object of key/value pairs or a predicate. If this query is an object, all
* key/value pairs must match a {@link Tweet} for it to be returned. If this query
* is a predicate, it must resolve to `true` for a {@link Tweet} to be returned.
* - All keys are optional.
* - If specified, the key must be implemented by that of {@link Tweet}.
*/
getTweetWhere(tweets: AsyncIterable<Tweet>, query: TweetQuery): Promise<Tweet | null>;
/**
* Fetches all tweets matching the given query.
*
* Example:
* ```js
* const timeline = scraper.getTweets('user', 200);
* const retweets = await scraper.getTweetsWhere(timeline, { isRetweet: true });
* ```
* @param tweets The {@link AsyncIterable} of tweets to search through.
* @param query A query to test **all** tweets against. This may be either an
* object of key/value pairs or a predicate. If this query is an object, all
* key/value pairs must match a {@link Tweet} for it to be returned. If this query
* is a predicate, it must resolve to `true` for a {@link Tweet} to be returned.
* - All keys are optional.
* - If specified, the key must be implemented by that of {@link Tweet}.
*/
getTweetsWhere(tweets: AsyncIterable<Tweet>, query: TweetQuery): Promise<Tweet[]>;
/**
* Fetches the most recent tweet from a Twitter user.

@@ -114,3 +148,3 @@ * @param user The user whose latest tweet should be returned.

*/
getLatestTweet(user: string, includeRetweets?: boolean): Promise<Tweet | null | void>;
getLatestTweet(user: string, includeRetweets?: boolean, max?: number): Promise<Tweet | null | void>;
/**

@@ -117,0 +151,0 @@ * Fetches a single tweet.

@@ -122,2 +122,40 @@ "use strict";

/**
* Fetches the first tweet matching the given query.
*
* Example:
* ```js
* const timeline = scraper.getTweets('user', 200);
* const retweet = await scraper.getTweetWhere(timeline, { isRetweet: true });
* ```
* @param tweets The {@link AsyncIterable} of tweets to search through.
* @param query A query to test **all** tweets against. This may be either an
* object of key/value pairs or a predicate. If this query is an object, all
* key/value pairs must match a {@link Tweet} for it to be returned. If this query
* is a predicate, it must resolve to `true` for a {@link Tweet} to be returned.
* - All keys are optional.
* - If specified, the key must be implemented by that of {@link Tweet}.
*/
getTweetWhere(tweets, query) {
return (0, tweets_1.getTweetWhere)(tweets, query);
}
/**
* Fetches all tweets matching the given query.
*
* Example:
* ```js
* const timeline = scraper.getTweets('user', 200);
* const retweets = await scraper.getTweetsWhere(timeline, { isRetweet: true });
* ```
* @param tweets The {@link AsyncIterable} of tweets to search through.
* @param query A query to test **all** tweets against. This may be either an
* object of key/value pairs or a predicate. If this query is an object, all
* key/value pairs must match a {@link Tweet} for it to be returned. If this query
* is a predicate, it must resolve to `true` for a {@link Tweet} to be returned.
* - All keys are optional.
* - If specified, the key must be implemented by that of {@link Tweet}.
*/
getTweetsWhere(tweets, query) {
return (0, tweets_1.getTweetsWhere)(tweets, query);
}
/**
* Fetches the most recent tweet from a Twitter user.

@@ -128,4 +166,4 @@ * @param user The user whose latest tweet should be returned.

*/
getLatestTweet(user, includeRetweets = false) {
return (0, tweets_1.getLatestTweet)(user, includeRetweets, this.auth);
getLatestTweet(user, includeRetweets = false, max = 200) {
return (0, tweets_1.getLatestTweet)(user, includeRetweets, max, this.auth);
}

@@ -132,0 +170,0 @@ /**

@@ -63,2 +63,5 @@ "use strict";

tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled: true,
interactive_text_enabled: false,
responsive_web_text_conversations_enabled: false,
vibe_api_enabled: false,
});

@@ -91,3 +94,3 @@ const fieldToggles = {

params.set('variables', (0, json_stable_stringify_1.default)(variables));
const res = await (0, api_1.requestApi)(`https://twitter.com/i/api/graphql/nK1dw4oV3k4w5TdtcAdSww/SearchTimeline?${params.toString()}`, auth);
const res = await (0, api_1.requestApi)(`https://api.twitter.com/graphql/gkjsKepM6gl_HmFWoWKfgg/SearchTimeline?${params.toString()}`, auth);
if (!res.success) {

@@ -94,0 +97,0 @@ throw res.err;

import { QueryProfilesResponse, QueryTweetsResponse } from './timeline-v1';
import { TimelineEntryRaw } from './timeline-v2';
import { SearchEntryRaw } from './timeline-v2';
export interface SearchTimeline {

@@ -9,4 +9,4 @@ data?: {

instructions?: {
entries?: TimelineEntryRaw[];
entry?: TimelineEntryRaw;
entries?: SearchEntryRaw[];
entry?: SearchEntryRaw;
type?: string;

@@ -13,0 +13,0 @@ }[];

@@ -18,3 +18,4 @@ "use strict";

}
for (const entry of instruction.entries ?? []) {
const entries = instruction.entries ?? [];
for (const entry of entries) {
const itemContent = entry.content?.itemContent;

@@ -55,3 +56,4 @@ if (itemContent?.tweetDisplayType === 'Tweet') {

}
for (const entry of instruction.entries ?? []) {
const entries = instruction.entries ?? [];
for (const entry of entries) {
const itemContent = entry.content?.itemContent;

@@ -61,5 +63,5 @@ if (itemContent?.userDisplayType === 'User') {

if (userResultRaw?.legacy) {
const profile = (0, profile_1.parseProfile)(userResultRaw.legacy);
const profile = (0, profile_1.parseProfile)(userResultRaw.legacy, userResultRaw.is_blue_verified);
if (!profile.userId) {
profile.userId = itemContent.user_results?.result?.rest_id;
profile.userId = userResultRaw.rest_id;
}

@@ -66,0 +68,0 @@ profiles.push(profile);

@@ -40,3 +40,4 @@ import { LegacyUserRaw, Profile } from './profile';

}
export interface TimelineResultRaw {
export interface SearchResultRaw {
rest_id?: string;
__typename?: string;

@@ -62,2 +63,28 @@ core?: {

quoted_status_result?: {
result?: SearchResultRaw;
};
legacy?: LegacyTweetRaw;
}
export interface TimelineResultRaw {
rest_id?: string;
__typename?: string;
core?: {
user_result?: {
result?: {
is_blue_verified?: boolean;
legacy?: LegacyUserRaw;
};
};
};
views?: {
count?: string;
};
note_tweet?: {
note_tweet_results?: {
result?: {
text?: string;
};
};
};
quoted_status_result?: {
result?: TimelineResultRaw;

@@ -64,0 +91,0 @@ };

import { LegacyUserRaw } from './profile';
import { LegacyTweetRaw, ParseTweetResult, QueryTweetsResponse, TimelineResultRaw } from './timeline-v1';
import { LegacyTweetRaw, ParseTweetResult, QueryTweetsResponse, SearchResultRaw, TimelineResultRaw } from './timeline-v1';
import { Tweet } from './tweets';

@@ -7,6 +7,7 @@ export interface TimelineUserResultRaw {

legacy?: LegacyUserRaw;
is_blue_verified?: boolean;
}
export interface TimelineEntryItemContentRaw {
tweetDisplayType?: string;
tweet_results?: {
tweetResult?: {
result?: TimelineResultRaw;

@@ -20,2 +21,3 @@ };

export interface TimelineEntryRaw {
entryId: string;
content?: {

@@ -26,19 +28,46 @@ cursorType?: string;

item?: {
itemContent?: TimelineEntryItemContentRaw;
content?: TimelineEntryItemContentRaw;
};
}[];
itemContent?: TimelineEntryItemContentRaw;
content?: TimelineEntryItemContentRaw;
};
}
export interface SearchEntryItemContentRaw {
tweetDisplayType?: string;
tweet_results?: {
result?: SearchResultRaw;
};
userDisplayType?: string;
user_results?: {
result?: TimelineUserResultRaw;
};
}
export interface SearchEntryRaw {
entryId: string;
sortIndex: string;
content?: {
cursorType?: string;
entryType?: string;
__typename?: string;
value?: string;
items?: {
item?: {
content?: SearchEntryItemContentRaw;
};
}[];
itemContent?: SearchEntryItemContentRaw;
};
}
export interface TimelineInstruction {
entries: TimelineEntryRaw[];
entry?: TimelineEntryRaw;
type?: string;
}
export interface TimelineV2 {
data?: {
user?: {
user_result?: {
result?: {
timeline_v2?: {
timeline_response?: {
timeline?: {
instructions?: {
entries?: TimelineEntryRaw[];
entry?: TimelineEntryRaw;
type?: string;
}[];
instructions: TimelineInstruction[];
};

@@ -52,8 +81,4 @@ };

data?: {
threaded_conversation_with_injections_v2?: {
instructions?: {
entries?: TimelineEntryRaw[];
entry?: TimelineEntryRaw;
type?: string;
}[];
timeline_response?: {
instructions?: TimelineInstruction[];
};

@@ -60,0 +85,0 @@ };

@@ -19,2 +19,11 @@ "use strict";

}
if (tweet.id_str == null) {
if (!tweet.conversation_id_str) {
return {
success: false,
err: new Error('Tweet ID was not found in object.'),
};
}
tweet.id_str = tweet.conversation_id_str;
}
const hashtags = tweet.entities?.hashtags ?? [];

@@ -26,8 +35,2 @@ const mentions = tweet.entities?.user_mentions ?? [];

const { photos, videos, sensitiveContent } = (0, timeline_tweet_util_1.parseMediaGroups)(media);
if (tweet.id_str == null) {
return {
success: false,
err: new Error('Tweet ID was not found in object.'),
};
}
const tw = {

@@ -58,2 +61,7 @@ conversationId: tweet.conversation_id_str,

videos,
isQuoted: false,
isReply: false,
isRetweet: false,
isPin: false,
sensitiveContent: false,
};

@@ -67,17 +75,21 @@ if (tweet.created_at) {

}
if (tweet.quoted_status_id_str) {
const quotedStatusIdStr = tweet.quoted_status_id_str;
const inReplyToStatusIdStr = tweet.in_reply_to_status_id_str;
const retweetedStatusIdStr = tweet.retweeted_status_id_str;
const retweetedStatusResult = tweet.retweeted_status_result?.result;
if (quotedStatusIdStr) {
tw.isQuoted = true;
tw.quotedStatusId = tweet.quoted_status_id_str;
tw.quotedStatusId = quotedStatusIdStr;
}
if (tweet.in_reply_to_status_id_str) {
if (inReplyToStatusIdStr) {
tw.isReply = true;
tw.inReplyToStatusId = tweet.in_reply_to_status_id_str;
tw.inReplyToStatusId = inReplyToStatusIdStr;
}
if (tweet.retweeted_status_id_str || tweet.retweeted_status_result?.result) {
if (retweetedStatusIdStr || retweetedStatusResult) {
tw.isRetweet = true;
tw.retweetedStatusId = tweet.retweeted_status_id_str;
if (tweet.retweeted_status_result?.result) {
const retweetedStatusResult = parseLegacyTweet(tweet.retweeted_status_result.result.core?.user_results?.result?.legacy, tweet.retweeted_status_result.result.legacy);
if (retweetedStatusResult.success) {
tw.retweetedStatus = retweetedStatusResult.tweet;
tw.retweetedStatusId = retweetedStatusIdStr;
if (retweetedStatusResult) {
const parsedResult = parseLegacyTweet(retweetedStatusResult?.core?.user_result?.result?.legacy, retweetedStatusResult?.legacy);
if (parsedResult.success) {
tw.retweetedStatus = parsedResult.tweet;
}

@@ -103,6 +115,7 @@ }

function parseResult(result) {
if (result?.legacy && result.note_tweet?.note_tweet_results?.result?.text) {
result.legacy.full_text = result.note_tweet.note_tweet_results.result.text;
const noteTweetResultText = result?.note_tweet?.note_tweet_results?.result?.text;
if (result?.legacy && noteTweetResultText) {
result.legacy.full_text = noteTweetResultText;
}
const tweetResult = parseLegacyTweet(result?.core?.user_results?.result?.legacy, result?.legacy);
const tweetResult = parseLegacyTweet(result?.core?.user_result?.result?.legacy, result?.legacy);
if (!tweetResult.success) {

@@ -117,4 +130,8 @@ return tweetResult;

}
if (result?.quoted_status_result?.result) {
const quotedTweetResult = parseResult(result.quoted_status_result.result);
const quotedResult = result?.quoted_status_result?.result;
if (quotedResult) {
if (quotedResult.legacy && quotedResult.rest_id) {
quotedResult.legacy.id_str = quotedResult.rest_id;
}
const quotedTweetResult = parseResult(quotedResult);
if (quotedTweetResult.success) {

@@ -129,16 +146,21 @@ tweetResult.tweet.quotedStatus = quotedTweetResult.tweet;

const tweets = [];
const instructions = timeline.data?.user?.result?.timeline_v2?.timeline?.instructions ?? [];
const instructions = timeline.data?.user_result?.result?.timeline_response?.timeline
?.instructions ?? [];
for (const instruction of instructions) {
for (const entry of instruction.entries ?? []) {
if (entry.content?.cursorType === 'Bottom') {
cursor = entry.content.value;
const entries = instruction.entries ?? [];
for (const entry of entries) {
const entryContent = entry.content;
if (!entryContent)
continue;
if (entryContent.cursorType === 'Bottom') {
cursor = entryContent.value;
continue;
}
if (entry.content?.itemContent?.tweet_results?.result?.__typename ===
'Tweet') {
const tweetResult = parseResult(entry.content.itemContent.tweet_results.result);
if (tweetResult.success) {
tweets.push(tweetResult.tweet);
}
const idStr = entry.entryId;
if (!idStr.startsWith('tweet')) {
continue;
}
if (entryContent.content) {
parseAndPush(tweets, entryContent.content, idStr);
}
}

@@ -149,27 +171,34 @@ }

exports.parseTimelineTweetsV2 = parseTimelineTweetsV2;
function parseAndPush(tweets, content, entryId, isConversation = false) {
const result = content.tweetResult?.result;
if (result?.__typename === 'Tweet') {
if (result.legacy) {
const toReplace = isConversation ? 'tweet-' : 'conversation-';
result.legacy.id_str = entryId.replace(toReplace, '');
}
const tweetResult = parseResult(result);
if (tweetResult.success) {
if (isConversation) {
if (content?.tweetDisplayType === 'SelfThread') {
tweetResult.tweet.isSelfThread = true;
}
}
tweets.push(tweetResult.tweet);
}
}
}
function parseThreadedConversation(conversation) {
const tweets = [];
const instructions = conversation.data?.threaded_conversation_with_injections_v2?.instructions ??
[];
const instructions = conversation.data?.timeline_response?.instructions ?? [];
for (const instruction of instructions) {
for (const entry of instruction.entries ?? []) {
if (entry.content?.itemContent?.tweet_results?.result?.__typename ===
'Tweet') {
const tweetResult = parseResult(entry.content.itemContent.tweet_results.result);
if (tweetResult.success) {
if (entry.content.itemContent.tweetDisplayType === 'SelfThread') {
tweetResult.tweet.isSelfThread = true;
}
tweets.push(tweetResult.tweet);
}
const entries = instruction.entries ?? [];
for (const entry of entries) {
const entryContent = entry.content?.content;
if (entryContent) {
parseAndPush(tweets, entryContent, entry.entryId, true);
}
for (const item of entry.content?.items ?? []) {
if (item.item?.itemContent?.tweet_results?.result?.__typename === 'Tweet') {
const tweetResult = parseResult(item.item.itemContent.tweet_results.result);
if (tweetResult.success) {
if (item.item.itemContent.tweetDisplayType === 'SelfThread') {
tweetResult.tweet.isSelfThread = true;
}
tweets.push(tweetResult.tweet);
}
const itemContent = item.item?.content;
if (itemContent) {
parseAndPush(tweets, itemContent, entry.entryId, true);
}

@@ -176,0 +205,0 @@ }

@@ -67,7 +67,39 @@ import { TwitterAuth } from './auth';

}
export type TweetQuery = Partial<Tweet> | ((tweet: Tweet) => boolean | Promise<boolean>);
export declare const features: {
rweb_lists_timeline_redesign_enabled: boolean;
responsive_web_graphql_exclude_directive_enabled: boolean;
verified_phone_label_enabled: boolean;
creator_subscriptions_tweet_preview_api_enabled: boolean;
responsive_web_graphql_timeline_navigation_enabled: boolean;
responsive_web_graphql_skip_user_profile_image_extensions_enabled: boolean;
tweetypie_unmention_optimization_enabled: boolean;
responsive_web_edit_tweet_api_enabled: boolean;
graphql_is_translatable_rweb_tweet_is_translatable_enabled: boolean;
view_counts_everywhere_api_enabled: boolean;
longform_notetweets_consumption_enabled: boolean;
tweet_awards_web_tipping_enabled: boolean;
freedom_of_speech_not_reach_fetch_enabled: boolean;
standardized_nudges_misinfo: boolean;
longform_notetweets_rich_text_read_enabled: boolean;
responsive_web_enhance_cards_enabled: boolean;
subscriptions_verification_info_enabled: boolean;
subscriptions_verification_info_reason_enabled: boolean;
subscriptions_verification_info_verified_since_enabled: boolean;
super_follow_badge_privacy_enabled: boolean;
super_follow_exclusive_tweet_notifications_enabled: boolean;
super_follow_tweet_api_enabled: boolean;
super_follow_user_api_enabled: boolean;
android_graphql_skip_api_media_color_palette: boolean;
creator_subscriptions_subscription_count_enabled: boolean;
blue_business_profile_image_shape_enabled: boolean;
unified_cards_ad_metadata_container_dynamic_card_content_query_enabled: boolean;
};
export declare function fetchTweets(userId: string, maxTweets: number, cursor: string | undefined, auth: TwitterAuth): Promise<QueryTweetsResponse>;
export declare function getTweets(user: string, maxTweets: number, auth: TwitterAuth): AsyncGenerator<Tweet, void>;
export declare function getTweetsByUserId(userId: string, maxTweets: number, auth: TwitterAuth): AsyncGenerator<Tweet, void>;
export declare function getLatestTweet(user: string, includeRetweets: boolean, auth: TwitterAuth): Promise<Tweet | null | void>;
export declare function getTweetWhere(tweets: AsyncIterable<Tweet>, query: TweetQuery): Promise<Tweet | null>;
export declare function getTweetsWhere(tweets: AsyncIterable<Tweet>, query: TweetQuery): Promise<Tweet[]>;
export declare function getLatestTweet(user: string, includeRetweets: boolean, max: number, auth: TwitterAuth): Promise<Tweet | null | void>;
export declare function getTweet(id: string, auth: TwitterAuth): Promise<Tweet | null>;
//# sourceMappingURL=tweets.d.ts.map

@@ -6,3 +6,3 @@ "use strict";

Object.defineProperty(exports, "__esModule", { value: true });
exports.getTweet = exports.getLatestTweet = exports.getTweetsByUserId = exports.getTweets = exports.fetchTweets = void 0;
exports.getTweet = exports.getLatestTweet = exports.getTweetsWhere = exports.getTweetWhere = exports.getTweetsByUserId = exports.getTweets = exports.fetchTweets = exports.features = void 0;
const api_1 = require("./api");

@@ -13,2 +13,9 @@ const profile_1 = require("./profile");

const json_stable_stringify_1 = __importDefault(require("json-stable-stringify"));
exports.features = (0, api_1.addApiFeatures)({
interactive_text_enabled: true,
longform_notetweets_inline_media_enabled: false,
responsive_web_text_conversations_enabled: false,
tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled: false,
vibe_api_enabled: false,
});
async function fetchTweets(userId, maxTweets, cursor, auth) {

@@ -19,16 +26,6 @@ if (maxTweets > 200) {

const variables = {
userId,
includeHasBirdwatchNotes: false,
rest_id: userId,
count: maxTweets,
includePromotedContent: false,
withQuickPromoteEligibilityTweetFields: false,
withVoice: true,
withV2Timeline: true,
};
const features = (0, api_1.addApiFeatures)({
interactive_text_enabled: true,
longform_notetweets_inline_media_enabled: false,
responsive_web_text_conversations_enabled: false,
tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled: false,
vibe_api_enabled: true,
});
if (cursor != null && cursor != '') {

@@ -39,4 +36,4 @@ variables['cursor'] = cursor;

params.set('variables', (0, json_stable_stringify_1.default)(variables));
params.set('features', (0, json_stable_stringify_1.default)(features));
const res = await (0, api_1.requestApi)(`https://twitter.com/i/api/graphql/UGi7tjRPr-d_U3bCPIko5Q/UserTweets?${params.toString()}`, auth);
params.set('features', (0, json_stable_stringify_1.default)(exports.features));
const res = await (0, api_1.requestApi)(`https://api.twitter.com/graphql/8IS8MaO-2EN6GZZZb8jF0g/UserWithProfileTweetsAndRepliesQueryV2?${params.toString()}`, auth);
if (!res.success) {

@@ -65,10 +62,9 @@ throw res.err;

exports.getTweetsByUserId = getTweetsByUserId;
async function getLatestTweet(user, includeRetweets, auth) {
const max = includeRetweets ? 1 : 200;
const timeline = getTweets(user, max, auth);
if (max == 1) {
return (await timeline.next()).value;
}
for await (const tweet of timeline) {
if (!tweet.isRetweet) {
async function getTweetWhere(tweets, query) {
const isCallback = typeof query === 'function';
for await (const tweet of tweets) {
const matches = isCallback
? await query(tweet)
: checkTweetMatches(tweet, query);
if (matches) {
return tweet;

@@ -79,2 +75,28 @@ }

}
exports.getTweetWhere = getTweetWhere;
async function getTweetsWhere(tweets, query) {
const isCallback = typeof query === 'function';
const filtered = [];
for await (const tweet of tweets) {
const matches = isCallback ? query(tweet) : checkTweetMatches(tweet, query);
if (!matches)
continue;
filtered.push(tweet);
}
return filtered;
}
exports.getTweetsWhere = getTweetsWhere;
function checkTweetMatches(tweet, options) {
return Object.keys(options).every((k) => {
const key = k;
return tweet[key] === options[key];
});
}
async function getLatestTweet(user, includeRetweets, max, auth) {
const timeline = getTweets(user, max, auth);
// No point looping if max is 1, just use first entry.
return max === 1
? (await timeline.next()).value
: await getTweetWhere(timeline, { isRetweet: includeRetweets });
}
exports.getLatestTweet = getLatestTweet;

@@ -84,18 +106,8 @@ async function getTweet(id, auth) {

focalTweetId: id,
with_rux_injections: false,
includePromotedContent: true,
withCommunity: true,
withQuickPromoteEligibilityTweetFields: true,
withBirdwatchNotes: true,
withVoice: true,
withV2Timeline: true,
includeHasBirdwatchNotes: false,
};
const features = (0, api_1.addApiFeatures)({
longform_notetweets_inline_media_enabled: true,
tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled: false,
});
const params = new URLSearchParams();
params.set('features', (0, json_stable_stringify_1.default)(features));
params.set('features', (0, json_stable_stringify_1.default)(exports.features));
params.set('variables', (0, json_stable_stringify_1.default)(variables));
const res = await (0, api_1.requestApi)(`https://twitter.com/i/api/graphql/VWFGPVAGkZMGRKGe3GFFnA/TweetDetail?${params.toString()}`, auth);
const res = await (0, api_1.requestApi)(`https://api.twitter.com/graphql/83h5UyHZ9wEKBVzALX8R_g/ConversationTimelineV2?${params.toString()}`, auth);
if (!res.success) {

@@ -105,10 +117,5 @@ throw res.err;

const tweets = (0, timeline_v2_1.parseThreadedConversation)(res.value);
for (const tweet of tweets) {
if (tweet.id === id) {
return tweet;
}
}
return null;
return tweets.find((t) => t.id === id) ?? null;
}
exports.getTweet = getTweet;
//# sourceMappingURL=tweets.js.map
{
"name": "@the-convocation/twitter-scraper",
"version": "0.4.0",
"version": "0.5.0",
"main": "dist/_module.js",

@@ -16,3 +16,3 @@ "repository": "https://github.com/the-convocation/twitter-scraper.git",

"docs:deploy": "yarn docs:generate && gh-pages -d docs",
"format": "prettier --write \\\"src/**/*.ts\\\" \\\"test/**/*.ts\\\"",
"format": "prettier --write src/**/*.ts",
"prepare": "husky install",

@@ -19,0 +19,0 @@ "test": "jest"

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc