import fs from "fs"; import configs from "../../system/config.js"; import { delay, getPathProfile, safeClosePage } from "../../system/utils.js"; import { ApiBid } from "../api-bid.js"; export class LawsonsApiBid 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 2 minute.` ) ); }, 120 * 1000); // 60 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 }); }); async isLogin() { if (!this.page_context) return false; const filePath = getPathProfile(this.origin_url); return ( !(await this.page_context.$("#emailLogin")) && fs.existsSync(filePath) ); } 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.` ) ); }, 60 * 1000); // 60 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 }); }); async enterOTP(otp) { try { // Selector cho tất cả các input OTP const inputSelector = ".MuiDialog-container .container input"; // Chờ cho các input OTP xuất hiện await this.page_context.waitForSelector(inputSelector, { timeout: 8000 }); // Lấy tất cả các input OTP const inputs = await this.page_context.$$(inputSelector); // Kiểm tra nếu có đúng 6 trường input if (inputs.length === 6 && otp.length === 6) { // Nhập mỗi ký tự của OTP vào các input tương ứng for (let i = 0; i < 6; i++) { await inputs[i].type(otp[i], { delay: 100 }); } console.log(`✅ OTP entered successfully: ${otp}`); } else { console.error("❌ Invalid OTP or input fields count"); } } catch (error) { console.error("❌ Error entering OTP:", error); } } async waitToTwoVerify() { try { if (!this.page_context) return false; // Selector của các phần tử trên trang const button = ".form-input-wrapper.form-group > .btn.btn-primary"; // Nút để tiếp tục quá trình xác minh const remember = ".PrivateSwitchBase-input"; // Checkbox "Remember me" const continueButton = ".MuiButtonBase-root.MuiButton-root.MuiButton-contained.MuiButton-containedPrimary.MuiButton-sizeMedium.MuiButton-containedSizeMedium.MuiButton-colorPrimary.MuiButton-root"; // Nút "Continue" // Chờ cho nút xác minh xuất hiện console.log( `🔎 [${this.id}] Waiting for the button with selector: ${button}` ); await this.page_context.waitForSelector(button, { timeout: 8000 }); console.log(`✅ [${this.id}] Button found, clicking the first button.`); // Lấy phần tử của nút và log nội dung của nó const firstButton = await this.page_context.$(button); // Lấy phần tử đầu tiên const buttonContent = await firstButton.evaluate((el) => el.textContent); // Lấy nội dung của nút console.log(`🔎 [${this.id}] Button content: ${buttonContent}`); // Chờ 2s cho button sẵn sàn await delay(2000); // Click vào nút xác minh await firstButton.click(); console.log(`✅ [${this.id}] Button clicked.`); // Nhận mã OTP để nhập vào form const { name, code } = await this.waitVerifyData(); console.log( `🔎 [${this.id}] Waiting for OTP input, received code: ${code}` ); // Nhập mã OTP vào form await this.enterOTP(code); console.log(`✅ [${this.id}] OTP entered successfully.`); // Chờ cho checkbox "Remember me" xuất hiện await this.page_context.waitForSelector(remember, { timeout: 8000 }); console.log( `🔎 [${this.id}] Waiting for remember me checkbox with selector: ${remember}` ); // Click vào checkbox "Remember me" await this.page_context.click(remember, { delay: 92 }); console.log(`✅ [${this.id}] Remember me checkbox clicked.`); // Chờ cho nút "Continue" xuất hiện await this.page_context.waitForSelector(continueButton, { timeout: 8000, }); console.log( `🔎 [${this.id}] Waiting for continue button with selector: ${continueButton}` ); // Click vào nút "Continue" await this.page_context.click(continueButton, { delay: 100 }); console.log(`✅ [${this.id}] Continue button clicked.`); // Chờ cho trang tải hoàn tất sau khi click "Continue" await this.page_context.waitForNavigation({ waitUntil: "domcontentloaded", }); console.log(`✅ [${this.id}] Navigation completed.`); return true; } catch (error) { console.error(`❌ [${this.id}] Error:`, error); return false; } } 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.$("#emailLogin")) && 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("#emailLogin", this.username, { delay: 100 }); // ⌨ Enter password console.log(`✍ [${this.id}] Entering password...`); await page.type("#passwordLogin", this.password, { delay: 150 }); // 🚀 Click the login button console.log(`🔘 [${this.id}] Clicking the "Login" button`); await page.click("#signInBtn", { delay: 92 }); const result = await this.waitToTwoVerify(); // ⏳ Wait for navigation after login if (!result) { console.log(`⏳ [${this.id}] Waiting for navigation after login...`); await page.waitForNavigation({ timeout: 8000, waitUntil: "domcontentloaded", }); } if (this.page_context.url() == this.url) { // 📂 Save session context to avoid re-login await this.saveContext(); console.log(`✅ [${this.id}] Login successful!`); } else { console.log(`❌ [${this.id}] Login Failure!`); } } catch (error) { console.error( `❌ [${this.id}] Error during login process:`, error.message ); } finally { global.IS_CLEANING = true; } } 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.LAWSONS.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(); const results = await this.handlePrevListen(); if (!results) return; 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.`); } 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 }; }