koishi-plugin-group-verification 1.0.33 → 1.0.34
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 +61 -55
- package/package.json +2 -2
- package/src/index.ts +84 -78
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}) 申请加入群 {gname}({group})\n
|
|
48
|
+
defaultReminderMessage: import_koishi.Schema.string().description("默认提醒消息模板(使用 \\n 表示换行,可包含下方变量)").default("{user}({id}) 申请加入群 {gname}({group})\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}) 申请加入群 {gname}({group})\n
|
|
197
|
+
let reminderMessage = defaultMessage || "{user}({id}) 申请加入群 {gname}({group})\n申请理由: {question}\n匹配情况: {answer}/{threshold}";
|
|
192
198
|
if (existingConfig) {
|
|
193
199
|
reminderEnabled = existingConfig.reminderEnabled;
|
|
194
200
|
reminderMessage = existingConfig.reminderMessage || reminderMessage;
|
|
@@ -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
|
}
|
|
@@ -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 = [];
|
|
@@ -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");
|
|
@@ -1017,14 +1023,14 @@ ${debugInfo}`];
|
|
|
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
1031
|
logger.info(`合并后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,7 +1113,7 @@ ${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
|
-
return `群 ${targetGroupId}
|
|
1116
|
+
return `群 ${targetGroupId} 配置:
|
|
1111
1117
|
关键词: ${decodedKeywords2.join(", ")}
|
|
1112
1118
|
审核方式: ${methodDesc}
|
|
1113
1119
|
阈值: ${thresholdInfo}
|
|
@@ -1164,7 +1170,7 @@ ${config2.reminderMessage || "无"}
|
|
|
1164
1170
|
if (cleanedOptions.method !== void 0 && cleanedOptions.method !== "") {
|
|
1165
1171
|
const methodNum = parseInt(cleanedOptions.method);
|
|
1166
1172
|
if (isNaN(methodNum) || methodNum < 0 || methodNum > 3) {
|
|
1167
|
-
return "
|
|
1173
|
+
return "审核方式参数错误: 0-全部同意, 1-按数量同意, 2-按比例同意, 3-全部拒绝";
|
|
1168
1174
|
}
|
|
1169
1175
|
const oldMethod = reviewMethod;
|
|
1170
1176
|
reviewMethod = methodNum;
|
|
@@ -1455,7 +1461,7 @@ ${reminderMessage}
|
|
|
1455
1461
|
const isGroupId = target && /^\d+$/.test(target);
|
|
1456
1462
|
const isSpecialTarget = target && validTargets.includes(target.toLowerCase());
|
|
1457
1463
|
if (target && !isGroupId && !isSpecialTarget) {
|
|
1458
|
-
return "
|
|
1464
|
+
return "参数错误: 只能指定群号、all、total或留空";
|
|
1459
1465
|
}
|
|
1460
1466
|
if (target?.toLowerCase() === "total" || target?.toLowerCase() === "all") {
|
|
1461
1467
|
const koishiAuthority = session.author?.authority || session.user?.authority;
|
|
@@ -1496,11 +1502,11 @@ ${reminderMessage}
|
|
|
1496
1502
|
if (pendingApplications.length === 0) {
|
|
1497
1503
|
return "当前没有待审核的加群申请";
|
|
1498
1504
|
}
|
|
1499
|
-
let result = "
|
|
1505
|
+
let result = "待审核申请列表: \n";
|
|
1500
1506
|
pendingApplications.forEach((app, index) => {
|
|
1501
1507
|
result += `${index + 1}. ${app.userName}(${app.userId})
|
|
1502
|
-
|
|
1503
|
-
|
|
1508
|
+
申请时间: ${app.applyTime.toLocaleString()}
|
|
1509
|
+
申请理由: ${app.requestMessage}
|
|
1504
1510
|
|
|
1505
1511
|
`;
|
|
1506
1512
|
});
|
|
@@ -1518,19 +1524,19 @@ ${reminderMessage}
|
|
|
1518
1524
|
return await processBlacklistCommand(ctx, session, args || "", config);
|
|
1519
1525
|
});
|
|
1520
1526
|
groupVerify.subcommand(".help", "显示帮助信息").alias("gv.帮助", "gverify.帮助", "group-verify.帮助", "帮助", "hlp", "帮助信息").action(() => {
|
|
1521
|
-
return
|
|
1522
|
-
|
|
1527
|
+
return `群组验证命令帮助:
|
|
1528
|
+
主指令别名: gv, gverify
|
|
1523
1529
|
|
|
1524
1530
|
配置命令 (.config/.cfg):
|
|
1525
|
-
|
|
1526
|
-
1.
|
|
1527
|
-
2.
|
|
1528
|
-
3.
|
|
1529
|
-
4.
|
|
1530
|
-
5.
|
|
1531
|
-
6.
|
|
1531
|
+
用法:
|
|
1532
|
+
1. 创建新配置: gv.cfg 关键词1,关键词2 -m 1 -t 2
|
|
1533
|
+
2. 修改现有配置: gv.cfg -m 1 -t 2 (不提供关键词)
|
|
1534
|
+
3. 启用提醒消息: gv.cfg -msg "消息内容"
|
|
1535
|
+
4. 禁用提醒消息: gv.cfg -nomsg
|
|
1536
|
+
5. 查询配置: gv.cfg -?
|
|
1537
|
+
6. 删除配置: gv.cfg -r
|
|
1532
1538
|
|
|
1533
|
-
|
|
1539
|
+
参数说明:
|
|
1534
1540
|
-i <群号> 指定群号(私聊时必需)
|
|
1535
1541
|
-m <方式> 审核方式 (0=全部同意, 1=按数量, 2=按比例, 3=全部拒绝)
|
|
1536
1542
|
-t <阈值> 阈值参数(方式1:0-关键词数, 方式2:0-100)
|
|
@@ -1542,19 +1548,19 @@ ${reminderMessage}
|
|
|
1542
1548
|
-? 查询当前配置
|
|
1543
1549
|
-r 删除配置
|
|
1544
1550
|
|
|
1545
|
-
|
|
1546
|
-
•
|
|
1547
|
-
•
|
|
1548
|
-
•
|
|
1549
|
-
•
|
|
1551
|
+
引号使用规则:
|
|
1552
|
+
• 关键词包含空格: gv.cfg "关键词1,关键词 2,关键词3"
|
|
1553
|
+
• 提醒消息包含空格: gv.cfg -msg "这是包含空格的消息"
|
|
1554
|
+
• 内部引号转义: gv.cfg -msg "包含\\"引号\\"的内容"
|
|
1555
|
+
• 换行符: gv.cfg -msg "第一行\\n第二行"
|
|
1550
1556
|
|
|
1551
|
-
|
|
1557
|
+
特殊说明:
|
|
1552
1558
|
• 阈值可设为0表示全部同意
|
|
1553
1559
|
• 关键词数量变化时阈值会自动调整
|
|
1554
1560
|
• 重复参数会使用最后出现的值并提醒
|
|
1555
1561
|
• 群号检查可在插件配置中开关
|
|
1556
1562
|
|
|
1557
|
-
|
|
1563
|
+
提醒消息变量:
|
|
1558
1564
|
{user} - 用户名
|
|
1559
1565
|
{id} - 用户ID
|
|
1560
1566
|
{group} - 群号
|
|
@@ -1563,9 +1569,9 @@ ${reminderMessage}
|
|
|
1563
1569
|
{answer} - 答对数量/比例
|
|
1564
1570
|
{threshold} - 阈值要求
|
|
1565
1571
|
|
|
1566
|
-
|
|
1572
|
+
使用示例:
|
|
1567
1573
|
gv.cfg "关键词1,关键词2" -m 1 -t 2
|
|
1568
|
-
gv.cfg -msg "用户 {user}\\n
|
|
1574
|
+
gv.cfg -msg "用户 {user}\\n申请理由: {question}"
|
|
1569
1575
|
gv.cfg -m 2 -t 80
|
|
1570
1576
|
gv.cfg -nomsg
|
|
1571
1577
|
|
|
@@ -1575,21 +1581,21 @@ ${reminderMessage}
|
|
|
1575
1581
|
l [group] 查询群黑名单;传入 all 时查看全局黑名单
|
|
1576
1582
|
i id [group] 查询账号黑名单;不带参数时查询本群与全局,
|
|
1577
1583
|
指定群号查询该群与全局,传入 all 则列出所有群及全局(需Koishi 3级)
|
|
1578
|
-
|
|
1584
|
+
使用示例:
|
|
1579
1585
|
gvb a 12345 作弊记录
|
|
1580
1586
|
gvb r 12345 67890
|
|
1581
1587
|
gvb l
|
|
1582
1588
|
gvb l all
|
|
1583
1589
|
gvb i 12345
|
|
1584
1590
|
|
|
1585
|
-
|
|
1591
|
+
快捷命令:
|
|
1586
1592
|
gvc - 配置命令快捷方式
|
|
1587
1593
|
gva - 同意申请快捷命令
|
|
1588
1594
|
gvr - 拒绝申请快捷命令
|
|
1589
1595
|
gvp - 查看待审核列表快捷命令
|
|
1590
1596
|
gvs - 查看统计信息快捷命令
|
|
1591
1597
|
|
|
1592
|
-
|
|
1598
|
+
权限说明:
|
|
1593
1599
|
- 群主/管理员权限
|
|
1594
1600
|
- koishi三级以上权限
|
|
1595
1601
|
- 私聊时必须指定群号(-i参数)`;
|
|
@@ -1649,7 +1655,7 @@ ${reminderMessage}
|
|
|
1649
1655
|
}
|
|
1650
1656
|
const stat = stats[0];
|
|
1651
1657
|
const lastUpdated = new Date(stat.lastUpdated).toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" });
|
|
1652
|
-
return `群 ${groupId}
|
|
1658
|
+
return `群 ${groupId} 验证统计:
|
|
1653
1659
|
自动批准: ${stat.autoApproved}
|
|
1654
1660
|
手动批准: ${stat.manuallyApproved}
|
|
1655
1661
|
拒绝: ${stat.rejected}
|
|
@@ -1663,7 +1669,7 @@ ${reminderMessage}
|
|
|
1663
1669
|
}
|
|
1664
1670
|
const stat = stats[0];
|
|
1665
1671
|
const lastUpdated = new Date(stat.lastUpdated).toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" });
|
|
1666
|
-
return
|
|
1672
|
+
return `总计验证统计:
|
|
1667
1673
|
自动批准: ${stat.autoApproved}
|
|
1668
1674
|
手动批准: ${stat.manuallyApproved}
|
|
1669
1675
|
拒绝: ${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.34",
|
|
12
12
|
"main": "lib/index.js",
|
|
13
13
|
"typings": "lib/index.d.ts",
|
|
14
14
|
"files": [
|
package/src/index.ts
CHANGED
|
@@ -21,7 +21,7 @@ export interface GroupVerificationConfig {
|
|
|
21
21
|
groupId: string
|
|
22
22
|
keywords: string[]
|
|
23
23
|
reviewMethod: 0 | 1 | 2 | 3 // 0:全部同意, 1:按数量同意, 2:按比例同意, 3:全部拒绝
|
|
24
|
-
reviewParameters: number //
|
|
24
|
+
reviewParameters: number // 直接存储数字: 0表示无阈值,其他表示具体阈值
|
|
25
25
|
reminderEnabled: boolean // 是否启用提醒消息
|
|
26
26
|
reminderMessage: string
|
|
27
27
|
createdBy: string
|
|
@@ -37,12 +37,12 @@ export interface GroupVerificationStats {
|
|
|
37
37
|
autoApproved: number
|
|
38
38
|
manuallyApproved: number
|
|
39
39
|
rejected: number
|
|
40
|
-
//
|
|
40
|
+
// 新增: 总入群人数(不论方式,只要检测到成员加入则增加)
|
|
41
41
|
totalJoined: number
|
|
42
42
|
lastUpdated: string | Date
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
//
|
|
45
|
+
// 黑名单行: 每个群/全局对应一条记录,entries 保存 userId->reason 对象
|
|
46
46
|
export interface GroupBlacklistEntry {
|
|
47
47
|
id: number
|
|
48
48
|
groupId: string // 群号或 "all" 表示全局
|
|
@@ -81,12 +81,12 @@ export interface Config {
|
|
|
81
81
|
export const Config: Schema<Config> = Schema.object({
|
|
82
82
|
defaultReminderMessage: Schema.string()
|
|
83
83
|
.description('默认提醒消息模板(使用 \\n 表示换行,可包含下方变量)')
|
|
84
|
-
.default('{user}({id}) 申请加入群 {gname}({group})\n
|
|
84
|
+
.default('{user}({id}) 申请加入群 {gname}({group})\n申请理由: {question}\n匹配情况: {answer}/{threshold}\n使用 gva 同意或 gvr 拒绝申请'),
|
|
85
85
|
enableStrictGroupCheck: Schema.boolean().description('是否启用严格的群号检查(检查群号长度)').default(false),
|
|
86
86
|
logLevel: Schema.union(['debug', 'info', 'warn', 'error']).description('日志级别').default('info'),
|
|
87
|
-
permissionDeniedMessage: Schema.string().description('权限不足时返回给调用者的提示').default('
|
|
87
|
+
permissionDeniedMessage: Schema.string().description('权限不足时返回给调用者的提示').default('权限不足: 需要群主/管理员权限或koishi三级以上权限'),
|
|
88
88
|
invalidGroupMessage: Schema.string().description('无效群号或机器人未在该群时的提示').default('群号 {group} 格式不合法或机器人不在该群中'),
|
|
89
|
-
parameterConflictMessage: Schema.string().description('参数冲突时提示').default('
|
|
89
|
+
parameterConflictMessage: Schema.string().description('参数冲突时提示').default('参数冲突: -? 或 -r 不能与其他参数或关键词一起使用(仅可搭配 -i)'),
|
|
90
90
|
noKeywordsMessage: Schema.string().description('未提供关键词且无法从现有配置继承时的提示').default('请先提供关键词创建配置,或使用 -? 查询配置,-r 删除配置'),
|
|
91
91
|
blacklistAddSuccess: Schema.string().description('将用户加入黑名单后的提示,可使用 {user},{group},{reason}').default('已将用户 {user} 加入群 {group} 黑名单{reason}'),
|
|
92
92
|
blacklistRemoveSuccess: Schema.string().description('从黑名单移除用户后的提示,可使用 {user},{group}').default('已从群 {group} 的黑名单中移除用户 {user}'),
|
|
@@ -104,7 +104,7 @@ export const inject = ['database']
|
|
|
104
104
|
*/
|
|
105
105
|
export interface TokenizeResult {
|
|
106
106
|
tokens: string[];
|
|
107
|
-
seps: string[]; //
|
|
107
|
+
seps: string[]; // 每个令牌前面的分隔符: ' ' 或 ',' 或 ''(表示开始)
|
|
108
108
|
error?: string;
|
|
109
109
|
}
|
|
110
110
|
|
|
@@ -224,7 +224,7 @@ export interface ParsedArgs {
|
|
|
224
224
|
}
|
|
225
225
|
|
|
226
226
|
/**
|
|
227
|
-
*
|
|
227
|
+
* 验证关键词格式: 仅允许用逗号和引号分隔,禁止纯空格分隔
|
|
228
228
|
*/
|
|
229
229
|
// 解析过程中使用的辅助函数
|
|
230
230
|
function validateKeywordFormat(raw: string): boolean {
|
|
@@ -251,25 +251,31 @@ function validateKeywordFormat(raw: string): boolean {
|
|
|
251
251
|
* 对于 -nomsg 不会清除已保存的 message,便于后续再次启用时恢复。
|
|
252
252
|
*/
|
|
253
253
|
export function usageString(): string {
|
|
254
|
-
return
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
254
|
+
return `用法:
|
|
255
|
+
-i 群号
|
|
256
|
+
-m (0|1|2|3)审核方式
|
|
257
|
+
-t 阈值
|
|
258
|
+
-msg [可选,消息内容] 并打开提醒消息
|
|
259
|
+
-nomsg 取消提醒消息(与-msg冲突)
|
|
260
|
+
-r 删除配置
|
|
261
|
+
-? 查询配置
|
|
262
|
+
|
|
263
|
+
对于存在空格的关键词/提醒消息用双引号""包裹
|
|
264
|
+
|
|
265
|
+
# 例
|
|
266
|
+
gvc 关键词1,关键词2 -m 1 -t 2 # 创建配置
|
|
267
|
+
gvc -msg "消息内容" # 修改提醒消息
|
|
261
268
|
gvc -nomsg # 禁用提醒消息
|
|
262
|
-
# 查询/删除
|
|
263
269
|
gvc -? # 查询配置
|
|
264
270
|
gvc -r # 删除配置
|
|
265
271
|
|
|
266
|
-
审核方式说明(使用 -m
|
|
272
|
+
审核方式说明(使用 -m 参数):
|
|
267
273
|
0 全部同意(默认)
|
|
268
|
-
1 按数量同意,需要 -t 指定数量
|
|
274
|
+
1 按数量同意,需要 -t 指定数量
|
|
269
275
|
2 按比例同意,需要 -t 指定百分比
|
|
270
|
-
3
|
|
276
|
+
3 全部拒绝
|
|
271
277
|
|
|
272
|
-
|
|
278
|
+
提醒消息可用变量: {user} 用户名 {id} 用户ID
|
|
273
279
|
{group} 群号 {gname} 群名称
|
|
274
280
|
{question} 申请理由 {answer} 匹配情况 {threshold} 阈值
|
|
275
281
|
使用 \\n 换行`;
|
|
@@ -290,7 +296,7 @@ export function mergeReminder(
|
|
|
290
296
|
) {
|
|
291
297
|
let reminderEnabled = true;
|
|
292
298
|
// 优先使用传入的默认模板,其次使用已有配置,再 fallback 到老写死的样式
|
|
293
|
-
let reminderMessage = defaultMessage || '{user}({id}) 申请加入群 {gname}({group})\n
|
|
299
|
+
let reminderMessage = defaultMessage || '{user}({id}) 申请加入群 {gname}({group})\n申请理由: {question}\n匹配情况: {answer}/{threshold}';
|
|
294
300
|
|
|
295
301
|
if (existingConfig) {
|
|
296
302
|
reminderEnabled = existingConfig.reminderEnabled;
|
|
@@ -298,7 +304,7 @@ export function mergeReminder(
|
|
|
298
304
|
reminderMessage = existingConfig.reminderMessage || reminderMessage;
|
|
299
305
|
}
|
|
300
306
|
|
|
301
|
-
//
|
|
307
|
+
// 优先级: disable > bare enable > new message content
|
|
302
308
|
if (hasRealDisableMessageParam) {
|
|
303
309
|
reminderEnabled = false;
|
|
304
310
|
logger.debug('禁用提醒消息功能 (保留现有内容)');
|
|
@@ -321,7 +327,7 @@ export function mergeReminder(
|
|
|
321
327
|
* 返回关键词数组和各类 flag 的值,未出现的 flag 保持 undefined。
|
|
322
328
|
* 若检测到格式错误(如纯空格分隔关键词),返回 error 字段。
|
|
323
329
|
*/
|
|
324
|
-
//
|
|
330
|
+
// 全局缓存: 记录通过机器人自动批准的用户,供 guild-member-added 事件使用
|
|
325
331
|
const autoQueue = new Map<string, Set<string>>();
|
|
326
332
|
|
|
327
333
|
// 更新统计信息函数,提取到模块层供多个位置调用
|
|
@@ -381,7 +387,7 @@ export async function updateStats(ctx: Context, groupId: string, action: 'autoAp
|
|
|
381
387
|
await syncTotalStats(ctx)
|
|
382
388
|
}
|
|
383
389
|
|
|
384
|
-
//
|
|
390
|
+
// 提取成独立函数: 增加总入群计数,供事件统一调用
|
|
385
391
|
export async function incrementTotal(ctx: Context, groupId: string) {
|
|
386
392
|
const existingStats = await ctx.database.get('group_verification_stats', { groupId })
|
|
387
393
|
if (existingStats.length > 0) {
|
|
@@ -526,7 +532,7 @@ export async function checkPermission(session: any, targetGroupId?: string): Pro
|
|
|
526
532
|
return [false, '权限不足']
|
|
527
533
|
}
|
|
528
534
|
|
|
529
|
-
//
|
|
535
|
+
// 提供给测试的辅助函数: 处理 guild-member-request 事件的逻辑
|
|
530
536
|
export async function handleGuildMemberRequestEvent(ctx: Context, session: any) {
|
|
531
537
|
logger.debug('guild-member-request event', session)
|
|
532
538
|
let guildId = (session.guildId || session.channelId || '').toString().trim();
|
|
@@ -687,7 +693,7 @@ export function parseConfigArgs(raw: string): ParsedArgs {
|
|
|
687
693
|
// remove everything from the first flag onward, including when flag sits at start
|
|
688
694
|
const keywordSection = raw.split(/(?:^|\s+)-(?:i|m|t|msg|nomsg|\?|r)\b/)[0].trim();
|
|
689
695
|
if (keywordSection && !validateKeywordFormat(keywordSection)) {
|
|
690
|
-
error = '
|
|
696
|
+
error = '关键词应使用逗号分隔或引号框起(如: k1,k2,k3 或 "k1","k2" 或 "k1,k2",k3)';
|
|
691
697
|
}
|
|
692
698
|
|
|
693
699
|
return { keywords, flags, error };
|
|
@@ -869,7 +875,7 @@ export async function processBlacklistCommand(ctx: Context, session: any, rawArg
|
|
|
869
875
|
if (!op || !['a','r','l','i'].includes(op)) {
|
|
870
876
|
// present a multiline Chinese usage guide without angle brackets
|
|
871
877
|
return [
|
|
872
|
-
'
|
|
878
|
+
'用法: ',
|
|
873
879
|
' gvb a id [reason] [group] 将用户加入黑名单',
|
|
874
880
|
' gvb r id [group] 将用户移出黑名单',
|
|
875
881
|
' gvb l [group] 查询群黑名单',
|
|
@@ -887,7 +893,7 @@ export async function processBlacklistCommand(ctx: Context, session: any, rawArg
|
|
|
887
893
|
targetUser = parts[1]
|
|
888
894
|
if (!targetUser) return '请提供用户ID'
|
|
889
895
|
// handle optional reason and group at end
|
|
890
|
-
//
|
|
896
|
+
// 规则: 如果只有一个附加参数,则作为 reason;两个及以上时最后一个为群号,其余拼成 reason
|
|
891
897
|
const rest = parts.slice(2)
|
|
892
898
|
if (rest.length === 1) {
|
|
893
899
|
reason = rest[0]
|
|
@@ -926,7 +932,7 @@ export async function processBlacklistCommand(ctx: Context, session: any, rawArg
|
|
|
926
932
|
const row = rows[0]
|
|
927
933
|
const entries = row.entries || {}
|
|
928
934
|
if (entries[targetUser] !== undefined) {
|
|
929
|
-
return `用户 ${targetUser} 已在群 ${group}
|
|
935
|
+
return `用户 ${targetUser} 已在群 ${group} 的黑名单中: ${entries[targetUser]}`
|
|
930
936
|
}
|
|
931
937
|
entries[targetUser] = storedReason
|
|
932
938
|
await ctx.database.set('group_verification_blacklist', { id: row.id }, { entries })
|
|
@@ -945,7 +951,7 @@ export async function processBlacklistCommand(ctx: Context, session: any, rawArg
|
|
|
945
951
|
}
|
|
946
952
|
}
|
|
947
953
|
const tmpl = (config && config.blacklistAddSuccess) || '已将用户 {user} 加入群 {group} 黑名单{reason}'
|
|
948
|
-
return tmpl.replace('{user}', targetUser).replace('{group}', group).replace('{reason}', reason ?
|
|
954
|
+
return tmpl.replace('{user}', targetUser).replace('{group}', group).replace('{reason}', reason ? `,原因: ${reason}` : '')
|
|
949
955
|
}
|
|
950
956
|
if (op === 'r') {
|
|
951
957
|
targetUser = parts[1]
|
|
@@ -1004,7 +1010,7 @@ export async function processBlacklistCommand(ctx: Context, session: any, rawArg
|
|
|
1004
1010
|
let prefix = group && group.toLowerCase() === 'all' ? '全局黑名单: \n' : `群 ${group} 黑名单: \n`
|
|
1005
1011
|
let msg = prefix
|
|
1006
1012
|
for (const uid in entries) {
|
|
1007
|
-
msg += `${uid}${entries[uid] ?
|
|
1013
|
+
msg += `${uid}${entries[uid] ? `: ${entries[uid]}` : ''}\n`
|
|
1008
1014
|
}
|
|
1009
1015
|
return msg
|
|
1010
1016
|
}
|
|
@@ -1041,7 +1047,7 @@ export async function processBlacklistCommand(ctx: Context, session: any, rawArg
|
|
|
1041
1047
|
// groupArg provided
|
|
1042
1048
|
if (groupArg.toLowerCase() === 'all') {
|
|
1043
1049
|
const auth = session.author?.authority || session.user?.authority
|
|
1044
|
-
if (!(auth && auth >= 3)) return '
|
|
1050
|
+
if (!(auth && auth >= 3)) return '权限不足: 查看全局/所有群黑名单需要 koishi 3 级以上权限'
|
|
1045
1051
|
const rows = await ctx.database.get('group_verification_blacklist', {})
|
|
1046
1052
|
const globalReason = globalHit ? globalRows[0].entries[targetUser] : '无'
|
|
1047
1053
|
const lines: string[] = []
|
|
@@ -1103,7 +1109,7 @@ export function apply(ctx: Context, config: Config) {
|
|
|
1103
1109
|
if (config.logLevel) logger.level = config.logLevel
|
|
1104
1110
|
|
|
1105
1111
|
// 设置日志级别
|
|
1106
|
-
//
|
|
1112
|
+
// 注意: Koishi logger的level设置可能需要不同的方式
|
|
1107
1113
|
|
|
1108
1114
|
// 记录插件启动信息
|
|
1109
1115
|
logger.info('群组验证插件已启动')
|
|
@@ -1142,7 +1148,7 @@ export function apply(ctx: Context, config: Config) {
|
|
|
1142
1148
|
autoInc: true
|
|
1143
1149
|
})
|
|
1144
1150
|
|
|
1145
|
-
//
|
|
1151
|
+
// 黑名单表: 每条记录对应一个群(或全局),entries 存储 userId->reason 键值对
|
|
1146
1152
|
ctx.model.extend('group_verification_blacklist', {
|
|
1147
1153
|
id: 'unsigned',
|
|
1148
1154
|
groupId: 'string',
|
|
@@ -1282,7 +1288,7 @@ export function apply(ctx: Context, config: Config) {
|
|
|
1282
1288
|
|
|
1283
1289
|
logger.debug(`权限检查 - 权限不足`)
|
|
1284
1290
|
const debugInfo = `调试信息 - 用户ID:${session.userId}, 群号:${groupId}, 权限等级:${koishiAuthority || '未知'}`
|
|
1285
|
-
return [false, (config.permissionDeniedMessage || '
|
|
1291
|
+
return [false, (config.permissionDeniedMessage || '权限不足: 需要群主/管理员权限或koishi三级以上权限') + `\n${debugInfo}`]
|
|
1286
1292
|
}
|
|
1287
1293
|
|
|
1288
1294
|
// Create main command with aliases
|
|
@@ -1305,7 +1311,7 @@ export function apply(ctx: Context, config: Config) {
|
|
|
1305
1311
|
.option('query', '-? 查询当前配置')
|
|
1306
1312
|
.option('remove', '-r 删除配置')
|
|
1307
1313
|
.action(async ({ session, options }: any, keywords: any) => {
|
|
1308
|
-
//
|
|
1314
|
+
// 详细调试: 记录所有输入信息
|
|
1309
1315
|
logger.info(`=== 命令解析调试 ===`)
|
|
1310
1316
|
logger.info(`session内容: guildId=${session.guildId}, userId=${session.userId}`)
|
|
1311
1317
|
|
|
@@ -1330,7 +1336,7 @@ export function apply(ctx: Context, config: Config) {
|
|
|
1330
1336
|
method: flags.method || (options.method === '' ? undefined : options.method),
|
|
1331
1337
|
threshold: flags.threshold || options.threshold,
|
|
1332
1338
|
message: flags.message !== undefined ? flags.message : options.message,
|
|
1333
|
-
enableMessage: flags.enableMessage, //
|
|
1339
|
+
enableMessage: flags.enableMessage, // 新增: -msg 裸调用标记
|
|
1334
1340
|
disableMessage: flags.disableMessage || options.disableMessage,
|
|
1335
1341
|
query: flags.query || options.query,
|
|
1336
1342
|
remove: flags.remove || options.remove,
|
|
@@ -1341,7 +1347,7 @@ export function apply(ctx: Context, config: Config) {
|
|
|
1341
1347
|
if ((cleanedOptions.query || cleanedOptions.remove) &&
|
|
1342
1348
|
(parsedKeywords.length > 0 || cleanedOptions.method !== undefined || cleanedOptions.threshold !== undefined ||
|
|
1343
1349
|
cleanedOptions.message !== undefined || cleanedOptions.enableMessage || cleanedOptions.disableMessage)) {
|
|
1344
|
-
return config.parameterConflictMessage || '
|
|
1350
|
+
return config.parameterConflictMessage || '参数冲突: -? 或 -r 不能与其他参数或关键词一起使用(仅可搭配 -i)'
|
|
1345
1351
|
}
|
|
1346
1352
|
|
|
1347
1353
|
// 检查消息参数冲突
|
|
@@ -1378,7 +1384,7 @@ export function apply(ctx: Context, config: Config) {
|
|
|
1378
1384
|
|
|
1379
1385
|
// 参数冲突检查
|
|
1380
1386
|
if ((hasRealMessageParam || hasRealEnableMessageParam) && hasRealDisableMessageParam) {
|
|
1381
|
-
return '
|
|
1387
|
+
return '参数冲突: 不能同时使用 -msg 和 -nomsg'
|
|
1382
1388
|
}
|
|
1383
1389
|
|
|
1384
1390
|
// 重复参数检测
|
|
@@ -1451,7 +1457,7 @@ export function apply(ctx: Context, config: Config) {
|
|
|
1451
1457
|
const updateTime = new Date(config.updatedAt).toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' })
|
|
1452
1458
|
const reminderStatus = config.reminderEnabled ? '启用' : '禁用'
|
|
1453
1459
|
|
|
1454
|
-
return `群 ${targetGroupId}
|
|
1460
|
+
return `群 ${targetGroupId} 配置:
|
|
1455
1461
|
关键词: ${decodedKeywords.join(', ')}
|
|
1456
1462
|
审核方式: ${methodDesc}
|
|
1457
1463
|
阈值: ${thresholdInfo}
|
|
@@ -1491,7 +1497,7 @@ export function apply(ctx: Context, config: Config) {
|
|
|
1491
1497
|
existingConfig = existingConfigs[0]
|
|
1492
1498
|
}
|
|
1493
1499
|
|
|
1494
|
-
//
|
|
1500
|
+
// 处理关键词: 如果没有关键词但有其他参数,从现有配置中获取
|
|
1495
1501
|
if (keywordList.length === 0 && !cleanedOptions.query && !cleanedOptions.remove) {
|
|
1496
1502
|
if (!existingConfig) {
|
|
1497
1503
|
return config.noKeywordsMessage || '请先提供关键词创建配置,或使用 -? 查询配置,-r 删除配置'
|
|
@@ -1512,13 +1518,13 @@ export function apply(ctx: Context, config: Config) {
|
|
|
1512
1518
|
|
|
1513
1519
|
// 解析审核方式和阈值(基于新的数据库模型设计)
|
|
1514
1520
|
let reviewMethod: 0 | 1 | 2 | 3 = 0 // 默认值
|
|
1515
|
-
let reviewParameters: number = 0 //
|
|
1521
|
+
let reviewParameters: number = 0 // 直接存储数字: 0表示无阈值
|
|
1516
1522
|
|
|
1517
1523
|
if (existingConfig) {
|
|
1518
1524
|
// 默认使用现有配置的参数
|
|
1519
1525
|
reviewMethod = existingConfig.reviewMethod
|
|
1520
1526
|
|
|
1521
|
-
//
|
|
1527
|
+
// 老版本兼容性处理: 处理可能的NaN或无效值
|
|
1522
1528
|
if (existingConfig.reviewParameters === undefined ||
|
|
1523
1529
|
existingConfig.reviewParameters === null ||
|
|
1524
1530
|
isNaN(existingConfig.reviewParameters)) {
|
|
@@ -1533,7 +1539,7 @@ export function apply(ctx: Context, config: Config) {
|
|
|
1533
1539
|
if (cleanedOptions.method !== undefined && cleanedOptions.method !== '') {
|
|
1534
1540
|
const methodNum = parseInt(cleanedOptions.method)
|
|
1535
1541
|
if (isNaN(methodNum) || methodNum < 0 || methodNum > 3) {
|
|
1536
|
-
return '
|
|
1542
|
+
return '审核方式参数错误: 0-全部同意, 1-按数量同意, 2-按比例同意, 3-全部拒绝'
|
|
1537
1543
|
}
|
|
1538
1544
|
const oldMethod = reviewMethod
|
|
1539
1545
|
reviewMethod = methodNum as 0 | 1 | 2 | 3
|
|
@@ -1873,16 +1879,16 @@ export function apply(ctx: Context, config: Config) {
|
|
|
1873
1879
|
'gvs'
|
|
1874
1880
|
)
|
|
1875
1881
|
.action(async ({ session }: any, target: any) => {
|
|
1876
|
-
//
|
|
1882
|
+
// 参数验证: 只能是群号、all、total或空
|
|
1877
1883
|
const validTargets = ['all', 'total']
|
|
1878
1884
|
const isGroupId = target && /^\d+$/.test(target)
|
|
1879
1885
|
const isSpecialTarget = target && validTargets.includes(target.toLowerCase())
|
|
1880
1886
|
|
|
1881
1887
|
if (target && !isGroupId && !isSpecialTarget) {
|
|
1882
|
-
return '
|
|
1888
|
+
return '参数错误: 只能指定群号、all、total或留空'
|
|
1883
1889
|
}
|
|
1884
1890
|
|
|
1885
|
-
//
|
|
1891
|
+
// 权限检查: 总计统计需要3级以上权限
|
|
1886
1892
|
if (target?.toLowerCase() === 'total' || target?.toLowerCase() === 'all') {
|
|
1887
1893
|
// 检查是否为koishi 3级以上权限
|
|
1888
1894
|
const koishiAuthority = (session as any).author?.authority || (session as any).user?.authority
|
|
@@ -1895,7 +1901,7 @@ export function apply(ctx: Context, config: Config) {
|
|
|
1895
1901
|
|
|
1896
1902
|
// 处理不同参数情况
|
|
1897
1903
|
if (!target) {
|
|
1898
|
-
//
|
|
1904
|
+
// 无参数: 显示当前群统计
|
|
1899
1905
|
if (!groupId) {
|
|
1900
1906
|
return '请在群聊中使用此命令或指定群号'
|
|
1901
1907
|
}
|
|
@@ -1936,11 +1942,11 @@ export function apply(ctx: Context, config: Config) {
|
|
|
1936
1942
|
return '当前没有待审核的加群申请'
|
|
1937
1943
|
}
|
|
1938
1944
|
|
|
1939
|
-
let result = '
|
|
1945
|
+
let result = '待审核申请列表: \n'
|
|
1940
1946
|
pendingApplications.forEach((app, index) => {
|
|
1941
1947
|
result += `${index + 1}. ${app.userName}(${app.userId})
|
|
1942
|
-
|
|
1943
|
-
|
|
1948
|
+
申请时间: ${app.applyTime.toLocaleString()}
|
|
1949
|
+
申请理由: ${app.requestMessage}
|
|
1944
1950
|
|
|
1945
1951
|
`
|
|
1946
1952
|
})
|
|
@@ -1965,19 +1971,19 @@ export function apply(ctx: Context, config: Config) {
|
|
|
1965
1971
|
.subcommand('.help', '显示帮助信息')
|
|
1966
1972
|
.alias('gv.帮助', 'gverify.帮助', 'group-verify.帮助', '帮助', 'hlp', '帮助信息')
|
|
1967
1973
|
.action(() => {
|
|
1968
|
-
return
|
|
1969
|
-
|
|
1974
|
+
return `群组验证命令帮助:
|
|
1975
|
+
主指令别名: gv, gverify
|
|
1970
1976
|
|
|
1971
1977
|
配置命令 (.config/.cfg):
|
|
1972
|
-
|
|
1973
|
-
1.
|
|
1974
|
-
2.
|
|
1975
|
-
3.
|
|
1976
|
-
4.
|
|
1977
|
-
5.
|
|
1978
|
-
6.
|
|
1978
|
+
用法:
|
|
1979
|
+
1. 创建新配置: gv.cfg 关键词1,关键词2 -m 1 -t 2
|
|
1980
|
+
2. 修改现有配置: gv.cfg -m 1 -t 2 (不提供关键词)
|
|
1981
|
+
3. 启用提醒消息: gv.cfg -msg "消息内容"
|
|
1982
|
+
4. 禁用提醒消息: gv.cfg -nomsg
|
|
1983
|
+
5. 查询配置: gv.cfg -?
|
|
1984
|
+
6. 删除配置: gv.cfg -r
|
|
1979
1985
|
|
|
1980
|
-
|
|
1986
|
+
参数说明:
|
|
1981
1987
|
-i <群号> 指定群号(私聊时必需)
|
|
1982
1988
|
-m <方式> 审核方式 (0=全部同意, 1=按数量, 2=按比例, 3=全部拒绝)
|
|
1983
1989
|
-t <阈值> 阈值参数(方式1:0-关键词数, 方式2:0-100)
|
|
@@ -1989,19 +1995,19 @@ export function apply(ctx: Context, config: Config) {
|
|
|
1989
1995
|
-? 查询当前配置
|
|
1990
1996
|
-r 删除配置
|
|
1991
1997
|
|
|
1992
|
-
|
|
1993
|
-
•
|
|
1994
|
-
•
|
|
1995
|
-
•
|
|
1996
|
-
•
|
|
1998
|
+
引号使用规则:
|
|
1999
|
+
• 关键词包含空格: gv.cfg "关键词1,关键词 2,关键词3"
|
|
2000
|
+
• 提醒消息包含空格: gv.cfg -msg "这是包含空格的消息"
|
|
2001
|
+
• 内部引号转义: gv.cfg -msg "包含\\"引号\\"的内容"
|
|
2002
|
+
• 换行符: gv.cfg -msg "第一行\\n第二行"
|
|
1997
2003
|
|
|
1998
|
-
|
|
2004
|
+
特殊说明:
|
|
1999
2005
|
• 阈值可设为0表示全部同意
|
|
2000
2006
|
• 关键词数量变化时阈值会自动调整
|
|
2001
2007
|
• 重复参数会使用最后出现的值并提醒
|
|
2002
2008
|
• 群号检查可在插件配置中开关
|
|
2003
2009
|
|
|
2004
|
-
|
|
2010
|
+
提醒消息变量:
|
|
2005
2011
|
{user} - 用户名
|
|
2006
2012
|
{id} - 用户ID
|
|
2007
2013
|
{group} - 群号
|
|
@@ -2010,9 +2016,9 @@ export function apply(ctx: Context, config: Config) {
|
|
|
2010
2016
|
{answer} - 答对数量/比例
|
|
2011
2017
|
{threshold} - 阈值要求
|
|
2012
2018
|
|
|
2013
|
-
|
|
2019
|
+
使用示例:
|
|
2014
2020
|
gv.cfg "关键词1,关键词2" -m 1 -t 2
|
|
2015
|
-
gv.cfg -msg "用户 {user}\\n
|
|
2021
|
+
gv.cfg -msg "用户 {user}\\n申请理由: {question}"
|
|
2016
2022
|
gv.cfg -m 2 -t 80
|
|
2017
2023
|
gv.cfg -nomsg
|
|
2018
2024
|
|
|
@@ -2022,21 +2028,21 @@ export function apply(ctx: Context, config: Config) {
|
|
|
2022
2028
|
l [group] 查询群黑名单;传入 all 时查看全局黑名单
|
|
2023
2029
|
i id [group] 查询账号黑名单;不带参数时查询本群与全局,
|
|
2024
2030
|
指定群号查询该群与全局,传入 all 则列出所有群及全局(需Koishi 3级)
|
|
2025
|
-
|
|
2031
|
+
使用示例:
|
|
2026
2032
|
gvb a 12345 作弊记录
|
|
2027
2033
|
gvb r 12345 67890
|
|
2028
2034
|
gvb l
|
|
2029
2035
|
gvb l all
|
|
2030
2036
|
gvb i 12345
|
|
2031
2037
|
|
|
2032
|
-
|
|
2038
|
+
快捷命令:
|
|
2033
2039
|
gvc - 配置命令快捷方式
|
|
2034
2040
|
gva - 同意申请快捷命令
|
|
2035
2041
|
gvr - 拒绝申请快捷命令
|
|
2036
2042
|
gvp - 查看待审核列表快捷命令
|
|
2037
2043
|
gvs - 查看统计信息快捷命令
|
|
2038
2044
|
|
|
2039
|
-
|
|
2045
|
+
权限说明:
|
|
2040
2046
|
- 群主/管理员权限
|
|
2041
2047
|
- koishi三级以上权限
|
|
2042
2048
|
- 私聊时必须指定群号(-i参数)`
|
|
@@ -2044,7 +2050,7 @@ export function apply(ctx: Context, config: Config) {
|
|
|
2044
2050
|
|
|
2045
2051
|
// 插件初始化时确保总计统计行存在
|
|
2046
2052
|
ctx.on('ready', async () => {
|
|
2047
|
-
//
|
|
2053
|
+
// 迁移: 将旧版保留的 Date 对象字段转换为 ISO 字符串,以避免绑定错误
|
|
2048
2054
|
try {
|
|
2049
2055
|
const configs = await ctx.database.get('group_verification_config', {})
|
|
2050
2056
|
for (const cfg of configs) {
|
|
@@ -2101,7 +2107,7 @@ export function apply(ctx: Context, config: Config) {
|
|
|
2101
2107
|
await syncTotalStats(ctx)
|
|
2102
2108
|
})
|
|
2103
2109
|
|
|
2104
|
-
//
|
|
2110
|
+
// 辅助函数: 获取群组统计
|
|
2105
2111
|
async function getGroupStats(groupId: string): Promise<string> {
|
|
2106
2112
|
const stats = await ctx.database.get('group_verification_stats', { groupId })
|
|
2107
2113
|
|
|
@@ -2112,14 +2118,14 @@ export function apply(ctx: Context, config: Config) {
|
|
|
2112
2118
|
const stat = stats[0]
|
|
2113
2119
|
const lastUpdated = new Date(stat.lastUpdated).toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' })
|
|
2114
2120
|
|
|
2115
|
-
return `群 ${groupId}
|
|
2121
|
+
return `群 ${groupId} 验证统计:
|
|
2116
2122
|
自动批准: ${stat.autoApproved}
|
|
2117
2123
|
手动批准: ${stat.manuallyApproved}
|
|
2118
2124
|
拒绝: ${stat.rejected}
|
|
2119
2125
|
最后更新: ${lastUpdated}`
|
|
2120
2126
|
}
|
|
2121
2127
|
|
|
2122
|
-
//
|
|
2128
|
+
// 辅助函数: 获取总计统计
|
|
2123
2129
|
async function getTotalStats(): Promise<string> {
|
|
2124
2130
|
const stats = await ctx.database.get('group_verification_stats', { groupId: 'TOTAL' })
|
|
2125
2131
|
|
|
@@ -2130,7 +2136,7 @@ export function apply(ctx: Context, config: Config) {
|
|
|
2130
2136
|
const stat = stats[0]
|
|
2131
2137
|
const lastUpdated = new Date(stat.lastUpdated).toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' })
|
|
2132
2138
|
|
|
2133
|
-
return
|
|
2139
|
+
return `总计验证统计:
|
|
2134
2140
|
自动批准: ${stat.autoApproved}
|
|
2135
2141
|
手动批准: ${stat.manuallyApproved}
|
|
2136
2142
|
拒绝: ${stat.rejected}
|