mioku-plugin-admin 2.3.5 → 3.0.1

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.
@@ -1,18 +1,5 @@
1
- import { MiokiContext } from "mioki";
2
- import type {
3
- MessageEvent,
4
- RecvAtElement,
5
- RecvElement,
6
- RecvFaceElement,
7
- RecvFileElement,
8
- RecvForwardElement,
9
- RecvImageElement,
10
- RecvJsonElement,
11
- RecvRecordElement,
12
- RecvReplyElement,
13
- RecvTextElement,
14
- RecvVideoElement,
15
- } from "napcat-sdk";
1
+ import type { Bot, CommandDefinition, MessageEvent, MessageInput, MessageTarget, MiokuContext, MessageSegment } from "mioku";
2
+ import {createGroupRef} from "mioku";
16
3
  import { extractImageUrl } from "../config";
17
4
  import { replyAdminErrorNotice } from "./notice";
18
5
 
@@ -23,80 +10,70 @@ function parseProfileSex(value: string): 0 | 1 | 2 {
23
10
  return 0;
24
11
  }
25
12
 
26
- function toSendSegment(ctx: MiokiContext, seg: RecvElement): any | null {
27
- if (seg.type === "text") {
28
- const text = String((seg as RecvTextElement).text || "");
29
- return text ? ctx.segment.text(text) : null;
30
- }
31
-
32
- if (seg.type === "image" || seg.type === "record" || seg.type === "video") {
33
- const mediaSeg = seg as
34
- | RecvImageElement
35
- | RecvRecordElement
36
- | RecvVideoElement;
37
- const source = String(mediaSeg.url || mediaSeg.file || "").trim();
38
- if (!source) return null;
39
- if (seg.type === "image") {
40
- return ctx.segment.image(source);
13
+ function toSendSegment(ctx: MiokuContext, seg: MessageSegment): MessageSegment | null {
14
+ const data = seg.data as Record<string, unknown>;
15
+ switch (seg.type) {
16
+ case "text": {
17
+ const text = String(data.text ?? "").trim();
18
+ return text ? ctx.segment.text(text) : null;
41
19
  }
42
- if (seg.type === "record") {
43
- return (ctx.segment as any).record(source);
20
+ case "image": {
21
+ const source = String(data.url ?? data.file ?? "").trim();
22
+ return source ? ctx.segment.image(source) : null;
44
23
  }
45
- return (ctx.segment as any).video(source);
46
- }
47
-
48
- if (seg.type === "file") {
49
- const fileSeg = seg as RecvFileElement;
50
- const source = String(fileSeg.url || fileSeg.file || "").trim();
51
- if (!source) return null;
52
- return (ctx.segment as any).file(source);
53
- }
54
-
55
- if (seg.type === "at") {
56
- const qq = (seg as RecvAtElement).qq;
57
- return qq == null ? null : ctx.segment.at(String(qq));
58
- }
59
-
60
- if (seg.type === "face") {
61
- const id = (seg as RecvFaceElement).id;
62
- return id == null ? null : ctx.segment.face(Number(id));
63
- }
64
-
65
- if (seg.type === "reply") {
66
- const id = (seg as RecvReplyElement).id;
67
- return id == null ? null : ctx.segment.reply(String(id));
68
- }
69
-
70
- if (seg.type === "forward") {
71
- const id = (seg as RecvForwardElement).id;
72
- return id == null
73
- ? null
74
- : ((ctx.segment as any).forward?.(String(id)) ?? null);
75
- }
76
-
77
- if (seg.type === "json") {
78
- const data = (seg as RecvJsonElement).data;
79
- return ctx.segment.json ? ctx.segment.json(data) : null;
24
+ case "record": {
25
+ const source = String(data.file ?? data.url ?? "").trim();
26
+ return source ? ctx.segment.raw("record", { file: source }) : null;
27
+ }
28
+ case "video": {
29
+ const source = String(data.file ?? data.url ?? "").trim();
30
+ return source ? ctx.segment.raw("video", { file: source }) : null;
31
+ }
32
+ case "file": {
33
+ const source = String(data.file ?? data.url ?? "").trim();
34
+ return source ? ctx.segment.raw("file", { file: source }) : null;
35
+ }
36
+ case "at": {
37
+ const target = String(data.qq ?? data.target ?? "");
38
+ return target && target !== "all" ? ctx.segment.at(target) : null;
39
+ }
40
+ case "face": {
41
+ const id = data.id;
42
+ return id == null ? null : ctx.segment.raw("face", { id: String(id) });
43
+ }
44
+ case "reply": {
45
+ const id = data.message_id ?? data.id;
46
+ return id == null ? null : ctx.segment.reply(String(id));
47
+ }
48
+ case "forward": {
49
+ const id = data.id;
50
+ return id == null ? null : ctx.segment.raw("forward", { id: String(id) });
51
+ }
52
+ case "json": {
53
+ return ctx.segment.raw("json", data);
54
+ }
55
+ default:
56
+ return seg;
80
57
  }
81
-
82
- return null;
83
58
  }
84
59
 
85
60
  function normalizeIncomingSegments(
86
- ctx: MiokiContext,
87
- segments: RecvElement[],
88
- ): any[] {
61
+ ctx: MiokuContext,
62
+ segments: readonly MessageSegment[],
63
+ ): MessageSegment[] {
89
64
  if (!Array.isArray(segments)) return [];
90
- return segments.map((seg) => toSendSegment(ctx, seg)).filter(Boolean);
65
+ return segments
66
+ .map((seg) => toSendSegment(ctx, seg))
67
+ .filter((seg): seg is MessageSegment => seg !== null);
91
68
  }
92
69
 
93
70
  function buildForwardPayloadAfterCommand(
94
- ctx: MiokiContext,
95
- message: RecvElement[],
71
+ ctx: MiokuContext,
72
+ message: readonly MessageSegment[],
96
73
  commandPattern: RegExp,
97
74
  fallbackText?: string,
98
- ): any[] {
99
- const payload: any[] = [];
75
+ ): MessageSegment[] {
76
+ const payload: MessageSegment[] = [];
100
77
  let stripped = false;
101
78
 
102
79
  for (const seg of message) {
@@ -107,7 +84,7 @@ function buildForwardPayloadAfterCommand(
107
84
  }
108
85
  continue;
109
86
  }
110
- const original = String((seg as RecvTextElement).text || "");
87
+ const original = String((seg.data as Record<string, unknown>).text ?? "");
111
88
  if (!stripped) {
112
89
  const nextText = original.replace(commandPattern, "");
113
90
  if (nextText !== original) {
@@ -134,110 +111,72 @@ function buildForwardPayloadAfterCommand(
134
111
  return payload;
135
112
  }
136
113
 
137
- function toForwardMessages(bot: any, nodes: any[]): any[] {
138
- const normalizeElements = (elements: any[]): any[] => {
139
- if (typeof bot?.normalizeSendable === "function") {
140
- return bot.normalizeSendable(elements);
141
- }
142
- return elements.map((element: any) => {
143
- if (
144
- element &&
145
- typeof element === "object" &&
146
- "type" in element &&
147
- "data" in element
148
- ) {
149
- return element;
150
- }
151
- if (element && typeof element === "object" && "type" in element) {
152
- const { type, ...data } = element;
153
- return { type, data };
154
- }
155
- return element;
156
- });
157
- };
158
-
159
- return nodes.map((node: any) => {
160
- const rawNode =
161
- node && typeof node === "object" && "type" in node && "data" in node
162
- ? { type: node.type, ...node.data }
163
- : node;
164
- if (!rawNode || rawNode.type !== "node") {
165
- return normalizeElements([rawNode])[0];
166
- }
167
-
168
- const content = Array.isArray(rawNode.content) ? rawNode.content : [];
169
- if ("id" in rawNode && rawNode.id) {
170
- return {
171
- type: "node",
172
- data: {
173
- user_id: rawNode.user_id,
174
- nickname: rawNode.nickname,
175
- id: rawNode.id,
176
- },
177
- };
178
- }
179
-
180
- return {
181
- type: "node",
182
- data: {
183
- user_id: rawNode.user_id,
184
- nickname: rawNode.nickname,
185
- content: normalizeElements(content),
186
- },
187
- };
188
- });
189
- }
190
-
191
114
  async function sendForwardByEvent(options: {
192
- bot: any;
193
- event: any;
194
- messages: any[];
115
+ bot: Bot;
116
+ event: MessageEvent;
117
+ messages: readonly unknown[];
195
118
  }): Promise<void> {
196
119
  const { bot, event, messages } = options;
197
120
  const chunkSize = 50;
198
121
 
199
- for (let i = 0; i < messages.length; i += chunkSize) {
200
- const chunk = messages.slice(i, i + chunkSize);
201
- if (event?.message_type === "group" && event?.group_id) {
202
- await bot.api("send_group_forward_msg", {
203
- group_id: event.group_id,
204
- messages: chunk,
205
- });
206
- continue;
207
- }
208
-
209
- await bot.api("send_private_forward_msg", {
210
- user_id: event.user_id,
211
- messages: chunk,
212
- });
122
+ const nodes = (messages as Array<{
123
+ type?: string;
124
+ data?: { user_id?: string; nickname?: string; content?: unknown };
125
+ }>)
126
+ .map((node) => ({
127
+ user_id: String(node?.data?.user_id ?? event.user_id ?? ""),
128
+ nickname:
129
+ String(node?.data?.nickname || "") ||
130
+ String(node?.data?.user_id ?? event.user_id ?? "转发"),
131
+ content: Array.isArray(node?.data?.content)
132
+ ? node.data.content
133
+ : [node?.data?.content],
134
+ }))
135
+ .filter((node) => node.content.length > 0)
136
+ .map((node) => ({
137
+ user_id: node.user_id,
138
+ nickname: node.nickname,
139
+ content: node.content as MessageInput,
140
+ }));
141
+
142
+ const target: MessageTarget =
143
+ event.message_type === "group" && event.group_id
144
+ ? { type: "group", group_id: event.group_id }
145
+ : { type: "private", user_id: event.user_id ?? "" };
146
+
147
+ for (let i = 0; i < nodes.length; i += chunkSize) {
148
+ await bot.sendForward(target, nodes.slice(i, i + chunkSize));
213
149
  }
214
150
  }
215
151
 
216
- export function registerPersonalCommands(ctx: MiokiContext) {
217
- ctx.handle("message", async (event: MessageEvent) => {
218
- const text = ctx.text(event)?.trim();
219
- if (!text) return;
220
- if (event.user_id === event.self_id) return;
221
-
222
- const isMaster = ctx.isOwner?.(event) ?? false;
223
-
224
- const selfId = event.self_id;
225
- const bot = ctx.pickBot(selfId);
226
- if (!bot) return;
227
-
228
- const isGroup = event.message_type === "group";
152
+ export function registerPersonalCommands(ctx: MiokuContext) {
153
+ const register = (command: CommandDefinition) => {
154
+ const { handler, ...rest } = command;
155
+ ctx.command({
156
+ ...rest,
157
+ prefixes: false,
158
+ handler: async (c) => {
159
+ if (c.event.user_id === c.event.self_id) return;
160
+ await handler(c);
161
+ },
162
+ });
163
+ };
229
164
 
230
- if (text.startsWith("/改头像")) {
231
- if (!isMaster) {
232
- ctx.logger.warn("[admin] 改头像功能仅主人可用");
233
- return;
234
- }
165
+ register({
166
+ name: "/改头像",
167
+ match: /^\/改头像/,
168
+ permission: "master",
169
+ description: "修改Bot头像",
170
+ handler: async ({ event }) => {
171
+ const bot = event.bot;
172
+ if (!bot) return;
235
173
  const imageUrl = extractImageUrl(event.message);
236
174
  if (!imageUrl) {
237
- return event.reply("图片呢图片呢~", true);
175
+ await event.reply("图片呢图片呢~", true);
176
+ return;
238
177
  }
239
178
  try {
240
- await bot.api("set_qq_avatar", { file: imageUrl });
179
+ await bot.setAvatar(imageUrl);
241
180
  await event.reply("done");
242
181
  } catch (err) {
243
182
  await replyAdminErrorNotice({
@@ -247,22 +186,25 @@ export function registerPersonalCommands(ctx: MiokiContext) {
247
186
  fallbackMessage: `出错了,笨蛋~ ${String(err)}`,
248
187
  error: err,
249
188
  });
250
- return;
251
189
  }
252
- return;
253
- }
190
+ },
191
+ });
254
192
 
255
- if (text.startsWith("/改昵称")) {
256
- if (!isMaster) {
257
- ctx.logger.warn("[admin] 改昵称功能仅主人可用");
258
- return;
259
- }
260
- const nickname = text.replace(/^\/改昵称\s*/, "").trim();
193
+ register({
194
+ name: "/改昵称",
195
+ match: /^\/改昵称/,
196
+ permission: "master",
197
+ description: "修改Bot昵称",
198
+ handler: async ({ event, body }) => {
199
+ const bot = event.bot;
200
+ if (!bot) return;
201
+ const nickname = body.replace(/^\/改昵称\s*/, "").trim();
261
202
  if (!nickname) {
262
- return event.reply("想改成什么昵称呀~", true);
203
+ await event.reply("想改成什么昵称呀~", true);
204
+ return;
263
205
  }
264
206
  try {
265
- await bot.api("set_qq_profile", { nickname });
207
+ await bot.setProfile({ nickname });
266
208
  await event.reply("done");
267
209
  } catch (err) {
268
210
  await replyAdminErrorNotice({
@@ -273,20 +215,24 @@ export function registerPersonalCommands(ctx: MiokiContext) {
273
215
  error: err,
274
216
  });
275
217
  }
276
- return;
277
- }
218
+ },
219
+ });
278
220
 
279
- if (text.startsWith("/改签名")) {
280
- if (!isMaster) {
281
- ctx.logger.warn("[admin] 改签名功能仅主人可用");
282
- return;
283
- }
284
- const personalNote = text.replace(/^\/改签名\s*/, "").trim();
221
+ register({
222
+ name: "/改签名",
223
+ match: /^\/改签名/,
224
+ permission: "master",
225
+ description: "修改Bot个性签名",
226
+ handler: async ({ event, body }) => {
227
+ const bot = event.bot;
228
+ if (!bot) return;
229
+ const personalNote = body.replace(/^\/改签名\s*/, "").trim();
285
230
  if (!personalNote) {
286
- return event.reply("想改成什么签名呀~", true);
231
+ await event.reply("想改成什么签名呀~", true);
232
+ return;
287
233
  }
288
234
  try {
289
- await bot.api("set_qq_profile", { personal_note: personalNote });
235
+ await bot.setProfile({ personal_note: personalNote });
290
236
  await event.reply("done");
291
237
  } catch (err) {
292
238
  await replyAdminErrorNotice({
@@ -297,18 +243,20 @@ export function registerPersonalCommands(ctx: MiokiContext) {
297
243
  error: err,
298
244
  });
299
245
  }
300
- return;
301
- }
246
+ },
247
+ });
302
248
 
303
- if (text.startsWith("/改性别")) {
304
- if (!isMaster) {
305
- ctx.logger.warn("[admin] 改性别功能仅主人可用");
306
- return;
307
- }
308
- const genderText = text.replace(/^\/改性别\s*/, "").trim();
309
- const sex = parseProfileSex(genderText);
249
+ register({
250
+ name: "/改性别",
251
+ match: /^\/改性别/,
252
+ permission: "master",
253
+ description: "修改Bot性别",
254
+ handler: async ({ event, body }) => {
255
+ const bot = event.bot;
256
+ if (!bot) return;
257
+ const sex = parseProfileSex(body.replace(/^\/改性别\s*/, ""));
310
258
  try {
311
- await bot.api("set_qq_profile", { sex });
259
+ await bot.setProfile({ sex });
312
260
  await event.reply("done");
313
261
  } catch (err) {
314
262
  await replyAdminErrorNotice({
@@ -319,15 +267,19 @@ export function registerPersonalCommands(ctx: MiokiContext) {
319
267
  error: err,
320
268
  });
321
269
  }
322
- return;
323
- }
270
+ },
271
+ });
324
272
 
325
- if (text.startsWith("/删好友")) {
326
- if (!isMaster) {
327
- ctx.logger.warn("[admin] 删好友功能仅主人可用");
328
- return;
329
- }
330
- const qq = parseInt(text.replace(/^\/删好友\s*/, "").trim(), 10);
273
+ register({
274
+ name: "/删好友",
275
+ match: /^\/删好友(?:\s|$)/,
276
+ permission: "master",
277
+ description: "删除好友",
278
+ usage: "/删好友 qq",
279
+ handler: async ({ event, body }) => {
280
+ const bot = event.bot;
281
+ if (!bot) return;
282
+ const qq = parseInt(body.replace(/^\/删好友\s*/, "").trim(), 10);
331
283
  if (!qq) {
332
284
  await replyAdminErrorNotice({
333
285
  ctx,
@@ -338,7 +290,7 @@ export function registerPersonalCommands(ctx: MiokiContext) {
338
290
  return;
339
291
  }
340
292
  try {
341
- await bot.api("delete_friend", { user_id: qq });
293
+ await bot.deleteFriend(qq);
342
294
  await event.reply("done");
343
295
  } catch (err) {
344
296
  await replyAdminErrorNotice({
@@ -349,15 +301,19 @@ export function registerPersonalCommands(ctx: MiokiContext) {
349
301
  error: err,
350
302
  });
351
303
  }
352
- return;
353
- }
304
+ },
305
+ });
354
306
 
355
- if (text.startsWith("/退群")) {
356
- if (!isMaster) {
357
- ctx.logger.warn("[admin] 退群功能仅主人可用");
358
- return;
359
- }
360
- const targetGroup = parseInt(text.replace(/^\/退群\s*/, "").trim(), 10);
307
+ register({
308
+ name: "/退群",
309
+ match: /^\/退群(?:\s|$)/,
310
+ permission: "master",
311
+ description: "退出群聊",
312
+ usage: "/退群 群号",
313
+ handler: async ({ event, body }) => {
314
+ const bot = event.bot;
315
+ if (!bot) return;
316
+ const targetGroup = parseInt(body.replace(/^\/退群\s*/, "").trim(), 10);
361
317
  if (!targetGroup) {
362
318
  await replyAdminErrorNotice({
363
319
  ctx,
@@ -368,10 +324,7 @@ export function registerPersonalCommands(ctx: MiokiContext) {
368
324
  return;
369
325
  }
370
326
  try {
371
- await bot.api("set_group_leave", {
372
- group_id: targetGroup,
373
- is_dismiss: false,
374
- });
327
+ await createGroupRef(bot, String(targetGroup)).leave(false);
375
328
  await event.reply("done");
376
329
  } catch (err) {
377
330
  await replyAdminErrorNotice({
@@ -382,15 +335,19 @@ export function registerPersonalCommands(ctx: MiokiContext) {
382
335
  error: err,
383
336
  });
384
337
  }
385
- return;
386
- }
338
+ },
339
+ });
387
340
 
388
- if (text.startsWith("/发好友")) {
389
- if (!isMaster) {
390
- ctx.logger.warn("[admin] 发好友功能仅主人可用");
391
- return;
392
- }
393
- const matched = text.match(/^\/发好友\s*(\d+)\s*([\s\S]*)$/);
341
+ register({
342
+ name: "/发好友",
343
+ match: /^\/发好友(?:\s|$)/,
344
+ permission: "master",
345
+ description: "给好友发送私聊消息",
346
+ usage: "/发好友 qq号 内容",
347
+ handler: async ({ event, body }) => {
348
+ const bot = event.bot;
349
+ if (!bot) return;
350
+ const matched = body.match(/^\/发好友\s*(\d+)\s*([\s\S]*)$/);
394
351
  if (!matched) {
395
352
  await replyAdminErrorNotice({
396
353
  ctx,
@@ -420,7 +377,7 @@ export function registerPersonalCommands(ctx: MiokiContext) {
420
377
  return;
421
378
  }
422
379
  try {
423
- await bot.sendPrivateMsg(targetUser, payload);
380
+ await bot.sendMessage({ type: "private", user_id: targetUser}, payload);
424
381
  await event.reply("done");
425
382
  } catch (err) {
426
383
  await replyAdminErrorNotice({
@@ -431,15 +388,19 @@ export function registerPersonalCommands(ctx: MiokiContext) {
431
388
  error: err,
432
389
  });
433
390
  }
434
- return;
435
- }
391
+ },
392
+ });
436
393
 
437
- if (text.startsWith("/发群聊")) {
438
- if (!isMaster) {
439
- ctx.logger.warn("[admin] 发群聊功能仅主人可用");
440
- return;
441
- }
442
- const matched = text.match(/^\/发群聊\s*(\d+)\s*([\s\S]*)$/);
394
+ register({
395
+ name: "/发群聊",
396
+ match: /^\/发群聊(?:\s|$)/,
397
+ permission: "master",
398
+ description: "给群聊发送消息",
399
+ usage: "/发群聊 群号 内容",
400
+ handler: async ({ event, body }) => {
401
+ const bot = event.bot;
402
+ if (!bot) return;
403
+ const matched = body.match(/^\/发群聊\s*(\d+)\s*([\s\S]*)$/);
443
404
  if (!matched) {
444
405
  await replyAdminErrorNotice({
445
406
  ctx,
@@ -469,7 +430,7 @@ export function registerPersonalCommands(ctx: MiokiContext) {
469
430
  return;
470
431
  }
471
432
  try {
472
- await bot.sendGroupMsg(targetGroup, payload);
433
+ await bot.sendMessage({ type: "group", group_id: targetGroup}, payload);
473
434
  await event.reply("done");
474
435
  } catch (err) {
475
436
  await replyAdminErrorNotice({
@@ -480,20 +441,23 @@ export function registerPersonalCommands(ctx: MiokiContext) {
480
441
  error: err,
481
442
  });
482
443
  }
483
- return;
484
- }
444
+ },
445
+ });
485
446
 
486
- if (text === "/全部好友") {
487
- if (!isMaster) {
488
- ctx.logger.warn("[admin] 全部好友功能仅主人可用");
489
- return;
490
- }
491
- if (isGroup) {
447
+ register({
448
+ name: "/全部好友",
449
+ match: /^\/全部好友$/,
450
+ permission: "master",
451
+ description: "获取全部好友列表",
452
+ handler: async ({ event }) => {
453
+ const bot = event.bot;
454
+ if (!bot) return;
455
+ if (event.message_type === "group") {
492
456
  await event.reply("在私聊使用试试看吧~", true);
493
457
  return;
494
458
  }
495
459
  try {
496
- const friendList: any[] = await bot.api("get_friend_list");
460
+ const friendList = await bot.getFriendList();
497
461
  if (!Array.isArray(friendList) || friendList.length === 0) {
498
462
  await replyAdminErrorNotice({
499
463
  ctx,
@@ -503,9 +467,9 @@ export function registerPersonalCommands(ctx: MiokiContext) {
503
467
  });
504
468
  return;
505
469
  }
506
- const nodes = friendList.map((friend: any) =>
507
- ctx.segment.node({
508
- user_id: String(friend.user_id),
470
+ const nodes = friendList.map((friend) =>
471
+ ctx.segment.raw("node", {
472
+ user_id: friend.user_id,
509
473
  nickname:
510
474
  friend.nickname || friend.remark || String(friend.user_id),
511
475
  content: [
@@ -518,11 +482,7 @@ export function registerPersonalCommands(ctx: MiokiContext) {
518
482
  ],
519
483
  }),
520
484
  );
521
- await sendForwardByEvent({
522
- bot,
523
- event,
524
- messages: toForwardMessages(bot, nodes),
525
- });
485
+ await sendForwardByEvent({ bot, event, messages: nodes });
526
486
  } catch (err) {
527
487
  await replyAdminErrorNotice({
528
488
  ctx,
@@ -532,20 +492,23 @@ export function registerPersonalCommands(ctx: MiokiContext) {
532
492
  error: err,
533
493
  });
534
494
  }
535
- return;
536
- }
495
+ },
496
+ });
537
497
 
538
- if (text === "/全部群聊") {
539
- if (!isMaster) {
540
- ctx.logger.warn("[admin] 全部群聊功能仅主人可用");
541
- return;
542
- }
543
- if (isGroup) {
498
+ register({
499
+ name: "/全部群聊",
500
+ match: /^\/全部群聊$/,
501
+ permission: "master",
502
+ description: "获取全部群聊列表",
503
+ handler: async ({ event }) => {
504
+ const bot = event.bot;
505
+ if (!bot) return;
506
+ if (event.message_type === "group") {
544
507
  await event.reply("在私聊使用试试看吧~", true);
545
508
  return;
546
509
  }
547
510
  try {
548
- const groupList: any[] = await bot.api("get_group_list");
511
+ const groupList = await bot.getGroupList();
549
512
  if (!Array.isArray(groupList) || groupList.length === 0) {
550
513
  await replyAdminErrorNotice({
551
514
  ctx,
@@ -555,10 +518,10 @@ export function registerPersonalCommands(ctx: MiokiContext) {
555
518
  });
556
519
  return;
557
520
  }
558
- const nodes = groupList.map((group: any) =>
559
- ctx.segment.node({
560
- user_id: String(selfId),
561
- nickname: String(selfId),
521
+ const nodes = groupList.map((group) =>
522
+ ctx.segment.raw("node", {
523
+ user_id: event.self_id ?? "",
524
+ nickname: String(event.self_id ?? ""),
562
525
  content: [
563
526
  ctx.segment.image(
564
527
  `https://p.qlogo.cn/gh/${group.group_id}/${group.group_id}/640/`,
@@ -569,11 +532,7 @@ export function registerPersonalCommands(ctx: MiokiContext) {
569
532
  ],
570
533
  }),
571
534
  );
572
- await sendForwardByEvent({
573
- bot,
574
- event,
575
- messages: toForwardMessages(bot, nodes),
576
- });
535
+ await sendForwardByEvent({ bot, event, messages: nodes });
577
536
  } catch (err) {
578
537
  await replyAdminErrorNotice({
579
538
  ctx,
@@ -583,7 +542,6 @@ export function registerPersonalCommands(ctx: MiokiContext) {
583
542
  error: err,
584
543
  });
585
544
  }
586
- return;
587
- }
545
+ },
588
546
  });
589
547
  }