koishi-plugin-cs2-update-log 2.3.0 → 2.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/index.js +84 -17
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -31,8 +31,8 @@ const BRAND_LOGO_PATH = node_path_1.default.join(ASSET_DIR, 'brand-logo.png');
|
|
|
31
31
|
const GITHUB_QRCODE_PATH = node_path_1.default.join(ASSET_DIR, 'github-qrcode.png');
|
|
32
32
|
const loggerName = 'cs2-update-log';
|
|
33
33
|
exports.Config = koishi_1.Schema.object({
|
|
34
|
-
interval: koishi_1.Schema.number().min(5).step(1).default(
|
|
35
|
-
count: koishi_1.Schema.number().min(1).max(100).step(1).default(
|
|
34
|
+
interval: koishi_1.Schema.number().min(5).step(1).default(30).description('轮询间隔,单位:秒。'),
|
|
35
|
+
count: koishi_1.Schema.number().min(1).max(100).step(1).default(5).description('每次从 Steam 拉取的新闻数量。'),
|
|
36
36
|
stateFile: koishi_1.Schema.string().default('.koishi-cs2-update-log.json').description('本地 gid 判重文件路径。相对路径会基于 Koishi 启动目录解析。'),
|
|
37
37
|
pushOnFirstRun: koishi_1.Schema.boolean().default(false).description('首次启动时是否推送历史内容。默认关闭,避免刷屏。'),
|
|
38
38
|
targets: koishi_1.Schema.array(koishi_1.Schema.object({
|
|
@@ -80,6 +80,9 @@ function apply(ctx, config) {
|
|
|
80
80
|
};
|
|
81
81
|
let stateInitialized = false;
|
|
82
82
|
let polling = false;
|
|
83
|
+
let appReady = false;
|
|
84
|
+
let stateLoadPromise;
|
|
85
|
+
let initialPollStarted = false;
|
|
83
86
|
function clearRuntimeCache(reason) {
|
|
84
87
|
cache.rssFetchedAt = 0;
|
|
85
88
|
cache.rssItems = [];
|
|
@@ -107,7 +110,9 @@ function apply(ctx, config) {
|
|
|
107
110
|
}
|
|
108
111
|
async function saveState(recentItems) {
|
|
109
112
|
try {
|
|
110
|
-
const recentGids = recentItems
|
|
113
|
+
const recentGids = recentItems
|
|
114
|
+
.map((item) => item.gid)
|
|
115
|
+
.filter((gid) => gid && knownGids.has(gid));
|
|
111
116
|
const recentSet = new Set(recentGids);
|
|
112
117
|
const gids = [
|
|
113
118
|
...recentGids,
|
|
@@ -240,7 +245,11 @@ function apply(ctx, config) {
|
|
|
240
245
|
let pushed = 0;
|
|
241
246
|
for (const item of freshItems) {
|
|
242
247
|
const classified = classifyNews(item);
|
|
243
|
-
await pushNews(classified);
|
|
248
|
+
const delivered = await pushNews(classified);
|
|
249
|
+
if (!delivered) {
|
|
250
|
+
logger.warn('新闻未成功送达,保留 gid 等待重试:gid=%s title=%s', item.gid, item.title);
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
244
253
|
knownGids.add(item.gid);
|
|
245
254
|
pushed += 1;
|
|
246
255
|
}
|
|
@@ -268,22 +277,55 @@ function apply(ctx, config) {
|
|
|
268
277
|
async function pushNews(news) {
|
|
269
278
|
if (!config.targets.length) {
|
|
270
279
|
logger.warn('未配置推送目标,已跳过:%s', news.item.title);
|
|
271
|
-
return;
|
|
280
|
+
return false;
|
|
281
|
+
}
|
|
282
|
+
const deliveries = config.targets.map((target) => ({
|
|
283
|
+
target,
|
|
284
|
+
bot: findTargetBot(ctx, target),
|
|
285
|
+
}));
|
|
286
|
+
const unavailable = deliveries.filter(({ bot }) => !bot);
|
|
287
|
+
if (unavailable.length) {
|
|
288
|
+
for (const { target } of unavailable) {
|
|
289
|
+
logger.warn('找不到在线推送机器人 platform=%s selfId=%s', target.platform, target.selfId || '*');
|
|
290
|
+
}
|
|
291
|
+
return false;
|
|
272
292
|
}
|
|
273
293
|
const content = await buildNewsContent(news);
|
|
274
|
-
await Promise.all(
|
|
294
|
+
const results = await Promise.all(deliveries.map(async ({ target, bot }) => {
|
|
275
295
|
try {
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
logger.warn('找不到推送机器人 platform=%s selfId=%s', target.platform, target.selfId || '*');
|
|
279
|
-
return;
|
|
280
|
-
}
|
|
296
|
+
if (!bot)
|
|
297
|
+
return false;
|
|
281
298
|
await bot.sendMessage(target.channelId, content);
|
|
299
|
+
return true;
|
|
282
300
|
}
|
|
283
301
|
catch (error) {
|
|
284
302
|
logger.error('推送到 channelId=%s 失败:%s', target.channelId, formatError(error));
|
|
303
|
+
return false;
|
|
285
304
|
}
|
|
286
305
|
}));
|
|
306
|
+
return results.every(Boolean);
|
|
307
|
+
}
|
|
308
|
+
function ensureStateLoaded() {
|
|
309
|
+
return stateLoadPromise ||= loadState();
|
|
310
|
+
}
|
|
311
|
+
function allTargetBotsOnline() {
|
|
312
|
+
return !!config.targets.length && config.targets.every((target) => !!findTargetBot(ctx, target));
|
|
313
|
+
}
|
|
314
|
+
async function pollWhenTargetsOnline(source) {
|
|
315
|
+
if (!appReady) {
|
|
316
|
+
logger.debug('应用尚未 ready,跳过本次 %s 轮询。', source);
|
|
317
|
+
return 0;
|
|
318
|
+
}
|
|
319
|
+
await ensureStateLoaded();
|
|
320
|
+
if (!allTargetBotsOnline()) {
|
|
321
|
+
logger.debug('目标机器人尚未全部在线,跳过本次 %s 轮询。', source);
|
|
322
|
+
return 0;
|
|
323
|
+
}
|
|
324
|
+
if (!initialPollStarted) {
|
|
325
|
+
initialPollStarted = true;
|
|
326
|
+
return pollAndPush('startup');
|
|
327
|
+
}
|
|
328
|
+
return pollAndPush(source);
|
|
287
329
|
}
|
|
288
330
|
async function renderOrFallbackText(news, title, bodyMarkdown, link) {
|
|
289
331
|
const puppeteer = ctx.puppeteer;
|
|
@@ -440,7 +482,9 @@ function apply(ctx, config) {
|
|
|
440
482
|
});
|
|
441
483
|
ctx.command('cs2log.push', '手动检查并推送新的 CS2 官方公告')
|
|
442
484
|
.action(async () => {
|
|
443
|
-
|
|
485
|
+
if (!allTargetBotsOnline())
|
|
486
|
+
return '目标机器人尚未在线,暂未执行推送。';
|
|
487
|
+
const pushed = await pollWhenTargetsOnline('manual');
|
|
444
488
|
return pushed ? `已推送 ${pushed} 条新的 CS2 官方新闻。` : '没有发现新的 CS2 官方新闻。';
|
|
445
489
|
});
|
|
446
490
|
ctx.command('cs2log.test', '测试推送最近 2 条 CS2 官方新闻')
|
|
@@ -457,15 +501,36 @@ function apply(ctx, config) {
|
|
|
457
501
|
}
|
|
458
502
|
return `已向当前会话触发 ${testItems.length} 条 CS2 官方新闻测试推送。本次测试不会写入 gid 判重 state。`;
|
|
459
503
|
});
|
|
460
|
-
ctx.on('ready', () => {
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
504
|
+
ctx.on('ready', async () => {
|
|
505
|
+
appReady = true;
|
|
506
|
+
try {
|
|
507
|
+
await pollWhenTargetsOnline('startup');
|
|
508
|
+
}
|
|
509
|
+
catch (error) {
|
|
464
510
|
logger.error('启动 CS2 新闻轮询失败:%s', formatError(error));
|
|
511
|
+
}
|
|
512
|
+
});
|
|
513
|
+
ctx.on('bot-status-updated', async (bot) => {
|
|
514
|
+
if (bot.status !== 1 /* Universal.Status.ONLINE */)
|
|
515
|
+
return;
|
|
516
|
+
const matchesTarget = config.targets.some((target) => {
|
|
517
|
+
if (target.platform && bot.platform !== target.platform)
|
|
518
|
+
return false;
|
|
519
|
+
if (target.selfId && bot.selfId !== target.selfId)
|
|
520
|
+
return false;
|
|
521
|
+
return true;
|
|
465
522
|
});
|
|
523
|
+
if (!matchesTarget)
|
|
524
|
+
return;
|
|
525
|
+
try {
|
|
526
|
+
await pollWhenTargetsOnline('online');
|
|
527
|
+
}
|
|
528
|
+
catch (error) {
|
|
529
|
+
logger.error('机器人上线后检查 CS2 新闻失败:%s', formatError(error));
|
|
530
|
+
}
|
|
466
531
|
});
|
|
467
532
|
ctx.setInterval(() => {
|
|
468
|
-
void
|
|
533
|
+
void pollWhenTargetsOnline('timer');
|
|
469
534
|
}, Math.max(5, config.interval) * 1000);
|
|
470
535
|
ctx.setInterval(() => {
|
|
471
536
|
clearRuntimeCache('short cache interval');
|
|
@@ -881,6 +946,8 @@ body {
|
|
|
881
946
|
function findTargetBot(ctx, target) {
|
|
882
947
|
const bots = Array.from(ctx.bots || []);
|
|
883
948
|
return bots.find((bot) => {
|
|
949
|
+
if (bot.status !== 1 /* Universal.Status.ONLINE */)
|
|
950
|
+
return false;
|
|
884
951
|
if (target.platform && bot.platform !== target.platform)
|
|
885
952
|
return false;
|
|
886
953
|
if (target.selfId && bot.selfId !== target.selfId)
|
package/package.json
CHANGED