/** * 竞品监测脚本 — 每周一 09:00 运行 * 对比主要竞品的工具数/技能数变化,检测显著变化 */ import "dotenv/config"; import fs from "fs"; import path from "path"; import { fileURLToPath } from "url"; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const DATA_DIR = path.join(__dirname, "..", "data", "competitor-snapshot"); // 主要竞品(硬编码) const COMPETITORS = [ { name: "TAAFT", url: "https://theresanaiforthat.com", estimatedTools: 15000, estimatedCategories: 120 }, { name: "Toolify", url: "https://www.toolify.ai", estimatedTools: 12000, estimatedCategories: 80 }, { name: "AI工具集", url: "https://ai-bot.cn", estimatedTools: 8000, estimatedCategories: 60 }, { name: "发现AI", url: "https://faxianai.com", estimatedTools: 5000, estimatedCategories: 40 }, { name: "Futurepedia", url: "https://www.futurepedia.io", estimatedTools: 6000, estimatedCategories: 50 }, ]; function todayDateStr() { return new Date().toISOString().slice(0, 10); } function log(message) { const ts = new Date().toISOString(); console.log(`[${ts}] ${message}`); } async function main() { log("竞品监测开始..."); // 确保数据目录存在 if (!fs.existsSync(DATA_DIR)) { fs.mkdirSync(DATA_DIR, { recursive: true }); } const dateStr = todayDateStr(); const snapshotFile = path.join(DATA_DIR, `${dateStr}.json`); // 当前快照 const snapshot = { date: dateStr, competitors: COMPETITORS.map((c) => ({ name: c.name, toolCount: c.estimatedTools, categoryCount: c.estimatedCategories, })), generatedAt: new Date().toISOString(), }; fs.writeFileSync(snapshotFile, JSON.stringify(snapshot, null, 2), "utf-8"); log(`快照已保存: ${snapshotFile}`); // 对比上一周的变化 const prevDate = new Date(); prevDate.setDate(prevDate.getDate() - 7); const prevDateStr = prevDate.toISOString().slice(0, 10); const prevFile = path.join(DATA_DIR, `${prevDateStr}.json`); if (fs.existsSync(prevFile)) { log(`找到上周快照: ${prevFile}`); const prevData = JSON.parse(fs.readFileSync(prevFile, "utf-8")); const prevMap = new Map(prevData.competitors.map((c) => [c.name, c])); for (const curr of snapshot.competitors) { const prev = prevMap.get(curr.name); if (prev) { const toolDiff = curr.toolCount - prev.toolCount; const toolPct = prev.toolCount > 0 ? ((toolDiff / prev.toolCount) * 100).toFixed(1) : "∞"; const catDiff = curr.categoryCount - prev.categoryCount; const catPct = prev.categoryCount > 0 ? ((catDiff / prev.categoryCount) * 100).toFixed(1) : "∞"; if (Math.abs(toolDiff / prev.toolCount) > 0.2) { log(`⚠️ 显著变化: ${curr.name} → 工具 ${prev.toolCount} → ${curr.toolCount} (${toolPct}%)`); } else { log(`✓ ${curr.name}: 工具 ${prev.toolCount} → ${curr.toolCount} (${toolPct}%), 分类 ${prev.categoryCount} → ${curr.categoryCount} (${catPct}%)`); } } } } else { log(`未找到上周快照 (${prevDateStr}), 跳过对比`); } log("竞品监测完成"); } main().catch((err) => { console.error("竞品监测失败:", err); process.exit(1); });