koishi-plugin-onebot-verifier 1.0.8 → 1.0.10

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 CHANGED
@@ -21,6 +21,12 @@ export interface Config {
21
21
  minLevel?: number;
22
22
  action?: 'accept' | 'reject';
23
23
  }[];
24
+ voteRatio?: string;
25
+ syncNotify?: boolean;
26
+ specialRules?: {
27
+ guildId: string;
28
+ mode: 'vote';
29
+ }[];
24
30
  }
25
31
  export declare const Config: Schema<Config>;
26
32
  export declare function apply(ctx: Context, config?: Config): void;
package/lib/index.js CHANGED
@@ -54,10 +54,10 @@ var Config = import_koishi.Schema.intersect([
54
54
  import_koishi.Schema.const("accept").description("同意"),
55
55
  import_koishi.Schema.const("reject").description("拒绝")
56
56
  ]).description("默认超时操作").default("accept"),
57
- friendLevel: import_koishi.Schema.number().description("最低好友等级").default(-1).min(-1).max(256),
57
+ friendLevel: import_koishi.Schema.number().description("最低好友等级").default(0).min(0).max(256),
58
58
  friendRegex: import_koishi.Schema.string().description("好友验证正则"),
59
- minMembers: import_koishi.Schema.number().description("最低群成员数").default(-1).min(-1).max(3e3),
60
- maxCapacity: import_koishi.Schema.number().description("最低受邀容量").default(-1).min(-1).max(3e3)
59
+ minMembers: import_koishi.Schema.number().description("最低群成员数").default(0).min(0).max(3e3),
60
+ maxCapacity: import_koishi.Schema.number().description("最低受邀容量").default(0).min(0).max(3e3)
61
61
  }).description("好友邀群配置"),
62
62
  import_koishi.Schema.object({
63
63
  verifyMode: import_koishi.Schema.union([
@@ -68,37 +68,53 @@ var Config = import_koishi.Schema.intersect([
68
68
  verifyRules: import_koishi.Schema.array(import_koishi.Schema.object({
69
69
  guildId: import_koishi.Schema.string().description("群号").required(),
70
70
  keyword: import_koishi.Schema.string().description("正则"),
71
- minLevel: import_koishi.Schema.number().description("等级").default(-1),
71
+ minLevel: import_koishi.Schema.number().description("等级").default(0),
72
72
  action: import_koishi.Schema.union([
73
73
  import_koishi.Schema.const("accept").description("同意"),
74
74
  import_koishi.Schema.const("reject").description("拒绝")
75
75
  ]).description("操作")
76
76
  })).description("加群验证配置").role("table")
77
- }).description("加群请求配置")
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
+ ]).description("模式").default("vote")
85
+ })).description("群组特殊配置").role("table"),
86
+ voteRatio: import_koishi.Schema.string().description("投票比例").default("3:2")
87
+ }).description("特殊验证配置")
78
88
  ]);
