feat: 首次推送到Gitea - 完整项目代码 + 安全加固 + 知识库
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
/**
|
||||
* 竞品监测脚本 — 每周一 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);
|
||||
});
|
||||
Reference in New Issue
Block a user