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/notify/welcome.ts CHANGED
@@ -1,17 +1,16 @@
1
- import { type MiokiContext, wait } from "mioki";
1
+ import { type MiokuContext, wait } from "mioku";
2
2
  import { getPluginRuntimeState, type AIService } from "mioku";
3
3
  import type { AdminConfig } from "../config";
4
4
 
5
5
  export async function resolveMemberName(
6
- ctx: MiokiContext,
6
+ ctx: MiokuContext,
7
+ bot: import("mioku").Bot | undefined,
7
8
  groupId: number,
8
9
  userId: number,
9
- selfId: number,
10
10
  ): Promise<string> {
11
11
  try {
12
- const member = await ctx
13
- .pickBot(selfId)
14
- .getGroupMemberInfo(groupId, userId);
12
+ if (!bot) return String(userId);
13
+ const member = await bot.getMemberInfo(groupId, userId);
15
14
  return (
16
15
  String(member?.card || "").trim() ||
17
16
  String(member?.nickname || "").trim() ||
@@ -43,7 +42,7 @@ function getBatchMap(): Map<string, BatchState> {
43
42
  return state[RUNTIME_KEY] as Map<string, BatchState>;
44
43
  }
45
44
 
46
- function batchKey(selfId: number, groupId: number): string {
45
+ function batchKey(selfId: string | number, groupId: number): string {
47
46
  return `${selfId}:${groupId}`;
48
47
  }
49
48
 
@@ -66,20 +65,21 @@ function normalizeGeneratedText(value: string): string {
66
65
  }
67
66
 
68
67
  async function flushBatch(options: {
69
- ctx: MiokiContext;
68
+ ctx: MiokuContext;
70
69
  aiService?: AIService;
71
70
  config: AdminConfig;
72
- selfId: number;
71
+ selfId: string | number;
73
72
  groupId: number;
74
73
  groupName: string;
75
74
  members: PendingMember[];
76
75
  promptInjections?: { content: string; title?: string }[];
76
+ bot?: import("mioku").Bot;
77
77
  tryCustomPrompt?: (info: {
78
- selfId: number;
78
+ selfId: string | number;
79
79
  groupId: number;
80
80
  userId: number;
81
81
  groupName: string;
82
- }) => Promise<boolean>;
82
+ }, bot?: import("mioku").Bot) => Promise<boolean>;
83
83
  }): Promise<string> {
84
84
  const {
85
85
  ctx,
@@ -90,6 +90,7 @@ async function flushBatch(options: {
90
90
  groupName,
91
91
  members,
92
92
  promptInjections,
93
+ bot,
93
94
  tryCustomPrompt,
94
95
  } = options;
95
96
  if (!members.length) return "";
@@ -97,12 +98,15 @@ async function flushBatch(options: {
97
98
  if (tryCustomPrompt) {
98
99
  let anyCustom = false;
99
100
  for (const m of members) {
100
- const handled = await tryCustomPrompt({
101
- selfId,
102
- groupId,
103
- userId: m.userId,
104
- groupName,
105
- });
101
+ const handled = await tryCustomPrompt(
102
+ {
103
+ selfId,
104
+ groupId,
105
+ userId: m.userId,
106
+ groupName,
107
+ },
108
+ bot,
109
+ );
106
110
  if (handled) anyCustom = true;
107
111
  }
108
112
  if (anyCustom) return "";
@@ -152,21 +156,22 @@ async function flushBatch(options: {
152
156
  }
153
157
 
154
158
  async function sendSingleWelcome(options: {
155
- ctx: MiokiContext;
159
+ ctx: MiokuContext;
156
160
  aiService?: AIService;
157
161
  config: AdminConfig;
158
- selfId: number;
162
+ selfId: string | number;
159
163
  groupId: number;
160
164
  groupName: string;
161
165
  userId: number;
162
166
  memberName: string;
163
167
  promptInjections?: { content: string; title?: string }[];
168
+ bot?: import("mioku").Bot;
164
169
  tryCustomPrompt?: (info: {
165
- selfId: number;
170
+ selfId: string | number;
166
171
  groupId: number;
167
172
  userId: number;
168
173
  groupName: string;
169
- }) => Promise<boolean>;
174
+ }, bot?: import("mioku").Bot) => Promise<boolean>;
170
175
  }): Promise<void> {
171
176
  const {
172
177
  ctx,
@@ -178,6 +183,7 @@ async function sendSingleWelcome(options: {
178
183
  userId,
179
184
  memberName,
180
185
  promptInjections,
186
+ bot,
181
187
  tryCustomPrompt,
182
188
  } = options;
183
189
  const welcomeMessage = await flushBatch({
@@ -189,42 +195,42 @@ async function sendSingleWelcome(options: {
189
195
  groupName,
190
196
  members: [{ userId, memberName }],
191
197
  promptInjections,
198
+ bot,
192
199
  tryCustomPrompt,
193
200
  });
194
201
  if (!welcomeMessage) return;
195
- const bot = ctx.pickBot(selfId);
196
202
  if (!bot) return;
197
203
  try {
198
- await bot.sendGroupMsg(groupId, [ctx.segment.text(welcomeMessage)]);
204
+ await bot.sendMessage({ type: "group", group_id: groupId}, [ctx.segment.text(welcomeMessage)]);
199
205
  } catch (error) {
200
206
  ctx.logger.warn(`发送入群欢迎失败: ${error}`);
201
207
  }
202
208
  }
203
209
 
204
210
  export async function triggerSingleWelcome(options: {
205
- ctx: MiokiContext;
211
+ ctx: MiokuContext;
206
212
  aiService?: AIService;
207
213
  getConfig: () => AdminConfig;
208
- selfId: number;
214
+ selfId: string | number;
209
215
  groupId: number;
210
216
  groupName: string;
211
217
  userId: number;
212
218
  memberName?: string;
213
219
  promptInjections?: { content: string; title?: string }[];
214
220
  tryCustomPrompt?: (info: {
215
- selfId: number;
221
+ selfId: string | number;
216
222
  groupId: number;
217
223
  userId: number;
218
224
  groupName: string;
219
- }) => Promise<boolean>;
220
- }): Promise<void> {
225
+ }, bot?: import("mioku").Bot) => Promise<boolean>;
226
+ }, bot?: import("mioku").Bot): Promise<void> {
221
227
  const memberName =
222
228
  options.memberName ||
223
229
  (await resolveMemberName(
224
230
  options.ctx,
231
+ bot,
225
232
  options.groupId,
226
233
  options.userId,
227
- options.selfId,
228
234
  ));
229
235
  await sendSingleWelcome({
230
236
  ctx: options.ctx,
@@ -236,26 +242,27 @@ export async function triggerSingleWelcome(options: {
236
242
  userId: options.userId,
237
243
  memberName,
238
244
  promptInjections: options.promptInjections,
245
+ bot,
239
246
  tryCustomPrompt: options.tryCustomPrompt,
240
247
  });
241
248
  }
242
249
 
243
250
  export function registerWelcomeHandler(
244
- ctx: MiokiContext,
251
+ ctx: MiokuContext,
245
252
  aiService: AIService | undefined,
246
253
  getConfig: () => AdminConfig,
247
254
  shouldSuppress?: (info: {
248
- selfId: number;
255
+ selfId: string | number;
249
256
  groupId: number;
250
257
  userId: number;
251
258
  groupName: string;
252
- }) => Promise<boolean> | boolean,
259
+ }, bot?: import("mioku").Bot) => Promise<boolean> | boolean,
253
260
  tryCustomPrompt?: (info: {
254
- selfId: number;
261
+ selfId: string | number;
255
262
  groupId: number;
256
263
  userId: number;
257
264
  groupName: string;
258
- }) => Promise<boolean>,
265
+ }, bot?: import("mioku").Bot) => Promise<boolean>,
259
266
  ): () => void {
260
267
  const batches = getBatchMap();
261
268
 
@@ -263,18 +270,19 @@ export function registerWelcomeHandler(
263
270
  "notice.group.increase",
264
271
  async (event) => {
265
272
  const cfg = getConfig();
266
- const selfId = Number(event?.self_id || ctx.self_id);
273
+ const bot = event.bot;
274
+ const selfId = event?.self_id || ctx.self_id || "";
267
275
  const groupId = Number(event?.group_id || 0);
268
276
  const userId = Number(event?.user_id || 0);
269
277
  if (!groupId || !userId) return;
270
- if (userId === selfId) return;
278
+ if (selfId != null && String(userId) === String(selfId)) return;
271
279
 
272
280
  const groupName =
273
- String(event?.group?.group_name || "").trim() || String(groupId);
281
+ String((event.raw as { group_name?: string } | undefined)?.group_name || "").trim() || String(groupId);
274
282
 
275
283
  if (
276
284
  shouldSuppress &&
277
- (await shouldSuppress({ selfId, groupId, userId, groupName }))
285
+ (await shouldSuppress({ selfId, groupId, userId, groupName }, bot))
278
286
  ) {
279
287
  return;
280
288
  }
@@ -286,9 +294,9 @@ export function registerWelcomeHandler(
286
294
  if (batchWindowMs === 0) {
287
295
  const memberName = await resolveMemberName(
288
296
  ctx,
297
+ bot,
289
298
  groupId,
290
299
  userId,
291
- selfId,
292
300
  );
293
301
  const welcomeMessage = await flushBatch({
294
302
  ctx,
@@ -298,12 +306,12 @@ export function registerWelcomeHandler(
298
306
  groupId,
299
307
  groupName,
300
308
  members: [{ userId, memberName }],
309
+ bot,
301
310
  });
302
311
  if (!welcomeMessage) return;
303
- const bot = ctx.pickBot(selfId);
304
312
  if (!bot) return;
305
313
  try {
306
- await bot.sendGroupMsg(groupId, [ctx.segment.text(welcomeMessage)]);
314
+ await bot.sendMessage({ type: "group", group_id: groupId}, [ctx.segment.text(welcomeMessage)]);
307
315
  } catch (error) {
308
316
  ctx.logger.warn(`发送入群欢迎失败: ${error}`);
309
317
  }
@@ -320,7 +328,7 @@ export function registerWelcomeHandler(
320
328
  state.groupName = groupName;
321
329
  }
322
330
 
323
- const memberName = await resolveMemberName(ctx, groupId, userId, selfId);
331
+ const memberName = await resolveMemberName(ctx, bot, groupId, userId);
324
332
  if (!state.members.some((m) => m.userId === userId)) {
325
333
  state.members.push({ userId, memberName });
326
334
  }
@@ -344,14 +352,14 @@ export function registerWelcomeHandler(
344
352
  groupId,
345
353
  groupName: pending.groupName,
346
354
  members: pending.members,
355
+ bot,
347
356
  tryCustomPrompt,
348
357
  });
349
358
  if (!welcomeMessage) return;
350
359
 
351
- const bot = ctx.pickBot(selfId);
352
360
  if (!bot) return;
353
361
  try {
354
- await bot.sendGroupMsg(groupId, [ctx.segment.text(welcomeMessage)]);
362
+ await bot.sendMessage({ type: "group", group_id: groupId}, [ctx.segment.text(welcomeMessage)]);
355
363
  } catch (error) {
356
364
  ctx.logger.warn(`发送入群欢迎失败: ${error}`);
357
365
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mioku-plugin-admin",
3
- "version": "2.3.5",
3
+ "version": "3.0.0",
4
4
  "description": "管理插件,提供事件通知与群管/个人管理指令",
5
5
  "main": "index.ts",
6
6
  "type": "module",
@@ -12,12 +12,10 @@
12
12
  "url": "https://github.com/mioku-lab/mioku-plugin-admin.git"
13
13
  },
14
14
  "peerDependencies": {
15
- "mioku": "^0.8.0",
16
- "mioki": "^0.16.0"
15
+ "mioku": "^1.0.0"
17
16
  },
18
17
  "devDependencies": {
19
- "mioku": "workspace:*",
20
- "mioki": "^0.16.0"
18
+ "mioku": "workspace:*"
21
19
  },
22
20
  "mioku": {
23
21
  "services": [
package/skills/group.ts CHANGED
@@ -1,25 +1,42 @@
1
- import type { AISkill } from "mioku";
1
+ import type { AISkill, Bot, MessageEvent } from "mioku";
2
+
2
3
  import { getMemberRole } from "../config";
3
4
  import { getImageUrlByMessageId } from "./message-image";
4
5
 
5
- async function resolveGroupRuntime(runtimeCtx: any) {
6
+ interface GroupRuntime {
7
+ ctx: { logger?: { error?: (...args: unknown[]) => void } };
8
+ event: MessageEvent;
9
+ bot: Bot;
10
+ groupId: number;
11
+ selfId: number;
12
+ }
13
+
14
+ interface SkillRuntimeContext {
15
+ ctx?: GroupRuntime["ctx"];
16
+ event?: MessageEvent;
17
+ rawEvent?: MessageEvent;
18
+ }
19
+
20
+ function resolveGroupRuntime(runtimeCtx: SkillRuntimeContext | undefined):
21
+ | GroupRuntime
22
+ | { error: string } {
6
23
  const ctx = runtimeCtx?.ctx;
7
24
  if (!ctx) return { error: "无法获取上下文" };
8
25
 
9
- const event = runtimeCtx?.event || runtimeCtx?.rawEvent;
10
- const groupId = Number(event?.group_id || 0);
26
+ const event = runtimeCtx?.event ?? runtimeCtx?.rawEvent;
27
+ const groupId = Number(event?.group_id ?? 0);
11
28
  if (!groupId) return { error: "这个工具只能在群聊中使用" };
12
29
 
13
- const selfId = runtimeCtx?.event?.self_id || runtimeCtx?.rawEvent?.self_id;
30
+ const selfId = Number(event?.self_id ?? 0);
14
31
  if (!selfId) return { error: "无法获取Bot ID" };
15
32
 
16
- const bot = ctx.pickBot(selfId);
33
+ const bot = event?.bot;
17
34
  if (!bot) return { error: "Bot不可用" };
18
35
 
19
- return { ctx, selfId, bot, groupId, event };
36
+ return { ctx, event: event!, bot, groupId, selfId };
20
37
  }
21
38
 
22
- function logAdminSkillError(runtimeCtx: any, toolName: string, err: unknown) {
39
+ function logAdminSkillError(runtimeCtx: SkillRuntimeContext | undefined, toolName: string, err: unknown) {
23
40
  runtimeCtx?.ctx?.logger?.error?.(
24
41
  `[admin skills] ${toolName} failed: ${String(err)}`,
25
42
  );
@@ -32,22 +49,16 @@ function formatGroupRole(role: string): string {
32
49
  }
33
50
 
34
51
  async function checkDangerousTargetPermission(
35
- runtime: any,
52
+ runtime: GroupRuntime,
36
53
  targetUserId: number,
37
54
  ): Promise<string | null> {
38
- const operatorUserId = Number(runtime.event?.user_id || 0);
55
+ const operatorUserId = Number(runtime.event.user_id ?? 0);
39
56
  if (!operatorUserId) return "无法获取当前操作人ID";
40
57
 
41
- const operatorRole = await getMemberRole(
42
- runtime.bot,
43
- runtime.groupId,
44
- operatorUserId,
45
- );
46
- const targetRole = await getMemberRole(
47
- runtime.bot,
48
- runtime.groupId,
49
- targetUserId,
50
- );
58
+ const [operatorRole, targetRole] = await Promise.all([
59
+ getMemberRole(runtime.bot, runtime.groupId, operatorUserId),
60
+ getMemberRole(runtime.bot, runtime.groupId, targetUserId),
61
+ ]);
51
62
  if (operatorRole !== targetRole) return null;
52
63
 
53
64
  return `无权操作同级成员:操作人与被操作人均为${formatGroupRole(operatorRole)}`;
@@ -94,108 +105,74 @@ const groupAdminSkill: AISkill = {
94
105
  },
95
106
  required: ["action"],
96
107
  },
97
- handler: async (args: any, runtimeCtx?: any) => {
98
- const runtime = await resolveGroupRuntime(runtimeCtx);
108
+ handler: async (args: unknown, runtimeCtx?: SkillRuntimeContext) => {
109
+ const runtime = resolveGroupRuntime(runtimeCtx);
99
110
  if ("error" in runtime) return { error: runtime.error };
100
- const { bot, selfId, groupId, event } = runtime;
101
- const action = String(args?.action || "");
111
+ const { bot, groupId, event } = runtime;
112
+ const action = String((args as { action?: unknown })?.action ?? "");
102
113
 
103
114
  try {
104
115
  switch (action) {
105
116
  case "kick": {
106
- const userId = Number(args?.user_id);
117
+ const userId = Number((args as { user_id?: unknown })?.user_id);
107
118
  if (!userId) return { error: "kick 需要提供 user_id" };
108
- const permErr = await checkDangerousTargetPermission(
109
- runtime,
110
- userId,
111
- );
119
+ const permErr = await checkDangerousTargetPermission(runtime, userId);
112
120
  if (permErr) return { error: permErr };
113
- await bot.api("set_group_kick", {
114
- group_id: groupId,
115
- user_id: userId,
116
- });
121
+ await bot.kickMember(groupId, userId);
117
122
  return { success: true, message: `已将 ${userId} 移出当前群` };
118
123
  }
119
124
  case "mute": {
120
- const userId = Number(args?.user_id);
125
+ const userId = Number((args as { user_id?: unknown })?.user_id);
121
126
  if (!userId) return { error: "mute 需要提供 user_id" };
122
- const permErr = await checkDangerousTargetPermission(
123
- runtime,
124
- userId,
125
- );
127
+ const permErr = await checkDangerousTargetPermission(runtime, userId);
126
128
  if (permErr) return { error: permErr };
127
129
  const duration =
128
- Number(args?.duration) > 0 ? Number(args.duration) : 10 * 60;
129
- await bot.setGroupBan(groupId, userId, duration);
130
- return {
131
- success: true,
132
- message: `已禁言 ${userId} ${duration}秒`,
133
- };
130
+ Number((args as { duration?: unknown })?.duration) > 0
131
+ ? Number((args as { duration?: unknown }).duration)
132
+ : 10 * 60;
133
+ await bot.banMember(groupId, userId, duration);
134
+ return { success: true, message: `已禁言 ${userId} ${duration}秒` };
134
135
  }
135
136
  case "unmute": {
136
- const userId = Number(args?.user_id);
137
+ const userId = Number((args as { user_id?: unknown })?.user_id);
137
138
  if (!userId) return { error: "unmute 需要提供 user_id" };
138
- await bot.setGroupBan(groupId, userId, 0);
139
+ await bot.banMember(groupId, userId, 0);
139
140
  return { success: true, message: `已解除 ${userId} 的禁言` };
140
141
  }
141
142
  case "set_admin": {
142
- const userId = Number(args?.user_id);
143
+ const userId = Number((args as { user_id?: unknown })?.user_id);
143
144
  if (!userId) return { error: "set_admin 需要提供 user_id" };
144
- await bot.api("set_group_admin", {
145
- group_id: groupId,
146
- user_id: userId,
147
- enable: true,
148
- });
149
- return {
150
- success: true,
151
- message: `已将 ${userId} 设为当前群的管理员`,
152
- };
145
+ await bot.setMemberAdmin(groupId, userId, true);
146
+ return { success: true, message: `已将 ${userId} 设为当前群的管理员` };
153
147
  }
154
148
  case "unset_admin": {
155
- const userId = Number(args?.user_id);
149
+ const userId = Number((args as { user_id?: unknown })?.user_id);
156
150
  if (!userId) return { error: "unset_admin 需要提供 user_id" };
157
- await bot.api("set_group_admin", {
158
- group_id: groupId,
159
- user_id: userId,
160
- enable: false,
161
- });
162
- return {
163
- success: true,
164
- message: `已取消 ${userId} 在当前群的管理员`,
165
- };
151
+ await bot.setMemberAdmin(groupId, userId, false);
152
+ return { success: true, message: `已取消 ${userId} 在当前群的管理员` };
166
153
  }
167
154
  case "set_title": {
168
- const userId = Number(args?.user_id);
169
- const title = String(args?.title || "").trim();
155
+ const userId = Number((args as { user_id?: unknown })?.user_id);
156
+ const title = String((args as { title?: unknown })?.title ?? "").trim();
170
157
  if (!userId || !title) {
171
158
  return { error: "set_title 需要提供 user_id 和 title" };
172
159
  }
173
- await (bot as any).setGroupSpecialTitle(groupId, userId, title);
174
- return {
175
- success: true,
176
- message: `已将 ${userId} 的头衔设为 "${title}"`,
177
- };
160
+ await bot.setMemberTitle(groupId, userId, title);
161
+ return { success: true, message: `已将 ${userId} 的头衔设为 "${title}"` };
178
162
  }
179
163
  case "set_self_title": {
180
- const userId = Number(event?.user_id);
164
+ const userId = Number(event.user_id);
181
165
  if (!userId) return { error: "无法获取当前用户ID" };
182
- const title = String(args?.title || "").trim();
166
+ const title = String((args as { title?: unknown })?.title ?? "").trim();
183
167
  if (!title) return { error: "set_self_title 需要提供 title" };
184
- await (bot as any).setGroupSpecialTitle(groupId, userId, title);
185
- return {
186
- success: true,
187
- message: `已将你的头衔设为 "${title}"`,
188
- };
168
+ await bot.setMemberTitle(groupId, userId, title);
169
+ return { success: true, message: `已将你的头衔设为 "${title}"` };
189
170
  }
190
171
  default:
191
172
  return { error: `未知的 action: ${action}` };
192
173
  }
193
174
  } catch (err) {
194
- logAdminSkillError(
195
- runtimeCtx,
196
- `admin_group.manage_member.${action}`,
197
- err,
198
- );
175
+ logAdminSkillError(runtimeCtx, `admin_group.manage_member.${action}`, err);
199
176
  return { error: `执行 ${action} 失败: ${err}` };
200
177
  }
201
178
  },
@@ -240,87 +217,58 @@ const groupAdminSkill: AISkill = {
240
217
  },
241
218
  required: ["action"],
242
219
  },
243
- handler: async (args: any, runtimeCtx?: any) => {
244
- const runtime = await resolveGroupRuntime(runtimeCtx);
220
+ handler: async (args: unknown, runtimeCtx?: SkillRuntimeContext) => {
221
+ const runtime = resolveGroupRuntime(runtimeCtx);
245
222
  if ("error" in runtime) return { error: runtime.error };
246
- const { bot, selfId, groupId } = runtime;
247
- const action = String(args?.action || "");
223
+ const { bot, groupId, selfId } = runtime;
224
+ const action = String((args as { action?: unknown })?.action ?? "");
248
225
 
249
226
  try {
250
227
  switch (action) {
251
228
  case "set_whole_ban":
252
- await bot.api("set_group_whole_ban", {
253
- group_id: groupId,
254
- enable: true,
255
- });
229
+ await bot.setGroupWholeBan(groupId, true);
256
230
  return { success: true, message: "当前群已开启全体禁言" };
257
231
  case "unset_whole_ban":
258
- await bot.api("set_group_whole_ban", {
259
- group_id: groupId,
260
- enable: false,
261
- });
232
+ await bot.setGroupWholeBan(groupId, false);
262
233
  return { success: true, message: "当前群已关闭全体禁言" };
263
234
  case "set_group_name": {
264
- const groupName = String(args?.group_name || "").trim();
265
- if (!groupName)
266
- return { error: "set_group_name 需要提供 group_name" };
267
- await bot.api("set_group_name", {
268
- group_id: groupId,
269
- group_name: groupName,
270
- });
271
- return {
272
- success: true,
273
- message: `当前群名称已修改为 ${groupName}`,
274
- };
235
+ const groupName = String((args as { group_name?: unknown })?.group_name ?? "").trim();
236
+ if (!groupName) return { error: "set_group_name 需要提供 group_name" };
237
+ await bot.setGroupName(groupId, groupName);
238
+ return { success: true, message: `当前群名称已修改为 ${groupName}` };
275
239
  }
276
240
  case "set_self_card": {
277
- const card = String(args?.card || "").trim();
241
+ const card = String((args as { card?: unknown })?.card ?? "").trim();
278
242
  if (!card) return { error: "set_self_card 需要提供 card" };
279
- await bot.setGroupCard(groupId, selfId, card);
280
- return {
281
- success: true,
282
- message: `Bot在当前群的群名片已修改为 ${card}`,
283
- };
243
+ await bot.setMemberCard(groupId, selfId, card);
244
+ return { success: true, message: `Bot在当前群的群名片已修改为 ${card}` };
284
245
  }
285
246
  case "set_group_avatar": {
286
- const messageId = Number(args?.message_id);
247
+ const messageId = Number((args as { message_id?: unknown })?.message_id);
287
248
  if (!Number.isFinite(messageId) || messageId <= 0) {
288
249
  return { error: "set_group_avatar 需要提供有效的 message_id" };
289
250
  }
290
251
  const imageUrl = await getImageUrlByMessageId(bot, messageId);
291
- if (!imageUrl) {
292
- return { error: "指定 message_id 中未找到图片" };
293
- }
294
- await bot.api("set_group_portrait", {
295
- group_id: groupId,
296
- file: imageUrl,
297
- });
252
+ if (!imageUrl) return { error: "指定 message_id 中未找到图片" };
253
+ await bot.setGroupPortrait(groupId, imageUrl);
298
254
  return { success: true, message: "当前群头像已修改" };
299
255
  }
300
256
  case "recall_messages": {
301
- const ids = Array.isArray(args?.message_ids)
302
- ? args.message_ids
303
- .map((v: any) => Number(v))
304
- .filter((n: number) => Number.isFinite(n) && n !== 0)
257
+ const ids = Array.isArray((args as { message_ids?: unknown })?.message_ids)
258
+ ? ((args as { message_ids: unknown[] }).message_ids)
259
+ .map((v) => Number(v))
260
+ .filter((n) => Number.isFinite(n) && n !== 0)
305
261
  : [];
306
262
  if (ids.length === 0) {
307
263
  return { error: "recall_messages 需要提供至少一个 message_id" };
308
264
  }
309
- const results: Array<{
310
- message_id: number;
311
- success: boolean;
312
- error?: string;
313
- }> = [];
265
+ const results: Array<{ message_id: number; success: boolean; error?: string }> = [];
314
266
  for (const id of ids) {
315
267
  try {
316
- await bot.api("delete_msg", { message_id: id });
268
+ await bot.recallMessage(id);
317
269
  results.push({ message_id: id, success: true });
318
270
  } catch (err) {
319
- results.push({
320
- message_id: id,
321
- success: false,
322
- error: String(err),
323
- });
271
+ results.push({ message_id: id, success: false, error: String(err) });
324
272
  }
325
273
  }
326
274
  const ok = results.filter((r) => r.success).length;
@@ -334,11 +282,7 @@ const groupAdminSkill: AISkill = {
334
282
  return { error: `未知的 action: ${action}` };
335
283
  }
336
284
  } catch (err) {
337
- logAdminSkillError(
338
- runtimeCtx,
339
- `admin_group.manage_group.${action}`,
340
- err,
341
- );
285
+ logAdminSkillError(runtimeCtx, `admin_group.manage_group.${action}`, err);
342
286
  return { error: `执行 ${action} 失败: ${err}` };
343
287
  }
344
288
  },
@@ -346,4 +290,4 @@ const groupAdminSkill: AISkill = {
346
290
  ],
347
291
  };
348
292
 
349
- export default groupAdminSkill;
293
+ export default groupAdminSkill;
@@ -1,17 +1,16 @@
1
+ import type { Bot } from "mioku";
2
+
1
3
  export async function getImageUrlByMessageId(
2
- bot: any,
4
+ bot: Bot,
3
5
  messageId: number,
4
6
  ): Promise<string | null> {
5
7
  try {
6
- const msg =
7
- typeof bot?.getMsg === "function"
8
- ? await bot.getMsg(messageId)
9
- : await bot.api("get_msg", { message_id: messageId });
8
+ const msg = await bot.sendApi<{
9
+ message?: Array<{ type?: string; url?: string; file?: string }>;
10
+ }>("get_msg", { message_id: messageId });
10
11
  const segments = Array.isArray(msg?.message) ? msg.message : [];
11
- const imageSeg = segments.find((seg: any) => seg?.type === "image");
12
- if (!imageSeg) {
13
- return null;
14
- }
12
+ const imageSeg = segments.find((seg) => seg?.type === "image");
13
+ if (!imageSeg) return null;
15
14
 
16
15
  return imageSeg.url || imageSeg.file || null;
17
16
  } catch {