mioku-plugin-meme 2.1.2 → 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/index.ts +16 -12
- package/package.json +3 -5
- package/shared.ts +14 -33
- package/skills/meme.ts +149 -0
- package/runtime.ts +0 -26
- package/skills.ts +0 -157
package/index.ts
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
definePlugin,
|
|
3
|
+
type MiokuContext,
|
|
4
|
+
getService,
|
|
5
|
+
Services,
|
|
6
|
+
} from "mioku";
|
|
3
7
|
import { MEME_BASE_CONFIG } from "./configs/base";
|
|
4
8
|
import { MEME_FILTER_CONFIG } from "./configs/filters";
|
|
5
9
|
import { MemePluginRuntime, replyWithParts } from "./shared";
|
|
6
|
-
import { resetMemeRuntimeState, setMemeRuntimeState } from "./runtime";
|
|
7
10
|
import type { MemeBaseConfig, MemeFilterConfig } from "./types";
|
|
11
|
+
import { createMemeSkills } from "./skills/meme";
|
|
8
12
|
|
|
9
13
|
function cloneConfig<T>(value: T): T {
|
|
10
14
|
return JSON.parse(JSON.stringify(value)) as T;
|
|
@@ -41,9 +45,9 @@ const memePlugin = definePlugin({
|
|
|
41
45
|
version: "1.0.0",
|
|
42
46
|
description: "基于 meme-generator API 的表情包制作插件",
|
|
43
47
|
|
|
44
|
-
async setup(ctx:
|
|
45
|
-
const configService = ctx.
|
|
46
|
-
const aiService = ctx.
|
|
48
|
+
async setup(ctx: MiokuContext) {
|
|
49
|
+
const configService = getService(ctx, Services.Config);
|
|
50
|
+
const aiService = getService(ctx, Services.AI);
|
|
47
51
|
|
|
48
52
|
let baseConfig = cloneConfig(MEME_BASE_CONFIG);
|
|
49
53
|
let filterConfig = cloneConfig(MEME_FILTER_CONFIG);
|
|
@@ -123,7 +127,9 @@ const memePlugin = definePlugin({
|
|
|
123
127
|
);
|
|
124
128
|
}
|
|
125
129
|
|
|
126
|
-
|
|
130
|
+
if (aiService) {
|
|
131
|
+
for (const skill of createMemeSkills(runtime)) aiService.registerSkill(skill);
|
|
132
|
+
}
|
|
127
133
|
|
|
128
134
|
const disposers: Array<() => void> = [];
|
|
129
135
|
if (configService) {
|
|
@@ -141,7 +147,7 @@ const memePlugin = definePlugin({
|
|
|
141
147
|
);
|
|
142
148
|
}
|
|
143
149
|
|
|
144
|
-
ctx.handle("message", async (event
|
|
150
|
+
ctx.handle("message", async (event) => {
|
|
145
151
|
const rawText = ctx.text(event)?.trim();
|
|
146
152
|
if (!rawText) {
|
|
147
153
|
return;
|
|
@@ -366,10 +372,8 @@ const memePlugin = definePlugin({
|
|
|
366
372
|
});
|
|
367
373
|
|
|
368
374
|
return () => {
|
|
369
|
-
for (const dispose of disposers)
|
|
370
|
-
|
|
371
|
-
}
|
|
372
|
-
resetMemeRuntimeState();
|
|
375
|
+
for (const dispose of disposers) dispose();
|
|
376
|
+
if (aiService) aiService.removeSkill("meme");
|
|
373
377
|
ctx.logger.info("meme 插件已卸载");
|
|
374
378
|
};
|
|
375
379
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mioku-plugin-meme",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "基于 meme-generator API 的表情包制作插件",
|
|
5
5
|
"main": "index.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -82,11 +82,9 @@
|
|
|
82
82
|
}
|
|
83
83
|
},
|
|
84
84
|
"peerDependencies": {
|
|
85
|
-
"mioku": "^0.
|
|
86
|
-
"mioki": "^0.16.0"
|
|
85
|
+
"mioku": "^1.0.0"
|
|
87
86
|
},
|
|
88
87
|
"devDependencies": {
|
|
89
|
-
"mioku": "workspace:*"
|
|
90
|
-
"mioki": "^0.16.0"
|
|
88
|
+
"mioku": "workspace:*"
|
|
91
89
|
}
|
|
92
90
|
}
|
package/shared.ts
CHANGED
|
@@ -187,10 +187,10 @@ export async function replyWithParts(options: {
|
|
|
187
187
|
parts: any[];
|
|
188
188
|
quoteReply?: boolean;
|
|
189
189
|
}): Promise<void> {
|
|
190
|
-
const { event, parts, quoteReply = false } = options;
|
|
190
|
+
const { ctx, event, parts, quoteReply = false } = options;
|
|
191
191
|
const payload = [...parts];
|
|
192
192
|
if (quoteReply && event?.message_id != null) {
|
|
193
|
-
payload.unshift(
|
|
193
|
+
payload.unshift(ctx.segment.reply(event.message_id));
|
|
194
194
|
}
|
|
195
195
|
await event.reply(payload.length === 1 ? payload[0] : payload);
|
|
196
196
|
}
|
|
@@ -208,9 +208,7 @@ export async function replyWithImage(options: {
|
|
|
208
208
|
parts.push(caption);
|
|
209
209
|
}
|
|
210
210
|
|
|
211
|
-
const imageSegment = ctx
|
|
212
|
-
? ctx.segment.image(image)
|
|
213
|
-
: { type: "image", file: image };
|
|
211
|
+
const imageSegment = ctx.segment.image(image);
|
|
214
212
|
parts.push(imageSegment);
|
|
215
213
|
|
|
216
214
|
try {
|
|
@@ -228,11 +226,7 @@ export async function replyWithImage(options: {
|
|
|
228
226
|
if (caption) {
|
|
229
227
|
fallbackParts.push(caption);
|
|
230
228
|
}
|
|
231
|
-
fallbackParts.push(
|
|
232
|
-
ctx?.segment?.image
|
|
233
|
-
? ctx.segment.image(base64Image)
|
|
234
|
-
: { type: "image", file: base64Image },
|
|
235
|
-
);
|
|
229
|
+
fallbackParts.push(ctx.segment.image(base64Image));
|
|
236
230
|
await replyWithParts({ ctx, event, parts: fallbackParts, quoteReply });
|
|
237
231
|
}
|
|
238
232
|
|
|
@@ -244,11 +238,7 @@ export async function sendSkillImage(options: {
|
|
|
244
238
|
quoteReply?: boolean;
|
|
245
239
|
}): Promise<void> {
|
|
246
240
|
const { ctx, event, image, caption, quoteReply = false } = options;
|
|
247
|
-
const
|
|
248
|
-
const bot =
|
|
249
|
-
selfId != null && typeof ctx?.pickBot === "function"
|
|
250
|
-
? ctx.pickBot(selfId)
|
|
251
|
-
: undefined;
|
|
241
|
+
const bot = event?.bot;
|
|
252
242
|
|
|
253
243
|
if (!bot) {
|
|
254
244
|
throw new Error("当前上下文不支持发送图片");
|
|
@@ -257,14 +247,12 @@ export async function sendSkillImage(options: {
|
|
|
257
247
|
const buildPayload = (file: string) => {
|
|
258
248
|
const payload: any[] = [];
|
|
259
249
|
if (quoteReply && event?.message_id != null) {
|
|
260
|
-
payload.push(
|
|
250
|
+
payload.push(ctx.segment.reply(event.message_id));
|
|
261
251
|
}
|
|
262
252
|
if (caption) {
|
|
263
253
|
payload.push(caption);
|
|
264
254
|
}
|
|
265
|
-
payload.push(
|
|
266
|
-
ctx?.segment?.image ? ctx.segment.image(file) : { type: "image", file },
|
|
267
|
-
);
|
|
255
|
+
payload.push(ctx.segment.image(file));
|
|
268
256
|
return payload;
|
|
269
257
|
};
|
|
270
258
|
|
|
@@ -622,11 +610,7 @@ export class MemePluginRuntime {
|
|
|
622
610
|
);
|
|
623
611
|
const previewBuffer = Buffer.from(await previewResponse.arrayBuffer());
|
|
624
612
|
const previewBase64 = await imageBufferToBase64(previewBuffer);
|
|
625
|
-
parts.push(
|
|
626
|
-
ctx?.segment?.image
|
|
627
|
-
? ctx.segment.image(previewBase64)
|
|
628
|
-
: { type: "image", file: previewBase64 },
|
|
629
|
-
);
|
|
613
|
+
parts.push(ctx.segment.image(previewBase64));
|
|
630
614
|
} catch (error) {
|
|
631
615
|
this.logger.warn(`[meme] 获取预览失败: ${error}`);
|
|
632
616
|
}
|
|
@@ -978,11 +962,11 @@ export class MemePluginRuntime {
|
|
|
978
962
|
messageId: number,
|
|
979
963
|
): Promise<string | null> {
|
|
980
964
|
try {
|
|
981
|
-
const
|
|
982
|
-
if (
|
|
965
|
+
const bot = event?.bot;
|
|
966
|
+
if (!bot) {
|
|
983
967
|
return null;
|
|
984
968
|
}
|
|
985
|
-
const msg = await
|
|
969
|
+
const msg = await bot.getMessage(messageId);
|
|
986
970
|
if (!msg?.message || !Array.isArray(msg.message)) {
|
|
987
971
|
return null;
|
|
988
972
|
}
|
|
@@ -1013,11 +997,8 @@ export class MemePluginRuntime {
|
|
|
1013
997
|
];
|
|
1014
998
|
}
|
|
1015
999
|
|
|
1016
|
-
const bot =
|
|
1017
|
-
|
|
1018
|
-
? ctx.pickBot(event.self_id)
|
|
1019
|
-
: undefined;
|
|
1020
|
-
if (!bot?.getGroupMemberInfo) {
|
|
1000
|
+
const bot = event?.bot;
|
|
1001
|
+
if (!bot) {
|
|
1021
1002
|
return atUserIds.map((userId) => ({
|
|
1022
1003
|
name: String(userId),
|
|
1023
1004
|
gender: "unknown",
|
|
@@ -1027,7 +1008,7 @@ export class MemePluginRuntime {
|
|
|
1027
1008
|
const infos = await Promise.all(
|
|
1028
1009
|
atUserIds.map(async (userId) => {
|
|
1029
1010
|
try {
|
|
1030
|
-
const member = await bot.
|
|
1011
|
+
const member = await bot.getMemberInfo(event.group_id, userId);
|
|
1031
1012
|
return {
|
|
1032
1013
|
name: member?.card || member?.nickname || String(userId),
|
|
1033
1014
|
gender: String(member?.sex || "unknown"),
|
package/skills/meme.ts
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import type { AISkill, AITool } from "mioku";
|
|
2
|
+
import type { MemePluginRuntime } from "../shared";
|
|
3
|
+
import type { MemeImageSourceOptions } from "../types";
|
|
4
|
+
|
|
5
|
+
function parseImageSource(args: any): {
|
|
6
|
+
ok: boolean;
|
|
7
|
+
source?: MemeImageSourceOptions;
|
|
8
|
+
message?: string;
|
|
9
|
+
} {
|
|
10
|
+
const sourceType = String(args?.source_type || "").trim();
|
|
11
|
+
if (!sourceType) {
|
|
12
|
+
return {
|
|
13
|
+
ok: false,
|
|
14
|
+
message: "缺少 source_type。可选值: user_avatar 或 message_image。",
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (sourceType === "user_avatar") {
|
|
19
|
+
const qq = Number(args?.qq);
|
|
20
|
+
if (!Number.isFinite(qq) || qq <= 0) {
|
|
21
|
+
return {
|
|
22
|
+
ok: false,
|
|
23
|
+
message: "source_type=user_avatar 时必须提供有效的 qq。",
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
ok: true,
|
|
28
|
+
source: {
|
|
29
|
+
type: "user_avatar",
|
|
30
|
+
qq,
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (sourceType === "message_image") {
|
|
36
|
+
const messageId = Number(args?.message_id);
|
|
37
|
+
if (!Number.isFinite(messageId) || messageId <= 0) {
|
|
38
|
+
return {
|
|
39
|
+
ok: false,
|
|
40
|
+
message: "source_type=message_image 时必须提供有效的 message_id。",
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
ok: true,
|
|
45
|
+
source: {
|
|
46
|
+
type: "message_image",
|
|
47
|
+
messageId,
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
ok: false,
|
|
54
|
+
message: "source_type 无效。只能是 user_avatar 或 message_image。",
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function createMemeSkills(runtime: MemePluginRuntime): AISkill[] {
|
|
59
|
+
return [
|
|
60
|
+
{
|
|
61
|
+
name: "meme",
|
|
62
|
+
description:
|
|
63
|
+
"搜索,制作和发送各种各样的整蛊群友的表情包,可以使用聊天中的图片或用户头像制作",
|
|
64
|
+
permission: "member",
|
|
65
|
+
tools: [
|
|
66
|
+
{
|
|
67
|
+
name: "search_memes",
|
|
68
|
+
description: "按关键词搜索可用的 meme 表情关键词",
|
|
69
|
+
parameters: {
|
|
70
|
+
type: "object",
|
|
71
|
+
properties: {
|
|
72
|
+
query: {
|
|
73
|
+
type: "string",
|
|
74
|
+
description: "搜索关键词,可以是中文或英文片段",
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
required: ["query"],
|
|
78
|
+
},
|
|
79
|
+
handler: async (args: any) => {
|
|
80
|
+
await runtime.ensureCache();
|
|
81
|
+
const hits = runtime.searchKeywords(String(args?.query || ""));
|
|
82
|
+
return hits.length > 0
|
|
83
|
+
? hits.join("、")
|
|
84
|
+
: "没有找到匹配的 meme 关键词";
|
|
85
|
+
},
|
|
86
|
+
} as AITool,
|
|
87
|
+
{
|
|
88
|
+
name: "send_meme",
|
|
89
|
+
description:
|
|
90
|
+
"发送 meme。必须指定图片来源:用户头像(qq)或聊天图片(message_id)。",
|
|
91
|
+
parameters: {
|
|
92
|
+
type: "object",
|
|
93
|
+
properties: {
|
|
94
|
+
keyword: {
|
|
95
|
+
type: "string",
|
|
96
|
+
description: "要生成的 meme 关键词",
|
|
97
|
+
},
|
|
98
|
+
text: {
|
|
99
|
+
type: "string",
|
|
100
|
+
description: "文本内容,多段文本用 / 分隔",
|
|
101
|
+
},
|
|
102
|
+
args: {
|
|
103
|
+
type: "string",
|
|
104
|
+
description: "额外参数,例如 圆、左、循环 等",
|
|
105
|
+
},
|
|
106
|
+
source_type: {
|
|
107
|
+
type: "string",
|
|
108
|
+
description:
|
|
109
|
+
"图片来源类型,只能是 user_avatar 或 message_image",
|
|
110
|
+
enum: ["user_avatar", "message_image"],
|
|
111
|
+
},
|
|
112
|
+
qq: {
|
|
113
|
+
type: "number",
|
|
114
|
+
description: "source_type=user_avatar 时必填,头像对应 QQ 号",
|
|
115
|
+
},
|
|
116
|
+
message_id: {
|
|
117
|
+
type: "number",
|
|
118
|
+
description:
|
|
119
|
+
"source_type=message_image 时必填,包含图片的消息 message_id",
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
required: ["keyword", "source_type"],
|
|
123
|
+
},
|
|
124
|
+
handler: async (args: any, runtimeCtx?: any) => {
|
|
125
|
+
const ctx = runtimeCtx?.ctx;
|
|
126
|
+
const event = runtimeCtx?.event || runtimeCtx?.rawEvent;
|
|
127
|
+
if (!ctx || !event) {
|
|
128
|
+
return "当前上下文不支持发送 meme";
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const source = parseImageSource(args);
|
|
132
|
+
if (!source.ok) {
|
|
133
|
+
return source.message || "图片来源参数不合法";
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const result = await runtime.generateByKeyword(ctx, event, {
|
|
137
|
+
keyword: String(args?.keyword || ""),
|
|
138
|
+
text: args?.text ? String(args.text) : "",
|
|
139
|
+
args: args?.args ? String(args.args) : "",
|
|
140
|
+
imageSource: source.source,
|
|
141
|
+
send: true,
|
|
142
|
+
});
|
|
143
|
+
return result.message;
|
|
144
|
+
},
|
|
145
|
+
} as AITool,
|
|
146
|
+
],
|
|
147
|
+
},
|
|
148
|
+
];
|
|
149
|
+
}
|
package/runtime.ts
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import type { MemePluginRuntime } from "./shared";
|
|
2
|
-
import {
|
|
3
|
-
getPluginRuntimeState,
|
|
4
|
-
resetPluginRuntimeState,
|
|
5
|
-
setPluginRuntimeState,
|
|
6
|
-
} from "mioku";
|
|
7
|
-
|
|
8
|
-
export interface MemeRuntimeState {
|
|
9
|
-
runtime?: MemePluginRuntime;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
const MEME_PLUGIN_NAME = "meme";
|
|
13
|
-
|
|
14
|
-
export function setMemeRuntimeState(
|
|
15
|
-
nextState: MemeRuntimeState,
|
|
16
|
-
): MemeRuntimeState {
|
|
17
|
-
return setPluginRuntimeState(MEME_PLUGIN_NAME, nextState) as MemeRuntimeState;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export function getMemeRuntimeState(): MemeRuntimeState {
|
|
21
|
-
return getPluginRuntimeState(MEME_PLUGIN_NAME) as MemeRuntimeState;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export function resetMemeRuntimeState(): void {
|
|
25
|
-
resetPluginRuntimeState(MEME_PLUGIN_NAME);
|
|
26
|
-
}
|
package/skills.ts
DELETED
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
import type { AISkill, AITool } from "mioku";
|
|
2
|
-
import type { MemeImageSourceOptions } from "./types";
|
|
3
|
-
import { getMemeRuntimeState } from "./runtime";
|
|
4
|
-
|
|
5
|
-
function parseImageSource(args: any): {
|
|
6
|
-
ok: boolean;
|
|
7
|
-
source?: MemeImageSourceOptions;
|
|
8
|
-
message?: string;
|
|
9
|
-
} {
|
|
10
|
-
const sourceType = String(args?.source_type || "").trim();
|
|
11
|
-
if (!sourceType) {
|
|
12
|
-
return {
|
|
13
|
-
ok: false,
|
|
14
|
-
message: "缺少 source_type。可选值: user_avatar 或 message_image。",
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
if (sourceType === "user_avatar") {
|
|
19
|
-
const qq = Number(args?.qq);
|
|
20
|
-
if (!Number.isFinite(qq) || qq <= 0) {
|
|
21
|
-
return {
|
|
22
|
-
ok: false,
|
|
23
|
-
message: "source_type=user_avatar 时必须提供有效的 qq。",
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
return {
|
|
27
|
-
ok: true,
|
|
28
|
-
source: {
|
|
29
|
-
type: "user_avatar",
|
|
30
|
-
qq,
|
|
31
|
-
},
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
if (sourceType === "message_image") {
|
|
36
|
-
const messageId = Number(args?.message_id);
|
|
37
|
-
if (!Number.isFinite(messageId) || messageId <= 0) {
|
|
38
|
-
return {
|
|
39
|
-
ok: false,
|
|
40
|
-
message: "source_type=message_image 时必须提供有效的 message_id。",
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
return {
|
|
44
|
-
ok: true,
|
|
45
|
-
source: {
|
|
46
|
-
type: "message_image",
|
|
47
|
-
messageId,
|
|
48
|
-
},
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
return {
|
|
53
|
-
ok: false,
|
|
54
|
-
message: "source_type 无效。只能是 user_avatar 或 message_image。",
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const memeSkills: AISkill[] = [
|
|
59
|
-
{
|
|
60
|
-
name: "meme",
|
|
61
|
-
description:
|
|
62
|
-
"搜索,制作和发送各种各样的整蛊群友的表情包,可以使用聊天中的图片或用户头像制作",
|
|
63
|
-
permission: "member",
|
|
64
|
-
tools: [
|
|
65
|
-
{
|
|
66
|
-
name: "search_memes",
|
|
67
|
-
description: "按关键词搜索可用的 meme 表情关键词",
|
|
68
|
-
parameters: {
|
|
69
|
-
type: "object",
|
|
70
|
-
properties: {
|
|
71
|
-
query: {
|
|
72
|
-
type: "string",
|
|
73
|
-
description: "搜索关键词,可以是中文或英文片段",
|
|
74
|
-
},
|
|
75
|
-
},
|
|
76
|
-
required: ["query"],
|
|
77
|
-
},
|
|
78
|
-
handler: async (args: any) => {
|
|
79
|
-
const runtime = getMemeRuntimeState().runtime;
|
|
80
|
-
if (!runtime) {
|
|
81
|
-
return "meme 插件尚未初始化";
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
await runtime.ensureCache();
|
|
85
|
-
const hits = runtime.searchKeywords(String(args?.query || ""));
|
|
86
|
-
return hits.length > 0
|
|
87
|
-
? hits.join("、")
|
|
88
|
-
: "没有找到匹配的 meme 关键词";
|
|
89
|
-
},
|
|
90
|
-
} as AITool,
|
|
91
|
-
{
|
|
92
|
-
name: "send_meme",
|
|
93
|
-
description:
|
|
94
|
-
"发送 meme。必须指定图片来源:用户头像(qq)或聊天图片(message_id)。",
|
|
95
|
-
parameters: {
|
|
96
|
-
type: "object",
|
|
97
|
-
properties: {
|
|
98
|
-
keyword: {
|
|
99
|
-
type: "string",
|
|
100
|
-
description: "要生成的 meme 关键词",
|
|
101
|
-
},
|
|
102
|
-
text: {
|
|
103
|
-
type: "string",
|
|
104
|
-
description: "文本内容,多段文本用 / 分隔",
|
|
105
|
-
},
|
|
106
|
-
args: {
|
|
107
|
-
type: "string",
|
|
108
|
-
description: "额外参数,例如 圆、左、循环 等",
|
|
109
|
-
},
|
|
110
|
-
source_type: {
|
|
111
|
-
type: "string",
|
|
112
|
-
description: "图片来源类型,只能是 user_avatar 或 message_image",
|
|
113
|
-
enum: ["user_avatar", "message_image"],
|
|
114
|
-
},
|
|
115
|
-
qq: {
|
|
116
|
-
type: "number",
|
|
117
|
-
description: "source_type=user_avatar 时必填,头像对应 QQ 号",
|
|
118
|
-
},
|
|
119
|
-
message_id: {
|
|
120
|
-
type: "number",
|
|
121
|
-
description:
|
|
122
|
-
"source_type=message_image 时必填,包含图片的消息 message_id",
|
|
123
|
-
},
|
|
124
|
-
},
|
|
125
|
-
required: ["keyword", "source_type"],
|
|
126
|
-
},
|
|
127
|
-
handler: async (args: any, runtimeCtx?: any) => {
|
|
128
|
-
const runtime = getMemeRuntimeState().runtime;
|
|
129
|
-
const ctx = runtimeCtx?.ctx;
|
|
130
|
-
const event = runtimeCtx?.event || runtimeCtx?.rawEvent;
|
|
131
|
-
if (!runtime) {
|
|
132
|
-
return "meme 插件尚未初始化";
|
|
133
|
-
}
|
|
134
|
-
if (!ctx || !event) {
|
|
135
|
-
return "当前上下文不支持发送 meme";
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
const source = parseImageSource(args);
|
|
139
|
-
if (!source.ok) {
|
|
140
|
-
return source.message || "图片来源参数不合法";
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
const result = await runtime.generateByKeyword(ctx, event, {
|
|
144
|
-
keyword: String(args?.keyword || ""),
|
|
145
|
-
text: args?.text ? String(args.text) : "",
|
|
146
|
-
args: args?.args ? String(args.args) : "",
|
|
147
|
-
imageSource: source.source,
|
|
148
|
-
send: true,
|
|
149
|
-
});
|
|
150
|
-
return result.message;
|
|
151
|
-
},
|
|
152
|
-
} as AITool,
|
|
153
|
-
],
|
|
154
|
-
},
|
|
155
|
-
];
|
|
156
|
-
|
|
157
|
-
export default memeSkills;
|