323 lines
8.5 KiB
JavaScript
323 lines
8.5 KiB
JavaScript
import {
|
|
outBid,
|
|
pushPrice,
|
|
updateBid,
|
|
updateStatusByPrice,
|
|
} from "../../system/apis/bid.js";
|
|
import CONSTANTS from "../../system/constants.js";
|
|
import {
|
|
delay,
|
|
extractNumber,
|
|
isNumber,
|
|
isTimeReached,
|
|
removeFalsyValues,
|
|
safeClosePage,
|
|
takeSnapshot,
|
|
} from "../../system/utils.js";
|
|
import { ProductBid } from "../product-bid.js";
|
|
|
|
export class GraysProductBidBackup extends ProductBid {
|
|
constructor({ ...prev }) {
|
|
super(prev);
|
|
}
|
|
|
|
async validate({ page, price_value }) {
|
|
if (!this.start_bid_time || !isTimeReached(this.start_bid_time)) {
|
|
console.log(`❌ [${this.id}] It's not time yet`);
|
|
return { result: false, bid_price: 0 };
|
|
}
|
|
|
|
if (!isNumber(price_value)) {
|
|
console.log(`❌ [${this.id}] Can't get PRICE_VALUE`);
|
|
await takeSnapshot(page, this, "price-value-null");
|
|
|
|
return { result: false, bid_price: 0 };
|
|
}
|
|
|
|
const bid_price = this.plus_price + Number(price_value);
|
|
|
|
if (bid_price > this.max_price) {
|
|
console.log(
|
|
`❌ ${this.id} PRICE BID is more than MAX_VALUE => STOP BID THIS PRODUCT`
|
|
);
|
|
await takeSnapshot(page, this, "price-bid-more-than");
|
|
|
|
await outBid(this.id);
|
|
|
|
return { result: false, bid_price: 0 };
|
|
}
|
|
|
|
const response = await pushPrice({
|
|
bid_id: this.id,
|
|
price: bid_price,
|
|
});
|
|
|
|
if (!response.status) {
|
|
return { result: false, bid_price: 0 };
|
|
}
|
|
|
|
this.histories = response.data;
|
|
|
|
// RESET first bid
|
|
if (this.histories.length > 0 && this.first_bid) {
|
|
this.first_bid = false;
|
|
}
|
|
|
|
return { result: true, bid_price };
|
|
}
|
|
|
|
getCloseTime = async () => {
|
|
try {
|
|
if (!this.page_context) return null;
|
|
|
|
await this.page_context.waitForSelector("#lot-closing-datetime", {
|
|
timeout: 3000,
|
|
});
|
|
|
|
return await this.page_context.$eval(
|
|
"#lot-closing-datetime",
|
|
(el) => el.value
|
|
);
|
|
} catch (error) {
|
|
return null;
|
|
}
|
|
};
|
|
|
|
getPriceWasBid = async () => {
|
|
try {
|
|
if (!this.page_context) return null;
|
|
|
|
await this.page_context.waitForSelector(
|
|
"#biddableLot form div div:nth-child(1) span span",
|
|
{ timeout: 3000 }
|
|
);
|
|
|
|
const element = await this.page_context.$(
|
|
"#biddableLot form div div:nth-child(1) span span"
|
|
);
|
|
|
|
const textPrice = await this.page_context.evaluate(
|
|
(el) => el.textContent,
|
|
element
|
|
);
|
|
|
|
return extractNumber(textPrice) || null;
|
|
} catch (error) {
|
|
return null;
|
|
}
|
|
};
|
|
|
|
async isCloseProduct() {
|
|
const close_time = await this.getCloseTime();
|
|
|
|
if (!close_time) {
|
|
const priceWasBid = await this.getPriceWasBid();
|
|
|
|
await updateStatusByPrice(this.id, priceWasBid);
|
|
return { result: true, close_time: null };
|
|
}
|
|
|
|
await delay(500);
|
|
|
|
if (!close_time || new Date(close_time).getTime() <= new Date().getTime()) {
|
|
console.log(`❌ [${this.id}] Product is close ${close_time}`);
|
|
return { result: true, close_time };
|
|
}
|
|
|
|
return { result: false, close_time };
|
|
}
|
|
|
|
async handleWritePrice(page, bid_price) {
|
|
await page.type("#price", String(bid_price));
|
|
await delay(500);
|
|
}
|
|
|
|
async placeBid(page) {
|
|
try {
|
|
await page.click("#bid-type-standard");
|
|
await delay(500);
|
|
|
|
await page.click("#btnSubmit");
|
|
await delay(1000);
|
|
|
|
await page.waitForSelector("button", { timeout: 5000 });
|
|
|
|
await delay(500);
|
|
|
|
await page.click("button");
|
|
|
|
await page.waitForNavigation({ timeout: 5000 });
|
|
|
|
await takeSnapshot(
|
|
page,
|
|
this,
|
|
"bid-success",
|
|
CONSTANTS.TYPE_IMAGE.SUCCESS
|
|
);
|
|
return true;
|
|
} catch (error) {
|
|
console.log(`❌ [${this.id}] Timeout to loading`);
|
|
await takeSnapshot(page, this, "timeout to loading");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
async handleReturnProductPage(page) {
|
|
await page.goto(this.url);
|
|
await delay(1000);
|
|
}
|
|
|
|
async handleUpdateBid({
|
|
lot_id,
|
|
close_time,
|
|
name,
|
|
current_price,
|
|
reserve_price,
|
|
}) {
|
|
const response = await updateBid(this.id, {
|
|
lot_id,
|
|
close_time,
|
|
name,
|
|
current_price,
|
|
reserve_price: Number(reserve_price) || 0,
|
|
});
|
|
|
|
if (response) {
|
|
this.lot_id = response.lot_id;
|
|
this.close_time = response.close_time;
|
|
this.start_bid_time = response.start_bid_time;
|
|
}
|
|
}
|
|
|
|
update = async () => {
|
|
if (!this.page_context) return;
|
|
|
|
const page = this.page_context;
|
|
|
|
try {
|
|
const close_time = await this.getCloseTime();
|
|
|
|
// Chờ phần tử xuất hiện trước khi lấy giá trị
|
|
await page
|
|
.waitForSelector("#priceValue", { timeout: 5000 })
|
|
.catch(() => null);
|
|
const price_value = await page
|
|
.$eval("#priceValue", (el) => el.value)
|
|
.catch(() => null);
|
|
|
|
await page.waitForSelector("#lotId", { timeout: 5000 }).catch(() => null);
|
|
const lot_id = await page
|
|
.$eval("#lotId", (el) => el.value)
|
|
.catch(() => null);
|
|
|
|
await page
|
|
.waitForSelector("#placebid-sticky > div:nth-child(2) > div > h3", {
|
|
timeout: 5000,
|
|
})
|
|
.catch(() => null);
|
|
const name = await page
|
|
.$eval(".dls-heading-3.lotPageTitle", (el) => el.innerText)
|
|
.catch(() => null);
|
|
|
|
await page
|
|
.waitForSelector(
|
|
"#biddableLot > form > div > div:nth-child(1) > div:nth-child(1) > div:nth-child(2) > div > span > span",
|
|
{ timeout: 5000 }
|
|
)
|
|
.catch(() => null);
|
|
const current_price = await page
|
|
.$eval(
|
|
"#biddableLot > form > div > div:nth-child(1) > div:nth-child(1) > div:nth-child(2) > div > span > span",
|
|
(el) => el.innerText
|
|
)
|
|
.catch(() => null);
|
|
|
|
console.log(
|
|
`📌 [${this.id}] Product Info: Lot ID: ${lot_id}, Name: ${name}, Current Price: ${current_price}, Reserve price: ${price_value}`
|
|
);
|
|
|
|
const data = removeFalsyValues(
|
|
{
|
|
lot_id,
|
|
reserve_price: price_value,
|
|
close_time: close_time ? String(close_time) : null,
|
|
name,
|
|
current_price: current_price ? extractNumber(current_price) : null,
|
|
},
|
|
["close_time"]
|
|
);
|
|
|
|
this.handleUpdateBid(data);
|
|
|
|
return { price_value, lot_id, name, current_price };
|
|
} catch (error) {
|
|
console.error(`🚨 Error updating product info: ${error.message}`);
|
|
return null;
|
|
}
|
|
};
|
|
|
|
action = async () => {
|
|
try {
|
|
const page = this.page_context;
|
|
|
|
await this.gotoLink();
|
|
console.log(`🌍 [${this.id}] Navigated to link.`);
|
|
|
|
await delay(1000);
|
|
|
|
const { close_time, ...isCloseProduct } = await this.isCloseProduct();
|
|
if (isCloseProduct.result) {
|
|
console.log(
|
|
`❌ [${this.id}] The product is closed, cannot place a bid.`
|
|
);
|
|
return;
|
|
}
|
|
|
|
await delay(500);
|
|
|
|
const { price_value } = await this.update();
|
|
if (!price_value) return;
|
|
|
|
const { result, bid_price } = await this.validate({ page, price_value });
|
|
if (!result) {
|
|
console.log(
|
|
`❌ [${this.id}] Validation failed. Unable to proceed with bidding.`
|
|
);
|
|
return;
|
|
}
|
|
|
|
const bidHistoriesItem = _.maxBy(this.histories, "price");
|
|
if (bidHistoriesItem && bidHistoriesItem.price === this.current_price) {
|
|
console.log(
|
|
`🔄 [${this.id}] You have already bid on this item! (Bid Price: ${bidHistoriesItem.price})`
|
|
);
|
|
return;
|
|
}
|
|
|
|
if (price_value != bid_price) {
|
|
console.log(
|
|
`✍️ [${this.id}] Updating bid price from ${price_value} → ${bid_price}`
|
|
);
|
|
await this.handleWritePrice(page, bid_price);
|
|
}
|
|
|
|
console.log(`🚀 [${this.id}] Placing the bid...`);
|
|
const resultPlaceBid = await this.placeBid(page);
|
|
if (!resultPlaceBid) {
|
|
console.log(`❌ [${this.id}] Error occurred while placing the bid.`);
|
|
await takeSnapshot(page, this, "place-bid-action");
|
|
return;
|
|
}
|
|
|
|
console.log(
|
|
`✅ [${this.id}] Bid placed successfully! 🏆 Bid Price: ${bid_price}, Closing Time: ${close_time}`
|
|
);
|
|
await this.handleReturnProductPage(page);
|
|
} catch (error) {
|
|
console.error(
|
|
`🚨 [${this.id}] Error navigating the page: ${error.message}`
|
|
);
|
|
}
|
|
};
|
|
}
|