import type { Page } from "playwright"; /** 官网匿名报价次数上限(cookie / 邮箱 / IP 维度) */ export async function isFlockQuoteLimitReached(page: Page): Promise { const body = ((await page.locator("body").innerText().catch(() => "")) || "") .slice(0, 8_000); return isFlockQuoteLimitText(body); } export function isFlockQuoteLimitText(body: string): boolean { return ( /quote\s*limit|reached\s*(your\s*)?limit|too\s*many\s*quotes/i.test(body) || /获取报价.*上限|报价.*达到.*上限|已达.*上限|次数.*上限|达到.*获取报价.*上限/i.test( body, ) || /Thank you for your interest/i.test(body) ); } export function describeFlockPagePhase(body: string): string { if (isFlockQuoteLimitText(body)) { return "quote_limit"; } if (/Create your free account/i.test(body) && /Work email/i.test(body)) { return "registration"; } if ( /Explore your custom quote options|fulfillment-option|Prices from \$/i.test( body, ) ) { return "quote_results"; } if (/Pickup ZIP Code|Number of pallets|Total shipment weight|Shipment Pickup|Freight Class|Generate my quote|Let'?s build a quote/i.test(body)) { return "quote_form"; } if (/Thank you for your interest/i.test(body)) { return "quote_limit_thank_you"; } return "unknown"; } export async function flockQuoteLimitMessage(page: Page): Promise { if (!(await isFlockQuoteLimitReached(page))) { return null; } const body = ((await page.locator("body").innerText().catch(() => "")) || "").slice( 0, 500, ); if (/Thank you for your interest/i.test(body)) { return "FLOCK_QUOTE_LIMIT:官网提示 Thank you for your interest(获取报价已达上限)"; } return "FLOCK_QUOTE_LIMIT:官网报价次数已达上限(请 npm run record:flock -- --fresh 或换无痕会话)"; } /** 注册失败提示(含虚构邮箱被拒、账号已存在) */ export async function flockRegistrationErrorMessage( page: Page, ): Promise { const body = await page.locator("body").innerText().catch(() => ""); if (/We had an issue creating your account/i.test(body)) { return "FLOCK_ACCOUNT_REJECTED:官网拒绝创建账号(邮箱已排除时多为:自动化指纹/同电话复用/IP 频控/姓名公司像机器人)"; } if (/already (have|registered)|email.*(taken|exists|in use)/i.test(body)) { return "FLOCK_ACCOUNT_EXISTS:该邮箱已注册,请加载录制会话或换邮箱"; } return null; }