import _ from "lodash"; import { outBid, pushPrice, updateBid } from "../../system/apis/bid.js"; import { sendMessage } from "../../system/apis/notification.js"; import { createOutBidLog } from "../../system/apis/out-bid-log.js"; import configs from "../../system/config.js"; import CONSTANTS from "../../system/constants.js"; import { convertAETtoUTC, isTimeReached, removeFalsyValues, takeSnapshot, } from "../../system/utils.js"; import { ProductBid } from "../product-bid.js"; import axios from "../../system/axios.js"; export class AllbidsProductBid extends ProductBid { constructor({ ...prev }) { super(prev); } async waitForApiResponse() { if (!this.page_context) return; try { // Chờ cho Angular load (có thể tùy chỉnh thời gian nếu cần) await this.page_context.waitForFunction( () => window.angular !== undefined ); const auctionData = await this.page_context.evaluate(() => { let data = null; const elements = document.querySelectorAll(".ng-scope"); for (let i = 0; i < elements.length; i++) { try { const scope = angular.element(elements[i]).scope(); if (scope?.auction) { data = scope.auction; break; } // Thử lấy từ $parent nếu không thấy if (scope?.$parent?.auction) { data = scope.$parent.auction; break; } } catch (e) { // Angular element có thể lỗi nếu phần tử không hợp lệ continue; } } return data; }); return auctionData; } catch (error) { console.log( `[${this.id}] Error in waitForApiResponse: ${error?.message}` ); } } async getHistoriesData() { if (!this.page_context) return; try { // Chờ cho Angular load (có thể tùy chỉnh thời gian nếu cần) await this.page_context.waitForFunction( () => window.angular !== undefined ); const historiesData = await this.page_context.evaluate(() => { let data = null; const elements = document.querySelectorAll(".ng-scope"); for (let i = 0; i < elements.length; i++) { try { const scope = angular.element(elements[i]).scope(); if (scope?.bidHistory) { data = scope.bidHistory; break; } // Thử lấy từ $parent nếu không thấy if (scope?.$parent?.bidHistory) { data = scope.$parent.bidHistory; break; } } catch (e) { // Angular element có thể lỗi nếu phần tử không hợp lệ continue; } } return data; }); return historiesData; } catch (error) { console.log( `[${this.id}] Error in waitForApiResponse: ${error?.message}` ); } } async handleUpdateBid({ lot_id, close_time, name, current_price, reserve_price, model, metadata, }) { const response = await updateBid(this.id, { lot_id, close_time, name, current_price, reserve_price: Number(reserve_price) || 0, model, metadata, }); if (response) { this.lot_id = response.lot_id; this.close_time = response.close_time; this.start_bid_time = response.start_bid_time; } } async handlePlaceBidLive() { if (!this.page_context) return; const response = await this.page_context.evaluate( async (aucID, bidAmount, submitUrl) => { try { const url = `${submitUrl}?aucID=${aucID}&bidAmount=${bidAmount}&bidType=maximum`; const res = await fetch(url, { method: "POST", }); if (!res.ok) { return { success: false, message: `HTTP error ${res.status}` }; } const data = await res.json(); return data; } catch (error) { return { success: false, message: error.message || "Fetch failed" }; } }, this.model, this.max_price, configs.WEB_URLS.ALLBIDS.PLACE_BID ); return response; } async handlePlaceBidSanbox() { if (!this.page_context) return; const response = await this.page_context.evaluate( async (aucID, submitUrl) => { try { const url = `${submitUrl}?aucID=${aucID}&bidAmount=${0}&bidType=maximum`; const res = await fetch(url, { method: "POST", }); if (!res.ok) { return { success: false, message: `HTTP error ${res.status}` }; } const data = await res.json(); return data; } catch (error) { return { success: false, message: error.message || "Fetch failed" }; } }, this.model, configs.WEB_URLS.ALLBIDS.PLACE_BID ); return response; } update = async () => { if (!this.page_context) return; console.log(`🔄 [${this.id}] Call update for ID: ${this.id}`); // 📌 Chờ phản hồi API từ trang, tối đa 10 giây const result = await this.waitForApiResponse(); const historiesData = await this.getHistoriesData(); // 📌 Nếu không có dữ liệu trả về thì dừng if (!result) { console.log(`⚠️ [${this.id}] No valid data received, skipping update.`); return; } // 📌 Loại bỏ các giá trị không hợp lệ và bổ sung thông tin cần thiết const data = removeFalsyValues( { // model: result?.pid || null, lot_id: String(result?.aucCurrentBidID) || null, reserve_price: result?.aucBidIncrement || null, current_price: result.aucCurrentBid || null, close_time: result?.aucCloseUtc ? new Date(result.aucCloseUtc).toUTCString() : null, // close_time: close_time && !this.close_time ? String(close_time) : null, // test name: result?.aucTitle || null, metadata: { competor_histories: historiesData, }, }, ["close_time"] ); console.log(`🚀 [${this.id}] Processed data ready for update`); // 📌 Gửi dữ liệu cập nhật lên hệ thống await this.handleUpdateBid(data); console.log("✅ Update successful!"); return { ...response, name: data.name, close_time: data.close_time }; }; async handlePlaceBid() { if (!this.page_context) { console.log( `⚠️ [${this.id}] No page context found, aborting bid process.` ); return; } const page = this.page_context; if (global[`IS_PLACE_BID-${this.id}`]) { console.log(`⚠️ [${this.id}] Bid is already in progress, skipping.`); return; } try { console.log(`🔄 [${this.id}] Starting bid process...`); global[`IS_PLACE_BID-${this.id}`] = true; // Đợi phản hồi từ API const response = await this.waitForApiResponse(); if ( !response || isTimeReached(new Date(response.aucCloseUtc).toUTCString()) ) { console.log( `⚠️ [${this.id}] Outbid detected, calling outBid function.` ); await outBid(this.id); return; } // Kiểm tra nếu giá hiện tại lớn hơn giá tối đa cộng thêm giá cộng thêm if (this.current_price > this.max_price + this.plus_price) { console.log(`⚠️ [${this.id}] Outbid bid`); // Ghi log cảnh báo nếu giá hiện tại vượt quá mức tối đa cho phép return; // Dừng hàm nếu giá đã vượt qua giới hạn } // Kiểm tra thời gian bid if (this.start_bid_time && !isTimeReached(this.start_bid_time)) { console.log( `⏳ [${this.id}] Not yet time to bid. Skipping Product: ${ this.name || "None" }` ); return; } // Kiểm tra nếu phản hồi không tồn tại hoặc nếu giá đấu của người dùng bằng với giá tối đa hiện tại if ( !response || (response?.aucUserMaxBid && response.aucUserMaxBid == this.max_price) || response?.aucBidIncrement > this.max_price ) { console.log( `⚠️ [${this.id}] No response or myBid equals max_price:`, response ); // Ghi log nếu không có phản hồi hoặc giá đấu của người dùng bằng giá tối đa return; // Nếu không có phản hồi hoặc giá đấu bằng giá tối đa thì dừng hàm } const bidHistoriesItem = _.maxBy(this.histories, "price"); console.log(`📜 [${this.id}] Current bid history:`, this.histories); if ( bidHistoriesItem && bidHistoriesItem?.price === this.current_price && this.max_price == response?.aucUserMaxBid ) { console.log( `🔄 [${this.id}] You have already bid on this item! (Bid Price: ${bidHistoriesItem.price})` ); return; } console.log("---------------------BIDDING--------------------"); if (this.isSandbox()) { await this.handleCallActionSanbox(); } else { await this.handleCallActionLive(); } } catch (error) { console.log(`🚨 [${this.id}] Error placing bid: ${error.message}`); } finally { console.log(`🔚 [${this.id}] Resetting bid flag.`); global[`IS_PLACE_BID-${this.id}`] = false; } } async handleCallActionLive() { const data = await this.handlePlaceBidLive(); await this.page_context.reload({ waitUntil: "networkidle0" }); const { aucUserMaxBid } = await this.waitForApiResponse(); console.log(`📡 [${this.id}] API Response received:`, { aucUserMaxBid }); // 📌 Kiểm tra trạng thái đấu giá từ API if (aucUserMaxBid == this.max_price) { console.log(`📸 [${this.id}] Taking bid success snapshot...`); await takeSnapshot( page, this, "bid-success", CONSTANTS.TYPE_IMAGE.SUCCESS ); // sendMessage(this); pushPrice({ bid_id: this.id, price: aucUserMaxBid, }); console.log(`✅ [${this.id}] Bid placed successfully!`); return; } console.log( `⚠️ [${this.id}] Bid action completed, but status is still "None".` ); } async handleCallActionSanbox() { const result = await this.handlePlaceBidSanbox(); await axios({ url: this.ACTION_URL({ type: "api" }), data: { id: this.id, data: JSON.stringify(result), }, method: "POST", }); await this.close(); return result; } action = async () => { try { const page = this.page_context; // 📌 Kiểm tra nếu trang chưa tải đúng URL thì điều hướng đến URL mục tiêu if (!page.url() || !page.url().includes(this.url)) { console.log(`🔄 [${this.id}] Navigating to target URL: ${this.url}`); await this.gotoLink(); } await this.handlePlaceBid(); } catch (error) { console.error(`🚨 [${this.id}] Error navigating the page: ${error}`); } }; }