koishi-plugin-lili-hub 0.3.0 → 0.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.
Files changed (2) hide show
  1. package/lib/index.js +442 -39
  2. package/package.json +1 -1
package/lib/index.js CHANGED
@@ -227,7 +227,9 @@ var Config = import_koishi.Schema.object({
227
227
  drunkMisfireRate: import_koishi.Schema.number().description("\u9189\u9152\u540E\u5B50\u5F39\u54D1\u5F39\u7684\u6982\u7387 (0-1)").default(0.35).min(0).max(1).step(0.01),
228
228
  drunkRampageRate: import_koishi.Schema.number().description("\u9189\u9152\u540E\u8FDE\u7EED\u5F00\u67AA\u7684\u6982\u7387 (0-1)").default(0.3).min(0).max(1).step(0.01),
229
229
  drunkRevengeRate: import_koishi.Schema.number().description("\u9189\u9152\u540E\u6CE2\u53CA\u5386\u53F2\u53C2\u4E0E\u8005\u7684\u6982\u7387 (0-1)").default(0.2).min(0).max(1).step(0.01),
230
- maxHistoryRounds: import_koishi.Schema.number().description("\u4FDD\u7559\u591A\u5C11\u8F6E\u5386\u53F2\u8BB0\u5F55\uFF08\u7528\u4E8E\u62A5\u4EC7\u673A\u5236\uFF09").default(3).min(1).max(20)
230
+ maxHistoryRounds: import_koishi.Schema.number().description("\u4FDD\u7559\u591A\u5C11\u8F6E\u5386\u53F2\u8BB0\u5F55\uFF08\u7528\u4E8E\u62A5\u4EC7\u673A\u5236\uFF09").default(3).min(1).max(20),
231
+ groups: import_koishi.Schema.string().description("\u7FA4\u53F7\u5217\u8868\uFF08\u9017\u53F7\u5206\u9694\uFF0C\u7559\u7A7A\u4E3A\u6240\u6709\u7FA4\uFF09").default(""),
232
+ groupMode: import_koishi.Schema.union(["whitelist", "blacklist"]).description("\u7FA4\u7EC4\u8FC7\u6EE4\u6A21\u5F0F\uFF1A\u767D\u540D\u5355\uFF08\u4EC5\u5728\u5217\u8868\u4E2D\u7684\u7FA4\u751F\u6548\uFF09/ \u9ED1\u540D\u5355\uFF08\u5217\u8868\u4E2D\u7684\u7FA4\u4E0D\u751F\u6548\uFF09").default("whitelist")
231
233
  }).description("\u{1F52B} \u4E3D\u4E3D\u8F6E\u76D8"),
