import path from 'path'; import { createOutBidLog } from '../../system/apis/out-bid-log.js'; import configs from '../../system/config.js'; import { delay, extractNumber, getPathProfile, isTimeReached, safeClosePage } from '../../system/utils.js'; import { ApiBid } from '../api-bid.js'; import fs from 'fs'; export class GrayApiBid extends ApiBid { retry_login = 0; retry_login_count = 3; constructor({ ...prev }) { super(prev); } async polling(page) { try { // // 🔥 Xóa tất cả event chặn request trước khi thêm mới // page.removeAllListeners('request'); // await page.setRequestInterception(true); // page.on('request', (request) => { // if (request.url().includes('api/Notifications/GetOutBidLots')) { // console.log('🚀 Fake response cho request:', request.url()); // const fakeData = fs.readFileSync('./data/fake-out-lots.json', 'utf8'); // request.respond({ // status: 200, // contentType: 'application/json', // body: fakeData, // }); // } else { // try { // request.continue(); // ⚠️ Chỉ tiếp tục nếu request chưa bị chặn // } catch (error) { // console.error('⚠️ Lỗi khi tiếp tục request:', error.message); // } // } // }); console.log('🔄 Starting polling process...'); await page.evaluateHandle( (apiUrl, interval) => { if (window._autoBidPollingStarted) { console.log('✅ Polling is already running. Skipping initialization.'); return; } console.log('🚀 Initializing polling...'); window._autoBidPollingStarted = true; function sendRequest() { console.log('📡 Sending request to track out-bid lots...'); fetch(apiUrl, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: JSON.stringify({ timeStamp: new Date().getTime() }), }) .then((response) => console.log(`✅ Response received: ${response.status}`)) .catch((err) => console.error('⚠️ Request error:', err)); } window._pollingInterval = setInterval(sendRequest, interval); }, configs.WEB_CONFIGS.GRAYS.API_CALL_TO_TRACKING, configs.WEB_CONFIGS.GRAYS.AUTO_CALL_API_TO_TRACKING, ); console.log('✅ Polling successfully started!'); } catch (error) { if (error.message.includes('Execution context was destroyed')) { console.log('⚠️ Page reload detected, restarting polling...'); await page.waitForNavigation({ waitUntil: 'networkidle2' }).catch(() => {}); return await this.polling(page); } console.error('🚨 Unexpected polling error:', error); throw error; } } async handleCreateLogsOnServer(data) { if (!Array.isArray(data)) return; const values = data.map((item) => { return { model: item.Sku, lot_id: item.Id, out_price: extractNumber(item.Bid) || 0, raw_data: JSON.stringify(item), }; }); await createOutBidLog(values); } listen_out_bids = async (data) => { if (this.children.length <= 0 || data.length <= 0) return; // SAVE LOGS ON SERVER this.handleCreateLogsOnServer(data); const bidOutLots = data.filter((bid) => !this.children_processing.some((item) => item.model === bid.Sku)); const handleChildren = this.children.filter((item) => bidOutLots.some((i) => i.Sku === item.model)); console.log({ handleChildren, children_processing: this.children_processing, data, bidOutLots }); for (const product_tab of handleChildren) { if (!isTimeReached(product_tab.start_bid_time)) { console.log("❌ It's not time yet ID: " + product_tab.id + ' continue waiting...'); return; } this.children_processing.push(product_tab); if (!product_tab.page_context) { await product_tab.puppeteer_connect(); } await product_tab.action(); this.children_processing = this.children_processing.filter((item) => item.id !== product_tab.id); } }; async handleLogin() { const page = this.page_context; const filePath = getPathProfile(this.origin_url); // 🔍 Check if already logged in (login input should not be visible) if (!(await page.$('input[name="username"]')) || fs.existsSync(filePath)) { console.log('✅ Already logged in, skipping login.'); global.IS_CLEANING = true; this.retry_login = 0; // Reset retry count return; } console.log('🔑 Starting login process...'); global.IS_CLEANING = false; try { await page.type('input[name="username"]', this.username, { delay: 100 }); await page.type('input[name="password"]', this.password, { delay: 150 }); await page.click('#loginButton'); await Promise.race([ page.waitForNavigation({ timeout: 8000, waitUntil: 'domcontentloaded' }), page.waitForFunction(() => !document.querySelector('input[name="username"]'), { timeout: 8000 }), // Check if login input disappears ]); if (!(await page.$('input[name="username"]'))) { console.log('✅ Login successful!'); this.retry_login = 0; // Reset retry count after success global.IS_CLEANING = true; return; } throw new Error('Login failed, login input is still visible.'); } catch (error) { console.log(`⚠️ Login error: ${error.message}. Retrying attempt ${this.retry_login + 1} ❌`); this.retry_login++; if (this.retry_login > this.retry_login_count) { console.log('🚨 Maximum login attempts reached. Stopping login process.'); safeClosePage(this); this.retry_login = 0; // Reset retry count return; } safeClosePage(this); // Close the current page await delay(1000); if (!this.page_context) { await this.puppeteer_connect(); // Reconnect if page is closed } return await this.action(); // Retry login } } action = async () => { try { const page = this.page_context; await page.goto(this.url, { waitUntil: 'networkidle2' }); await page.bringToFront(); // Set userAgent await page.setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'); // await takeSnapshot(this.page_context, this, 'working', CONSTANTS.TYPE_IMAGE.WORK); page.on('response', async (response) => { if (response.request().url().includes('api/Notifications/GetOutBidLots')) { console.log('🚀 API POST:', response.url()); try { const responseBody = await response.json(); await this.listen_out_bids(responseBody.AuctionOutBidLots || []); } catch (error) { console.error('❌ Error get response', error?.message); } } }); page.on('load', async () => { console.log('🔄 Trang đã reload, khởi động lại polling...'); // await takeSnapshot(this.page_context, this, 'working', CONSTANTS.TYPE_IMAGE.WORK); await this.polling(page); await this.handleLogin(); }); await this.polling(page); // Call when fist load await this.handleLogin(); } catch (error) { console.log(error.message); } }; }