vault backup: 2026-11-09 09:42:19
This commit is contained in:
1 parent
511f907980
commit
5022dc27b2
8 files changed
+103
-9
No files matched your search
+63
-2
@@ -6,7 +6,7 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { CDP, listPages, EXPR } = require('./cdp');
|
||||
const { notify } = require('./notify');
|
||||
const { notify, sendWebhook } = require('./notify');
|
||||
|
||||
const DIR = __dirname;
|
||||
const CONFIG_PATH = path.join(DIR, 'config.json');
|
||||
@@ -25,6 +25,7 @@ function log(...parts) {
|
||||
if (fs.existsSync(LOG_PATH) && fs.statSync(LOG_PATH).size > 2 * 1024 * 1024) {
|
||||
fs.renameSync(LOG_PATH, LOG_PATH + '.old');
|
||||
}
|
||||
if (!fs.existsSync(LOG_PATH)) fs.writeFileSync(LOG_PATH, '\uFEFF', 'utf8'); // BOM: 记事本/编辑器不乱码
|
||||
fs.appendFileSync(LOG_PATH, line + '\r\n', 'utf8');
|
||||
} catch { }
|
||||
}
|
||||
@@ -117,6 +118,58 @@ function savePrev(map) {
|
||||
} catch { }
|
||||
}
|
||||
|
||||
// ---------- webhook 节流 + 合并(防刷屏、防企微 20条/分钟限流) ----------
|
||||
const pushLog = []; // 已推送时间戳
|
||||
let pendingPush = []; // 被节流时暂存的提醒,攒批后一次发出
|
||||
|
||||
function rateState() {
|
||||
const rl = cfg.rateLimit || {};
|
||||
const minI = Math.max(0, Number(rl.minIntervalSeconds ?? 60)) * 1000;
|
||||
const maxH = Number(rl.maxPerHour ?? 30);
|
||||
const now = Date.now();
|
||||
while (pushLog.length && now - pushLog[0] > 3600000) pushLog.shift();
|
||||
if (maxH > 0 && pushLog.length >= maxH) {
|
||||
return { ok: false, reason: `已达每小时上限 ${maxH} 条` };
|
||||
}
|
||||
const last = pushLog[pushLog.length - 1];
|
||||
if (last && now - last < minI) {
|
||||
return { ok: false, reason: `距上条推送 ${Math.round((now - last) / 1000)}s(下限 ${minI / 1000}s)` };
|
||||
}
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
async function queuePush(lines, pageUrl) {
|
||||
const hooks = cfg.webhooks || [];
|
||||
if (!hooks.length) return;
|
||||
pendingPush.push({ lines, pageUrl, at: new Date().toLocaleTimeString('zh-CN', { hour12: false }) });
|
||||
const st = rateState();
|
||||
if (!st.ok) {
|
||||
log(`webhook 节流:${st.reason},已并入待发队列(${pendingPush.length} 条)`);
|
||||
return;
|
||||
}
|
||||
await flushPush();
|
||||
}
|
||||
|
||||
async function flushPush() {
|
||||
if (!pendingPush.length) return;
|
||||
const hooks = cfg.webhooks || [];
|
||||
if (!hooks.length) { pendingPush = []; return; }
|
||||
const batch = pendingPush.slice();
|
||||
pendingPush = [];
|
||||
const lastUrl = batch[batch.length - 1].pageUrl;
|
||||
const body = batch.map(b => `[${b.at}] ${b.lines.join(' / ')}`).join('\n') + `\n${lastUrl}`;
|
||||
const title = batch.length > 1 ? `闲鱼新消息(合并 ${batch.length} 条提醒)` : '闲鱼有新消息';
|
||||
pushLog.push(Date.now());
|
||||
await Promise.all(hooks.map(async (h) => {
|
||||
try {
|
||||
const r = await sendWebhook(h, title, body, { url: lastUrl });
|
||||
log(`[webhook] ${h.type} -> ${r.status}`);
|
||||
} catch (e) {
|
||||
log(`[webhook] ${h.type} 失败: ${e.message}`);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
function inQuietHours() {
|
||||
const q = cfg.quietHours;
|
||||
if (!q || !q.from || !q.to) return false;
|
||||
@@ -185,10 +238,18 @@ async function tick(prev) {
|
||||
log('静默时段,跳过通知:', JSON.stringify(events));
|
||||
} else {
|
||||
log('检测到新消息:', JSON.stringify(events));
|
||||
await notify(cfg, title, text, { url: head.page }, log);
|
||||
// 本地弹窗不节流(这才是工具的意义);webhook 走节流 + 合并
|
||||
await notify({ ...cfg, webhooks: [] }, title, text, { url: head.page }, log);
|
||||
await queuePush(lines, head.page);
|
||||
}
|
||||
}
|
||||
|
||||
// 有积压且节流窗口已过,补发
|
||||
if (pendingPush.length) {
|
||||
const st = rateState();
|
||||
if (st.ok) await flushPush();
|
||||
}
|
||||
|
||||
savePrev(map);
|
||||
return map;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user