koishi-plugin-cat-raising 1.3.7 → 1.3.9
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.d.ts +2 -0
- package/lib/index.js +78 -31
- package/package.json +1 -1
package/lib/index.d.ts
CHANGED
|
@@ -28,6 +28,8 @@ export interface Config {
|
|
|
28
28
|
biliAccessKeys: BiliAccessKeyConfig[];
|
|
29
29
|
/** 硬拒绝关键词列表 */
|
|
30
30
|
hardRejectionKeywords: string[];
|
|
31
|
+
/** 仅弹幕模式关键词(包含任一关键词则只发弹幕不转发) */
|
|
32
|
+
danmakuOnlyKeywords: string[];
|
|
31
33
|
/** 发送弹幕的延迟下限(秒) */
|
|
32
34
|
danmakuDelayMinSeconds?: number;
|
|
33
35
|
/** 发送弹幕的延迟上限(秒) */
|
package/lib/index.js
CHANGED
|
@@ -60,7 +60,8 @@ var Config = import_koishi.Schema.intersect([
|
|
|
60
60
|
danmakuDelayMaxSeconds: import_koishi.Schema.number().description("弹幕延迟上限(秒)").role("slider", { min: 0, max: 60, step: 1 }).default(10)
|
|
61
61
|
}).description("弹幕设置"),
|
|
62
62
|
import_koishi.Schema.object({
|
|
63
|
-
hardRejectionKeywords: import_koishi.Schema.array(import_koishi.Schema.string()).description("命中则忽略的关键词(硬拒绝)").default(["发言榜单", "投稿数:", "预告"])
|
|
63
|
+
hardRejectionKeywords: import_koishi.Schema.array(import_koishi.Schema.string()).description("命中则忽略的关键词(硬拒绝)").default(["发言榜单", "投稿数:", "预告"]),
|
|
64
|
+
danmakuOnlyKeywords: import_koishi.Schema.array(import_koishi.Schema.string()).description("仅弹幕模式关键词(包含任一关键词则只发弹幕不转发)").default(["闪击"])
|
|
64
65
|
}).description("过滤规则")
|
|
65
66
|
]);
|
|
66
67
|
function preprocessChineseNumerals(text) {
|
|
@@ -315,6 +316,9 @@ __name(fetchBilibiliInfo, "fetchBilibiliInfo");
|
|
|
315
316
|
function apply(ctx, config) {
|
|
316
317
|
const forwardedHistory = [];
|
|
317
318
|
const warningMessageMap = /* @__PURE__ */ new Map();
|
|
319
|
+
const pendingDanmakuTimersByMessage = /* @__PURE__ */ new Map();
|
|
320
|
+
const pendingDanmakuTimersByRoom = /* @__PURE__ */ new Map();
|
|
321
|
+
const pendingDanmakuRoomMap = /* @__PURE__ */ new Map();
|
|
318
322
|
ctx.on("message", async (session) => {
|
|
319
323
|
const groupConfig = config.monitorGroups.find((g) => g.groupId === session.channelId);
|
|
320
324
|
if (!groupConfig) return;
|
|
@@ -342,8 +346,10 @@ function apply(ctx, config) {
|
|
|
342
346
|
if (!hasStrongContext && !hasTime) return;
|
|
343
347
|
const biliInfo = await fetchBilibiliInfo(ctx, roomId);
|
|
344
348
|
if (!biliInfo) return;
|
|
349
|
+
const danmakuOnlyKeywords = config.danmakuOnlyKeywords || ["闪击"];
|
|
350
|
+
const isDanmakuOnly = danmakuOnlyKeywords.some((keyword) => preprocessedMessage.includes(keyword));
|
|
345
351
|
let helperMessageId;
|
|
346
|
-
if (groupConfig.sendHelperMessages) {
|
|
352
|
+
if (!isDanmakuOnly && groupConfig.sendHelperMessages) {
|
|
347
353
|
try {
|
|
348
354
|
const displayAnchor = biliInfo.anchorName;
|
|
349
355
|
[helperMessageId] = await session.send(`直播间: ${roomId}
|
|
@@ -354,51 +360,92 @@ function apply(ctx, config) {
|
|
|
354
360
|
}
|
|
355
361
|
}
|
|
356
362
|
const { dateTime } = parsedEvent;
|
|
357
|
-
if (forwardedHistory.some((entry) => entry.roomId === roomId && entry.dateTime === dateTime)) {
|
|
363
|
+
if (!isDanmakuOnly && forwardedHistory.some((entry) => entry.roomId === roomId && entry.dateTime === dateTime)) {
|
|
358
364
|
ctx.logger.info(`[防复读] 检测到重复活动,已发送辅助信息,跳过转发: 房间=${roomId}, 时间=${dateTime}`);
|
|
359
365
|
return;
|
|
360
366
|
}
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
367
|
+
if (!isDanmakuOnly) {
|
|
368
|
+
try {
|
|
369
|
+
const displayAnchor = biliInfo.anchorName;
|
|
370
|
+
const forwardMessage = `${session.content}
|
|
364
371
|
|
|
365
372
|
---
|
|
366
373
|
主播: ${displayAnchor}
|
|
367
374
|
投稿数: ${biliInfo.videoCount}`;
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
const delayMs = (Math.floor(Math.random() * (max - min + 1)) + min) * 1e3;
|
|
382
|
-
ctx.logger.info(`[弹幕] ${config.biliAccessKeys.length} 个账号将于 ${Math.round(delayMs / 1e3)} 秒后发送弹幕到直播间 ${roomId}。`);
|
|
383
|
-
setTimeout(() => {
|
|
384
|
-
const danmakuPromises = config.biliAccessKeys.map(
|
|
385
|
-
(keyConfig) => sendBilibiliDanmaku(ctx, keyConfig, roomId, "喵喵喵")
|
|
386
|
-
);
|
|
387
|
-
Promise.allSettled(danmakuPromises);
|
|
388
|
-
}, delayMs);
|
|
375
|
+
const [forwardedMessageId] = config.isGroup ? await session.bot.sendMessage(config.targetQQ, forwardMessage) : await session.bot.sendPrivateMessage(config.targetQQ, forwardMessage);
|
|
376
|
+
forwardedHistory.push({
|
|
377
|
+
originalMessageId: session.messageId,
|
|
378
|
+
forwardedMessageId,
|
|
379
|
+
helperMessageId,
|
|
380
|
+
// 存储辅助消息ID用于撤回联动
|
|
381
|
+
roomId,
|
|
382
|
+
dateTime
|
|
383
|
+
});
|
|
384
|
+
if (forwardedHistory.length > config.historySize) forwardedHistory.shift();
|
|
385
|
+
} catch (error) {
|
|
386
|
+
session.send("🐱 - 转发失败,请检查目标QQ/群号配置是否正确");
|
|
387
|
+
ctx.logger.error("[转发] 失败:", error);
|
|
389
388
|
}
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
|
|
389
|
+
}
|
|
390
|
+
if (config.biliAccessKeys && config.biliAccessKeys.length > 0) {
|
|
391
|
+
const min = Math.max(0, config.danmakuDelayMinSeconds ?? 8);
|
|
392
|
+
const max = Math.max(min, config.danmakuDelayMaxSeconds ?? 10);
|
|
393
|
+
const delayMs = (Math.floor(Math.random() * (max - min + 1)) + min) * 1e3;
|
|
394
|
+
ctx.logger.info(`[弹幕] ${config.biliAccessKeys.length} 个账号将于 ${Math.round(delayMs / 1e3)} 秒后发送弹幕到直播间 ${roomId}。`);
|
|
395
|
+
const timer = setTimeout(() => {
|
|
396
|
+
const list1 = pendingDanmakuTimersByMessage.get(session.messageId);
|
|
397
|
+
if (list1) {
|
|
398
|
+
const newList = list1.filter((t) => t !== timer);
|
|
399
|
+
if (newList.length === 0) {
|
|
400
|
+
pendingDanmakuTimersByMessage.delete(session.messageId);
|
|
401
|
+
pendingDanmakuRoomMap.delete(session.messageId);
|
|
402
|
+
} else {
|
|
403
|
+
pendingDanmakuTimersByMessage.set(session.messageId, newList);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
const list2 = pendingDanmakuTimersByRoom.get(roomId);
|
|
407
|
+
if (list2) {
|
|
408
|
+
const newList = list2.filter((t) => t !== timer);
|
|
409
|
+
if (newList.length === 0) {
|
|
410
|
+
pendingDanmakuTimersByRoom.delete(roomId);
|
|
411
|
+
} else {
|
|
412
|
+
pendingDanmakuTimersByRoom.set(roomId, newList);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
const danmakuPromises = config.biliAccessKeys.map(
|
|
416
|
+
(keyConfig) => sendBilibiliDanmaku(ctx, keyConfig, roomId, "喵喵喵")
|
|
417
|
+
);
|
|
418
|
+
Promise.allSettled(danmakuPromises);
|
|
419
|
+
}, delayMs);
|
|
420
|
+
const byMsg = pendingDanmakuTimersByMessage.get(session.messageId) || [];
|
|
421
|
+
byMsg.push(timer);
|
|
422
|
+
pendingDanmakuTimersByMessage.set(session.messageId, byMsg);
|
|
423
|
+
pendingDanmakuRoomMap.set(session.messageId, roomId);
|
|
424
|
+
const byRoom = pendingDanmakuTimersByRoom.get(roomId) || [];
|
|
425
|
+
byRoom.push(timer);
|
|
426
|
+
pendingDanmakuTimersByRoom.set(roomId, byRoom);
|
|
393
427
|
}
|
|
394
428
|
});
|
|
395
429
|
ctx.on("message-deleted", async (session) => {
|
|
396
430
|
const isMonitored = config.monitorGroups.some((g) => g.groupId === session.channelId);
|
|
397
431
|
if (!isMonitored) return;
|
|
398
432
|
const originalMessageId = session.messageId;
|
|
433
|
+
const timersByMsg = pendingDanmakuTimersByMessage.get(originalMessageId);
|
|
434
|
+
if (timersByMsg) {
|
|
435
|
+
for (const t of timersByMsg) clearTimeout(t);
|
|
436
|
+
pendingDanmakuTimersByMessage.delete(originalMessageId);
|
|
437
|
+
const roomId = pendingDanmakuRoomMap.get(originalMessageId);
|
|
438
|
+
pendingDanmakuRoomMap.delete(originalMessageId);
|
|
439
|
+
ctx.logger.info(`[撤回] 已取消源消息 ${originalMessageId}${roomId ? ` (直播间: ${roomId})` : ""} 的延迟弹幕。`);
|
|
440
|
+
}
|
|
399
441
|
const entryIndex = forwardedHistory.findIndex((entry) => entry.originalMessageId === originalMessageId);
|
|
400
442
|
if (entryIndex !== -1) {
|
|
401
443
|
const entry = forwardedHistory[entryIndex];
|
|
444
|
+
const timersByRoom = pendingDanmakuTimersByRoom.get(entry.roomId);
|
|
445
|
+
if (timersByRoom) {
|
|
446
|
+
for (const t of timersByRoom) clearTimeout(t);
|
|
447
|
+
pendingDanmakuTimersByRoom.delete(entry.roomId);
|
|
448
|
+
}
|
|
402
449
|
if (entry.helperMessageId) {
|
|
403
450
|
try {
|
|
404
451
|
await session.bot.deleteMessage(session.channelId, entry.helperMessageId);
|
|
@@ -413,7 +460,7 @@ function apply(ctx, config) {
|
|
|
413
460
|
ctx.logger.warn(`[撤回] 转发消息 (ID: ${entry.forwardedMessageId}) 失败:`, e);
|
|
414
461
|
} finally {
|
|
415
462
|
forwardedHistory.splice(entryIndex, 1);
|
|
416
|
-
ctx.logger.info(`[撤回] 已联动撤回与源消息 ${originalMessageId} 相关的转发。`);
|
|
463
|
+
ctx.logger.info(`[撤回] 已联动撤回与源消息 ${originalMessageId} (直播间: ${entry.roomId}) 相关的转发。`);
|
|
417
464
|
}
|
|
418
465
|
}
|
|
419
466
|
if (warningMessageMap.has(originalMessageId)) {
|