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 GraysProductBid extends ProductBid { constructor({ ...prev }) { super(prev); } async validate({ page, price_value }) { if (!this.start_bid_time || !isTimeReached(this.start_bid_time)) { console.log("It's not time yet ❌"); return { result: false, bid_price: 0 }; } if (!isNumber(price_value)) { console.log("Can't get PRICE_VALUE ❌"); await takeSnapshot(page, this, 'price-value-null'); // await safeClosePage(this); return { result: false, bid_price: 0 }; } const bid_price = this.plus_price + Number(price_value); if (bid_price > this.max_price) { console.log('PRICE BID is more than MAX_VALUE => STOP BID THIS PRODUCT ❌'); await takeSnapshot(page, this, 'price-bid-more-than'); // await safeClosePage(this); await outBid(this.id); return { result: false, bid_price: 0 }; } const response = await pushPrice({ bid_id: this.id, price: bid_price, }); if (!response.status) { // await this.handleReturnProductPage(page); // await safeClosePage(this); 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(page) { 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(`Product is close ${close_time} ❌`); // await safeClosePage(this); 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({ error: error.message }); console.log('❌ Timeout to loading'); await takeSnapshot(page, this, 'timeout to loading'); // await safeClosePage(this); return false; } } async handleReturnProductPage(page) { await page.goto(this.url); await delay(1000); } async handleUpdateBid({ lot_id, close_time, name, current_price, reserve_price }) { // if (close_time && this.close_time == close_time) return; 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; // const close_time = await this.getCloseTime(); // const price_value = (await page.$eval('#priceValue', (el) => el.value)) || null; // const lot_id = await page.$eval('#lotId', (el) => el.value); // const name = (await page.$eval('#placebid-sticky > div:nth-child(2) > div > h3', (el) => el.innerText)) || 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)) || null; // console.log(`📌 Product Info: Lot ID: ${lot_id}, Name: ${name}, Current Price: ${current_price}, Reserve price ${price_value}`); // this.handleUpdateBid({ lot_id, reserve_price: price_value, close_time, name, current_price: current_price ? extractNumber(current_price) : null }); // return { price_value, lot_id, name, current_price }; // }; 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('#placebid-sticky > div:nth-child(2) > div > h3', (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(`📌 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: String(close_time), name, current_price: current_price ? extractNumber(current_price) : null, }); 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(); await delay(1000); const { close_time, ...isCloseProduct } = await this.isCloseProduct(page); if (isCloseProduct.result) { console.log('❌ 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('❌ Validation failed. Unable to proceed with bidding.'); return; } if (price_value != bid_price) { console.log(`✍️ Updating bid price from ${price_value} → ${bid_price}`); await this.handleWritePrice(page, bid_price); } console.log('🚀 Placing the bid...'); const resultPlaceBid = await this.placeBid(page); if (!resultPlaceBid) { console.log('❌ Error occurred while placing the bid.'); await takeSnapshot(page, this, 'place-bid-action'); // await safeClosePage(this); return; } console.log(`✅ Bid placed successfully! 🏆 Bid Price: ${bid_price}, Closing Time: ${close_time}`); await this.handleReturnProductPage(page); } catch (error) { console.error(`🚨 Error navigating the page: ${error.message}`); // safeClosePage(this); } }; }