koishi-plugin-group-verification 1.0.36 → 1.0.38

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 (3) hide show
  1. package/lib/index.js +55 -26
  2. package/package.json +1 -1
  3. package/src/index.ts +73 -28
package/lib/index.js CHANGED
@@ -60,19 +60,23 @@ function clogV(msg, ...args) {
60
60
  if (pluginLogLevel === "详细") logger.debug(msg, ...args);
61
61
  }
62
62
  __name(clogV, "clogV");
63
+ function renderMsg(tmpl) {
64
+ return tmpl.replace(/\\\\n/g, "").replace(/\\n/g, "\n").replace(/\x01/g, "\\n");
65
+ }
66
+ __name(renderMsg, "renderMsg");
63
67
  var Config = import_koishi.Schema.object({
64
68
  defaultReminderMessage: import_koishi.Schema.string().description("默认提醒消息模板(使用 \\n 表示换行,可包含下方变量)").default("{user}({id}) 申请入群\\n申请理由: {question}\\n匹配情况: {answer}/{threshold}\\n使用 gva 同意或 gvr 拒绝申请"),
65
69
  enableStrictGroupCheck: import_koishi.Schema.boolean().description("是否启用严格的群号检查(检查群号长度)").default(false),
66
70
  logLevel: import_koishi.Schema.union(["debug", "info", "warn"]).description("日志详细程度").default("info"),
67
71
  // debug=详细, info=中等, warn=简洁
68
- permissionDeniedMessage: import_koishi.Schema.string().description("权限不足时返回给调用者的提示").default("权限不足: 需要群主/管理员权限或koishi三级以上权限"),
69
- invalidGroupMessage: import_koishi.Schema.string().description("无效群号或机器人未在该群时的提示").default("群号 {group} 格式不合法或机器人不在该群中"),
70
- parameterConflictMessage: import_koishi.Schema.string().description("参数冲突时提示").default("参数冲突: -? 或 -r 不能与其他参数或关键词一起使用(仅可搭配 -i)"),
71
- noKeywordsMessage: import_koishi.Schema.string().description("未提供关键词且无法从现有配置继承时的提示").default("请先提供关键词创建配置,或使用 -? 查询配置,-r 删除配置"),
72
- blacklistAddSuccess: import_koishi.Schema.string().description("将用户加入黑名单后的提示,可使用 {user},{group},{reason}").default("已将用户 {user} 加入群 {group} 黑名单{reason}"),
73
- blacklistRemoveSuccess: import_koishi.Schema.string().description("从黑名单移除用户后的提示,可使用 {user},{group}").default("已从群 {group} 的黑名单中移除用户 {user}"),
74
- blacklistListEmpty: import_koishi.Schema.string().description("黑名单为空时提示").default("{group} 的黑名单为空"),
75
- blacklistInfoTemplate: import_koishi.Schema.string().description("查询指定用户状态时的模板,可用 {global},{group}").default("全局黑名单: {global}\n本群黑名单: {group}")
72
+ permissionDeniedMessage: import_koishi.Schema.string().description("权限不足时返回给调用者的提示,支持 \\n 换行").default("权限不足: 需要群主/管理员权限或koishi三级以上权限"),
73
+ invalidGroupMessage: import_koishi.Schema.string().description("无效群号或机器人未在该群时的提示,可用 {group},支持 \\n 换行").default("群号 {group} 格式不合法或机器人不在该群中"),
74
+ parameterConflictMessage: import_koishi.Schema.string().description("参数冲突时提示,支持 \\n 换行").default("参数冲突: -? 或 -r 不能与其他参数或关键词一起使用(仅可搭配 -i)"),
75
+ noKeywordsMessage: import_koishi.Schema.string().description("未提供关键词且无法从现有配置继承时的提示,支持 \\n 换行").default("请先提供关键词创建配置,或使用 -? 查询配置,-r 删除配置"),
76
+ blacklistAddSuccess: import_koishi.Schema.string().description('将用户加入黑名单后的提示,可用 {user},{group},{reason};{group} 含"群"前缀(all时为"全局"),支持 \\n 换行').default("已将用户 {user} 加入{group}黑名单{reason}"),
77
+ blacklistRemoveSuccess: import_koishi.Schema.string().description('从黑名单移除用户后的提示,可用 {user},{group};{group} 含"群"前缀,支持 \\n 换行').default("已从{group}的黑名单中移除用户 {user}"),
78
+ blacklistListEmpty: import_koishi.Schema.string().description('黑名单为空时提示,可用 {group};{group} 含""前缀,支持 \\n 换行').default("{group}的黑名单为空"),
79
+ blacklistInfoTemplate: import_koishi.Schema.string().description("查询指定用户状态时的模板,可用 {global},{group},支持 \\n 换行").default("全局黑名单: {global}\\n本群黑名单: {group}")
76
80
  }).description("群组验证插件配置");
