koishi-plugin-cat-raising 1.3.5 → 1.3.7

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 CHANGED
@@ -26,6 +26,12 @@ export interface Config {
26
26
  historySize: number;
27
27
  /** 用于发送B站弹幕的 access_key 列表 */
28
28
  biliAccessKeys: BiliAccessKeyConfig[];
29
+ /** 硬拒绝关键词列表 */
30
+ hardRejectionKeywords: string[];
31
+ /** 发送弹幕的延迟下限(秒) */
32
+ danmakuDelayMinSeconds?: number;
33
+ /** 发送弹幕的延迟上限(秒) */
34
+ danmakuDelayMaxSeconds?: number;
29
35
  }
30
36
  export declare const Config: Schema<Config>;
31
37
  /**
package/lib/index.js CHANGED
@@ -39,19 +39,30 @@ var import_koishi = require("koishi");
39
39
  var crypto = __toESM(require("crypto"));
40
40
  var import_url = require("url");
41
41
  var name = "cat-raising";
42
- var Config = import_koishi.Schema.object({
43
- targetQQ: import_koishi.Schema.string().description("目标QQ号或QQ群号").required(),
44
- isGroup: import_koishi.Schema.boolean().description("是否为QQ").default(false),
45
- monitorGroups: import_koishi.Schema.array(import_koishi.Schema.object({
46
- groupId: import_koishi.Schema.string().description("要监听的 QQ 群号").required(),
47
- sendHelperMessages: import_koishi.Schema.boolean().description("是否在此群内发送“看到啦”之类的辅助/警告消息").default(true)
48
- })).description("监听的群组列表及其配置").required(),
49
- historySize: import_koishi.Schema.number().description("用于防复读的历史记录大小,防止短期内对同一活动重复转发").default(30).min(5).max(100),
50
- biliAccessKeys: import_koishi.Schema.array(import_koishi.Schema.object({
51
- key: import_koishi.Schema.string().description("Bilibili access_key").required(),
52
- remark: import_koishi.Schema.string().description("对此 access_key 的备注,例如所属账号")
53
- })).description("用于发送B站弹幕的 access_key 列表。插件会为列表中的每个 key 发送弹幕。如果留空,则不执行发送弹幕功能。").default([])
54
- });
42
+ var Config = import_koishi.Schema.intersect([
43
+ import_koishi.Schema.object({
44
+ targetQQ: import_koishi.Schema.string().description("转发目标:填 QQ 或 群号").role("link").required(),
45
+ isGroup: import_koishi.Schema.boolean().description("目标类型:群聊则勾选").default(false)
46
+ }).description("基础设置"),
47
+ import_koishi.Schema.object({
48
+ monitorGroups: import_koishi.Schema.array(import_koishi.Schema.object({
49
+ groupId: import_koishi.Schema.string().description("监听群号").role("link").required(),
50
+ sendHelperMessages: import_koishi.Schema.boolean().description("在群内发送「直播间/主播/投稿数」等提示消息").default(true)
51
+ })).description("监听群组").required(),
52
+ historySize: import_koishi.Schema.number().description("防复读历史条数(避免短期重复转发)").role("slider", { min: 5, max: 100, step: 1 }).default(30)
53
+ }).description("监听设置"),
54
+ import_koishi.Schema.object({
55
+ biliAccessKeys: import_koishi.Schema.array(import_koishi.Schema.object({
56
+ key: import_koishi.Schema.string().description("Bilibili access_key").required(),
57
+ remark: import_koishi.Schema.string().description("备注(例如所属账号)")
58
+ })).description("弹幕账号配置(留空则不发弹幕)").default([]),
59
+ danmakuDelayMinSeconds: import_koishi.Schema.number().description("弹幕延迟下限(秒)").role("slider", { min: 0, max: 60, step: 1 }).default(8),
60
+ danmakuDelayMaxSeconds: import_koishi.Schema.number().description("弹幕延迟上限(秒)").role("slider", { min: 0, max: 60, step: 1 }).default(10)
61
+ }).description("弹幕设置"),
62
+ import_koishi.Schema.object({
63
+ hardRejectionKeywords: import_koishi.Schema.array(import_koishi.Schema.string()).description("命中则忽略的关键词(硬拒绝)").default(["发言榜单", "投稿数:", "预告"])
64
+ }).description("过滤规则")
65
+ ]);
55
66
  function preprocessChineseNumerals(text) {
56
67
  const numMap = { "零": 0, "一": 1, "二": 2, "两": 2, "三": 3, "四": 4, "五": 5, "六": 6, "七": 7, "八": 8, "九": 9 };
57
68
  const unitMap = {
@@ -151,7 +162,6 @@ function parseEventFromText(text) {
151
162
  return allRewards.length > 0 ? { dateTime: globalDateTime || "时间未知", rewards: allRewards } : null;
152
163
  }
153
164
  __name(parseEventFromText, "parseEventFromText");
154
- var HARD_REJECTION_KEYWORDS = ["发言榜单", "投稿数:"];
155
165
  var REJECTION_KEYWORDS = ["签到", "打卡"];
156
166
  var OVERRIDE_KEYWORDS = ["神金", "发"];
157
167
  var CHECK_IN_REJECTION_REGEX = /\b\d{2,3}\s*\+/;
@@ -237,28 +247,47 @@ async function fetchBilibiliInfo(ctx, roomId) {
237
247
  let uid;
238
248
  let anchorName = "未知";
239
249
  try {
240
- const infoByRoom = await ctx.http.get(
241
- `https://api.live.bilibili.com/xlive/web-room/v1/index/getInfoByRoom?room_id=${roomId}`,
250
+ const anchorInRoom = await ctx.http.get(
251
+ `https://api.live.bilibili.com/live_user/v1/UserInfo/get_anchor_in_room?roomid=${roomId}`,
242
252
  { headers: commonHeaders }
243
253
  );
244
- uid = infoByRoom?.data?.room_info?.uid || uid;
245
- anchorName = infoByRoom?.data?.anchor_info?.base_info?.uname || infoByRoom?.data?.anchor_info?.uname || anchorName;
254
+ uid = anchorInRoom?.data?.info?.uid || uid;
255
+ anchorName = anchorInRoom?.data?.info?.uname || anchorName;
246
256
  } catch {
247
257
  }
248
258
  if (!uid || anchorName === "未知") {
249
259
  try {
250
- const initInfo = await ctx.http.get(
251
- `https://api.live.bilibili.com/room/v1/Room/room_init?id=${roomId}`,
260
+ const roomInfo = await ctx.http.get(
261
+ `https://api.live.bilibili.com/room/v1/Room/get_info?room_id=${roomId}`,
252
262
  { headers: commonHeaders }
253
263
  );
254
- uid = initInfo?.data?.uid || uid;
255
- const roomInfo2 = await ctx.http.get(
256
- `https://api.live.bilibili.com/room/v1/Room/get_info?room_id=${initInfo?.data?.room_id || roomId}`,
264
+ uid = roomInfo?.data?.uid || uid;
265
+ anchorName = roomInfo?.data?.uname || anchorName;
266
+ } catch {
267
+ }
268
+ }
269
+ if (anchorName === "未知" || !uid) {
270
+ await sleep(300);
271
+ try {
272
+ const anchorInRoom2 = await ctx.http.get(
273
+ `https://api.live.bilibili.com/live_user/v1/UserInfo/get_anchor_in_room?roomid=${roomId}`,
257
274
  { headers: commonHeaders }
258
275
  );
259
- anchorName = roomInfo2?.data?.uname || anchorName;
276
+ uid = uid || anchorInRoom2?.data?.info?.uid;
277
+ anchorName = anchorInRoom2?.data?.info?.uname || anchorName;
260
278
  } catch {
261
279
  }
280
+ if (!uid || anchorName === "未知") {
281
+ try {
282
+ const roomInfo2 = await ctx.http.get(
283
+ `https://api.live.bilibili.com/room/v1/Room/get_info?room_id=${roomId}`,
284
+ { headers: commonHeaders }
285
+ );
286
+ uid = uid || roomInfo2?.data?.uid;
287
+ anchorName = roomInfo2?.data?.uname || anchorName;
288
+ } catch {
289
+ }
290
+ }
262
291
  }
263
292
  if (!uid) throw new Error("无法从房间信息中获取UID");
264
293
  const statsInfo = await ctx.http.get(
@@ -273,93 +302,6 @@ async function fetchBilibiliInfo(ctx, roomId) {
273
302
  );
274
303
  const videoCount = statsInfo?.data?.video;
275
304
  if (videoCount === void 0) throw new Error("无法从空间信息中获取投稿数");
276
- try {
277
- const anchorInfo = await ctx.http.get(
278
- `https://api.bilibili.com/x/space/acc/info?mid=${uid}`,
279
- {
280
- headers: {
281
- ...commonHeaders,
282
- Origin: "https://space.bilibili.com",
283
- Referer: `https://space.bilibili.com/${uid}`
284
- }
285
- }
286
- );
287
- anchorName = anchorInfo?.data?.name || anchorName;
288
- } catch {
289
- }
290
- if (anchorName === "未知") {
291
- try {
292
- const liveUser = await ctx.http.get(
293
- `https://api.live.bilibili.com/live_user/v3/UserInfo/get_info?uid=${uid}`,
294
- {
295
- headers: {
296
- ...commonHeaders,
297
- Origin: "https://live.bilibili.com",
298
- Referer: `https://live.bilibili.com/${roomId}`
299
- }
300
- }
301
- );
302
- anchorName = liveUser?.data?.info?.uname || anchorName;
303
- } catch {
304
- }
305
- }
306
- if (anchorName === "未知") {
307
- try {
308
- const baseInfo = await ctx.http.get(
309
- `https://api.live.bilibili.com/xlive/web-room/v1/index/getRoomBaseInfo?room_id=${roomId}`,
310
- { headers: commonHeaders }
311
- );
312
- anchorName = baseInfo?.data?.anchor_info?.base_info?.uname || baseInfo?.data?.anchor_info?.uname || baseInfo?.data?.room_info?.uname || baseInfo?.data?.uname || anchorName;
313
- } catch {
314
- }
315
- }
316
- if (anchorName === "未知") {
317
- await sleep(300);
318
- try {
319
- const infoByRoom2 = await ctx.http.get(
320
- `https://api.live.bilibili.com/xlive/web-room/v1/index/getInfoByRoom?room_id=${roomId}`,
321
- { headers: commonHeaders }
322
- );
323
- anchorName = infoByRoom2?.data?.anchor_info?.base_info?.uname || infoByRoom2?.data?.anchor_info?.uname || anchorName;
324
- } catch {
325
- }
326
- try {
327
- const anchorInRoom = await ctx.http.get(
328
- `https://api.live.bilibili.com/live_user/v1/guard/get_anchor_in_room?roomid=${roomId}`,
329
- { headers: commonHeaders }
330
- );
331
- anchorName = anchorInRoom?.data?.info?.uname || anchorName;
332
- } catch {
333
- }
334
- try {
335
- const anchorInfo2 = await ctx.http.get(
336
- `https://api.bilibili.com/x/space/acc/info?mid=${uid}`,
337
- {
338
- headers: {
339
- ...commonHeaders,
340
- Origin: "https://space.bilibili.com",
341
- Referer: `https://space.bilibili.com/${uid}`
342
- }
343
- }
344
- );
345
- anchorName = anchorInfo2?.data?.name || anchorName;
346
- } catch {
347
- }
348
- try {
349
- const liveUser2 = await ctx.http.get(
350
- `https://api.live.bilibili.com/live_user/v3/UserInfo/get_info?uid=${uid}`,
351
- {
352
- headers: {
353
- ...commonHeaders,
354
- Origin: "https://live.bilibili.com",
355
- Referer: `https://live.bilibili.com/${roomId}`
356
- }
357
- }
358
- );
359
- anchorName = liveUser2?.data?.info?.uname || anchorName;
360
- } catch {
361
- }
362
- }
363
305
  return { videoCount, anchorName };
364
306
  } catch (error) {
365
307
  const status = error?.response?.status;
@@ -378,7 +320,7 @@ function apply(ctx, config) {
378
320
  if (!groupConfig) return;
379
321
  const strippedContent = session.stripped.content;
380
322
  if (!strippedContent.trim()) return;
381
- if (HARD_REJECTION_KEYWORDS.some((keyword) => strippedContent.includes(keyword))) return;
323
+ if (config.hardRejectionKeywords.some((keyword) => strippedContent.includes(keyword))) return;
382
324
  if (CHECK_IN_REJECTION_REGEX.test(strippedContent)) {
383
325
  ctx.logger.info(`[忽略] 消息包含签到模式 (如 110+),判定为非奖励信息。内容: "${strippedContent.replace(/\n/g, " ")}"`);
384
326
  return;
@@ -434,11 +376,16 @@ function apply(ctx, config) {
434
376
  });
435
377
  if (forwardedHistory.length > config.historySize) forwardedHistory.shift();
436
378
  if (config.biliAccessKeys && config.biliAccessKeys.length > 0) {
437
- ctx.logger.info(`[弹幕] 准备为 ${config.biliAccessKeys.length} 个账号发送弹幕到直播间 ${roomId}...`);
438
- const danmakuPromises = config.biliAccessKeys.map(
439
- (keyConfig) => sendBilibiliDanmaku(ctx, keyConfig, roomId, "喵喵喵")
440
- );
441
- Promise.allSettled(danmakuPromises);
379
+ const min = Math.max(0, config.danmakuDelayMinSeconds ?? 8);
380
+ const max = Math.max(min, config.danmakuDelayMaxSeconds ?? 10);
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);
442
389
  }
443
390
  } catch (error) {
444
391
  session.send("🐱 - 转发失败,请检查目标QQ/群号配置是否正确");
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-cat-raising",
3
3
  "description": "",
4
- "version": "1.3.5",
4
+ "version": "1.3.7",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [