bid-tool/auto-bid-tool/models/grays.com/grays-api-bid.js

293 lines
8.7 KiB
JavaScript

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(`🔄 [${this.id}] Starting polling process...`);
await page.evaluateHandle(
(apiUrl, interval, bidId) => {
if (window._autoBidPollingStarted) {
console.log(
`✅ [${bidId}] Polling is already running. Skipping initialization.`
);
return;
}
console.log(`🚀 [${bidId}] Initializing polling...`);
window._autoBidPollingStarted = true;
function sendRequest() {
console.log(
`📡 [${bidId}] 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(
`✅ [${bidId}] Response received: ${response.status}`
)
)
.catch((err) =>
console.error(`⚠️ [${bidId}] 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,
this.id
);
} catch (error) {
if (error.message.includes("Execution context was destroyed")) {
console.log(
`⚠️ [${this.id}] Page reload detected, restarting polling...`
);
await page
.waitForNavigation({ waitUntil: "networkidle2" })
.catch(() => {});
return await this.polling(page);
}
console.error(`🚨 [${this.id}] 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(
`❌ [${this.id}] 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
);
}
};
isLogin = async () => {
if (!this.page_context) return false;
const filePath = getPathProfile(this.origin_url);
if (
!(await this.page_context.$('input[name="username"]')) ||
fs.existsSync(filePath)
) {
return true;
}
return false;
};
async handleLogin() {
const page = this.page_context;
global.IS_CLEANING = false;
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(`✅ [${this.id}] Already logged in, skipping login.`);
global.IS_CLEANING = true;
this.retry_login = 0; // Reset retry count
return;
}
console.log(`🔑 [${this.id}] Starting login process...`);
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(`✅ [${this.id}] Login successful!`);
this.retry_login = 0; // Reset retry count after success
return;
}
throw new Error("Login failed, login input is still visible.");
} catch (error) {
console.log(
`⚠️ [${this.id}] Login error: ${error.message}. Retrying attempt ${
this.retry_login + 1
}`
);
this.retry_login++;
if (this.retry_login > this.retry_login_count) {
console.log(
`🚨 [${this.id}] 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
} finally {
global.IS_CLEANING = true;
}
}
action = async () => {
try {
const page = this.page_context;
await page.goto(this.url, { waitUntil: "networkidle2" });
console.log(`🌍 [${this.id}] Navigated to URL: ${this.url}`);
await page.bringToFront();
console.log(`🎯 [${this.id}] Brought page to front.`);
// 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"
);
console.log(`🛠️ [${this.id}] UserAgent set.`);
page.on("response", async (response) => {
if (
response.request().url().includes("api/Notifications/GetOutBidLots")
) {
console.log(`🚀 [${this.id}] API POST detected: ${response.url()}`);
try {
const responseBody = await response.json();
await this.listen_out_bids(responseBody.AuctionOutBidLots || []);
} catch (error) {
console.error(
`❌ [${this.id}] Error processing response:`,
error?.message
);
}
}
});
page.on("load", async () => {
console.log(`🔄 [${this.id}] Page has reloaded, restarting polling...`);
await this.polling(page);
await this.handleLogin();
});
await this.polling(page); // Call when first load
await this.handleLogin();
} catch (error) {
console.log(`❌ [${this.id}] Action error: ${error.message}`);
}
};
}