mioku-plugin-admin 2.3.5 → 3.0.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/commands/group.ts +34 -54
- package/commands/notice.ts +3 -3
- package/commands/personal.ts +112 -184
- package/commands/verify.ts +43 -35
- package/config.ts +29 -17
- package/index.ts +5 -5
- package/notify/index.ts +108 -138
- package/notify/welcome.ts +52 -44
- package/package.json +3 -5
- package/skills/group.ts +87 -143
- package/skills/message-image.ts +8 -9
- package/skills/personal.ts +72 -98
- package/verify/chiral.ts +4 -4
- package/verify/index.ts +59 -45
- package/verify/number.ts +4 -4
- package/verify/reaction.ts +62 -14
- package/verify/state.ts +1 -1
- package/verify/types.ts +18 -8
package/skills/personal.ts
CHANGED
|
@@ -1,18 +1,30 @@
|
|
|
1
|
-
import type { AISkill } from "mioku";
|
|
1
|
+
import type { AISkill, Bot, MessageEvent } from "mioku";
|
|
2
|
+
|
|
2
3
|
import { getImageUrlByMessageId } from "./message-image";
|
|
3
4
|
|
|
5
|
+
import type { MessageSegment } from "mioku";
|
|
6
|
+
|
|
7
|
+
interface SkillRuntimeContext {
|
|
8
|
+
ctx?: {
|
|
9
|
+
segment: { text(text: string): MessageSegment };
|
|
10
|
+
logger?: { error?: (...args: unknown[]) => void };
|
|
11
|
+
};
|
|
12
|
+
event?: MessageEvent;
|
|
13
|
+
rawEvent?: MessageEvent;
|
|
14
|
+
}
|
|
15
|
+
|
|
4
16
|
function parseProfileSex(value: string): 0 | 1 | 2 {
|
|
5
|
-
const normalized = String(value || "")
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
if (normalized === "男" || normalized === "male" || normalized === "1")
|
|
9
|
-
return 1;
|
|
10
|
-
if (normalized === "女" || normalized === "female" || normalized === "2")
|
|
11
|
-
return 2;
|
|
17
|
+
const normalized = String(value || "").trim().toLowerCase();
|
|
18
|
+
if (normalized === "男" || normalized === "male" || normalized === "1") return 1;
|
|
19
|
+
if (normalized === "女" || normalized === "female" || normalized === "2") return 2;
|
|
12
20
|
return 0;
|
|
13
21
|
}
|
|
14
22
|
|
|
15
|
-
function logAdminSkillError(
|
|
23
|
+
function logAdminSkillError(
|
|
24
|
+
runtimeCtx: SkillRuntimeContext | undefined,
|
|
25
|
+
toolName: string,
|
|
26
|
+
err: unknown,
|
|
27
|
+
) {
|
|
16
28
|
runtimeCtx?.ctx?.logger?.error?.(
|
|
17
29
|
`[admin skills] ${toolName} failed: ${String(err)}`,
|
|
18
30
|
);
|
|
@@ -51,10 +63,7 @@ const personalSkill: AISkill = {
|
|
|
51
63
|
type: "number",
|
|
52
64
|
description: "包含图片的消息 message_id,仅 set_avatar 需要",
|
|
53
65
|
},
|
|
54
|
-
nickname: {
|
|
55
|
-
type: "string",
|
|
56
|
-
description: "新昵称,仅 set_nickname 需要",
|
|
57
|
-
},
|
|
66
|
+
nickname: { type: "string", description: "新昵称,仅 set_nickname 需要" },
|
|
58
67
|
personal_note: {
|
|
59
68
|
type: "string",
|
|
60
69
|
description: "新个性签名,仅 set_signature 需要",
|
|
@@ -79,94 +88,72 @@ const personalSkill: AISkill = {
|
|
|
79
88
|
},
|
|
80
89
|
required: ["action"],
|
|
81
90
|
},
|
|
82
|
-
handler: async (args:
|
|
91
|
+
handler: async (args: unknown, runtimeCtx?: SkillRuntimeContext) => {
|
|
83
92
|
const ctx = runtimeCtx?.ctx;
|
|
84
93
|
if (!ctx) return { error: "无法获取上下文" };
|
|
85
|
-
const event = runtimeCtx?.event
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
const bot = ctx.pickBot(selfId);
|
|
94
|
+
const event = runtimeCtx?.event ?? runtimeCtx?.rawEvent;
|
|
95
|
+
if (!event) return { error: "无法获取事件" };
|
|
96
|
+
const bot = event.bot;
|
|
89
97
|
if (!bot) return { error: "Bot不可用" };
|
|
90
|
-
const
|
|
98
|
+
const a = args as Record<string, unknown>;
|
|
99
|
+
const action = String(a?.action ?? "");
|
|
91
100
|
|
|
92
101
|
try {
|
|
93
102
|
switch (action) {
|
|
94
103
|
case "set_avatar": {
|
|
95
|
-
const messageId = Number(
|
|
104
|
+
const messageId = Number(a?.message_id);
|
|
96
105
|
if (!Number.isFinite(messageId) || messageId <= 0) {
|
|
97
106
|
return { error: "set_avatar 需要提供有效的 message_id" };
|
|
98
107
|
}
|
|
99
108
|
const imageUrl = await getImageUrlByMessageId(bot, messageId);
|
|
100
|
-
if (!imageUrl) {
|
|
101
|
-
|
|
102
|
-
}
|
|
103
|
-
await bot.api("set_qq_avatar", { file: imageUrl });
|
|
109
|
+
if (!imageUrl) return { error: "指定 message_id 中未找到图片" };
|
|
110
|
+
await bot.setAvatar(imageUrl);
|
|
104
111
|
return { success: true, message: "Bot头像已修改" };
|
|
105
112
|
}
|
|
106
113
|
case "set_nickname": {
|
|
107
|
-
const nickname = String(
|
|
108
|
-
if (!nickname) {
|
|
109
|
-
|
|
110
|
-
}
|
|
111
|
-
await bot.api("set_qq_profile", { nickname });
|
|
112
|
-
return {
|
|
113
|
-
success: true,
|
|
114
|
-
message: `Bot昵称已修改为: ${nickname}`,
|
|
115
|
-
};
|
|
114
|
+
const nickname = String(a?.nickname ?? "").trim();
|
|
115
|
+
if (!nickname) return { error: "set_nickname 需要提供 nickname" };
|
|
116
|
+
await bot.setProfile({ nickname });
|
|
117
|
+
return { success: true, message: `Bot昵称已修改为: ${nickname}` };
|
|
116
118
|
}
|
|
117
119
|
case "set_signature": {
|
|
118
|
-
const personalNote = String(
|
|
119
|
-
if (!personalNote) {
|
|
120
|
-
|
|
121
|
-
}
|
|
122
|
-
await bot.api("set_qq_profile", { personal_note: personalNote });
|
|
120
|
+
const personalNote = String(a?.personal_note ?? "").trim();
|
|
121
|
+
if (!personalNote) return { error: "set_signature 需要提供 personal_note" };
|
|
122
|
+
await bot.setProfile({ personal_note: personalNote });
|
|
123
123
|
return { success: true, message: "Bot个性签名已修改" };
|
|
124
124
|
}
|
|
125
125
|
case "set_gender": {
|
|
126
|
-
const sex = parseProfileSex(
|
|
127
|
-
await bot.
|
|
128
|
-
const genderMap: Record<number, string> = {
|
|
129
|
-
|
|
130
|
-
1: "男",
|
|
131
|
-
2: "女",
|
|
132
|
-
};
|
|
133
|
-
return {
|
|
134
|
-
success: true,
|
|
135
|
-
message: `Bot性别已修改为: ${genderMap[sex]}`,
|
|
136
|
-
};
|
|
126
|
+
const sex = parseProfileSex(String(a?.gender ?? ""));
|
|
127
|
+
await bot.setProfile({ sex });
|
|
128
|
+
const genderMap: Record<number, string> = { 0: "无", 1: "男", 2: "女" };
|
|
129
|
+
return { success: true, message: `Bot性别已修改为: ${genderMap[sex]}` };
|
|
137
130
|
}
|
|
138
131
|
case "send_private": {
|
|
139
|
-
const userId = Number(
|
|
140
|
-
const content = String(
|
|
141
|
-
if (!userId || !content) {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
message: `已发送私聊消息给 ${userId}`,
|
|
148
|
-
};
|
|
132
|
+
const userId = Number(a?.user_id);
|
|
133
|
+
const content = String(a?.content ?? "");
|
|
134
|
+
if (!userId || !content) return { error: "send_private 需要提供 user_id 和 content" };
|
|
135
|
+
await bot.sendMessage(
|
|
136
|
+
{ type: "private", user_id: userId},
|
|
137
|
+
[ctx.segment.text(content)],
|
|
138
|
+
);
|
|
139
|
+
return { success: true, message: `已发送私聊消息给 ${userId}` };
|
|
149
140
|
}
|
|
150
141
|
case "send_group": {
|
|
151
|
-
const groupId = Number(
|
|
152
|
-
const content = String(
|
|
153
|
-
if (!groupId || !content) {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
message: `已发送群消息到 ${groupId}`,
|
|
160
|
-
};
|
|
142
|
+
const groupId = Number(a?.group_id);
|
|
143
|
+
const content = String(a?.content ?? "");
|
|
144
|
+
if (!groupId || !content) return { error: "send_group 需要提供 group_id 和 content" };
|
|
145
|
+
await bot.sendMessage(
|
|
146
|
+
{ type: "group", group_id: groupId},
|
|
147
|
+
[ctx.segment.text(content)],
|
|
148
|
+
);
|
|
149
|
+
return { success: true, message: `已发送群消息到 ${groupId}` };
|
|
161
150
|
}
|
|
162
151
|
case "list_friends": {
|
|
163
|
-
if (event?.message_type === "group") {
|
|
164
|
-
|
|
165
|
-
}
|
|
166
|
-
const friendList: any[] = await bot.api("get_friend_list");
|
|
152
|
+
if (event?.message_type === "group") return { error: "在私聊使用试试看吧~" };
|
|
153
|
+
const friendList = await bot.getFriendList();
|
|
167
154
|
if (!Array.isArray(friendList)) return { friends: [] };
|
|
168
155
|
return {
|
|
169
|
-
friends: friendList.map((f) => ({
|
|
156
|
+
friends: friendList.map((f: { user_id?: unknown; nickname?: unknown; remark?: unknown }) => ({
|
|
170
157
|
user_id: f.user_id,
|
|
171
158
|
nickname: f.nickname,
|
|
172
159
|
remark: f.remark,
|
|
@@ -174,13 +161,11 @@ const personalSkill: AISkill = {
|
|
|
174
161
|
};
|
|
175
162
|
}
|
|
176
163
|
case "list_groups": {
|
|
177
|
-
if (event?.message_type === "group") {
|
|
178
|
-
|
|
179
|
-
}
|
|
180
|
-
const groupList: any[] = await bot.api("get_group_list");
|
|
164
|
+
if (event?.message_type === "group") return { error: "在私聊使用试试看吧~" };
|
|
165
|
+
const groupList = await bot.getGroupList();
|
|
181
166
|
if (!Array.isArray(groupList)) return { groups: [] };
|
|
182
167
|
return {
|
|
183
|
-
groups: groupList.map((g) => ({
|
|
168
|
+
groups: groupList.map((g: { group_id?: unknown; group_name?: unknown; member_count?: unknown }) => ({
|
|
184
169
|
group_id: g.group_id,
|
|
185
170
|
group_name: g.group_name,
|
|
186
171
|
member_count: g.member_count,
|
|
@@ -188,33 +173,22 @@ const personalSkill: AISkill = {
|
|
|
188
173
|
};
|
|
189
174
|
}
|
|
190
175
|
case "delete_friend": {
|
|
191
|
-
const userId = Number(
|
|
192
|
-
if (!userId) {
|
|
193
|
-
|
|
194
|
-
}
|
|
195
|
-
await bot.api("delete_friend", { user_id: userId });
|
|
176
|
+
const userId = Number(a?.user_id);
|
|
177
|
+
if (!userId) return { error: "delete_friend 需要提供 user_id" };
|
|
178
|
+
await bot.deleteFriend(userId);
|
|
196
179
|
return { success: true, message: `已删除好友 ${userId}` };
|
|
197
180
|
}
|
|
198
181
|
case "leave_group": {
|
|
199
|
-
const groupId = Number(
|
|
200
|
-
if (!groupId) {
|
|
201
|
-
|
|
202
|
-
}
|
|
203
|
-
await bot.api("set_group_leave", {
|
|
204
|
-
group_id: groupId,
|
|
205
|
-
is_dismiss: false,
|
|
206
|
-
});
|
|
182
|
+
const groupId = Number(a?.group_id);
|
|
183
|
+
if (!groupId) return { error: "leave_group 需要提供 group_id" };
|
|
184
|
+
await bot.leaveGroup(groupId, false);
|
|
207
185
|
return { success: true, message: `已退出群 ${groupId}` };
|
|
208
186
|
}
|
|
209
187
|
default:
|
|
210
188
|
return { error: `未知的 action: ${action}` };
|
|
211
189
|
}
|
|
212
190
|
} catch (err) {
|
|
213
|
-
logAdminSkillError(
|
|
214
|
-
runtimeCtx,
|
|
215
|
-
`admin_personal.manage_personal.${action}`,
|
|
216
|
-
err,
|
|
217
|
-
);
|
|
191
|
+
logAdminSkillError(runtimeCtx, `admin_personal.manage_personal.${action}`, err);
|
|
218
192
|
return { error: `执行 ${action} 失败: ${err}` };
|
|
219
193
|
}
|
|
220
194
|
},
|
|
@@ -222,4 +196,4 @@ const personalSkill: AISkill = {
|
|
|
222
196
|
],
|
|
223
197
|
};
|
|
224
198
|
|
|
225
|
-
export default personalSkill;
|
|
199
|
+
export default personalSkill;
|
package/verify/chiral.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { MiokuContext } from "mioku";
|
|
2
2
|
import type { VerifyConfig } from "./config";
|
|
3
3
|
import type { PendingVerify } from "./types";
|
|
4
4
|
|
|
@@ -41,11 +41,11 @@ async function fetchChiralCaptcha(
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
export async function prepareChiral(
|
|
44
|
-
ctx:
|
|
44
|
+
ctx: MiokuContext,
|
|
45
45
|
cfg: VerifyConfig,
|
|
46
46
|
p: PendingVerify,
|
|
47
47
|
): Promise<boolean> {
|
|
48
|
-
const bot =
|
|
48
|
+
const bot = p.bot;
|
|
49
49
|
if (!bot) return false;
|
|
50
50
|
try {
|
|
51
51
|
const captcha = await fetchChiralCaptcha(cfg.chiralApiUrl, cfg.chiralDifficulty);
|
|
@@ -55,7 +55,7 @@ export async function prepareChiral(
|
|
|
55
55
|
"{count}",
|
|
56
56
|
String(captcha.regions.length),
|
|
57
57
|
);
|
|
58
|
-
await bot.
|
|
58
|
+
await bot.sendMessage({ type: "group", group_id: p.groupId}, [
|
|
59
59
|
ctx.segment.at(String(p.userId)),
|
|
60
60
|
ctx.segment.text(` ${prompt}`),
|
|
61
61
|
ctx.segment.image(captcha.imageDataUrl),
|
package/verify/index.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Bot, MiokuContext, RouteEvent } from "mioku";
|
|
2
|
+
|
|
2
3
|
import { existsSync } from "fs";
|
|
3
4
|
import { getMemberRole } from "../config";
|
|
4
5
|
import { resolveMemberName, triggerSingleWelcome } from "../notify/welcome";
|
|
@@ -50,21 +51,17 @@ export function createVerifyController(
|
|
|
50
51
|
}
|
|
51
52
|
}
|
|
52
53
|
|
|
53
|
-
async function recallMessage(bot:
|
|
54
|
+
async function recallMessage(bot: Bot, messageId: number) {
|
|
54
55
|
try {
|
|
55
|
-
await bot.
|
|
56
|
+
await bot.recallMessage(messageId);
|
|
56
57
|
} catch (err) {
|
|
57
58
|
ctx.logger.warn(`admin verify 撤回消息失败: ${err}`);
|
|
58
59
|
}
|
|
59
60
|
}
|
|
60
61
|
|
|
61
|
-
async function kickMember(bot:
|
|
62
|
+
async function kickMember(bot: Bot, groupId: number, userId: number) {
|
|
62
63
|
try {
|
|
63
|
-
await bot.
|
|
64
|
-
group_id: groupId,
|
|
65
|
-
user_id: userId,
|
|
66
|
-
reject_add_request: false,
|
|
67
|
-
});
|
|
64
|
+
await bot.kickMember(groupId, userId, false);
|
|
68
65
|
} catch (err) {
|
|
69
66
|
ctx.logger.warn(`admin verify 踢出成员失败: ${err}`);
|
|
70
67
|
}
|
|
@@ -78,20 +75,32 @@ export function createVerifyController(
|
|
|
78
75
|
}
|
|
79
76
|
}
|
|
80
77
|
|
|
81
|
-
async function passVerification(p: PendingVerify) {
|
|
78
|
+
async function passVerification(p: PendingVerify, bot?: Bot) {
|
|
82
79
|
if (p.passed) return;
|
|
83
80
|
p.passed = true;
|
|
84
81
|
clearTimers(p);
|
|
85
82
|
pending.delete(pendingKey(p.selfId, p.groupId, p.userId));
|
|
86
83
|
|
|
87
|
-
if (
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
84
|
+
if (
|
|
85
|
+
p.mode === "reaction" &&
|
|
86
|
+
p.promptMessageId &&
|
|
87
|
+
bot &&
|
|
88
|
+
(bot.adapter === "onebotv11" || bot.adapter === "icqq")
|
|
89
|
+
) {
|
|
90
|
+
try {
|
|
91
|
+
if (bot.adapter === "onebotv11") {
|
|
92
|
+
// onebot:set_msg_emoji_like 专属 action
|
|
93
|
+
await bot.sendApi("set_msg_emoji_like", {
|
|
94
|
+
message_id: p.promptMessageId,
|
|
95
|
+
emoji_id: PASS_REACTION_EMOJI_ID,
|
|
96
|
+
set: true,
|
|
97
|
+
});
|
|
98
|
+
} else {
|
|
99
|
+
// icqq:Group.setReaction(0x9082)
|
|
100
|
+
await bot.setReaction(p.promptMessageId, PASS_REACTION_EMOJI_ID, true);
|
|
94
101
|
}
|
|
102
|
+
} catch (err) {
|
|
103
|
+
ctx.logger.warn(`admin verify 通过表态失败: ${err}`);
|
|
95
104
|
}
|
|
96
105
|
}
|
|
97
106
|
|
|
@@ -102,7 +111,7 @@ export function createVerifyController(
|
|
|
102
111
|
groupId: p.groupId,
|
|
103
112
|
userId: p.userId,
|
|
104
113
|
groupName: p.groupName,
|
|
105
|
-
});
|
|
114
|
+
}, bot);
|
|
106
115
|
if (customSent) return;
|
|
107
116
|
await triggerSingleWelcome({
|
|
108
117
|
ctx,
|
|
@@ -120,33 +129,33 @@ export function createVerifyController(
|
|
|
120
129
|
}
|
|
121
130
|
}
|
|
122
131
|
|
|
123
|
-
async function failKick(p: PendingVerify, reason: string) {
|
|
132
|
+
async function failKick(p: PendingVerify, reason: string, bot?: Bot) {
|
|
124
133
|
clearTimers(p);
|
|
125
134
|
pending.delete(pendingKey(p.selfId, p.groupId, p.userId));
|
|
126
135
|
const cfg = getVerifyConfig();
|
|
127
136
|
if (!cfg.kickOnFail) return;
|
|
128
|
-
const
|
|
129
|
-
if (!
|
|
137
|
+
const target = bot ?? p.bot;
|
|
138
|
+
if (!target) return;
|
|
130
139
|
ctx.logger.info(
|
|
131
140
|
`admin verify 踢出群 ${p.groupId} 用户 ${p.userId}(${reason})`,
|
|
132
141
|
);
|
|
133
|
-
await kickMember(
|
|
142
|
+
await kickMember(target, p.groupId, p.userId);
|
|
134
143
|
}
|
|
135
144
|
|
|
136
|
-
async function timeoutExpire(p: PendingVerify) {
|
|
145
|
+
async function timeoutExpire(p: PendingVerify, bot?: Bot) {
|
|
137
146
|
p.timeoutTimer = null;
|
|
138
147
|
const cfg = getVerifyConfig();
|
|
139
148
|
if (!cfg.kickOnTimeout) {
|
|
140
149
|
pending.delete(pendingKey(p.selfId, p.groupId, p.userId));
|
|
141
150
|
return;
|
|
142
151
|
}
|
|
143
|
-
const
|
|
144
|
-
if (!
|
|
152
|
+
const target = bot ?? p.bot;
|
|
153
|
+
if (!target) {
|
|
145
154
|
pending.delete(pendingKey(p.selfId, p.groupId, p.userId));
|
|
146
155
|
return;
|
|
147
156
|
}
|
|
148
157
|
try {
|
|
149
|
-
await
|
|
158
|
+
await target.sendMessage({ type: "group", group_id: p.groupId}, [
|
|
150
159
|
ctx.segment.at(String(p.userId)),
|
|
151
160
|
ctx.segment.text(" 验证超时啦,下次再来哦~"),
|
|
152
161
|
]);
|
|
@@ -154,25 +163,25 @@ export function createVerifyController(
|
|
|
154
163
|
ctx.logger.warn(`admin verify 超时提示发送失败: ${err}`);
|
|
155
164
|
}
|
|
156
165
|
ctx.logger.info(`admin verify 超时踢出群 ${p.groupId} 用户 ${p.userId}`);
|
|
157
|
-
await kickMember(
|
|
166
|
+
await kickMember(target, p.groupId, p.userId);
|
|
158
167
|
pending.delete(pendingKey(p.selfId, p.groupId, p.userId));
|
|
159
168
|
}
|
|
160
169
|
|
|
161
170
|
async function startVerification(
|
|
162
171
|
info: MemberJoinInfo,
|
|
172
|
+
bot?: Bot,
|
|
163
173
|
skipDelay = false,
|
|
164
174
|
): Promise<boolean> {
|
|
165
175
|
const cfg = getVerifyConfig();
|
|
166
176
|
const groupCfg = getGroupVerifyConfig(cfg, info.groupId);
|
|
167
177
|
if (!groupCfg.enabled) return false;
|
|
168
178
|
|
|
169
|
-
const bot = ctx.pickBot(info.selfId);
|
|
170
179
|
if (!bot) return false;
|
|
171
180
|
|
|
172
181
|
const botRole = await getMemberRole(bot, info.groupId, info.selfId);
|
|
173
182
|
if (botRole !== "owner" && botRole !== "admin") {
|
|
174
183
|
try {
|
|
175
|
-
await bot.
|
|
184
|
+
await bot.sendMessage({ type: "group", group_id: info.groupId}, [
|
|
176
185
|
ctx.segment.text("我在不是管理员,没法入群验证啦,本群验证已关闭~"),
|
|
177
186
|
]);
|
|
178
187
|
} catch (err) {
|
|
@@ -188,9 +197,9 @@ export function createVerifyController(
|
|
|
188
197
|
const mode = groupCfg.mode;
|
|
189
198
|
const memberName = await resolveMemberName(
|
|
190
199
|
ctx,
|
|
200
|
+
bot,
|
|
191
201
|
info.groupId,
|
|
192
202
|
info.userId,
|
|
193
|
-
info.selfId,
|
|
194
203
|
);
|
|
195
204
|
|
|
196
205
|
const entry: PendingVerify = {
|
|
@@ -200,6 +209,7 @@ export function createVerifyController(
|
|
|
200
209
|
memberName,
|
|
201
210
|
groupName: info.groupName,
|
|
202
211
|
mode,
|
|
212
|
+
bot,
|
|
203
213
|
invalidCount: 0,
|
|
204
214
|
startedAt: Date.now(),
|
|
205
215
|
passed: false,
|
|
@@ -232,7 +242,7 @@ export function createVerifyController(
|
|
|
232
242
|
|
|
233
243
|
entry.timeoutTimer = setTimeout(
|
|
234
244
|
() => {
|
|
235
|
-
void timeoutExpire(entry);
|
|
245
|
+
void timeoutExpire(entry, bot);
|
|
236
246
|
},
|
|
237
247
|
Math.max(1000, cfg.verifyTimeoutMs),
|
|
238
248
|
);
|
|
@@ -240,7 +250,7 @@ export function createVerifyController(
|
|
|
240
250
|
return true;
|
|
241
251
|
}
|
|
242
252
|
|
|
243
|
-
async function onGroupMessage(event:
|
|
253
|
+
async function onGroupMessage(event: RouteEvent<"message.group">) {
|
|
244
254
|
if (event?.message_type !== "group") return;
|
|
245
255
|
const selfId = Number(event?.self_id || 0);
|
|
246
256
|
const groupId = Number(event?.group_id || 0);
|
|
@@ -255,23 +265,23 @@ export function createVerifyController(
|
|
|
255
265
|
const cfg = getVerifyConfig();
|
|
256
266
|
const text = ctx.text(event) || "";
|
|
257
267
|
const messageId = Number(event?.message_id || 0);
|
|
258
|
-
const bot =
|
|
268
|
+
const bot = event.bot;
|
|
259
269
|
|
|
260
270
|
if (p.mode === "number" && isNumberAnswerCorrect(p, text)) {
|
|
261
|
-
await passVerification(p);
|
|
271
|
+
await passVerification(p, bot);
|
|
262
272
|
return;
|
|
263
273
|
}
|
|
264
274
|
|
|
265
275
|
if (p.mode === "chiral") {
|
|
266
276
|
const result = checkChiralAnswer(p, text);
|
|
267
277
|
if (result.status === "pass") {
|
|
268
|
-
await passVerification(p);
|
|
278
|
+
await passVerification(p, bot);
|
|
269
279
|
return;
|
|
270
280
|
}
|
|
271
281
|
if (result.status === "progress") {
|
|
272
282
|
if (bot) {
|
|
273
283
|
try {
|
|
274
|
-
await bot.
|
|
284
|
+
await bot.sendMessage({ type: "group", group_id: groupId}, [
|
|
275
285
|
ctx.segment.at(String(userId)),
|
|
276
286
|
ctx.segment.text(` 答对一部分啦,还差 ${result.remaining} 个哦~`),
|
|
277
287
|
]);
|
|
@@ -289,24 +299,25 @@ export function createVerifyController(
|
|
|
289
299
|
|
|
290
300
|
p.invalidCount += 1;
|
|
291
301
|
if (p.invalidCount >= cfg.maxInvalidMessages) {
|
|
292
|
-
await failKick(p, `连续 ${cfg.maxInvalidMessages}
|
|
302
|
+
await failKick(p, `连续 ${cfg.maxInvalidMessages} 次无关消息`, bot);
|
|
293
303
|
}
|
|
294
304
|
}
|
|
295
305
|
|
|
296
|
-
async function onGroupReaction(event:
|
|
306
|
+
async function onGroupReaction(event: RouteEvent<"notice.group.reaction">) {
|
|
297
307
|
const selfId = Number(event?.self_id || 0);
|
|
298
308
|
const groupId = Number(event?.group_id || 0);
|
|
299
309
|
const userId = Number(event?.user_id || 0);
|
|
300
310
|
if (!selfId || !groupId || !userId) return;
|
|
301
311
|
if (userId === selfId) return;
|
|
302
|
-
|
|
312
|
+
const raw = event.raw as { is_add?: boolean } | undefined;
|
|
313
|
+
if (raw?.is_add === false) return;
|
|
303
314
|
|
|
304
315
|
const key = pendingKey(selfId, groupId, userId);
|
|
305
316
|
const p = pending.get(key);
|
|
306
317
|
if (!p || p.passed || p.mode !== "reaction") return;
|
|
307
318
|
|
|
308
319
|
if (isReactionPass(p, event)) {
|
|
309
|
-
await passVerification(p);
|
|
320
|
+
await passVerification(p, event.bot);
|
|
310
321
|
}
|
|
311
322
|
}
|
|
312
323
|
|
|
@@ -348,20 +359,23 @@ export function createVerifyController(
|
|
|
348
359
|
},
|
|
349
360
|
);
|
|
350
361
|
|
|
351
|
-
async function restartVerification(info: MemberJoinInfo): Promise<boolean> {
|
|
362
|
+
async function restartVerification(info: MemberJoinInfo, bot?: Bot): Promise<boolean> {
|
|
352
363
|
removePending(pendingKey(info.selfId, info.groupId, info.userId));
|
|
353
|
-
return startVerification(info, true);
|
|
364
|
+
return startVerification(info, bot, true);
|
|
354
365
|
}
|
|
355
366
|
|
|
356
367
|
async function bypassVerification(info: MemberJoinInfo): Promise<void> {
|
|
357
368
|
removePending(pendingKey(info.selfId, info.groupId, info.userId));
|
|
358
369
|
}
|
|
359
370
|
|
|
360
|
-
async function trySendCustomWelcome(
|
|
371
|
+
async function trySendCustomWelcome(
|
|
372
|
+
info: MemberJoinInfo,
|
|
373
|
+
bot?: Bot,
|
|
374
|
+
): Promise<boolean> {
|
|
361
375
|
const groupCfg = getGroupVerifyConfig(getVerifyConfig(), info.groupId);
|
|
362
376
|
if (!hasCustomPrompt(groupCfg)) return false;
|
|
363
|
-
const bot = ctx.pickBot(info.selfId);
|
|
364
377
|
if (!bot) return false;
|
|
378
|
+
const target = bot;
|
|
365
379
|
const segments: any[] = [];
|
|
366
380
|
const prompt = String(groupCfg.customPrompt || "").trim();
|
|
367
381
|
if (prompt) {
|
|
@@ -380,7 +394,7 @@ export function createVerifyController(
|
|
|
380
394
|
}
|
|
381
395
|
if (!segments.length) return false;
|
|
382
396
|
try {
|
|
383
|
-
await
|
|
397
|
+
await target.sendMessage({ type: "group", group_id: info.groupId}, segments);
|
|
384
398
|
return true;
|
|
385
399
|
} catch (err) {
|
|
386
400
|
ctx.logger.error(`admin verify 发送自定义入群提示失败: ${err}`);
|
package/verify/number.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { MiokuContext } from "mioku";
|
|
2
2
|
import type { VerifyConfig } from "./config";
|
|
3
3
|
import type { PendingVerify } from "./types";
|
|
4
4
|
|
|
@@ -17,17 +17,17 @@ function extractNumbers(text: string): number[] {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
export async function sendNumberPrompt(
|
|
20
|
-
ctx:
|
|
20
|
+
ctx: MiokuContext,
|
|
21
21
|
cfg: VerifyConfig,
|
|
22
22
|
p: PendingVerify,
|
|
23
23
|
): Promise<void> {
|
|
24
|
-
const bot =
|
|
24
|
+
const bot = p.bot;
|
|
25
25
|
if (!bot) return;
|
|
26
26
|
const { question, answer } = genNumberQuestion();
|
|
27
27
|
p.numberAnswer = answer;
|
|
28
28
|
const prompt = cfg.numberPrompt.replace("{question}", question);
|
|
29
29
|
try {
|
|
30
|
-
await bot.
|
|
30
|
+
await bot.sendMessage({ type: "group", group_id: p.groupId}, [
|
|
31
31
|
ctx.segment.at(String(p.userId)),
|
|
32
32
|
ctx.segment.text(` ${prompt}`),
|
|
33
33
|
]);
|