diff --git a/auto-bid-admin/src/components/bid/show-histories-modal.tsx b/auto-bid-admin/src/components/bid/show-histories-modal.tsx index 3cddae8..1d89d1a 100644 --- a/auto-bid-admin/src/components/bid/show-histories-modal.tsx +++ b/auto-bid-admin/src/components/bid/show-histories-modal.tsx @@ -1,44 +1,60 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ -import { Modal, ModalProps, Table } from '@mantine/core'; -import { IBid } from '../../system/type'; -import { formatTime } from '../../utils'; +import { Modal, ModalProps, Table } from "@mantine/core"; +import { IBid } from "../../system/type"; +import { formatTime } from "../../utils"; +import _ from "lodash"; export interface IShowHistoriesModalModalProps extends ModalProps { - data: IBid | null; - onUpdated?: () => void; + data: IBid | null; + onUpdated?: () => void; } -export default function ShowHistoriesModal({ data, onUpdated, ...props }: IShowHistoriesModalModalProps) { - const rows = data?.histories.map((element) => ( - - {element.id} - {element.price} - {formatTime(new Date(element.created_at).toUTCString(), 'HH:mm:ss DD/MM/YYYY')} - - )); +export default function ShowHistoriesModal({ + data, + onUpdated, + ...props +}: IShowHistoriesModalModalProps) { + const sortedHistories = _.orderBy(data?.histories || [], ["price"], ["desc"]); - return ( - Histories} centered> - - - - ID - Price - Created at - - - - {data && data.histories.length <= 0 ? ( - - - None - - - ) : ( - rows - )} - -
-
- ); + const rows = sortedHistories.map((element) => ( + + {element.id} + {element.price} + + {formatTime( + new Date(element.created_at).toUTCString(), + "HH:mm:ss DD/MM/YYYY", + )} + + + )); + return ( + Histories} + centered + > + + + + ID + Price + Created at + + + + {data && data.histories.length <= 0 ? ( + + + None + + + ) : ( + rows + )} + +
+
+ ); } diff --git a/auto-bid-server/src/modules/bids/entities/send-message-histories.entity.ts b/auto-bid-server/src/modules/bids/entities/send-message-histories.entity.ts index a1317d5..ec5889a 100644 --- a/auto-bid-server/src/modules/bids/entities/send-message-histories.entity.ts +++ b/auto-bid-server/src/modules/bids/entities/send-message-histories.entity.ts @@ -1,16 +1,14 @@ import { Column, Entity, - JoinColumn, ManyToOne, PrimaryGeneratedColumn, Unique, } from 'typeorm'; -import { Timestamp } from './timestamp'; import { Bid } from './bid.entity'; +import { Timestamp } from './timestamp'; @Entity('send_message_histories') -@Unique(['max_price', 'type', 'reserve_price', 'bid']) export class SendMessageHistory extends Timestamp { @PrimaryGeneratedColumn('increment') id: number; diff --git a/auto-bid-server/src/modules/bids/services/bid-histories.service.ts b/auto-bid-server/src/modules/bids/services/bid-histories.service.ts index 438cce7..d89e2bc 100644 --- a/auto-bid-server/src/modules/bids/services/bid-histories.service.ts +++ b/auto-bid-server/src/modules/bids/services/bid-histories.service.ts @@ -100,8 +100,15 @@ export class BidHistoriesService { }); // Nếu đây là lần đặt giá đầu tiên, cập nhật cờ `first_bid` thành false + // Nếu là web all bids thì không cần set về false if (response.length === 1) { - this.bidsService.bidsRepo.update(bid_id, { first_bid: false }); + this.bidsService.bidsRepo.update(bid_id, { + first_bid: ['https://www.allbids.com.au'].includes( + bid.web_bid.origin_url, + ) + ? true + : false, + }); } // Gửi thông tin bid đến bot telegram @@ -112,10 +119,14 @@ export class BidHistoriesService { this.eventEmitter.emit(Event.BID_SUBMITED, botData); // Lưu message đã gửi để theo dõi - this.sendMessageHistoriesService.sendMessageRepo.save({ - message: this.botTelegramApi.formatBidMessage(botData), - bid, - }); + try { + this.sendMessageHistoriesService.sendMessageRepo.save({ + message: this.botTelegramApi.formatBidMessage(botData), + bid, + }); + } catch (error) { + console.log(`BidHistoriesService - create: ${error?.message}`); + } // Kiểm tra nếu trạng thái bid thay đổi sau khi lưu, phát sự kiện cập nhật tất cả bid const bidUpdated = await this.bidsService.bidsRepo.findOne({ diff --git a/auto-bid-tool/index.js b/auto-bid-tool/index.js index e420ed3..2d9dae8 100644 --- a/auto-bid-tool/index.js +++ b/auto-bid-tool/index.js @@ -77,7 +77,7 @@ const addProductTab = (data) => { if (children.length === 0) { console.warn( - `⚠️ No children found for bid id ${web.id}, skipping addProductTab` + `⚠️ No children found for bid id ${web.id}, skipping addProductTab`, ); return; } @@ -91,7 +91,7 @@ const addProductTab = (data) => { children.forEach((newChild) => { const existingChildIndex = updatedChildren.findIndex( - (c) => c.id === newChild.id + (c) => c.id === newChild.id, ); if (existingChildIndex !== -1) { @@ -128,7 +128,7 @@ const tracking = async () => { MANAGER_BIDS.filter((bid) => !bid.page_context).map((apiBid) => { console.log(`🎧 Listening to events for API Bid ID: ${apiBid.id}`); return apiBid.listen_events(); - }) + }), ); await Promise.allSettled( @@ -137,12 +137,12 @@ const tracking = async () => { return (apiBid.onCloseLogin = (data) => { // Loại bỏ class hiện có. Tạo tiền đề cho việc tạo đối tượng mới lại MANAGER_BIDS = MANAGER_BIDS.filter( - (item) => item.id !== data.id && item.type !== data.type + (item) => item.id !== data.id && item.type !== data.type, ); addProductTab(data); }); - }) + }), ); Promise.allSettled( @@ -151,7 +151,7 @@ const tracking = async () => { console.log( `🔍 [${ productTab.id - }] Current URL: ${await productTab.page_context?.url?.()}` + }] Current URL: ${await productTab.page_context?.url?.()}`, ); // Xác định parent context @@ -160,7 +160,7 @@ const tracking = async () => { productTab.parent_browser_context = parent?.browser_context; if (!productTab.parent_browser_context) { console.log( - `⏳ Waiting for parent process... (Product ID: ${productTab.id})` + `⏳ Waiting for parent process... (Product ID: ${productTab.id})`, ); return; } @@ -169,14 +169,14 @@ const tracking = async () => { // Thời điểm tracking liên tục const earlyTrackingTime = subtractSeconds( productTab.close_time, - productTab.getEarlyTrackingSeconds() || 0 + productTab.getEarlyTrackingSeconds() || 0, ); // Check không mở tab nếu chưa đến giờ if (productTab.close_time && !isTimeReached(earlyTrackingTime)) { console.log( `⏳ [${productTab.id}] Early tracking time not reached yet. ` + - `Waiting until ${earlyTrackingTime} (current time: ${new Date().toISOString()})` + `Waiting until ${earlyTrackingTime} (current time: ${new Date().toISOString()})`, ); return; } @@ -184,7 +184,7 @@ const tracking = async () => { // Kết nối Puppeteer nếu chưa có page_context if (!productTab.page_context) { console.log( - `🔌 Connecting to page for Product ID: ${productTab.id}` + `🔌 Connecting to page for Product ID: ${productTab.id}`, ); await productTab.puppeteer_connect(); } @@ -194,7 +194,7 @@ const tracking = async () => { if (global[`IS_PLACE_BID-${productTab.id}`]) return; console.log( - `🔄 Redirecting to new URL for Product ID: ${productTab.id}` + `🔄 Redirecting to new URL for Product ID: ${productTab.id}`, ); await productTab.gotoLink(); } @@ -205,14 +205,14 @@ const tracking = async () => { await productTab.update(); } else { console.log( - `⏳ Product ID: ${productTab.id} was updated recently. Skipping update.` + `⏳ Product ID: ${productTab.id} was updated recently. Skipping update.`, ); } // Chờ first bid if (!productTab.first_bid) { console.log( - `🎯 Waiting for first bid for Product ID: ${productTab.id}` + `🎯 Waiting for first bid for Product ID: ${productTab.id}`, ); return; } @@ -223,13 +223,13 @@ const tracking = async () => { !isTimeReached(productTab.start_bid_time) ) { console.log( - `⏳ Not yet time to bid. Skipping Product ID: ${productTab.id}` + `⏳ Not yet time to bid. Skipping Product ID: ${productTab.id}`, ); return; } await productTab.action(); - }) + }), ); // Dọn dẹp tab không dùng @@ -250,7 +250,7 @@ const tracking = async () => { console.log( `⏳ Waiting ${ configs.AUTO_TRACKING_DELAY / 1000 - } seconds before the next iteration...` + } seconds before the next iteration...`, ); await delay(configs.AUTO_TRACKING_DELAY); } @@ -294,7 +294,7 @@ const clearLazyTab = async () => { const earlyTrackingTime = subtractSeconds( productTab.close_time, - productTab.getEarlyTrackingSeconds() || 0 + productTab.getEarlyTrackingSeconds() || 0, ); if (!isTimeReached(earlyTrackingTime)) { @@ -314,7 +314,7 @@ const clearLazyTab = async () => { (item) => item.model === modelProductTab && isTimeReached(item.close_time) && - item.status === "biding" + item.status === "biding", ); if (productWatingUpdate) { @@ -347,12 +347,12 @@ const clearLazyTab = async () => { await Promise.race([ page.close(), new Promise((_, reject) => - setTimeout(() => reject(new Error("Close timeout")), 3000) + setTimeout(() => reject(new Error("Close timeout")), 3000), ), ]); } catch (closeErr) { console.warn( - `⚠️ Error closing page ${pageUrl}: ${closeErr.message}` + `⚠️ Error closing page ${pageUrl}: ${closeErr.message}`, ); } } @@ -369,7 +369,7 @@ const clearLazyTab = async () => { if (await item.isLazy()) { safeClosePage(item); } - }) + }), ); } catch (err) { console.error("❌ Error in clearLazyTab:", err.message); @@ -396,18 +396,18 @@ const workTracking = async () => { } catch (error) { console.error( `[❌ ERROR] Snapshot failed for Product ID: ${item.id}`, - error + error, ); } finally { activeTasks.delete(item.id); } - }) - ) + }), + ), ); } catch (error) { console.error( `[❌ ERROR] Work tracking failed: ${error.message}\n`, - error.stack + error.stack, ); } }; @@ -441,10 +441,10 @@ const trackingLoginStatus = async () => { console.warn( `[⚠️ WARN] Failed to check login for bid ${ item?.id || "unknown" - }: ${err.message}` + }: ${err.message}`, ); } - }) + }), ); // Optional: log summary @@ -455,7 +455,7 @@ const trackingLoginStatus = async () => { } catch (error) { console.error( `[❌ ERROR] Login status tracking failed: ${error.message}\n`, - error.stack + error.stack, ); } }; diff --git a/auto-bid-tool/models/allbids.com.au/allbids-product-bid.js b/auto-bid-tool/models/allbids.com.au/allbids-product-bid.js index 703872d..2175bed 100644 --- a/auto-bid-tool/models/allbids.com.au/allbids-product-bid.js +++ b/auto-bid-tool/models/allbids.com.au/allbids-product-bid.js @@ -11,6 +11,8 @@ import { import { ProductBid } from "../product-bid.js"; export class AllbidsProductBid extends ProductBid { + price_to_bid = 0; + constructor({ ...prev }) { super(prev); } @@ -20,7 +22,7 @@ export class AllbidsProductBid extends ProductBid { 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 + () => window.angular !== undefined, ); const auctionData = await this.page_context.evaluate(() => { @@ -56,7 +58,7 @@ export class AllbidsProductBid extends ProductBid { return auctionData; } catch (error) { console.log( - `[${this.id}] Error in waitForApiResponse: ${error?.message}` + `[${this.id}] Error in waitForApiResponse: ${error?.message}`, ); } } @@ -66,7 +68,7 @@ export class AllbidsProductBid extends ProductBid { 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 + () => window.angular !== undefined, ); const historiesData = await this.page_context.evaluate((model) => { @@ -103,7 +105,7 @@ export class AllbidsProductBid extends ProductBid { return historiesData; } catch (error) { console.log( - `[${this.id}] Error in waitForApiResponse: ${error?.message}` + `[${this.id}] Error in waitForApiResponse: ${error?.message}`, ); } } @@ -158,8 +160,8 @@ export class AllbidsProductBid extends ProductBid { } }, this.model, - this.max_price, - configs.WEB_CONFIGS.ALLBIDS.PLACE_BID + this.price_to_bid, + configs.WEB_CONFIGS.ALLBIDS.PLACE_BID, ); return response; @@ -189,7 +191,7 @@ export class AllbidsProductBid extends ProductBid { } }, this.model, - configs.WEB_CONFIGS.ALLBIDS.PLACE_BID + configs.WEB_CONFIGS.ALLBIDS.PLACE_BID, ); return response; @@ -200,20 +202,20 @@ export class AllbidsProductBid extends ProductBid { console.log(`🔄 [${this.id}] Call update for ID: ${this.id}`); - // 📌 Chờ phản hồi API từ trang, tối đa 10 giây + // Chờ phản hồi API từ trang, tối đa 10 giây const result = await this.waitForApiResponse(); const historiesData = await this.getHistoriesData(); console.log({ historiesData }); - // 📌 Nếu không có dữ liệu trả về thì dừng + // 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 + // 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, @@ -231,12 +233,12 @@ export class AllbidsProductBid extends ProductBid { competor_histories: historiesData, }, }, - ["close_time"] + ["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 + // Gửi dữ liệu cập nhật lên hệ thống await this.handleUpdateBid(data); console.log("✅ Update successful!"); @@ -251,7 +253,7 @@ export class AllbidsProductBid extends ProductBid { currentHigherBid?.amount >= this.max_price ) { console.log( - `⚠️ [${this.id}] ${currentHigherBid?.userName} is highter price` + `⚠️ [${this.id}] ${currentHigherBid?.userName} is highter price`, ); outBid(this.id); @@ -263,7 +265,7 @@ export class AllbidsProductBid extends ProductBid { async handlePlaceBid() { if (!this.page_context) { console.log( - `⚠️ [${this.id}] No page context found, aborting bid process.` + `⚠️ [${this.id}] No page context found, aborting bid process.`, ); return; } @@ -286,14 +288,14 @@ export class AllbidsProductBid extends ProductBid { isTimeReached(new Date(response.aucCloseUtc).toUTCString()) ) { console.log( - `⚠️ [${this.id}] Outbid detected, calling outBid function.` + `⚠️ [${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) { + if (this.current_price > this.max_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 } @@ -303,7 +305,7 @@ export class AllbidsProductBid extends ProductBid { console.log( `⏳ [${this.id}] Not yet time to bid. Skipping Product: ${ this.name || "None" - }` + }`, ); return; } @@ -316,8 +318,19 @@ export class AllbidsProductBid extends ProductBid { ) { console.log( `⚠️ [${this.id}] No response or myBid equals max_price:`, - response + 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 + + if ( + response?.aucCurrentBidder != this.web_bid.display_name && + this.max_price >= response.aucCurrentBid + ) { + console.log( + `[${this.id}] The ${response?.aucCurrentBidder} bidded higher price ${response.aucCurrentBid}`, + ); + outBid(this.id); + return; + } 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 } @@ -325,16 +338,28 @@ export class AllbidsProductBid extends ProductBid { console.log(`📜 [${this.id}] Current bid history:`, this.histories); if ( - bidHistoriesItem && - bidHistoriesItem?.price === this.current_price && - this.max_price == response?.aucUserMaxBid + (bidHistoriesItem && + bidHistoriesItem?.price === this.current_price && + this.max_price == response?.aucUserMaxBid) || + response?.aucCurrentBidder === this.web_bid.display_name ) { console.log( - `🔄 [${this.id}] You have already bid on this item! (Bid Price: ${bidHistoriesItem.price})` + `🔄 [${this.id}] You have already bid on this item! (Bid Price: ${bidHistoriesItem.price})`, ); return; } + this.price_to_bid = + (response.aucCurrentBid || this.current_price) + this.plus_price + 1; + + if (this.price_to_bid > this.max_price) { + console.log( + `[${this.id}] The ${response?.aucCurrentBidder} bidded higher price ${response.aucCurrentBid}`, + ); + outBid(this.id); + return; + } + console.log("---------------------BIDDING--------------------"); if (this.isSandbox()) { @@ -358,12 +383,13 @@ export class AllbidsProductBid extends ProductBid { // const { aucUserMaxBid } = await this.waitForApiResponse(); console.log(`📡 [${this.id}] API Response received:`, { aucUserMaxBid: this.max_price, + priceToBid: this.price_to_bid, }); // 📌 Kiểm tra trạng thái đấu giá từ API if ( data?.bidResult?.result || - data?.bidResult?.bidAmount == this.max_price + data?.bidResult?.bidAmount <= this.max_price ) { console.log(`📸 [${this.id}] Taking bid success snapshot...`); @@ -371,14 +397,14 @@ export class AllbidsProductBid extends ProductBid { pushPrice({ bid_id: this.id, - price: this.max_price, + price: this.price_to_bid, }); await takeSnapshot( this.page_context, this, "bid-success", - CONSTANTS.TYPE_IMAGE.SUCCESS + CONSTANTS.TYPE_IMAGE.SUCCESS, ); console.log(`✅ [${this.id}] Bid placed successfully!`); @@ -386,7 +412,7 @@ export class AllbidsProductBid extends ProductBid { } console.log( - `⚠️ [${this.id}] Bid action completed, but status is still "None".` + `⚠️ [${this.id}] Bid action completed, but status is still "None".`, ); }