bid-tool/auto-bid-tool/models/allbids.com.au/allbids-product-bid.js

409 lines
12 KiB
JavaScript

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 &&
scope?.auction.aucID === this.model &&
scope?.auction?.aucBidIncrement
) {
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((model) => {
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 &&
scope.auction &&
scope?.auction.aucID === model &&
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;
}, this.model);
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}`;
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_CONFIGS.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_CONFIGS.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: this.max_price,
});
// 📌 Kiểm tra trạng thái đấu giá từ API
if (
data?.bidResult?.result ||
data?.bidResult?.bidAmount == this.max_price
) {
console.log(`📸 [${this.id}] Taking bid success snapshot...`);
// sendMessage(this);
pushPrice({
bid_id: this.id,
price: this.max_price,
});
await takeSnapshot(
this.page_context,
this,
"bid-success",
CONSTANTS.TYPE_IMAGE.SUCCESS
);
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}`);
}
};
}