koishi-plugin-aka-ai-generator 0.4.1 → 0.4.4

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/lib/index.js CHANGED
@@ -29,6 +29,36 @@ var import_koishi = require("koishi");
29
29
  var import_fs = require("fs");
30
30
  var import_path = require("path");
31
31
 
32
+ // src/providers/types.ts
33
+ function sanitizeError(error) {
34
+ if (!error) return error;
35
+ if (typeof error === "string") {
36
+ return sanitizeString(error);
37
+ }
38
+ if (Array.isArray(error)) {
39
+ return error.map((item) => sanitizeError(item));
40
+ }
41
+ if (typeof error === "object") {
42
+ const sanitized = {};
43
+ for (const key in error) {
44
+ const lowerKey = key.toLowerCase();
45
+ if (lowerKey.includes("apikey") || lowerKey.includes("api_key") || lowerKey.includes("apikey") || lowerKey === "key" || lowerKey === "authorization" || lowerKey === "token" || lowerKey === "secret" || lowerKey === "password") {
46
+ sanitized[key] = "[REDACTED]";
47
+ continue;
48
+ }
49
+ sanitized[key] = sanitizeError(error[key]);
50
+ }
51
+ return sanitized;
52
+ }
53
+ return error;
54
+ }
55
+ __name(sanitizeError, "sanitizeError");
56
+ function sanitizeString(str) {
57
+ if (typeof str !== "string") return str;
58
+ return str.replace(/key["\s:=]+([a-zA-Z0-9_-]{20,})/gi, 'key="[REDACTED]"').replace(/apikey["\s:=]+([a-zA-Z0-9_-]{20,})/gi, 'apikey="[REDACTED]"').replace(/api_key["\s:=]+([a-zA-Z0-9_-]{20,})/gi, 'api_key="[REDACTED]"').replace(/authorization["\s:=]+(Bearer\s+)?([a-zA-Z0-9_-]{20,})/gi, 'authorization="[REDACTED]"').replace(/Bearer\s+([a-zA-Z0-9_-]{20,})/gi, "Bearer [REDACTED]");
59
+ }
60
+ __name(sanitizeString, "sanitizeString");
61
+
32
62
  // src/providers/yunwu.ts
