bid-tool/auto-bid-tool/models/api-bid.js

325 lines
8.3 KiB
JavaScript

import * as fs from "fs";
import path from "path";
import BID_TYPE from "../system/bid-type.js";
import browser from "../system/browser.js";
import CONSTANTS from "../system/constants.js";
import {
findEarlyLoginTime,
findNearestClosingChild,
getPathLocalData,
getPathProfile,
isTimeReached,
sanitizeFileName,
subtractSeconds,
} from "../system/utils.js";
import { Bid } from "./bid.js";
export class ApiBid extends Bid {
id;
account;
children = [];
children_processing = [];
created_at;
updated_at;
origin_url;
active;
// browser_context;
username;
password;
early_tracking_seconds;
snapshot_at;
constructor({
url,
username,
password,
id,
children,
created_at,
updated_at,
origin_url,
active,
early_tracking_seconds,
snapshot_at,
}) {
super(BID_TYPE.API_BID, url);
this.created_at = created_at;
this.updated_at = updated_at;
this.children = children;
this.origin_url = origin_url;
this.active = active;
this.username = username;
this.password = password;
this.early_tracking_seconds = early_tracking_seconds;
this.snapshot_at = snapshot_at;
this.id = id;
}
setNewData({
url,
username,
password,
id,
children,
created_at,
updated_at,
origin_url,
active,
early_tracking_seconds,
snapshot_at,
}) {
this.created_at = created_at;
this.updated_at = updated_at;
this.children = children;
this.origin_url = origin_url;
this.active = active;
this.username = username;
this.password = password;
this.url = url;
this.early_tracking_seconds = early_tracking_seconds;
this.snapshot_at = snapshot_at;
}
puppeteer_connect = async () => {
this.browser_context = await browser.createBrowserContext();
const page = await this.browser_context.newPage();
this.page_context = page;
await this.restoreContext();
};
async handlePrevListen() {
console.log(`👂 [${this.id}] Start handlePrevListen...`);
// Chỉ bắt đầu check khi ảnh đã được chụp
if (this.snapshot_at) {
const nearestCloseTime = findNearestClosingChild(this);
if (!nearestCloseTime) {
console.log(`❌ [${this.id}] No nearest closing child found.`);
return false;
}
const { close_time } = nearestCloseTime;
console.log(`📅 [${this.id}] Nearest close_time: ${close_time}`);
const timeToTracking = subtractSeconds(
close_time,
this.early_tracking_seconds || 0
);
console.log(
`🕰️ [${this.id}] Time to tracking: ${new Date(
timeToTracking
).toISOString()}`
);
if (!isTimeReached(timeToTracking)) {
console.log(`⏳ [${this.id}] Not time to track yet.`);
return false;
}
}
console.log(`🔌 [${this.id}] Connecting to puppeteer...`);
await this.puppeteer_connect();
console.log(`✅ [${this.id}] Connected. Executing actions...`);
await this.action();
console.log(`🎯 [${this.id}] handlePrevListen completed.`);
return true;
}
async isLazy() {
// Nếu chưa có ảnh chụp working => tab not lazy
if (!this.snapshot_at) return false;
const nearestCloseTime = findNearestClosingChild(this);
// Nếu không có nearest close => tab lazy
if (!nearestCloseTime) return true;
const { close_time } = nearestCloseTime;
const timeToTracking = subtractSeconds(
close_time,
this.early_tracking_seconds || 0
);
// Nếu chưa đến giờ tracking => tab lazy
if (!isTimeReached(timeToTracking)) return true;
// Các trường hợp còn lại => not lazy
return false;
}
listen_events = async () => {
if (this.page_context) return;
// await this.puppeteer_connect();
// await this.action();
const results = await this.handlePrevListen();
if (!results) return;
await this.saveContext();
};
async saveContext() {
if (!this.browser_context || !this.page_context) return;
try {
const cookies = await this.browser_context.cookies();
const localStorageData = await this.page_context.evaluate(() =>
JSON.stringify(localStorage)
);
const contextData = {
cookies,
localStorage: localStorageData,
};
const dirPath = path.join(CONSTANTS.PROFILE_PATH);
if (!fs.existsSync(dirPath)) {
fs.mkdirSync(dirPath, { recursive: true });
console.log(`📂 [${this.id}] Save at folder: ${dirPath}`);
}
fs.writeFileSync(
path.join(dirPath, sanitizeFileName(this.origin_url) + ".json"),
JSON.stringify(contextData, null, 2)
);
console.log(`✅ [${this.id}] Context saved!`);
} catch (error) {
console.log(`[${this.id}] Save Context: , ${error.message}`);
}
}
async restoreContext() {
if (!this.browser_context || !this.page_context) return;
const filePath = getPathProfile(this.origin_url);
if (!fs.existsSync(filePath)) return;
const contextData = JSON.parse(fs.readFileSync(filePath, "utf8"));
// Restore Cookies
await this.page_context.setCookie(...contextData.cookies);
console.log(`🔄 [${this.id}] Context restored!`);
}
async onCloseLogin() {}
async isTimeToLogin() {
const earlyLoginTime = findEarlyLoginTime(this);
return earlyLoginTime && isTimeReached(earlyLoginTime);
}
async saveCodeToLocal({ name, code }) {
try {
const filePath = getPathLocalData(this.origin_url); // file path
const dirPath = path.dirname(filePath); // lấy thư mục cha
// kiểm tra folder chứa file đã tồn tại chưa
if (!fs.existsSync(dirPath)) {
fs.mkdirSync(dirPath, { recursive: true });
}
// ghi file
fs.writeFileSync(
filePath,
JSON.stringify({ name, code, time: Date.now() }, null, 2) // format JSON đẹp
);
} catch (error) {
console.log(
`%cerror [${this.id}] models/api-bid.js line:149`,
"color: red; display: block; width: 100%;",
error
);
}
}
async clearCodeFromLocal() {
try {
const filePath = getPathLocalData(this.origin_url); // file path
const dirPath = path.dirname(filePath); // lấy thư mục cha
// kiểm tra folder chứa file đã tồn tại chưa
if (!fs.existsSync(dirPath)) {
fs.mkdirSync(dirPath, { recursive: true });
}
// ghi file
fs.writeFileSync(
filePath,
JSON.stringify({}, null, 2) // format JSON đẹp
);
} catch (error) {
console.log(
`%cerror [${this.id}] models/api-bid.js line:187`,
"color: red; display: block; width: 100%;",
error
);
}
}
async loadCodeFromLocal() {
try {
const filePath = getPathLocalData(this.origin_url);
if (!fs.existsSync(filePath)) {
console.warn(
`%cwarn [${this.id}] models/api-bid.js`,
"color: orange; display: block; width: 100%;",
`File not found: ${filePath}`
);
return null; // hoặc {} tùy bạn muốn trả gì
}
const fileContent = fs.readFileSync(filePath, "utf-8");
const data = JSON.parse(fileContent);
return data; // { name, code, time }
} catch (error) {
console.error(
`%cerror [${this.id}] models/api-bid.js`,
"color: red; display: block; width: 100%;",
error
);
return null;
}
}
async isCodeValid(minutes = 8) {
try {
const data = await this.loadCodeFromLocal();
if (!data || !data.time) {
return false;
}
const now = Date.now();
const timeDiff = now - data.time; // tính chênh lệch thời gian (ms)
const validDuration = minutes * 60 * 1000; // phút -> mili giây
return timeDiff <= validDuration;
} catch (error) {
console.error(
`%cerror [${this.id}] models/api-bid.js`,
"color: red; display: block; width: 100%;",
error
);
return false;
}
}
}