bid-tool/auto-bid-tool/models/langtons.com.au/langtons-api-bid.js

285 lines
8.9 KiB
JavaScript

import fs from "fs";
import configs from "../../system/config.js";
import { getPathProfile, safeClosePage } from "../../system/utils.js";
import { ApiBid } from "../api-bid.js";
import _ from "lodash";
import { updateStatusByPrice } from "../../system/apis/bid.js";
export class LangtonsApiBid extends ApiBid {
reloadInterval = null;
constructor({ ...prev }) {
super(prev);
}
waitVerifyData = async () =>
new Promise((rev, rej) => {
// Tạo timeout để reject sau 1 phút nếu không có phản hồi
const timeout = setTimeout(() => {
global.socket.off(`verify-code.${this.origin_url}`); // Xóa listener tránh rò rỉ bộ nhớ
rej(
new Error(
`[${this.id}] Timeout: No verification code received within 1 minute.`
)
);
}, 120 * 1000); // 120 giây
global.socket.on(`verify-code.${this.origin_url}`, async (data) => {
console.log(`📢 [${this.id}] VERIFY CODE:`, data);
clearTimeout(timeout); // Hủy timeout vì đã nhận được mã
global.socket.off(`verify-code.${this.origin_url}`); // Xóa listener tránh lặp lại
rev(data); // Resolve với dữ liệu nhận được
});
});
isLogin = async () => {
if (!this.page_context) return false;
const filePath = getPathProfile(this.origin_url);
return (
!(await this.page_context.$('input[name="loginEmail"]')) &&
fs.existsSync(filePath)
);
};
async handleLogin() {
const page = this.page_context;
global.IS_CLEANING = false;
const filePath = getPathProfile(this.origin_url);
await page.waitForNavigation({ waitUntil: "domcontentloaded" });
// 🛠 Check if already logged in (login input should not be visible or profile exists)
if (
!(await page.$('input[name="loginEmail"]')) &&
fs.existsSync(filePath)
) {
console.log(`✅ [${this.id}] Already logged in, skipping login process.`);
return;
}
if (fs.existsSync(filePath)) {
console.log(`🗑 [${this.id}] Deleting existing file: ${filePath}`);
fs.unlinkSync(filePath);
}
const children = this.children.filter((item) => item.page_context);
console.log(
`🔍 [${this.id}] Found ${children.length} child pages to close.`
);
if (children.length > 0) {
console.log(`🛑 [${this.id}] Closing child pages...`);
await Promise.all(
children.map((item) => {
console.log(
`➡ [${this.id}] Closing child page with context: ${item.page_context}`
);
return safeClosePage(item);
})
);
console.log(
`➡ [${this.id}] Closing main page context: ${this.page_context}`
);
await safeClosePage(this);
}
console.log(`🔑 [${this.id}] Starting login process...`);
try {
// ⌨ Enter email
console.log(`✍ [${this.id}] Entering email:`, this.username);
await page.type('input[name="loginEmail"]', this.username, {
delay: 100,
});
// ⌨ Enter password
console.log(`✍ [${this.id}] Entering password...`);
await page.type('input[name="loginPassword"]', this.password, {
delay: 150,
});
// ✅ Click the "Remember Me" checkbox
console.log(`🔘 [${this.id}] Clicking the "Remember Me" checkbox`);
await page.click("#rememberMe", { delay: 80 });
// 🚀 Click the login button
console.log(`🔘 [${this.id}] Clicking the "Login" button`);
await page.click("#loginFormSubmitButton", { delay: 92 });
// ⏳ Wait for navigation after login
console.log(`⏳ [${this.id}] Waiting for navigation after login...`);
await page.waitForNavigation({
timeout: 8000,
waitUntil: "domcontentloaded",
});
console.log(`🌍 [${this.id}] Current page after login:`, page.url());
// 📢 Listen for verification code event
console.log(
`👂 [${this.id}] Listening for event: verify-code.${this.origin_url}`
);
// ⏳ Wait for verification code from socket event
const { name, code } = await this.waitVerifyData();
console.log(`✅ [${this.id}] Verification code received:`, {
name,
code,
});
// ⌨ Enter verification code
console.log(`✍ [${this.id}] Entering verification code...`);
await page.type("#code", code, { delay: 120 });
// 🚀 Click the verification confirmation button
console.log(
`🔘 [${this.id}] Clicking the verification confirmation button`
);
await page.click(".btn.btn-block.btn-primary", { delay: 90 });
// ⏳ Wait for navigation after verification
console.log(
`⏳ [${this.id}] Waiting for navigation after verification...`
);
await page.waitForNavigation({
timeout: 15000,
waitUntil: "domcontentloaded",
});
await page.goto(this.url, { waitUntil: "networkidle2" });
// 📂 Save session context to avoid re-login
await this.saveContext();
console.log(`✅ [${this.id}] Login successful!`);
// await page.goto(this.url);
console.log(`✅ [${this.id}] Navigation successful!`);
} catch (error) {
console.error(
`❌ [${this.id}] Error during login process:`,
error.message
);
} finally {
global.IS_CLEANING = true;
}
}
async getWonList() {
try {
await page.waitForSelector(".row.account-product-list", {
timeout: 30000,
});
const items = await page.evaluate(() => {
return Array.from(
document.querySelectorAll(".row.account-product-list")
).map((item) => item.getAttribute("data-lotid") || null);
});
return _.compact(items);
} catch (error) {
return [];
}
}
async handleUpdateWonItem() {
console.log(`🔄 [${this.id}] Starting to update the won list...`);
// Lấy danh sách các lot_id thắng
const items = await this.getWonList();
console.log(`📌 [${this.id}] List of won lot_ids:`, items);
// Nếu không có item nào, thoát ra
if (items.length === 0) {
console.log(`⚠️ [${this.id}] No items to update.`);
return;
}
// Lọc danh sách `this.children` chỉ giữ lại những item có trong danh sách thắng
const result = _.filter(this.children, (item) =>
_.includes(items, item.lot_id)
);
console.log(
`✅ [${this.id}] ${result.length} items need to be updated:`,
result
);
// Gọi API updateStatusByPrice cho mỗi item và đợi tất cả hoàn thành
const responses = await Promise.allSettled(
result.map((i) => updateStatusByPrice(i.id, i.current_price))
);
// Log kết quả của mỗi request
responses.forEach((response, index) => {
if (response.status === "fulfilled") {
console.log(`✔️ [${this.id}] Successfully updated:`, result[index]);
} else {
console.error(
`❌ [${this.id}] Update failed:`,
result[index],
response.reason
);
}
});
console.log(`🏁 [${this.id}] Finished updating the won list.`);
return responses;
}
action = async () => {
try {
const page = this.page_context;
page.on("response", async (response) => {
const request = response.request();
if (request.redirectChain().length > 0) {
if (response.url().includes(configs.WEB_CONFIGS.LANGTONS.LOGIN_URL)) {
await this.handleLogin();
}
}
});
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"
);
} catch (error) {
console.log("Error [action]: ", error.message);
}
};
listen_events = async () => {
if (this.page_context) return;
await this.puppeteer_connect();
await this.action();
this.reloadInterval = setInterval(async () => {
try {
if (this.page_context && !this.page_context.isClosed()) {
console.log(`🔄 [${this.id}] Reloading page...`);
await this.page_context.reload({ waitUntil: "networkidle2" });
console.log(`✅ [${this.id}] Page reloaded successfully.`);
// this.handleUpdateWonItem();
} else {
console.log(
`❌ [${this.id}] Page context is closed. Stopping reload.`
);
clearInterval(this.reloadInterval);
}
} catch (error) {
console.error(`🚨 [${this.id}] Error reloading page:`, error.message);
}
}, 60000); // 1p reload
};
}