409 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
			
		
		
	
	
			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}`);
 | 
						|
    }
 | 
						|
  };
 | 
						|
}
 |