mioku-plugin-meme 3.0.1 → 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/configs/base.ts CHANGED
@@ -7,7 +7,7 @@ export const MEME_BASE_CONFIG: MemeBaseConfig = {
7
7
  },
8
8
  trigger: {
9
9
  requirePrefix: true,
10
- prefixes: ["/", "#"],
10
+ prefixes: [".", "/", "#"],
11
11
  directKeywordNeedPrefix: false,
12
12
  },
13
13
  behavior: {
package/index.ts CHANGED
@@ -42,8 +42,6 @@ function stripCommandPrefix(
42
42
 
43
43
  const memePlugin = definePlugin({
44
44
  name: "meme",
45
- version: "1.0.0",
46
- description: "基于 meme-generator API 的表情包制作插件",
47
45
 
48
46
  async setup(ctx: MiokuContext) {
49
47
  const configService = getService(ctx, Services.Config);
@@ -80,8 +78,8 @@ const memePlugin = definePlugin({
80
78
  );
81
79
  }
82
80
  const rawText = ctx.text(event)?.trim() ?? "";
83
- const hasSlashPrefix = baseConfig.trigger.prefixes.some((p: string) => rawText.startsWith(p));
84
- if (!hasSlashPrefix) {
81
+ const hasPrefix = baseConfig.trigger.prefixes.some((p: string) => rawText.startsWith(p));
82
+ if (!hasPrefix) {
85
83
  return;
86
84
  }
87
85
  const chatRuntime = aiService?.getChatRuntime();
@@ -147,6 +145,96 @@ const memePlugin = definePlugin({
147
145
  );
148
146
  }
149
147
 
148
+ ctx.command({
149
+ name: "meme 菜单",
150
+ match: /^(?:(?:表情|meme)包?(?:菜单|展示|制作(?:列表)?)|meme\s+(?:菜单|列表|展示))$/i,
151
+ prefixes: baseConfig.trigger.prefixes,
152
+ description: "查看已同步的表情菜单",
153
+ usage: ".meme 菜单",
154
+ handler: ({ event }) => runtime.sendMenu(ctx, event),
155
+ });
156
+ ctx.command({
157
+ name: "随机表情",
158
+ match: /^随机(?:表情|meme)(?:包)?$/i,
159
+ prefixes: baseConfig.trigger.prefixes,
160
+ description: "随机生成一个头像类表情",
161
+ usage: ".随机表情",
162
+ handler: ({ event }) => runtime.sendRandom(ctx, event),
163
+ });
164
+ ctx.command({
165
+ name: "meme 帮助",
166
+ match: /^(?:(?:头像|文字)?(?:表情|meme)包?(?:制作(?:菜单|教程)?|帮助|说明|指令|使用说明)|(?:头像|文字)(?:表情|meme)包?|meme\s+帮助)$/i,
167
+ prefixes: baseConfig.trigger.prefixes,
168
+ description: "查看表情制作教程与参数说明",
169
+ usage: ".meme 帮助",
170
+ handler: ({ event }) => runtime.sendHelp(ctx, event),
171
+ });
172
+ ctx.command({
173
+ name: "meme 搜索",
174
+ match: /^(?:表情|meme)包?(?:搜索|检索)\s*(.*)$/i,
175
+ prefixes: baseConfig.trigger.prefixes,
176
+ description: "搜索相关表情关键词",
177
+ usage: ".meme 搜索 摸",
178
+ handler: async ({ event, match }) => {
179
+ const query = String(match?.[1] ?? "").trim();
180
+ if (!query) {
181
+ await notifyByAIRuntime(
182
+ event,
183
+ "用户发起了 meme 搜索但没有给关键词。请自然提醒他补一个搜索词",
184
+ "你想搜什么?",
185
+ );
186
+ return;
187
+ }
188
+ await runtime.sendSearch(ctx, event, query);
189
+ },
190
+ });
191
+ ctx.command({
192
+ name: "meme 详情",
193
+ match: /^meme包?\s+详情\s+(.+)$/i,
194
+ prefixes: baseConfig.trigger.prefixes,
195
+ description: "查看表情参数和预览",
196
+ usage: ".meme 详情 摸头",
197
+ handler: async ({ event, match }) => {
198
+ const keyword = String(match?.[1] ?? "").trim();
199
+ if (!runtime.getDetail(keyword)) {
200
+ await notifyByAIRuntime(
201
+ event,
202
+ `用户查询了不存在的表情详情关键词 "${keyword}"。请自然提醒先搜索可用关键词`,
203
+ `未找到表情关键词:${keyword}`,
204
+ );
205
+ return;
206
+ }
207
+ await runtime.sendDetail(ctx, event, keyword);
208
+ },
209
+ });
210
+ ctx.command({
211
+ name: "meme 更新",
212
+ match: /^(?:(?:表情|meme)包?更新|meme\s+更新)$/i,
213
+ prefixes: baseConfig.trigger.prefixes,
214
+ permission: baseConfig.permissions.ownerOnlyUpdate ? "master" : "member",
215
+ description: "刷新远端表情缓存",
216
+ usage: ".meme 更新",
217
+ handler: async ({ event }) => {
218
+ await replyWithParts({
219
+ ctx,
220
+ event,
221
+ parts: ["开始刷新 meme 缓存,请稍等..."],
222
+ quoteReply: baseConfig.behavior.quoteReply,
223
+ });
224
+ try {
225
+ await runtime.refreshCache();
226
+ await runtime.sendMenu(ctx, event);
227
+ } catch (error) {
228
+ await notifyByAIRuntime(
229
+ event,
230
+ `meme 缓存刷新失败,错误信息:${error}。请自然告知用户稍后重试或让管理员检查 meme API 服务状态。`,
231
+ `刷新 meme 缓存失败:${error}`,
232
+ error,
233
+ );
234
+ }
235
+ },
236
+ });
237
+
150
238
  ctx.handle("message", async (event) => {
151
239
  const rawText = ctx.text(event)?.trim();
152
240
  if (!rawText) {
@@ -169,99 +257,6 @@ const memePlugin = definePlugin({
169
257
  : rawText;
170
258
 
171
259
  try {
172
- if (
173
- commandText &&
174
- (/^(?:表情|meme)包?(?:菜单|展示|制作(?:列表)?)$/i.test(commandText) ||
175
- /^meme\s+(?:菜单|列表|展示)$/i.test(commandText))
176
- ) {
177
- await runtime.sendMenu(ctx, event);
178
- return;
179
- }
180
-
181
- if (commandText && /^随机(?:表情|meme)(?:包)?$/i.test(commandText)) {
182
- await runtime.sendRandom(ctx, event);
183
- return;
184
- }
185
-
186
- if (
187
- commandText &&
188
- (/^(?:(?:头像|文字)?(?:表情|meme)包?(?:制作(?:菜单|教程)?|帮助|说明|指令|使用说明)|(?:头像|文字)(?:表情|meme)包?)$/i.test(
189
- commandText,
190
- ) ||
191
- /^meme\s+帮助$/i.test(commandText))
192
- ) {
193
- await runtime.sendHelp(ctx, event);
194
- return;
195
- }
196
-
197
- const searchMatch = commandText.match(
198
- /^(?:表情|meme)包?(?:搜索|检索)\s*(.*)$/i,
199
- );
200
- if (searchMatch) {
201
- const query = (searchMatch[1] || "").trim();
202
- if (!query) {
203
- await notifyByAIRuntime(
204
- event,
205
- "用户发起了 meme 搜索但没有给关键词。请自然提醒他补一个搜索词",
206
- "你想搜什么?",
207
- );
208
- return;
209
- }
210
- await runtime.sendSearch(ctx, event, query);
211
- return;
212
- }
213
-
214
- const detailCommandMatch =
215
- commandText.match(/^meme包?\s+详情\s+(.+)$/i);
216
- if (detailCommandMatch) {
217
- const keyword = detailCommandMatch[1].trim();
218
- if (!runtime.getDetail(keyword)) {
219
- await notifyByAIRuntime(
220
- event,
221
- `用户查询了不存在的表情详情关键词 "${keyword}"。请自然提醒先搜索可用关键词`,
222
- `未找到表情关键词:${keyword}`,
223
- );
224
- return;
225
- }
226
- await runtime.sendDetail(ctx, event, keyword);
227
- return;
228
- }
229
-
230
- if (
231
- commandText &&
232
- (/^(?:表情|meme)包?更新$/i.test(commandText) ||
233
- /^meme\s+更新$/i.test(commandText))
234
- ) {
235
- if (baseConfig.permissions.ownerOnlyUpdate && !ctx.isOwner(event)) {
236
- await notifyByAIRuntime(
237
- event,
238
- "用户尝试刷新 meme 缓存,但权限不足。请自然提醒该操作仅主人可用",
239
- "不支持小男娘使用喵",
240
- );
241
- return;
242
- }
243
-
244
- await replyWithParts({
245
- ctx,
246
- event,
247
- parts: ["开始刷新 meme 缓存,请稍等..."],
248
- quoteReply: baseConfig.behavior.quoteReply,
249
- });
250
-
251
- try {
252
- await runtime.refreshCache();
253
- await runtime.sendMenu(ctx, event);
254
- } catch (error) {
255
- await notifyByAIRuntime(
256
- event,
257
- `meme 缓存刷新失败,错误信息:${error}。请自然告知用户稍后重试或让管理员检查 meme API 服务状态。`,
258
- `刷新 meme 缓存失败:${error}`,
259
- error,
260
- );
261
- }
262
- return;
263
- }
264
-
265
260
  const genericGenerateMatch = commandText.match(
266
261
  /^meme包?\s+(?:生成|制作)\s+(.+)$/i,
267
262
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mioku-plugin-meme",
3
- "version": "3.0.1",
3
+ "version": "3.0.3",
4
4
  "description": "基于 meme-generator API 的表情包制作插件",
5
5
  "main": "index.ts",
6
6
  "type": "module",
@@ -15,71 +15,7 @@
15
15
  "services": [
16
16
  "config",
17
17
  "ai"
18
- ],
19
- "accessHooks": [
20
- {
21
- "id": "meme-菜单",
22
- "match": "/^\\/meme\\s*菜单/"
23
- },
24
- {
25
- "id": "meme-搜索",
26
- "match": "/^\\/meme\\s*搜索/"
27
- },
28
- {
29
- "id": "meme-详情",
30
- "match": "/^\\/meme\\s*详情/"
31
- },
32
- {
33
- "id": "meme-更新",
34
- "match": "/^\\/meme\\s*更新/"
35
- },
36
- {
37
- "id": "随机表情",
38
- "match": "/^\\/随机表情/"
39
- }
40
- ],
41
- "help": {
42
- "title": "Meme 表情包",
43
- "description": "接入 meme-generator API 的表情包制作插件",
44
- "commands": [
45
- {
46
- "cmd": "/meme 菜单",
47
- "desc": "查看已同步的表情菜单",
48
- "usage": "/meme 菜单",
49
- "role": "member"
50
- },
51
- {
52
- "cmd": "/meme 搜索 <关键词>",
53
- "desc": "搜索相关表情关键词",
54
- "usage": "/meme 搜索 摸",
55
- "role": "member"
56
- },
57
- {
58
- "cmd": "/meme 详情 <关键词>",
59
- "desc": "查看表情参数和预览",
60
- "usage": "/meme 详情 摸头",
61
- "role": "member"
62
- },
63
- {
64
- "cmd": "/随机表情",
65
- "desc": "随机生成一个头像类表情",
66
- "usage": "/随机表情",
67
- "role": "member"
68
- },
69
- {
70
- "cmd": "<表情关键词> [文本1/文本2][#参数]",
71
- "desc": "直接按关键词生成表情",
72
- "usage": "摸头 小明#圆",
73
- "role": "member"
74
- },
75
- {
76
- "cmd": "/meme 更新",
77
- "desc": "刷新远端表情缓存",
78
- "usage": "/meme 更新",
79
- "role": "master"
80
- }
81
- ]
82
- }
18
+ ]
83
19
  },
84
20
  "peerDependencies": {
85
21
  "mioku": "^1.0.0"
package/shared.ts CHANGED
@@ -74,8 +74,15 @@ function getSenderName(event: any): string {
74
74
  );
75
75
  }
76
76
 
77
- function getAvatarUrl(userId: number | string | undefined): string {
78
- return `https://q1.qlogo.cn/g?b=qq&s=640&nk=${userId || 0}`;
77
+ /** qlogo 头像服务只认数字 QQ 号,openid 平台直接跳过 */
78
+ function getAvatarUrl(
79
+ userId: string | number | undefined,
80
+ adapter?: string,
81
+ ): string {
82
+ const id = String(userId ?? "").trim();
83
+ if (!id || !/^\d+$/.test(id)) return "";
84
+ if (adapter && adapter !== "onebotv11" && adapter !== "icqq") return "";
85
+ return `https://q1.qlogo.cn/g?b=qq&s=640&nk=${id}`;
79
86
  }
80
87
 
81
88
  function extractTextAndArgs(input: string): { text: string; args: string } {
@@ -906,11 +913,11 @@ export class MemePluginRuntime {
906
913
  ): Promise<string[]> {
907
914
  const sourceType = source?.type || "auto";
908
915
  if (sourceType === "user_avatar") {
909
- const qq = Number(source?.qq);
910
- if (!Number.isFinite(qq) || qq <= 0) {
911
- return [];
912
- }
913
- return [getAvatarUrl(qq)].slice(0, params.max_images || 1);
916
+ const qq = String(source?.qq ?? "").trim();
917
+ if (!qq) return [];
918
+ return [getAvatarUrl(qq, event?.bot?.adapter)]
919
+ .filter(Boolean)
920
+ .slice(0, params.max_images || 1);
914
921
  }
915
922
 
916
923
  if (sourceType === "message_image") {
@@ -942,15 +949,20 @@ export class MemePluginRuntime {
942
949
 
943
950
  const atUserIds = getAtUserIds(event?.message || []);
944
951
  if (urls.length < (params.min_images || 0) && atUserIds.length > 0) {
945
- urls.push(...atUserIds.map((userId) => getAvatarUrl(userId)));
952
+ urls.push(
953
+ ...atUserIds
954
+ .map((userId) => getAvatarUrl(userId, event?.bot?.adapter))
955
+ .filter(Boolean),
956
+ );
946
957
  }
947
958
 
948
959
  if (
949
960
  urls.length === 0 &&
950
961
  this.baseConfig.behavior.useSenderAvatarFallback &&
951
- event?.user_id != null
962
+ String(event?.user_id ?? "").trim()
952
963
  ) {
953
- urls.push(getAvatarUrl(event.user_id));
964
+ const selfAvatar = getAvatarUrl(event.user_id, event?.bot?.adapter);
965
+ if (selfAvatar) urls.push(selfAvatar);
954
966
  }
955
967
 
956
968
  return uniqueStrings(urls).slice(0, maxImages);
@@ -1008,7 +1020,10 @@ export class MemePluginRuntime {
1008
1020
  const infos = await Promise.all(
1009
1021
  atUserIds.map(async (userId) => {
1010
1022
  try {
1011
- const member = await bot.getMemberInfo(event.group_id, userId);
1023
+ const member = await bot.getMemberInfo(
1024
+ String(event.group_id ?? ""),
1025
+ String(userId),
1026
+ );
1012
1027
  return {
1013
1028
  name: member?.card || member?.nickname || String(userId),
1014
1029
  gender: String(member?.sex || "unknown"),