77
81
  var inject = ["database"];
78
82
  var ESC_QUOTE = "\0";
@@ -442,6 +446,31 @@ async function handleGuildMemberRequestEvent(ctx, session) {
442
446
  } catch (e) {
443
447
  logger.warn("黑名单检查失败", e);
444
448
  }
449
+ {
450
+ const isId = /* @__PURE__ */ __name((s) => !s || s === userId, "isId");
451
+ let resolvedName;
452
+ const evUser = session.event?.user;
453
+ if (!isId(evUser?.name)) resolvedName = evUser.name;
454
+ else if (!isId(evUser?.nick)) resolvedName = evUser.nick;
455
+ if (!resolvedName) {
456
+ const author = session.author;
457
+ if (!isId(author?.name)) resolvedName = author.name;
458
+ else if (!isId(author?.nick)) resolvedName = author.nick;
459
+ }
460
+ if (!resolvedName && !isId(session.username)) {
461
+ resolvedName = session.username;
462
+ }
463
+ if (!resolvedName) {
464
+ try {
465
+ const userInfo = await session.bot.getUser(userId);
466
+ const n = userInfo?.name || userInfo?.nickname;
467
+ if (!isId(n)) resolvedName = n;
468
+ } catch (_) {
469
+ }
470
+ }
471
+ session.username = resolvedName || userId;
472
+ clogV(`昵称解析: userId=${userId} resolved="${session.username}" evUser=${JSON.stringify(evUser)} author=${JSON.stringify(session.author)}`);
473
+ }
445
474
  const { isValid, matchedCount, requiredThreshold } = await verifyApplication(config, message, session);
446
475
  clogV(`验证结果 guild=${guildId} user=${userId} msg="${message}" matched=${matchedCount} threshold=${requiredThreshold} valid=${isValid}`);
