mioku-plugin-admin 2.3.2 → 2.3.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.
@@ -29,6 +29,13 @@ const VERIFY_MODE_LABELS: Record<string, string> = {
29
29
  chiral: "手性碳",
30
30
  };
31
31
 
32
+ async function extractQuoteImageUrls(event: any): Promise<string[]> {
33
+ if (!event || typeof event.getQuoteMsg !== "function") return [];
34
+ const quoteMsg = await event.getQuoteMsg().catch(() => null);
35
+ if (!quoteMsg || !Array.isArray(quoteMsg.message)) return [];
36
+ return extractImageUrls(quoteMsg.message);
37
+ }
38
+
32
39
  export function registerVerifyCommands(options: VerifyCommandOptions) {
33
40
  const { ctx, getVerifyConfig, setVerifyConfig, verifyController } = options;
34
41
 
@@ -250,29 +257,32 @@ export function registerVerifyCommands(options: VerifyCommandOptions) {
250
257
 
251
258
  // /入群提示 xxx
252
259
  // /入群提示 关闭
253
- // /入群提示(带图片)— 仅图片也可
260
+ // /入群提示(带图片,可来自当前消息或引用消息)— 仅图片也可
254
261
  if (text.startsWith("/入群提示") || text.startsWith("#入群提示")) {
255
262
  if (!(await ensureAdminPermission())) return;
256
263
 
257
264
  const rawArg = text.replace(/^[/#]入群提示\s*/, "").trim();
258
265
  const current = getVerifyConfig();
259
266
  const groupCfg = getGroupVerifyConfig(current, groupId);
260
- const imageUrls = extractImageUrls(event.message);
267
+ const directImageUrls = extractImageUrls(event.message);
268
+ const quoteImageUrls = await extractQuoteImageUrls(event).catch(() => []);
269
+ const imageUrls =
270
+ directImageUrls.length > 0 ? directImageUrls : quoteImageUrls;
261
271
 
262
272
  if (rawArg === "关闭" || rawArg === "清空" || rawArg === "关") {
263
273
  if (!hasCustomPrompt(groupCfg)) {
264
274
  await event.reply("本群还没设置自定义入群提示哦~", true);
265
275
  return;
266
276
  }
267
- const removedFiles = [...groupCfg.promptImages];
277
+ const removedFile = groupCfg.promptImage;
268
278
  const next = upsertGroupVerifyConfig(current, groupId, {
269
279
  customPrompt: "",
270
- promptImages: [],
280
+ promptImage: "",
271
281
  });
272
282
  await setVerifyConfig(next);
273
283
  await pruneGroupPromptImages(groupId, []).catch(() => {});
274
284
  ctx.logger.info(
275
- `admin verify 关闭群 ${groupId} 自定义入群提示,清理图片 ${removedFiles.join(", ")}`,
285
+ `admin verify 关闭群 ${groupId} 自定义入群提示,清理图片 ${removedFile}`,
276
286
  );
277
287
  await event.reply("已关闭本群自定义入群提示~", true);
278
288
  return;
@@ -281,23 +291,26 @@ export function registerVerifyCommands(options: VerifyCommandOptions) {
281
291
  if (!rawArg && !imageUrls.length) {
282
292
  if (!hasCustomPrompt(groupCfg)) {
283
293
  await event.reply(
284
- "用法:/入群提示 xxx(最多 50 字),可附带图片一起发送;/入群提示 关闭 关闭自定义提示~",
294
+ "用法:/入群提示 xxx(最多 50 字),可附带图片(当前消息或引用消息中的图片)一起发送;/入群提示 关闭 关闭自定义提示~",
285
295
  true,
286
296
  );
287
297
  return;
288
298
  }
289
- const summary = [
290
- `已开启本群自定义入群提示`,
299
+ const lines = ["已开启本群自定义入群提示"];
300
+ lines.push(
291
301
  groupCfg.customPrompt
292
302
  ? `文字:${groupCfg.customPrompt}`
293
303
  : "文字:(未设置)",
294
- `图片:${groupCfg.promptImages.length} 张`,
295
- ].join("\n");
296
- await event.reply(summary, true);
304
+ );
305
+ lines.push(
306
+ groupCfg.promptImage
307
+ ? `图片:已设置(filename=${groupCfg.promptImage})`
308
+ : "图片:(未设置)",
309
+ );
310
+ await event.reply(lines.join("\n"), true);
297
311
  return;
298
312
  }
299
313
 
300
- let nextPrompt = groupCfg.customPrompt;
301
314
  if (rawArg) {
302
315
  const argLength = Array.from(rawArg).length;
303
316
  if (argLength > MAX_CUSTOM_PROMPT_LENGTH) {
@@ -309,39 +322,46 @@ export function registerVerifyCommands(options: VerifyCommandOptions) {
309
322
  });
310
323
  return;
311
324
  }
312
- nextPrompt = normalizeCustomPrompt(rawArg);
313
325
  }
326
+ const nextPrompt = rawArg
327
+ ? normalizeCustomPrompt(rawArg)
328
+ : groupCfg.customPrompt;
314
329
 
315
- const saved: string[] = [];
316
- const failed: number = imageUrls.length;
317
- for (const url of imageUrls) {
318
- const savedImage = await saveRemoteImageAsPrompt(groupId, url);
319
- if (savedImage) saved.push(savedImage.filename);
330
+ let nextImage = groupCfg.promptImage;
331
+ let imageNote = "";
332
+ if (imageUrls.length) {
333
+ const targetUrl = imageUrls[0];
334
+ const savedImage = await saveRemoteImageAsPrompt(groupId, targetUrl);
335
+ if (savedImage) {
336
+ const previousImage = groupCfg.promptImage;
337
+ nextImage = savedImage.filename;
338
+ imageNote = previousImage
339
+ ? `图片已替换(旧文件 ${previousImage} 已删除)`
340
+ : `图片已设置(${savedImage.filename})`;
341
+ if (previousImage) {
342
+ ctx.logger.info(
343
+ `admin verify 替换群 ${groupId} 入群提示图片:${previousImage} -> ${savedImage.filename}`,
344
+ );
345
+ }
346
+ } else {
347
+ imageNote = "图片下载失败,请稍后重试";
348
+ }
320
349
  }
321
350
 
322
- const nextImages = imageUrls.length
323
- ? [...groupCfg.promptImages, ...saved]
324
- : groupCfg.promptImages;
325
-
326
351
  const next = upsertGroupVerifyConfig(current, groupId, {
327
352
  customPrompt: nextPrompt,
328
- promptImages: nextImages,
353
+ promptImage: nextImage,
329
354
  });
330
355
  await setVerifyConfig(next);
331
- await pruneGroupPromptImages(groupId, nextImages).catch(() => {});
356
+ await pruneGroupPromptImages(groupId, nextImage ? [nextImage] : []).catch(
357
+ () => {},
358
+ );
332
359
 
333
- const lines: string[] = ["已更新本群自定义入群提示~"];
360
+ const lines = ["已更新本群自定义入群提示"];
334
361
  if (rawArg) lines.push(`文字:${nextPrompt}`);
335
362
  else if (groupCfg.customPrompt)
336
363
  lines.push(`文字(保持):${groupCfg.customPrompt}`);
337
- if (imageUrls.length) {
338
- lines.push(
339
- saved.length
340
- ? `新增图片 ${saved.length} 张${failed > saved.length ? `,${failed - saved.length} 张保存失败` : ""}`
341
- : `图片保存失败 ${failed} 张,请稍后重试`,
342
- );
343
- }
344
- lines.push(`当前共 ${nextImages.length} 张图片`);
364
+ if (imageUrls.length && imageNote) lines.push(imageNote);
345
365
  await event.reply(lines.join("\n"), true);
346
366
  return;
347
367
  }
package/config.md CHANGED
@@ -106,11 +106,11 @@ fields:
106
106
  description: 一旦设置该群将跳过 AI 生成欢迎,验证通过后直接发送这段文字(可附带本地图片一起)。上限 50 字,建议在群里直接用 /入群提示 xxx 设置或 /入群提示 关闭 关闭。
107
107
  placeholder: 例:欢迎来到本群,请先看群公告~
108
108
 
109
- - key: promptImages
109
+ - key: promptImage
110
110
  label: 群自定义入群提示图片
111
- type: textarea
112
- description: 图片文件名列表(保存在 data/admin/<群号>/prompt-images/),与文字一起发送。在群里发送 /入群提示 xxx 时附带图片即可追加;/入群提示 关闭 会一并清空图片。
113
- placeholder: 例:["prompt-l8t9a-3f2c.png"]
111
+ type: text
112
+ description: 单张图片文件名(保存在 data/admin/<群号>/prompt-images/),与文字一起发送。在群里发送 /入群提示 时附带图片(当前消息或引用消息中的图片均可)即可替换;/入群提示 关闭 会一并清空。
113
+ placeholder: 例:prompt-l8t9a-3f2c.png
114
114
 
115
115
  - key: verify.reactionEmojiId
116
116
  label: 回应模式表态表情ID
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mioku-plugin-admin",
3
- "version": "2.3.2",
3
+ "version": "2.3.3",
4
4
  "description": "管理插件,提供事件通知与群管/个人管理指令",
5
5
  "main": "index.ts",
6
6
  "type": "module",
package/verify/config.ts CHANGED
@@ -7,7 +7,7 @@ export interface VerifyGroupConfig {
7
7
  enabled: boolean;
8
8
  mode: VerifyMode;
9
9
  customPrompt: string;
10
- promptImages: string[];
10
+ promptImage: string;
11
11
  }
12
12
 
13
13
  export interface VerifyConfig {
@@ -50,25 +50,16 @@ export function normalizeCustomPrompt(value: unknown): string {
50
50
  return Array.from(trimmed).slice(0, MAX_CUSTOM_PROMPT_LENGTH).join("");
51
51
  }
52
52
 
53
- export function normalizePromptImages(value: unknown): string[] {
54
- if (!Array.isArray(value)) return [];
55
- const seen = new Set<string>();
56
- const result: string[] = [];
57
- for (const item of value) {
58
- if (typeof item !== "string") continue;
59
- const name = item.trim();
60
- if (!name || seen.has(name)) continue;
61
- seen.add(name);
62
- result.push(name);
63
- }
64
- return result;
53
+ export function normalizePromptImage(value: unknown): string {
54
+ if (typeof value !== "string") return "";
55
+ return value.trim();
65
56
  }
66
57
 
67
58
  export function hasCustomPrompt(groupCfg: VerifyGroupConfig | undefined): boolean {
68
59
  if (!groupCfg) return false;
69
60
  return (
70
61
  Boolean(groupCfg.customPrompt && groupCfg.customPrompt.trim()) ||
71
- groupCfg.promptImages.length > 0
62
+ Boolean(groupCfg.promptImage && groupCfg.promptImage.trim())
72
63
  );
73
64
  }
74
65
 
@@ -81,6 +72,11 @@ export function normalizeVerifyMode(value: unknown): VerifyMode {
81
72
 
82
73
  function normalizeVerifyGroup(raw: any): VerifyGroupConfig {
83
74
  const groupId = Number(raw?.groupId || raw?.group_id || 0);
75
+ const promptImageRaw =
76
+ raw?.promptImage ??
77
+ raw?.prompt_image ??
78
+ (Array.isArray(raw?.promptImages) ? raw.promptImages[0] : undefined) ??
79
+ (Array.isArray(raw?.images) ? raw.images[0] : undefined);
84
80
  return {
85
81
  groupId: groupId > 0 ? groupId : 0,
86
82
  enabled: raw?.enabled === true,
@@ -88,9 +84,7 @@ function normalizeVerifyGroup(raw: any): VerifyGroupConfig {
88
84
  customPrompt: normalizeCustomPrompt(
89
85
  raw?.customPrompt ?? raw?.custom_prompt ?? raw?.extraPrompt,
90
86
  ),
91
- promptImages: normalizePromptImages(
92
- raw?.promptImages ?? raw?.prompt_images ?? raw?.images,
93
- ),
87
+ promptImage: normalizePromptImage(promptImageRaw),
94
88
  };
95
89
  }
96
90
 
@@ -157,7 +151,7 @@ export function getGroupVerifyConfig(
157
151
  enabled: false,
158
152
  mode: "reaction",
159
153
  customPrompt: "",
160
- promptImages: [],
154
+ promptImage: "",
161
155
  };
162
156
  }
163
157
 
@@ -174,8 +168,8 @@ export function upsertGroupVerifyConfig(
174
168
  if (patch.customPrompt !== undefined) {
175
169
  normalizedPatch.customPrompt = normalizeCustomPrompt(patch.customPrompt);
176
170
  }
177
- if (patch.promptImages !== undefined) {
178
- normalizedPatch.promptImages = normalizePromptImages(patch.promptImages);
171
+ if (patch.promptImage !== undefined) {
172
+ normalizedPatch.promptImage = normalizePromptImage(patch.promptImage);
179
173
  }
180
174
  if (idx >= 0) {
181
175
  next.groups = config.groups.map((g, i) =>
@@ -189,7 +183,7 @@ export function upsertGroupVerifyConfig(
189
183
  enabled: false,
190
184
  mode: "reaction",
191
185
  customPrompt: "",
192
- promptImages: [],
186
+ promptImage: "",
193
187
  ...normalizedPatch,
194
188
  },
195
189
  ];
package/verify/index.ts CHANGED
@@ -367,15 +367,16 @@ export function createVerifyController(
367
367
  if (prompt) {
368
368
  segments.push(ctx.segment.text(prompt));
369
369
  }
370
- for (const filename of groupCfg.promptImages) {
370
+ const filename = String(groupCfg.promptImage || "").trim();
371
+ if (filename) {
371
372
  const imagePath = getGroupPromptImagePath(info.groupId, filename);
372
373
  if (!existsSync(imagePath)) {
373
374
  ctx.logger.warn(
374
375
  `admin verify 自定义入群提示图片缺失: ${imagePath}`,
375
376
  );
376
- continue;
377
+ } else {
378
+ segments.push(ctx.segment.image(`file://${imagePath}`));
377
379
  }
378
- segments.push(ctx.segment.image(`file://${imagePath}`));
379
380
  }
380
381
  if (!segments.length) return false;
381
382
  try {