0x0x230x

Untitled

Jan 29th, 2025
28
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 6.21 KB | None | 0 0
  1. import { extractAddressFromMessage } from '../../utils/text-validator';
  2. import { SocialPlatformRecipient } from '../../types/scraper';
  3. import { Scraper } from 'agent-twitter-client';
  4. import { env } from '@/lib/config/env';
  5. import { CookieJar, Cookie } from 'tough-cookie';
  6.  
  7. export class TwitterService {
  8. private scraper: Scraper;
  9. private cookieJar: CookieJar;
  10. private static instance: TwitterService;
  11. private initializationPromise: Promise<void>;
  12. private initialized = false;
  13.  
  14. private constructor() {
  15. console.log('[TwitterService] Creating new instance');
  16. this.scraper = new Scraper();
  17. this.cookieJar = new CookieJar();
  18. this.initializationPromise = this.initialize();
  19. }
  20.  
  21. public static getInstance(): TwitterService {
  22. if (!TwitterService.instance) {
  23. console.log('[TwitterService] Initializing singleton instance');
  24. TwitterService.instance = new TwitterService();
  25. }
  26. return TwitterService.instance;
  27. }
  28.  
  29. private async initialize() {
  30. if (this.initialized) {
  31. console.log('[TwitterService] Already initialized, skipping');
  32. return;
  33. }
  34.  
  35. try {
  36. console.log('[TwitterService] Starting fresh login...');
  37. await this.scraper.login(env.TWITTER_USERNAME, env.TWITTER_PASSWORD);
  38. console.log('[TwitterService] Login successful, fetching cookies');
  39.  
  40. const cookies = await this.scraper.getCookies();
  41. console.log(
  42. `[TwitterService] Got ${cookies.length} cookies from Twitter`,
  43. );
  44.  
  45. let savedCookies = 0;
  46. for (const cookie of cookies) {
  47. if (!cookie) continue;
  48.  
  49. try {
  50. const cookieString = `${cookie.key}=${cookie.value}; Domain=${
  51. cookie.domain
  52. }; Path=${cookie.path}${cookie.secure ? '; Secure' : ''}${
  53. cookie.httpOnly ? '; HttpOnly' : ''
  54. }`;
  55. const parsedCookie = Cookie.parse(cookieString);
  56. if (parsedCookie) {
  57. await this.cookieJar.setCookie(parsedCookie, 'https://twitter.com');
  58. savedCookies++;
  59. }
  60. } catch (error) {
  61. console.error('[TwitterService] Error saving cookie:', error);
  62. }
  63. }
  64. console.log(
  65. `[TwitterService] Successfully saved ${savedCookies} cookies to jar`,
  66. );
  67.  
  68. this.initialized = true;
  69. console.log('[TwitterService] Initialization completed successfully');
  70. } catch (error) {
  71. console.error('[TwitterService] Initialization failed:', error);
  72. throw error;
  73. }
  74. }
  75.  
  76. private async ensureInitialized() {
  77. if (!this.initialized) {
  78. console.log('[TwitterService] Waiting for initialization to complete...');
  79. await this.initializationPromise;
  80. console.log('[TwitterService] Initialization finished');
  81. } else {
  82. console.log('[TwitterService] Already initialized');
  83. }
  84. }
  85.  
  86. private async loadCookiesToScraper() {
  87. console.log('[TwitterService] Loading cookies from jar to scraper');
  88. const cookies = await this.cookieJar.getCookies('https://twitter.com');
  89. console.log(`[TwitterService] Found ${cookies.length} cookies in jar`);
  90.  
  91. const cookieStrings = cookies.map((cookie) => cookie.toString());
  92. await this.scraper.setCookies(cookieStrings);
  93. console.log('[TwitterService] Cookies loaded into scraper');
  94.  
  95. // Log important cookies presence
  96. const hasAuthToken = cookies.some((cookie) => cookie.key === 'auth_token');
  97. const hasCSRF = cookies.some((cookie) => cookie.key === 'ct0');
  98. console.log('[TwitterService] Cookie check:', {
  99. hasAuthToken,
  100. hasCSRF,
  101. totalCookies: cookies.length,
  102. });
  103. }
  104.  
  105. async extractRecipient(url: string): Promise<SocialPlatformRecipient> {
  106. console.log(`[TwitterService] Extracting recipient from URL: ${url}`);
  107.  
  108. try {
  109. await this.ensureInitialized();
  110. await this.loadCookiesToScraper();
  111.  
  112. const tweetId = this.extractTweetId(url);
  113. console.log(`[TwitterService] Fetching tweet ID: ${tweetId}`);
  114.  
  115. const data = await this.scraper.getTweet(tweetId);
  116. console.log('[TwitterService] Successfully fetched tweet data');
  117.  
  118. if (!data?.text || !data.username) {
  119. throw new Error('Tweet data not found');
  120. }
  121.  
  122. try {
  123. const address = extractAddressFromMessage(data.text, true);
  124. console.log(`[TwitterService] Extracted address: ${address}`);
  125. return {
  126. message: data.text,
  127. platform_user_id: data.username,
  128. platform: 'twitter',
  129. address,
  130. };
  131. } catch (error) {
  132. throw new Error(
  133. error instanceof Error ? error.message : 'Failed to extract address',
  134. );
  135. }
  136. } catch (error) {
  137. if (
  138. error instanceof Error &&
  139. (error.message.includes('csrf') ||
  140. error.message.includes('unauthorized'))
  141. ) {
  142. console.log(
  143. '[TwitterService] Auth error detected, performing reinitialization',
  144. );
  145. this.initialized = false;
  146. this.initializationPromise = this.initialize();
  147. await this.initializationPromise;
  148. return this.extractRecipient(url);
  149. }
  150.  
  151. console.error('[TwitterService] Error in extractRecipient:', error);
  152. throw error instanceof Error
  153. ? error
  154. : new Error('Failed to extract recipient');
  155. }
  156. }
  157.  
  158. private extractTweetId(url: string): string {
  159. const match = url.match(/status\/(\d+)/);
  160. if (!match) {
  161. throw new Error('Invalid tweet URL');
  162. }
  163. return match[1];
  164. }
  165.  
  166. async logout(): Promise<void> {
  167. try {
  168. console.log('[TwitterService] Starting logout process');
  169. await this.ensureInitialized();
  170. await this.scraper.logout();
  171. await this.cookieJar.removeAllCookies();
  172. this.initialized = false;
  173. console.log(
  174. '[TwitterService] Logged out successfully, all cookies cleared',
  175. );
  176. } catch (error) {
  177. console.error('[TwitterService] Error during logout:', error);
  178. throw error;
  179. }
  180. }
  181.  
  182. isTwitterUrl(url: string): boolean {
  183. return url.includes('twitter.com') || url.includes('x.com');
  184. }
  185. }
  186.  
  187. // Export the singleton instance
  188. export const twitterService = TwitterService.getInstance();
  189.  
Advertisement
Add Comment
Please, Sign In to add comment