koishi-plugin-onebot-verifier 1.0.9 → 1.1.0
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 +7 -0
- package/lib/index.js +105 -6
- package/package.json +1 -1
package/lib/index.d.ts
CHANGED
|
@@ -21,6 +21,13 @@ export interface Config {
|
|
|
21
21
|
minLevel?: number;
|
|
22
22
|
action?: 'accept' | 'reject';
|
|
23
23
|
}[];
|
|
24
|
+
syncNotify?: boolean;
|
|
25
|
+
specialRules?: {
|
|
26
|
+
guildId: string;
|
|
27
|
+
enabled: boolean;
|
|
28
|
+
mode: 'vote' | 'captcha';
|
|
29
|
+
}[];
|
|
30
|
+
voteRatio?: string;
|
|
24
31
|
}
|
|
25
32
|
export declare const Config: Schema<Config>;
|
|
26
33
|
export declare function apply(ctx: Context, config?: Config): void;
|
package/lib/index.js
CHANGED
|
@@ -73,12 +73,25 @@ var Config = import_koishi.Schema.intersect([
|
|
|
73
73
|
import_koishi.Schema.const("accept").description("同意"),
|
|
74
74
|
import_koishi.Schema.const("reject").description("拒绝")
|
|
75
75
|
]).description("操作")
|
|
76
|
-
})).description("
|
|
77
|
-
}).description("加群请求配置")
|
|
76
|
+
})).description("普通验证配置").role("table")
|
|
77
|
+
}).description("加群请求配置"),
|
|
78
|
+
import_koishi.Schema.object({
|
|
79
|
+
syncNotify: import_koishi.Schema.boolean().description("同步通知目标").default(true),
|
|
80
|
+
specialRules: import_koishi.Schema.array(import_koishi.Schema.object({
|
|
81
|
+
guildId: import_koishi.Schema.string().description("群号").required(),
|
|
82
|
+
mode: import_koishi.Schema.union([
|
|
83
|
+
import_koishi.Schema.const("vote").description("投票"),
|
|
84
|
+
import_koishi.Schema.const("captcha").description("验证码")
|
|
85
|
+
]).description("模式").default("vote"),
|
|
86
|
+
enabled: import_koishi.Schema.boolean().description("前置规则").default(true)
|
|
87
|
+
})).description("配置列表").role("table"),
|
|
88
|
+
voteRatio: import_koishi.Schema.string().description("投票比例").default("3:2")
|
|
89
|
+
}).description("特殊验证配置")
|
|
78
90
|
]);
|
|
79
91
|
function apply(ctx, config = {}) {
|
|
80
92
|
const logger = new import_koishi.Logger("onebot-verifier");
|
|
81
93
|
const activeTasks = /* @__PURE__ */ new Map();
|
|
94
|
+
const activeCaptchas = /* @__PURE__ */ new Map();
|
|
82
95
|
const inviterMap = /* @__PURE__ */ new Map();
|
|
83
96
|
const getComment = /* @__PURE__ */ __name((comment) => {
|
|
84
97
|
if (!comment) return "";
|
|
@@ -130,6 +143,7 @@ function apply(ctx, config = {}) {
|
|
|
130
143
|
if (adminId) infoLines.push(`管理:${adminInfo?.name ? `${adminInfo.name}(${adminId})` : adminId}`);
|
|
131
144
|
if (session.guildId) infoLines.push(`群组:${groupInfo?.name ? `${groupInfo.name}(${session.guildId})` : session.guildId}`);
|
|
132
145
|
if (eventData.comment) infoLines.push(`验证信息:${eventData.comment}`);
|
|
146
|
+
if (status === "waiting") infoLines.push(`使用"y/n"回复本消息,以同意/拒绝该请求`);
|
|
133
147
|
const content = infoLines.join("\n");
|
|
134
148
|
const msgIds = await (targetType === "private" ? session.bot.sendPrivateMessage(targetId, content) : session.bot.sendMessage(targetId, content)) || [];
|
|
135
149
|
return msgIds;
|
|
@@ -194,9 +208,29 @@ function apply(ctx, config = {}) {
|
|
|
194
208
|
return;
|
|
195
209
|
}
|
|
196
210
|
}
|
|
211
|
+
const specialRule = config.specialRules?.find((r) => String(r.guildId) === String(session.guildId) && r.enabled);
|
|
212
|
+
if (specialRule) {
|
|
213
|
+
if (specialRule.mode === "vote") {
|
|
214
|
+
const [yesStr, noStr] = config.voteRatio.split(":");
|
|
215
|
+
const targetYes = parseInt(yesStr) || 0;
|
|
216
|
+
const targetNo = parseInt(noStr) || 0;
|
|
217
|
+
let msgIds = [];
|
|
218
|
+
if (config.syncNotify !== false) msgIds = await sendNotice(session, kind, "waiting");
|
|
219
|
+
if (msgIds.length > 0) {
|
|
220
|
+
const task = { session, kind, messages: msgIds, specialMode: "vote", voteTarget: { yes: targetYes, no: targetNo }, votes: { yes: /* @__PURE__ */ new Set(), no: /* @__PURE__ */ new Set() } };
|
|
221
|
+
msgIds.forEach((id) => activeTasks.set(id, task));
|
|
222
|
+
}
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
if (specialRule.mode === "captcha") {
|
|
226
|
+
await executeAction(session, kind, true, "验证码验证,自动通过");
|
|
227
|
+
if (config.syncNotify !== false) await sendNotice(session, kind, "auto_pass");
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
197
231
|
if (config.verifyMode && config.verifyMode !== "manual") {
|
|
198
232
|
const isApprove = config.verifyMode === "accept";
|
|
199
|
-
await executeAction(session, kind, isApprove, "
|
|
233
|
+
await executeAction(session, kind, isApprove, "默认规则,自动处理");
|
|
200
234
|
await sendNotice(session, kind, isApprove ? "auto_pass" : "auto_reject");
|
|
201
235
|
return;
|
|
202
236
|
}
|
|
@@ -255,10 +289,57 @@ function apply(ctx, config = {}) {
|
|
|
255
289
|
logger.error(`处理失败: ${error}`);
|
|
256
290
|
}
|
|
257
291
|
}, "hookEvent");
|
|
292
|
+
const handleSpecialVote = /* @__PURE__ */ __name(async (session, task, isApprove, extraInfo, targetType, targetId) => {
|
|
293
|
+
if (!task.voteTarget || !task.votes) return;
|
|
294
|
+
const voterId = session.userId;
|
|
295
|
+
if (!voterId) return;
|
|
296
|
+
task.votes.yes.delete(voterId);
|
|
297
|
+
task.votes.no.delete(voterId);
|
|
298
|
+
if (isApprove) {
|
|
299
|
+
task.votes.yes.add(voterId);
|
|
300
|
+
} else {
|
|
301
|
+
task.votes.no.add(voterId);
|
|
302
|
+
}
|
|
303
|
+
if (config.debugMode) logger.info(`[投票] 赞成: ${task.votes.yes.size}/${task.voteTarget.yes} | 反对: ${task.votes.no.size}/${task.voteTarget.no}`);
|
|
304
|
+
let thresholdMet = false;
|
|
305
|
+
let finalVerdict = false;
|
|
306
|
+
if (task.voteTarget.yes > 0 && task.votes.yes.size >= task.voteTarget.yes) {
|
|
307
|
+
thresholdMet = true;
|
|
308
|
+
finalVerdict = true;
|
|
309
|
+
} else if (task.voteTarget.no > 0 && task.votes.no.size >= task.voteTarget.no) {
|
|
310
|
+
thresholdMet = true;
|
|
311
|
+
finalVerdict = false;
|
|
312
|
+
}
|
|
313
|
+
if (!thresholdMet) return;
|
|
314
|
+
task.messages.forEach((msg) => activeTasks.delete(msg));
|
|
315
|
+
const isSuccess = await executeAction(task.session, task.kind, finalVerdict, finalVerdict ? "" : extraInfo);
|
|
316
|
+
const replyText = isSuccess ? `已${finalVerdict ? "通过" : "拒绝"}该投票` : `处理投票失败`;
|
|
317
|
+
if (session.bot) await (targetType === "private" ? session.bot.sendPrivateMessage(targetId, replyText) : session.bot.sendMessage(targetId, replyText)).catch(() => {
|
|
318
|
+
});
|
|
319
|
+
}, "handleSpecialVote");
|
|
258
320
|
ctx.on("friend-request", hookEvent("friend"));
|
|
259
321
|
ctx.on("guild-request", hookEvent("guild"));
|
|
260
322
|
ctx.on("guild-member-request", hookEvent("member"));
|
|
261
323
|
ctx.on("guild-added", hookEvent("guild"));
|
|
324
|
+
ctx.on("guild-member-added", async (session) => {
|
|
325
|
+
if (!config.specialRules || !session.guildId || !session.userId) return;
|
|
326
|
+
const rule = config.specialRules.find((r) => String(r.guildId) === String(session.guildId) && r.enabled);
|
|
327
|
+
if (rule?.mode === "captcha") {
|
|
328
|
+
const a = Math.floor(Math.random() * 20) + 1;
|
|
329
|
+
const b = Math.floor(Math.random() * 20) + 1;
|
|
330
|
+
const answer = (a + b).toString();
|
|
331
|
+
const captchaKey = `${session.guildId}:${session.userId}`;
|
|
332
|
+
await session.send(`<at id="${session.userId}"/> 请在 60 秒内回复计算结果,以进行验证:${a} + ${b} =`);
|
|
333
|
+
const timer = setTimeout(async () => {
|
|
334
|
+
if (activeCaptchas.has(captchaKey)) {
|
|
335
|
+
activeCaptchas.delete(captchaKey);
|
|
336
|
+
await session.send(`<at id="${session.userId}"/> 验证失败,将被移出本群。`);
|
|
337
|
+
await session.onebot?.setGroupKick(session.guildId, session.userId, false);
|
|
338
|
+
}
|
|
339
|
+
}, 6e4);
|
|
340
|
+
activeCaptchas.set(captchaKey, { guildId: session.guildId, userId: session.userId, answer, timer });
|
|
341
|
+
}
|
|
342
|
+
});
|
|
262
343
|
ctx.on("guild-removed", async (session) => {
|
|
263
344
|
if (session.event?._data?.sub_type === "kick_me") {
|
|
264
345
|
const gid = session.guildId;
|
|
@@ -277,7 +358,21 @@ function apply(ctx, config = {}) {
|
|
|
277
358
|
await sendNotice(session, "removed");
|
|
278
359
|
});
|
|
279
360
|
ctx.middleware(async (session, next) => {
|
|
280
|
-
if (typeof session.content !== "string"
|
|
361
|
+
if (typeof session.content !== "string") return next();
|
|
362
|
+
if (session.guildId && session.userId) {
|
|
363
|
+
const captchaKey = `${session.guildId}:${session.userId}`;
|
|
364
|
+
const captcha = activeCaptchas.get(captchaKey);
|
|
365
|
+
if (captcha) {
|
|
366
|
+
const input2 = session.content.trim();
|
|
367
|
+
if (input2 === captcha.answer) {
|
|
368
|
+
clearTimeout(captcha.timer);
|
|
369
|
+
activeCaptchas.delete(captchaKey);
|
|
370
|
+
await session.send(`<at id="${session.userId}"/> 验证成功,欢迎加入本群!`);
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
if (!session.quote?.id) return next();
|
|
281
376
|
const task = activeTasks.get(session.quote.id);
|
|
282
377
|
if (!task) return next();
|
|
283
378
|
const [targetType, targetId] = (config.notifyTarget || "").split(":");
|
|
@@ -286,11 +381,15 @@ function apply(ctx, config = {}) {
|
|
|
286
381
|
const input = session.content.replace(/<(quote|at)\s+[^>]*\/>/gi, "").trim();
|
|
287
382
|
const cmdMatch = input.match(/^(y|n|通过|拒绝)(?:\s+(.*))?$/i);
|
|
288
383
|
if (!cmdMatch) return next();
|
|
289
|
-
if (task.timer) clearTimeout(task.timer);
|
|
290
|
-
task.messages.forEach((msg) => activeTasks.delete(msg));
|
|
291
384
|
const isApprove = ["y", "通过"].includes(cmdMatch[1].toLowerCase());
|
|
292
385
|
const extraInfo = cmdMatch[2]?.trim() || "";
|
|
293
386
|
if (config.debugMode) logger.info(`[操作] 收到指令: ${isApprove ? "同意" : "拒绝"}`);
|
|
387
|
+
if (task.specialMode === "vote") {
|
|
388
|
+
await handleSpecialVote(session, task, isApprove, extraInfo, targetType, targetId);
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
if (task.timer) clearTimeout(task.timer);
|
|
392
|
+
task.messages.forEach((msg) => activeTasks.delete(msg));
|
|
294
393
|
const isSuccess = await executeAction(task.session, task.kind, isApprove, isApprove ? "" : extraInfo, isApprove && task.kind === "friend" ? extraInfo : "");
|
|
295
394
|
const replyText = isSuccess ? `已${isApprove ? "通过" : "拒绝"}该请求` : `处理请求失败`;
|
|
296
395
|
if (session.bot) await (targetType === "private" ? session.bot.sendPrivateMessage(targetId, replyText) : session.bot.sendMessage(targetId, replyText)).catch(() => {
|