447
476
  if (isValid) {
@@ -604,7 +633,7 @@ async function handleFailedVerification(ctx, session, config, matchedCount, requ
604
633
  logger.warn("handleFailedVerification invoked without guildId, aborting");
605
634
  return;
606
635
  }
607
- const username = session.username || "未知用户";
636
+ const username = session.username || userId;
608
637
  const message = session.content || "";
609
638
  clog(
610
639
  "debug",
@@ -716,7 +745,7 @@ async function processBlacklistCommand(ctx, session, rawArgs, config) {
716
745
  } else {
717
746
  if (config?.enableStrictGroupCheck) {
718
747
  if (!/^\d{5,15}$/.test(group)) {
719
- return (config.invalidGroupMessage || "群号 {group} 格式不合法或机器人不在该群中").replace("{group}", group);
748
+ return renderMsg((config.invalidGroupMessage || "群号 {group} 格式不合法或机器人不在该群中").replace("{group}", group));
720
749
  }
721
750
  }
722
751
  const [ok, err] = await checkPermission(session, group);
@@ -746,8 +775,9 @@ async function processBlacklistCommand(ctx, session, rawArgs, config) {
746
775
  logger.warn(`踢出用户 ${targetUser} 失败`, e);
747
776
  }
748
777
  }
749
- const tmpl = config && config.blacklistAddSuccess || "已将用户 {user} 加入群 {group} 黑名单{reason}";
750
- return tmpl.replace("{user}", targetUser).replace("{group}", group).replace("{reason}", reason ? `,原因: ${reason}` : "");
778
+ const tmpl = config && config.blacklistAddSuccess || "已将用户 {user} 加入{group}黑名单{reason}";
779
+ const groupLabelA = group.toLowerCase() === "all" ? "全局" : "" + group;
780
+ return renderMsg(tmpl.replace("{user}", targetUser).replace("{group}", groupLabelA).replace("{reason}", reason ? `,原因: ${reason}` : ""));
751
781
  }
752
782
  if (op === "r") {
753
783
  targetUser = parts[1];
@@ -760,7 +790,7 @@ async function processBlacklistCommand(ctx, session, rawArgs, config) {
760
790
  } else {
761
791
  if (config?.enableStrictGroupCheck) {
762
792
  if (!/^\d{5,15}$/.test(group)) {
763
- return (config.invalidGroupMessage || "群号 {group} 格式不合法或机器人不在该群中").replace("{group}", group);
793
+ return renderMsg((config.invalidGroupMessage || "群号 {group} 格式不合法或机器人不在该群中").replace("{group}", group));
764
794
  }
765
795
  }
766
796
  const [ok, err] = await checkPermission(session, group);
@@ -773,8 +803,9 @@ async function processBlacklistCommand(ctx, session, rawArgs, config) {
773
803
  delete entries[targetUser];
774
804
  await ctx.database.set("group_verification_blacklist", { id: row.id }, { entries });
775
805
  }
776
- const tmpl = config && config.blacklistRemoveSuccess || "已从群 {group} 的黑名单中移除用户 {user}";
777
- return tmpl.replace("{user}", targetUser).replace("{group}", group);
806
+ const tmpl = config && config.blacklistRemoveSuccess || "已从{group}的黑名单中移除用户 {user}";
807
+ const groupLabelR = group.toLowerCase() === "all" ? "全局" : "群 " + group;
808
+ return renderMsg(tmpl.replace("{user}", targetUser).replace("{group}", groupLabelR));
778
809
  }
779
810
  if (op === "l") {
780
811
  group = parts[1] || getCurrentGroup();
@@ -785,7 +816,7 @@ async function processBlacklistCommand(ctx, session, rawArgs, config) {
785
816
  } else {
786
817
  if (config?.enableStrictGroupCheck) {
787
818
  if (!/^\d{5,15}$/.test(group)) {
788
- return (config.invalidGroupMessage || "群号 {group} 格式不合法或机器人不在该群中").replace("{group}", group);
819
+ return renderMsg((config.invalidGroupMessage || "群号 {group} 格式不合法或机器人不在该群中").replace("{group}", group));
789
820
  }
790
821
  }
791
822
  const [ok, err] = await checkPermission(session, group);
@@ -793,11 +824,9 @@ async function processBlacklistCommand(ctx, session, rawArgs, config) {
793
824
  }
794
825
  const rows = await ctx.database.get("group_verification_blacklist", { groupId: group });
795
826
  if (rows.length === 0) {
796
- if (group && group.toLowerCase() === "all") {
797
- return "全局黑名单为空";
798
- }
799
- const tmpl = config && config.blacklistListEmpty || "群 {group} 的黑名单为空";
800
- return tmpl.replace("{group}", group);
827
+ const tmpl = config && config.blacklistListEmpty || "{group}的黑名单为空";
828
+ const groupLabelL = group && group.toLowerCase() === "all" ? "全局" : "群 " + group;
829
+ return renderMsg(tmpl.replace("{group}", groupLabelL));
801
830
  }
802
831
  const entries = rows[0].entries || {};
803
832
  let prefix = group && group.toLowerCase() === "all" ? "全局黑名单: \n" : `群 ${group} 黑名单:
@@ -815,7 +844,7 @@ async function processBlacklistCommand(ctx, session, rawArgs, config) {
815
844
  const groupArg = parts[2];
816
845
  const globalRows = await ctx.database.get("group_verification_blacklist", { groupId: "all" });
817
846
  const globalHit = globalRows.length > 0 && (globalRows[0].entries || {})[targetUser] !== void 0;
818
- const tmpl = config && config.blacklistInfoTemplate || "全局黑名单: {global}\n本群黑名单: {group}";
847
+ const tmpl = (config && config.blacklistInfoTemplate || "全局黑名单: {global}\\n本群黑名单: {group}").replace(/\\\\n/g, "").replace(/\\n/g, "\n").replace(/\x01/g, "\\n");
819
848
  const formatReply = /* @__PURE__ */ __name((localHit, groupsList) => {
820
849
  if (groupsList) {
821
850
  return tmpl.replace("{global}", globalHit ? "有" : "无").replace(
@@ -860,7 +889,7 @@ async function processBlacklistCommand(ctx, session, rawArgs, config) {
860
889
  const groupId = groupArg;
861
890
  if (config?.enableStrictGroupCheck && groupId.toLowerCase() !== "all") {
862
891
  if (!/^\d{5,15}$/.test(groupId)) {
863
- return (config.invalidGroupMessage || "群号 {group} 格式不合法或机器人不在该群中").replace("{group}", groupId);
892
+ return renderMsg((config.invalidGroupMessage || "群号 {group} 格式不合法或机器人不在该群中").replace("{group}", groupId));
864
893
  }
865
894
  }
866
895
  const [ok, err] = await checkPermission(session, groupId);
@@ -1008,7 +1037,7 @@ function apply(ctx, config) {
1008
1037
  return [false, `无法获取群 ${groupId} 的成员信息,请确认机器人已在该群中`];
1009
1038
  }
1010
1039
  clogV(`权限检查 - 权限不足`);
1011
- return [false, config.permissionDeniedMessage || "权限不足: 需要群主/管理员权限或koishi三级以上权限"];
1040
+ return [false, renderMsg(config.permissionDeniedMessage || "权限不足: 需要群主/管理员权限或koishi三级以上权限")];
1012
1041
  }
1013
1042
  __name(checkPermission2, "checkPermission");
1014
1043
  const groupVerify = ctx.command("group-verify", "群组验证管理命令").alias("gv", "gverify");
@@ -1046,7 +1075,7 @@ function apply(ctx, config) {
1046
1075
  clogV(`合并后options: ${JSON.stringify(cleanedOptions, null, 2)}
1047
1076
  `);
1048
1077
  if ((cleanedOptions.query || cleanedOptions.remove) && (parsedKeywords.length > 0 || cleanedOptions.method !== void 0 || cleanedOptions.threshold !== void 0 || cleanedOptions.message !== void 0 || cleanedOptions.enableMessage || cleanedOptions.disableMessage)) {
1049
- return config.parameterConflictMessage || "参数冲突: -? 或 -r 不能与其他参数或关键词一起使用(仅可搭配 -i)";
1078
+ return renderMsg(config.parameterConflictMessage || "参数冲突: -? 或 -r 不能与其他参数或关键词一起使用(仅可搭配 -i)");
1050
1079
  }
1051
1080
  const hasRealMessageParam = cleanedOptions.message !== void 0;
1052
1081
  const hasRealEnableMessageParam = cleanedOptions.enableMessage === true;
@@ -1160,7 +1189,7 @@ ${displayMsg}
1160
1189
  }
1161
1190
  if (keywordList.length === 0 && !cleanedOptions.query && !cleanedOptions.remove) {
1162
1191
  if (!existingConfig) {
1163
- return config.noKeywordsMessage || "请先提供关键词创建配置,或使用 -? 查询配置,-r 删除配置";
1192
+ return renderMsg(config.noKeywordsMessage || "请先提供关键词创建配置,或使用 -? 查询配置,-r 删除配置");
1164
1193
  }
1165
1194
  keywordList = existingConfig.keywords;
1166
1195
  }
package/package.json CHANGED
@@ -8,7 +8,7 @@
8
8
  "bugs": {
9
9
  "url": "https://github.com/LHDyx/koishi-plugin-group-verification/issues"
10
10
  },
11
- "version": "1.0.36",
11
+ "version": "1.0.38",
12
12
  "main": "lib/index.js",
13
13
  "typings": "lib/index.d.ts",
14
14
  "files": [
package/src/index.ts CHANGED
@@ -35,6 +35,11 @@ function clogV(msg: string, ...args: any[]): void {
35
35
  if (pluginLogLevel === '详细') logger.debug(msg, ...args)
36
36
  }
37
37
 
38
+ /** 处理配置文本中的换行转义:\n→换行,\\n→字面量 \n */
39
+ function renderMsg(tmpl: string): string {
40
+ return tmpl.replace(/\\\\n/g, '\x01').replace(/\\n/g, '\n').replace(/\x01/g, '\\n')
41
+ }
42
+
38
43
  // 数据库模型定义
39
44
  declare module 'koishi' {
40
45
  interface Tables {
@@ -114,14 +119,14 @@ export const Config: Schema<Config> = Schema.object({
114
119
  .default('{user}({id}) 申请入群\\n申请理由: {question}\\n匹配情况: {answer}/{threshold}\\n使用 gva 同意或 gvr 拒绝申请'),
115
120
  enableStrictGroupCheck: Schema.boolean().description('是否启用严格的群号检查(检查群号长度)').default(false),
116
121
  logLevel: Schema.union(['debug', 'info', 'warn']).description('日志详细程度').default('info'), // debug=详细, info=中等, warn=简洁
117
- permissionDeniedMessage: Schema.string().description('权限不足时返回给调用者的提示').default('权限不足: 需要群主/管理员权限或koishi三级以上权限'),
118
- invalidGroupMessage: Schema.string().description('无效群号或机器人未在该群时的提示').default('群号 {group} 格式不合法或机器人不在该群中'),
119
- parameterConflictMessage: Schema.string().description('参数冲突时提示').default('参数冲突: -? 或 -r 不能与其他参数或关键词一起使用(仅可搭配 -i)'),
120
- noKeywordsMessage: Schema.string().description('未提供关键词且无法从现有配置继承时的提示').default('请先提供关键词创建配置,或使用 -? 查询配置,-r 删除配置'),
121
- blacklistAddSuccess: Schema.string().description('将用户加入黑名单后的提示,可使用 {user},{group},{reason}').default('已将用户 {user} 加入群 {group} 黑名单{reason}'),
122
- blacklistRemoveSuccess: Schema.string().description('从黑名单移除用户后的提示,可使用 {user},{group}').default('已从群 {group} 的黑名单中移除用户 {user}'),
123
- blacklistListEmpty: Schema.string().description('黑名单为空时提示').default('{group} 的黑名单为空'),
124
- blacklistInfoTemplate: Schema.string().description('查询指定用户状态时的模板,可用 {global},{group}').default('全局黑名单: {global}\n本群黑名单: {group}')
122
+ permissionDeniedMessage: Schema.string().description('权限不足时返回给调用者的提示,支持 \\n 换行').default('权限不足: 需要群主/管理员权限或koishi三级以上权限'),
123
+ invalidGroupMessage: Schema.string().description('无效群号或机器人未在该群时的提示,可用 {group},支持 \\n 换行').default('群号 {group} 格式不合法或机器人不在该群中'),
124
+ parameterConflictMessage: Schema.string().description('参数冲突时提示,支持 \\n 换行').default('参数冲突: -? 或 -r 不能与其他参数或关键词一起使用(仅可搭配 -i)'),
125
+ noKeywordsMessage: Schema.string().description('未提供关键词且无法从现有配置继承时的提示,支持 \\n 换行').default('请先提供关键词创建配置,或使用 -? 查询配置,-r 删除配置'),
126
+ blacklistAddSuccess: Schema.string().description('将用户加入黑名单后的提示,可用 {user},{group},{reason};{group} 含"群"前缀(all时为"全局"),支持 \\n 换行').default('已将用户 {user} 加入{group}黑名单{reason}'),
127
+ blacklistRemoveSuccess: Schema.string().description('从黑名单移除用户后的提示,可用 {user},{group};{group} 含"群"前缀,支持 \\n 换行').default('已从{group}的黑名单中移除用户 {user}'),
128
+ blacklistListEmpty: Schema.string().description('黑名单为空时提示,可用 {group};{group} 含"群"前缀,支持 \\n 换行').default('{group}的黑名单为空'),
129
+ blacklistInfoTemplate: Schema.string().description('查询指定用户状态时的模板,可用 {global},{group},支持 \\n 换行').default('全局黑名单: {global}\\n本群黑名单: {group}')
125
130
  })
126
131
  .description('群组验证插件配置')
127
132
 
@@ -604,6 +609,47 @@ export async function handleGuildMemberRequestEvent(ctx: Context, session: any)
604
609
  logger.warn('黑名单检查失败', e);
605
610
  }
606
611
 
612
+ // 解析用户昵称优先级:
613
+ // 1) event 原始数据(NapCat/OneBot 入群申请事件本身携带昵称)
614
+ // 2) session.author.name / session.author.nick
615
+ // 3) session.username(若非 userId)
616
+ // 4) getUser() API
617
+ // 5) 最终 fallback:userId
618
+ {
619
+ const isId = (s: string | undefined) => !s || s === userId
620
+
621
+ let resolvedName: string | undefined
622
+
623
+ // 优先级1:event 原始数据
624
+ const evUser = (session.event as any)?.user
625
+ if (!isId(evUser?.name)) resolvedName = evUser.name
626
+ else if (!isId(evUser?.nick)) resolvedName = evUser.nick
627
+
628
+ // 优先级2:author 字段
629
+ if (!resolvedName) {
630
+ const author = session.author as any
631
+ if (!isId(author?.name)) resolvedName = author.name
632
+ else if (!isId(author?.nick)) resolvedName = author.nick
633
+ }
634
+
635
+ // 优先级3:session.username
636
+ if (!resolvedName && !isId(session.username)) {
637
+ resolvedName = session.username
638
+ }
639
+
640
+ // 优先级4:API 拉取(get_stranger_info)
641
+ if (!resolvedName) {
642
+ try {
643
+ const userInfo = await session.bot.getUser(userId)
644
+ const n = (userInfo as any)?.name || (userInfo as any)?.nickname
645
+ if (!isId(n)) resolvedName = n
646
+ } catch (_) {}
647
+ }
648
+
649
+ session.username = resolvedName || userId
650
+ clogV(`昵称解析: userId=${userId} resolved="${session.username}" evUser=${JSON.stringify(evUser)} author=${JSON.stringify((session as any).author)}`)
651
+ }
652
+
607
653
  const { isValid, matchedCount, requiredThreshold } = await verifyApplication(config, message, session);
608
654
  clogV(`验证结果 guild=${guildId} user=${userId} msg="${message}" matched=${matchedCount} threshold=${requiredThreshold} valid=${isValid}`);
609
655
 
@@ -803,7 +849,7 @@ export async function handleFailedVerification(
803
849
  logger.warn('handleFailedVerification invoked without guildId, aborting')
804
850
  return
805
851
  }
806
- const username = session.username || '未知用户'
852
+ const username = session.username || userId
807
853
  const message = session.content || ''
808
854
  clog('debug', `待审核: guild=${guildId} user=${userId} matched=${matchedCount}/${requiredThreshold}`,
809
855
  `处理失败验证 guild=${guildId} user=${userId} msg="${session.content || ''}" matched=${matchedCount} threshold=${requiredThreshold}`)
@@ -955,7 +1001,7 @@ export async function processBlacklistCommand(ctx: Context, session: any, rawArg
955
1001
  // 严格群号检查(若开启)
956
1002
  if (config?.enableStrictGroupCheck) {
957
1003
  if (!/^\d{5,15}$/.test(group)) {
958
- return (config.invalidGroupMessage || '群号 {group} 格式不合法或机器人不在该群中').replace('{group}', group)
1004
+ return renderMsg((config.invalidGroupMessage || '群号 {group} 格式不合法或机器人不在该群中').replace('{group}', group))
959
1005
  }
960
1006
  }
961
1007
  const [ok, err] = await checkPermission(session, group)
@@ -988,8 +1034,9 @@ export async function processBlacklistCommand(ctx: Context, session: any, rawArg
988
1034
  logger.warn(`踢出用户 ${targetUser} 失败`, e)
989
1035
  }
990
1036
  }
991
- const tmpl = (config && config.blacklistAddSuccess) || '已将用户 {user} 加入群 {group} 黑名单{reason}'
992
- return tmpl.replace('{user}', targetUser).replace('{group}', group).replace('{reason}', reason ? `,原因: ${reason}` : '')
1037
+ const tmpl = (config && config.blacklistAddSuccess) || '已将用户 {user} 加入{group}黑名单{reason}'
1038
+ const groupLabelA = group.toLowerCase() === 'all' ? '全局' : '' + group
1039
+ return renderMsg(tmpl.replace('{user}', targetUser).replace('{group}', groupLabelA).replace('{reason}', reason ? `,原因: ${reason}` : ''))
993
1040
  }
994
1041
  if (op === 'r') {
995
1042
  targetUser = parts[1]
@@ -1002,7 +1049,7 @@ export async function processBlacklistCommand(ctx: Context, session: any, rawArg
1002
1049
  } else {
1003
1050
  if (config?.enableStrictGroupCheck) {
1004
1051
  if (!/^\d{5,15}$/.test(group)) {
1005
- return (config.invalidGroupMessage || '群号 {group} 格式不合法或机器人不在该群中').replace('{group}', group)
1052
+ return renderMsg((config.invalidGroupMessage || '群号 {group} 格式不合法或机器人不在该群中').replace('{group}', group))
1006
1053
  }
1007
1054
  }
1008
1055
  const [ok, err] = await checkPermission(session, group)
@@ -1015,8 +1062,9 @@ export async function processBlacklistCommand(ctx: Context, session: any, rawArg
1015
1062
  delete entries[targetUser]
1016
1063
  await ctx.database.set('group_verification_blacklist', { id: row.id }, { entries })
1017
1064
  }
1018
- const tmpl = (config && config.blacklistRemoveSuccess) || '已从群 {group} 的黑名单中移除用户 {user}'
1019
- return tmpl.replace('{user}', targetUser).replace('{group}', group)
1065
+ const tmpl = (config && config.blacklistRemoveSuccess) || '已从{group}的黑名单中移除用户 {user}'
1066
+ const groupLabelR = group.toLowerCase() === 'all' ? '全局' : '群 ' + group
1067
+ return renderMsg(tmpl.replace('{user}', targetUser).replace('{group}', groupLabelR))
1020
1068
  }
1021
1069
  if (op === 'l') {
1022
1070
  group = parts[1] || getCurrentGroup()
@@ -1027,7 +1075,7 @@ export async function processBlacklistCommand(ctx: Context, session: any, rawArg
1027
1075
  } else {
1028
1076
  if (config?.enableStrictGroupCheck) {
1029
1077
  if (!/^\d{5,15}$/.test(group)) {
1030
- return (config.invalidGroupMessage || '群号 {group} 格式不合法或机器人不在该群中').replace('{group}', group)
1078
+ return renderMsg((config.invalidGroupMessage || '群号 {group} 格式不合法或机器人不在该群中').replace('{group}', group))
1031
1079
  }
1032
1080
  }
1033
1081
  const [ok, err] = await checkPermission(session, group)
@@ -1035,13 +1083,9 @@ export async function processBlacklistCommand(ctx: Context, session: any, rawArg
1035
1083
  }
1036
1084
  const rows = await ctx.database.get('group_verification_blacklist', { groupId: group })
1037
1085
  if (rows.length === 0) {
1038
- // 特殊处理 all 表示全局黑名单
1039
- if (group && group.toLowerCase() === 'all') {
1040
- // 不使用模板,因为默认模板会产生 "群 all 的黑名单为空" 这种奇怪输出
1041
- return '全局黑名单为空'
1042
- }
1043
- const tmpl = (config && config.blacklistListEmpty) || '群 {group} 的黑名单为空'
1044
- return tmpl.replace('{group}', group)
1086
+ const tmpl = (config && config.blacklistListEmpty) || '{group}的黑名单为空'
1087
+ const groupLabelL = group && group.toLowerCase() === 'all' ? '全局' : '群 ' + group
1088
+ return renderMsg(tmpl.replace('{group}', groupLabelL))
1045
1089
  }
1046
1090
  const entries = rows[0].entries || {}
1047
1091
  // 构造列表消息,all 也是专用前缀
@@ -1060,7 +1104,8 @@ export async function processBlacklistCommand(ctx: Context, session: any, rawArg
1060
1104
  const globalHit = globalRows.length > 0 && (globalRows[0].entries || {})[targetUser] !== undefined
1061
1105
 
1062
1106
  // 使用模板格式化回复的辅助函数
1063
- const tmpl = (config && config.blacklistInfoTemplate) || '全局黑名单: {global}\n本群黑名单: {group}'
1107
+ const tmpl = ((config && config.blacklistInfoTemplate) || '全局黑名单: {global}\\n本群黑名单: {group}')
1108
+ .replace(/\\\\n/g, '\x01').replace(/\\n/g, '\n').replace(/\x01/g, '\\n')
1064
1109
  const formatReply = (localHit: boolean, groupsList?: string[]) => {
1065
1110
  if (groupsList) {
1066
1111
  return tmpl.replace('{global}', globalHit ? '有' : '无').replace(
@@ -1109,7 +1154,7 @@ export async function processBlacklistCommand(ctx: Context, session: any, rawArg
1109
1154
  const groupId = groupArg
1110
1155
  if (config?.enableStrictGroupCheck && groupId.toLowerCase() !== 'all') {
1111
1156
  if (!/^\d{5,15}$/.test(groupId)) {
1112
- return (config.invalidGroupMessage || '群号 {group} 格式不合法或机器人不在该群中').replace('{group}', groupId)
1157
+ return renderMsg((config.invalidGroupMessage || '群号 {group} 格式不合法或机器人不在该群中').replace('{group}', groupId))
1113
1158
  }
1114
1159
  }
1115
1160
  const [ok, err] = await checkPermission(session, groupId)
@@ -1294,7 +1339,7 @@ export function apply(ctx: Context, config: Config) {
1294
1339
  }
1295
1340
 
1296
1341
  clogV(`权限检查 - 权限不足`)
1297
- return [false, config.permissionDeniedMessage || '权限不足: 需要群主/管理员权限或koishi三级以上权限']
1342
+ return [false, renderMsg(config.permissionDeniedMessage || '权限不足: 需要群主/管理员权限或koishi三级以上权限')]
1298
1343
  }
1299
1344
 
1300
1345
  // 创建主命令及别名
@@ -1355,7 +1400,7 @@ export function apply(ctx: Context, config: Config) {
1355
1400
  if ((cleanedOptions.query || cleanedOptions.remove) &&
1356
1401
  (parsedKeywords.length > 0 || cleanedOptions.method !== undefined || cleanedOptions.threshold !== undefined ||
1357
1402
  cleanedOptions.message !== undefined || cleanedOptions.enableMessage || cleanedOptions.disableMessage)) {
1358
- return config.parameterConflictMessage || '参数冲突: -? 或 -r 不能与其他参数或关键词一起使用(仅可搭配 -i)'
1403
+ return renderMsg(config.parameterConflictMessage || '参数冲突: -? 或 -r 不能与其他参数或关键词一起使用(仅可搭配 -i)')
1359
1404
  }
1360
1405
 
1361
1406
  // 检查消息参数冲突
@@ -1511,7 +1556,7 @@ export function apply(ctx: Context, config: Config) {
1511
1556
  // 处理关键词: 如果没有关键词但有其他参数,从现有配置中获取
1512
1557
  if (keywordList.length === 0 && !cleanedOptions.query && !cleanedOptions.remove) {
1513
1558
  if (!existingConfig) {
1514
- return config.noKeywordsMessage || '请先提供关键词创建配置,或使用 -? 查询配置,-r 删除配置'
1559
+ return renderMsg(config.noKeywordsMessage || '请先提供关键词创建配置,或使用 -? 查询配置,-r 删除配置')
1515
1560
  }
1516
1561
  keywordList = existingConfig.keywords
1517
1562
  }