koishi-plugin-cat-raising 1.3.8 → 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 +71 -46
- 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) {
|
|
@@ -317,6 +318,7 @@ function apply(ctx, config) {
|
|
|
317
318
|
const warningMessageMap = /* @__PURE__ */ new Map();
|
|
318
319
|
const pendingDanmakuTimersByMessage = /* @__PURE__ */ new Map();
|
|
319
320
|
const pendingDanmakuTimersByRoom = /* @__PURE__ */ new Map();
|
|
321
|
+
const pendingDanmakuRoomMap = /* @__PURE__ */ new Map();
|
|
320
322
|
ctx.on("message", async (session) => {
|
|
321
323
|
const groupConfig = config.monitorGroups.find((g) => g.groupId === session.channelId);
|
|
322
324
|
if (!groupConfig) return;
|
|
@@ -344,8 +346,10 @@ function apply(ctx, config) {
|
|
|
344
346
|
if (!hasStrongContext && !hasTime) return;
|
|
345
347
|
const biliInfo = await fetchBilibiliInfo(ctx, roomId);
|
|
346
348
|
if (!biliInfo) return;
|
|
349
|
+
const danmakuOnlyKeywords = config.danmakuOnlyKeywords || ["闪击"];
|
|
350
|
+
const isDanmakuOnly = danmakuOnlyKeywords.some((keyword) => preprocessedMessage.includes(keyword));
|
|
347
351
|
let helperMessageId;
|
|
348
|
-
if (groupConfig.sendHelperMessages) {
|
|
352
|
+
if (!isDanmakuOnly && groupConfig.sendHelperMessages) {
|
|
349
353
|
try {
|
|
350
354
|
const displayAnchor = biliInfo.anchorName;
|
|
351
355
|
[helperMessageId] = await session.send(`直播间: ${roomId}
|
|
@@ -356,66 +360,87 @@ function apply(ctx, config) {
|
|
|
356
360
|
}
|
|
357
361
|
}
|
|
358
362
|
const { dateTime } = parsedEvent;
|
|
359
|
-
if (forwardedHistory.some((entry) => entry.roomId === roomId && entry.dateTime === dateTime)) {
|
|
363
|
+
if (!isDanmakuOnly && forwardedHistory.some((entry) => entry.roomId === roomId && entry.dateTime === dateTime)) {
|
|
360
364
|
ctx.logger.info(`[防复读] 检测到重复活动,已发送辅助信息,跳过转发: 房间=${roomId}, 时间=${dateTime}`);
|
|
361
365
|
return;
|
|
362
366
|
}
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
367
|
+
if (!isDanmakuOnly) {
|
|
368
|
+
try {
|
|
369
|
+
const displayAnchor = biliInfo.anchorName;
|
|
370
|
+
const forwardMessage = `${session.content}
|
|
366
371
|
|
|
367
372
|
---
|
|
368
373
|
主播: ${displayAnchor}
|
|
369
374
|
投稿数: ${biliInfo.videoCount}`;
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
const delayMs = (Math.floor(Math.random() * (max - min + 1)) + min) * 1e3;
|
|
384
|
-
ctx.logger.info(`[弹幕] ${config.biliAccessKeys.length} 个账号将于 ${Math.round(delayMs / 1e3)} 秒后发送弹幕到直播间 ${roomId}。`);
|
|
385
|
-
const timer = setTimeout(() => {
|
|
386
|
-
const list1 = pendingDanmakuTimersByMessage.get(session.messageId);
|
|
387
|
-
if (list1) pendingDanmakuTimersByMessage.set(session.messageId, list1.filter((t) => t !== timer));
|
|
388
|
-
const list2 = pendingDanmakuTimersByRoom.get(roomId);
|
|
389
|
-
if (list2) pendingDanmakuTimersByRoom.set(roomId, list2.filter((t) => t !== timer));
|
|
390
|
-
const danmakuPromises = config.biliAccessKeys.map(
|
|
391
|
-
(keyConfig) => sendBilibiliDanmaku(ctx, keyConfig, roomId, "喵喵喵")
|
|
392
|
-
);
|
|
393
|
-
Promise.allSettled(danmakuPromises);
|
|
394
|
-
}, delayMs);
|
|
395
|
-
const byMsg = pendingDanmakuTimersByMessage.get(session.messageId) || [];
|
|
396
|
-
byMsg.push(timer);
|
|
397
|
-
pendingDanmakuTimersByMessage.set(session.messageId, byMsg);
|
|
398
|
-
const byRoom = pendingDanmakuTimersByRoom.get(roomId) || [];
|
|
399
|
-
byRoom.push(timer);
|
|
400
|
-
pendingDanmakuTimersByRoom.set(roomId, byRoom);
|
|
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);
|
|
401
388
|
}
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
|
|
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);
|
|
405
427
|
}
|
|
406
428
|
});
|
|
407
429
|
ctx.on("message-deleted", async (session) => {
|
|
408
430
|
const isMonitored = config.monitorGroups.some((g) => g.groupId === session.channelId);
|
|
409
431
|
if (!isMonitored) return;
|
|
410
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
|
+
}
|
|
411
441
|
const entryIndex = forwardedHistory.findIndex((entry) => entry.originalMessageId === originalMessageId);
|
|
412
442
|
if (entryIndex !== -1) {
|
|
413
443
|
const entry = forwardedHistory[entryIndex];
|
|
414
|
-
const timersByMsg = pendingDanmakuTimersByMessage.get(originalMessageId);
|
|
415
|
-
if (timersByMsg) {
|
|
416
|
-
for (const t of timersByMsg) clearTimeout(t);
|
|
417
|
-
pendingDanmakuTimersByMessage.delete(originalMessageId);
|
|
418
|
-
}
|
|
419
444
|
const timersByRoom = pendingDanmakuTimersByRoom.get(entry.roomId);
|
|
420
445
|
if (timersByRoom) {
|
|
421
446
|
for (const t of timersByRoom) clearTimeout(t);
|
|
@@ -435,7 +460,7 @@ function apply(ctx, config) {
|
|
|
435
460
|
ctx.logger.warn(`[撤回] 转发消息 (ID: ${entry.forwardedMessageId}) 失败:`, e);
|
|
436
461
|
} finally {
|
|
437
462
|
forwardedHistory.splice(entryIndex, 1);
|
|
438
|
-
ctx.logger.info(`[撤回] 已联动撤回与源消息 ${originalMessageId} 相关的转发。`);
|
|
463
|
+
ctx.logger.info(`[撤回] 已联动撤回与源消息 ${originalMessageId} (直播间: ${entry.roomId}) 相关的转发。`);
|
|
439
464
|
}
|
|
440
465
|
}
|
|
441
466
|
if (warningMessageMap.has(originalMessageId)) {
|