koishi-plugin-aka-ai-generator 0.5.0 → 0.5.1
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.d.ts +2 -2
- package/lib/index.js +74 -103
- package/package.json +1 -1
package/lib/index.d.ts
CHANGED
|
@@ -25,8 +25,6 @@ export interface UserData {
|
|
|
25
25
|
donationAmount: number;
|
|
26
26
|
lastUsed: string;
|
|
27
27
|
createdAt: string;
|
|
28
|
-
safetyBlockCount: number;
|
|
29
|
-
safetyBlockHistory: string[];
|
|
30
28
|
}
|
|
31
29
|
export interface UsersData {
|
|
32
30
|
[userId: string]: UserData;
|
|
@@ -51,6 +49,8 @@ export interface Config {
|
|
|
51
49
|
styles: StyleConfig[];
|
|
52
50
|
styleGroups?: Record<string, StyleGroupConfig>;
|
|
53
51
|
logLevel: 'info' | 'debug';
|
|
52
|
+
securityBlockWindow: number;
|
|
53
|
+
securityBlockWarningThreshold: number;
|
|
54
54
|
}
|
|
55
55
|
export interface RechargeRecord {
|
|
56
56
|
id: string;
|
package/lib/index.js
CHANGED
|
@@ -943,7 +943,10 @@ var Config = import_koishi.Schema.intersect([
|
|
|
943
943
|
logLevel: import_koishi.Schema.union([
|
|
944
944
|
import_koishi.Schema.const("info").description("普通信息"),
|
|
945
945
|
import_koishi.Schema.const("debug").description("完整的debug信息")
|
|
946
|
-
]).default("info").description("日志输出详细程度")
|
|
946
|
+
]).default("info").description("日志输出详细程度"),
|
|
947
|
+
// 安全策略拦截设置
|
|
948
|
+
securityBlockWindow: import_koishi.Schema.number().default(600).min(60).max(3600).description("安全策略拦截追踪时间窗口(秒),在此时间窗口内连续触发拦截会被记录"),
|
|
949
|
+
securityBlockWarningThreshold: import_koishi.Schema.number().default(3).min(1).max(10).description("安全策略拦截警示阈值,连续触发此次数拦截后将发送警示消息,再次触发将被扣除积分")
|
|
947
950
|
}),
|
|
948
951
|
// 自定义风格命令配置
|
|
949
952
|
import_koishi.Schema.object({
|
|
@@ -968,6 +971,8 @@ function apply(ctx, config) {
|
|
|
968
971
|
const logger = ctx.logger("aka-ai-generator");
|
|
969
972
|
const activeTasks = /* @__PURE__ */ new Map();
|
|
970
973
|
const rateLimitMap = /* @__PURE__ */ new Map();
|
|
974
|
+
const securityBlockMap = /* @__PURE__ */ new Map();
|
|
975
|
+
const securityWarningMap = /* @__PURE__ */ new Map();
|
|
971
976
|
const providerCache = /* @__PURE__ */ new Map();
|
|
972
977
|
function getProviderInstance(providerType, modelId) {
|
|
973
978
|
const cacheKey = `${providerType}:${modelId || "default"}`;
|
|
@@ -1207,46 +1212,6 @@ function apply(ctx, config) {
|
|
|
1207
1212
|
return { allowed: true, isAdmin: false };
|
|
1208
1213
|
}
|
|
1209
1214
|
__name(checkDailyLimit, "checkDailyLimit");
|
|
1210
|
-
function isSafetyBlockError(error) {
|
|
1211
|
-
if (!error) return false;
|
|
1212
|
-
const errorMessage = typeof error === "string" ? error : error?.message || "";
|
|
1213
|
-
if (!errorMessage) return false;
|
|
1214
|
-
const lowerMessage = errorMessage.toLowerCase();
|
|
1215
|
-
const safetyKeywords = [
|
|
1216
|
-
"安全策略",
|
|
1217
|
-
"被阻止",
|
|
1218
|
-
"被拦截",
|
|
1219
|
-
"blocked",
|
|
1220
|
-
"safety",
|
|
1221
|
-
"prohibited",
|
|
1222
|
-
"recitation",
|
|
1223
|
-
"内容被阻止",
|
|
1224
|
-
"内容被拦截",
|
|
1225
|
-
"安全策略阻止",
|
|
1226
|
-
"安全策略拦截"
|
|
1227
|
-
];
|
|
1228
|
-
return safetyKeywords.some((keyword) => lowerMessage.includes(keyword.toLowerCase()));
|
|
1229
|
-
}
|
|
1230
|
-
__name(isSafetyBlockError, "isSafetyBlockError");
|
|
1231
|
-
async function checkSafetyBlockLimit(userId) {
|
|
1232
|
-
const usersData = await loadUsersData();
|
|
1233
|
-
const userData = usersData[userId];
|
|
1234
|
-
if (!userData || !userData.safetyBlockHistory || userData.safetyBlockHistory.length === 0) {
|
|
1235
|
-
return { allowed: true };
|
|
1236
|
-
}
|
|
1237
|
-
const windowSize = 10;
|
|
1238
|
-
const recentBlocks = userData.safetyBlockHistory.slice(-windowSize);
|
|
1239
|
-
const blockRate = recentBlocks.length / windowSize;
|
|
1240
|
-
const threshold = 0.5;
|
|
1241
|
-
if (blockRate > threshold) {
|
|
1242
|
-
return {
|
|
1243
|
-
allowed: false,
|
|
1244
|
-
message: `检测到您最近多次发送被安全策略拦截的内容(拦截率:${(blockRate * 100).toFixed(0)}%),请检查您的内容是否符合使用规范。如继续发送违规内容,可能会被进一步限制。`
|
|
1245
|
-
};
|
|
1246
|
-
}
|
|
1247
|
-
return { allowed: true };
|
|
1248
|
-
}
|
|
1249
|
-
__name(checkSafetyBlockLimit, "checkSafetyBlockLimit");
|
|
1250
1215
|
async function getPromptInput(session, message) {
|
|
1251
1216
|
await session.send(message);
|
|
1252
1217
|
const input = await session.prompt(3e4);
|
|
@@ -1326,24 +1291,15 @@ function apply(ctx, config) {
|
|
|
1326
1291
|
donationCount: 0,
|
|
1327
1292
|
donationAmount: 0,
|
|
1328
1293
|
lastUsed: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1329
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1330
|
-
safetyBlockCount: 0,
|
|
1331
|
-
safetyBlockHistory: []
|
|
1294
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1332
1295
|
};
|
|
1333
1296
|
await saveUsersData(usersData);
|
|
1334
1297
|
logger.info("创建新用户数据", { userId, userName });
|
|
1335
|
-
} else {
|
|
1336
|
-
if (usersData[userId].safetyBlockCount === void 0) {
|
|
1337
|
-
usersData[userId].safetyBlockCount = 0;
|
|
1338
|
-
}
|
|
1339
|
-
if (usersData[userId].safetyBlockHistory === void 0) {
|
|
1340
|
-
usersData[userId].safetyBlockHistory = [];
|
|
1341
|
-
}
|
|
1342
1298
|
}
|
|
1343
1299
|
return usersData[userId];
|
|
1344
1300
|
}
|
|
1345
1301
|
__name(getUserData, "getUserData");
|
|
1346
|
-
async function updateUserData(userId, userName, commandName, numImages = 1
|
|
1302
|
+
async function updateUserData(userId, userName, commandName, numImages = 1) {
|
|
1347
1303
|
const usersData = await loadUsersData();
|
|
1348
1304
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1349
1305
|
const today = (/* @__PURE__ */ new Date()).toDateString();
|
|
@@ -1359,19 +1315,11 @@ function apply(ctx, config) {
|
|
|
1359
1315
|
donationCount: 0,
|
|
1360
1316
|
donationAmount: 0,
|
|
1361
1317
|
lastUsed: now,
|
|
1362
|
-
createdAt: now
|
|
1363
|
-
safetyBlockCount: isSafetyBlock ? numImages : 0,
|
|
1364
|
-
safetyBlockHistory: isSafetyBlock ? [now] : []
|
|
1318
|
+
createdAt: now
|
|
1365
1319
|
};
|
|
1366
1320
|
await saveUsersData(usersData);
|
|
1367
1321
|
return { userData: usersData[userId], consumptionType: "free", freeUsed: numImages, purchasedUsed: 0 };
|
|
1368
1322
|
}
|
|
1369
|
-
if (usersData[userId].safetyBlockCount === void 0) {
|
|
1370
|
-
usersData[userId].safetyBlockCount = 0;
|
|
1371
|
-
}
|
|
1372
|
-
if (usersData[userId].safetyBlockHistory === void 0) {
|
|
1373
|
-
usersData[userId].safetyBlockHistory = [];
|
|
1374
|
-
}
|
|
1375
1323
|
usersData[userId].totalUsageCount += numImages;
|
|
1376
1324
|
usersData[userId].lastUsed = now;
|
|
1377
1325
|
const lastReset = new Date(usersData[userId].lastDailyReset || usersData[userId].createdAt).toDateString();
|
|
@@ -1395,13 +1343,6 @@ function apply(ctx, config) {
|
|
|
1395
1343
|
purchasedUsed = purchasedToUse;
|
|
1396
1344
|
remainingToConsume -= purchasedToUse;
|
|
1397
1345
|
}
|
|
1398
|
-
if (isSafetyBlock) {
|
|
1399
|
-
usersData[userId].safetyBlockCount += numImages;
|
|
1400
|
-
usersData[userId].safetyBlockHistory.push(now);
|
|
1401
|
-
if (usersData[userId].safetyBlockHistory.length > 20) {
|
|
1402
|
-
usersData[userId].safetyBlockHistory = usersData[userId].safetyBlockHistory.slice(-20);
|
|
1403
|
-
}
|
|
1404
|
-
}
|
|
1405
1346
|
await saveUsersData(usersData);
|
|
1406
1347
|
let consumptionType;
|
|
1407
1348
|
if (freeUsed > 0 && purchasedUsed > 0) {
|
|
@@ -1414,17 +1355,16 @@ function apply(ctx, config) {
|
|
|
1414
1355
|
return { userData: usersData[userId], consumptionType, freeUsed, purchasedUsed };
|
|
1415
1356
|
}
|
|
1416
1357
|
__name(updateUserData, "updateUserData");
|
|
1417
|
-
async function recordUserUsage(session, commandName, numImages = 1
|
|
1358
|
+
async function recordUserUsage(session, commandName, numImages = 1) {
|
|
1418
1359
|
const userId = session.userId;
|
|
1419
1360
|
const userName = session.username || session.userId || "未知用户";
|
|
1420
1361
|
if (!userId) return;
|
|
1421
|
-
const { userData, consumptionType, freeUsed, purchasedUsed } = await updateUserData(userId, userName, commandName, numImages
|
|
1362
|
+
const { userData, consumptionType, freeUsed, purchasedUsed } = await updateUserData(userId, userName, commandName, numImages);
|
|
1422
1363
|
if (isAdmin(userId)) {
|
|
1423
|
-
const blockInfo = isSafetyBlock ? "\n⚠️ 本次请求被安全策略拦截,已扣除使用次数" : "";
|
|
1424
1364
|
await session.send(`📊 使用统计 [管理员]
|
|
1425
1365
|
用户:${userData.userName}
|
|
1426
1366
|
总调用次数:${userData.totalUsageCount}次
|
|
1427
|
-
|
|
1367
|
+
状态:无限制使用`);
|
|
1428
1368
|
} else {
|
|
1429
1369
|
const remainingToday = Math.max(0, config.dailyFreeLimit - userData.dailyUsageCount);
|
|
1430
1370
|
let consumptionText = "";
|
|
@@ -1435,14 +1375,13 @@ function apply(ctx, config) {
|
|
|
1435
1375
|
} else {
|
|
1436
1376
|
consumptionText = `充值次数 -${purchasedUsed}`;
|
|
1437
1377
|
}
|
|
1438
|
-
const blockInfo = isSafetyBlock ? "\n⚠️ 本次请求被安全策略拦截,已扣除使用次数。请检查内容是否符合使用规范。" : "";
|
|
1439
1378
|
await session.send(`📊 使用统计
|
|
1440
1379
|
用户:${userData.userName}
|
|
1441
1380
|
本次生成:${numImages}张图片
|
|
1442
1381
|
本次消费:${consumptionText}
|
|
1443
1382
|
总调用次数:${userData.totalUsageCount}次
|
|
1444
1383
|
今日剩余免费:${remainingToday}次
|
|
1445
|
-
充值剩余:${userData.remainingPurchasedCount}
|
|
1384
|
+
充值剩余:${userData.remainingPurchasedCount}次`);
|
|
1446
1385
|
}
|
|
1447
1386
|
logger.info("用户调用记录", {
|
|
1448
1387
|
userId,
|
|
@@ -1455,12 +1394,44 @@ function apply(ctx, config) {
|
|
|
1455
1394
|
totalUsageCount: userData.totalUsageCount,
|
|
1456
1395
|
dailyUsageCount: userData.dailyUsageCount,
|
|
1457
1396
|
remainingPurchasedCount: userData.remainingPurchasedCount,
|
|
1458
|
-
isSafetyBlock,
|
|
1459
|
-
safetyBlockCount: userData.safetyBlockCount,
|
|
1460
1397
|
isAdmin: isAdmin(userId)
|
|
1461
1398
|
});
|
|
1462
1399
|
}
|
|
1463
1400
|
__name(recordUserUsage, "recordUserUsage");
|
|
1401
|
+
async function recordSecurityBlock(session, numImages = 1) {
|
|
1402
|
+
const userId = session.userId;
|
|
1403
|
+
if (!userId) return;
|
|
1404
|
+
if (isAdmin(userId)) {
|
|
1405
|
+
return;
|
|
1406
|
+
}
|
|
1407
|
+
const now = Date.now();
|
|
1408
|
+
const windowMs = config.securityBlockWindow * 1e3;
|
|
1409
|
+
const windowStart = now - windowMs;
|
|
1410
|
+
let blockTimestamps = securityBlockMap.get(userId) || [];
|
|
1411
|
+
blockTimestamps = blockTimestamps.filter((timestamp) => timestamp > windowStart);
|
|
1412
|
+
blockTimestamps.push(now);
|
|
1413
|
+
securityBlockMap.set(userId, blockTimestamps);
|
|
1414
|
+
const blockCount = blockTimestamps.length;
|
|
1415
|
+
const hasWarning = securityWarningMap.get(userId) || false;
|
|
1416
|
+
logger.info("安全策略拦截记录", {
|
|
1417
|
+
userId,
|
|
1418
|
+
blockCount,
|
|
1419
|
+
threshold: config.securityBlockWarningThreshold,
|
|
1420
|
+
hasWarning,
|
|
1421
|
+
numImages
|
|
1422
|
+
});
|
|
1423
|
+
if (blockCount >= config.securityBlockWarningThreshold && !hasWarning) {
|
|
1424
|
+
securityWarningMap.set(userId, true);
|
|
1425
|
+
await session.send(`⚠️ 安全策略警示
|
|
1426
|
+
您已连续${config.securityBlockWarningThreshold}次触发安全策略拦截,再次发送被拦截内容将被扣除积分`);
|
|
1427
|
+
logger.warn("用户收到安全策略警示", { userId, blockCount, threshold: config.securityBlockWarningThreshold });
|
|
1428
|
+
} else if (hasWarning) {
|
|
1429
|
+
const commandName = "安全策略拦截";
|
|
1430
|
+
await recordUserUsage(session, commandName, numImages);
|
|
1431
|
+
logger.warn("用户因安全策略拦截被扣除积分", { userId, numImages });
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
1434
|
+
__name(recordSecurityBlock, "recordSecurityBlock");
|
|
1464
1435
|
async function getInputData(session, imgParam, mode) {
|
|
1465
1436
|
const collectedImages = [];
|
|
1466
1437
|
let collectedText = "";
|
|
@@ -1564,11 +1535,19 @@ function apply(ctx, config) {
|
|
|
1564
1535
|
new Promise(
|
|
1565
1536
|
(_, reject) => setTimeout(() => reject(new Error("命令执行超时")), config.commandTimeout * 1e3)
|
|
1566
1537
|
)
|
|
1567
|
-
]).catch((error) => {
|
|
1538
|
+
]).catch(async (error) => {
|
|
1568
1539
|
const userId = session.userId;
|
|
1569
1540
|
if (userId) activeTasks.delete(userId);
|
|
1570
1541
|
const sanitizedError = sanitizeError(error);
|
|
1571
1542
|
logger.error("图像处理超时或失败", { userId, error: sanitizedError });
|
|
1543
|
+
if (error?.message !== "命令执行超时") {
|
|
1544
|
+
const errorMessage = error?.message || "";
|
|
1545
|
+
const isSecurityBlock = errorMessage.includes("内容被安全策略拦截") || errorMessage.includes("内容被安全策略阻止") || errorMessage.includes("内容被阻止") || errorMessage.includes("被阻止") || errorMessage.includes("SAFETY") || errorMessage.includes("RECITATION");
|
|
1546
|
+
if (isSecurityBlock) {
|
|
1547
|
+
const imageCount = requestContext?.numImages || config.defaultNumImages;
|
|
1548
|
+
await recordSecurityBlock(session, imageCount);
|
|
1549
|
+
}
|
|
1550
|
+
}
|
|
1572
1551
|
const safeMessage = typeof error?.message === "string" ? sanitizeString(error.message) : "未知错误";
|
|
1573
1552
|
return error.message === "命令执行超时" ? "图像处理超时,请重试" : `图像处理失败:${safeMessage}`;
|
|
1574
1553
|
});
|
|
@@ -1657,17 +1636,10 @@ ${infoParts.join("\n")}`;
|
|
|
1657
1636
|
activeTasks.delete(userId);
|
|
1658
1637
|
const sanitizedError = sanitizeError(error);
|
|
1659
1638
|
logger.error("图像处理失败", { userId, error: sanitizedError });
|
|
1660
|
-
const
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
const blockLimitCheck = await checkSafetyBlockLimit(userId);
|
|
1665
|
-
if (!blockLimitCheck.allowed) {
|
|
1666
|
-
return `图像处理失败:内容被安全策略拦截。${blockLimitCheck.message}`;
|
|
1667
|
-
}
|
|
1668
|
-
} catch (recordError) {
|
|
1669
|
-
logger.error("记录安全拦截使用次数失败", { userId, error: recordError });
|
|
1670
|
-
}
|
|
1639
|
+
const errorMessage = error?.message || "";
|
|
1640
|
+
const isSecurityBlock = errorMessage.includes("内容被安全策略拦截") || errorMessage.includes("内容被安全策略阻止") || errorMessage.includes("内容被阻止") || errorMessage.includes("被阻止") || errorMessage.includes("SAFETY") || errorMessage.includes("RECITATION");
|
|
1641
|
+
if (isSecurityBlock) {
|
|
1642
|
+
await recordSecurityBlock(session, imageCount);
|
|
1671
1643
|
}
|
|
1672
1644
|
if (error?.message) {
|
|
1673
1645
|
const safeMessage = sanitizeString(error.message);
|
|
@@ -1835,17 +1807,10 @@ Prompt: ${prompt}`);
|
|
|
1835
1807
|
activeTasks.delete(userId);
|
|
1836
1808
|
const sanitizedError = sanitizeError(error);
|
|
1837
1809
|
logger.error("图片合成失败", { userId, error: sanitizedError });
|
|
1838
|
-
const
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
const blockLimitCheck = await checkSafetyBlockLimit(userId);
|
|
1843
|
-
if (!blockLimitCheck.allowed) {
|
|
1844
|
-
return `图片合成失败:内容被安全策略拦截。${blockLimitCheck.message}`;
|
|
1845
|
-
}
|
|
1846
|
-
} catch (recordError) {
|
|
1847
|
-
logger.error("记录安全拦截使用次数失败", { userId, error: recordError });
|
|
1848
|
-
}
|
|
1810
|
+
const errorMessage = error?.message || "";
|
|
1811
|
+
const isSecurityBlock = errorMessage.includes("内容被安全策略拦截") || errorMessage.includes("内容被安全策略阻止") || errorMessage.includes("内容被阻止") || errorMessage.includes("被阻止") || errorMessage.includes("SAFETY") || errorMessage.includes("RECITATION");
|
|
1812
|
+
if (isSecurityBlock) {
|
|
1813
|
+
await recordSecurityBlock(session, imageCount);
|
|
1849
1814
|
}
|
|
1850
1815
|
if (error?.message) {
|
|
1851
1816
|
const safeMessage = sanitizeString(error.message);
|
|
@@ -1857,11 +1822,19 @@ Prompt: ${prompt}`);
|
|
|
1857
1822
|
new Promise(
|
|
1858
1823
|
(_, reject) => setTimeout(() => reject(new Error("命令执行超时")), config.commandTimeout * 1e3)
|
|
1859
1824
|
)
|
|
1860
|
-
]).catch((error) => {
|
|
1825
|
+
]).catch(async (error) => {
|
|
1861
1826
|
const userId = session.userId;
|
|
1862
1827
|
if (userId) activeTasks.delete(userId);
|
|
1863
1828
|
const sanitizedError = sanitizeError(error);
|
|
1864
1829
|
logger.error("图片合成超时或失败", { userId, error: sanitizedError });
|
|
1830
|
+
if (error?.message !== "命令执行超时") {
|
|
1831
|
+
const errorMessage = error?.message || "";
|
|
1832
|
+
const isSecurityBlock = errorMessage.includes("内容被安全策略拦截") || errorMessage.includes("内容被安全策略阻止") || errorMessage.includes("内容被阻止") || errorMessage.includes("被阻止") || errorMessage.includes("SAFETY") || errorMessage.includes("RECITATION");
|
|
1833
|
+
if (isSecurityBlock) {
|
|
1834
|
+
const imageCount = options?.num || config.defaultNumImages;
|
|
1835
|
+
await recordSecurityBlock(session, imageCount);
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1865
1838
|
const safeMessage = typeof error?.message === "string" ? sanitizeString(error.message) : "未知错误";
|
|
1866
1839
|
return error.message === "命令执行超时" ? "图片合成超时,请重试" : `图片合成失败:${safeMessage}`;
|
|
1867
1840
|
});
|
|
@@ -1917,9 +1890,7 @@ Prompt: ${prompt}`);
|
|
|
1917
1890
|
donationCount: 0,
|
|
1918
1891
|
donationAmount: 0,
|
|
1919
1892
|
lastUsed: now,
|
|
1920
|
-
createdAt: now
|
|
1921
|
-
safetyBlockCount: 0,
|
|
1922
|
-
safetyBlockHistory: []
|
|
1893
|
+
createdAt: now
|
|
1923
1894
|
};
|
|
1924
1895
|
}
|
|
1925
1896
|
const beforeBalance = usersData[userId].remainingPurchasedCount;
|