79
89
  function apply(ctx, config = {}) {
80
90
  const logger = new import_koishi.Logger("onebot-verifier");
81
91
  const activeTasks = /* @__PURE__ */ new Map();
82
92
  const inviterMap = /* @__PURE__ */ new Map();
93
+ const getComment = /* @__PURE__ */ __name((comment) => {
94
+ if (!comment) return "";
95
+ const lines = comment.split(/[\r\n]+/).map((s) => s.trim());
96
+ const answers = lines.filter((s) => /^(回答|答案)[::]/i.test(s)).map((s) => s.replace(/^(回答|答案)[::]\s*/i, ""));
97
+ return answers.length > 0 ? answers.join("\n") : comment;
98
+ }, "getComment");
83
99
  const executeAction = /* @__PURE__ */ __name(async (session, kind, pass, reason = "", remark = "") => {
84
100
  try {
85
101
  const eventData = session.event?._data || {};
86
- if (config.debugMode) logger.info(`[执行操作] 类型:${kind} 结果:${pass ? "同意" : "拒绝"} 原因:${reason || "无"}`);
102
+ if (config.debugMode) logger.info(`[操作] 类型:${kind} 结果:${pass ? "同意" : "拒绝"} 原因:${reason || "无"}`);
87
103
  if (pass && kind === "guild" && session.guildId && session.userId) inviterMap.set(session.guildId, session.userId);
88
104
  if (!pass && kind === "guild" && session.guildId && (session.event?.type === "guild-added" || eventData.notice_type === "group_increase")) {
89
- if (reason) {
90
- try {
91
- await session.bot.sendMessage(session.guildId, `${reason},将退出该群`);
92
- } catch (error) {
93
- logger.warn(`发送退群通知失败: ${error}`);
94
- }
95
- }
105
+ if (reason) await session.bot?.sendMessage(session.guildId, `${reason},将退出该群`).catch(() => {
106
+ });
96
107
  await session.onebot?.setGroupLeave(session.guildId, false);
108
+ if (config.debugMode) logger.info(`[操作] 退出群组: ${session.guildId}`);
97
109
  return true;
98
110
  }
99
- if (!eventData.flag || !session.onebot) return false;
100
- if (kind === "friend") await session.onebot.setFriendAddRequest(eventData.flag, pass, remark);
101
- else await session.onebot.setGroupAddRequest(eventData.flag, eventData.sub_type ?? "add", pass, pass ? "" : reason);
111
+ const flag = eventData.flag;
112
+ if (!flag || !session.onebot) return false;
113
+ if (kind === "friend") {
114
+ await session.onebot.setFriendAddRequest(flag, pass, remark);
115
+ } else {
116
+ await session.onebot.setGroupAddRequest(flag, eventData.sub_type ?? "add", pass, pass ? "" : reason);
117
+ }
102
118
  return true;
103
119
  } catch (error) {
104
120
  logger.error(`操作失败: ${error}`);
@@ -107,143 +123,155 @@ function apply(ctx, config = {}) {
107
123
  }, "executeAction");
108
124
  const sendNotice = /* @__PURE__ */ __name(async (session, kind, status = "waiting") => {
109
125
  const notifyConfig = config.notifyTarget || "";
110
- if (!notifyConfig) return [];
111
126
  const [targetType, targetId] = notifyConfig.split(":");
112
- if (!targetId || targetType !== "guild" && targetType !== "private") return [];
127
+ if (!targetId || !session.bot) return [];
113
128
  try {
114
129
  const eventData = session.event?._data || {};
115
- const userInfo = session.userId ? await session.bot.getUser?.(session.userId)?.catch(() => null) : null;
116
- const groupInfo = kind !== "friend" && session.guildId ? await session.bot.getGuild?.(session.guildId)?.catch(() => null) : null;
117
- const adminId = eventData.operator_id || session.event.operator?.id;
118
- const adminInfo = adminId && session.userId && adminId !== session.userId ? await session.bot.getUser?.(adminId)?.catch(() => null) : null;
130
+ const userInfo = session.userId ? await session.bot.getUser?.(session.userId).catch(() => null) : null;
131
+ const groupInfo = kind !== "friend" && session.guildId ? await session.bot.getGuild?.(session.guildId).catch(() => null) : null;
132
+ const adminId = String(eventData.operator_id || session.event?.operator?.id || "");
133
+ const adminInfo = adminId && adminId !== session.userId ? await session.bot.getUser?.(adminId).catch(() => null) : null;
134
+ const typeMap = { friend: "好友申请", member: "加群请求", guild: "群组邀请", removed: eventData.sub_type === "kick_me" ? "移出群组" : "退出群组" };
135
+ const statusMap = { auto_pass: " [自动通过]", auto_reject: " [自动拒绝]", waiting: " [等待处理]" };
119
136
  const infoLines = [];
120
137
  if (userInfo?.avatar) infoLines.push(`<image url="${userInfo.avatar}"/>`);
121
- let typeName = "";
122
- if (kind === "friend") typeName = "好友申请";
123
- else if (kind === "member") typeName = "加群请求";
124
- else if (kind === "guild") typeName = "群组邀请";
125
- else if (kind === "removed") typeName = eventData.sub_type === "kick_me" ? "机器人被踢" : "机器人退群";
126
- let statusText = "";
127
- if (kind === "removed") {
128
- if (eventData.sub_type === "kick_me" && config.kickBan) statusText = " [自动清理]";
129
- } else {
130
- statusText = status === "auto_pass" ? " [自动通过]" : status === "auto_reject" ? " [自动拒绝]" : " [等待处理]";
131
- }
132
- infoLines.push(`类型:${typeName}${statusText}`);
133
- if (kind !== "guild" && kind !== "removed" || session.userId !== session.selfId) {
134
- infoLines.push(`用户:${userInfo?.name || session.userId}${session.userId ? `(${session.userId})` : ""}`);
135
- }
136
- if (adminInfo) infoLines.push(`管理:${adminInfo.name ? `${adminInfo.name}(${adminId})` : adminId}`);
137
- if (groupInfo) infoLines.push(`群组:${groupInfo.name ? `${groupInfo.name}(${session.guildId})` : session.guildId}`);
138
+ infoLines.push(`类型:${typeMap[kind] || "未知"}${kind === "removed" ? eventData.sub_type === "kick_me" && config.kickBan ? " [自动清理]" : "" : statusMap[status]}`);
139
+ if (kind !== "guild" && kind !== "removed" || session.userId && session.userId !== session.selfId) infoLines.push(`用户:${userInfo?.name || session.userId}${session.userId ? `(${session.userId})` : ""}`);
140
+ if (adminId) infoLines.push(`管理:${adminInfo?.name ? `${adminInfo.name}(${adminId})` : adminId}`);
141
+ if (session.guildId) infoLines.push(`群组:${groupInfo?.name ? `${groupInfo.name}(${session.guildId})` : session.guildId}`);
138
142
  if (eventData.comment) infoLines.push(`验证信息:${eventData.comment}`);
143
+ if (status === "waiting") infoLines.push(`使用"y/n"回复本消息,以同意/拒绝该请求`);
139
144
  const content = infoLines.join("\n");
140
- return await (targetType === "private" ? session.bot.sendPrivateMessage(targetId, content) : session.bot.sendMessage(targetId, content)) || [];
145
+ const msgIds = await (targetType === "private" ? session.bot.sendPrivateMessage(targetId, content) : session.bot.sendMessage(targetId, content)) || [];
146
+ return msgIds;
141
147
  } catch (error) {
142
148
  logger.error(`通知失败: ${error}`);
143
149
  return [];
144
150
  }
145
151
  }, "sendNotice");
146
- const checkCriteria = /* @__PURE__ */ __name(async (session, kind) => {
147
- const rawText = session.event?._data?.comment || "";
148
- const cleanLines = rawText.split(/[\r\n]+/).map((s) => s.trim()).filter((s) => /^(回答|答案)[::]/i.test(s)).map((s) => s.replace(/^(回答|答案)[::]\s*/i, ""));
149
- const verifyText = cleanLines.length > 0 ? cleanLines.join("\n") : rawText;
150
- if (kind === "friend") {
151
- try {
152
- if (config.friendRegex && new RegExp(config.friendRegex, "i").test(verifyText)) {
153
- if (config.debugMode) logger.info(`[规则匹配] 好友检查: ${config.friendRegex}`);
154
- return true;
155
- }
156
- const limitLevel = config.friendLevel ?? -1;
157
- if (limitLevel >= 0 && session.onebot && session.userId) {
158
- const stats = await session.onebot.getStrangerInfo(session.userId, true);
159
- const isPassed = (stats.qqLevel ?? 0) >= limitLevel;
160
- if (config.debugMode) logger.info(`[规则判定] 等级检查: ${stats.qqLevel} > ${limitLevel} = ${isPassed}`);
161
- if (!isPassed) return `QQ 等级低于 ${limitLevel} 级`;
162
- return true;
163
- }
164
- } catch {
165
- return false;
152
+ const handleSpecialRule = /* @__PURE__ */ __name(async (session, kind) => {
153
+ if (kind !== "member" || !config.specialRules || config.specialRules.length === 0) return false;
154
+ const rule = config.specialRules.find((r) => String(r.guildId) === String(session.guildId));
155
+ if (!rule) return false;
156
+ if (rule.mode === "vote") {
157
+ const [yesStr, noStr] = config.voteRatio.split(":");
158
+ const targetYes = parseInt(yesStr) || 0;
159
+ const targetNo = parseInt(noStr) || 0;
160
+ let msgIds = [];
161
+ if (config.syncNotify !== false) msgIds = await sendNotice(session, kind, "waiting");
162
+ if (msgIds.length > 0) {
163
+ const task = { session, kind, messages: msgIds, specialMode: "vote", voteTarget: { yes: targetYes, no: targetNo }, votes: { yes: /* @__PURE__ */ new Set(), no: /* @__PURE__ */ new Set() } };
164
+ msgIds.forEach((id) => activeTasks.set(id, task));
166
165
  }
167
- return false;
168
- }
169
- if (kind === "guild") {
170
- try {
171
- const userData = session.userId ? await ctx.database.getUser(session.platform, session.userId) : null;
172
- if (userData && userData.authority > 1) {
173
- if (config.debugMode) logger.info(`[规则匹配] 白名单: ${userData.authority}`);
174
- return true;
175
- }
176
- } catch {
177
- }
178
- if (session.onebot && session.guildId && ((config.minMembers ?? -1) >= 0 || (config.maxCapacity ?? -1) >= 0)) {
179
- try {
180
- const stats = await session.onebot.getGroupInfo(session.guildId, true);
181
- if ((config.minMembers ?? -1) >= 0 && stats.member_count < (config.minMembers ?? 0)) {
182
- if (config.debugMode) logger.info(`[规则判定] 成员检查: ${stats.member_count} < ${config.minMembers}`);
183
- return `群成员不足 ${config.minMembers} 人`;
184
- }
185
- if ((config.maxCapacity ?? -1) >= 0 && stats.max_member_count < (config.maxCapacity ?? 0)) {
186
- if (config.debugMode) logger.info(`[规则判定] 容量检查: ${stats.max_member_count} < ${config.maxCapacity}`);
187
- return `群容量不足 ${config.maxCapacity} 人`;
188
- }
189
- return true;
190
- } catch {
191
- return false;
192
- }
193
- }
194
- return false;
166
+ return true;
195
167
  }
196
168
  return false;
197
- }, "checkCriteria");
169
+ }, "handleSpecialRule");
198
170
  const setupManual = /* @__PURE__ */ __name(async (session, kind) => {
199
- const msgIds = await sendNotice(session, kind, "waiting");
200
- if (!msgIds?.length) return;
201
- const task = { session, kind, messages: msgIds };
202
- msgIds.forEach((id) => activeTasks.set(id, task));
203
171
  const waitMinutes = config.timeout ?? 0;
172
+ const action = kind === "member" ? config.verifyMode : config.timeoutAction;
204
173
  if (waitMinutes > 0) {
174
+ const msgIds = await sendNotice(session, kind, "waiting");
175
+ if (!msgIds?.length) return;
176
+ const task = { session, kind, messages: msgIds };
177
+ msgIds.forEach((id) => activeTasks.set(id, task));
205
178
  task.timer = setTimeout(async () => {
206
179
  if (!activeTasks.has(msgIds[0])) return;
207
180
  msgIds.forEach((id) => activeTasks.delete(id));
208
- const action = kind === "member" ? config.verifyMode : config.timeoutAction;
209
- if (action === "manual" || !action) return;
210
- const isPass = action === "accept";
211
- await executeAction(session, kind, isPass, isPass ? "" : "等待人工超时,自动拒绝");
212
- const notifyConfig = config.notifyTarget || "";
213
- const [targetType, targetId] = notifyConfig.split(":");
214
- if (targetId) {
181
+ const finalAction = kind === "member" ? config.verifyMode ?? "manual" : config.timeoutAction ?? "accept";
182
+ if (finalAction === "manual") return;
183
+ const isPass = finalAction === "accept";
184
+ await executeAction(session, kind, isPass, isPass ? "" : "等待超时,自动拒绝");
185
+ const [targetType, targetId] = (config.notifyTarget || "").split(":");
186
+ if (targetId && session.bot) {
215
187
  const statusText = `已自动${isPass ? "通过" : "拒绝"}该请求`;
216
- await (targetType === "private" ? session.bot.sendPrivateMessage(targetId, statusText) : session.bot.sendMessage(targetId, statusText));
188
+ await (targetType === "private" ? session.bot.sendPrivateMessage(targetId, statusText) : session.bot.sendMessage(targetId, statusText)).catch(() => {
189
+ });
217
190
  }
191
+ if (config.debugMode) logger.info(`[操作] 等待超时,默认${isPass ? "通过" : "拒绝"}`);
218
192
  }, waitMinutes * 6e4);
193
+ } else {
194
+ if (action === "manual" || !action) return await sendNotice(session, kind, "waiting");
195
+ const isPass = action === "accept";
196
+ await executeAction(session, kind, isPass, "等待超时,自动处理");
197
+ await sendNotice(session, kind, isPass ? "auto_pass" : "auto_reject");
198
+ if (config.debugMode) logger.info(`[操作] 无需等待,默认${isPass ? "通过" : "拒绝"}`);
219
199
  }
220
200
  }, "setupManual");
221
- const handleEvent = /* @__PURE__ */ __name(async (session, kind) => {
201
+ const hookEvent = /* @__PURE__ */ __name((kind) => async (session) => {
202
+ const eventData = session.event?._data || {};
203
+ if (eventData.user_id) session.userId = String(eventData.user_id);
204
+ if (eventData.group_id) session.guildId = String(eventData.group_id);
222
205
  try {
223
- if (config.debugMode) logger.info(`[收到请求] 类型: ${kind} 数据:${JSON.stringify(session.event?._data || {})}`);
206
+ if (config.debugMode) logger.info(`[收到请求] 类型: ${kind} 数据: ${JSON.stringify(session.event?._data || {})}`);
207
+ const verifyText = getComment(session.event?._data?.comment);
224
208
  if (kind === "member") {
209
+ if (await handleSpecialRule(session, kind)) return;
225
210
  const rules = config.verifyRules?.filter((r) => String(r.guildId) === String(session.guildId)) || [];
226
- if (rules.length > 0) {
227
- const rawText = session.event?._data?.comment || "";
228
- const cleanLines = rawText.split(/[\r\n]+/).map((s) => s.trim()).filter((s) => /^(回答|答案)[::]/i.test(s)).map((s) => s.replace(/^(回答|答案)[::]\s*/i, ""));
229
- const verifyText = cleanLines.length > 0 ? cleanLines.join("\n") : rawText;
230
- for (const rule of rules) {
231
- const stats = (rule.minLevel ?? -1) >= 0 && session.onebot && session.userId ? await session.onebot.getStrangerInfo(session.userId, true).catch(() => ({})) : null;
232
- const keywordMatch = !rule.keyword || new RegExp(rule.keyword, "i").test(verifyText);
233
- const levelMatch = !stats || (stats.qqLevel ?? 0) >= rule.minLevel;
234
- const isMatch = keywordMatch && levelMatch;
235
- if (config.debugMode) logger.info(`[规则判定] ${rule.guildId}: 关键词="${rule.keyword}"=${keywordMatch}; 等级="${rule.minLevel}"=${levelMatch}`);
236
- if (isMatch && rule.action) {
237
- const isApprove = rule.action === "accept";
238
- await executeAction(session, kind, isApprove, isApprove ? "" : "命中拒绝规则,自动拒绝");
239
- await sendNotice(session, kind, isApprove ? "auto_pass" : "auto_reject");
240
- return;
241
- }
211
+ for (const rule of rules) {
212
+ const minL = rule.minLevel ?? 0;
213
+ const stats = minL > 0 && session.onebot && session.userId ? await session.onebot.getStrangerInfo(session.userId, true).catch(() => ({})) : null;
214
+ const keywordMatch = !rule.keyword || new RegExp(rule.keyword, "i").test(verifyText);
215
+ const levelMatch = !stats || (stats.qqLevel ?? 0) >= minL;
216
+ if (config.debugMode) {
217
+ if (rule.keyword) logger.info(`[加群验证] 内容 "${verifyText}" ${keywordMatch ? "匹配" : "不匹配"}正则 "${rule.keyword}"`);
218
+ if (stats) logger.info(`[加群验证] 用户 ${session.userId} 等级 ${stats.qqLevel ?? 0}${levelMatch ? ">" : "<"}${minL}`);
219
+ }
220
+ if (keywordMatch && levelMatch && rule.action) {
221
+ const isApprove = rule.action === "accept";
222
+ await executeAction(session, kind, isApprove, isApprove ? "" : "命中规则,自动拒绝");
223
+ await sendNotice(session, kind, isApprove ? "auto_pass" : "auto_reject");
224
+ return;
242
225
  }
243
226
  }
227
+ if (config.verifyMode && config.verifyMode !== "manual") {
228
+ const isApprove = config.verifyMode === "accept";
229
+ await executeAction(session, kind, isApprove, "等待超时,自动处理");
230
+ await sendNotice(session, kind, isApprove ? "auto_pass" : "auto_reject");
231
+ return;
232
+ }
244
233
  return await setupManual(session, kind);
245
234
  }
246
- const verdict = await checkCriteria(session, kind);
235
+ let verdict = false;
236
+ if (kind === "friend") {
237
+ if (config.friendRegex) {
238
+ const isRegexMatched = new RegExp(config.friendRegex, "i").test(verifyText);
239
+ if (config.debugMode) logger.info(`[好友验证] 内容 "${verifyText}" ${isRegexMatched ? "匹配" : "不匹配"}正则 "${config.friendRegex}" `);
240
+ if (isRegexMatched) verdict = true;
241
+ }
242
+ if (verdict !== true) {
243
+ const fLevel = config.friendLevel ?? 0;
244
+ if (fLevel > 0 && session.onebot && session.userId) {
245
+ const stats = await session.onebot.getStrangerInfo(session.userId, true).catch(() => ({}));
246
+ const isLevelMatched = (stats.qqLevel ?? 0) >= fLevel;
247
+ if (config.debugMode) logger.info(`[好友验证] 用户 ${session.userId} 等级 ${stats.qqLevel ?? 0}${isLevelMatched ? ">" : "<"}${fLevel}`);
248
+ if (isLevelMatched) verdict = true;
249
+ }
250
+ }
251
+ } else if (kind === "guild") {
252
+ if (ctx.database && session.userId) {
253
+ const userData = await ctx.database.getUser(session.platform, session.userId, ["authority"]).catch(() => null);
254
+ if (userData && userData.authority > 3) {
255
+ if (config.debugMode) logger.info(`[群组邀请] 用户 ${session.userId} 权限 ${userData.authority}>3`);
256
+ verdict = true;
257
+ }
258
+ }
259
+ if (verdict !== true && session.onebot && session.guildId) {
260
+ const stats = await session.onebot.getGroupInfo(session.guildId, true).catch(() => ({}));
261
+ const minM = config.minMembers ?? 0;
262
+ const maxC = config.maxCapacity ?? 0;
263
+ if (minM > 0 && (stats.member_count ?? 0) < minM) {
264
+ verdict = `群成员不足 ${minM} 人`;
265
+ if (config.debugMode) logger.info(`[群组邀请] 群组 ${session.guildId} 成员数 ${stats.member_count ?? 0}<${minM}`);
266
+ } else if (maxC > 0 && (stats.max_member_count ?? 0) < maxC) {
267
+ verdict = `群容量不足 ${maxC} 人`;
268
+ if (config.debugMode) logger.info(`[群组邀请] 群组 ${session.guildId} 受邀容量 ${stats.max_member_count ?? 0}<${maxC}`);
269
+ } else {
270
+ verdict = minM > 0 || maxC > 0;
271
+ if (config.debugMode && verdict) logger.info(`[群组邀请] 群组 ${session.guildId} 成员数 ${stats.member_count ?? 0}>${minM},受邀容量 ${stats.max_member_count ?? 0}>${maxC}`);
272
+ }
273
+ }
274
+ }
247
275
  if (verdict === true) {
248
276
  await executeAction(session, kind, true);
249
277
  await sendNotice(session, kind, "auto_pass");
@@ -256,48 +284,79 @@ function apply(ctx, config = {}) {
256
284
  } catch (error) {
257
285
  logger.error(`处理失败: ${error}`);
258
286
  }
259
- }, "handleEvent");
260
- const hookEvent = /* @__PURE__ */ __name((kind) => async (session) => {
261
- const eventData = session.event?._data || {};
262
- session.userId = eventData.user_id;
263
- if (kind !== "friend") session.guildId = eventData.group_id;
264
- await handleEvent(session, kind);
265
287
  }, "hookEvent");
288
+ const handleSpecialVote = /* @__PURE__ */ __name(async (session, task, isApprove, extraInfo, targetType, targetId) => {
289
+ if (!task.voteTarget || !task.votes) return;
290
+ const voterId = session.userId;
291
+ if (!voterId) return;
292
+ task.votes.yes.delete(voterId);
293
+ task.votes.no.delete(voterId);
294
+ if (isApprove) {
295
+ task.votes.yes.add(voterId);
296
+ } else {
297
+ task.votes.no.add(voterId);
298
+ }
299
+ if (config.debugMode) logger.info(`[投票] 赞成: ${task.votes.yes.size}/${task.voteTarget.yes} | 反对: ${task.votes.no.size}/${task.voteTarget.no}`);
300
+ let thresholdMet = false;
301
+ let finalVerdict = false;
302
+ if (task.voteTarget.yes > 0 && task.votes.yes.size >= task.voteTarget.yes) {
303
+ thresholdMet = true;
304
+ finalVerdict = true;
305
+ } else if (task.voteTarget.no > 0 && task.votes.no.size >= task.voteTarget.no) {
306
+ thresholdMet = true;
307
+ finalVerdict = false;
308
+ }
309
+ if (!thresholdMet) return;
310
+ task.messages.forEach((msg) => activeTasks.delete(msg));
311
+ const isSuccess = await executeAction(task.session, task.kind, finalVerdict, finalVerdict ? "" : extraInfo);
312
+ const replyText = isSuccess ? `已${finalVerdict ? "通过" : "拒绝"}该投票` : `处理投票失败`;
313
+ if (session.bot) await (targetType === "private" ? session.bot.sendPrivateMessage(targetId, replyText) : session.bot.sendMessage(targetId, replyText)).catch(() => {
314
+ });
315
+ }, "handleSpecialVote");
266
316
  ctx.on("friend-request", hookEvent("friend"));
267
317
  ctx.on("guild-request", hookEvent("guild"));
268
318
  ctx.on("guild-member-request", hookEvent("member"));
269
319
  ctx.on("guild-added", hookEvent("guild"));
270
320
  ctx.on("guild-removed", async (session) => {
271
- const subType = session.event?._data?.sub_type;
272
- if (subType === "kick_me") {
273
- const inviterId = inviterMap.get(session.guildId);
274
- if (inviterId) {
275
- await session.onebot?.deleteFriend(inviterId);
276
- inviterMap.delete(session.guildId);
321
+ if (session.event?._data?.sub_type === "kick_me") {
322
+ const gid = session.guildId;
323
+ if (gid) {
324
+ const inviterId = inviterMap.get(gid);
325
+ if (inviterId) {
326
+ await session.onebot?.deleteFriend(inviterId).catch(() => {
327
+ });
328
+ inviterMap.delete(gid);
329
+ if (config.debugMode) logger.info(`[操作] 删除好友: ${inviterId}`);
330
+ }
331
+ await session.execute(`analyse.clear -g ${gid}`).catch(() => {
332
+ });
277
333
  }
278
- await session.execute(`analyse.clear -g ${session.guildId}`).catch(() => {
279
- });
280
334
  }
281
335
  await sendNotice(session, "removed");
282
336
  });
283
337
  ctx.middleware(async (session, next) => {
284
338
  if (typeof session.content !== "string" || !session.quote?.id) return next();
285
- const activeTask = activeTasks.get(session.quote.id);
286
- if (!activeTask) return next();
287
- const notifyConfig = config.notifyTarget || "";
288
- const [targetType, targetId] = notifyConfig.split(":");
289
- if (targetType === "private" ? session.userId !== targetId : session.guildId !== targetId) return next();
339
+ const task = activeTasks.get(session.quote.id);
340
+ if (!task) return next();
341
+ const [targetType, targetId] = (config.notifyTarget || "").split(":");
342
+ const isMatched = targetType === "private" ? session.userId === targetId : session.guildId === targetId;
343
+ if (!isMatched) return next();
290
344
  const input = session.content.replace(/<(quote|at)\s+[^>]*\/>/gi, "").trim();
291
- const cmdMatch = input.match(/^(y|n|通过|拒绝)(?:\s+(.*))?$/);
345
+ const cmdMatch = input.match(/^(y|n|通过|拒绝)(?:\s+(.*))?$/i);
292
346
  if (!cmdMatch) return next();
293
- if (activeTask.timer) clearTimeout(activeTask.timer);
294
- activeTask.messages.forEach((msg) => activeTasks.delete(msg));
295
- const isApprove = cmdMatch[1] === "y" || cmdMatch[1] === "通过";
347
+ const isApprove = ["y", "通过"].includes(cmdMatch[1].toLowerCase());
296
348
  const extraInfo = cmdMatch[2]?.trim() || "";
297
- if (config.debugMode) logger.info(`[人工回复] 用户 ${session.userId} 回复: ${cmdMatch[1]} 备注: ${extraInfo}`);
298
- const isSuccess = await executeAction(activeTask.session, activeTask.kind, isApprove, isApprove ? "" : extraInfo, isApprove && activeTask.kind === "friend" ? extraInfo : "");
349
+ if (config.debugMode) logger.info(`[操作] 收到指令: ${isApprove ? "同意" : "拒绝"}`);
350
+ if (task.specialMode === "vote") {
351
+ await handleSpecialVote(session, task, isApprove, extraInfo, targetType, targetId);
352
+ return;
353
+ }
354
+ if (task.timer) clearTimeout(task.timer);
355
+ task.messages.forEach((msg) => activeTasks.delete(msg));
356
+ const isSuccess = await executeAction(task.session, task.kind, isApprove, isApprove ? "" : extraInfo, isApprove && task.kind === "friend" ? extraInfo : "");
299
357
  const replyText = isSuccess ? `已${isApprove ? "通过" : "拒绝"}该请求` : `处理请求失败`;
300
- await (targetType === "private" ? session.bot.sendPrivateMessage(targetId, replyText) : session.bot.sendMessage(targetId, replyText));
358
+ if (session.bot) await (targetType === "private" ? session.bot.sendPrivateMessage(targetId, replyText) : session.bot.sendMessage(targetId, replyText)).catch(() => {
359
+ });
301
360
  });
302
361
  }
303
362
  __name(apply, "apply");
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-onebot-verifier",
3
3
  "description": "适用于 Onebot 的审核插件,支持自动审核好友/加群/邀请请求",
4
- "version": "1.0.8",
4
+ "version": "1.0.10",
5
5
  "contributors": [
6
6
  "Yis_Rime <yis_rime@outlook.com>"
7
7
  ],