Files
zn-ai/src/main/scripts/fg_trace.mjs
2026-03-12 17:05:16 +08:00

142 lines
4.7 KiB
JavaScript

import { chromium } from 'playwright';
import log from 'electron-log';
import tabsPkg from './common/tabs.js';
const { preparePage, safeDisconnectBrowser } = tabsPkg;
const parseDateInput = (dateStr) => {
if (!dateStr) return null;
if (typeof dateStr === 'number' && Number.isFinite(dateStr)) return new Date(dateStr);
if (dateStr instanceof Date && !Number.isNaN(dateStr.getTime())) return dateStr;
const raw = String(dateStr).trim();
const ymdMatch = raw.match(/(\d{4}-\d{2}-\d{2})/);
if (ymdMatch) return new Date(`${ymdMatch[1]}T00:00:00`);
const mmddMatch = raw.match(/(\d{2}-\d{2})/);
if (mmddMatch) {
const year = new Date().getFullYear();
return new Date(`${year}-${mmddMatch[1]}T00:00:00`);
}
return null;
};
const formatMMDD = (d) => {
const mm = String(d.getMonth() + 1).padStart(2, '0');
const dd = String(d.getDate()).padStart(2, '0');
return `${mm}-${dd}`;
};
const buildDateList = (startDate, endDate) => {
const start = parseDateInput(startDate);
const end = parseDateInput(endDate || startDate);
if (!start || !end) return [];
const from = start <= end ? start : end;
const to = start <= end ? end : start;
const dates = [];
const cur = new Date(from.getTime());
while (cur <= to) {
dates.push(formatMMDD(cur));
cur.setDate(cur.getDate() + 1);
}
return dates;
};
const findHeaderDateIndex = async (container, mmdd) => {
for (let attempt = 0; attempt < 10; attempt++) {
const headerCells = container.locator(
"xpath=.//div[contains(@class,'headerRow')]/div[contains(@class,'headerContent')]",
);
const count = await headerCells.count();
for (let i = 0; i < count; i++) {
const text = (await headerCells.nth(i).innerText()).replace(/\s+/g, ' ').trim();
if (text.includes(mmdd)) return i;
}
await container.evaluate((el) => {
el.scrollLeft += 600;
});
await container.page().waitForTimeout(120);
}
return -1;
};
const toggleRoomByDateIndex = async (container, { roomType, dateIndex, operation }) => {
const roomAnchor = container.getByText(roomType, { exact: true }).first();
await roomAnchor.scrollIntoViewIfNeeded();
const row = roomAnchor.locator(
"xpath=ancestor::*[.//div[contains(@class,'boardRow')]][1]",
);
const boardRow = row.locator("xpath=.//div[contains(@class,'boardRow')]").first();
const boardColPosition = dateIndex + 2;
const dayColumn = boardRow.locator(`xpath=./div[${boardColPosition}]`);
const targetStateClass = operation === 'open' ? 'error' : 'success';
const expectedCurrentLabel = operation === 'open' ? '满' : '有';
const barByLabel = dayColumn
.locator(
`xpath=.//*[ (contains(@class,'success') or contains(@class,'error')) and .//*[contains(normalize-space(),'${expectedCurrentLabel}')] ]//*[contains(@class,'bar')]`,
)
.first();
const barByClass = dayColumn
.locator(`xpath=.//*[contains(@class,'${targetStateClass}')]//*[contains(@class,'bar')]`)
.first();
const bar = (await barByLabel.count()) > 0 ? barByLabel : barByClass;
await bar.waitFor({ state: 'visible' });
await bar.scrollIntoViewIfNeeded();
await bar.click();
};
(async () => {
let browser;
try {
const prepared = await preparePage(chromium);
browser = prepared.browser;
const page = prepared.page;
const targetUrl = 'https://hotel.fliggy.com/ebooking/hotelBaseInfoUv.htm#/ebk/homeV1';
const currentUrl = page.url();
if (!currentUrl || !currentUrl.startsWith(targetUrl)) {
await page.goto(targetUrl);
}
await page.waitForTimeout(4000 + Math.random() * 300);
await page.getByRole('menuitem', { name: '房价房量管理' }).click();
await page.waitForTimeout(4000 + Math.random() * 1000);
await page.getByText('房价房量日历').click();
const roomType = process.env.ROOM_TYPE;
const startDate = process.env.START_DATE;
const endDate = process.env.END_DATE;
const operation = process.env.OPERATION === 'close' ? 'close' : 'open';
if (!roomType || !startDate) {
log.info('ROOM_TYPE/START_DATE not provided, skip room toggle.');
return;
}
const container = page.locator('#price-reserve-table-container');
await container.waitFor({ state: 'visible' });
const dateList = buildDateList(startDate, endDate);
for (const mmdd of dateList) {
const dateIndex = await findHeaderDateIndex(container, mmdd);
if (dateIndex < 0) {
throw new Error(`Date not found in header: ${mmdd}`);
}
await toggleRoomByDateIndex(container, { roomType, dateIndex, operation });
await page.waitForTimeout(600 + Math.random() * 600);
}
} catch (error) {
log.error(error);
process.exitCode = 1;
}finally {
await safeDisconnectBrowser(browser);
}
})();