import { Cookie } from 'tough-cookie'; import { bearerToken, FetchTransformOptions, requestApi, RequestApiResult, } from './api'; import { TwitterAuth, TwitterAuthOptions, TwitterGuestAuth } from './auth'; import { TwitterUserAuth } from './auth-user'; import { getProfile, getUserIdByScreenName, getScreenNameByUserId, Profile, } from './profile'; import { fetchSearchProfiles, fetchSearchTweets, SearchMode, searchProfiles, searchTweets, } from './search'; import { fetchProfileFollowing, fetchProfileFollowers, getFollowing, getFollowers, followUser, } from './relationships'; import { QueryProfilesResponse, QueryTweetsResponse } from './timeline-v1'; import { getTrends } from './trends'; import { Tweet, getTweetAnonymous, getTweets, getLatestTweet, getTweetWhere, getTweetsWhere, getTweetsByUserId, TweetQuery, getTweet, fetchListTweets, getTweetsAndRepliesByUserId, getTweetsAndReplies, createCreateTweetRequest, PollData, createCreateTweetRequestV2, getTweetV2, getTweetsV2, defaultOptions, createQuoteTweetRequest, likeTweet, retweet, createCreateNoteTweetRequest, createCreateLongTweetRequest, } from './tweets'; import { parseTimelineTweetsV2, TimelineV2 } from './timeline-v2'; import { fetchHomeTimeline } from './timeline-home'; import { fetchFollowingTimeline } from './timeline-following'; import { TTweetv2Expansion, TTweetv2MediaField, TTweetv2PlaceField, TTweetv2PollField, TTweetv2TweetField, TTweetv2UserField, } from 'twitter-api-v2'; import { DirectMessagesResponse, getDirectMessageConversations, sendDirectMessage, SendDirectMessageResponse, } from './messages'; import { fetchAudioSpaceById, fetchAuthenticatePeriscope, fetchBrowseSpaceTopics, fetchCommunitySelectQuery, fetchLiveVideoStreamStatus, fetchLoginTwitterToken } from './spaces'; import {AudioSpace, Community, LiveVideoStreamStatus, LoginTwitterTokenResponse, Subtopic} from './types/spaces'; const twUrl = 'https://twitter.com'; const UserTweetsUrl = 'https://twitter.com/i/api/graphql/E3opETHurmVJflFsUBVuUQ/UserTweets'; export interface ScraperOptions { /** * An alternative fetch function to use instead of the default fetch function. This may be useful * in nonstandard runtime environments, such as edge workers. */ fetch: typeof fetch; /** * Additional options that control how requests and responses are processed. This can be used to * proxy requests through other hosts, for example. */ transform: Partial; } /** * An interface to Twitter's undocumented API. * - Reusing Scraper objects is recommended to minimize the time spent authenticating unnecessarily. */ export class Scraper { private auth!: TwitterAuth; private authTrends!: TwitterAuth; private token: string; /** * Creates a new Scraper object. * - Scrapers maintain their own guest tokens for Twitter's internal API. * - Reusing Scraper objects is recommended to minimize the time spent authenticating unnecessarily. */ constructor(private readonly options?: Partial) { this.token = bearerToken; this.useGuestAuth(); } /** * Initializes auth properties using a guest token. * Used when creating a new instance of this class, and when logging out. * @internal */ private useGuestAuth() { this.auth = new TwitterGuestAuth(this.token, this.getAuthOptions()); this.authTrends = new TwitterGuestAuth(this.token, this.getAuthOptions()); } /** * Fetches a Twitter profile. * @param username The Twitter username of the profile to fetch, without an `@` at the beginning. * @returns The requested {@link Profile}. */ public async getProfile(username: string): Promise { const res = await getProfile(username, this.auth); return this.handleResponse(res); } /** * Fetches the user ID corresponding to the provided screen name. * @param screenName The Twitter screen name of the profile to fetch. * @returns The ID of the corresponding account. */ public async getUserIdByScreenName(screenName: string): Promise { const res = await getUserIdByScreenName(screenName, this.auth); return this.handleResponse(res); } /** * * @param userId The user ID of the profile to fetch. * @returns The screen name of the corresponding account. */ public async getScreenNameByUserId(userId: string): Promise { const response = await getScreenNameByUserId(userId, this.auth); return this.handleResponse(response); } /** * Fetches tweets from Twitter. * @param query The search query. Any Twitter-compatible query format can be used. * @param maxTweets The maximum number of tweets to return. * @param includeReplies Whether or not replies should be included in the response. * @param searchMode The category filter to apply to the search. Defaults to `Top`. * @returns An {@link AsyncGenerator} of tweets matching the provided filters. */ public searchTweets( query: string, maxTweets: number, searchMode: SearchMode = SearchMode.Top, ): AsyncGenerator { return searchTweets(query, maxTweets, searchMode, this.auth); } /** * Fetches profiles from Twitter. * @param query The search query. Any Twitter-compatible query format can be used. * @param maxProfiles The maximum number of profiles to return. * @returns An {@link AsyncGenerator} of tweets matching the provided filter(s). */ public searchProfiles( query: string, maxProfiles: number, ): AsyncGenerator { return searchProfiles(query, maxProfiles, this.auth); } /** * Fetches tweets from Twitter. * @param query The search query. Any Twitter-compatible query format can be used. * @param maxTweets The maximum number of tweets to return. * @param includeReplies Whether or not replies should be included in the response. * @param searchMode The category filter to apply to the search. Defaults to `Top`. * @param cursor The search cursor, which can be passed into further requests for more results. * @returns A page of results, containing a cursor that can be used in further requests. */ public fetchSearchTweets( query: string, maxTweets: number, searchMode: SearchMode, cursor?: string, ): Promise { return fetchSearchTweets(query, maxTweets, searchMode, this.auth, cursor); } /** * Fetches profiles from Twitter. * @param query The search query. Any Twitter-compatible query format can be used. * @param maxProfiles The maximum number of profiles to return. * @param cursor The search cursor, which can be passed into further requests for more results. * @returns A page of results, containing a cursor that can be used in further requests. */ public fetchSearchProfiles( query: string, maxProfiles: number, cursor?: string, ): Promise { return fetchSearchProfiles(query, maxProfiles, this.auth, cursor); } /** * Fetches list tweets from Twitter. * @param listId The list id * @param maxTweets The maximum number of tweets to return. * @param cursor The search cursor, which can be passed into further requests for more results. * @returns A page of results, containing a cursor that can be used in further requests. */ public fetchListTweets( listId: string, maxTweets: number, cursor?: string, ): Promise { return fetchListTweets(listId, maxTweets, cursor, this.auth); } /** * Fetch the profiles a user is following * @param userId The user whose following should be returned * @param maxProfiles The maximum number of profiles to return. * @returns An {@link AsyncGenerator} of following profiles for the provided user. */ public getFollowing( userId: string, maxProfiles: number, ): AsyncGenerator { return getFollowing(userId, maxProfiles, this.auth); } /** * Fetch the profiles that follow a user * @param userId The user whose followers should be returned * @param maxProfiles The maximum number of profiles to return. * @returns An {@link AsyncGenerator} of profiles following the provided user. */ public getFollowers( userId: string, maxProfiles: number, ): AsyncGenerator { return getFollowers(userId, maxProfiles, this.auth); } /** * Fetches following profiles from Twitter. * @param userId The user whose following should be returned * @param maxProfiles The maximum number of profiles to return. * @param cursor The search cursor, which can be passed into further requests for more results. * @returns A page of results, containing a cursor that can be used in further requests. */ public fetchProfileFollowing( userId: string, maxProfiles: number, cursor?: string, ): Promise { return fetchProfileFollowing(userId, maxProfiles, this.auth, cursor); } /** * Fetches profile followers from Twitter. * @param userId The user whose following should be returned * @param maxProfiles The maximum number of profiles to return. * @param cursor The search cursor, which can be passed into further requests for more results. * @returns A page of results, containing a cursor that can be used in further requests. */ public fetchProfileFollowers( userId: string, maxProfiles: number, cursor?: string, ): Promise { return fetchProfileFollowers(userId, maxProfiles, this.auth, cursor); } /** * Fetches the home timeline for the current user. (for you feed) * @param count The number of tweets to fetch. * @param seenTweetIds An array of tweet IDs that have already been seen. * @returns A promise that resolves to the home timeline response. */ public async fetchHomeTimeline( count: number, seenTweetIds: string[], ): Promise { return await fetchHomeTimeline(count, seenTweetIds, this.auth); } /** * Fetches the home timeline for the current user. (following feed) * @param count The number of tweets to fetch. * @param seenTweetIds An array of tweet IDs that have already been seen. * @returns A promise that resolves to the home timeline response. */ public async fetchFollowingTimeline( count: number, seenTweetIds: string[], ): Promise { return await fetchFollowingTimeline(count, seenTweetIds, this.auth); } async getUserTweets( userId: string, maxTweets = 200, cursor?: string, ): Promise<{ tweets: Tweet[]; next?: string }> { if (maxTweets > 200) { maxTweets = 200; } const variables: Record = { userId, count: maxTweets, includePromotedContent: true, withQuickPromoteEligibilityTweetFields: true, withVoice: true, withV2Timeline: true, }; if (cursor) { variables['cursor'] = cursor; } const features = { rweb_tipjar_consumption_enabled: true, responsive_web_graphql_exclude_directive_enabled: true, verified_phone_label_enabled: false, creator_subscriptions_tweet_preview_api_enabled: true, responsive_web_graphql_timeline_navigation_enabled: true, responsive_web_graphql_skip_user_profile_image_extensions_enabled: false, communities_web_enable_tweet_community_results_fetch: true, c9s_tweet_anatomy_moderator_badge_enabled: true, articles_preview_enabled: true, responsive_web_edit_tweet_api_enabled: true, graphql_is_translatable_rweb_tweet_is_translatable_enabled: true, view_counts_everywhere_api_enabled: true, longform_notetweets_consumption_enabled: true, responsive_web_twitter_article_tweet_consumption_enabled: true, tweet_awards_web_tipping_enabled: false, creator_subscriptions_quote_tweet_preview_enabled: false, freedom_of_speech_not_reach_fetch_enabled: true, standardized_nudges_misinfo: true, tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled: true, rweb_video_timestamps_enabled: true, longform_notetweets_rich_text_read_enabled: true, longform_notetweets_inline_media_enabled: true, responsive_web_enhance_cards_enabled: false, }; const fieldToggles = { withArticlePlainText: false, }; const res = await requestApi( `${UserTweetsUrl}?variables=${encodeURIComponent( JSON.stringify(variables), )}&features=${encodeURIComponent( JSON.stringify(features), )}&fieldToggles=${encodeURIComponent(JSON.stringify(fieldToggles))}`, this.auth, ); if (!res.success) { throw res.err; } const timelineV2 = parseTimelineTweetsV2(res.value); return { tweets: timelineV2.tweets, next: timelineV2.next, }; } async *getUserTweetsIterator( userId: string, maxTweets = 200, ): AsyncGenerator { let cursor: string | undefined; let retrievedTweets = 0; while (retrievedTweets < maxTweets) { const response = await this.getUserTweets( userId, maxTweets - retrievedTweets, cursor, ); for (const tweet of response.tweets) { yield tweet; retrievedTweets++; if (retrievedTweets >= maxTweets) { break; } } cursor = response.next; if (!cursor) { break; } } } /** * Fetches the current trends from Twitter. * @returns The current list of trends. */ public getTrends(): Promise { return getTrends(this.authTrends); } /** * Fetches tweets from a Twitter user. * @param user The user whose tweets should be returned. * @param maxTweets The maximum number of tweets to return. Defaults to `200`. * @returns An {@link AsyncGenerator} of tweets from the provided user. */ public getTweets(user: string, maxTweets = 200): AsyncGenerator { return getTweets(user, maxTweets, this.auth); } /** * Fetches tweets from a Twitter user using their ID. * @param userId The user whose tweets should be returned. * @param maxTweets The maximum number of tweets to return. Defaults to `200`. * @returns An {@link AsyncGenerator} of tweets from the provided user. */ public getTweetsByUserId( userId: string, maxTweets = 200, ): AsyncGenerator { return getTweetsByUserId(userId, maxTweets, this.auth); } /** * Send a tweet * @param text The text of the tweet * @param tweetId The id of the tweet to reply to * @param mediaData Optional media data * @returns */ async sendTweet( text: string, replyToTweetId?: string, mediaData?: { data: Buffer; mediaType: string }[], ) { return await createCreateTweetRequest( text, this.auth, replyToTweetId, mediaData, ); } async sendNoteTweet( text: string, replyToTweetId?: string, mediaData?: { data: Buffer; mediaType: string }[], ) { return await createCreateNoteTweetRequest( text, this.auth, replyToTweetId, mediaData, ); } /** * Send a long tweet (Note Tweet) * @param text The text of the tweet * @param tweetId The id of the tweet to reply to * @param mediaData Optional media data * @returns */ async sendLongTweet( text: string, replyToTweetId?: string, mediaData?: { data: Buffer; mediaType: string }[], ) { return await createCreateLongTweetRequest( text, this.auth, replyToTweetId, mediaData, ); } /** * Send a tweet * @param text The text of the tweet * @param tweetId The id of the tweet to reply to * @param options The options for the tweet * @returns */ async sendTweetV2( text: string, replyToTweetId?: string, options?: { poll?: PollData; }, ) { return await createCreateTweetRequestV2( text, this.auth, replyToTweetId, options, ); } /** * Fetches tweets and replies from a Twitter user. * @param user The user whose tweets should be returned. * @param maxTweets The maximum number of tweets to return. Defaults to `200`. * @returns An {@link AsyncGenerator} of tweets from the provided user. */ public getTweetsAndReplies( user: string, maxTweets = 200, ): AsyncGenerator { return getTweetsAndReplies(user, maxTweets, this.auth); } /** * Fetches tweets and replies from a Twitter user using their ID. * @param userId The user whose tweets should be returned. * @param maxTweets The maximum number of tweets to return. Defaults to `200`. * @returns An {@link AsyncGenerator} of tweets from the provided user. */ public getTweetsAndRepliesByUserId( userId: string, maxTweets = 200, ): AsyncGenerator { return getTweetsAndRepliesByUserId(userId, maxTweets, this.auth); } /** * 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}. */ public getTweetWhere( tweets: AsyncIterable, query: TweetQuery, ): Promise { return 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}. */ public getTweetsWhere( tweets: AsyncIterable, query: TweetQuery, ): Promise { return getTweetsWhere(tweets, query); } /** * Fetches the most recent tweet from a Twitter user. * @param user The user whose latest tweet should be returned. * @param includeRetweets Whether or not to include retweets. Defaults to `false`. * @returns The {@link Tweet} object or `null`/`undefined` if it couldn't be fetched. */ public getLatestTweet( user: string, includeRetweets = false, max = 200, ): Promise { return getLatestTweet(user, includeRetweets, max, this.auth); } /** * Fetches a single tweet. * @param id The ID of the tweet to fetch. * @returns The {@link Tweet} object, or `null` if it couldn't be fetched. */ public getTweet(id: string): Promise { if (this.auth instanceof TwitterUserAuth) { return getTweet(id, this.auth); } else { return getTweetAnonymous(id, this.auth); } } /** * Fetches a single tweet by ID using the Twitter API v2. * Allows specifying optional expansions and fields for more detailed data. * * @param {string} id - The ID of the tweet to fetch. * @param {Object} [options] - Optional parameters to customize the tweet data. * @param {string[]} [options.expansions] - Array of expansions to include, e.g., 'attachments.poll_ids'. * @param {string[]} [options.tweetFields] - Array of tweet fields to include, e.g., 'created_at', 'public_metrics'. * @param {string[]} [options.pollFields] - Array of poll fields to include, if the tweet has a poll, e.g., 'options', 'end_datetime'. * @param {string[]} [options.mediaFields] - Array of media fields to include, if the tweet includes media, e.g., 'url', 'preview_image_url'. * @param {string[]} [options.userFields] - Array of user fields to include, if user information is requested, e.g., 'username', 'verified'. * @param {string[]} [options.placeFields] - Array of place fields to include, if the tweet includes location data, e.g., 'full_name', 'country'. * @returns {Promise} - The tweet data, including requested expansions and fields. */ async getTweetV2( id: string, options: { expansions?: TTweetv2Expansion[]; tweetFields?: TTweetv2TweetField[]; pollFields?: TTweetv2PollField[]; mediaFields?: TTweetv2MediaField[]; userFields?: TTweetv2UserField[]; placeFields?: TTweetv2PlaceField[]; } = defaultOptions, ): Promise { return await getTweetV2(id, this.auth, options); } /** * Fetches multiple tweets by IDs using the Twitter API v2. * Allows specifying optional expansions and fields for more detailed data. * * @param {string[]} ids - Array of tweet IDs to fetch. * @param {Object} [options] - Optional parameters to customize the tweet data. * @param {string[]} [options.expansions] - Array of expansions to include, e.g., 'attachments.poll_ids'. * @param {string[]} [options.tweetFields] - Array of tweet fields to include, e.g., 'created_at', 'public_metrics'. * @param {string[]} [options.pollFields] - Array of poll fields to include, if tweets contain polls, e.g., 'options', 'end_datetime'. * @param {string[]} [options.mediaFields] - Array of media fields to include, if tweets contain media, e.g., 'url', 'preview_image_url'. * @param {string[]} [options.userFields] - Array of user fields to include, if user information is requested, e.g., 'username', 'verified'. * @param {string[]} [options.placeFields] - Array of place fields to include, if tweets contain location data, e.g., 'full_name', 'country'. * @returns {Promise } - Array of tweet data, including requested expansions and fields. */ async getTweetsV2( ids: string[], options: { expansions?: TTweetv2Expansion[]; tweetFields?: TTweetv2TweetField[]; pollFields?: TTweetv2PollField[]; mediaFields?: TTweetv2MediaField[]; userFields?: TTweetv2UserField[]; placeFields?: TTweetv2PlaceField[]; } = defaultOptions, ): Promise { return await getTweetsV2(ids, this.auth, options); } /** * Returns if the scraper has a guest token. The token may not be valid. * @returns `true` if the scraper has a guest token; otherwise `false`. */ public hasGuestToken(): boolean { return this.auth.hasToken() || this.authTrends.hasToken(); } /** * Returns if the scraper is logged in as a real user. * @returns `true` if the scraper is logged in with a real user account; otherwise `false`. */ public async isLoggedIn(): Promise { return ( (await this.auth.isLoggedIn()) && (await this.authTrends.isLoggedIn()) ); } /** * Returns the currently logged in user * @returns The currently logged in user */ public async me(): Promise { return this.auth.me(); } /** * Login to Twitter as a real Twitter account. This enables running * searches. * @param username The username of the Twitter account to login with. * @param password The password of the Twitter account to login with. * @param email The email to log in with, if you have email confirmation enabled. * @param twoFactorSecret The secret to generate two factor authentication tokens with, if you have two factor authentication enabled. */ public async login( username: string, password: string, email?: string, twoFactorSecret?: string, appKey?: string, appSecret?: string, accessToken?: string, accessSecret?: string, ): Promise { // Swap in a real authorizer for all requests const userAuth = new TwitterUserAuth(this.token, this.getAuthOptions()); await userAuth.login( username, password, email, twoFactorSecret, appKey, appSecret, accessToken, accessSecret, ); this.auth = userAuth; this.authTrends = userAuth; } /** * Log out of Twitter. */ public async logout(): Promise { await this.auth.logout(); await this.authTrends.logout(); // Swap in guest authorizers for all requests this.useGuestAuth(); } /** * Retrieves all cookies for the current session. * @returns All cookies for the current session. */ public async getCookies(): Promise { return await this.authTrends .cookieJar() .getCookies( typeof document !== 'undefined' ? document.location.toString() : twUrl, ); } /** * Set cookies for the current session. * @param cookies The cookies to set for the current session. */ public async setCookies(cookies: (string | Cookie)[]): Promise { const userAuth = new TwitterUserAuth(this.token, this.getAuthOptions()); for (const cookie of cookies) { await userAuth.cookieJar().setCookie(cookie, twUrl); } this.auth = userAuth; this.authTrends = userAuth; } /** * Clear all cookies for the current session. */ public async clearCookies(): Promise { await this.auth.cookieJar().removeAllCookies(); await this.authTrends.cookieJar().removeAllCookies(); } /** * Sets the optional cookie to be used in requests. * @param _cookie The cookie to be used in requests. * @deprecated This function no longer represents any part of Twitter's auth flow. * @returns This scraper instance. */ // eslint-disable-next-line @typescript-eslint/no-unused-vars public withCookie(_cookie: string): Scraper { console.warn( 'Warning: Scraper#withCookie is deprecated and will be removed in a later version. Use Scraper#login or Scraper#setCookies instead.', ); return this; } /** * Sets the optional CSRF token to be used in requests. * @param _token The CSRF token to be used in requests. * @deprecated This function no longer represents any part of Twitter's auth flow. * @returns This scraper instance. */ // eslint-disable-next-line @typescript-eslint/no-unused-vars public withXCsrfToken(_token: string): Scraper { console.warn( 'Warning: Scraper#withXCsrfToken is deprecated and will be removed in a later version.', ); return this; } /** * Sends a quote tweet. * @param text The text of the tweet. * @param quotedTweetId The ID of the tweet to quote. * @param options Optional parameters, such as media data. * @returns The response from the Twitter API. */ public async sendQuoteTweet( text: string, quotedTweetId: string, options?: { mediaData: { data: Buffer; mediaType: string }[]; }, ) { return await createQuoteTweetRequest( text, quotedTweetId, this.auth, options?.mediaData, ); } /** * Likes a tweet with the given tweet ID. * @param tweetId The ID of the tweet to like. * @returns A promise that resolves when the tweet is liked. */ public async likeTweet(tweetId: string): Promise { // Call the likeTweet function from tweets.ts await likeTweet(tweetId, this.auth); } /** * Retweets a tweet with the given tweet ID. * @param tweetId The ID of the tweet to retweet. * @returns A promise that resolves when the tweet is retweeted. */ public async retweet(tweetId: string): Promise { // Call the retweet function from tweets.ts await retweet(tweetId, this.auth); } /** * Follows a user with the given user ID. * @param userId The user ID of the user to follow. * @returns A promise that resolves when the user is followed. */ public async followUser(userName: string): Promise { // Call the followUser function from relationships.ts await followUser(userName, this.auth); } /** * Fetches direct message conversations * @param count Number of conversations to fetch (default: 50) * @param cursor Pagination cursor for fetching more conversations * @returns Array of DM conversations and other details */ public async getDirectMessageConversations( userId: string, cursor?: string, ): Promise { return await getDirectMessageConversations(userId, this.auth, cursor); } /** * Sends a direct message to a user. * @param conversationId The ID of the conversation to send the message to. * @param text The text of the message to send. * @returns The response from the Twitter API. */ public async sendDirectMessage( conversationId: string, text: string, ): Promise { return await sendDirectMessage(this.auth, conversationId, text); } private getAuthOptions(): Partial { return { fetch: this.options?.fetch, transform: this.options?.transform, }; } private handleResponse(res: RequestApiResult): T { if (!res.success) { throw res.err; } return res.value; } /** * Retrieves the details of an Audio Space by its ID. * @param id The ID of the Audio Space. * @returns The details of the Audio Space. */ public async getAudioSpaceById(id: string): Promise { const variables = { id, isMetatagsQuery: false, withReplays: true, withListeners: true, }; return await fetchAudioSpaceById(variables, this.auth); } /** * Retrieves available space topics. * @returns An array of space topics. */ public async browseSpaceTopics(): Promise { return await fetchBrowseSpaceTopics(this.auth); } /** * Retrieves available communities. * @returns An array of communities. */ public async communitySelectQuery(): Promise { return await fetchCommunitySelectQuery(this.auth); } /** * Retrieves the status of an Audio Space stream by its media key. * @param mediaKey The media key of the Audio Space. * @returns The status of the Audio Space stream. */ public async getAudioSpaceStreamStatus( mediaKey: string, ): Promise { return await fetchLiveVideoStreamStatus(mediaKey, this.auth); } /** * Retrieves the status of an Audio Space by its ID. * This method internally fetches the Audio Space to obtain the media key, * then retrieves the stream status using the media key. * @param audioSpaceId The ID of the Audio Space. * @returns The status of the Audio Space stream. */ public async getAudioSpaceStatus( audioSpaceId: string, ): Promise { const audioSpace = await this.getAudioSpaceById(audioSpaceId); const mediaKey = audioSpace.metadata.media_key; if (!mediaKey) { throw new Error('Media Key not found in Audio Space metadata.'); } return await this.getAudioSpaceStreamStatus(mediaKey); } /** * Authenticates Periscope to obtain a token. * @returns The Periscope authentication token. */ public async authenticatePeriscope(): Promise { return await fetchAuthenticatePeriscope(this.auth); } /** * Logs in to Twitter via Proxsee using the Periscope JWT. * @param jwt The JWT obtained from AuthenticatePeriscope. * @returns The response containing the cookie and user information. */ public async loginTwitterToken( jwt: string, ): Promise { return await fetchLoginTwitterToken(jwt, this.auth); } /** * Orchestrates the flow: get token -> login -> return Periscope cookie */ public async getPeriscopeCookie(): Promise { const periscopeToken = await this.authenticatePeriscope(); const loginResponse = await this.loginTwitterToken(periscopeToken); return loginResponse.cookie; } }