232
234
  voice: import_koishi.Schema.object({
233
235
  voiceEnabled: import_koishi.Schema.boolean().description("\u662F\u5426\u64AD\u653E\u4E3D\u4E3D\u8BED\u97F3").default(true),
@@ -251,7 +253,8 @@ var Config = import_koishi.Schema.object({
251
253
  }).description("\u{1F50A} \u8BED\u97F3\u4E0E\u53F0\u8BCD\u8BBE\u7F6E"),
252
254
  voteMute: import_koishi.Schema.object({
253
255
  enabled: import_koishi.Schema.boolean().description("\u542F\u7528\u7D2F\u8BA1\u6295\u7968\u7981\u8A00\u529F\u80FD").default(false),
254
- groups: import_koishi.Schema.string().description("\u767D\u540D\u5355\u7FA4\u53F7\uFF08\u9017\u53F7\u5206\u9694\uFF0C\u7559\u7A7A\u4E3A\u6240\u6709\u7FA4\uFF09").default(""),
256
+ groups: import_koishi.Schema.string().description("\u7FA4\u53F7\u5217\u8868\uFF08\u9017\u53F7\u5206\u9694\uFF0C\u7559\u7A7A\u4E3A\u6240\u6709\u7FA4\uFF09").default(""),
257
+ groupMode: import_koishi.Schema.union(["whitelist", "blacklist"]).description("\u7FA4\u7EC4\u8FC7\u6EE4\u6A21\u5F0F\uFF1A\u767D\u540D\u5355 / \u9ED1\u540D\u5355").default("whitelist"),
255
258
  voteWindowDays: import_koishi.Schema.number().description("\u7D2F\u8BA1\u6295\u7968\u7684\u65F6\u95F4\u7A97\u53E3\uFF08\u5929\uFF09").default(7).min(1).max(365),
256
259
  muteThreshold: import_koishi.Schema.number().description("\u7D2F\u8BA1\u7981\u8A00\u7968 \u2265 \u6B64\u503C\u65F6\u6267\u884C\u7981\u8A00").default(5).min(1).max(1e3),
257
260
  unmuteThreshold: import_koishi.Schema.number().description("\u7981\u8A00\u7968 \u2264 \u6B64\u503C\u65F6\u81EA\u52A8\u89E3\u7981").default(2).min(0).max(1e3),
@@ -259,19 +262,37 @@ var Config = import_koishi.Schema.object({
259
262
  }).description("\u{1F5F3}\uFE0F \u7D2F\u8BA1\u6295\u7968\u7981\u8A00"),
260
263
  fold: import_koishi.Schema.object({
261
264
  enabled: import_koishi.Schema.boolean().description("\u542F\u7528\u4E3D\u4E3D\u6298\u53E0\u529F\u80FD").default(false),
262
- groups: import_koishi.Schema.string().description("\u767D\u540D\u5355\u7FA4\u53F7\uFF08\u9017\u53F7\u5206\u9694\uFF09").default(""),
265
+ groups: import_koishi.Schema.string().description("\u7FA4\u53F7\u5217\u8868\uFF08\u9017\u53F7\u5206\u9694\uFF09").default(""),
266
+ groupMode: import_koishi.Schema.union(["whitelist", "blacklist"]).description("\u7FA4\u7EC4\u8FC7\u6EE4\u6A21\u5F0F\uFF1A\u767D\u540D\u5355 / \u9ED1\u540D\u5355").default("whitelist"),
263
267
  minutes: import_koishi.Schema.number().description("\u6298\u53E0\u65F6\u95F4\u8303\u56F4\uFF08\u5206\u949F\uFF09").default(10).min(1).max(1440)
264
268
  }).description("\u{1F4E6} \u4E3D\u4E3D\u6298\u53E0"),
265
269
  imitation: import_koishi.Schema.object({
266
270
  enabled: import_koishi.Schema.boolean().description("\u542F\u7528\u6A21\u4EFF\u7FA4\u53CB\u8BF4\u8BDD\u529F\u80FD").default(false),
267
- groups: import_koishi.Schema.string().description("\u767D\u540D\u5355\u7FA4\u53F7\uFF08\u9017\u53F7\u5206\u9694\uFF09").default(""),
271
+ groups: import_koishi.Schema.string().description("\u7FA4\u53F7\u5217\u8868\uFF08\u9017\u53F7\u5206\u9694\uFF09").default(""),
272
+ groupMode: import_koishi.Schema.union(["whitelist", "blacklist"]).description("\u7FA4\u7EC4\u8FC7\u6EE4\u6A21\u5F0F\uFF1A\u767D\u540D\u5355 / \u9ED1\u540D\u5355").default("whitelist"),
268
273
  interval: import_koishi.Schema.number().description("\u6BCF\u591A\u5C11\u6761\u6D88\u606F\u68C0\u67E5\u4E00\u6B21\u6A21\u4EFF").default(50).min(1).max(1e4),
269
274
  rate: import_koishi.Schema.number().description("\u6A21\u4EFF\u89E6\u53D1\u6982\u7387 (0-1)").default(0.05).min(0).max(1).step(0.01)
270
275
  }).description("\u{1F5E3}\uFE0F \u6A21\u4EFF\u7FA4\u53CB"),
271
276
  dedup: import_koishi.Schema.object({
272
277
  enabled: import_koishi.Schema.boolean().description("\u542F\u7528\u5408\u5E76\u6D88\u606F\u67E5\u91CD\u529F\u80FD\uFF08\u6C34\u8FC7\u4E86\uFF09").default(false),
273
- groups: import_koishi.Schema.string().description("\u767D\u540D\u5355\u7FA4\u53F7\uFF08\u9017\u53F7\u5206\u9694\uFF09").default("")
274
- }).description("\u{1F4A7} \u67E5\u91CD")
278
+ groups: import_koishi.Schema.string().description("\u7FA4\u53F7\u5217\u8868\uFF08\u9017\u53F7\u5206\u9694\uFF09").default(""),
279
+ groupMode: import_koishi.Schema.union(["whitelist", "blacklist"]).description("\u7FA4\u7EC4\u8FC7\u6EE4\u6A21\u5F0F\uFF1A\u767D\u540D\u5355 / \u9ED1\u540D\u5355").default("whitelist")
280
+ }).description("\u{1F4A7} \u67E5\u91CD"),
281
+ duel: import_koishi.Schema.object({
282
+ enabled: import_koishi.Schema.boolean().description("\u542F\u7528\u4E3D\u4E3D\u5BF9\u51B3\u529F\u80FD").default(false),
283
+ groups: import_koishi.Schema.string().description("\u7FA4\u53F7\u5217\u8868\uFF08\u9017\u53F7\u5206\u9694\uFF09").default(""),
284
+ groupMode: import_koishi.Schema.union(["whitelist", "blacklist"]).description("\u7FA4\u7EC4\u8FC7\u6EE4\u6A21\u5F0F\uFF1A\u767D\u540D\u5355 / \u9ED1\u540D\u5355").default("whitelist"),
285
+ countdownSeconds: import_koishi.Schema.number().description("\u5012\u8BA1\u65F6\u79D2\u6570").default(5).min(1).max(30),
286
+ acceptTimeoutSeconds: import_koishi.Schema.number().description("\u7B49\u5F85\u63A5\u53D7\u8D85\u65F6\uFF08\u79D2\uFF09").default(30).min(5).max(120),
287
+ drawTimeoutSeconds: import_koishi.Schema.number().description("\u62D4\u67AA\u7A97\u53E3\u8D85\u65F6\uFF08\u79D2\uFF09").default(10).min(3).max(60),
288
+ earlyDrawMuteSeconds: import_koishi.Schema.number().description("\u63D0\u524D\u62D4\u67AA\u7981\u8A00\u65F6\u957F\uFF08\u79D2\uFF09").default(600).min(0).max(2592e3),
289
+ lateMuteSeconds: import_koishi.Schema.number().description("\u665A\u62D4\u67AA/\u672A\u62D4\u67AA\u7981\u8A00\u65F6\u957F\uFF08\u79D2\uFF09").default(300).min(0).max(2592e3)
290
+ }).description("\u2694\uFE0F \u4E3D\u4E3D\u5BF9\u51B3"),
291
+ chain: import_koishi.Schema.object({
292
+ enabled: import_koishi.Schema.boolean().description("\u542F\u7528\u7FA4\u63A5\u9F99\u529F\u80FD").default(false),
293
+ groups: import_koishi.Schema.string().description("\u7FA4\u53F7\u5217\u8868\uFF08\u9017\u53F7\u5206\u9694\uFF09").default(""),
294
+ groupMode: import_koishi.Schema.union(["whitelist", "blacklist"]).description("\u7FA4\u7EC4\u8FC7\u6EE4\u6A21\u5F0F\uFF1A\u767D\u540D\u5355 / \u9ED1\u540D\u5355").default("whitelist")
295
+ }).description("\u{1F4DD} \u7FA4\u63A5\u9F99")
275
296
  });
