301 lines
10 KiB
JavaScript
301 lines
10 KiB
JavaScript
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
|
|
};
|
|
}
|