mioku-plugin-chat 2.9.9 → 2.9.11

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.
@@ -68,12 +68,7 @@ export async function runChat(
68
68
  return isSkillAllowedForRole(skill, toolCtx.triggerSkillRole);
69
69
  },
70
70
  );
71
- const emotionState = await humanize.emotionAgent.refreshIfNeeded({
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);
@@ -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 { prepareImageUrlsForModel } from "./image-compress";
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) {
@@ -146,7 +157,7 @@ export async function processImage(
146
157
 
147
158
  const record: ImageRecord = {
148
159
  hash,
149
- url: normalizeImageUrl(imageUrl) || imageUrl,
160
+ url: imageUrl,
150
161
  type: "image",
151
162
  description: analysis.description || "未知",
152
163
  createdAt: Date.now(),
@@ -166,17 +177,11 @@ export async function getImageTag(
166
177
  const exact = db.getImageByUrl(imageUrl);
167
178
  if (exact) return `[image:${exact.description}]`;
168
179
 
169
- const normalized = normalizeImageUrl(imageUrl);
170
- if (normalized && normalized !== imageUrl) {
171
- const normalizedHit = db.getImageByUrl(normalized);
172
- if (normalizedHit) return `[image:${normalizedHit.description}]`;
173
- }
174
-
175
180
  const hash = await calculateImageHash(imageUrl);
176
181
  if (hash) {
177
182
  const byHash = db.getImageByHash(hash);
178
183
  if (byHash) {
179
- db.saveImage(asImageRecord(byHash, normalized || imageUrl));
184
+ db.saveImage(asImageRecord(byHash, imageUrl));
180
185
  return `[image:${byHash.description}]`;
181
186
  }
182
187
  }
@@ -187,7 +192,7 @@ export async function getImageTag(
187
192
  function asImageRecord(record: ImageRecord, url: string): ImageRecord {
188
193
  return {
189
194
  ...record,
190
- url: normalizeImageUrl(url) || url,
195
+ url,
191
196
  type: "image",
192
197
  };
193
198
  }
@@ -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 FETCH_HEADERS = {
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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mioku-plugin-chat",
3
- "version": "2.9.9",
3
+ "version": "2.9.11",
4
4
  "description": "AI 智能聊天插件",
5
5
  "main": "index.ts",
6
6
  "type": "module",
package/utils/message.ts CHANGED
@@ -14,10 +14,7 @@ import {
14
14
  type HistoryMediaProcessingOptions,
15
15
  type MediaMessageSegment,
16
16
  } from "../core/media/history-media";
17
- import {
18
- calculateImageHash,
19
- normalizeImageUrl,
20
- } from "../core/media/image-analyzer";
17
+ import { calculateImageHash } from "../core/media/image-analyzer";
21
18
 
22
19
  const HISTORY_MEDIA_CONCURRENCY = 8;
23
20
 
@@ -310,12 +307,6 @@ async function getImageTagWithHashCache(
310
307
  const exact = db.getImageByUrl?.(imageUrl);
311
308
  if (exact) return `[image:${exact.description}]`;
312
309
 
313
- const normalized = normalizeImageUrl(imageUrl);
314
- if (normalized && normalized !== imageUrl) {
315
- const hit = db.getImageByUrl?.(normalized);
316
- if (hit) return `[image:${hit.description}]`;
317
- }
318
-
319
310
  let hash = ctx.hashLookupCache.get(`hash:${imageUrl}`);
320
311
  if (hash === undefined) {
321
312
  hash = (await calculateImageHash(imageUrl)) || null;
@@ -328,7 +319,7 @@ async function getImageTagWithHashCache(
328
319
  try {
329
320
  db.saveImage?.({
330
321
  hash: byHash.hash,
331
- url: normalized || imageUrl,
322
+ url: imageUrl,
332
323
  type: "image",
333
324
  description: byHash.description,
334
325
  createdAt: byHash.createdAt,