koishi-plugin-group-verification 1.0.2 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/index.d.ts +2 -0
- package/lib/index.js +128 -60
- package/package.json +4 -4
- package/src/index.ts +179 -66
package/lib/index.d.ts
CHANGED
package/lib/index.js
CHANGED
|
@@ -36,6 +36,8 @@ function apply(ctx, config) {
|
|
|
36
36
|
reviewMethod: "integer",
|
|
37
37
|
reviewParameters: "json",
|
|
38
38
|
reminderMessage: "string",
|
|
39
|
+
createdBy: "string",
|
|
40
|
+
updatedBy: "string",
|
|
39
41
|
createdAt: "date",
|
|
40
42
|
updatedAt: "date"
|
|
41
43
|
}, {
|
|
@@ -75,7 +77,7 @@ function apply(ctx, config) {
|
|
|
75
77
|
return;
|
|
76
78
|
}
|
|
77
79
|
const config2 = groupConfig[0];
|
|
78
|
-
const isValid = await verifyApplication(config2, message);
|
|
80
|
+
const { isValid, matchedCount, requiredThreshold } = await verifyApplication(config2, message, session);
|
|
79
81
|
if (isValid) {
|
|
80
82
|
await updateStats(guildId, "autoApproved");
|
|
81
83
|
} else {
|
|
@@ -87,6 +89,13 @@ function apply(ctx, config) {
|
|
|
87
89
|
const userId = session.userId;
|
|
88
90
|
const username = session.username || "未知用户";
|
|
89
91
|
const message = session.content || "";
|
|
92
|
+
let groupName = "未知群组";
|
|
93
|
+
try {
|
|
94
|
+
const guild = await session.bot.getGuild(guildId);
|
|
95
|
+
groupName = guild.name || groupName;
|
|
96
|
+
} catch (error) {
|
|
97
|
+
}
|
|
98
|
+
const { matchedCount, requiredThreshold } = await verifyApplication(config2, message, session);
|
|
90
99
|
await ctx2.database.create("group_verification_pending", {
|
|
91
100
|
groupId: guildId,
|
|
92
101
|
userId,
|
|
@@ -94,30 +103,41 @@ function apply(ctx, config) {
|
|
|
94
103
|
requestMessage: message,
|
|
95
104
|
applyTime: /* @__PURE__ */ new Date()
|
|
96
105
|
});
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
请管理员使用 #同意 或 #拒绝 来处理此申请`;
|
|
106
|
+
let reminderMsg = config2.reminderMessage;
|
|
107
|
+
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);
|
|
100
108
|
await ctx2.broadcast([guildId], reminderMsg);
|
|
101
109
|
}
|
|
102
110
|
__name(handleFailedVerification, "handleFailedVerification");
|
|
103
|
-
async function verifyApplication(config2, message) {
|
|
111
|
+
async function verifyApplication(config2, message, session) {
|
|
104
112
|
const matchedCount = config2.keywords.filter(
|
|
105
113
|
(keyword) => message.toLowerCase().includes(keyword.toLowerCase())
|
|
106
114
|
).length;
|
|
115
|
+
let isValid = false;
|
|
116
|
+
let requiredThreshold = "";
|
|
107
117
|
switch (config2.reviewMethod) {
|
|
108
118
|
case 0:
|
|
109
|
-
|
|
119
|
+
isValid = true;
|
|
120
|
+
requiredThreshold = "null";
|
|
121
|
+
break;
|
|
110
122
|
case 1:
|
|
111
|
-
|
|
123
|
+
isValid = matchedCount >= (config2.reviewParameters.threshold || 1);
|
|
124
|
+
requiredThreshold = `${config2.reviewParameters.threshold || 1}`;
|
|
125
|
+
break;
|
|
112
126
|
case 2:
|
|
113
127
|
const ratio = matchedCount / config2.keywords.length;
|
|
114
128
|
const requiredRatio = (config2.reviewParameters.threshold || 100) / 100;
|
|
115
|
-
|
|
129
|
+
isValid = ratio >= requiredRatio;
|
|
130
|
+
requiredThreshold = `${config2.reviewParameters.threshold || 100}%`;
|
|
131
|
+
break;
|
|
116
132
|
case 3:
|
|
117
|
-
|
|
133
|
+
isValid = false;
|
|
134
|
+
requiredThreshold = "null";
|
|
135
|
+
break;
|
|
118
136
|
default:
|
|
119
|
-
|
|
137
|
+
isValid = false;
|
|
138
|
+
requiredThreshold = "null";
|
|
120
139
|
}
|
|
140
|
+
return { isValid, matchedCount, requiredThreshold };
|
|
121
141
|
}
|
|
122
142
|
__name(verifyApplication, "verifyApplication");
|
|
123
143
|
async function updateStats(groupId, statType) {
|
|
@@ -157,29 +177,42 @@ function apply(ctx, config) {
|
|
|
157
177
|
__name(updateStats, "updateStats");
|
|
158
178
|
async function checkPermission(session, targetGroupId) {
|
|
159
179
|
const groupId = targetGroupId || session.guildId;
|
|
160
|
-
if (groupId) {
|
|
161
|
-
|
|
162
|
-
const member = await session.bot.getGuildMember(groupId, session.userId);
|
|
163
|
-
if (member && (member.roles?.includes("admin") || member.permissions?.includes("ADMINISTRATOR"))) {
|
|
164
|
-
return true;
|
|
165
|
-
}
|
|
166
|
-
} catch (error) {
|
|
167
|
-
}
|
|
180
|
+
if (!groupId) {
|
|
181
|
+
return [false, "请在群聊中使用此命令或使用 -i 参数指定群号"];
|
|
168
182
|
}
|
|
169
183
|
if (session.author?.authority && session.author.authority >= 3) {
|
|
170
|
-
return true;
|
|
184
|
+
return [true];
|
|
185
|
+
}
|
|
186
|
+
try {
|
|
187
|
+
const member = await session.bot.getGuildMember(groupId, session.userId);
|
|
188
|
+
if (member) {
|
|
189
|
+
if (member.permissions?.includes("OWNER")) {
|
|
190
|
+
return [true];
|
|
191
|
+
}
|
|
192
|
+
if (member.roles?.includes("admin") || member.permissions?.includes("ADMINISTRATOR")) {
|
|
193
|
+
return [true];
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
} catch (error) {
|
|
197
|
+
return [false, `无法获取群 ${groupId} 的成员信息,请确认机器人已在该群中`];
|
|
171
198
|
}
|
|
172
|
-
return false;
|
|
199
|
+
return [false, "权限不足:需要群主/管理员权限或koishi三级以上权限"];
|
|
173
200
|
}
|
|
174
201
|
__name(checkPermission, "checkPermission");
|
|
175
|
-
const groupVerify = ctx.command("group-verify", "群组验证管理命令");
|
|
176
|
-
groupVerify.subcommand(".config [keywords:text]", "配置群组验证规则").alias("cfg", "配置", "conf", "设置").option("groupId", "-i <groupId> 指定群号").option("method", "-m <method> 审核方式 (0-3)").option("threshold", "-t <threshold> 阈值参数").option("query", "-? 查询当前配置").option("remove", "-r 删除配置").action(async ({ session, options }, keywords) => {
|
|
202
|
+
const groupVerify = ctx.command("group-verify", "群组验证管理命令").alias("gv", "gverify");
|
|
203
|
+
groupVerify.subcommand(".config [keywords:text]", "配置群组验证规则").alias("cfg", "配置", "conf", "设置").option("groupId", "-i <groupId> 指定群号").option("method", "-m <method> 审核方式 (0-3)").option("threshold", "-t <threshold> 阈值参数").option("message", "-msg <message> 自定义提醒消息").option("query", "-? 查询当前配置").option("remove", "-r 删除配置").action(async ({ session, options }, keywords) => {
|
|
177
204
|
const targetGroupId = options.groupId || session.guildId;
|
|
178
|
-
|
|
179
|
-
|
|
205
|
+
const [hasPermission, errorMsg] = await checkPermission(session, targetGroupId);
|
|
206
|
+
if (!hasPermission) {
|
|
207
|
+
return errorMsg || "权限不足";
|
|
180
208
|
}
|
|
181
|
-
|
|
182
|
-
|
|
209
|
+
let groupName = "未知群组";
|
|
210
|
+
if (targetGroupId) {
|
|
211
|
+
try {
|
|
212
|
+
const guild = await session.bot.getGuild(targetGroupId);
|
|
213
|
+
groupName = guild.name || groupName;
|
|
214
|
+
} catch (error) {
|
|
215
|
+
}
|
|
183
216
|
}
|
|
184
217
|
if (options.remove) {
|
|
185
218
|
const existingConfig2 = await ctx.database.get("group_verification_config", { groupId: targetGroupId });
|
|
@@ -195,34 +228,41 @@ function apply(ctx, config) {
|
|
|
195
228
|
if (existingConfig2.length > 0) {
|
|
196
229
|
const config2 = existingConfig2[0];
|
|
197
230
|
let methodDesc = "";
|
|
231
|
+
let thresholdInfo = "";
|
|
198
232
|
switch (config2.reviewMethod) {
|
|
199
233
|
case 0:
|
|
200
234
|
methodDesc = "全部同意";
|
|
235
|
+
thresholdInfo = "null";
|
|
201
236
|
break;
|
|
202
237
|
case 1:
|
|
203
|
-
methodDesc =
|
|
238
|
+
methodDesc = `按数量同意`;
|
|
239
|
+
thresholdInfo = `${config2.reviewParameters.threshold || 1}`;
|
|
204
240
|
break;
|
|
205
241
|
case 2:
|
|
206
|
-
methodDesc =
|
|
242
|
+
methodDesc = `按比例同意`;
|
|
243
|
+
thresholdInfo = `${config2.reviewParameters.threshold || 100}%`;
|
|
207
244
|
break;
|
|
208
245
|
case 3:
|
|
209
246
|
methodDesc = "全部拒绝";
|
|
247
|
+
thresholdInfo = "null";
|
|
210
248
|
break;
|
|
211
249
|
}
|
|
250
|
+
const createTime = new Date(config2.createdAt).toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" });
|
|
251
|
+
const updateTime = new Date(config2.updatedAt).toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" });
|
|
212
252
|
return `群 ${targetGroupId} 配置:
|
|
213
253
|
关键词: ${config2.keywords.join(", ")}
|
|
214
|
-
审核方式: ${methodDesc}
|
|
254
|
+
审核方式: ${methodDesc}
|
|
255
|
+
阈值: ${thresholdInfo}
|
|
256
|
+
提醒消息: ${config2.reminderMessage}
|
|
257
|
+
创建时间: ${createTime}
|
|
258
|
+
更新时间: ${updateTime}
|
|
259
|
+
创建者: ${config2.createdBy}
|
|
260
|
+
更新者: ${config2.updatedBy}`;
|
|
215
261
|
} else {
|
|
216
262
|
return `群 ${targetGroupId} 无验证配置`;
|
|
217
263
|
}
|
|
218
264
|
}
|
|
219
|
-
|
|
220
|
-
return "请提供关键词参数,或使用 -? 查询配置,-r 删除配置";
|
|
221
|
-
}
|
|
222
|
-
const keywordList = keywords.split(",").map((k) => k.trim()).filter((k) => k);
|
|
223
|
-
if (keywordList.length === 0) {
|
|
224
|
-
return "请提供有效的关键词";
|
|
225
|
-
}
|
|
265
|
+
const keywordList = keywords ? keywords.split(",").map((k) => k.trim()).filter((k) => k) : [];
|
|
226
266
|
let reviewMethod = 0;
|
|
227
267
|
let reviewParameters = {};
|
|
228
268
|
if (options.method !== void 0) {
|
|
@@ -249,25 +289,30 @@ function apply(ctx, config) {
|
|
|
249
289
|
}
|
|
250
290
|
}
|
|
251
291
|
}
|
|
292
|
+
let reminderMessage = options.message || `用户 {user}({id}) 申请加入群 {gname}({group})
|
|
293
|
+
申请理由:{question}
|
|
294
|
+
答对情况:{answer}
|
|
295
|
+
阈值要求:{threshold}
|
|
296
|
+
请管理员使用 #同意 或 #拒绝 来处理此申请`;
|
|
297
|
+
reminderMessage = reminderMessage.replace(/\\n/g, "\n");
|
|
252
298
|
const existingConfig = await ctx.database.get("group_verification_config", { groupId: targetGroupId });
|
|
299
|
+
const configData = {
|
|
300
|
+
keywords: keywordList,
|
|
301
|
+
reviewMethod,
|
|
302
|
+
reviewParameters,
|
|
303
|
+
reminderMessage,
|
|
304
|
+
updatedBy: session.username || session.userId,
|
|
305
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
306
|
+
};
|
|
253
307
|
if (existingConfig.length > 0) {
|
|
254
|
-
await ctx.database.set("group_verification_config", { id: existingConfig[0].id },
|
|
255
|
-
keywords: keywordList,
|
|
256
|
-
reviewMethod,
|
|
257
|
-
reviewParameters,
|
|
258
|
-
reminderMessage: `New join request received. Please use #approve or #reject to handle this request`,
|
|
259
|
-
updatedAt: /* @__PURE__ */ new Date()
|
|
260
|
-
});
|
|
308
|
+
await ctx.database.set("group_verification_config", { id: existingConfig[0].id }, configData);
|
|
261
309
|
return `已更新群 ${targetGroupId} 的验证配置`;
|
|
262
310
|
} else {
|
|
263
311
|
await ctx.database.create("group_verification_config", {
|
|
312
|
+
...configData,
|
|
264
313
|
groupId: targetGroupId,
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
reviewParameters,
|
|
268
|
-
reminderMessage: `New join request received. Please use #approve or #reject to handle this request`,
|
|
269
|
-
createdAt: /* @__PURE__ */ new Date(),
|
|
270
|
-
updatedAt: /* @__PURE__ */ new Date()
|
|
314
|
+
createdBy: session.username || session.userId,
|
|
315
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
271
316
|
});
|
|
272
317
|
return `已为群 ${targetGroupId} 创建验证配置`;
|
|
273
318
|
}
|
|
@@ -357,19 +402,42 @@ function apply(ctx, config) {
|
|
|
357
402
|
});
|
|
358
403
|
groupVerify.subcommand(".help", "显示帮助信息").alias("帮助", "hlp", "帮助信息").action(() => {
|
|
359
404
|
return `群组验证命令帮助:
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
405
|
+
主指令别名:gv, gverify
|
|
406
|
+
|
|
407
|
+
配置命令 (.config):
|
|
408
|
+
选项:
|
|
409
|
+
-i <群号> 指定群号(私聊时必需)
|
|
410
|
+
-m <方式> 审核方式 (0=全部同意, 1=按数量, 2=按比例, 3=全部拒绝)
|
|
411
|
+
-t <阈值> 阈值参数(方式1或2时必需)
|
|
412
|
+
-msg <消息> 自定义提醒消息
|
|
413
|
+
-? 查询当前配置
|
|
414
|
+
-r 删除配置
|
|
415
|
+
|
|
416
|
+
示例:
|
|
417
|
+
gv.config 关键词1,关键词2 -m 1 -t 2
|
|
418
|
+
gv.config -i 123456789 -?
|
|
419
|
+
gv.config -r -i 123456789
|
|
420
|
+
|
|
421
|
+
提醒消息变量:
|
|
422
|
+
{user} - 用户名
|
|
423
|
+
{id} - 用户ID
|
|
424
|
+
{group} - 群号
|
|
425
|
+
{gname} - 群名称
|
|
426
|
+
{question} - 申请理由
|
|
427
|
+
{answer} - 答对数量/比例
|
|
428
|
+
{threshold} - 阈值要求
|
|
429
|
+
\\n - 换行符
|
|
364
430
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
.help - 显示此帮助信息
|
|
431
|
+
权限说明:
|
|
432
|
+
- 群主/管理员权限
|
|
433
|
+
- koishi三级以上权限
|
|
434
|
+
- 私聊时必须指定群号(-i参数)
|
|
370
435
|
|
|
371
|
-
|
|
372
|
-
|
|
436
|
+
其他命令:
|
|
437
|
+
.approve <用户ID> - 同意加群申请
|
|
438
|
+
.reject <用户ID> - 拒绝加群申请
|
|
439
|
+
.pending - 查看待审核列表
|
|
440
|
+
.stats [群号] - 查看统计信息`;
|
|
373
441
|
});
|
|
374
442
|
}
|
|
375
443
|
__name(apply, "apply");
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "koishi-plugin-group-verification",
|
|
3
3
|
"description": "Koishi 群组加群验证插件,支持多关键词匹配审核、多种审核方式和详细统计功能",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.3",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"typings": "lib/index.d.ts",
|
|
7
7
|
"files": [
|
|
@@ -34,10 +34,10 @@
|
|
|
34
34
|
}
|
|
35
35
|
},
|
|
36
36
|
"peerDependencies": {
|
|
37
|
-
"koishi": "^4.
|
|
37
|
+
"koishi": "^4.10.0"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
|
-
"typescript": "^
|
|
41
|
-
"@types/node": "^
|
|
40
|
+
"typescript": "^4.9.0",
|
|
41
|
+
"@types/node": "^16.0.0"
|
|
42
42
|
}
|
|
43
43
|
}
|
package/src/index.ts
CHANGED
|
@@ -21,6 +21,8 @@ export interface GroupVerificationConfig {
|
|
|
21
21
|
threshold?: number // 数量阈值或比例阈值
|
|
22
22
|
}
|
|
23
23
|
reminderMessage: string
|
|
24
|
+
createdBy: string
|
|
25
|
+
updatedBy: string
|
|
24
26
|
createdAt: Date
|
|
25
27
|
updatedAt: Date
|
|
26
28
|
}
|
|
@@ -58,6 +60,8 @@ export function apply(ctx: Context, config: Config) {
|
|
|
58
60
|
reviewMethod: 'integer',
|
|
59
61
|
reviewParameters: 'json',
|
|
60
62
|
reminderMessage: 'string',
|
|
63
|
+
createdBy: 'string',
|
|
64
|
+
updatedBy: 'string',
|
|
61
65
|
createdAt: 'date',
|
|
62
66
|
updatedAt: 'date'
|
|
63
67
|
}, {
|
|
@@ -110,7 +114,7 @@ export function apply(ctx: Context, config: Config) {
|
|
|
110
114
|
const config = groupConfig[0]
|
|
111
115
|
|
|
112
116
|
// 执行验证
|
|
113
|
-
const isValid = await verifyApplication(config, message)
|
|
117
|
+
const { isValid, matchedCount, requiredThreshold } = await verifyApplication(config, message, session)
|
|
114
118
|
|
|
115
119
|
if (isValid) {
|
|
116
120
|
// 验证成功,自动同意入群
|
|
@@ -131,6 +135,18 @@ export function apply(ctx: Context, config: Config) {
|
|
|
131
135
|
const username = session.username || '未知用户'
|
|
132
136
|
const message = session.content || ''
|
|
133
137
|
|
|
138
|
+
// 获取群信息
|
|
139
|
+
let groupName = '未知群组'
|
|
140
|
+
try {
|
|
141
|
+
const guild = await session.bot.getGuild(guildId)
|
|
142
|
+
groupName = guild.name || groupName
|
|
143
|
+
} catch (error) {
|
|
144
|
+
// 无法获取群名称时使用默认值
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// 执行验证获取详细信息
|
|
148
|
+
const { matchedCount, requiredThreshold } = await verifyApplication(config, message, session)
|
|
149
|
+
|
|
134
150
|
// 将申请加入待审核列表
|
|
135
151
|
await ctx.database.create('group_verification_pending', {
|
|
136
152
|
groupId: guildId,
|
|
@@ -140,32 +156,56 @@ export function apply(ctx: Context, config: Config) {
|
|
|
140
156
|
applyTime: new Date()
|
|
141
157
|
})
|
|
142
158
|
|
|
159
|
+
// 替换提醒消息中的变量
|
|
160
|
+
let reminderMsg = config.reminderMessage
|
|
161
|
+
reminderMsg = reminderMsg
|
|
162
|
+
.replace(/{user}/g, username)
|
|
163
|
+
.replace(/{id}/g, userId)
|
|
164
|
+
.replace(/{group}/g, guildId)
|
|
165
|
+
.replace(/{gname}/g, groupName)
|
|
166
|
+
.replace(/{question}/g, message)
|
|
167
|
+
.replace(/{answer}/g, matchedCount.toString())
|
|
168
|
+
.replace(/{threshold}/g, requiredThreshold)
|
|
169
|
+
|
|
143
170
|
// 发送提醒消息到群内
|
|
144
|
-
const reminderMsg = config.reminderMessage || `收到新的加群申请:${username}(${userId})\n申请理由:${message}\n请管理员使用 #同意 或 #拒绝 来处理此申请`
|
|
145
171
|
await ctx.broadcast([guildId], reminderMsg)
|
|
146
172
|
}
|
|
147
173
|
|
|
148
174
|
// 验证申请
|
|
149
|
-
async function verifyApplication(config: GroupVerificationConfig, message: string): Promise<boolean> {
|
|
175
|
+
async function verifyApplication(config: GroupVerificationConfig, message: string, session: any): Promise<{isValid: boolean, matchedCount: number, requiredThreshold: string}> {
|
|
150
176
|
// 统计匹配的关键词数量
|
|
151
177
|
const matchedCount = config.keywords.filter(keyword =>
|
|
152
178
|
message.toLowerCase().includes(keyword.toLowerCase())
|
|
153
179
|
).length
|
|
154
180
|
|
|
181
|
+
let isValid = false
|
|
182
|
+
let requiredThreshold = ''
|
|
183
|
+
|
|
155
184
|
switch (config.reviewMethod) {
|
|
156
185
|
case 0: // 全部同意
|
|
157
|
-
|
|
186
|
+
isValid = true
|
|
187
|
+
requiredThreshold = 'null'
|
|
188
|
+
break
|
|
158
189
|
case 1: // 按数量同意
|
|
159
|
-
|
|
190
|
+
isValid = matchedCount >= (config.reviewParameters.threshold || 1)
|
|
191
|
+
requiredThreshold = `${config.reviewParameters.threshold || 1}`
|
|
192
|
+
break
|
|
160
193
|
case 2: // 按比例同意
|
|
161
194
|
const ratio = matchedCount / config.keywords.length
|
|
162
195
|
const requiredRatio = (config.reviewParameters.threshold || 100) / 100
|
|
163
|
-
|
|
196
|
+
isValid = ratio >= requiredRatio
|
|
197
|
+
requiredThreshold = `${config.reviewParameters.threshold || 100}%`
|
|
198
|
+
break
|
|
164
199
|
case 3: // 全部拒绝
|
|
165
|
-
|
|
200
|
+
isValid = false
|
|
201
|
+
requiredThreshold = 'null'
|
|
202
|
+
break
|
|
166
203
|
default:
|
|
167
|
-
|
|
204
|
+
isValid = false
|
|
205
|
+
requiredThreshold = 'null'
|
|
168
206
|
}
|
|
207
|
+
|
|
208
|
+
return { isValid, matchedCount, requiredThreshold }
|
|
169
209
|
}
|
|
170
210
|
|
|
171
211
|
// 更新统计信息
|
|
@@ -210,31 +250,42 @@ export function apply(ctx: Context, config: Config) {
|
|
|
210
250
|
}
|
|
211
251
|
|
|
212
252
|
// 权限检查函数
|
|
213
|
-
async function checkPermission(session: any, targetGroupId?: string): Promise<boolean> {
|
|
253
|
+
async function checkPermission(session: any, targetGroupId?: string): Promise<[boolean, string?]> {
|
|
214
254
|
const groupId = targetGroupId || session.guildId
|
|
215
255
|
|
|
216
|
-
//
|
|
217
|
-
if (groupId) {
|
|
218
|
-
|
|
219
|
-
const member = await session.bot.getGuildMember(groupId, session.userId)
|
|
220
|
-
if (member && (member.roles?.includes('admin') || member.permissions?.includes('ADMINISTRATOR'))) {
|
|
221
|
-
return true
|
|
222
|
-
}
|
|
223
|
-
} catch (error) {
|
|
224
|
-
// 如果无法获取群成员信息,继续检查其他权限
|
|
225
|
-
}
|
|
256
|
+
// 私聊情况下必须指定群号
|
|
257
|
+
if (!groupId) {
|
|
258
|
+
return [false, '请在群聊中使用此命令或使用 -i 参数指定群号']
|
|
226
259
|
}
|
|
227
260
|
|
|
228
261
|
// 检查koishi权限等级(三级及以上)
|
|
229
262
|
if (session.author?.authority && session.author.authority >= 3) {
|
|
230
|
-
return true
|
|
263
|
+
return [true]
|
|
231
264
|
}
|
|
232
265
|
|
|
233
|
-
|
|
266
|
+
// 检查是否为群主或管理员
|
|
267
|
+
try {
|
|
268
|
+
const member = await session.bot.getGuildMember(groupId, session.userId)
|
|
269
|
+
if (member) {
|
|
270
|
+
// 检查群主权限
|
|
271
|
+
if (member.permissions?.includes('OWNER')) {
|
|
272
|
+
return [true]
|
|
273
|
+
}
|
|
274
|
+
// 检查管理员权限
|
|
275
|
+
if (member.roles?.includes('admin') || member.permissions?.includes('ADMINISTRATOR')) {
|
|
276
|
+
return [true]
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
} catch (error) {
|
|
280
|
+
return [false, `无法获取群 ${groupId} 的成员信息,请确认机器人已在该群中`]
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
return [false, '权限不足:需要群主/管理员权限或koishi三级以上权限']
|
|
234
284
|
}
|
|
235
285
|
|
|
236
|
-
// Create main command
|
|
286
|
+
// Create main command with aliases
|
|
237
287
|
const groupVerify = ctx.command('group-verify', '群组验证管理命令')
|
|
288
|
+
.alias('gv', 'gverify')
|
|
238
289
|
|
|
239
290
|
// Subcommand: configure group verification
|
|
240
291
|
groupVerify
|
|
@@ -243,18 +294,27 @@ export function apply(ctx: Context, config: Config) {
|
|
|
243
294
|
.option('groupId', '-i <groupId> 指定群号')
|
|
244
295
|
.option('method', '-m <method> 审核方式 (0-3)')
|
|
245
296
|
.option('threshold', '-t <threshold> 阈值参数')
|
|
297
|
+
.option('message', '-msg <message> 自定义提醒消息')
|
|
246
298
|
.option('query', '-? 查询当前配置')
|
|
247
299
|
.option('remove', '-r 删除配置')
|
|
248
300
|
.action(async ({ session, options }, keywords) => {
|
|
249
301
|
const targetGroupId = options.groupId || session.guildId
|
|
250
302
|
|
|
251
303
|
// 权限检查
|
|
252
|
-
|
|
253
|
-
|
|
304
|
+
const [hasPermission, errorMsg] = await checkPermission(session, targetGroupId)
|
|
305
|
+
if (!hasPermission) {
|
|
306
|
+
return errorMsg || '权限不足'
|
|
254
307
|
}
|
|
255
308
|
|
|
256
|
-
|
|
257
|
-
|
|
309
|
+
// 获取群信息
|
|
310
|
+
let groupName = '未知群组'
|
|
311
|
+
if (targetGroupId) {
|
|
312
|
+
try {
|
|
313
|
+
const guild = await session.bot.getGuild(targetGroupId)
|
|
314
|
+
groupName = guild.name || groupName
|
|
315
|
+
} catch (error) {
|
|
316
|
+
// 无法获取群名称时使用默认值
|
|
317
|
+
}
|
|
258
318
|
}
|
|
259
319
|
|
|
260
320
|
// 处理删除配置
|
|
@@ -274,30 +334,47 @@ export function apply(ctx: Context, config: Config) {
|
|
|
274
334
|
if (existingConfig.length > 0) {
|
|
275
335
|
const config = existingConfig[0]
|
|
276
336
|
let methodDesc = ''
|
|
337
|
+
let thresholdInfo = ''
|
|
277
338
|
switch (config.reviewMethod) {
|
|
278
|
-
case 0:
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
339
|
+
case 0:
|
|
340
|
+
methodDesc = '全部同意'
|
|
341
|
+
thresholdInfo = 'null'
|
|
342
|
+
break
|
|
343
|
+
case 1:
|
|
344
|
+
methodDesc = `按数量同意`
|
|
345
|
+
thresholdInfo = `${config.reviewParameters.threshold || 1}`
|
|
346
|
+
break
|
|
347
|
+
case 2:
|
|
348
|
+
methodDesc = `按比例同意`
|
|
349
|
+
thresholdInfo = `${config.reviewParameters.threshold || 100}%`
|
|
350
|
+
break
|
|
351
|
+
case 3:
|
|
352
|
+
methodDesc = '全部拒绝'
|
|
353
|
+
thresholdInfo = 'null'
|
|
354
|
+
break
|
|
282
355
|
}
|
|
283
|
-
|
|
356
|
+
|
|
357
|
+
const createTime = new Date(config.createdAt).toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' })
|
|
358
|
+
const updateTime = new Date(config.updatedAt).toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' })
|
|
359
|
+
|
|
360
|
+
return `群 ${targetGroupId} 配置:
|
|
361
|
+
关键词: ${config.keywords.join(', ')}
|
|
362
|
+
审核方式: ${methodDesc}
|
|
363
|
+
阈值: ${thresholdInfo}
|
|
364
|
+
提醒消息: ${config.reminderMessage}
|
|
365
|
+
创建时间: ${createTime}
|
|
366
|
+
更新时间: ${updateTime}
|
|
367
|
+
创建者: ${config.createdBy}
|
|
368
|
+
更新者: ${config.updatedBy}`
|
|
284
369
|
} else {
|
|
285
370
|
return `群 ${targetGroupId} 无验证配置`
|
|
286
371
|
}
|
|
287
372
|
}
|
|
288
373
|
|
|
289
374
|
// 处理配置创建/更新
|
|
290
|
-
|
|
291
|
-
return '请提供关键词参数,或使用 -? 查询配置,-r 删除配置'
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
const keywordList = keywords.split(',').map(k => k.trim()).filter(k => k)
|
|
375
|
+
const keywordList = keywords ? keywords.split(',').map(k => k.trim()).filter(k => k) : []
|
|
295
376
|
|
|
296
|
-
|
|
297
|
-
return '请提供有效的关键词'
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
// 解析审核方式
|
|
377
|
+
// 解析审核方式和阈值
|
|
301
378
|
let reviewMethod: 0 | 1 | 2 | 3 = 0 // 默认全部同意
|
|
302
379
|
let reviewParameters: { threshold?: number } = {}
|
|
303
380
|
|
|
@@ -329,29 +406,40 @@ export function apply(ctx: Context, config: Config) {
|
|
|
329
406
|
}
|
|
330
407
|
}
|
|
331
408
|
|
|
409
|
+
// 处理提醒消息
|
|
410
|
+
let reminderMessage = options.message ||
|
|
411
|
+
`用户 {user}({id}) 申请加入群 {gname}({group})
|
|
412
|
+
申请理由:{question}
|
|
413
|
+
答对情况:{answer}
|
|
414
|
+
阈值要求:{threshold}
|
|
415
|
+
请管理员使用 #同意 或 #拒绝 来处理此申请`
|
|
416
|
+
|
|
417
|
+
// 替换换行符
|
|
418
|
+
reminderMessage = reminderMessage.replace(/\\n/g, '\n')
|
|
419
|
+
|
|
332
420
|
// Check if configuration already exists
|
|
333
421
|
const existingConfig = await ctx.database.get('group_verification_config', { groupId: targetGroupId })
|
|
334
422
|
|
|
423
|
+
const configData = {
|
|
424
|
+
keywords: keywordList,
|
|
425
|
+
reviewMethod: reviewMethod,
|
|
426
|
+
reviewParameters: reviewParameters,
|
|
427
|
+
reminderMessage: reminderMessage,
|
|
428
|
+
updatedBy: session.username || session.userId,
|
|
429
|
+
updatedAt: new Date()
|
|
430
|
+
}
|
|
431
|
+
|
|
335
432
|
if (existingConfig.length > 0) {
|
|
336
433
|
// Update existing configuration
|
|
337
|
-
await ctx.database.set('group_verification_config', { id: existingConfig[0].id },
|
|
338
|
-
keywords: keywordList,
|
|
339
|
-
reviewMethod: reviewMethod,
|
|
340
|
-
reviewParameters: reviewParameters,
|
|
341
|
-
reminderMessage: `New join request received. Please use #approve or #reject to handle this request`,
|
|
342
|
-
updatedAt: new Date()
|
|
343
|
-
})
|
|
434
|
+
await ctx.database.set('group_verification_config', { id: existingConfig[0].id }, configData)
|
|
344
435
|
return `已更新群 ${targetGroupId} 的验证配置`
|
|
345
436
|
} else {
|
|
346
437
|
// Create new configuration
|
|
347
438
|
await ctx.database.create('group_verification_config', {
|
|
439
|
+
...configData,
|
|
348
440
|
groupId: targetGroupId,
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
reviewParameters: reviewParameters,
|
|
352
|
-
reminderMessage: `New join request received. Please use #approve or #reject to handle this request`,
|
|
353
|
-
createdAt: new Date(),
|
|
354
|
-
updatedAt: new Date()
|
|
441
|
+
createdBy: session.username || session.userId,
|
|
442
|
+
createdAt: new Date()
|
|
355
443
|
})
|
|
356
444
|
return `已为群 ${targetGroupId} 创建验证配置`
|
|
357
445
|
}
|
|
@@ -486,17 +574,42 @@ export function apply(ctx: Context, config: Config) {
|
|
|
486
574
|
.subcommand('.help', '显示帮助信息')
|
|
487
575
|
.alias('帮助', 'hlp', '帮助信息')
|
|
488
576
|
.action(() => {
|
|
489
|
-
return
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
577
|
+
return `群组验证命令帮助:
|
|
578
|
+
主指令别名:gv, gverify
|
|
579
|
+
|
|
580
|
+
配置命令 (.config):
|
|
581
|
+
选项:
|
|
582
|
+
-i <群号> 指定群号(私聊时必需)
|
|
583
|
+
-m <方式> 审核方式 (0=全部同意, 1=按数量, 2=按比例, 3=全部拒绝)
|
|
584
|
+
-t <阈值> 阈值参数(方式1或2时必需)
|
|
585
|
+
-msg <消息> 自定义提醒消息
|
|
586
|
+
-? 查询当前配置
|
|
587
|
+
-r 删除配置
|
|
588
|
+
|
|
589
|
+
示例:
|
|
590
|
+
gv.config 关键词1,关键词2 -m 1 -t 2
|
|
591
|
+
gv.config -i 123456789 -?
|
|
592
|
+
gv.config -r -i 123456789
|
|
593
|
+
|
|
594
|
+
提醒消息变量:
|
|
595
|
+
{user} - 用户名
|
|
596
|
+
{id} - 用户ID
|
|
597
|
+
{group} - 群号
|
|
598
|
+
{gname} - 群名称
|
|
599
|
+
{question} - 申请理由
|
|
600
|
+
{answer} - 答对数量/比例
|
|
601
|
+
{threshold} - 阈值要求
|
|
602
|
+
\\n - 换行符
|
|
603
|
+
|
|
604
|
+
权限说明:
|
|
605
|
+
- 群主/管理员权限
|
|
606
|
+
- koishi三级以上权限
|
|
607
|
+
- 私聊时必须指定群号(-i参数)
|
|
608
|
+
|
|
609
|
+
其他命令:
|
|
610
|
+
.approve <用户ID> - 同意加群申请
|
|
611
|
+
.reject <用户ID> - 拒绝加群申请
|
|
612
|
+
.pending - 查看待审核列表
|
|
613
|
+
.stats [群号] - 查看统计信息`
|
|
501
614
|
})
|
|
502
615
|
}
|