276
297
  function getGroupId(session) {
277
298
  return session.guildId || session.channelId;
@@ -289,9 +310,12 @@ function parseGroupList(raw) {
289
310
  function normalizeText(text) {
290
311
  return text.replace(/\[CQ:[^\]]*\]/g, "").replace(/<[^>]*>/g, " ").replace(/\s+/g, " ").trim();
291
312
  }
292
- function isGroupAllowed(gid, groups) {
313
+ function isGroupAllowed(gid, groups, mode = "whitelist") {
293
314
  const list = parseGroupList(groups);
294
315
  if (list.length === 0) return true;
316
+ if (mode === "blacklist") {
317
+ return !list.includes(gid);
318
+ }
295
319
  return list.includes(gid);
296
320
  }
297
321
  function getMultiKillEvent(v, killCount) {
@@ -354,9 +378,30 @@ function apply(ctx, config) {
354
378
  voteType: "string",
355
379
  timestamp: "unsigned"
356
380
  }, { autoInc: true });
381
+ ctx.model.extend("lili_duel", {
382
+ id: "unsigned",
383
+ gid: "string",
384
+ winnerId: "string",
385
+ winnerName: "string",
386
+ loserId: "string",
387
+ loserName: "string",
388
+ winnerTime: "unsigned",
389
+ loserTime: "unsigned",
390
+ timestamp: "unsigned"
391
+ }, { autoInc: true });
357
392
  const drinkStates = /* @__PURE__ */ new Map();
358
393
  const rouletteStates = /* @__PURE__ */ new Map();
359
394
  const imitationCounters = /* @__PURE__ */ new Map();
395
+ const duelStates = /* @__PURE__ */ new Map();
396
+ const chainStates = /* @__PURE__ */ new Map();
397
+ function formatChain(chain) {
398
+ const lines = [chain.content];
399
+ for (let i = 0; i < chain.participants.length; i++) {
400
+ const p = chain.participants[i];
401
+ lines.push(`${i + 1}.${p.userName}\uFF08${p.userId}\uFF09`);
402
+ }
403
+ return lines.join("\n");
404
+ }
360
405
  function isDrunk(gid) {
361
406
  const s = drinkStates.get(gid);
362
407
  if (!s || s.drunkUntil === 0) return false;
@@ -535,6 +580,7 @@ function apply(ctx, config) {
535
580
  ctx.command("\u4E3D\u4E3D\u8F6E\u76D8", "\u4FC4\u7F57\u65AF\u8F6E\u76D8\u8D4C").alias("\u4E3D\u4E3D\u5F00\u67AA").action(async ({ session }) => {
536
581
  if (!session?.userId) return;
537
582
  const gid = getGroupId(session);
583
+ if (!isGroupAllowed(gid, config.roulette.groups, config.roulette.groupMode)) return;
538
584
  if (!rouletteStates.has(gid)) {
539
585
  const bullet = randInt(1, config.roulette.chamberSize);
540
586
  const startTime = Date.now();
@@ -568,6 +614,7 @@ function apply(ctx, config) {
568
614
  ctx.command("\u4E3D\u4E3D\u559D\u9152", "\u4E3D\u4E3D\u5F00\u59CB\u559D\u9152\uFF0C\u9700\u8981\u559D 2-4 \u676F\u624D\u4F1A\u559D\u9189").action(async ({ session }) => {
569
615
  if (!session?.userId) return;
570
616
  const gid = getGroupId(session);
617
+ if (!isGroupAllowed(gid, config.roulette.groups, config.roulette.groupMode)) return;
571
618
  if (isDrunk(gid)) {
572
619
  await playEffect(session, config.voice.stillDrunk, 500);
573
620
  return;
@@ -616,7 +663,7 @@ function apply(ctx, config) {
616
663
  const gid = getGroupId(session);
617
664
  const vm = config.voteMute;
618
665
  if (!vm.enabled) return;
619
- if (!isGroupAllowed(gid, vm.groups)) return;
666
+ if (!isGroupAllowed(gid, vm.groups, vm.groupMode)) return;
620
667
  const targetId = extractTarget(session, targetUser);
621
668
  if (!targetId) return "\u{1F507} \u65E0\u6CD5\u89E3\u6790\u76EE\u6807\u7528\u6237\u3002\u8BF7\u4F7F\u7528 @ \u529F\u80FD\u9009\u62E9\u76EE\u6807\u3002";
622
669
  if (targetId === session.userId) return "\u{1F507} \u4E0D\u80FD\u7ED9\u81EA\u5DF1\u6295\u7968\u3002";
@@ -648,7 +695,7 @@ ${import_koishi.segment.at(targetId)} \u5DF2\u88AB\u7981\u8A00 ${timeStr}\u3002`
648
695
  const gid = getGroupId(session);
649
696
  const vm = config.voteMute;
650
697
  if (!vm.enabled) return;
651
- if (!isGroupAllowed(gid, vm.groups)) return;
698
+ if (!isGroupAllowed(gid, vm.groups, vm.groupMode)) return;
652
699
  const targetId = extractTarget(session, targetUser);
653
700
  if (!targetId) return "\u{1F50A} \u65E0\u6CD5\u89E3\u6790\u76EE\u6807\u7528\u6237\u3002\u8BF7\u4F7F\u7528 @ \u529F\u80FD\u9009\u62E9\u76EE\u6807\u3002";
654
701
  try {
@@ -669,22 +716,26 @@ ${import_koishi.segment.at(targetId)} \u5DF2\u88AB\u89E3\u9664\u7981\u8A00\u3002
669
716
  return `\u{1F50A} \u5DF2\u64A4\u56DE\u4F60\u5BF9 ${import_koishi.segment.at(targetId)} \u7684\u7981\u8A00\u7968\u3002
670
717
  \u{1F4CA} \u5F53\u524D\u7981\u8A00\u7968\uFF1A${muteCount}\uFF08\u9700 \u2264 ${vm.unmuteThreshold} \u624D\u4F1A\u89E3\u7981\uFF09`;
671
718
  });
672
- ctx.command("\u4E3D\u4E3D\u6298\u53E0 <user:user>", "\u5408\u5E76\u8F6C\u53D1\u5E76\u64A4\u56DE\u76EE\u6807\u7528\u6237\u6700\u8FD1 X \u5206\u949F\u5185\u7684\u6D88\u606F").action(async ({ session }, targetUser) => {
719
+ ctx.command("\u4E3D\u4E3D\u6298\u53E0 <user:user> [minutes:number]", "\u5408\u5E76\u8F6C\u53D1\u5E76\u64A4\u56DE\u76EE\u6807\u7528\u6237\u6700\u8FD1 X \u5206\u949F\u5185\u7684\u6D88\u606F").action(async ({ session }, targetUser, minutesArg) => {
673
720
  if (!session?.userId) return;
674
721
  const gid = getGroupId(session);
675
722
  const fc = config.fold;
676
723
  if (!fc.enabled) return;
677
- if (!isGroupAllowed(gid, fc.groups)) return;
724
+ if (!isGroupAllowed(gid, fc.groups, fc.groupMode)) return;
678
725
  const targetId = extractTarget(session, targetUser);
679
726
  if (!targetId) return "\u{1F4E6} \u65E0\u6CD5\u89E3\u6790\u76EE\u6807\u7528\u6237\u3002\u8BF7\u4F7F\u7528 @ \u529F\u80FD\u6216\u76F4\u63A5\u8F93\u5165 QQ \u53F7\u3002";
680
- const since = Date.now() - fc.minutes * 60 * 1e3;
727
+ const foldedMinutes = typeof minutesArg === "number" && minutesArg >= 1 && minutesArg <= 1440 ? minutesArg : fc.minutes;
728
+ const since = Date.now() - foldedMinutes * 60 * 1e3;
729
+ dbg("\u6298\u53E0-\u67E5\u8BE2", { gid, targetId, since: new Date(since).toLocaleTimeString("zh-CN"), foldedMinutes });
681
730
  let messages;
682
731
  try {
683
732
  messages = await ctx.database.get("lili_message", { gid, userId: targetId, timestamp: { $gte: since } }, { sort: { timestamp: "asc" }, limit: 100 });
684
- } catch {
733
+ } catch (e) {
734
+ dbg("\u6298\u53E0-\u67E5\u8BE2\u5931\u8D25", { gid, error: String(e) });
685
735
  return "\u{1F4E6} \u67E5\u8BE2\u6D88\u606F\u5931\u8D25\u3002";
686
736
  }
687
- if (!messages || messages.length === 0) return `\u{1F4E6} ${import_koishi.segment.at(targetId)} \u6700\u8FD1 ${fc.minutes} \u5206\u949F\u5185\u6CA1\u6709\u53D1\u8A00\u8BB0\u5F55\u3002`;
737
+ dbg("\u6298\u53E0-\u67E5\u8BE2\u7ED3\u679C", { gid, targetId, count: messages?.length || 0 });
738
+ if (!messages || messages.length === 0) return `\u{1F4E6} ${import_koishi.segment.at(targetId)} \u6700\u8FD1 ${foldedMinutes} \u5206\u949F\u5185\u6CA1\u6709\u53D1\u8A00\u8BB0\u5F55\u3002`;
688
739
  const nodes = messages.map((m) => ({
689
740
  type: "node",
690
741
  data: { name: m.userName, uin: m.userId, content: m.content, time: String(Math.floor(m.timestamp / 1e3)) }
@@ -704,7 +755,7 @@ ${import_koishi.segment.at(targetId)} \u5DF2\u88AB\u89E3\u9664\u7981\u8A00\u3002
704
755
  const time = new Date(m.timestamp).toLocaleTimeString("zh-CN", { hour: "2-digit", minute: "2-digit" });
705
756
  return `[${time}] ${m.userName}\uFF1A${m.content}`;
706
757
  });
707
- await session.send(`\u{1F4E6} \u4E3D\u4E3D\u6298\u53E0\u4E86 ${import_koishi.segment.at(targetId)} \u6700\u8FD1 ${fc.minutes} \u5206\u949F\u7684\u6D88\u606F\uFF1A
758
+ await session.send(`\u{1F4E6} \u4E3D\u4E3D\u6298\u53E0\u4E86 ${import_koishi.segment.at(targetId)} \u6700\u8FD1 ${foldedMinutes} \u5206\u949F\u7684\u6D88\u606F\uFF1A
708
759
  ` + lines.join("\n"));
709
760
  }
710
761
  for (const m of messages) {
@@ -716,6 +767,348 @@ ${import_koishi.segment.at(targetId)} \u5DF2\u88AB\u89E3\u9664\u7981\u8A00\u3002
716
767
  }
717
768
  }
718
769
  });
770
+ ctx.command("\u4E3D\u4E3D\u5BF9\u51B3 <user:user>", "\u53D1\u8D77\u5BF9\u51B3").action(async ({ session }, targetUser) => {
771
+ if (!session?.userId) return;
772
+ const gid = getGroupId(session);
773
+ const dc = config.duel;
774
+ if (!dc.enabled) return;
775
+ if (!isGroupAllowed(gid, dc.groups, dc.groupMode)) return;
776
+ const targetId = extractTarget(session, targetUser);
777
+ if (!targetId) return "\u2694\uFE0F \u65E0\u6CD5\u89E3\u6790\u76EE\u6807\u7528\u6237\u3002\u8BF7\u4F7F\u7528 @ \u529F\u80FD\u9009\u62E9\u76EE\u6807\u3002";
778
+ if (targetId === String(session.userId)) return "\u2694\uFE0F \u4E0D\u80FD\u5411\u81EA\u5DF1\u53D1\u8D77\u5BF9\u51B3\uFF01";
779
+ const botId = session.bot.userId || session.bot.selfId;
780
+ if (botId && targetId === String(botId)) return "\u2694\uFE0F \u4E0D\u80FD\u5411 Bot \u53D1\u8D77\u5BF9\u51B3\uFF01";
781
+ const existing = duelStates.get(gid);
782
+ if (existing && existing.phase !== "finished") {
783
+ return "\u2694\uFE0F \u672C\u7FA4\u6B63\u5728\u8FDB\u884C\u4E00\u573A\u5BF9\u51B3\uFF0C\u8BF7\u7B49\u5F85\u7ED3\u675F\u540E\u518D\u53D1\u8D77\uFF01";
784
+ }
785
+ const challengerId = String(session.userId);
786
+ const challengerName = session.username || challengerId;
787
+ const targetName = session.elements?.find((el) => el.type === "at")?.attrs?.name || targetId;
788
+ const duel = {
789
+ challengerId,
790
+ challengerName,
791
+ targetId,
792
+ targetName,
793
+ session,
794
+ phase: "waiting_accept",
795
+ countdownEndTime: 0,
796
+ earlyDrawers: /* @__PURE__ */ new Set(),
797
+ drawTimes: /* @__PURE__ */ new Map(),
798
+ acceptTimer: null,
799
+ countdownTimer: null,
800
+ drawTimer: null
801
+ };
802
+ duel.acceptTimer = setTimeout(() => {
803
+ const d = duelStates.get(gid);
804
+ if (d && d.phase === "waiting_accept") {
805
+ session.send(`\u23F0 \u5BF9\u51B3\u8D85\u65F6\uFF01${targetName} \u672A\u5728 ${dc.acceptTimeoutSeconds} \u79D2\u5185\u63A5\u53D7\u6311\u6218\u3002`);
806
+ cleanupDuel(gid);
807
+ }
808
+ }, dc.acceptTimeoutSeconds * 1e3);
809
+ duelStates.set(gid, duel);
810
+ dbg("\u5BF9\u51B3-\u53D1\u8D77", { gid, challengerId, challengerName, targetId, targetName });
811
+ return `\u2694\uFE0F ${challengerName} \u5411 ${import_koishi.segment.at(targetId)} \u53D1\u8D77\u4E86\u5BF9\u51B3\uFF01
812
+ \u56DE\u590D"\u63A5\u53D7"\u5E94\u6218\uFF08${dc.acceptTimeoutSeconds}\u79D2\u5185\u6709\u6548\uFF09`;
813
+ });
814
+ ctx.command("\u4E3D\u4E3D\u5BF9\u51B3\u7FA4\u699C", "\u67E5\u770B\u672C\u7FA4\u5BF9\u51B3\u6392\u884C\u699C").action(async ({ session }) => {
815
+ const gid = getGroupId(session);
816
+ try {
817
+ const records = await ctx.database.get("lili_duel", { gid }, {
818
+ sort: { winnerTime: "asc" },
819
+ limit: 10
820
+ });
821
+ if (!records || records.length === 0) return "\u{1F3C6} \u672C\u7FA4\u6682\u65E0\u5BF9\u51B3\u8BB0\u5F55\u3002";
822
+ const lines = ["\u{1F3C6} \u4E3D\u4E3D\u5BF9\u51B3 \u7FA4\u6392\u884C\u699C"];
823
+ for (let i = 0; i < records.length; i++) {
824
+ const r = records[i];
825
+ const time = new Date(r.timestamp).toLocaleString("zh-CN", { month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit" });
826
+ lines.push(`${i + 1}. ${r.winnerName} ${r.winnerTime}ms \u26A1\u80DC ${r.loserName} ${r.loserTime >= 0 ? r.loserTime + "ms" : "\u672A\u62D4\u67AA"} ${time}`);
827
+ }
828
+ return lines.join("\n");
829
+ } catch {
830
+ return "\u{1F3C6} \u67E5\u8BE2\u6392\u884C\u699C\u5931\u8D25\u3002";
831
+ }
832
+ });
833
+ ctx.command("\u4E3D\u4E3D\u5BF9\u51B3\u603B\u699C", "\u67E5\u770B\u5168\u7FA4\u5BF9\u51B3\u6392\u884C\u699C").action(async () => {
834
+ try {
835
+ const records = await ctx.database.get("lili_duel", {}, {
836
+ sort: { winnerTime: "asc" },
837
+ limit: 10
838
+ });
839
+ if (!records || records.length === 0) return "\u{1F3C6} \u6682\u65E0\u5BF9\u51B3\u8BB0\u5F55\u3002";
840
+ const lines = ["\u{1F3C6} \u4E3D\u4E3D\u5BF9\u51B3 \u603B\u6392\u884C\u699C"];
841
+ for (let i = 0; i < records.length; i++) {
842
+ const r = records[i];
843
+ const time = new Date(r.timestamp).toLocaleString("zh-CN", { month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit" });
844
+ lines.push(`${i + 1}. ${r.winnerName} ${r.winnerTime}ms \u26A1\u80DC ${r.loserName} ${r.loserTime >= 0 ? r.loserTime + "ms" : "\u672A\u62D4\u67AA"} ${time}`);
845
+ }
846
+ return lines.join("\n");
847
+ } catch {
848
+ return "\u{1F3C6} \u67E5\u8BE2\u6392\u884C\u699C\u5931\u8D25\u3002";
849
+ }
850
+ });
851
+ function cleanupDuel(gid) {
852
+ const duel = duelStates.get(gid);
853
+ if (!duel) return;
854
+ if (duel.acceptTimer) clearTimeout(duel.acceptTimer);
855
+ if (duel.countdownTimer) clearTimeout(duel.countdownTimer);
856
+ if (duel.drawTimer) clearTimeout(duel.drawTimer);
857
+ duelStates.delete(gid);
858
+ }
859
+ async function resolveDuel(gid) {
860
+ const duel = duelStates.get(gid);
861
+ if (!duel || duel.phase === "finished") return;
862
+ duel.phase = "finished";
863
+ const dc = config.duel;
864
+ const { challengerId, challengerName, targetId, targetName, drawTimes, earlyDrawers, session } = duel;
865
+ if (duel.acceptTimer) clearTimeout(duel.acceptTimer);
866
+ if (duel.countdownTimer) clearTimeout(duel.countdownTimer);
867
+ if (duel.drawTimer) clearTimeout(duel.drawTimer);
868
+ const cTime = drawTimes.get(challengerId) ?? null;
869
+ const tTime = drawTimes.get(targetId) ?? null;
870
+ let winnerId, winnerName, loserId, loserName;
871
+ let winnerTime, loserTime;
872
+ let muteTargets = [];
873
+ let resultLines = [];
874
+ if (earlyDrawers.size > 0) {
875
+ for (const id of earlyDrawers) {
876
+ const name2 = id === challengerId ? challengerName : targetName;
877
+ muteTargets.push({ id, name: name2, duration: dc.earlyDrawMuteSeconds });
878
+ resultLines.push(`\u26A0\uFE0F ${name2} \u63D0\u524D\u62D4\u67AA\uFF01`);
879
+ }
880
+ const otherId = earlyDrawers.has(challengerId) ? targetId : challengerId;
881
+ const otherName = otherId === challengerId ? challengerName : targetName;
882
+ winnerId = otherId;
883
+ winnerName = otherName;
884
+ loserId = [...earlyDrawers][0];
885
+ loserName = loserId === challengerId ? challengerName : targetName;
886
+ winnerTime = drawTimes.get(otherId) ?? 0;
887
+ loserTime = -1;
888
+ for (const mt of muteTargets) {
889
+ await tryMute(session, mt.id, mt.duration);
890
+ resultLines.push(`\u{1F507} ${mt.name} \u88AB\u7981\u8A00 ${mt.duration} \u79D2\uFF01`);
891
+ }
892
+ } else if (cTime !== null && tTime !== null) {
893
+ if (cTime < tTime) {
894
+ winnerId = challengerId;
895
+ winnerName = challengerName;
896
+ winnerTime = cTime;
897
+ loserId = targetId;
898
+ loserName = targetName;
899
+ loserTime = tTime;
900
+ } else {
901
+ winnerId = targetId;
902
+ winnerName = targetName;
903
+ winnerTime = tTime;
904
+ loserId = challengerId;
905
+ loserName = challengerName;
906
+ loserTime = cTime;
907
+ }
908
+ resultLines.push(`\u{1F52B} ${winnerName} \u62D4\u67AA\u66F4\u5FEB\uFF01`);
909
+ muteTargets.push({ id: loserId, name: loserName, duration: dc.lateMuteSeconds });
910
+ } else if (cTime !== null) {
911
+ winnerId = challengerId;
912
+ winnerName = challengerName;
913
+ winnerTime = cTime;
914
+ loserId = targetId;
915
+ loserName = targetName;
916
+ loserTime = -1;
917
+ resultLines.push(`\u{1F52B} ${winnerName} \u62D4\u67AA\u4E86\uFF0C${loserName} \u6CA1\u6709\u62D4\u67AA\uFF01`);
918
+ muteTargets.push({ id: loserId, name: loserName, duration: dc.lateMuteSeconds });
919
+ } else if (tTime !== null) {
920
+ winnerId = targetId;
921
+ winnerName = targetName;
922
+ winnerTime = tTime;
923
+ loserId = challengerId;
924
+ loserName = challengerName;
925
+ loserTime = -1;
926
+ resultLines.push(`\u{1F52B} ${winnerName} \u62D4\u67AA\u4E86\uFF0C${loserName} \u6CA1\u6709\u62D4\u67AA\uFF01`);
927
+ muteTargets.push({ id: loserId, name: loserName, duration: dc.lateMuteSeconds });
928
+ } else {
929
+ resultLines.push("\u{1F52B} \u53CC\u65B9\u90FD\u6CA1\u6709\u62D4\u67AA\uFF01");
930
+ muteTargets.push(
931
+ { id: challengerId, name: challengerName, duration: dc.lateMuteSeconds },
932
+ { id: targetId, name: targetName, duration: dc.lateMuteSeconds }
933
+ );
934
+ winnerId = "";
935
+ winnerName = "";
936
+ winnerTime = 0;
937
+ loserId = "";
938
+ loserName = "";
939
+ loserTime = -1;
940
+ }
941
+ for (const mt of muteTargets) {
942
+ const muted = await tryMute(session, mt.id, mt.duration);
943
+ resultLines.push(muted ? `\u{1F507} ${mt.name} \u88AB\u7981\u8A00 ${mt.duration} \u79D2\uFF01` : `\u{1F507} ${mt.name} \u7981\u8A00\u5931\u8D25\uFF08\u6743\u9650\u4E0D\u8DB3\uFF09`);
944
+ }
945
+ if (cTime !== null) {
946
+ resultLines.push(`${challengerName} \u7528\u65F6\uFF1A${cTime}ms`);
947
+ } else {
948
+ resultLines.push(`${challengerName} \u672A\u62D4\u67AA`);
949
+ }
950
+ if (tTime !== null) {
951
+ resultLines.push(`${targetName} \u7528\u65F6\uFF1A${tTime}ms`);
952
+ } else {
953
+ resultLines.push(`${targetName} \u672A\u62D4\u67AA`);
954
+ }
955
+ if (winnerId && loserId) {
956
+ try {
957
+ await ctx.database.create("lili_duel", {
958
+ gid,
959
+ winnerId,
960
+ winnerName,
961
+ loserId,
962
+ loserName,
963
+ winnerTime,
964
+ loserTime,
965
+ timestamp: Date.now()
966
+ });
967
+ } catch (e) {
968
+ dbg("\u5BF9\u51B3\u8BB0\u5F55\u5165\u5E93\u5931\u8D25", { gid, error: String(e) });
969
+ }
970
+ }
971
+ await session.send(resultLines.join("\n"));
972
+ cleanupDuel(gid);
973
+ }
974
+ ctx.middleware(async (session, next) => {
975
+ if (!config.duel.enabled) return next();
976
+ const gid = getGroupId(session);
977
+ if (!gid) return next();
978
+ if (!isGroupAllowed(gid, config.duel.groups, config.duel.groupMode)) return next();
979
+ const duel = duelStates.get(gid);
980
+ if (!duel) return next();
981
+ const text = (session.content || "").trim();
982
+ const userId = String(session.userId);
983
+ if (userId !== duel.challengerId && userId !== duel.targetId) return next();
984
+ if (duel.phase === "waiting_accept" && userId === duel.targetId && text === "\u63A5\u53D7") {
985
+ if (duel.acceptTimer) clearTimeout(duel.acceptTimer);
986
+ duel.acceptTimer = null;
987
+ const dc = config.duel;
988
+ duel.phase = "countdown";
989
+ duel.countdownEndTime = Date.now() + dc.countdownSeconds * 1e3;
990
+ duel.earlyDrawers = /* @__PURE__ */ new Set();
991
+ duel.drawTimes = /* @__PURE__ */ new Map();
992
+ dbg("\u5BF9\u51B3-\u5F00\u59CB\u5012\u8BA1\u65F6", { gid, countdownSeconds: dc.countdownSeconds, challenger: duel.challengerId, target: duel.targetId });
993
+ duel.countdownTimer = setTimeout(async () => {
994
+ const d = duelStates.get(gid);
995
+ if (!d || d.phase !== "countdown") return;
996
+ d.phase = "drawing";
997
+ try {
998
+ await duel.session.send("\u2694\uFE0F \u5348\u65F6\u5DF2\u5230\uFF01");
999
+ } catch {
1000
+ }
1001
+ dbg("\u5BF9\u51B3-\u62D4\u67AA\u9636\u6BB5", { gid });
1002
+ d.drawTimer = setTimeout(() => {
1003
+ const d2 = duelStates.get(gid);
1004
+ if (!d2 || d2.phase !== "drawing") return;
1005
+ resolveDuel(gid);
1006
+ }, dc.drawTimeoutSeconds * 1e3);
1007
+ }, dc.countdownSeconds * 1e3);
1008
+ return;
1009
+ }
1010
+ if (text === "\u62D4\u67AA" && (duel.phase === "countdown" || duel.phase === "drawing")) {
1011
+ const now = Date.now();
1012
+ if (duel.phase === "countdown") {
1013
+ duel.earlyDrawers.add(userId);
1014
+ const name3 = userId === duel.challengerId ? duel.challengerName : duel.targetName;
1015
+ await session.send(`\u26A0\uFE0F ${name3} \u63D0\u524D\u62D4\u67AA\uFF01`);
1016
+ dbg("\u5BF9\u51B3-\u63D0\u524D\u62D4\u67AA", { gid, userId, name: name3 });
1017
+ if (duel.earlyDrawers.size >= 2) {
1018
+ duel.drawTimes.set(userId, now);
1019
+ const otherId = userId === duel.challengerId ? duel.targetId : duel.challengerId;
1020
+ if (!duel.drawTimes.has(otherId)) duel.drawTimes.set(otherId, now);
1021
+ resolveDuel(gid);
1022
+ }
1023
+ return;
1024
+ }
1025
+ if (duel.drawTimes.has(userId)) {
1026
+ await session.send("\u4F60\u5DF2\u7ECF\u62D4\u8FC7\u67AA\u4E86\uFF01");
1027
+ return;
1028
+ }
1029
+ const reactionTime = now - duel.countdownEndTime;
1030
+ duel.drawTimes.set(userId, reactionTime);
1031
+ const name2 = userId === duel.challengerId ? duel.challengerName : duel.targetName;
1032
+ dbg("\u5BF9\u51B3-\u62D4\u67AA", { gid, userId, name: name2, reactionTime });
1033
+ if (duel.drawTimes.has(duel.challengerId) && duel.drawTimes.has(duel.targetId)) {
1034
+ resolveDuel(gid);
1035
+ }
1036
+ return;
1037
+ }
1038
+ return next();
1039
+ });
1040
+ ctx.command("\u521B\u5EFA\u63A5\u9F99 <content:string>", "\u521B\u5EFA\u7FA4\u63A5\u9F99\uFF0C\u521B\u5EFA\u8005\u81EA\u52A8\u4E3A\u7B2C\u4E00\u4F4D").action(async ({ session }, content) => {
1041
+ if (!session?.userId) return;
1042
+ const gid = getGroupId(session);
1043
+ const cc = config.chain;
1044
+ if (!cc.enabled) return;
1045
+ if (!isGroupAllowed(gid, cc.groups, cc.groupMode)) return;
1046
+ if (!content || !content.trim()) {
1047
+ return "\u{1F4DD} \u8BF7\u63D0\u4F9B\u63A5\u9F99\u5185\u5BB9\uFF0C\u4F8B\u5982\uFF1A\u521B\u5EFA\u63A5\u9F99 \u4ECA\u5929\u4E2D\u5348\u5403\u4EC0\u4E48";
1048
+ }
1049
+ const chainContent = content.trim();
1050
+ const userId = String(session.userId);
1051
+ const userName = session.username || userId;
1052
+ chainStates.set(gid, {
1053
+ content: chainContent,
1054
+ creatorId: userId,
1055
+ creatorName: userName,
1056
+ participants: [{ userId, userName }],
1057
+ createdAt: Date.now()
1058
+ });
1059
+ dbg("\u63A5\u9F99-\u521B\u5EFA", { gid, creatorId: userId, content: chainContent });
1060
+ return formatChain(chainStates.get(gid));
1061
+ });
1062
+ ctx.command("\u67E5\u770B\u63A5\u9F99", "\u67E5\u770B\u5F53\u524D\u7FA4\u63A5\u9F99").action(async ({ session }) => {
1063
+ const gid = getGroupId(session);
1064
+ const cc = config.chain;
1065
+ if (!cc.enabled) return;
1066
+ if (!isGroupAllowed(gid, cc.groups, cc.groupMode)) return;
1067
+ const chain = chainStates.get(gid);
1068
+ if (!chain) return '\u{1F4DD} \u672C\u7FA4\u6682\u65E0\u63A5\u9F99\uFF0C\u4F7F\u7528"\u521B\u5EFA\u63A5\u9F99 <\u5185\u5BB9>"\u5F00\u59CB\u4E00\u4E2A\u5427\uFF01';
1069
+ return formatChain(chain);
1070
+ });
1071
+ ctx.command("\u9000\u51FA\u63A5\u9F99", "\u9000\u51FA\u5F53\u524D\u7FA4\u63A5\u9F99").alias("\u53D6\u6D88\u63A5\u9F99").action(async ({ session }) => {
1072
+ const gid = getGroupId(session);
1073
+ const cc = config.chain;
1074
+ if (!cc.enabled) return;
1075
+ if (!isGroupAllowed(gid, cc.groups, cc.groupMode)) return;
1076
+ const chain = chainStates.get(gid);
1077
+ if (!chain) return "\u{1F4DD} \u672C\u7FA4\u6682\u65E0\u63A5\u9F99\u3002";
1078
+ const userId = String(session.userId);
1079
+ const idx = chain.participants.findIndex((p) => p.userId === userId);
1080
+ if (idx === -1) return "\u{1F4DD} \u4F60\u6CA1\u6709\u53C2\u4E0E\u8FD9\u4E2A\u63A5\u9F99\u3002";
1081
+ const userName = chain.participants[idx].userName;
1082
+ chain.participants.splice(idx, 1);
1083
+ dbg("\u63A5\u9F99-\u9000\u51FA", { gid, userId, userName, remaining: chain.participants.length });
1084
+ if (chain.participants.length === 0) {
1085
+ chainStates.delete(gid);
1086
+ return "\u{1F4DD} \u4F60\u9000\u51FA\u4E86\u63A5\u9F99\uFF0C\u63A5\u9F99\u5DF2\u56E0\u65E0\u4EBA\u53C2\u4E0E\u800C\u7ED3\u675F\u3002";
1087
+ }
1088
+ await session.send(`\u{1F4DD} ${userName} \u5DF2\u9000\u51FA\u63A5\u9F99\u3002
1089
+
1090
+ ${formatChain(chain)}`);
1091
+ });
1092
+ ctx.middleware(async (session, next) => {
1093
+ if (!config.chain.enabled) return next();
1094
+ const gid = getGroupId(session);
1095
+ if (!gid) return next();
1096
+ if (!isGroupAllowed(gid, config.chain.groups, config.chain.groupMode)) return next();
1097
+ const text = (session.content || "").trim();
1098
+ if (text !== "\u63A5\u9F99") return next();
1099
+ const chain = chainStates.get(gid);
1100
+ if (!chain) return next();
1101
+ const userId = String(session.userId);
1102
+ if (chain.participants.some((p) => p.userId === userId)) {
1103
+ await session.send("\u{1F4DD} \u4F60\u5DF2\u7ECF\u53C2\u4E0E\u8FC7\u8FD9\u4E2A\u63A5\u9F99\u4E86\uFF01");
1104
+ return;
1105
+ }
1106
+ const userName = session.username || userId;
1107
+ chain.participants.push({ userId, userName });
1108
+ dbg("\u63A5\u9F99-\u53C2\u4E0E", { gid, userId, participantCount: chain.participants.length });
1109
+ await session.send(formatChain(chain));
1110
+ return;
1111
+ });
719
1112
  ctx.middleware(async (session, next) => {
720
1113
  const gid = getGroupId(session);
721
1114
  const content = session.content || "";
@@ -802,7 +1195,7 @@ ${import_koishi.segment.at(targetId)} \u5DF2\u88AB\u89E3\u9664\u7981\u8A00\u3002
802
1195
  forwardTextsPreview: forwardTexts.slice(0, 5).map((t) => t.substring(0, 30))
803
1196
  });
804
1197
  }
805
- if (config.dedup.enabled && isGroupAllowed(gid, config.dedup.groups)) {
1198
+ if (config.dedup.enabled && isGroupAllowed(gid, config.dedup.groups, config.dedup.groupMode)) {
806
1199
  dbg("\u67E5\u91CD-\u4E2D\u95F4\u4EF6\u5165\u53E3", {
807
1200
  gid,
808
1201
  userId: session.userId,
@@ -813,15 +1206,19 @@ ${import_koishi.segment.at(targetId)} \u5DF2\u88AB\u89E3\u9664\u7981\u8A00\u3002
813
1206
  forwardTextsCount: forwardTexts.length
814
1207
  });
815
1208
  if (isFwd && forwardTexts.length > 0) {
816
- const sevenDaysAgo = Date.now() - 7 * 24 * 60 * 60 * 1e3;
817
1209
  dbg("\u67E5\u91CD\u68C0\u6D4B", { gid, forwardTextsCount: forwardTexts.length });
818
1210
  for (const fwdText of forwardTexts) {
819
1211
  if (!fwdText) continue;
820
1212
  try {
821
- const matches = await ctx.database.get("lili_message", { gid, content: fwdText }, { sort: { timestamp: "asc" }, limit: 1 });
822
- dbg("\u67E5\u91CD\u67E5\u8BE2", { gid, fwdText: fwdText.substring(0, 50), matchCount: matches?.length || 0 });
823
- if (matches && matches.length > 0 && matches[0].timestamp >= sevenDaysAgo) {
824
- const original = matches[0];
1213
+ const sevenDaysAgoMs = Date.now() - 7 * 24 * 60 * 60 * 1e3;
1214
+ const recentMsgs = await ctx.database.get("lili_message", { gid, timestamp: { $gte: sevenDaysAgoMs } }, { sort: { timestamp: "asc" }, limit: 1e3 });
1215
+ const match = recentMsgs.find((m) => {
1216
+ if (m.content === fwdText) return true;
1217
+ return normalizeText(m.content) === fwdText;
1218
+ });
1219
+ dbg("\u67E5\u91CD\u67E5\u8BE2", { gid, fwdText: fwdText.substring(0, 50), recentMsgCount: recentMsgs?.length || 0, matched: !!match });
1220
+ if (match) {
1221
+ const original = match;
825
1222
  const dateStr = new Date(original.timestamp).toLocaleString("zh-CN", { month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit" });
826
1223
  await session.send([
827
1224
  (0, import_koishi.h)("quote", { id: original.messageId }),
@@ -839,8 +1236,8 @@ ${import_koishi.segment.at(targetId)} \u5DF2\u88AB\u89E3\u9664\u7981\u8A00\u3002
839
1236
  const imitationGroups = parseGroupList(config.imitation.groups);
840
1237
  const dedupGroups = parseGroupList(config.dedup.groups);
841
1238
  const foldGroups = config.fold.enabled ? parseGroupList(config.fold.groups) : [];
842
- const dedupInGroup = config.dedup.enabled && isGroupAllowed(gid, config.dedup.groups);
843
- const foldInGroup = config.fold.enabled && isGroupAllowed(gid, config.fold.groups);
1239
+ const dedupInGroup = config.dedup.enabled && isGroupAllowed(gid, config.dedup.groups, config.dedup.groupMode);
1240
+ const foldInGroup = config.fold.enabled && isGroupAllowed(gid, config.fold.groups, config.fold.groupMode);
844
1241
  const shouldRecordAll = foldInGroup || dedupInGroup;
845
1242
  const shouldRecordLimited = textLen >= 15 && textLen <= 60 && normalizedText;
846
1243
  const recordGroups = /* @__PURE__ */ new Set([...imitationGroups, ...dedupGroups, ...foldGroups]);
@@ -864,8 +1261,8 @@ ${import_koishi.segment.at(targetId)} \u5DF2\u88AB\u89E3\u9664\u7981\u8A00\u3002
864
1261
  try {
865
1262
  await ctx.database.create("lili_message", {
866
1263
  gid,
867
- userId: session.userId,
868
- userName: session.username || session.userId,
1264
+ userId: String(session.userId),
1265
+ userName: session.username || String(session.userId),
869
1266
  content: fwdText,
870
1267
  timestamp: Date.now(),
871
1268
  messageId: session.messageId || ""
@@ -876,22 +1273,28 @@ ${import_koishi.segment.at(targetId)} \u5DF2\u88AB\u89E3\u9664\u7981\u8A00\u3002
876
1273
  }
877
1274
  }
878
1275
  }
879
- if (shouldRecord && !isFwd && normalizedText && textLen >= 15 && textLen <= 60) {
880
- try {
881
- await ctx.database.create("lili_message", {
882
- gid,
883
- userId: session.userId,
884
- userName: session.username || session.userId,
885
- content: normalizedText,
886
- timestamp: Date.now(),
887
- messageId: session.messageId || ""
888
- });
889
- dbg("\u6D88\u606F\u5DF2\u5165\u5E93", { gid, userId: session.userId, contentLen: normalizedText.length, messageId: session.messageId });
890
- } catch (e) {
891
- dbg("\u6D88\u606F\u5165\u5E93\u5931\u8D25", { gid, error: String(e) });
1276
+ if (shouldRecord && !isFwd) {
1277
+ const shouldStore = shouldRecordAll || normalizedText && textLen >= 15 && textLen <= 60;
1278
+ if (shouldStore) {
1279
+ const storeContent = shouldRecordAll ? session.content || normalizedText || "" : normalizedText;
1280
+ if (storeContent) {
1281
+ try {
1282
+ await ctx.database.create("lili_message", {
1283
+ gid,
1284
+ userId: String(session.userId),
1285
+ userName: session.username || String(session.userId),
1286
+ content: storeContent,
1287
+ timestamp: Date.now(),
1288
+ messageId: session.messageId || ""
1289
+ });
1290
+ dbg("\u6D88\u606F\u5DF2\u5165\u5E93", { gid, userId: String(session.userId), contentLen: storeContent.length, shouldRecordAll, messageId: session.messageId });
1291
+ } catch (e) {
1292
+ dbg("\u6D88\u606F\u5165\u5E93\u5931\u8D25", { gid, error: String(e) });
1293
+ }
1294
+ }
892
1295
  }
893
1296
  }
894
- if (config.imitation.enabled && isGroupAllowed(gid, config.imitation.groups)) {
1297
+ if (config.imitation.enabled && isGroupAllowed(gid, config.imitation.groups, config.imitation.groupMode)) {
895
1298
  let count = imitationCounters.get(gid) || 0;
896
1299
  count++;
897
1300
  imitationCounters.set(gid, count);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-lili-hub",
3
3
  "description": "丽丽Hub — 自用丽丽主题QQ群娱乐插件:俄罗斯轮盘赌、喝酒发酒疯、模仿群友说话、合并消息查重(水过了)",
4
- "version": "0.3.0",
4
+ "version": "0.3.2",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [