koishi-plugin-aka-ai-generator 0.4.2 → 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 +76 -24
- package/lib/providers/types.d.ts +8 -0
- package/package.json +9 -1
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:
|
|
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:
|
|
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
|
-
|
|
599
|
-
|
|
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
|
-
|
|
702
|
-
|
|
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:
|
|
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调用失败: ${
|
|
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(`📊 使用统计 [管理员]
|
|
@@ -1455,8 +1499,10 @@ function apply(ctx, config) {
|
|
|
1455
1499
|
]).catch((error) => {
|
|
1456
1500
|
const userId = session.userId;
|
|
1457
1501
|
if (userId) activeTasks.delete(userId);
|
|
1458
|
-
|
|
1459
|
-
|
|
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}`;
|
|
1460
1506
|
});
|
|
1461
1507
|
}
|
|
1462
1508
|
__name(processImageWithTimeout, "processImageWithTimeout");
|
|
@@ -1541,9 +1587,11 @@ ${infoParts.join("\n")}`;
|
|
|
1541
1587
|
activeTasks.delete(userId);
|
|
1542
1588
|
} catch (error) {
|
|
1543
1589
|
activeTasks.delete(userId);
|
|
1544
|
-
|
|
1590
|
+
const sanitizedError = sanitizeError(error);
|
|
1591
|
+
logger.error("图像处理失败", { userId, error: sanitizedError });
|
|
1545
1592
|
if (error?.message) {
|
|
1546
|
-
|
|
1593
|
+
const safeMessage = sanitizeString(error.message);
|
|
1594
|
+
return `图像处理失败:${safeMessage}`;
|
|
1547
1595
|
}
|
|
1548
1596
|
return "图像处理失败,请稍后重试";
|
|
1549
1597
|
}
|
|
@@ -1705,9 +1753,11 @@ Prompt: ${prompt}`);
|
|
|
1705
1753
|
activeTasks.delete(userId);
|
|
1706
1754
|
} catch (error) {
|
|
1707
1755
|
activeTasks.delete(userId);
|
|
1708
|
-
|
|
1756
|
+
const sanitizedError = sanitizeError(error);
|
|
1757
|
+
logger.error("图片合成失败", { userId, error: sanitizedError });
|
|
1709
1758
|
if (error?.message) {
|
|
1710
|
-
|
|
1759
|
+
const safeMessage = sanitizeString(error.message);
|
|
1760
|
+
return `图片合成失败:${safeMessage}`;
|
|
1711
1761
|
}
|
|
1712
1762
|
return "图片合成失败,请稍后重试";
|
|
1713
1763
|
}
|
|
@@ -1718,8 +1768,10 @@ Prompt: ${prompt}`);
|
|
|
1718
1768
|
]).catch((error) => {
|
|
1719
1769
|
const userId = session.userId;
|
|
1720
1770
|
if (userId) activeTasks.delete(userId);
|
|
1721
|
-
|
|
1722
|
-
|
|
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}`;
|
|
1723
1775
|
});
|
|
1724
1776
|
});
|
|
1725
1777
|
ctx.command(`${COMMANDS.RECHARGE} [content:text]`, "为用户充值次数(仅管理员)").action(async ({ session }, content) => {
|
package/lib/providers/types.d.ts
CHANGED
|
@@ -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.
|
|
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
|
}
|