bid-tool/auto-bid-tool/index.js

310 lines
11 KiB
JavaScript

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 { 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) {
// 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;
// }
// }
// if (!productTab.first_bid) {
// console.log(`🎯 Tracking out-bid event for Product ID: ${productTab.id}`);
// const updatedAt = new Date(productTab.updated_at).getTime();
// const now = Date.now();
// if (!productTab.page_context) {
// await productTab.puppeteer_connect();
// }
// if (productTab.page_context.url() !== productTab.url) {
// await productTab.gotoLink();
// }
// if (now - updatedAt < ONE_MINUTE) {
// console.log(`⏳ Product ID: ${productTab.id} was updated recently. Skipping update.`);
// }
// await productTab.update();
// console.log(`🔄 Updating Product ID: ${productTab.id}...`);
// continue;
// }
// if (productTab.start_bid_time && !isTimeReached(productTab.start_bid_time)) {
// console.log(`⏳ Not yet time to bid. Skipping Product ID: ${productTab.id}`);
// const updatedAt = new Date(productTab.updated_at).getTime();
// const now = Date.now();
// if (!productTab.page_context) {
// await productTab.puppeteer_connect();
// }
// if (productTab.page_context.url() !== productTab.url) {
// await productTab.gotoLink();
// }
// if (now - updatedAt < ONE_MINUTE) {
// console.log(`⏳ Product ID: ${productTab.id} was updated recently. Skipping update.`);
// }
// await productTab.update();
// continue;
// }
// if (!productTab.page_context) {
// console.log(`🔌 Connecting to page for Product ID: ${productTab.id}`);
// await productTab.puppeteer_connect();
// }
// console.log(`🚀 Executing action for Product ID: ${productTab.id}`);
// await productTab.action();
// }
// }, configs.AUTO_TRACKING_DELAY);
// };
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 clearLazyTab = async () => {
if (_CLEAR_LAZY_TAB_ID) {
clearInterval(_CLEAR_LAZY_TAB_ID);
_CLEAR_LAZY_TAB_ID = null;
}
try {
_CLEAR_LAZY_TAB_ID = setInterval(async () => {
if (!global.IS_CLEANING) return;
if (!browser) {
console.warn('⚠️ Browser is not available or disconnected.');
clearInterval(_CLEAR_LAZY_TAB_ID);
_CLEAR_LAZY_TAB_ID = null;
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);
}
}, configs.AUTO_TRACKING_CLEANING);
} catch (error) {
console.log('CLEAR LAZY TAB ERROR: ', error.message);
}
};
const workTracking = () => {
try {
if (_WORK_TRACKING_ID) {
clearInterval(_WORK_TRACKING_ID);
_WORK_TRACKING_ID = null;
}
_WORK_TRACKING_ID = setInterval(() => {
const activeData = _.flatMap(MANAGER_BIDS, (item) => [item, ...item.children]);
for (const item of activeData) {
if (item.page_context && !item.page_context.isClosed()) {
item.handleTakeWorkSnapshot();
}
}
}, 10000);
} catch (error) {
console.log('Loi oi day');
}
};
(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();
})();