- Add tailwind.config.ts with proper content paths and theme config - Add postcss.config.js for Tailwind and autoprefixer processing - Downgrade tailwindcss to v3.4.17 and add missing PostCSS dependencies - Update Dockerfile to clarify build process - Fix UI styling issues in Docker environment
240 lines
8.5 KiB
TypeScript
240 lines
8.5 KiB
TypeScript
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<string[]> {
|
|
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<string | null> {
|
|
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<string[]> {
|
|
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<boolean> {
|
|
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<string[]> {
|
|
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<void> {
|
|
await tradingViewAutomation.close()
|
|
}
|
|
}
|
|
|
|
export const enhancedScreenshotService = new EnhancedScreenshotService()
|