mioku-plugin-admin 3.0.2 → 3.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/commands/group.ts +41 -52
- package/commands/notice.ts +2 -2
- package/commands/personal.ts +29 -30
- package/commands/verify.ts +15 -16
- package/config.ts +20 -10
- package/notify/index.ts +64 -57
- package/notify/welcome.ts +32 -32
- package/package.json +1 -1
- package/skills/group.ts +9 -7
- package/skills/message-image.ts +18 -8
- package/skills/personal.ts +2 -2
- package/utils/prompt-image-store.ts +7 -7
- package/verify/config.ts +6 -6
- package/verify/index.ts +16 -31
- package/verify/reaction.ts +35 -10
- package/verify/state.ts +2 -2
- package/verify/types.ts +7 -7
package/notify/index.ts
CHANGED
|
@@ -16,8 +16,8 @@ interface NotifyPayload {
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
interface PendingFriendRequest {
|
|
19
|
-
selfId:
|
|
20
|
-
userId:
|
|
19
|
+
selfId: string;
|
|
20
|
+
userId: string;
|
|
21
21
|
flag: string;
|
|
22
22
|
createdAt: number;
|
|
23
23
|
approve: () => Promise<void>;
|
|
@@ -25,9 +25,9 @@ interface PendingFriendRequest {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
interface PendingGroupInvite {
|
|
28
|
-
selfId:
|
|
29
|
-
groupId:
|
|
30
|
-
userId:
|
|
28
|
+
selfId: string;
|
|
29
|
+
groupId: string;
|
|
30
|
+
userId: string;
|
|
31
31
|
flag: string;
|
|
32
32
|
subType: string;
|
|
33
33
|
createdAt: number;
|
|
@@ -50,10 +50,12 @@ function normalizeErrorMessage(error: unknown): string {
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
/** 获取应该通知的主人列表 */
|
|
53
|
-
function getNotifyOwners(config: AdminConfig):
|
|
53
|
+
function getNotifyOwners(config: AdminConfig): string[] {
|
|
54
54
|
if (config.notifyTarget.length > 0) return config.notifyTarget;
|
|
55
55
|
const owners = Array.isArray(botConfig?.owners) ? botConfig.owners : [];
|
|
56
|
-
return owners
|
|
56
|
+
return owners
|
|
57
|
+
.map((v: unknown) => String(v ?? "").trim())
|
|
58
|
+
.filter((id: string) => id.length > 0);
|
|
57
59
|
}
|
|
58
60
|
|
|
59
61
|
/** 注册所有事件通知处理器 */
|
|
@@ -65,7 +67,7 @@ export function registerNotificationHandlers(
|
|
|
65
67
|
const pendingGroupInvites: PendingGroupInvite[] = [];
|
|
66
68
|
const recentEventKeys = new Map<string, number>();
|
|
67
69
|
|
|
68
|
-
function owners():
|
|
70
|
+
function owners(): string[] {
|
|
69
71
|
return getNotifyOwners(getConfig());
|
|
70
72
|
}
|
|
71
73
|
|
|
@@ -187,8 +189,8 @@ export function registerNotificationHandlers(
|
|
|
187
189
|
if (ctx.isMaster?.(event)) {
|
|
188
190
|
return true;
|
|
189
191
|
}
|
|
190
|
-
const userId =
|
|
191
|
-
if (userId
|
|
192
|
+
const userId = String(event.user_id ?? "").trim();
|
|
193
|
+
if (!userId) {
|
|
192
194
|
return false;
|
|
193
195
|
}
|
|
194
196
|
return owners().includes(userId);
|
|
@@ -206,8 +208,8 @@ export function registerNotificationHandlers(
|
|
|
206
208
|
}
|
|
207
209
|
|
|
208
210
|
function pushPendingFriendRequest(event: RequestEvent) {
|
|
209
|
-
const selfId =
|
|
210
|
-
const userId =
|
|
211
|
+
const selfId = String(event.self_id ?? "").trim();
|
|
212
|
+
const userId = String(event.user_id ?? "").trim();
|
|
211
213
|
const flag = String(event.flag || "").trim();
|
|
212
214
|
if (!selfId || !userId || !flag) return;
|
|
213
215
|
pendingFriendRequests.push({
|
|
@@ -222,9 +224,9 @@ export function registerNotificationHandlers(
|
|
|
222
224
|
}
|
|
223
225
|
|
|
224
226
|
function pushPendingGroupInvite(event: RequestEvent) {
|
|
225
|
-
const selfId =
|
|
226
|
-
const groupId =
|
|
227
|
-
const userId =
|
|
227
|
+
const selfId = String(event.self_id ?? "").trim();
|
|
228
|
+
const groupId = String(event.group_id ?? "").trim();
|
|
229
|
+
const userId = String(event.user_id ?? "").trim();
|
|
228
230
|
const flag = String(event.flag || "").trim();
|
|
229
231
|
const subType = String(event.sub_type || "invite").trim() || "invite";
|
|
230
232
|
if (!selfId || !groupId || !userId || !flag) return;
|
|
@@ -242,8 +244,8 @@ export function registerNotificationHandlers(
|
|
|
242
244
|
}
|
|
243
245
|
|
|
244
246
|
function shiftLatestFriendRequest(
|
|
245
|
-
selfId:
|
|
246
|
-
userId:
|
|
247
|
+
selfId: string,
|
|
248
|
+
userId: string,
|
|
247
249
|
): PendingFriendRequest | undefined {
|
|
248
250
|
prunePendingRequests();
|
|
249
251
|
for (let i = pendingFriendRequests.length - 1; i >= 0; i--) {
|
|
@@ -257,9 +259,9 @@ export function registerNotificationHandlers(
|
|
|
257
259
|
}
|
|
258
260
|
|
|
259
261
|
function shiftLatestGroupInvite(
|
|
260
|
-
selfId:
|
|
261
|
-
groupId:
|
|
262
|
-
userId?:
|
|
262
|
+
selfId: string,
|
|
263
|
+
groupId: string,
|
|
264
|
+
userId?: string,
|
|
263
265
|
): PendingGroupInvite | undefined {
|
|
264
266
|
prunePendingRequests();
|
|
265
267
|
for (let i = pendingGroupInvites.length - 1; i >= 0; i--) {
|
|
@@ -296,47 +298,53 @@ export function registerNotificationHandlers(
|
|
|
296
298
|
function parseQuotedApprovalTarget(
|
|
297
299
|
quotedText: string,
|
|
298
300
|
):
|
|
299
|
-
| { type: "friend_message"; userId:
|
|
300
|
-
| { type: "friend_request"; userId:
|
|
301
|
-
| { type: "group_invite"; groupId:
|
|
302
|
-
| { type: "group_ban"; groupId:
|
|
301
|
+
| { type: "friend_message"; userId: string }
|
|
302
|
+
| { type: "friend_request"; userId: string }
|
|
303
|
+
| { type: "group_invite"; groupId: string; userId?: string }
|
|
304
|
+
| { type: "group_ban"; groupId: string }
|
|
303
305
|
| null {
|
|
304
306
|
const text = String(quotedText || "");
|
|
305
307
|
if (!text) return null;
|
|
306
308
|
|
|
309
|
+
// 文本里的 id 可能是 QQ 号,也可能是 openid
|
|
310
|
+
const ID_PATTERN = "([A-Za-z0-9_-]{4,64})";
|
|
311
|
+
|
|
307
312
|
if (text.includes("[好友消息]")) {
|
|
308
313
|
const userIdMatch =
|
|
309
|
-
text.match(
|
|
310
|
-
text.match(
|
|
311
|
-
const userId =
|
|
312
|
-
return userId
|
|
314
|
+
text.match(new RegExp(`好友(?:QQ|ID)[::]\\s*${ID_PATTERN}`)) ||
|
|
315
|
+
text.match(new RegExp(`(?:QQ|ID)[::]\\s*${ID_PATTERN}`));
|
|
316
|
+
const userId = String(userIdMatch?.[1] ?? "").trim();
|
|
317
|
+
return userId ? { type: "friend_message", userId } : null;
|
|
313
318
|
}
|
|
314
319
|
|
|
315
320
|
if (text.includes("[好友申请]")) {
|
|
316
321
|
const userIdMatch =
|
|
317
|
-
text.match(
|
|
318
|
-
text.match(
|
|
319
|
-
text.match(
|
|
320
|
-
const userId =
|
|
321
|
-
return userId
|
|
322
|
+
text.match(new RegExp(`好友(?:QQ|ID)[::]\\s*${ID_PATTERN}`)) ||
|
|
323
|
+
text.match(new RegExp(`(?:QQ|ID)[::]\\s*${ID_PATTERN}`)) ||
|
|
324
|
+
text.match(new RegExp(`\\[好友申请\\]\\s*${ID_PATTERN}`));
|
|
325
|
+
const userId = String(userIdMatch?.[1] ?? "").trim();
|
|
326
|
+
return userId ? { type: "friend_request", userId } : null;
|
|
322
327
|
}
|
|
323
328
|
|
|
324
329
|
if (text.includes("[群邀请]")) {
|
|
325
330
|
const groupIdMatch =
|
|
326
|
-
text.match(
|
|
327
|
-
|
|
328
|
-
const
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
331
|
+
text.match(new RegExp(`群号[::]\\s*${ID_PATTERN}`)) ||
|
|
332
|
+
text.match(new RegExp(`\\[群邀请\\][^A-Za-z0-9_-]*${ID_PATTERN}`));
|
|
333
|
+
const inviterIdMatch = text.match(
|
|
334
|
+
new RegExp(`邀请人(?:QQ|ID)[::]\\s*${ID_PATTERN}`),
|
|
335
|
+
);
|
|
336
|
+
const groupId = String(groupIdMatch?.[1] ?? "").trim();
|
|
337
|
+
const userId = String(inviterIdMatch?.[1] ?? "").trim();
|
|
338
|
+
if (!groupId) return null;
|
|
339
|
+
return userId
|
|
332
340
|
? { type: "group_invite", groupId, userId }
|
|
333
341
|
: { type: "group_invite", groupId };
|
|
334
342
|
}
|
|
335
343
|
|
|
336
344
|
if (text.includes("[Bot被禁言]")) {
|
|
337
|
-
const groupIdMatch = text.match(
|
|
338
|
-
const groupId =
|
|
339
|
-
return groupId
|
|
345
|
+
const groupIdMatch = text.match(new RegExp(`群号[::]\\s*${ID_PATTERN}`));
|
|
346
|
+
const groupId = String(groupIdMatch?.[1] ?? "").trim();
|
|
347
|
+
return groupId ? { type: "group_ban", groupId } : null;
|
|
340
348
|
}
|
|
341
349
|
|
|
342
350
|
return null;
|
|
@@ -363,9 +371,9 @@ export function registerNotificationHandlers(
|
|
|
363
371
|
): Promise<void> {
|
|
364
372
|
if (!getConfig().notifyGroupInvite) return;
|
|
365
373
|
|
|
366
|
-
const selfId =
|
|
367
|
-
const groupId =
|
|
368
|
-
const userId =
|
|
374
|
+
const selfId = String(event.self_id ?? "").trim();
|
|
375
|
+
const groupId = String(event.group_id ?? "").trim();
|
|
376
|
+
const userId = String(event.user_id ?? "").trim();
|
|
369
377
|
const flag = String(event.flag || "").trim();
|
|
370
378
|
if (!selfId || !groupId || !userId || !flag) return;
|
|
371
379
|
|
|
@@ -380,7 +388,7 @@ export function registerNotificationHandlers(
|
|
|
380
388
|
lines: [
|
|
381
389
|
"[群邀请]",
|
|
382
390
|
`群号:${groupId}`,
|
|
383
|
-
`邀请人QQ:${userId}`,
|
|
391
|
+
`邀请人QQ:${userId}`, // 可能是 openid,保留原值便于引用回复
|
|
384
392
|
`验证消息:${comment}`,
|
|
385
393
|
"备注:引用该消息回复「同意」或「拒绝」",
|
|
386
394
|
],
|
|
@@ -388,15 +396,15 @@ export function registerNotificationHandlers(
|
|
|
388
396
|
}
|
|
389
397
|
|
|
390
398
|
async function notifyGroupBan(event: NoticeEvent): Promise<void> {
|
|
391
|
-
const selfId =
|
|
392
|
-
const groupId =
|
|
393
|
-
const userId =
|
|
399
|
+
const selfId = String(event.self_id ?? "").trim();
|
|
400
|
+
const groupId = String(event.group_id ?? "").trim();
|
|
401
|
+
const userId = String(event.user_id ?? "").trim();
|
|
394
402
|
const rawBan = event.raw as { action_type?: string; duration?: number } | undefined;
|
|
395
403
|
const duration = Number(rawBan?.duration || 0);
|
|
396
404
|
if (!selfId || !groupId) return;
|
|
397
405
|
if (userId !== selfId) return;
|
|
398
406
|
|
|
399
|
-
const operatorId =
|
|
407
|
+
const operatorId = String(event.operator_id ?? "").trim();
|
|
400
408
|
const isUnban = rawBan?.action_type === "lift_ban";
|
|
401
409
|
if (isUnban) {
|
|
402
410
|
if (!getConfig().notifyGroupUnban) return;
|
|
@@ -424,22 +432,21 @@ export function registerNotificationHandlers(
|
|
|
424
432
|
): Promise<void> {
|
|
425
433
|
if (!getConfig().notifyGroupKick) return;
|
|
426
434
|
|
|
427
|
-
const selfId =
|
|
428
|
-
const groupId =
|
|
429
|
-
const userId =
|
|
435
|
+
const selfId = String(event.self_id ?? "").trim();
|
|
436
|
+
const groupId = String(event.group_id ?? "").trim();
|
|
437
|
+
const userId = String(event.user_id ?? "").trim();
|
|
430
438
|
const rawKick = event.raw as { action_type?: string } | undefined;
|
|
431
439
|
const leaveType = String(rawKick?.action_type || "").trim();
|
|
432
440
|
if (!selfId || !groupId) return;
|
|
433
441
|
if (userId !== selfId) return;
|
|
434
442
|
if (leaveType !== "kick" && leaveType !== "kick_me") return;
|
|
435
443
|
|
|
436
|
-
const operatorId =
|
|
444
|
+
const operatorId = String(event.operator_id ?? "").trim();
|
|
437
445
|
const eventKey = `group-kick:${selfId}:${groupId}:${operatorId}:${leaveType}:${Number(event.time || 0)}`;
|
|
438
446
|
if (!markEventOnce(eventKey)) return;
|
|
439
447
|
|
|
440
448
|
await sendNotify(event.bot, {
|
|
441
|
-
avatarUrl:
|
|
442
|
-
operatorId > 0 ? getAvatarUrl(operatorId) : getGroupAvatarUrl(groupId),
|
|
449
|
+
avatarUrl: operatorId ? getAvatarUrl(operatorId) : getGroupAvatarUrl(groupId),
|
|
443
450
|
lines: ["[Bot被踢]", `群号:${groupId}`, `操作者QQ:${operatorId}`],
|
|
444
451
|
});
|
|
445
452
|
}
|
|
@@ -520,7 +527,7 @@ export function registerNotificationHandlers(
|
|
|
520
527
|
}
|
|
521
528
|
const text = (ctx.text(event) || "").trim();
|
|
522
529
|
|
|
523
|
-
const selfId =
|
|
530
|
+
const selfId = String(event.self_id ?? "").trim();
|
|
524
531
|
const bot = event.bot;
|
|
525
532
|
if (!bot) {
|
|
526
533
|
await event.reply("Bot不可用", true);
|
package/notify/welcome.ts
CHANGED
|
@@ -5,8 +5,8 @@ import type { AdminConfig } from "../config";
|
|
|
5
5
|
export async function resolveMemberName(
|
|
6
6
|
ctx: MiokuContext,
|
|
7
7
|
bot: import("mioku").Bot | undefined,
|
|
8
|
-
groupId:
|
|
9
|
-
userId:
|
|
8
|
+
groupId: string,
|
|
9
|
+
userId: string,
|
|
10
10
|
): Promise<string> {
|
|
11
11
|
try {
|
|
12
12
|
if (!bot) return String(userId);
|
|
@@ -22,7 +22,7 @@ export async function resolveMemberName(
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
interface PendingMember {
|
|
25
|
-
userId:
|
|
25
|
+
userId: string;
|
|
26
26
|
memberName: string;
|
|
27
27
|
}
|
|
28
28
|
|
|
@@ -42,7 +42,7 @@ function getBatchMap(): Map<string, BatchState> {
|
|
|
42
42
|
return state[RUNTIME_KEY] as Map<string, BatchState>;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
function batchKey(selfId: string
|
|
45
|
+
function batchKey(selfId: string, groupId: string): string {
|
|
46
46
|
return `${selfId}:${groupId}`;
|
|
47
47
|
}
|
|
48
48
|
|
|
@@ -68,16 +68,16 @@ async function flushBatch(options: {
|
|
|
68
68
|
ctx: MiokuContext;
|
|
69
69
|
aiService?: AIService;
|
|
70
70
|
config: AdminConfig;
|
|
71
|
-
selfId: string
|
|
72
|
-
groupId:
|
|
71
|
+
selfId: string;
|
|
72
|
+
groupId: string;
|
|
73
73
|
groupName: string;
|
|
74
74
|
members: PendingMember[];
|
|
75
75
|
promptInjections?: { content: string; title?: string }[];
|
|
76
76
|
bot?: import("mioku").Bot;
|
|
77
77
|
tryCustomPrompt?: (info: {
|
|
78
|
-
selfId: string
|
|
79
|
-
groupId:
|
|
80
|
-
userId:
|
|
78
|
+
selfId: string;
|
|
79
|
+
groupId: string;
|
|
80
|
+
userId: string;
|
|
81
81
|
groupName: string;
|
|
82
82
|
}, bot?: import("mioku").Bot) => Promise<boolean>;
|
|
83
83
|
}): Promise<string> {
|
|
@@ -135,13 +135,13 @@ async function flushBatch(options: {
|
|
|
135
135
|
|
|
136
136
|
try {
|
|
137
137
|
await chatRuntime.generateNotice({
|
|
138
|
-
selfId
|
|
138
|
+
selfId,
|
|
139
139
|
groupId,
|
|
140
140
|
send: true,
|
|
141
141
|
instruction: [
|
|
142
142
|
`当前有 ${members.length} 位新成员同时入群,请一次性发送一段统一的欢迎语(不要逐个 @ 欢迎、不要重复点名)不要长篇大论,精简即可。`,
|
|
143
143
|
`新成员昵称:${userList}`,
|
|
144
|
-
`新成员
|
|
144
|
+
`新成员 ID:${userIdList}`,
|
|
145
145
|
`所在群:${groupName}`,
|
|
146
146
|
`${config.welcome.aiPrompt || ""}`,
|
|
147
147
|
].join("\n"),
|
|
@@ -159,17 +159,17 @@ async function sendSingleWelcome(options: {
|
|
|
159
159
|
ctx: MiokuContext;
|
|
160
160
|
aiService?: AIService;
|
|
161
161
|
config: AdminConfig;
|
|
162
|
-
selfId: string
|
|
163
|
-
groupId:
|
|
162
|
+
selfId: string;
|
|
163
|
+
groupId: string;
|
|
164
164
|
groupName: string;
|
|
165
|
-
userId:
|
|
165
|
+
userId: string;
|
|
166
166
|
memberName: string;
|
|
167
167
|
promptInjections?: { content: string; title?: string }[];
|
|
168
168
|
bot?: import("mioku").Bot;
|
|
169
169
|
tryCustomPrompt?: (info: {
|
|
170
|
-
selfId: string
|
|
171
|
-
groupId:
|
|
172
|
-
userId:
|
|
170
|
+
selfId: string;
|
|
171
|
+
groupId: string;
|
|
172
|
+
userId: string;
|
|
173
173
|
groupName: string;
|
|
174
174
|
}, bot?: import("mioku").Bot) => Promise<boolean>;
|
|
175
175
|
}): Promise<void> {
|
|
@@ -211,16 +211,16 @@ export async function triggerSingleWelcome(options: {
|
|
|
211
211
|
ctx: MiokuContext;
|
|
212
212
|
aiService?: AIService;
|
|
213
213
|
getConfig: () => AdminConfig;
|
|
214
|
-
selfId: string
|
|
215
|
-
groupId:
|
|
214
|
+
selfId: string;
|
|
215
|
+
groupId: string;
|
|
216
216
|
groupName: string;
|
|
217
|
-
userId:
|
|
217
|
+
userId: string;
|
|
218
218
|
memberName?: string;
|
|
219
219
|
promptInjections?: { content: string; title?: string }[];
|
|
220
220
|
tryCustomPrompt?: (info: {
|
|
221
|
-
selfId: string
|
|
222
|
-
groupId:
|
|
223
|
-
userId:
|
|
221
|
+
selfId: string;
|
|
222
|
+
groupId: string;
|
|
223
|
+
userId: string;
|
|
224
224
|
groupName: string;
|
|
225
225
|
}, bot?: import("mioku").Bot) => Promise<boolean>;
|
|
226
226
|
}, bot?: import("mioku").Bot): Promise<void> {
|
|
@@ -252,15 +252,15 @@ export function registerWelcomeHandler(
|
|
|
252
252
|
aiService: AIService | undefined,
|
|
253
253
|
getConfig: () => AdminConfig,
|
|
254
254
|
shouldSuppress?: (info: {
|
|
255
|
-
selfId: string
|
|
256
|
-
groupId:
|
|
257
|
-
userId:
|
|
255
|
+
selfId: string;
|
|
256
|
+
groupId: string;
|
|
257
|
+
userId: string;
|
|
258
258
|
groupName: string;
|
|
259
259
|
}, bot?: import("mioku").Bot) => Promise<boolean> | boolean,
|
|
260
260
|
tryCustomPrompt?: (info: {
|
|
261
|
-
selfId: string
|
|
262
|
-
groupId:
|
|
263
|
-
userId:
|
|
261
|
+
selfId: string;
|
|
262
|
+
groupId: string;
|
|
263
|
+
userId: string;
|
|
264
264
|
groupName: string;
|
|
265
265
|
}, bot?: import("mioku").Bot) => Promise<boolean>,
|
|
266
266
|
): () => void {
|
|
@@ -272,13 +272,13 @@ export function registerWelcomeHandler(
|
|
|
272
272
|
const cfg = getConfig();
|
|
273
273
|
const bot = event.bot;
|
|
274
274
|
const selfId = event?.self_id || ctx.self_id || "";
|
|
275
|
-
const groupId =
|
|
276
|
-
const userId =
|
|
275
|
+
const groupId = String(event?.group_id ?? "").trim();
|
|
276
|
+
const userId = String(event?.user_id ?? "").trim();
|
|
277
277
|
if (!groupId || !userId) return;
|
|
278
278
|
if (selfId != null && String(userId) === String(selfId)) return;
|
|
279
279
|
|
|
280
280
|
const groupName =
|
|
281
|
-
String((event.raw as { group_name?: string } | undefined)?.group_name || "").trim() ||
|
|
281
|
+
String((event.raw as { group_name?: string } | undefined)?.group_name || "").trim() || groupId;
|
|
282
282
|
|
|
283
283
|
if (
|
|
284
284
|
shouldSuppress &&
|
package/package.json
CHANGED
package/skills/group.ts
CHANGED
|
@@ -7,8 +7,8 @@ interface GroupRuntime {
|
|
|
7
7
|
ctx: { logger?: { error?: (...args: unknown[]) => void } };
|
|
8
8
|
event: MessageEvent;
|
|
9
9
|
bot: Bot;
|
|
10
|
-
groupId:
|
|
11
|
-
selfId:
|
|
10
|
+
groupId: string;
|
|
11
|
+
selfId: string;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
interface SkillRuntimeContext {
|
|
@@ -24,10 +24,10 @@ function resolveGroupRuntime(runtimeCtx: SkillRuntimeContext | undefined):
|
|
|
24
24
|
if (!ctx) return { error: "无法获取上下文" };
|
|
25
25
|
|
|
26
26
|
const event = runtimeCtx?.event ?? runtimeCtx?.rawEvent;
|
|
27
|
-
const groupId =
|
|
27
|
+
const groupId = String(event?.group_id ?? "").trim();
|
|
28
28
|
if (!groupId) return { error: "这个工具只能在群聊中使用" };
|
|
29
29
|
|
|
30
|
-
const selfId =
|
|
30
|
+
const selfId = String(event?.self_id ?? "").trim();
|
|
31
31
|
if (!selfId) return { error: "无法获取Bot ID" };
|
|
32
32
|
|
|
33
33
|
const bot = event?.bot;
|
|
@@ -161,7 +161,7 @@ const groupAdminSkill: AISkill = {
|
|
|
161
161
|
return { success: true, message: `已将 ${userId} 的头衔设为 "${title}"` };
|
|
162
162
|
}
|
|
163
163
|
case "set_self_title": {
|
|
164
|
-
const userId =
|
|
164
|
+
const userId = String(event.user_id ?? "").trim();
|
|
165
165
|
if (!userId) return { error: "无法获取当前用户ID" };
|
|
166
166
|
const title = String((args as { title?: unknown })?.title ?? "").trim();
|
|
167
167
|
if (!title) return { error: "set_self_title 需要提供 title" };
|
|
@@ -244,8 +244,10 @@ const groupAdminSkill: AISkill = {
|
|
|
244
244
|
return { success: true, message: `Bot在当前群的群名片已修改为 ${card}` };
|
|
245
245
|
}
|
|
246
246
|
case "set_group_avatar": {
|
|
247
|
-
const messageId =
|
|
248
|
-
|
|
247
|
+
const messageId = String(
|
|
248
|
+
(args as { message_id?: unknown })?.message_id ?? "",
|
|
249
|
+
).trim();
|
|
250
|
+
if (!messageId) {
|
|
249
251
|
return { error: "set_group_avatar 需要提供有效的 message_id" };
|
|
250
252
|
}
|
|
251
253
|
const imageUrl = await getImageUrlByMessageId(bot, messageId);
|
package/skills/message-image.ts
CHANGED
|
@@ -1,18 +1,28 @@
|
|
|
1
1
|
import type { Bot } from "mioku";
|
|
2
2
|
|
|
3
|
+
/** 通过框架能力取消息里的第一张图片(适配器各自实现,不依赖 OneBot 原生 action) */
|
|
3
4
|
export async function getImageUrlByMessageId(
|
|
4
5
|
bot: Bot,
|
|
5
|
-
messageId:
|
|
6
|
+
messageId: string,
|
|
6
7
|
): Promise<string | null> {
|
|
7
8
|
try {
|
|
8
|
-
const msg = await bot.
|
|
9
|
-
message?: Array<{ type?: string; url?: string; file?: string }>;
|
|
10
|
-
}>("get_msg", { message_id: messageId });
|
|
9
|
+
const msg = await bot.getMessage(messageId);
|
|
11
10
|
const segments = Array.isArray(msg?.message) ? msg.message : [];
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
for (const segment of segments) {
|
|
12
|
+
if (segment?.type !== "image") continue;
|
|
13
|
+
const data = (segment.data ?? {}) as Record<string, unknown>;
|
|
14
|
+
const attachment = segment.attachment as
|
|
15
|
+
| { url?: string; file?: string }
|
|
16
|
+
| undefined;
|
|
17
|
+
const url =
|
|
18
|
+
(typeof data.url === "string" && data.url) ||
|
|
19
|
+
(typeof data.file === "string" && data.file) ||
|
|
20
|
+
attachment?.url ||
|
|
21
|
+
attachment?.file ||
|
|
22
|
+
"";
|
|
23
|
+
if (url) return url;
|
|
24
|
+
}
|
|
25
|
+
return null;
|
|
16
26
|
} catch {
|
|
17
27
|
return null;
|
|
18
28
|
}
|
package/skills/personal.ts
CHANGED
|
@@ -101,8 +101,8 @@ const personalSkill: AISkill = {
|
|
|
101
101
|
try {
|
|
102
102
|
switch (action) {
|
|
103
103
|
case "set_avatar": {
|
|
104
|
-
const messageId =
|
|
105
|
-
if (!
|
|
104
|
+
const messageId = String(a?.message_id ?? "").trim();
|
|
105
|
+
if (!messageId) {
|
|
106
106
|
return { error: "set_avatar 需要提供有效的 message_id" };
|
|
107
107
|
}
|
|
108
108
|
const imageUrl = await getImageUrlByMessageId(bot, messageId);
|
|
@@ -20,7 +20,7 @@ export interface SavedPromptImage {
|
|
|
20
20
|
size: number;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
export function getGroupPromptImageDir(groupId:
|
|
23
|
+
export function getGroupPromptImageDir(groupId: string): string {
|
|
24
24
|
return path.join(
|
|
25
25
|
getPluginDataDir("admin"),
|
|
26
26
|
String(groupId),
|
|
@@ -28,13 +28,13 @@ export function getGroupPromptImageDir(groupId: number): string {
|
|
|
28
28
|
);
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
export function ensureGroupPromptImageDir(groupId:
|
|
31
|
+
export function ensureGroupPromptImageDir(groupId: string): string {
|
|
32
32
|
const dir = getGroupPromptImageDir(groupId);
|
|
33
33
|
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
34
34
|
return dir;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
export function getGroupPromptImagePath(groupId:
|
|
37
|
+
export function getGroupPromptImagePath(groupId: string, filename: string): string {
|
|
38
38
|
return path.join(getGroupPromptImageDir(groupId), filename);
|
|
39
39
|
}
|
|
40
40
|
|
|
@@ -57,7 +57,7 @@ function generateFilename(ext: string): string {
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
export async function saveRemoteImageAsPrompt(
|
|
60
|
-
groupId:
|
|
60
|
+
groupId: string,
|
|
61
61
|
sourceUrl: string,
|
|
62
62
|
): Promise<SavedPromptImage | null> {
|
|
63
63
|
const trimmed = String(sourceUrl || "").trim();
|
|
@@ -95,7 +95,7 @@ export async function saveRemoteImageAsPrompt(
|
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
async function copyLocalFileAsPrompt(
|
|
98
|
-
groupId:
|
|
98
|
+
groupId: string,
|
|
99
99
|
filePath: string,
|
|
100
100
|
): Promise<SavedPromptImage | null> {
|
|
101
101
|
try {
|
|
@@ -171,7 +171,7 @@ function downloadToFile(url: string, savePath: string): Promise<number> {
|
|
|
171
171
|
}
|
|
172
172
|
|
|
173
173
|
export async function deletePromptImage(
|
|
174
|
-
groupId:
|
|
174
|
+
groupId: string,
|
|
175
175
|
filename: string,
|
|
176
176
|
): Promise<void> {
|
|
177
177
|
if (!filename) return;
|
|
@@ -182,7 +182,7 @@ export async function deletePromptImage(
|
|
|
182
182
|
}
|
|
183
183
|
|
|
184
184
|
export async function pruneGroupPromptImages(
|
|
185
|
-
groupId:
|
|
185
|
+
groupId: string,
|
|
186
186
|
keep: readonly string[],
|
|
187
187
|
): Promise<void> {
|
|
188
188
|
const dir = getGroupPromptImageDir(groupId);
|
package/verify/config.ts
CHANGED
|
@@ -3,7 +3,7 @@ export type VerifyMode = "reaction" | "number" | "chiral";
|
|
|
3
3
|
export const MAX_CUSTOM_PROMPT_LENGTH = 50;
|
|
4
4
|
|
|
5
5
|
export interface VerifyGroupConfig {
|
|
6
|
-
groupId:
|
|
6
|
+
groupId: string;
|
|
7
7
|
enabled: boolean;
|
|
8
8
|
mode: VerifyMode;
|
|
9
9
|
customPrompt: string;
|
|
@@ -71,14 +71,14 @@ export function normalizeVerifyMode(value: unknown): VerifyMode {
|
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
function normalizeVerifyGroup(raw: any): VerifyGroupConfig {
|
|
74
|
-
const groupId =
|
|
74
|
+
const groupId = String(raw?.groupId ?? raw?.group_id ?? "").trim();
|
|
75
75
|
const promptImageRaw =
|
|
76
76
|
raw?.promptImage ??
|
|
77
77
|
raw?.prompt_image ??
|
|
78
78
|
(Array.isArray(raw?.promptImages) ? raw.promptImages[0] : undefined) ??
|
|
79
79
|
(Array.isArray(raw?.images) ? raw.images[0] : undefined);
|
|
80
80
|
return {
|
|
81
|
-
groupId
|
|
81
|
+
groupId,
|
|
82
82
|
enabled: raw?.enabled === true,
|
|
83
83
|
mode: normalizeVerifyMode(raw?.mode),
|
|
84
84
|
customPrompt: normalizeCustomPrompt(
|
|
@@ -92,7 +92,7 @@ export function normalizeVerifyConfig(raw: any): VerifyConfig {
|
|
|
92
92
|
const groups: VerifyGroupConfig[] = Array.isArray(raw?.groups)
|
|
93
93
|
? raw.groups
|
|
94
94
|
.map((g: any) => normalizeVerifyGroup(g))
|
|
95
|
-
.filter((g: VerifyGroupConfig) => g.groupId > 0)
|
|
95
|
+
.filter((g: VerifyGroupConfig) => g.groupId.length > 0)
|
|
96
96
|
: [];
|
|
97
97
|
|
|
98
98
|
const numOr = (value: unknown, fallback: number): number => {
|
|
@@ -142,7 +142,7 @@ export function normalizeVerifyConfig(raw: any): VerifyConfig {
|
|
|
142
142
|
|
|
143
143
|
export function getGroupVerifyConfig(
|
|
144
144
|
config: VerifyConfig,
|
|
145
|
-
groupId:
|
|
145
|
+
groupId: string,
|
|
146
146
|
): VerifyGroupConfig {
|
|
147
147
|
const found = config.groups.find((g) => g.groupId === groupId);
|
|
148
148
|
if (found) return found;
|
|
@@ -157,7 +157,7 @@ export function getGroupVerifyConfig(
|
|
|
157
157
|
|
|
158
158
|
export function upsertGroupVerifyConfig(
|
|
159
159
|
config: VerifyConfig,
|
|
160
|
-
groupId:
|
|
160
|
+
groupId: string,
|
|
161
161
|
patch: Partial<Omit<VerifyGroupConfig, "groupId">>,
|
|
162
162
|
): VerifyConfig {
|
|
163
163
|
const idx = config.groups.findIndex((g) => g.groupId === groupId);
|