93 lines
3.2 KiB
JavaScript
93 lines
3.2 KiB
JavaScript
/**
|
|
* 竞品监测脚本 — 每周一 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);
|
|
});
|