33
63
  async function downloadImageAsBase64(ctx, url, timeout, logger) {
34
64
  try {
@@ -148,8 +178,9 @@ var YunwuProvider = class {
148
178
  allImages.push(...images);
149
179
  logger.success("云雾图像编辑 API 调用成功", { current: i + 1, total: numImages });
150
180
  } catch (error) {
181
+ const safeMessage = typeof error?.message === "string" ? sanitizeString(error.message) : "未知错误";
151
182
  logger.error("云雾图像编辑 API 调用失败", {
152
- message: error?.message || "未知错误",
183
+ message: safeMessage,
153
184
  code: error?.code,
154
185
  status: error?.response?.status,
155
186
  current: i + 1,
@@ -516,23 +547,25 @@ var GptGodProvider = class {
516
547
  await new Promise((resolve) => setTimeout(resolve, delay));
517
548
  continue;
518
549
  }
550
+ const sanitizedError = sanitizeError(error);
551
+ const safeMessage = typeof error?.message === "string" ? sanitizeString(error.message) : "未知错误";
519
552
  logger.error("GPTGod 图像编辑 API 调用失败", {
520
- message: error?.message || "未知错误",
553
+ message: safeMessage,
521
554
  name: error?.name,
522
555
  code: error?.code,
523
556
  status: error?.response?.status,
524
557
  statusText: error?.response?.statusText,
525
- data: error?.response?.data,
526
- stack: error?.stack,
527
- cause: error?.cause,
558
+ data: sanitizeError(error?.response?.data),
559
+ stack: sanitizeString(error?.stack || ""),
560
+ cause: sanitizeError(error?.cause),
528
561
  attempt,
529
562
  maxRetries,
530
563
  current: i + 1,
531
564
  total: numImages,
532
- // 如果是 axios 错误,通常会有 config 和 request 信息
565
+ // 如果是 axios 错误,通常会有 config 和 request 信息(清理敏感信息)
533
566
  url: error?.config?.url,
534
567
  method: error?.config?.method,
535
- headers: error?.config?.headers
568
+ headers: sanitizeError(error?.config?.headers)
536
569
  });
537
570
  if (error?.cause?.code === "UND_ERR_SOCKET" || error?.message?.includes("other side closed")) {
538
571
  throw new Error("图像处理失败:服务器连接中断,可能是服务器负载过高或网络不稳定,请稍后重试");
@@ -595,8 +628,11 @@ function parseGeminiResponse(response, logger) {
595
628
  return [];
596
629
  }
597
630
  if (response.error) {
598
- logger?.error("Gemini API 返回错误", { error: response.error });
599
- throw new Error(`Gemini API 错误: ${response.error.message || JSON.stringify(response.error)}`);
631
+ const sanitizedError = sanitizeError(response.error);
632
+ logger?.error("Gemini API 返回错误", { error: sanitizedError });
633
+ const errorMessage = response.error.message || JSON.stringify(sanitizedError);
634
+ const safeMessage = sanitizeString(errorMessage);
635
+ throw new Error(`Gemini API 错误: ${safeMessage}`);
600
636
  }
601
637
  if (response.promptFeedback) {
602
638
  const blockReason = response.promptFeedback.blockReason;
@@ -698,8 +734,12 @@ function parseGeminiResponse(response, logger) {
698
734
  }
699
735
  return images;
700
736
  } catch (error) {
701
- logger?.error("解析 Gemini 响应时出错", { error: error.message, stack: error.stack });
702
- throw error;
737
+ const safeMessage = sanitizeString(error?.message || "未知错误");
738
+ const safeStack = sanitizeString(error?.stack || "");
739
+ logger?.error("解析 Gemini 响应时出错", { error: safeMessage, stack: safeStack });
740
+ const sanitizedError = new Error(safeMessage);
741
+ sanitizedError.name = error?.name || "Error";
742
+ throw sanitizedError;
703
743
  }
704
744
  }
705
745
  __name(parseGeminiResponse, "parseGeminiResponse");
@@ -783,11 +823,13 @@ var GeminiProvider = class {
783
823
  }
784
824
  allImages.push(...images);
785
825
  } catch (error) {
826
+ const sanitizedError = sanitizeError(error);
827
+ const safeMessage = typeof error?.message === "string" ? sanitizeString(error.message) : "未知错误";
786
828
  logger.error("Gemini API 调用失败", {
787
- message: error?.message || "未知错误",
829
+ message: safeMessage,
788
830
  code: error?.code,
789
831
  status: error?.response?.status,
790
- responseData: error?.response?.data ? JSON.stringify(error.response.data).substring(0, 500) : void 0,
832
+ responseData: error?.response?.data ? sanitizeString(JSON.stringify(error.response.data).substring(0, 500)) : void 0,
791
833
  current: i + 1,
792
834
  total: numImages
793
835
  });
@@ -795,7 +837,7 @@ var GeminiProvider = class {
795
837
  logger.warn("部分图片生成失败,返回已生成的图片", { generated: allImages.length, requested: numImages });
796
838
  break;
797
839
  }
798
- throw new Error(`图像处理API调用失败: ${error?.message || "未知错误"}`);
840
+ throw new Error(`图像处理API调用失败: ${safeMessage}`);
799
841
  }
800
842
  }
801
843
  if (allImages.length === 0) {
@@ -1122,7 +1164,7 @@ function apply(ctx, config) {
1122
1164
  rateLimitMap.set(userId, userTimestamps);
1123
1165
  }
1124
1166
  __name(updateRateLimit, "updateRateLimit");
1125
- async function checkDailyLimit(userId, numImages = 1) {
1167
+ async function checkDailyLimit(userId, numImages = 1, updateRateLimitImmediately = true) {
1126
1168
  if (isAdmin(userId)) {
1127
1169
  return { allowed: true, isAdmin: true };
1128
1170
  }
@@ -1130,6 +1172,9 @@ function apply(ctx, config) {
1130
1172
  if (!rateLimitCheck.allowed) {
1131
1173
  return { ...rateLimitCheck, isAdmin: false };
1132
1174
  }
1175
+ if (updateRateLimitImmediately) {
1176
+ updateRateLimit(userId);
1177
+ }
1133
1178
  const usersData = await loadUsersData();
1134
1179
  const userData = usersData[userId];
1135
1180
  if (!userData) {
@@ -1309,7 +1354,6 @@ function apply(ctx, config) {
1309
1354
  const userId = session.userId;
1310
1355
  const userName = session.username || session.userId || "未知用户";
1311
1356
  if (!userId) return;
1312
- updateRateLimit(userId);
1313
1357
  const { userData, consumptionType, freeUsed, purchasedUsed } = await updateUserData(userId, userName, commandName, numImages);
1314
1358
  if (isAdmin(userId)) {
1315
1359
  await session.send(`📊 使用统计 [管理员]
@@ -1357,22 +1401,18 @@ function apply(ctx, config) {
1357
1401
  return { images: [], text: imgParam.trim() };
1358
1402
  }
1359
1403
  await session.send("请输入画面描述");
1360
- while (true) {
1361
- const msg = await session.prompt(3e4);
1362
- if (!msg) return { error: "等待超时" };
1363
- const elements = import_koishi.h.parse(msg);
1364
- const images = import_koishi.h.select(elements, "img");
1365
- if (images.length > 0) {
1366
- await session.send("检测到图片,请发送文字描述");
1367
- continue;
1368
- }
1369
- const text = import_koishi.h.select(elements, "text").map((e) => e.attrs.content).join(" ").trim();
1370
- if (!text) {
1371
- await session.send("未检测到描述,请重新发送");
1372
- continue;
1373
- }
1374
- return { images: [], text };
1404
+ const msg = await session.prompt(3e4);
1405
+ if (!msg) return { error: "等待超时" };
1406
+ const elements = import_koishi.h.parse(msg);
1407
+ const images = import_koishi.h.select(elements, "img");
1408
+ if (images.length > 0) {
1409
+ return { error: "检测到图片,本功能仅支持文字输入" };
1375
1410
  }
1411
+ const text = import_koishi.h.select(elements, "text").map((e) => e.attrs.content).join(" ").trim();
1412
+ if (!text) {
1413
+ return { error: "未检测到描述,操作已取消" };
1414
+ }
1415
+ return { images: [], text };
1376
1416
  }
1377
1417
  if (imgParam) {
1378
1418
  if (typeof imgParam === "object" && imgParam.attrs?.src) {
@@ -1427,8 +1467,7 @@ function apply(ctx, config) {
1427
1467
  }
1428
1468
  if (text) {
1429
1469
  if (collectedImages.length === 0) {
1430
- await session.send("未检测到图片,请先发送图片");
1431
- continue;
1470
+ return { error: "未检测到图片,请重新发起指令并发送图片" };
1432
1471
  }
1433
1472
  collectedText = text;
1434
1473
  break;
@@ -1460,8 +1499,10 @@ function apply(ctx, config) {
1460
1499
  ]).catch((error) => {
1461
1500
  const userId = session.userId;
1462
1501
  if (userId) activeTasks.delete(userId);
1463
- logger.error("图像处理超时或失败", { userId, error });
1464
- return error.message === "命令执行超时" ? "图像处理超时,请重试" : `图像处理失败:${error.message}`;
1502
+ const sanitizedError = sanitizeError(error);
1503
+ logger.error("图像处理超时或失败", { userId, error: sanitizedError });
1504
+ const safeMessage = typeof error?.message === "string" ? sanitizeString(error.message) : "未知错误";
1505
+ return error.message === "命令执行超时" ? "图像处理超时,请重试" : `图像处理失败:${safeMessage}`;
1465
1506
  });
1466
1507
  }
1467
1508
  __name(processImageWithTimeout, "processImageWithTimeout");
@@ -1486,23 +1527,20 @@ function apply(ctx, config) {
1486
1527
  finalPrompt = finalPrompt.trim();
1487
1528
  if (!finalPrompt) {
1488
1529
  await session.send("请发送画面描述");
1489
- while (true) {
1490
- const promptMsg = await session.prompt(3e4);
1491
- if (!promptMsg) {
1492
- return "未检测到描述,请重新发送";
1493
- }
1494
- const elements = import_koishi.h.parse(promptMsg);
1495
- const images = import_koishi.h.select(elements, "img");
1496
- if (images.length > 0) {
1497
- await session.send("检测到图片,请发送文字描述");
1498
- continue;
1499
- }
1500
- const text = import_koishi.h.select(elements, "text").map((e) => e.attrs.content).join(" ").trim();
1501
- if (text) {
1502
- finalPrompt = text;
1503
- break;
1504
- }
1505
- await session.send("未检测到有效文字描述,请重新发送");
1530
+ const promptMsg = await session.prompt(3e4);
1531
+ if (!promptMsg) {
1532
+ return "未检测到描述,操作已取消";
1533
+ }
1534
+ const elements = import_koishi.h.parse(promptMsg);
1535
+ const images = import_koishi.h.select(elements, "img");
1536
+ if (images.length > 0) {
1537
+ return "检测到图片,本功能仅支持文字输入";
1538
+ }
1539
+ const text = import_koishi.h.select(elements, "text").map((e) => e.attrs.content).join(" ").trim();
1540
+ if (text) {
1541
+ finalPrompt = text;
1542
+ } else {
1543
+ return "未检测到有效文字描述,操作已取消";
1506
1544
  }
1507
1545
  }
1508
1546
  const providerType = requestContext?.provider || config.provider;
@@ -1549,9 +1587,11 @@ ${infoParts.join("\n")}`;
1549
1587
  activeTasks.delete(userId);
1550
1588
  } catch (error) {
1551
1589
  activeTasks.delete(userId);
1552
- logger.error("图像处理失败", { userId, error });
1590
+ const sanitizedError = sanitizeError(error);
1591
+ logger.error("图像处理失败", { userId, error: sanitizedError });
1553
1592
  if (error?.message) {
1554
- return `图像处理失败:${error.message}`;
1593
+ const safeMessage = sanitizeString(error.message);
1594
+ return `图像处理失败:${safeMessage}`;
1555
1595
  }
1556
1596
  return "图像处理失败,请稍后重试";
1557
1597
  }
@@ -1670,7 +1710,7 @@ ${infoParts.join("\n")}`;
1670
1710
  prompt = text;
1671
1711
  break;
1672
1712
  }
1673
- return "未检测到有效内容,请重新发送";
1713
+ return "未检测到有效内容,操作已取消";
1674
1714
  }
1675
1715
  if (collectedImages.length < 2) {
1676
1716
  return "需要至少两张图片进行合成,请重新发送";
@@ -1713,9 +1753,11 @@ Prompt: ${prompt}`);
1713
1753
  activeTasks.delete(userId);
1714
1754
  } catch (error) {
1715
1755
  activeTasks.delete(userId);
1716
- logger.error("图片合成失败", { userId, error });
1756
+ const sanitizedError = sanitizeError(error);
1757
+ logger.error("图片合成失败", { userId, error: sanitizedError });
1717
1758
  if (error?.message) {
1718
- return `图片合成失败:${error.message}`;
1759
+ const safeMessage = sanitizeString(error.message);
1760
+ return `图片合成失败:${safeMessage}`;
1719
1761
  }
1720
1762
  return "图片合成失败,请稍后重试";
1721
1763
  }
@@ -1726,8 +1768,10 @@ Prompt: ${prompt}`);
1726
1768
  ]).catch((error) => {
1727
1769
  const userId = session.userId;
1728
1770
  if (userId) activeTasks.delete(userId);
1729
- logger.error("图片合成超时或失败", { userId, error });
1730
- return error.message === "命令执行超时" ? "图片合成超时,请重试" : `图片合成失败:${error.message}`;
1771
+ const sanitizedError = sanitizeError(error);
1772
+ logger.error("图片合成超时或失败", { userId, error: sanitizedError });
1773
+ const safeMessage = typeof error?.message === "string" ? sanitizeString(error.message) : "未知错误";
1774
+ return error.message === "命令执行超时" ? "图片合成超时,请重试" : `图片合成失败:${safeMessage}`;
1731
1775
  });
1732
1776
  });
1733
1777
  ctx.command(`${COMMANDS.RECHARGE} [content:text]`, "为用户充值次数(仅管理员)").action(async ({ session }, content) => {
@@ -14,3 +14,11 @@ export interface ProviderConfig {
14
14
  logger: any;
15
15
  ctx: any;
16
16
  }
17
+ /**
18
+ * 清理对象中的敏感信息(API KEY、密钥等)
19
+ */
20
+ export declare function sanitizeError(error: any): any;
21
+ /**
22
+ * 清理字符串中的 API KEY 模式
23
+ */
24
+ export declare function sanitizeString(str: string): string;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-aka-ai-generator",
3
3
  "description": "自用AI生成插件(GPTGod & Yunwu)",
4
- "version": "0.4.1",
4
+ "version": "0.4.4",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [
@@ -9,11 +9,19 @@
9
9
  "dist"
10
10
  ],
11
11
  "license": "MIT",
12
+ "scripts": {
13
+ "build": "tsc",
14
+ "dev": "tsc --watch"
15
+ },
12
16
  "keywords": [
13
17
  "chatbot",
14
18
  "koishi",
15
19
  "plugin"
16
20
  ],
21
+ "devDependencies": {
22
+ "@types/node": "^20.0.0",
23
+ "typescript": "^5.0.0"
24
+ },
17
25
  "peerDependencies": {
18
26
  "koishi": "^4.18.9"
19
27
  }