koishi-plugin-group-verification 1.0.33 → 1.0.35
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.js +97 -89
- package/package.json +4 -4
- package/readme.md +2 -2
- package/src/index.ts +2155 -2140
package/lib/index.js
CHANGED
|
@@ -45,12 +45,12 @@ var import_koishi = require("koishi");
|
|
|
45
45
|
var name = "group-verification";
|
|
46
46
|
var logger = console;
|
|
47
47
|
var Config = import_koishi.Schema.object({
|
|
48
|
-
defaultReminderMessage: import_koishi.Schema.string().description("默认提醒消息模板(使用 \\n 表示换行,可包含下方变量)").default("{user}({id})
|
|
48
|
+
defaultReminderMessage: import_koishi.Schema.string().description("默认提醒消息模板(使用 \\n 表示换行,可包含下方变量)").default("{user}({id}) 申请入群\\n申请理由: {question}\\n匹配情况: {answer}/{threshold}\\n使用 gva 同意或 gvr 拒绝申请"),
|
|
49
49
|
enableStrictGroupCheck: import_koishi.Schema.boolean().description("是否启用严格的群号检查(检查群号长度)").default(false),
|
|
50
50
|
logLevel: import_koishi.Schema.union(["debug", "info", "warn", "error"]).description("日志级别").default("info"),
|
|
51
|
-
permissionDeniedMessage: import_koishi.Schema.string().description("权限不足时返回给调用者的提示").default("
|
|
51
|
+
permissionDeniedMessage: import_koishi.Schema.string().description("权限不足时返回给调用者的提示").default("权限不足: 需要群主/管理员权限或koishi三级以上权限"),
|
|
52
52
|
invalidGroupMessage: import_koishi.Schema.string().description("无效群号或机器人未在该群时的提示").default("群号 {group} 格式不合法或机器人不在该群中"),
|
|
53
|
-
parameterConflictMessage: import_koishi.Schema.string().description("参数冲突时提示").default("
|
|
53
|
+
parameterConflictMessage: import_koishi.Schema.string().description("参数冲突时提示").default("参数冲突: -? 或 -r 不能与其他参数或关键词一起使用(仅可搭配 -i)"),
|
|
54
54
|
noKeywordsMessage: import_koishi.Schema.string().description("未提供关键词且无法从现有配置继承时的提示").default("请先提供关键词创建配置,或使用 -? 查询配置,-r 删除配置"),
|
|
55
55
|
blacklistAddSuccess: import_koishi.Schema.string().description("将用户加入黑名单后的提示,可使用 {user},{group},{reason}").default("已将用户 {user} 加入群 {group} 黑名单{reason}"),
|
|
56
56
|
blacklistRemoveSuccess: import_koishi.Schema.string().description("从黑名单移除用户后的提示,可使用 {user},{group}").default("已从群 {group} 的黑名单中移除用户 {user}"),
|
|
@@ -162,25 +162,31 @@ function validateKeywordFormat(raw) {
|
|
|
162
162
|
}
|
|
163
163
|
__name(validateKeywordFormat, "validateKeywordFormat");
|
|
164
164
|
function usageString() {
|
|
165
|
-
return
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
165
|
+
return `用法:
|
|
166
|
+
-i 群号
|
|
167
|
+
-m (0|1|2|3)审核方式
|
|
168
|
+
-t 阈值
|
|
169
|
+
-msg [可选,消息内容] 并打开提醒消息
|
|
170
|
+
-nomsg 取消提醒消息(与-msg冲突)
|
|
171
|
+
-r 删除配置
|
|
172
|
+
-? 查询配置
|
|
169
173
|
|
|
170
|
-
|
|
171
|
-
|
|
174
|
+
对于存在空格的关键词/提醒消息用双引号""包裹
|
|
175
|
+
|
|
176
|
+
# 例
|
|
177
|
+
gvc 关键词1,关键词2 -m 1 -t 2 # 创建配置
|
|
178
|
+
gvc -msg "消息内容" # 修改提醒消息
|
|
172
179
|
gvc -nomsg # 禁用提醒消息
|
|
173
|
-
# 查询/删除
|
|
174
180
|
gvc -? # 查询配置
|
|
175
181
|
gvc -r # 删除配置
|
|
176
182
|
|
|
177
|
-
审核方式说明(使用 -m
|
|
183
|
+
审核方式说明(使用 -m 参数):
|
|
178
184
|
0 全部同意(默认)
|
|
179
|
-
1 按数量同意,需要 -t 指定数量
|
|
185
|
+
1 按数量同意,需要 -t 指定数量
|
|
180
186
|
2 按比例同意,需要 -t 指定百分比
|
|
181
|
-
3
|
|
187
|
+
3 全部拒绝
|
|
182
188
|
|
|
183
|
-
|
|
189
|
+
提醒消息可用变量: {user} 用户名 {id} 用户ID
|
|
184
190
|
{group} 群号 {gname} 群名称
|
|
185
191
|
{question} 申请理由 {answer} 匹配情况 {threshold} 阈值
|
|
186
192
|
使用 \\n 换行`;
|
|
@@ -188,7 +194,7 @@ function usageString() {
|
|
|
188
194
|
__name(usageString, "usageString");
|
|
189
195
|
function mergeReminder(existingConfig, cleanedOptions, hasRealMessageParam, hasRealEnableMessageParam, hasRealDisableMessageParam, logger2, defaultMessage) {
|
|
190
196
|
let reminderEnabled = true;
|
|
191
|
-
let reminderMessage = defaultMessage || "{user}({id})
|
|
197
|
+
let reminderMessage = defaultMessage || "{user}({id}) 申请入群\\n申请理由: {question}\\n匹配情况: {answer}/{threshold}\\n使用 gva 同意或 gvr 拒绝申请";
|
|
192
198
|
if (existingConfig) {
|
|
193
199
|
reminderEnabled = existingConfig.reminderEnabled;
|
|
194
200
|
reminderMessage = existingConfig.reminderMessage || reminderMessage;
|
|
@@ -202,7 +208,7 @@ function mergeReminder(existingConfig, cleanedOptions, hasRealMessageParam, hasR
|
|
|
202
208
|
} else if (hasRealMessageParam) {
|
|
203
209
|
reminderEnabled = true;
|
|
204
210
|
if (cleanedOptions.message !== void 0) {
|
|
205
|
-
reminderMessage = cleanedOptions.message
|
|
211
|
+
reminderMessage = cleanedOptions.message;
|
|
206
212
|
logger2.debug(`设置自定义提醒消息: ${reminderMessage.substring(0, 50)}...`);
|
|
207
213
|
}
|
|
208
214
|
}
|
|
@@ -353,17 +359,17 @@ async function checkPermission(session, targetGroupId) {
|
|
|
353
359
|
}
|
|
354
360
|
try {
|
|
355
361
|
const member = await session.bot.getGuildMember(groupId, session.userId);
|
|
356
|
-
logger.
|
|
362
|
+
logger.debug(`权限检查 - 获取到成员信息:`, {
|
|
357
363
|
roles: member?.roles,
|
|
358
364
|
permissions: member?.permissions
|
|
359
365
|
});
|
|
360
366
|
if (member) {
|
|
361
367
|
if (member.permissions?.includes("OWNER") || member.roles?.includes("owner")) {
|
|
362
|
-
logger.
|
|
368
|
+
logger.debug(`权限检查 - 用户是群主`);
|
|
363
369
|
return [true];
|
|
364
370
|
}
|
|
365
371
|
if (member.roles?.includes("admin") || member.permissions?.includes("ADMINISTRATOR")) {
|
|
366
|
-
logger.
|
|
372
|
+
logger.debug(`权限检查 - 用户是管理员`);
|
|
367
373
|
return [true];
|
|
368
374
|
}
|
|
369
375
|
}
|
|
@@ -455,7 +461,7 @@ function parseConfigArgs(raw) {
|
|
|
455
461
|
}
|
|
456
462
|
}
|
|
457
463
|
tokens = tokens.map(
|
|
458
|
-
(t) => t.replace(new RegExp(ESC_QUOTE, "g"), '"').replace(new RegExp(ESC_BACKSLASH, "g"), "
|
|
464
|
+
(t) => t.replace(new RegExp(ESC_QUOTE, "g"), '"').replace(new RegExp(ESC_BACKSLASH, "g"), "\\\\")
|
|
459
465
|
);
|
|
460
466
|
for (let i = 0; i < tokens.length; i++) {
|
|
461
467
|
const tok = tokens[i];
|
|
@@ -516,7 +522,7 @@ function parseConfigArgs(raw) {
|
|
|
516
522
|
}
|
|
517
523
|
const keywordSection = raw.split(/(?:^|\s+)-(?:i|m|t|msg|nomsg|\?|r)\b/)[0].trim();
|
|
518
524
|
if (keywordSection && !validateKeywordFormat(keywordSection)) {
|
|
519
|
-
error = '
|
|
525
|
+
error = '关键词应使用逗号分隔或引号框起(如: k1,k2,k3 或 "k1","k2" 或 "k1,k2",k3)';
|
|
520
526
|
}
|
|
521
527
|
return { keywords, flags, error };
|
|
522
528
|
}
|
|
@@ -600,7 +606,7 @@ async function handleFailedVerification(ctx, session, config, matchedCount, requ
|
|
|
600
606
|
logger.debug(`群 ${guildId} 的提醒消息已被禁用,跳过发送`);
|
|
601
607
|
return;
|
|
602
608
|
}
|
|
603
|
-
let reminderMsg = config.reminderMessage;
|
|
609
|
+
let reminderMsg = config.reminderMessage.replace(/\\\\n/g, "").replace(/\\n/g, "\n").replace(/\x01/g, "\\n");
|
|
604
610
|
reminderMsg = reminderMsg.replace(/{user}/g, username).replace(/{id}/g, userId).replace(/{group}/g, guildId).replace(/{gname}/g, groupName).replace(/{question}/g, message).replace(/{answer}/g, matchedCount.toString()).replace(/{threshold}/g, requiredThreshold);
|
|
605
611
|
const rawChannel = (session.channelId || "").toString().trim();
|
|
606
612
|
const channel = rawChannel || guildId;
|
|
@@ -610,7 +616,7 @@ async function handleFailedVerification(ctx, session, config, matchedCount, requ
|
|
|
610
616
|
try {
|
|
611
617
|
await session.bot.broadcast([target], reminderMsg);
|
|
612
618
|
} catch (err) {
|
|
613
|
-
logger.warn("bot.broadcast
|
|
619
|
+
logger.warn("bot.broadcast 失败,回退到 ctx.broadcast", err);
|
|
614
620
|
if (typeof ctx.broadcast === "function") {
|
|
615
621
|
await ctx.broadcast([target], reminderMsg);
|
|
616
622
|
} else {
|
|
@@ -645,7 +651,7 @@ async function processBlacklistCommand(ctx, session, rawArgs, config) {
|
|
|
645
651
|
const op = parts[0]?.toLowerCase();
|
|
646
652
|
if (!op || !["a", "r", "l", "i"].includes(op)) {
|
|
647
653
|
return [
|
|
648
|
-
"
|
|
654
|
+
"用法: ",
|
|
649
655
|
" gvb a id [reason] [group] 将用户加入黑名单",
|
|
650
656
|
" gvb r id [group] 将用户移出黑名单",
|
|
651
657
|
" gvb l [group] 查询群黑名单",
|
|
@@ -693,7 +699,7 @@ async function processBlacklistCommand(ctx, session, rawArgs, config) {
|
|
|
693
699
|
const row = rows[0];
|
|
694
700
|
const entries = row.entries || {};
|
|
695
701
|
if (entries[targetUser] !== void 0) {
|
|
696
|
-
return `用户 ${targetUser} 已在群 ${group}
|
|
702
|
+
return `用户 ${targetUser} 已在群 ${group} 的黑名单中: ${entries[targetUser]}`;
|
|
697
703
|
}
|
|
698
704
|
entries[targetUser] = storedReason;
|
|
699
705
|
await ctx.database.set("group_verification_blacklist", { id: row.id }, { entries });
|
|
@@ -711,7 +717,7 @@ async function processBlacklistCommand(ctx, session, rawArgs, config) {
|
|
|
711
717
|
}
|
|
712
718
|
}
|
|
713
719
|
const tmpl = config && config.blacklistAddSuccess || "已将用户 {user} 加入群 {group} 黑名单{reason}";
|
|
714
|
-
return tmpl.replace("{user}", targetUser).replace("{group}", group).replace("{reason}", reason ?
|
|
720
|
+
return tmpl.replace("{user}", targetUser).replace("{group}", group).replace("{reason}", reason ? `,原因: ${reason}` : "");
|
|
715
721
|
}
|
|
716
722
|
if (op === "r") {
|
|
717
723
|
targetUser = parts[1];
|
|
@@ -768,7 +774,7 @@ async function processBlacklistCommand(ctx, session, rawArgs, config) {
|
|
|
768
774
|
`;
|
|
769
775
|
let msg = prefix;
|
|
770
776
|
for (const uid in entries) {
|
|
771
|
-
msg += `${uid}${entries[uid] ?
|
|
777
|
+
msg += `${uid}${entries[uid] ? `: ${entries[uid]}` : ""}
|
|
772
778
|
`;
|
|
773
779
|
}
|
|
774
780
|
return msg;
|
|
@@ -802,7 +808,7 @@ async function processBlacklistCommand(ctx, session, rawArgs, config) {
|
|
|
802
808
|
}
|
|
803
809
|
if (groupArg.toLowerCase() === "all") {
|
|
804
810
|
const auth = session.author?.authority || session.user?.authority;
|
|
805
|
-
if (!(auth && auth >= 3)) return "
|
|
811
|
+
if (!(auth && auth >= 3)) return "权限不足: 查看全局/所有群黑名单需要 koishi 3 级以上权限";
|
|
806
812
|
const rows2 = await ctx.database.get("group_verification_blacklist", {});
|
|
807
813
|
const globalReason2 = globalHit ? globalRows[0].entries[targetUser] : "无";
|
|
808
814
|
const lines = [];
|
|
@@ -869,8 +875,8 @@ function apply(ctx, config) {
|
|
|
869
875
|
manuallyApproved: "integer",
|
|
870
876
|
rejected: "integer",
|
|
871
877
|
totalJoined: "integer",
|
|
872
|
-
//
|
|
873
|
-
// Koishi
|
|
878
|
+
// 以字符串(ISO 时间戳)格式存储,保留完整日期+时间;
|
|
879
|
+
// Koishi 的 date 类型会截断到天,导致时间显示为 00:00:00。
|
|
874
880
|
lastUpdated: "string"
|
|
875
881
|
}, {
|
|
876
882
|
primary: "id",
|
|
@@ -884,7 +890,7 @@ function apply(ctx, config) {
|
|
|
884
890
|
requestMessage: "string",
|
|
885
891
|
// 保存 OneBot 事件提供的原始 requestId;用于同意/拒绝操作
|
|
886
892
|
requestId: "string",
|
|
887
|
-
//
|
|
893
|
+
// 以字符串格式记录完整时间戳,保留时间分量
|
|
888
894
|
applyTime: "string"
|
|
889
895
|
}, {
|
|
890
896
|
primary: "id",
|
|
@@ -900,7 +906,7 @@ function apply(ctx, config) {
|
|
|
900
906
|
indexes: [["groupId"]]
|
|
901
907
|
});
|
|
902
908
|
ctx.on("guild-member-request", async (session) => {
|
|
903
|
-
logger.
|
|
909
|
+
logger.debug("收到 guild-member-request 事件,转发给处理函数");
|
|
904
910
|
await handleGuildMemberRequestEvent(ctx, session);
|
|
905
911
|
});
|
|
906
912
|
ctx.on("guild-member-added", async (session) => {
|
|
@@ -973,7 +979,7 @@ function apply(ctx, config) {
|
|
|
973
979
|
});
|
|
974
980
|
if (member) {
|
|
975
981
|
if (member.permissions?.includes("OWNER") || member.roles?.includes("owner")) {
|
|
976
|
-
logger.
|
|
982
|
+
logger.debug(`权限检查 - 用户是群主`);
|
|
977
983
|
return [true];
|
|
978
984
|
}
|
|
979
985
|
if (member.roles?.includes("admin") || member.permissions?.includes("ADMINISTRATOR")) {
|
|
@@ -987,7 +993,7 @@ function apply(ctx, config) {
|
|
|
987
993
|
}
|
|
988
994
|
logger.debug(`权限检查 - 权限不足`);
|
|
989
995
|
const debugInfo = `调试信息 - 用户ID:${session.userId}, 群号:${groupId}, 权限等级:${koishiAuthority || "未知"}`;
|
|
990
|
-
return [false, (config.permissionDeniedMessage || "
|
|
996
|
+
return [false, (config.permissionDeniedMessage || "权限不足: 需要群主/管理员权限或koishi三级以上权限") + `
|
|
991
997
|
${debugInfo}`];
|
|
992
998
|
}
|
|
993
999
|
__name(checkPermission2, "checkPermission");
|
|
@@ -1001,30 +1007,30 @@ ${debugInfo}`];
|
|
|
1001
1007
|
"group-verify.配置",
|
|
1002
1008
|
"gvc"
|
|
1003
1009
|
).option("groupId", "-i <groupId> 指定群号").option("method", "-m <method> 审核方式 (0-3)").option("threshold", "-t <threshold> 阈值参数").option("message", "-msg [message] 自定义提醒消息").option("disableMessage", "-nomsg 禁用提醒消息").option("query", "-? 查询当前配置").option("remove", "-r 删除配置").action(async ({ session, options }, keywords) => {
|
|
1004
|
-
logger.
|
|
1005
|
-
logger.
|
|
1010
|
+
logger.debug(`=== 命令解析调试 ===`);
|
|
1011
|
+
logger.debug(`session内容: guildId=${session.guildId}, userId=${session.userId}`);
|
|
1006
1012
|
const rawInput = session.content.split(/\s+/).slice(1).join(" ");
|
|
1007
|
-
logger.
|
|
1013
|
+
logger.debug(`原始命令参数: "${rawInput}"`);
|
|
1008
1014
|
const parsed = parseConfigArgs(rawInput);
|
|
1009
1015
|
const { keywords: parsedKeywords, flags, error: parseError } = parsed;
|
|
1010
1016
|
if (parseError) {
|
|
1011
1017
|
return parseError;
|
|
1012
1018
|
}
|
|
1013
|
-
logger.
|
|
1019
|
+
logger.debug(`解析结果 flags=${JSON.stringify(flags)}, keywords=[${parsedKeywords.join(", ")}]`);
|
|
1014
1020
|
const cleanedOptions = {
|
|
1015
1021
|
groupId: flags.groupId || options.groupId,
|
|
1016
1022
|
method: flags.method || (options.method === "" ? void 0 : options.method),
|
|
1017
1023
|
threshold: flags.threshold || options.threshold,
|
|
1018
1024
|
message: flags.message !== void 0 ? flags.message : options.message,
|
|
1019
1025
|
enableMessage: flags.enableMessage,
|
|
1020
|
-
//
|
|
1026
|
+
// 新增: -msg 裸调用标记
|
|
1021
1027
|
disableMessage: flags.disableMessage || options.disableMessage,
|
|
1022
1028
|
query: flags.query || options.query,
|
|
1023
1029
|
remove: flags.remove || options.remove
|
|
1024
1030
|
};
|
|
1025
|
-
logger.
|
|
1031
|
+
logger.debug(`合并后options: ${JSON.stringify(cleanedOptions, null, 2)}`);
|
|
1026
1032
|
if ((cleanedOptions.query || cleanedOptions.remove) && (parsedKeywords.length > 0 || cleanedOptions.method !== void 0 || cleanedOptions.threshold !== void 0 || cleanedOptions.message !== void 0 || cleanedOptions.enableMessage || cleanedOptions.disableMessage)) {
|
|
1027
|
-
return config.parameterConflictMessage || "
|
|
1033
|
+
return config.parameterConflictMessage || "参数冲突: -? 或 -r 不能与其他参数或关键词一起使用(仅可搭配 -i)";
|
|
1028
1034
|
}
|
|
1029
1035
|
const hasRealMessageParam = cleanedOptions.message !== void 0;
|
|
1030
1036
|
const hasRealEnableMessageParam = cleanedOptions.enableMessage === true;
|
|
@@ -1049,7 +1055,7 @@ ${debugInfo}`];
|
|
|
1049
1055
|
return "请在群聊中使用此命令或指定群号(-i参数)";
|
|
1050
1056
|
}
|
|
1051
1057
|
if ((hasRealMessageParam || hasRealEnableMessageParam) && hasRealDisableMessageParam) {
|
|
1052
|
-
return "
|
|
1058
|
+
return "参数冲突: 不能同时使用 -msg 和 -nomsg";
|
|
1053
1059
|
}
|
|
1054
1060
|
const usedOptions = [];
|
|
1055
1061
|
if (cleanedOptions.method !== void 0) usedOptions.push("-m");
|
|
@@ -1107,13 +1113,14 @@ ${debugInfo}`];
|
|
|
1107
1113
|
const createTime = new Date(config2.createdAt).toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" });
|
|
1108
1114
|
const updateTime = new Date(config2.updatedAt).toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" });
|
|
1109
1115
|
const reminderStatus = config2.reminderEnabled ? "启用" : "禁用";
|
|
1110
|
-
|
|
1116
|
+
const displayMsg = (config2.reminderMessage || "无").replace(/\\\\n/g, "").replace(/\\n/g, "\n").replace(/\x01/g, "\\n");
|
|
1117
|
+
return `群 ${targetGroupId} 配置:
|
|
1111
1118
|
关键词: ${decodedKeywords2.join(", ")}
|
|
1112
1119
|
审核方式: ${methodDesc}
|
|
1113
1120
|
阈值: ${thresholdInfo}
|
|
1114
1121
|
提醒消息: ${reminderStatus}
|
|
1115
1122
|
自定义消息:
|
|
1116
|
-
${
|
|
1123
|
+
${displayMsg}
|
|
1117
1124
|
创建时间: ${createTime}
|
|
1118
1125
|
更新时间: ${updateTime}
|
|
1119
1126
|
创建者: ${config2.createdBy}
|
|
@@ -1123,7 +1130,7 @@ ${config2.reminderMessage || "无"}
|
|
|
1123
1130
|
}
|
|
1124
1131
|
}
|
|
1125
1132
|
let keywordList = parsedKeywords.slice();
|
|
1126
|
-
logger.
|
|
1133
|
+
logger.debug(`关键词解析结果: [${keywordList.join(", ")}] - 原始输入: "${rawInput}"`);
|
|
1127
1134
|
if (keywordList.length === 0 && !cleanedOptions.query && !cleanedOptions.remove) {
|
|
1128
1135
|
const hasConfigParams = cleanedOptions.method !== void 0 || cleanedOptions.threshold !== void 0 || hasRealMessageParam || hasRealEnableMessageParam || hasRealDisableMessageParam;
|
|
1129
1136
|
if (!hasConfigParams) {
|
|
@@ -1156,7 +1163,7 @@ ${config2.reminderMessage || "无"}
|
|
|
1156
1163
|
reviewMethod = existingConfig.reviewMethod;
|
|
1157
1164
|
if (existingConfig.reviewParameters === void 0 || existingConfig.reviewParameters === null || isNaN(existingConfig.reviewParameters)) {
|
|
1158
1165
|
reviewParameters = 0;
|
|
1159
|
-
logger.
|
|
1166
|
+
logger.debug(`检测到老版本数据或无效值,使用默认阈值: 0`);
|
|
1160
1167
|
} else {
|
|
1161
1168
|
reviewParameters = existingConfig.reviewParameters;
|
|
1162
1169
|
}
|
|
@@ -1164,7 +1171,7 @@ ${config2.reminderMessage || "无"}
|
|
|
1164
1171
|
if (cleanedOptions.method !== void 0 && cleanedOptions.method !== "") {
|
|
1165
1172
|
const methodNum = parseInt(cleanedOptions.method);
|
|
1166
1173
|
if (isNaN(methodNum) || methodNum < 0 || methodNum > 3) {
|
|
1167
|
-
return "
|
|
1174
|
+
return "审核方式参数错误: 0-全部同意, 1-按数量同意, 2-按比例同意, 3-全部拒绝";
|
|
1168
1175
|
}
|
|
1169
1176
|
const oldMethod = reviewMethod;
|
|
1170
1177
|
reviewMethod = methodNum;
|
|
@@ -1209,7 +1216,7 @@ ${config2.reminderMessage || "无"}
|
|
|
1209
1216
|
const encodedKeywords = keywordList.map((keyword) => {
|
|
1210
1217
|
return keyword.replace(/,/g, "[[COMMA]]");
|
|
1211
1218
|
});
|
|
1212
|
-
logger.
|
|
1219
|
+
logger.debug(`编码后准备存储的关键词: ${JSON.stringify(encodedKeywords)}`);
|
|
1213
1220
|
const dbData = {
|
|
1214
1221
|
keywords: encodedKeywords,
|
|
1215
1222
|
reviewMethod,
|
|
@@ -1253,24 +1260,25 @@ ${config2.reminderMessage || "无"}
|
|
|
1253
1260
|
feedbackMessage += `提醒状态: ${reminderEnabled ? "启用" : "禁用"}
|
|
1254
1261
|
`;
|
|
1255
1262
|
if (reminderMessage && reminderEnabled) {
|
|
1263
|
+
const displayReminder = reminderMessage.replace(/\\\\n/g, "").replace(/\\n/g, "\n").replace(/\x01/g, "\\n");
|
|
1256
1264
|
feedbackMessage += `提醒消息:
|
|
1257
|
-
${
|
|
1265
|
+
${displayReminder}
|
|
1258
1266
|
`;
|
|
1259
1267
|
}
|
|
1260
|
-
logger.
|
|
1261
|
-
logger.
|
|
1262
|
-
logger.
|
|
1263
|
-
logger.
|
|
1264
|
-
logger.
|
|
1265
|
-
logger.
|
|
1266
|
-
logger.
|
|
1268
|
+
logger.debug(`准备存储到数据库的关键词: ${JSON.stringify(encodedKeywords)}`);
|
|
1269
|
+
logger.debug(`=== 配置处理详情 ===`);
|
|
1270
|
+
logger.debug(`原始输入: ${keywords || "无关键词"}`);
|
|
1271
|
+
logger.debug(`审核方式: ${reviewMethod} (${["全部同意", "按数量", "按比例", "全部拒绝"][reviewMethod]})`);
|
|
1272
|
+
logger.debug(`阈值参数: ${JSON.stringify(reviewParameters)}`);
|
|
1273
|
+
logger.debug(`关键词列表: [${keywordList.map((k) => `"${k}"`).join(", ")}]`);
|
|
1274
|
+
logger.debug(`现有配置: ${existingConfig ? "存在" : "不存在"}`);
|
|
1267
1275
|
if (existingConfig) {
|
|
1268
|
-
logger.
|
|
1269
|
-
logger.
|
|
1270
|
-
logger.
|
|
1271
|
-
logger.
|
|
1276
|
+
logger.debug(`原审核方式: ${existingConfig.reviewMethod}`);
|
|
1277
|
+
logger.debug(`原阈值: ${JSON.stringify(existingConfig.reviewParameters)}`);
|
|
1278
|
+
logger.debug(`原关键词数: ${existingConfig.keywords.length}`);
|
|
1279
|
+
logger.debug(`新关键词数: ${keywordList.length}`);
|
|
1272
1280
|
}
|
|
1273
|
-
logger.
|
|
1281
|
+
logger.debug(`==================`);
|
|
1274
1282
|
logger.info(feedbackMessage.replace(/\n/g, "; "));
|
|
1275
1283
|
return feedbackMessage;
|
|
1276
1284
|
});
|
|
@@ -1455,7 +1463,7 @@ ${reminderMessage}
|
|
|
1455
1463
|
const isGroupId = target && /^\d+$/.test(target);
|
|
1456
1464
|
const isSpecialTarget = target && validTargets.includes(target.toLowerCase());
|
|
1457
1465
|
if (target && !isGroupId && !isSpecialTarget) {
|
|
1458
|
-
return "
|
|
1466
|
+
return "参数错误: 只能指定群号、all、total或留空";
|
|
1459
1467
|
}
|
|
1460
1468
|
if (target?.toLowerCase() === "total" || target?.toLowerCase() === "all") {
|
|
1461
1469
|
const koishiAuthority = session.author?.authority || session.user?.authority;
|
|
@@ -1496,11 +1504,11 @@ ${reminderMessage}
|
|
|
1496
1504
|
if (pendingApplications.length === 0) {
|
|
1497
1505
|
return "当前没有待审核的加群申请";
|
|
1498
1506
|
}
|
|
1499
|
-
let result = "
|
|
1507
|
+
let result = "待审核申请列表: \n";
|
|
1500
1508
|
pendingApplications.forEach((app, index) => {
|
|
1501
1509
|
result += `${index + 1}. ${app.userName}(${app.userId})
|
|
1502
|
-
|
|
1503
|
-
|
|
1510
|
+
申请时间: ${app.applyTime.toLocaleString()}
|
|
1511
|
+
申请理由: ${app.requestMessage}
|
|
1504
1512
|
|
|
1505
1513
|
`;
|
|
1506
1514
|
});
|
|
@@ -1518,19 +1526,19 @@ ${reminderMessage}
|
|
|
1518
1526
|
return await processBlacklistCommand(ctx, session, args || "", config);
|
|
1519
1527
|
});
|
|
1520
1528
|
groupVerify.subcommand(".help", "显示帮助信息").alias("gv.帮助", "gverify.帮助", "group-verify.帮助", "帮助", "hlp", "帮助信息").action(() => {
|
|
1521
|
-
return
|
|
1522
|
-
|
|
1529
|
+
return `群组验证命令帮助:
|
|
1530
|
+
主指令别名: gv, gverify
|
|
1523
1531
|
|
|
1524
1532
|
配置命令 (.config/.cfg):
|
|
1525
|
-
|
|
1526
|
-
1.
|
|
1527
|
-
2.
|
|
1528
|
-
3.
|
|
1529
|
-
4.
|
|
1530
|
-
5.
|
|
1531
|
-
6.
|
|
1533
|
+
用法:
|
|
1534
|
+
1. 创建新配置: gv.cfg 关键词1,关键词2 -m 1 -t 2
|
|
1535
|
+
2. 修改现有配置: gv.cfg -m 1 -t 2 (不提供关键词)
|
|
1536
|
+
3. 启用提醒消息: gv.cfg -msg "消息内容"
|
|
1537
|
+
4. 禁用提醒消息: gv.cfg -nomsg
|
|
1538
|
+
5. 查询配置: gv.cfg -?
|
|
1539
|
+
6. 删除配置: gv.cfg -r
|
|
1532
1540
|
|
|
1533
|
-
|
|
1541
|
+
参数说明:
|
|
1534
1542
|
-i <群号> 指定群号(私聊时必需)
|
|
1535
1543
|
-m <方式> 审核方式 (0=全部同意, 1=按数量, 2=按比例, 3=全部拒绝)
|
|
1536
1544
|
-t <阈值> 阈值参数(方式1:0-关键词数, 方式2:0-100)
|
|
@@ -1542,19 +1550,19 @@ ${reminderMessage}
|
|
|
1542
1550
|
-? 查询当前配置
|
|
1543
1551
|
-r 删除配置
|
|
1544
1552
|
|
|
1545
|
-
|
|
1546
|
-
•
|
|
1547
|
-
•
|
|
1548
|
-
•
|
|
1549
|
-
•
|
|
1553
|
+
引号使用规则:
|
|
1554
|
+
• 关键词包含空格: gv.cfg "关键词1,关键词 2,关键词3"
|
|
1555
|
+
• 提醒消息包含空格: gv.cfg -msg "这是包含空格的消息"
|
|
1556
|
+
• 内部引号转义: gv.cfg -msg "包含\\"引号\\"的内容"
|
|
1557
|
+
• 换行符: gv.cfg -msg "第一行\\n第二行"
|
|
1550
1558
|
|
|
1551
|
-
|
|
1559
|
+
特殊说明:
|
|
1552
1560
|
• 阈值可设为0表示全部同意
|
|
1553
1561
|
• 关键词数量变化时阈值会自动调整
|
|
1554
1562
|
• 重复参数会使用最后出现的值并提醒
|
|
1555
1563
|
• 群号检查可在插件配置中开关
|
|
1556
1564
|
|
|
1557
|
-
|
|
1565
|
+
提醒消息变量:
|
|
1558
1566
|
{user} - 用户名
|
|
1559
1567
|
{id} - 用户ID
|
|
1560
1568
|
{group} - 群号
|
|
@@ -1563,9 +1571,9 @@ ${reminderMessage}
|
|
|
1563
1571
|
{answer} - 答对数量/比例
|
|
1564
1572
|
{threshold} - 阈值要求
|
|
1565
1573
|
|
|
1566
|
-
|
|
1574
|
+
使用示例:
|
|
1567
1575
|
gv.cfg "关键词1,关键词2" -m 1 -t 2
|
|
1568
|
-
gv.cfg -msg "用户 {user}\\n
|
|
1576
|
+
gv.cfg -msg "用户 {user}\\n申请理由: {question}"
|
|
1569
1577
|
gv.cfg -m 2 -t 80
|
|
1570
1578
|
gv.cfg -nomsg
|
|
1571
1579
|
|
|
@@ -1575,21 +1583,21 @@ ${reminderMessage}
|
|
|
1575
1583
|
l [group] 查询群黑名单;传入 all 时查看全局黑名单
|
|
1576
1584
|
i id [group] 查询账号黑名单;不带参数时查询本群与全局,
|
|
1577
1585
|
指定群号查询该群与全局,传入 all 则列出所有群及全局(需Koishi 3级)
|
|
1578
|
-
|
|
1586
|
+
使用示例:
|
|
1579
1587
|
gvb a 12345 作弊记录
|
|
1580
1588
|
gvb r 12345 67890
|
|
1581
1589
|
gvb l
|
|
1582
1590
|
gvb l all
|
|
1583
1591
|
gvb i 12345
|
|
1584
1592
|
|
|
1585
|
-
|
|
1593
|
+
快捷命令:
|
|
1586
1594
|
gvc - 配置命令快捷方式
|
|
1587
1595
|
gva - 同意申请快捷命令
|
|
1588
1596
|
gvr - 拒绝申请快捷命令
|
|
1589
1597
|
gvp - 查看待审核列表快捷命令
|
|
1590
1598
|
gvs - 查看统计信息快捷命令
|
|
1591
1599
|
|
|
1592
|
-
|
|
1600
|
+
权限说明:
|
|
1593
1601
|
- 群主/管理员权限
|
|
1594
1602
|
- koishi三级以上权限
|
|
1595
1603
|
- 私聊时必须指定群号(-i参数)`;
|
|
@@ -1649,7 +1657,7 @@ ${reminderMessage}
|
|
|
1649
1657
|
}
|
|
1650
1658
|
const stat = stats[0];
|
|
1651
1659
|
const lastUpdated = new Date(stat.lastUpdated).toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" });
|
|
1652
|
-
return `群 ${groupId}
|
|
1660
|
+
return `群 ${groupId} 验证统计:
|
|
1653
1661
|
自动批准: ${stat.autoApproved}
|
|
1654
1662
|
手动批准: ${stat.manuallyApproved}
|
|
1655
1663
|
拒绝: ${stat.rejected}
|
|
@@ -1663,7 +1671,7 @@ ${reminderMessage}
|
|
|
1663
1671
|
}
|
|
1664
1672
|
const stat = stats[0];
|
|
1665
1673
|
const lastUpdated = new Date(stat.lastUpdated).toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" });
|
|
1666
|
-
return
|
|
1674
|
+
return `总计验证统计:
|
|
1667
1675
|
自动批准: ${stat.autoApproved}
|
|
1668
1676
|
手动批准: ${stat.manuallyApproved}
|
|
1669
1677
|
拒绝: ${stat.rejected}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "koishi-plugin-group-verification",
|
|
3
|
-
"description": "Koishi
|
|
3
|
+
"description": "Koishi 群组加群验证插件,支持多关键词匹配审核、多种审核方式、黑名单和详细统计功能",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/LHDyx/koishi-plugin-group-verification.git"
|
|
@@ -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.
|
|
11
|
+
"version": "1.0.35",
|
|
12
12
|
"main": "lib/index.js",
|
|
13
13
|
"typings": "lib/index.d.ts",
|
|
14
14
|
"files": [
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"koishi": "^4.15.0"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
|
-
"
|
|
48
|
-
"
|
|
47
|
+
"@types/node": "^16.0.0",
|
|
48
|
+
"typescript": "^4.9.0"
|
|
49
49
|
}
|
|
50
50
|
}
|
package/readme.md
CHANGED
|
@@ -138,11 +138,11 @@ group-verify.blacklist i <用户ID> [群号|all]
|
|
|
138
138
|
|
|
139
139
|
插件启动时会输出运行状态,并根据 `logLevel` 调整输出量。
|
|
140
140
|
- `debug`:打印所有调试细节,包括权限检查、命令解析等。
|
|
141
|
-
- `info
|
|
141
|
+
- `info`:默认值,记录关键事件(插件启动、配置修改、自动拒绝、黑名单踢人等),包括成功踢人日志。
|
|
142
142
|
- `warn`:记录可恢复的异常,例如尝试踢出用户失败、数据库操作问题。
|
|
143
143
|
- `error`:仅在遇到严重错误时输出。
|
|
144
144
|
|
|
145
|
-
添加黑名单时会尝试在对应群踢出该用户,成功记为 `info`,失败记为 `warn
|
|
145
|
+
添加黑名单时会尝试在对应群踢出该用户,成功记为 `info`,失败记为 `warn`。权限检查的详细过程则只会在 `debug` 级别输出。
|
|
146
146
|
|
|
147
147
|
### 严格群号检查
|
|
148
148
|
|