/** * IB Session Operations - Browser automation for session headers */ import { Browser } from '@stock-bot/browser'; import { OperationContext } from '@stock-bot/di'; import type { ServiceContainer } from '@stock-bot/di'; import { IB_CONFIG } from '../shared/config'; export async function fetchSession(container: ServiceContainer): Promise | undefined> { const ctx = OperationContext.create('ib', 'session', { container }); try { await Browser.initialize({ headless: true, timeout: IB_CONFIG.BROWSER_TIMEOUT, blockResources: false }); ctx.logger.info('✅ Browser initialized'); const { page } = await Browser.createPageWithProxy( IB_CONFIG.BASE_URL + IB_CONFIG.PRODUCTS_PAGE, IB_CONFIG.DEFAULT_PROXY ); ctx.logger.info('✅ Page created with proxy'); const headersPromise = new Promise | undefined>(resolve => { let resolved = false; page.onNetworkEvent(event => { if (event.url.includes('/webrest/search/product-types/summary')) { if (event.type === 'request') { try { resolve(event.headers); } catch (e) { resolve(undefined); ctx.logger.debug('Raw Summary Response error', { error: (e as Error).message }); } } } }); // Timeout fallback setTimeout(() => { if (!resolved) { resolved = true; ctx.logger.warn('Timeout waiting for headers'); resolve(undefined); } }, IB_CONFIG.HEADERS_TIMEOUT); }); ctx.logger.info('⏳ Waiting for page load...'); await page.waitForLoadState('domcontentloaded', { timeout: IB_CONFIG.PAGE_LOAD_TIMEOUT }); ctx.logger.info('✅ Page loaded'); //Products tabs ctx.logger.info('🔍 Looking for Products tab...'); const productsTab = page.locator('#productSearchTab[role=\"tab\"][href=\"#products\"]'); await productsTab.waitFor({ timeout: IB_CONFIG.ELEMENT_TIMEOUT }); ctx.logger.info('✅ Found Products tab'); ctx.logger.info('🖱️ Clicking Products tab...'); await productsTab.click(); ctx.logger.info('✅ Products tab clicked'); // New Products Checkbox ctx.logger.info('🔍 Looking for \"New Products Only\" radio button...'); const radioButton = page.locator('span.checkbox-text:has-text(\"New Products Only\")'); await radioButton.waitFor({ timeout: IB_CONFIG.ELEMENT_TIMEOUT }); ctx.logger.info(`🎯 Found \"New Products Only\" radio button`); await radioButton.first().click(); ctx.logger.info('✅ \"New Products Only\" radio button clicked'); // Wait for and return headers immediately when captured ctx.logger.info('⏳ Waiting for headers to be captured...'); const headers = await headersPromise; page.close(); if (headers) { ctx.logger.info('✅ Headers captured successfully'); } else { ctx.logger.warn('⚠️ No headers were captured'); } return headers; } catch (error) { ctx.logger.error('Failed to fetch IB symbol summary', { error }); return; } finally { await ctx.dispose(); } }