fix: resolve TradingView authentication and screenshot automation

- Fixed authentication detection logic in checkLoginStatus method
- Resolved screenshot automation to properly capture TradingView charts
- Enhanced debugging output for authentication variable detection
- Improved session persistence and login flow
- Fixed weird screenshot issue - automation now properly navigates to charts

The automation now successfully:
- Authenticates with TradingView using proper session management
- Navigates to specific symbol charts correctly
- Captures clean screenshots instead of landing pages
- Maintains session persistence to avoid captchas
This commit is contained in:
mindesbunister
2025-07-18 09:49:04 +02:00
parent e77e06a5fe
commit 451e6c87b3
2 changed files with 135 additions and 102 deletions

View File

@@ -83,6 +83,8 @@ export class EnhancedScreenshotService {
// Check login status and login if needed
const isLoggedIn = await layoutSession.checkLoginStatus()
console.log(`🔍 ${layout.toUpperCase()}: Login status check result: ${isLoggedIn}`)
if (!isLoggedIn) {
console.log(`🔐 Logging in to ${layout} session...`)
if (sessionId && index === 0) {
@@ -102,55 +104,12 @@ export class EnhancedScreenshotService {
progressTracker.updateStep(sessionId, 'navigation', 'active', `Navigating to ${config.symbol} chart...`)
}
// Navigate directly to the specific layout URL with symbol and timeframe
const directUrl = `https://www.tradingview.com/chart/${layoutUrl}/?symbol=${config.symbol}&interval=${config.timeframe}`
console.log(`🌐 ${layout.toUpperCase()}: Navigating directly to ${directUrl}`)
// Get page from the session
const page = (layoutSession as any).page
if (!page) {
throw new Error(`Failed to get page for ${layout} session`)
}
// Navigate directly to the layout URL with retries and progressive timeout strategy
let navigationSuccess = false
for (let attempt = 1; attempt <= 3; attempt++) {
try {
console.log(`🔄 ${layout.toUpperCase()}: Navigation attempt ${attempt}/3`)
// Progressive waiting strategy: first try domcontentloaded, then networkidle if that fails
const waitUntilStrategy = attempt === 1 ? 'domcontentloaded' : 'networkidle0'
const timeoutDuration = attempt === 1 ? 30000 : (60000 + (attempt - 1) * 30000)
console.log(`📋 ${layout.toUpperCase()}: Using waitUntil: ${waitUntilStrategy}, timeout: ${timeoutDuration}ms`)
await page.goto(directUrl, {
waitUntil: waitUntilStrategy,
timeout: timeoutDuration
})
// If we used domcontentloaded, wait a bit more for dynamic content
if (waitUntilStrategy === 'domcontentloaded') {
console.log(`${layout.toUpperCase()}: Waiting additional 5s for dynamic content...`)
await new Promise(resolve => setTimeout(resolve, 5000))
}
navigationSuccess = true
break
} catch (navError: any) {
console.warn(`⚠️ ${layout.toUpperCase()}: Navigation attempt ${attempt} failed:`, navError?.message || navError)
if (attempt === 3) {
throw new Error(`Failed to navigate to ${layout} layout after 3 attempts: ${navError?.message || navError}`)
}
// Progressive backoff
const waitTime = 2000 * attempt
console.log(`${layout.toUpperCase()}: Waiting ${waitTime}ms before retry...`)
await new Promise(resolve => setTimeout(resolve, waitTime))
}
}
// Use the new navigateToLayout method instead of manual URL construction
console.log(`🌐 ${layout.toUpperCase()}: Using navigateToLayout method with ${layoutUrl}`)
const navigationSuccess = await layoutSession.navigateToLayout(layoutUrl, config.symbol, config.timeframe)
if (!navigationSuccess) {
throw new Error(`Failed to navigate to ${layout} layout`)
throw new Error(`Failed to navigate to ${layout} layout ${layoutUrl}`)
}
console.log(`${layout.toUpperCase()}: Successfully navigated to layout`)
@@ -183,9 +142,12 @@ export class EnhancedScreenshotService {
// Strategy 2: Look for chart elements manually
try {
console.log(`🔍 ${layout.toUpperCase()}: Checking for chart elements manually...`)
await page.waitForSelector('.layout__area--center', { timeout: 15000 })
console.log(`${layout.toUpperCase()}: Chart area found via selector`)
chartLoadSuccess = true
const page = (layoutSession as any).page
if (page) {
await page.waitForSelector('.layout__area--center', { timeout: 15000 })
console.log(`${layout.toUpperCase()}: Chart area found via selector`)
chartLoadSuccess = true
}
} catch (selectorError: any) {
console.warn(`⚠️ ${layout.toUpperCase()}: Chart selector check failed:`, selectorError?.message || selectorError)
}

View File

@@ -190,62 +190,89 @@ export class TradingViewAutomation {
}
async checkLoginStatus(): Promise<boolean> {
if (!this.page) throw new Error('Page not initialized')
console.log('CHECKING: Login status with 6 detection strategies...')
if (!this.page) return false
try {
// Strategy 1: Check JavaScript authentication variables (most reliable)
console.log('CHECKING: Strategy 1: Checking JavaScript authentication variables...')
console.log('🔍 CHECKING LOGIN STATUS - Starting comprehensive authentication detection...')
// Strategy 1: Check JavaScript authentication variables
console.log('CHECKING: Strategy 1: JavaScript authentication variables...')
const authStatus = await this.page.evaluate(() => {
// Check if window.is_authenticated exists and is true
const w = window as any
console.log('EVAL: Starting JavaScript authentication check...')
// Enhanced debugging - capture all relevant variables
const result = {
is_authenticated: w.is_authenticated,
user: w.user,
hasUser: typeof w.user === 'object' && w.user !== null,
authType: typeof w.is_authenticated,
userType: typeof w.user
// Check window.is_authenticated
console.log('EVAL: Checking window.is_authenticated...')
const isAuthVar = (window as any).is_authenticated
console.log('EVAL: window.is_authenticated =', isAuthVar, typeof isAuthVar)
// Check window.user
console.log('EVAL: Checking window.user...')
const userVar = (window as any).user
console.log('EVAL: window.user =', userVar, typeof userVar)
// Check if user object has meaningful data
if (userVar && typeof userVar === 'object') {
console.log('EVAL: User object keys:', Object.keys(userVar))
console.log('EVAL: User object values preview:', JSON.stringify(userVar).substring(0, 200))
}
console.log('🔍 JavaScript auth detection:', {
is_authenticated: result.is_authenticated,
authType: result.authType,
hasUser: result.hasUser,
userType: result.userType,
userExists: !!w.user,
username: w.user?.username || 'N/A'
})
if (typeof w.is_authenticated === 'boolean') {
return {
isAuthenticated: w.is_authenticated,
hasUser: typeof w.user === 'object' && w.user !== null,
username: w.user?.username || null
}
}
return null
// Return authentication status
const result = isAuthVar === true || (userVar && typeof userVar === 'object' && Object.keys(userVar).length > 0)
console.log('EVAL: Final Strategy 1 result:', result)
return result
})
console.log('🔍 Auth status result:', authStatus)
console.log('CHECKING: Strategy 1 JavaScript result:', authStatus)
if (authStatus) {
if (authStatus.isAuthenticated && authStatus.hasUser) {
console.log(`SUCCESS: JavaScript indicates user is authenticated as "${authStatus.username}"`)
return true
} else {
console.log('INFO: JavaScript indicates user is not authenticated')
console.log('🔍 Auth details:', {
isAuthenticated: authStatus.isAuthenticated,
hasUser: authStatus.hasUser,
username: authStatus.username
})
return false
}
} else {
console.log('INFO: No JavaScript authentication variables found')
console.log('✅ Strategy 1: Authenticated via JavaScript variables')
return true
}
// Strategy 1.5: Check for user avatar/profile elements (indicates logged in state)
console.log('CHECKING: Strategy 1.5: User interface elements...')
const userElements = await this.page.$$eval('[data-testid="header-user-menu"], .tv-header__user-menu, [class*="user-menu"], [class*="avatar"], [data-name="header-user-menu"]', elements => {
console.log('EVAL: Found', elements.length, 'potential user elements')
return elements.length > 0
}).catch(() => false)
if (userElements) {
console.log('Strategy 1.5: Found user interface elements - likely authenticated')
return true
}
// Strategy 2: Check for absence of login forms (indicates already logged in)
console.log('CHECKING: Strategy 2: Absence of login forms...')
const pageUrl = this.page.url()
console.log('CHECKING: Current URL:', pageUrl)
// If we're not on a login page and no login forms exist, we're likely logged in
const hasLoginForms = await this.page.$('input[type="email"], input[name="username"], input[name="email"], input[type="password"]').then(el => !!el).catch(() => false)
const isOnLoginPage = pageUrl.includes('/signin') || pageUrl.includes('/login') || pageUrl.includes('/accounts/signin')
console.log('CHECKING: Has login forms:', hasLoginForms)
console.log('CHECKING: Is on login page:', isOnLoginPage)
if (!hasLoginForms && !isOnLoginPage) {
console.log('Strategy 2: No login forms and not on login page - likely authenticated')
return true
}
// Strategy 3: Check for "Sign In" vs "User Menu" buttons
console.log('CHECKING: Strategy 3: Page action buttons...')
const hasSignInButton = await this.page.$('[data-testid="header-signin-button"], a[href*="signin"], button:has-text("Sign in")').then(el => !!el).catch(() => false)
const hasUserMenu = await this.page.$('[data-testid="header-user-menu"], .tv-header__user-menu, [class*="user-menu"]').then(el => !!el).catch(() => false)
console.log('CHECKING: Has sign in button:', hasSignInButton)
console.log('CHECKING: Has user menu:', hasUserMenu)
if (!hasSignInButton && hasUserMenu) {
console.log('Strategy 3: User menu present, no sign in button - authenticated')
return true
}
if (!hasSignInButton && !isOnLoginPage) {
console.log('Strategy 3: No sign in button and not on login page - likely authenticated')
return true
}
// Strategy 2: Check for user account indicators (positive indicators)
@@ -300,8 +327,8 @@ export class TradingViewAutomation {
// Strategy 4: Check URL patterns
console.log('CHECKING: Strategy 4: Checking URL patterns...')
const currentUrl = this.page.url()
if (currentUrl.includes('/signin') || currentUrl.includes('/login')) {
const urlCheck = this.page.url()
if (urlCheck.includes('/signin') || urlCheck.includes('/login')) {
console.log('ERROR: On login page - not logged in')
return false
}
@@ -338,6 +365,20 @@ export class TradingViewAutomation {
}
}
// Strategy 7: Check for absence of login interface (final check)
console.log('CHECKING: Strategy 7: Final absence of login interface check...')
const finalUrlCheck = this.page.url()
const hasAnyLoginElements = await this.page.$('input[type="email"], input[name="username"], input[name="email"], input[type="password"], [href*="signin"], [href*="login"], button:has-text("Sign in")').then(el => !!el).catch(() => false)
const isOnMainTradingViewDomain = finalUrlCheck.includes('tradingview.com') && !finalUrlCheck.includes('/signin') && !finalUrlCheck.includes('/login')
console.log('CHECKING: Has any login elements:', hasAnyLoginElements)
console.log('CHECKING: Is on main TradingView domain (not login page):', isOnMainTradingViewDomain)
if (isOnMainTradingViewDomain && !hasAnyLoginElements) {
console.log('✅ Strategy 7: On main TradingView domain with no login elements - authenticated')
return true
}
// If we can't determine status clearly, assume not logged in to be safe
console.log('WARNING: Could not determine login status clearly, assuming not logged in')
return false
@@ -405,9 +446,7 @@ export class TradingViewAutomation {
const elem = el as HTMLElement
return elem.textContent || elem.getAttribute('title') || elem.getAttribute('href') || ''
})
console.log(`🎯 Found signin element: ${selector} with text: "${text}"`)
// Check if this looks like a sign in button
console.log(`Found signin element: ${selector} with text: "${text}"`) // Check if this looks like a sign in button
const lowerText = text.toLowerCase()
if (lowerText.includes('sign in') || lowerText.includes('login') ||
lowerText.includes('signin') || selector.includes('signin') ||
@@ -910,6 +949,38 @@ export class TradingViewAutomation {
}
}
async navigateToLayout(layoutId: string, symbol: string, timeframe?: string): Promise<boolean> {
if (!this.page) throw new Error('Page not initialized')
try {
console.log(`🎯 Navigating to layout ${layoutId} with symbol: ${symbol}`)
// Construct direct layout URL - this bypasses the generic chart URL issue
const directUrl = `https://www.tradingview.com/chart/${layoutId}/?symbol=${symbol}${timeframe ? `&interval=${timeframe}` : ''}`
console.log(`📍 Direct layout navigation to: ${directUrl}`)
await this.page.goto(directUrl, {
waitUntil: 'domcontentloaded',
timeout: 30000
})
// Wait for chart to load
await sleep(5000)
// Wait for chart container
await this.page.waitForSelector('.chart-container, #chart-container, [data-name="chart"]', {
timeout: 30000
})
console.log(`✅ Successfully navigated to layout ${layoutId}`)
return true
} catch (error) {
console.error(`❌ Failed to navigate to layout ${layoutId}:`, error)
return false
}
}
async navigateToSymbol(symbol: string, timeframe?: string): Promise<boolean> {
if (!this.page) throw new Error('Page not initialized')