250 lines
		
	
	
		
			8.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
			
		
		
	
	
			250 lines
		
	
	
		
			8.6 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 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');
 | 
						|
 | 
						|
            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 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(`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('❌ 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('#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: 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();
 | 
						|
 | 
						|
            await delay(1000);
 | 
						|
 | 
						|
            const { close_time, ...isCloseProduct } = await this.isCloseProduct();
 | 
						|
            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;
 | 
						|
            }
 | 
						|
 | 
						|
            const bidHistoriesItem = _.maxBy(this.histories, 'price');
 | 
						|
 | 
						|
            if (bidHistoriesItem && bidHistoriesItem.price === this.current_price) {
 | 
						|
                console.log(`🔄 You have already bid on this item! (Bid Price: ${bidHistoriesItem.price})`);
 | 
						|
                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');
 | 
						|
                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}`);
 | 
						|
        }
 | 
						|
    };
 | 
						|
}
 |