mioku-plugin-chat 2.9.0 → 2.9.10
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/core/chat-engine.ts +12 -6
- package/core/chat-turn.ts +2 -0
- package/core/media/gif-extractor.ts +3 -6
- package/core/media/image-analyzer.ts +13 -2
- package/core/media/image-compress.ts +3 -1
- package/handlers/idle-debug.ts +1 -0
- package/handlers/poke.ts +1 -0
- package/handlers/tts-command.ts +7 -5
- package/manage/cooldown.ts +2 -0
- package/manage/idle-check.ts +1 -0
- package/manage/queue-processor.ts +2 -0
- package/package.json +1 -1
package/core/chat-engine.ts
CHANGED
|
@@ -68,12 +68,7 @@ export async function runChat(
|
|
|
68
68
|
return isSkillAllowedForRole(skill, toolCtx.triggerSkillRole);
|
|
69
69
|
},
|
|
70
70
|
);
|
|
71
|
-
const emotionState =
|
|
72
|
-
sessionId: toolCtx.sessionId,
|
|
73
|
-
botNickname: promptCtx.botNickname,
|
|
74
|
-
chatHistory: history,
|
|
75
|
-
targetMessage,
|
|
76
|
-
});
|
|
71
|
+
const emotionState = humanize.emotionAgent.getCurrent(toolCtx.sessionId);
|
|
77
72
|
|
|
78
73
|
const staticCtx: StaticPromptContext = {
|
|
79
74
|
config: promptCtx.config,
|
|
@@ -249,6 +244,17 @@ export async function runChat(
|
|
|
249
244
|
? await ai.withUsageContext(usageContext, runComplete)
|
|
250
245
|
: await runComplete();
|
|
251
246
|
|
|
247
|
+
humanize.emotionAgent
|
|
248
|
+
.refreshIfNeeded({
|
|
249
|
+
sessionId: toolCtx.sessionId,
|
|
250
|
+
botNickname: promptCtx.botNickname,
|
|
251
|
+
chatHistory: history,
|
|
252
|
+
targetMessage,
|
|
253
|
+
})
|
|
254
|
+
.catch((err) =>
|
|
255
|
+
logger.warn(`[chat-engine] background emotion refresh failed: ${err}`),
|
|
256
|
+
);
|
|
257
|
+
|
|
252
258
|
if (streamEnabled) {
|
|
253
259
|
streamBuffer += streamThinkFilter.push("", true);
|
|
254
260
|
await flushStreamBuffer(true);
|
package/core/chat-turn.ts
CHANGED
|
@@ -312,6 +312,7 @@ export async function processChat(
|
|
|
312
312
|
humanize: pluginCtx.humanize,
|
|
313
313
|
targetMessage,
|
|
314
314
|
selfId: e.self_id,
|
|
315
|
+
audioService: pluginCtx.audioService,
|
|
315
316
|
});
|
|
316
317
|
|
|
317
318
|
const result = await pluginCtx.runWithRateLimitGuard(
|
|
@@ -497,6 +498,7 @@ async function executeChatRuntimeRequestNow(
|
|
|
497
498
|
humanize: pluginCtx.humanize,
|
|
498
499
|
targetMessage,
|
|
499
500
|
selfId,
|
|
501
|
+
audioService: pluginCtx.audioService,
|
|
500
502
|
});
|
|
501
503
|
|
|
502
504
|
if (options.send === false) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { logger } from "mioki";
|
|
2
2
|
import sharp from "sharp";
|
|
3
|
+
import { QQ_IMAGE_FETCH_HEADERS } from "./image-compress";
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* GIF 帧提取结果
|
|
@@ -19,7 +20,7 @@ export async function extractGifFrames(
|
|
|
19
20
|
): Promise<GifFramesResult | null> {
|
|
20
21
|
try {
|
|
21
22
|
// 下载 GIF
|
|
22
|
-
const response = await fetch(gifUrl);
|
|
23
|
+
const response = await fetch(gifUrl, { headers: QQ_IMAGE_FETCH_HEADERS });
|
|
23
24
|
if (!response.ok) {
|
|
24
25
|
logger.error(
|
|
25
26
|
`[gif-extractor] Failed to fetch GIF: ${response.statusText}`,
|
|
@@ -102,11 +103,7 @@ export async function isGifUrl(url: string): Promise<boolean> {
|
|
|
102
103
|
try {
|
|
103
104
|
const response = await fetch(url, {
|
|
104
105
|
method: "GET",
|
|
105
|
-
headers:
|
|
106
|
-
"User-Agent":
|
|
107
|
-
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
|
|
108
|
-
Referer: "https://qq.com/",
|
|
109
|
-
},
|
|
106
|
+
headers: QQ_IMAGE_FETCH_HEADERS,
|
|
110
107
|
});
|
|
111
108
|
const contentType = response.headers.get("content-type");
|
|
112
109
|
if (contentType && contentType.includes("image/gif")) {
|
|
@@ -3,7 +3,10 @@ import type { AIInstance } from "mioku";
|
|
|
3
3
|
import { logger } from "mioki";
|
|
4
4
|
import type { ChatDatabase } from "../../db";
|
|
5
5
|
import type { ImageRecord } from "../../types";
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
prepareImageUrlsForModel,
|
|
8
|
+
QQ_IMAGE_FETCH_HEADERS,
|
|
9
|
+
} from "./image-compress";
|
|
7
10
|
|
|
8
11
|
export interface ImageAnalysisResult {
|
|
9
12
|
success: boolean;
|
|
@@ -17,7 +20,7 @@ export interface ImageAnalysisOptions {
|
|
|
17
20
|
|
|
18
21
|
export async function calculateImageHash(url: string): Promise<string | null> {
|
|
19
22
|
try {
|
|
20
|
-
const response = await fetch(url);
|
|
23
|
+
const response = await fetch(url, { headers: QQ_IMAGE_FETCH_HEADERS });
|
|
21
24
|
if (!response.ok) {
|
|
22
25
|
logger.warn(
|
|
23
26
|
`[image-analyzer] Failed to download image for hashing: ${response.status} ${response.statusText}`,
|
|
@@ -25,6 +28,14 @@ export async function calculateImageHash(url: string): Promise<string | null> {
|
|
|
25
28
|
return null;
|
|
26
29
|
}
|
|
27
30
|
|
|
31
|
+
const contentType = response.headers.get("content-type");
|
|
32
|
+
if (contentType && contentType.startsWith("text/")) {
|
|
33
|
+
logger.warn(
|
|
34
|
+
`[image-analyzer] Non-image content for hashing (${contentType}): ${url}`,
|
|
35
|
+
);
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
|
|
28
39
|
const buffer = Buffer.from(await response.arrayBuffer());
|
|
29
40
|
return crypto.createHash("md5").update(buffer).digest("hex");
|
|
30
41
|
} catch (err) {
|
|
@@ -5,12 +5,14 @@ const IMAGE_MAX_BYTES = 1 * 1024 * 1024;
|
|
|
5
5
|
const COMPRESS_MAX_WIDTH = 1280;
|
|
6
6
|
const COMPRESS_JPEG_QUALITY = 80;
|
|
7
7
|
|
|
8
|
-
const
|
|
8
|
+
export const QQ_IMAGE_FETCH_HEADERS = {
|
|
9
9
|
"User-Agent":
|
|
10
10
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
|
|
11
11
|
Referer: "https://qq.com/",
|
|
12
12
|
};
|
|
13
13
|
|
|
14
|
+
const FETCH_HEADERS = QQ_IMAGE_FETCH_HEADERS;
|
|
15
|
+
|
|
14
16
|
/**
|
|
15
17
|
* 准备发给模型的图片 URL:体积超过 1MB 时压缩为 JPEG data URL,否则原样返回。
|
|
16
18
|
* data URL(如 GIF 抽帧结果)与非 http(s) 链接直接放行。
|
package/handlers/idle-debug.ts
CHANGED
package/handlers/poke.ts
CHANGED
package/handlers/tts-command.ts
CHANGED
|
@@ -16,10 +16,10 @@ export async function handleTtsCommand(
|
|
|
16
16
|
): Promise<void> {
|
|
17
17
|
const { ctx } = pluginCtx;
|
|
18
18
|
const isOwner = ctx.isOwner?.(e) ?? false;
|
|
19
|
-
if (!isOwner) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
19
|
+
// if (!isOwner) {
|
|
20
|
+
// await e.reply("只有主人才能使用 /tts 指令~");
|
|
21
|
+
// return;
|
|
22
|
+
// }
|
|
23
23
|
|
|
24
24
|
const text = String(payload || "").trim();
|
|
25
25
|
if (!text) {
|
|
@@ -42,7 +42,9 @@ export async function handleTtsCommand(
|
|
|
42
42
|
return;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
ctx.logger.info(
|
|
45
|
+
ctx.logger.info(
|
|
46
|
+
`[tts-cmd] 推理: "${text.slice(0, 40)}" lang=${detectLang(text)}`,
|
|
47
|
+
);
|
|
46
48
|
|
|
47
49
|
let result;
|
|
48
50
|
try {
|
package/manage/cooldown.ts
CHANGED
|
@@ -227,6 +227,7 @@ export class CooldownManager {
|
|
|
227
227
|
humanize: this.humanize,
|
|
228
228
|
targetMessage,
|
|
229
229
|
selfId,
|
|
230
|
+
audioService: this.pluginCtx.audioService,
|
|
230
231
|
});
|
|
231
232
|
|
|
232
233
|
const contexts = await this.getHumanizeContexts(
|
|
@@ -353,6 +354,7 @@ export class CooldownManager {
|
|
|
353
354
|
humanize: this.humanize,
|
|
354
355
|
targetMessage,
|
|
355
356
|
selfId,
|
|
357
|
+
audioService: this.pluginCtx.audioService,
|
|
356
358
|
});
|
|
357
359
|
const { groupName, memberCount } = await this.getGroupInfoData(
|
|
358
360
|
this.ctx,
|
package/manage/idle-check.ts
CHANGED
|
@@ -184,6 +184,7 @@ export class QueueProcessor {
|
|
|
184
184
|
const toolCtx = this.buildToolContext({
|
|
185
185
|
ctx: this.ctx, event: firstMsg.event, groupSessionId, groupId, userId: firstMsg.userId,
|
|
186
186
|
config: cfg, aiService: this.aiService, db: this.db, botRole, humanize: this.humanize, targetMessage, selfId,
|
|
187
|
+
audioService: this.pluginCtx.audioService,
|
|
187
188
|
});
|
|
188
189
|
|
|
189
190
|
this.sessionManager.getOrCreate(groupSessionId, "group", groupId);
|
|
@@ -264,6 +265,7 @@ export class QueueProcessor {
|
|
|
264
265
|
const toolCtx = this.buildToolContext({
|
|
265
266
|
ctx: this.ctx, event: null, groupSessionId, groupId, userId: targetMessage.userId,
|
|
266
267
|
config: cfg, aiService: this.aiService, db: this.db, botRole, humanize: this.humanize, targetMessage, selfId,
|
|
268
|
+
audioService: this.pluginCtx.audioService,
|
|
267
269
|
});
|
|
268
270
|
|
|
269
271
|
const { history } = await this.getGroupHistoryMessages(groupId, groupSessionId, this.ctx, cfg.historyCount, this.db, selfId, this.buildHistoryMediaOptions(this.aiInstance, cfg));
|