mioku-plugin-help 3.0.2 → 3.0.4

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/help/role.ts CHANGED
@@ -33,7 +33,7 @@ export function canInvokeCommand(
33
33
  * Resolve the requesting user's effective role for help filtering.
34
34
  *
35
35
  * - `master` is honored everywhere (private or group) — it comes from
36
- * mioki's `isOwner` allowlist.
36
+ * mioki's `isMaster` allowlist (the bot `owners` list).
37
37
  * - Private chat skips the `isAdmin` check and defaults to `admin`,
38
38
  * so anyone DM-ing the bot can see admin-level commands.
39
39
  * - Group chat resolves bot admin (mioki `isAdmin` allowlist), group
@@ -49,7 +49,7 @@ export async function resolveViewerRole(
49
49
  ctx: any,
50
50
  event: any,
51
51
  ): Promise<CommandRole> {
52
- if (ctx?.isOwner?.(event)) {
52
+ if (ctx?.isMaster?.(event)) {
53
53
  return "master";
54
54
  }
55
55
 
package/index.ts CHANGED
@@ -21,8 +21,6 @@ import { createHelpSkills } from "./skills";
21
21
 
22
22
  const helpPlugin = definePlugin({
23
23
  name: "help",
24
- version: "2.1.0",
25
- description: "帮助插件,生成帮助图片,并提供 #状态 指令",
26
24
 
27
25
  async setup(ctx: MiokuContext) {
28
26
  // 启动后台采样器
@@ -75,7 +73,7 @@ const helpPlugin = definePlugin({
75
73
  for (const skill of createHelpSkills()) aiService.registerSkill(skill);
76
74
  }
77
75
 
78
- ctx.handle("message", async (event) => {
76
+ const handleHelpMessage = async (event: import("mioku").MessageEvent) => {
79
77
  const text = ctx.text(event);
80
78
  if (!text) {
81
79
  return;
@@ -155,6 +153,25 @@ const helpPlugin = definePlugin({
155
153
  ctx.logger.error(`生成帮助图片失败: ${error}`);
156
154
  await event.reply(`生成帮助图片失败: ${error}`);
157
155
  }
156
+ };
157
+
158
+ ctx.command({
159
+ id: "帮助菜单",
160
+ name: "help",
161
+ aliases: ["帮助", "菜单"],
162
+ match: /^(?:help|帮助|菜单)(?:\s|$)/i,
163
+ prefixes: ["#", "/", ""],
164
+ description: "生成帮助图片",
165
+ handler: ({ event }) => handleHelpMessage(event),
166
+ });
167
+ ctx.command({
168
+ id: "status",
169
+ name: "status",
170
+ aliases: ["状态", "zt"],
171
+ match: /^(?:status|状态|zt)(?:\s|$)/i,
172
+ prefixes: ["#", "/", ""],
173
+ description: "生成系统状态图片",
174
+ handler: ({ event }) => handleHelpMessage(event),
158
175
  });
159
176
 
160
177
  return () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mioku-plugin-help",
3
- "version": "3.0.2",
3
+ "version": "3.0.4",
4
4
  "description": "帮助插件,使用截图服务生成美观的帮助图片,并提供 #状态 指令",
5
5
  "main": "index.ts",
6
6
  "type": "module",
@@ -17,18 +17,6 @@
17
17
  "screenshot",
18
18
  "config",
19
19
  "ai"
20
- ],
21
- "accessHooks": [
22
- {
23
- "id": "帮助菜单",
24
- "match": "/(?:[#/]\\s*(?:帮助|菜单|help)|^(?:帮助|菜单|help)(?:\\s|$))/",
25
- "description": "匹配 #help / 帮助 / 菜单 等帮助指令"
26
- },
27
- {
28
- "id": "状态",
29
- "match": "/(?:[#/]\\s*(?:状态|zt|status)|^(?:状态|zt|status)(?:\\s|$))/",
30
- "description": "匹配 #状态 / 状态 / zt / status 等系统状态指令"
31
- }
32
20
  ]
33
21
  },
34
22
  "peerDependencies": {
@@ -200,8 +200,7 @@ async function collectBots(
200
200
  }
201
201
  // stdin 适配器恒排第一位(终端是本机主人通道,优先展示)
202
202
  const sorted = [...bots].sort(
203
- (a, b) =>
204
- Number(b.adapter === "stdin") - Number(a.adapter === "stdin"),
203
+ (a, b) => Number(b.adapter === "stdin") - Number(a.adapter === "stdin"),
205
204
  );
206
205
  return collectBotStatuses(sorted, ctx, stdinAvatar);
207
206
  }
@@ -226,11 +225,14 @@ async function collectBotStatuses(
226
225
  version: adapter.version,
227
226
  impl: adapter.impl,
228
227
  }));
229
- const report = await withTimeout(buildAdapterReport({ bots, adapters })).catch(
230
- () => null,
231
- );
228
+ const report = await withTimeout(
229
+ buildAdapterReport({ bots, adapters }),
230
+ ).catch(() => null);
232
231
  const instances = new Map<string, AdapterInstanceStatus>();
233
- const implOf = new Map<string, { name: string; version?: string } | undefined>();
232
+ const implOf = new Map<
233
+ string,
234
+ { name: string; version?: string } | undefined
235
+ >();
234
236
  for (const entry of report?.adapters ?? []) {
235
237
  implOf.set(entry.name, entry.impl);
236
238
  for (const instance of entry.instances) {
@@ -238,37 +240,58 @@ async function collectBotStatuses(
238
240
  }
239
241
  }
240
242
 
241
- return bots.map((bot) => {
242
- const uin = String(bot.bot_id);
243
- const adapter = String(bot.adapter);
244
- const isStdin = adapter === "stdin";
245
- const instance = instances.get(`${adapter}:${uin}`);
246
- const lib = implOf.get(adapter);
247
- // 实现端优先用平台自报的(NapCat / LLOneBot),否则退回适配器的底层库(ICQQ)
248
- const framework = instance?.impl || lib?.name || adapter;
249
- const appVersion = instance?.version || lib?.version || "";
243
+ return Promise.all(
244
+ bots.map(async (bot) => {
245
+ const uin = String(bot.bot_id);
246
+ const adapter = String(bot.adapter);
247
+ const isStdin = adapter === "stdin";
248
+ const instance = instances.get(`${adapter}:${uin}`);
249
+ const lib = implOf.get(adapter);
250
+ const framework = instance?.impl || lib?.name || adapter;
251
+ const appVersion = instance?.version || lib?.version || "";
252
+ const avatarFromBot =
253
+ typeof bot.getAvatar === "function"
254
+ ? await bot.getAvatar().catch(() => null)
255
+ : null;
256
+ // 协议端版本文案:平台自报的用 impl/version(如 LLOneBot/7.12.4),
257
+ // 平台不自报的退回适配器元数据(如 ICQQ v1.12.3),与 .status 的展示约定一致
258
+ const implLabel = isStdin
259
+ ? "stdin"
260
+ : instance?.impl
261
+ ? instance.version
262
+ ? `${instance.impl}/${instance.version}`
263
+ : String(instance.impl)
264
+ : lib?.name
265
+ ? lib.version
266
+ ? `${lib.name} v${lib.version}`
267
+ : lib.name
268
+ : "";
250
269
 
251
- return {
252
- uin,
253
- nickname: String(bot.nickname || "Unknown Bot"),
254
- avatarUrl: isStdin
255
- ? toImageSrc(stdinAvatar || "")
256
- : `https://q1.qlogo.cn/g?b=qq&nk=${uin}&s=160`,
257
- adapter,
258
- framework: isStdin ? adapter : framework,
259
- appVersion: isStdin
260
- ? String(ctx.getAdapter(adapter)?.version ?? "")
261
- : appVersion,
262
- protocolVersion: instance?.protocol ?? "",
263
- platform: instance?.platform ?? "",
264
- platformVersion: instance?.platformVersion ?? "",
265
- online: bot.online,
266
- groupCount: instance?.stats.groups ?? 0,
267
- friendCount: instance?.stats.friends ?? 0,
268
- send: instance?.stats.sent ?? 0,
269
- receive: instance?.stats.received ?? 0,
270
- };
271
- });
270
+ return {
271
+ uin,
272
+ nickname: String(bot.nickname || "Unknown Bot"),
273
+ avatarUrl: isStdin
274
+ ? toImageSrc(stdinAvatar || "")
275
+ : avatarFromBot
276
+ ? toImageSrc(avatarFromBot)
277
+ : `https://q1.qlogo.cn/g?b=qq&nk=${uin}&s=160`,
278
+ adapter,
279
+ implLabel,
280
+ framework: isStdin ? adapter : framework,
281
+ appVersion: isStdin
282
+ ? String(ctx.getAdapter(adapter)?.version ?? "")
283
+ : appVersion,
284
+ protocolVersion: instance?.protocol ?? "",
285
+ platform: instance?.platform ?? "",
286
+ platformVersion: instance?.platformVersion ?? "",
287
+ online: bot.online,
288
+ groupCount: instance?.stats.groups ?? 0,
289
+ friendCount: instance?.stats.friends ?? 0,
290
+ send: instance?.stats.sent ?? 0,
291
+ receive: instance?.stats.received ?? 0,
292
+ };
293
+ }),
294
+ );
272
295
  }
273
296
 
274
297
  async function collectFramework(
@@ -140,13 +140,7 @@ function renderHero(
140
140
  .map((bot) => {
141
141
  const statusText = bot.online ? "在线" : "离线";
142
142
  const statusColor = bot.online ? "#10b981" : "#ef4444";
143
- const adapterLabel = displayAdapterId(bot.adapter);
144
- const implText =
145
- bot.adapter === "stdin"
146
- ? `${adapterLabel}@${bot.appVersion}`
147
- : bot.appVersion
148
- ? `${bot.framework} ${bot.appVersion}${bot.protocolVersion ? ` · 协议 ${bot.protocolVersion}` : ""}`
149
- : bot.framework;
143
+ const implText = bot.implLabel;
150
144
  const platformText = bot.platform
151
145
  ? `${bot.platform}${bot.platformVersion ? ` v${bot.platformVersion}` : ""}`
152
146
  : "";
@@ -159,7 +153,7 @@ function renderHero(
159
153
  { text: `群聊 ${fmtNumber(bot.groupCount)}`, kind: "data" },
160
154
  { text: `收 ${fmtNumber(bot.receive)}`, kind: "data" },
161
155
  { text: `发 ${fmtNumber(bot.send)}`, kind: "data" },
162
- { text: implText, kind: "data" },
156
+ ...(implText ? [{ text: implText, kind: "data" }] : []),
163
157
  ...(platformText ? [{ text: platformText, kind: "data" }] : []),
164
158
  ];
165
159
  const tagsHtml = tags
package/status/types.ts CHANGED
@@ -13,6 +13,8 @@ export interface BotAccountStatus {
13
13
  avatarUrl: string;
14
14
  /** 适配器名,如 "stdin" / "onebotv11" / "icqq"。 */
15
15
  adapter: string;
16
+ /** 协议端与版本的可展示文案,如 "ICQQ v1.12.3" / "LLOneBot/7.12.4",取不到时为空串 */
17
+ implLabel: string;
16
18
  /** 实现端名称,如 "NapCat" / "LLOneBot" / "ICQQ",取不到时为空串 */
17
19
  framework: string;
18
20
  /** 实现端版本,如 "5.0.6"*/