Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import { extractAddressFromMessage } from '../../utils/text-validator';
- import { SocialPlatformRecipient } from '../../types/scraper';
- import { Scraper } from 'agent-twitter-client';
- import { env } from '@/lib/config/env';
- import { CookieJar, Cookie } from 'tough-cookie';
- export class TwitterService {
- private scraper: Scraper;
- private cookieJar: CookieJar;
- private static instance: TwitterService;
- private initializationPromise: Promise<void>;
- private initialized = false;
- private constructor() {
- console.log('[TwitterService] Creating new instance');
- this.scraper = new Scraper();
- this.cookieJar = new CookieJar();
- this.initializationPromise = this.initialize();
- }
- public static getInstance(): TwitterService {
- if (!TwitterService.instance) {
- console.log('[TwitterService] Initializing singleton instance');
- TwitterService.instance = new TwitterService();
- }
- return TwitterService.instance;
- }
- private async initialize() {
- if (this.initialized) {
- console.log('[TwitterService] Already initialized, skipping');
- return;
- }
- try {
- console.log('[TwitterService] Starting fresh login...');
- await this.scraper.login(env.TWITTER_USERNAME, env.TWITTER_PASSWORD);
- console.log('[TwitterService] Login successful, fetching cookies');
- const cookies = await this.scraper.getCookies();
- console.log(
- `[TwitterService] Got ${cookies.length} cookies from Twitter`,
- );
- let savedCookies = 0;
- for (const cookie of cookies) {
- if (!cookie) continue;
- try {
- const cookieString = `${cookie.key}=${cookie.value}; Domain=${
- cookie.domain
- }; Path=${cookie.path}${cookie.secure ? '; Secure' : ''}${
- cookie.httpOnly ? '; HttpOnly' : ''
- }`;
- const parsedCookie = Cookie.parse(cookieString);
- if (parsedCookie) {
- await this.cookieJar.setCookie(parsedCookie, 'https://twitter.com');
- savedCookies++;
- }
- } catch (error) {
- console.error('[TwitterService] Error saving cookie:', error);
- }
- }
- console.log(
- `[TwitterService] Successfully saved ${savedCookies} cookies to jar`,
- );
- this.initialized = true;
- console.log('[TwitterService] Initialization completed successfully');
- } catch (error) {
- console.error('[TwitterService] Initialization failed:', error);
- throw error;
- }
- }
- private async ensureInitialized() {
- if (!this.initialized) {
- console.log('[TwitterService] Waiting for initialization to complete...');
- await this.initializationPromise;
- console.log('[TwitterService] Initialization finished');
- } else {
- console.log('[TwitterService] Already initialized');
- }
- }
- private async loadCookiesToScraper() {
- console.log('[TwitterService] Loading cookies from jar to scraper');
- const cookies = await this.cookieJar.getCookies('https://twitter.com');
- console.log(`[TwitterService] Found ${cookies.length} cookies in jar`);
- const cookieStrings = cookies.map((cookie) => cookie.toString());
- await this.scraper.setCookies(cookieStrings);
- console.log('[TwitterService] Cookies loaded into scraper');
- // Log important cookies presence
- const hasAuthToken = cookies.some((cookie) => cookie.key === 'auth_token');
- const hasCSRF = cookies.some((cookie) => cookie.key === 'ct0');
- console.log('[TwitterService] Cookie check:', {
- hasAuthToken,
- hasCSRF,
- totalCookies: cookies.length,
- });
- }
- async extractRecipient(url: string): Promise<SocialPlatformRecipient> {
- console.log(`[TwitterService] Extracting recipient from URL: ${url}`);
- try {
- await this.ensureInitialized();
- await this.loadCookiesToScraper();
- const tweetId = this.extractTweetId(url);
- console.log(`[TwitterService] Fetching tweet ID: ${tweetId}`);
- const data = await this.scraper.getTweet(tweetId);
- console.log('[TwitterService] Successfully fetched tweet data');
- if (!data?.text || !data.username) {
- throw new Error('Tweet data not found');
- }
- try {
- const address = extractAddressFromMessage(data.text, true);
- console.log(`[TwitterService] Extracted address: ${address}`);
- return {
- message: data.text,
- platform_user_id: data.username,
- platform: 'twitter',
- address,
- };
- } catch (error) {
- throw new Error(
- error instanceof Error ? error.message : 'Failed to extract address',
- );
- }
- } catch (error) {
- if (
- error instanceof Error &&
- (error.message.includes('csrf') ||
- error.message.includes('unauthorized'))
- ) {
- console.log(
- '[TwitterService] Auth error detected, performing reinitialization',
- );
- this.initialized = false;
- this.initializationPromise = this.initialize();
- await this.initializationPromise;
- return this.extractRecipient(url);
- }
- console.error('[TwitterService] Error in extractRecipient:', error);
- throw error instanceof Error
- ? error
- : new Error('Failed to extract recipient');
- }
- }
- private extractTweetId(url: string): string {
- const match = url.match(/status\/(\d+)/);
- if (!match) {
- throw new Error('Invalid tweet URL');
- }
- return match[1];
- }
- async logout(): Promise<void> {
- try {
- console.log('[TwitterService] Starting logout process');
- await this.ensureInitialized();
- await this.scraper.logout();
- await this.cookieJar.removeAllCookies();
- this.initialized = false;
- console.log(
- '[TwitterService] Logged out successfully, all cookies cleared',
- );
- } catch (error) {
- console.error('[TwitterService] Error during logout:', error);
- throw error;
- }
- }
- isTwitterUrl(url: string): boolean {
- return url.includes('twitter.com') || url.includes('x.com');
- }
- }
- // Export the singleton instance
- export const twitterService = TwitterService.getInstance();
Advertisement
Add Comment
Please, Sign In to add comment