import { tradingViewAutomation, TradingViewCredentials, NavigationOptions } from './tradingview-automation' import fs from 'fs/promises' import path from 'path' export interface ScreenshotConfig { symbol: string timeframe: string layouts?: string[] // Multiple chart layouts if needed credentials?: TradingViewCredentials // Optional if using .env } export class EnhancedScreenshotService { private static readonly OPERATION_TIMEOUT = 120000 // 2 minutes timeout async captureWithLogin(config: ScreenshotConfig): Promise { const screenshotFiles: string[] = [] return new Promise(async (resolve, reject) => { // Set overall timeout for the operation const timeoutId = setTimeout(() => { reject(new Error('Screenshot capture operation timed out after 2 minutes')) }, EnhancedScreenshotService.OPERATION_TIMEOUT) try { // Ensure screenshots directory exists const screenshotsDir = path.join(process.cwd(), 'screenshots') await fs.mkdir(screenshotsDir, { recursive: true }) console.log('Initializing TradingView automation for Docker container...') // Ensure browser is healthy before operations await tradingViewAutomation.ensureBrowserReady() // Initialize automation with Docker-optimized settings await tradingViewAutomation.init() // Check if already logged in using session persistence const alreadyLoggedIn = await tradingViewAutomation.isLoggedIn() if (!alreadyLoggedIn) { console.log('No active session found...') // Try to use enhanced session persistence first to avoid captcha const sessionTest = await tradingViewAutomation.testSessionPersistence() console.log('📊 Current session info:', sessionTest) if (sessionTest.isValid && sessionTest.cookiesCount > 0) { console.log('✅ Saved session data found') console.log(`🍪 Cookies: ${sessionTest.cookiesCount}`) console.log(`💾 Storage: ${sessionTest.hasStorage ? 'Yes' : 'No'}`) } else { console.log('⚠️ Session data exists but appears to be expired') } // Always try smart login which handles session validation and human-like behavior console.log('⚠️ No valid session - manual login may be required') console.log('💡 Using smart login to handle captcha scenario...') // Use smart login which prioritizes session persistence and anti-detection const loginSuccess = await tradingViewAutomation.smartLogin(config.credentials) if (!loginSuccess) { throw new Error('Smart login failed - manual intervention may be required') } } else { console.log('✅ Already logged in using saved session') } // Navigate to chart const navOptions: NavigationOptions = { symbol: config.symbol, timeframe: config.timeframe, waitForChart: true } console.log(`Navigating to ${config.symbol} chart...`) const navSuccess = await tradingViewAutomation.navigateToChart(navOptions) if (!navSuccess) { throw new Error('Chart navigation failed') } // Wait for chart data to fully load const chartLoaded = await tradingViewAutomation.waitForChartData() if (!chartLoaded) { console.warn('Chart data may not be fully loaded, proceeding with screenshot anyway') } // Take screenshot const timestamp = Date.now() const filename = `${config.symbol}_${config.timeframe}_${timestamp}_ai.png` console.log(`Taking screenshot: ${filename}`) const screenshotFile = await tradingViewAutomation.takeScreenshot(filename) screenshotFiles.push(screenshotFile) // If multiple layouts are needed, handle them here if (config.layouts && config.layouts.length > 0) { for (const layout of config.layouts) { // Logic to switch to different layouts would go here // This depends on your specific TradingView setup const layoutFilename = `${config.symbol}_${config.timeframe}_${layout}_${timestamp}_ai.png` const layoutScreenshot = await tradingViewAutomation.takeScreenshot(layoutFilename) screenshotFiles.push(layoutScreenshot) } } console.log(`Successfully captured ${screenshotFiles.length} screenshot(s)`) clearTimeout(timeoutId) resolve(screenshotFiles) } catch (error) { console.error('Enhanced screenshot capture failed:', error) clearTimeout(timeoutId) reject(error) } // Note: Don't close browser here - keep it alive for subsequent operations }) } async captureQuick(symbol: string, timeframe: string, credentials: TradingViewCredentials): Promise { try { const config: ScreenshotConfig = { symbol, timeframe, credentials } const screenshots = await this.captureWithLogin(config) return screenshots.length > 0 ? screenshots[0] : null } catch (error) { console.error('Quick screenshot capture failed:', error) return null } } async captureMultipleTimeframes( symbol: string, timeframes: string[], credentials: TradingViewCredentials ): Promise { const allScreenshots: string[] = [] for (const timeframe of timeframes) { try { console.log(`Capturing ${symbol} ${timeframe} chart...`) const screenshot = await this.captureQuick(symbol, timeframe, credentials) if (screenshot) { allScreenshots.push(screenshot) } } catch (error) { console.error(`Failed to capture ${symbol} ${timeframe}:`, error) } } return allScreenshots } // Method to check if we can access TradingView in Docker environment async healthCheck(): Promise<{ status: 'ok' | 'error'; message: string }> { try { console.log('Performing TradingView health check in Docker...') await tradingViewAutomation.init() // Navigate to TradingView homepage to check accessibility const page = (tradingViewAutomation as any).page if (!page) { return { status: 'error', message: 'Failed to initialize browser page in Docker' } } await page.goto('https://www.tradingview.com/', { waitUntil: 'networkidle', timeout: 30000 }) const currentUrl = await tradingViewAutomation.getCurrentUrl() if (currentUrl.includes('tradingview.com')) { return { status: 'ok', message: 'TradingView is accessible from Docker container' } } else { return { status: 'error', message: 'TradingView is not accessible from Docker container' } } } catch (error) { return { status: 'error', message: `TradingView health check failed: ${error}` } } finally { await tradingViewAutomation.close() } } // Method to verify credentials in Docker environment async verifyCredentials(credentials?: TradingViewCredentials): Promise { try { console.log('Verifying TradingView credentials in Docker...') await tradingViewAutomation.init() const loginSuccess = await tradingViewAutomation.login(credentials) return loginSuccess } catch (error) { console.error('Credential verification error in Docker:', error) return false } finally { await tradingViewAutomation.close() } } // Backward compatibility method - matches old tradingViewCapture.capture() API async capture(symbol: string, filename: string, layouts?: string[], timeframe?: string): Promise { try { console.log(`Starting Playwright-based capture for ${symbol} in Docker container`) const config: ScreenshotConfig = { symbol: symbol, timeframe: timeframe || '5', // Default to 5-minute timeframe layouts: layouts || [] } const screenshots = await this.captureWithLogin(config) // Return full paths to screenshots for backward compatibility const screenshotsDir = path.join(process.cwd(), 'screenshots') return screenshots.map(filename => path.join(screenshotsDir, filename)) } catch (error) { console.error('Backward compatible capture failed:', error) throw error } } /** * Cleanup browser resources (can be called when shutting down the application) */ async cleanup(): Promise { await tradingViewAutomation.close() } } export const enhancedScreenshotService = new EnhancedScreenshotService()