import 'dotenv/config'; import _ from 'lodash'; import { io } from 'socket.io-client'; import { createApiBid, createBidProduct, deleteProfile, shouldUpdateProductTab } from './service/app-service.js'; import browser from './system/browser.js'; import configs from './system/config.js'; import { delay, isTimeReached, safeClosePage } from './system/utils.js'; let MANAGER_BIDS = []; let _INTERVAL_TRACKING_ID = null; let _CLEAR_LAZY_TAB_ID = null; let _WORK_TRACKING_ID = null; global.IS_CLEANING = false; const handleUpdateProductTabs = (data) => { if (!Array.isArray(data)) { console.log('Data must be array'); return; } const managerBidMap = new Map(MANAGER_BIDS.map((bid) => [bid.id, bid])); const newDataManager = data.map(({ children, ...web }) => { const prevApiBid = managerBidMap.get(web.id); const newChildren = children.map((item) => { const prevProductTab = prevApiBid?.children.find((i) => i.id === item.id); if (prevProductTab) { prevProductTab.setNewData(item); return prevProductTab; } return createBidProduct(web, item); }); if (prevApiBid) { prevApiBid.setNewData({ children: newChildren, ...web }); return prevApiBid; } return createApiBid({ ...web, children: newChildren }); }); MANAGER_BIDS = newDataManager; }; // const tracking = async () => { // if (_INTERVAL_TRACKING_ID) { // clearInterval(_INTERVAL_TRACKING_ID); // _INTERVAL_TRACKING_ID = null; // } // _INTERVAL_TRACKING_ID = setInterval(async () => { // const productTabs = _.flatMap(MANAGER_BIDS, 'children'); // for (const productTab of productTabs) { // // Tìm parent context nếu chưa có // if (!productTab.parent_browser_context) { // const parent = _.find(MANAGER_BIDS, { id: productTab.web_bid.id }); // productTab.parent_browser_context = parent?.browser_context; // if (!productTab.parent_browser_context) { // console.log(`🔄 Waiting for parent process to start... (Product ID: ${productTab.id})`); // continue; // } // } // // Kết nối Puppeteer nếu chưa có page_context // if (!productTab.page_context) { // console.log(`🔌 Connecting to page for Product ID: ${productTab.id}`); // await productTab.puppeteer_connect(); // } // // Nếu URL thay đổi, điều hướng đến URL mới // if (productTab.page_context.url() !== productTab.url) { // await productTab.gotoLink(); // } // // Kiểm tra nếu cần cập nhật trước khi gọi update() // if (shouldUpdateProductTab(productTab)) { // console.log(`🔄 Updating Product ID: ${productTab.id}...`); // await productTab.update(); // } else { // console.log(`⏳ Product ID: ${productTab.id} was updated recently. Skipping update.`); // } // // Nếu chưa có first_bid (trạng thái chưa đặt giá) // if (!productTab.first_bid) { // console.log(`🎯 Tracking out-bid event for Product ID: ${productTab.id}`); // continue; // } // // Nếu chưa đến giờ bid // if (productTab.start_bid_time && !isTimeReached(productTab.start_bid_time)) { // console.log(`⏳ Not yet time to bid. Skipping Product ID: ${productTab.id}`); // continue; // } // console.log(`🚀 Executing action for Product ID: ${productTab.id}`); // await productTab.action(); // } // }, configs.AUTO_TRACKING_DELAY); // }; const tracking = async () => { console.log('🚀 Tracking process started...'); while (true) { console.log('🔍 Scanning active bids...'); const productTabs = _.flatMap(MANAGER_BIDS, 'children'); for (const apiBid of MANAGER_BIDS) { if (apiBid.page_context) continue; console.log(`🎧 Listening to events for API Bid ID: ${apiBid.id}`); await apiBid.listen_events(); } for (const productTab of productTabs) { console.log(`📌 Processing Product ID: ${productTab.id}`); // Tìm parent context nếu chưa có if (!productTab.parent_browser_context) { const parent = _.find(MANAGER_BIDS, { id: productTab.web_bid.id }); productTab.parent_browser_context = parent?.browser_context; if (!productTab.parent_browser_context) { console.log(`⏳ Waiting for parent process to start... (Product ID: ${productTab.id})`); continue; } } // Kết nối Puppeteer nếu chưa có page_context if (!productTab.page_context) { console.log(`🔌 Connecting to page for Product ID: ${productTab.id}`); await productTab.puppeteer_connect(); } // Nếu URL thay đổi, điều hướng đến URL mới if (productTab.page_context.url() !== productTab.url) { console.log(`🔄 Redirecting to new URL for Product ID: ${productTab.id}`); await productTab.gotoLink(); } // Kiểm tra nếu cần cập nhật trước khi gọi update() if (shouldUpdateProductTab(productTab)) { console.log(`🔄 Updating Product ID: ${productTab.id}...`); await productTab.update(); } else { console.log(`⏳ Product ID: ${productTab.id} was updated recently. Skipping update.`); } // Nếu chưa có first_bid (trạng thái chưa đặt giá) if (!productTab.first_bid) { console.log(`🎯 Waiting for first bid for Product ID: ${productTab.id}`); continue; } // Nếu chưa đến giờ bid if (productTab.start_bid_time && !isTimeReached(productTab.start_bid_time)) { console.log(`⏳ Not yet time to bid. Skipping Product ID: ${productTab.id}`); continue; } console.log(`🚀 Executing action for Product ID: ${productTab.id}`); await productTab.action(); } console.log('🧹 Cleaning up unused tabs...'); await clearLazyTab(); console.log('📊 Tracking work status...'); workTracking(); console.log(`⏳ Waiting ${configs.AUTO_TRACKING_DELAY / 1000} seconds before the next iteration...`); await delay(configs.AUTO_TRACKING_DELAY); } }; const clearLazyTab = async () => { if (!global.IS_CLEANING) return; if (!browser) { console.warn('⚠️ Browser is not available or disconnected.'); return; } try { const pages = await browser.pages(); // Lấy danh sách URL từ flattenedArray const activeUrls = _.flatMap(MANAGER_BIDS, (item) => [item.url, ...item.children.map((child) => child.url)]).filter(Boolean); // Lọc bỏ null hoặc undefined console.log( '🔍 Page URLs:', pages.map((page) => page.url()), ); for (const page of pages) { const pageUrl = page.url(); // 🔥 Bỏ qua tab 'about:blank' hoặc tab không có URL if (!pageUrl || pageUrl === 'about:blank') continue; if (!activeUrls.includes(pageUrl)) { if (!page.isClosed() && browser.isConnected()) { try { await page.close(); console.log(`🛑 Closing unused tab: ${pageUrl}`); } catch (err) { console.warn(`⚠️ Error closing tab ${pageUrl}:`, err.message); } } } } } catch (err) { console.error('❌ Error in clearLazyTab:', err.message); } }; const workTracking = async () => { try { const activeData = _.flatMap(MANAGER_BIDS, (item) => [item, ...item.children]); for (const item of activeData) { if (item.page_context && !item.page_context.isClosed()) { item.handleTakeWorkSnapshot(); } } } catch (error) { console.log('Lỗi rồi:', error); } }; (async () => { const socket = io(configs.SOCKET_URL, { transports: ['websocket'], reconnection: true, }); // listen connect socket.on('connect', () => { console.log('✅ Connected to WebSocket server'); console.log('🔗 Socket ID:', socket.id); }); // listen event socket.on('bidsUpdated', async (data) => { console.log('📢 Bids Data:', data); handleUpdateProductTabs(data); // await Promise.all(MANAGER_BIDS.map((apiBid) => apiBid.listen_events())); }); socket.on('webUpdated', async (data) => { console.log('📢 Account was updated:', data); const isDeleted = deleteProfile(data); if (isDeleted) { console.log('✅ Profile deleted successfully!'); const tabs = MANAGER_BIDS.filter((item) => item.url === data.url || item?.web_bid.url === data.url); if (tabs.length <= 0) return; await Promise.all(tabs.map((tab) => safeClosePage(tab))); await Promise.all(MANAGER_BIDS.map((apiBid) => apiBid.listen_events())); } else { console.log('⚠️ No profile found to delete.'); } }); // AUTO TRACKING tracking(); // clearLazyTab(); // workTracking(); })();