0x0x230x

Untitled

Jan 29th, 2025
35
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 4.94 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. this.scraper = new Scraper();
  16. this.cookieJar = new CookieJar();
  17. this.initializationPromise = this.initialize();
  18. }
  19.  
  20. public static getInstance(): TwitterService {
  21. if (!TwitterService.instance) {
  22. TwitterService.instance = new TwitterService();
  23. }
  24. return TwitterService.instance;
  25. }
  26.  
  27. private async initialize() {
  28. if (this.initialized) return;
  29.  
  30. try {
  31. console.log('🔑 [Twitter Service] Signing in...');
  32. await this.scraper.login(env.TWITTER_USERNAME, env.TWITTER_PASSWORD);
  33.  
  34. const cookies = await this.scraper.getCookies();
  35. for (const cookie of cookies) {
  36. if (!cookie) continue;
  37.  
  38. try {
  39. const cookieString = `${cookie.key}=${cookie.value}; Domain=${
  40. cookie.domain
  41. }; Path=${cookie.path}${cookie.secure ? '; Secure' : ''}${
  42. cookie.httpOnly ? '; HttpOnly' : ''
  43. }`;
  44. const parsedCookie = Cookie.parse(cookieString);
  45. if (parsedCookie) {
  46. await this.cookieJar.setCookie(parsedCookie, 'https://twitter.com');
  47. }
  48. } catch (error) {
  49. console.error('Twitter: Failed to save cookie:', error);
  50. }
  51. }
  52.  
  53. this.initialized = true;
  54. console.log('✅ [Twitter Service] Successfully signed in and saved cookies');
  55. } catch (error) {
  56. console.error('❌ [Twitter Service] Authentication failed:', error);
  57. throw error;
  58. }
  59. }
  60.  
  61. private async ensureInitialized() {
  62. if (!this.initialized) {
  63. await this.initializationPromise;
  64. }
  65. }
  66.  
  67. private async loadCookiesToScraper() {
  68. const cookies = await this.cookieJar.getCookies('https://twitter.com');
  69. const hasAuthToken = cookies.some((cookie) => cookie.key === 'auth_token');
  70. const hasCSRF = cookies.some((cookie) => cookie.key === 'ct0');
  71.  
  72. if (!hasAuthToken || !hasCSRF) {
  73. console.log('🔄 [Twitter Service] Missing required cookies, reinitializing...');
  74. this.initialized = false;
  75. this.initializationPromise = this.initialize();
  76. await this.initializationPromise;
  77. return;
  78. }
  79.  
  80. const cookieStrings = cookies.map((cookie) => cookie.toString());
  81. await this.scraper.setCookies(cookieStrings);
  82. console.log('🍪 [Twitter Service] Using existing cookies');
  83. }
  84.  
  85. async extractRecipient(url: string): Promise<SocialPlatformRecipient> {
  86. try {
  87. await this.ensureInitialized();
  88. await this.loadCookiesToScraper();
  89.  
  90. const tweetId = this.extractTweetId(url);
  91. const data = await this.scraper.getTweet(tweetId);
  92.  
  93. if (!data?.text || !data.username) {
  94. throw new Error('Tweet data not found');
  95. }
  96.  
  97. try {
  98. const address = extractAddressFromMessage(data.text, true);
  99. return {
  100. message: data.text,
  101. platform_user_id: data.username,
  102. platform: 'twitter',
  103. address,
  104. };
  105. } catch (error) {
  106. throw new Error(
  107. error instanceof Error ? error.message : 'Failed to extract address',
  108. );
  109. }
  110. } catch (error) {
  111. if (
  112. error instanceof Error &&
  113. (error.message.includes('csrf') ||
  114. error.message.includes('unauthorized'))
  115. ) {
  116. console.log('🔄 [Twitter Service] Auth error, reinitializing...');
  117. this.initialized = false;
  118. this.initializationPromise = this.initialize();
  119. await this.initializationPromise;
  120. return this.extractRecipient(url);
  121. }
  122.  
  123. console.error('❌ [Twitter Service] Failed to extract data:', error);
  124. throw error instanceof Error
  125. ? error
  126. : new Error('Failed to extract recipient');
  127. }
  128. }
  129.  
  130. private extractTweetId(url: string): string {
  131. const match = url.match(/status\/(\d+)/);
  132. if (!match) {
  133. throw new Error('Invalid tweet URL');
  134. }
  135. return match[1];
  136. }
  137.  
  138. async logout(): Promise<void> {
  139. try {
  140. await this.ensureInitialized();
  141. await this.scraper.logout();
  142. await this.cookieJar.removeAllCookies();
  143. this.initialized = false;
  144. console.log('👋 [Twitter Service] Logged out');
  145. } catch (error) {
  146. console.error('❌ [Twitter Service] Logout failed:', error);
  147. throw error;
  148. }
  149. }
  150.  
  151. isTwitterUrl(url: string): boolean {
  152. return url.includes('twitter.com') || url.includes('x.com');
  153. }
  154. }
  155.  
  156. export const twitterService = TwitterService.getInstance();
  157.  
Advertisement
Add Comment
Please, Sign In to add comment