koishi-plugin-aka-ai-generator 0.2.12 → 0.2.14

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.
Files changed (2) hide show
  1. package/lib/index.js +99 -48
  2. package/package.json +1 -1
package/lib/index.js CHANGED
@@ -599,7 +599,8 @@ var COMMANDS = {
599
599
  QUERY_QUOTA: "图像额度",
600
600
  RECHARGE: "图像充值",
601
601
  RECHARGE_HISTORY: "图像充值记录",
602
- FUNCTION_LIST: "图像功能"
602
+ FUNCTION_LIST: "图像功能",
603
+ IMAGE_COMMANDS: "图像指令"
603
604
  };
604
605
  var StyleItemSchema = import_koishi.Schema.object({
605
606
  commandName: import_koishi.Schema.string().required().description("命令名称(不含前缀斜杠)"),
@@ -883,7 +884,7 @@ function apply(ctx, config) {
883
884
  rateLimitMap.set(userId, userTimestamps);
884
885
  }
885
886
  __name(updateRateLimit, "updateRateLimit");
886
- async function checkDailyLimit(userId) {
887
+ async function checkDailyLimit(userId, numImages = 1) {
887
888
  if (isAdmin(userId)) {
888
889
  return { allowed: true, isAdmin: true };
889
890
  }
@@ -894,25 +895,33 @@ function apply(ctx, config) {
894
895
  const usersData = await loadUsersData();
895
896
  const userData = usersData[userId];
896
897
  if (!userData) {
898
+ if (numImages > config.dailyFreeLimit) {
899
+ return {
900
+ allowed: false,
901
+ message: `生成 ${numImages} 张图片需要 ${numImages} 次可用次数,但您的可用次数不足(今日免费:${config.dailyFreeLimit}次,充值:0次)`,
902
+ isAdmin: false
903
+ };
904
+ }
897
905
  return { allowed: true, isAdmin: false };
898
906
  }
899
907
  const today = (/* @__PURE__ */ new Date()).toDateString();
900
908
  const lastReset = new Date(userData.lastDailyReset || userData.createdAt).toDateString();
909
+ let dailyCount = userData.dailyUsageCount;
901
910
  if (today !== lastReset) {
911
+ dailyCount = 0;
902
912
  userData.dailyUsageCount = 0;
903
913
  userData.lastDailyReset = (/* @__PURE__ */ new Date()).toISOString();
904
914
  }
905
- if (userData.dailyUsageCount < config.dailyFreeLimit) {
906
- return { allowed: true, isAdmin: false };
907
- }
908
- if (userData.remainingPurchasedCount > 0) {
909
- return { allowed: true, isAdmin: false };
915
+ const remainingToday = Math.max(0, config.dailyFreeLimit - dailyCount);
916
+ const totalAvailable = remainingToday + userData.remainingPurchasedCount;
917
+ if (totalAvailable < numImages) {
918
+ return {
919
+ allowed: false,
920
+ message: `生成 ${numImages} 张图片需要 ${numImages} 次可用次数,但您的可用次数不足(今日免费剩余:${remainingToday}次,充值剩余:${userData.remainingPurchasedCount}次,共${totalAvailable}次)`,
921
+ isAdmin: false
922
+ };
910
923
  }
911
- return {
912
- allowed: false,
913
- message: `今日免费次数已用完(${config.dailyFreeLimit}次),充值次数也已用完。请联系管理员充值或明天再试`,
914
- isAdmin: false
915
- };
924
+ return { allowed: true, isAdmin: false };
916
925
  }
917
926
  __name(checkDailyLimit, "checkDailyLimit");
918
927
  async function getPromptInput(session, message) {
@@ -1002,7 +1011,7 @@ function apply(ctx, config) {
1002
1011
  return usersData[userId];
1003
1012
  }
1004
1013
  __name(getUserData, "getUserData");
1005
- async function updateUserData(userId, userName, commandName) {
1014
+ async function updateUserData(userId, userName, commandName, numImages = 1) {
1006
1015
  const usersData = await loadUsersData();
1007
1016
  const now = (/* @__PURE__ */ new Date()).toISOString();
1008
1017
  const today = (/* @__PURE__ */ new Date()).toDateString();
@@ -1010,8 +1019,8 @@ function apply(ctx, config) {
1010
1019
  usersData[userId] = {
1011
1020
  userId,
1012
1021
  userName: userId,
1013
- totalUsageCount: 1,
1014
- dailyUsageCount: 1,
1022
+ totalUsageCount: numImages,
1023
+ dailyUsageCount: numImages,
1015
1024
  lastDailyReset: now,
1016
1025
  purchasedCount: 0,
1017
1026
  remainingPurchasedCount: 0,
@@ -1021,35 +1030,49 @@ function apply(ctx, config) {
1021
1030
  createdAt: now
1022
1031
  };
1023
1032
  await saveUsersData(usersData);
1024
- return { userData: usersData[userId], consumptionType: "free" };
1033
+ return { userData: usersData[userId], consumptionType: "free", freeUsed: numImages, purchasedUsed: 0 };
1025
1034
  }
1026
- usersData[userId].totalUsageCount += 1;
1035
+ usersData[userId].totalUsageCount += numImages;
1027
1036
  usersData[userId].lastUsed = now;
1028
1037
  const lastReset = new Date(usersData[userId].lastDailyReset || usersData[userId].createdAt).toDateString();
1029
1038
  if (today !== lastReset) {
1030
1039
  usersData[userId].dailyUsageCount = 0;
1031
1040
  usersData[userId].lastDailyReset = now;
1032
1041
  }
1033
- if (usersData[userId].dailyUsageCount < config.dailyFreeLimit) {
1034
- usersData[userId].dailyUsageCount += 1;
1035
- await saveUsersData(usersData);
1036
- return { userData: usersData[userId], consumptionType: "free" };
1037
- }
1038
- if (usersData[userId].remainingPurchasedCount > 0) {
1039
- usersData[userId].remainingPurchasedCount -= 1;
1040
- await saveUsersData(usersData);
1041
- return { userData: usersData[userId], consumptionType: "purchased" };
1042
+ let remainingToConsume = numImages;
1043
+ let freeUsed = 0;
1044
+ let purchasedUsed = 0;
1045
+ const availableFree = Math.max(0, config.dailyFreeLimit - usersData[userId].dailyUsageCount);
1046
+ if (availableFree > 0) {
1047
+ const freeToUse = Math.min(availableFree, remainingToConsume);
1048
+ usersData[userId].dailyUsageCount += freeToUse;
1049
+ freeUsed = freeToUse;
1050
+ remainingToConsume -= freeToUse;
1051
+ }
1052
+ if (remainingToConsume > 0) {
1053
+ const purchasedToUse = Math.min(usersData[userId].remainingPurchasedCount, remainingToConsume);
1054
+ usersData[userId].remainingPurchasedCount -= purchasedToUse;
1055
+ purchasedUsed = purchasedToUse;
1056
+ remainingToConsume -= purchasedToUse;
1042
1057
  }
1043
1058
  await saveUsersData(usersData);
1044
- return { userData: usersData[userId], consumptionType: "free" };
1059
+ let consumptionType;
1060
+ if (freeUsed > 0 && purchasedUsed > 0) {
1061
+ consumptionType = "mixed";
1062
+ } else if (freeUsed > 0) {
1063
+ consumptionType = "free";
1064
+ } else {
1065
+ consumptionType = "purchased";
1066
+ }
1067
+ return { userData: usersData[userId], consumptionType, freeUsed, purchasedUsed };
1045
1068
  }
1046
1069
  __name(updateUserData, "updateUserData");
1047
- async function recordUserUsage(session, commandName) {
1070
+ async function recordUserUsage(session, commandName, numImages = 1) {
1048
1071
  const userId = session.userId;
1049
1072
  const userName = session.username || session.userId || "未知用户";
1050
1073
  if (!userId) return;
1051
1074
  updateRateLimit(userId);
1052
- const { userData, consumptionType } = await updateUserData(userId, userName, commandName);
1075
+ const { userData, consumptionType, freeUsed, purchasedUsed } = await updateUserData(userId, userName, commandName, numImages);
1053
1076
  if (isAdmin(userId)) {
1054
1077
  await session.send(`📊 使用统计 [管理员]
1055
1078
  用户:${userData.userName}
@@ -1057,10 +1080,18 @@ function apply(ctx, config) {
1057
1080
  状态:无限制使用`);
1058
1081
  } else {
1059
1082
  const remainingToday = Math.max(0, config.dailyFreeLimit - userData.dailyUsageCount);
1060
- const consumptionText = consumptionType === "free" ? "每日免费次数" : "充值次数";
1083
+ let consumptionText = "";
1084
+ if (consumptionType === "mixed") {
1085
+ consumptionText = `每日免费次数 -${freeUsed},充值次数 -${purchasedUsed}`;
1086
+ } else if (consumptionType === "free") {
1087
+ consumptionText = `每日免费次数 -${freeUsed}`;
1088
+ } else {
1089
+ consumptionText = `充值次数 -${purchasedUsed}`;
1090
+ }
1061
1091
  await session.send(`📊 使用统计
1062
1092
  用户:${userData.userName}
1063
- 本次消费:${consumptionText} -1
1093
+ 本次生成:${numImages}张图片
1094
+ 本次消费:${consumptionText}
1064
1095
  总调用次数:${userData.totalUsageCount}次
1065
1096
  今日剩余免费:${remainingToday}次
1066
1097
  充值剩余:${userData.remainingPurchasedCount}次`);
@@ -1069,10 +1100,13 @@ function apply(ctx, config) {
1069
1100
  userId,
1070
1101
  userName: userData.userName,
1071
1102
  commandName,
1103
+ numImages,
1104
+ consumptionType,
1105
+ freeUsed,
1106
+ purchasedUsed,
1072
1107
  totalUsageCount: userData.totalUsageCount,
1073
1108
  dailyUsageCount: userData.dailyUsageCount,
1074
1109
  remainingPurchasedCount: userData.remainingPurchasedCount,
1075
- consumptionType,
1076
1110
  isAdmin: isAdmin(userId)
1077
1111
  });
1078
1112
  }
@@ -1207,7 +1241,7 @@ ${infoParts.join("\n")}`;
1207
1241
  await new Promise((resolve) => setTimeout(resolve, 1e3));
1208
1242
  }
1209
1243
  }
1210
- await recordUserUsage(session, styleName);
1244
+ await recordUserUsage(session, styleName, images.length);
1211
1245
  activeTasks.delete(userId);
1212
1246
  } catch (error) {
1213
1247
  activeTasks.delete(userId);
@@ -1225,10 +1259,6 @@ ${infoParts.join("\n")}`;
1225
1259
  ctx.command(`${style.commandName} [img:text]`, "图像风格转换").option("num", "-n <num:number> 生成图片数量 (1-4)").action(async (argv, img) => {
1226
1260
  const { session, options } = argv;
1227
1261
  if (!session?.userId) return "会话无效";
1228
- const limitCheck = await checkDailyLimit(session.userId);
1229
- if (!limitCheck.allowed) {
1230
- return limitCheck.message;
1231
- }
1232
1262
  const modifiers = parseStyleCommandModifiers(argv, img);
1233
1263
  let userPromptParts = [];
1234
1264
  if (modifiers.customAdditions?.length) {
@@ -1250,13 +1280,18 @@ ${infoParts.join("\n")}`;
1250
1280
  }
1251
1281
  }
1252
1282
  }
1283
+ const numImages = options?.num || promptNumImages || config.defaultNumImages;
1284
+ const limitCheck = await checkDailyLimit(session.userId, numImages);
1285
+ if (!limitCheck.allowed) {
1286
+ return limitCheck.message;
1287
+ }
1253
1288
  const promptSegments = [style.prompt];
1254
1289
  if (cleanedUserPrompt) {
1255
1290
  promptSegments.push(cleanedUserPrompt);
1256
1291
  }
1257
1292
  const mergedPrompt = promptSegments.filter(Boolean).join(" - ");
1258
1293
  const requestContext = {
1259
- numImages: options?.num || promptNumImages
1294
+ numImages
1260
1295
  };
1261
1296
  if (modifiers.modelMapping?.provider) {
1262
1297
  requestContext.provider = modifiers.modelMapping.provider;
@@ -1280,10 +1315,6 @@ ${infoParts.join("\n")}`;
1280
1315
  }
1281
1316
  ctx.command(COMMANDS.GENERATE_IMAGE, "使用自定义prompt进行图像处理").option("num", "-n <num:number> 生成图片数量 (1-4)").action(async ({ session, options }) => {
1282
1317
  if (!session?.userId) return "会话无效";
1283
- const limitCheck = await checkDailyLimit(session.userId);
1284
- if (!limitCheck.allowed) {
1285
- return limitCheck.message;
1286
- }
1287
1318
  return Promise.race([
1288
1319
  (async () => {
1289
1320
  const userId = session.userId;
@@ -1350,6 +1381,10 @@ ${infoParts.join("\n")}`;
1350
1381
  if (imageCount < 1 || imageCount > 4) {
1351
1382
  return "生成数量必须在 1-4 之间";
1352
1383
  }
1384
+ const limitCheck = await checkDailyLimit(userId, imageCount);
1385
+ if (!limitCheck.allowed) {
1386
+ return limitCheck.message;
1387
+ }
1353
1388
  logger.info("开始自定义图像处理", {
1354
1389
  userId,
1355
1390
  imageUrl,
@@ -1372,7 +1407,7 @@ Prompt: ${prompt}`);
1372
1407
  await new Promise((resolve) => setTimeout(resolve, 1e3));
1373
1408
  }
1374
1409
  }
1375
- await recordUserUsage(session, COMMANDS.GENERATE_IMAGE);
1410
+ await recordUserUsage(session, COMMANDS.GENERATE_IMAGE, resultImages.length);
1376
1411
  activeTasks.delete(userId);
1377
1412
  } catch (error) {
1378
1413
  activeTasks.delete(userId);
@@ -1395,10 +1430,6 @@ Prompt: ${prompt}`);
1395
1430
  });
1396
1431
  ctx.command(COMMANDS.COMPOSE_IMAGE, "合成多张图片,使用自定义prompt控制合成效果").option("num", "-n <num:number> 生成图片数量 (1-4)").action(async ({ session, options }) => {
1397
1432
  if (!session?.userId) return "会话无效";
1398
- const limitCheck = await checkDailyLimit(session.userId);
1399
- if (!limitCheck.allowed) {
1400
- return limitCheck.message;
1401
- }
1402
1433
  return Promise.race([
1403
1434
  (async () => {
1404
1435
  const userId = session.userId;
@@ -1455,6 +1486,10 @@ Prompt: ${prompt}`);
1455
1486
  if (imageCount < 1 || imageCount > 4) {
1456
1487
  return "生成数量必须在 1-4 之间";
1457
1488
  }
1489
+ const limitCheck = await checkDailyLimit(userId, imageCount);
1490
+ if (!limitCheck.allowed) {
1491
+ return limitCheck.message;
1492
+ }
1458
1493
  logger.info("开始图片合成处理", {
1459
1494
  userId,
1460
1495
  imageUrls: collectedImages,
@@ -1478,7 +1513,7 @@ Prompt: ${prompt}`);
1478
1513
  await new Promise((resolve) => setTimeout(resolve, 1e3));
1479
1514
  }
1480
1515
  }
1481
- await recordUserUsage(session, COMMANDS.COMPOSE_IMAGE);
1516
+ await recordUserUsage(session, COMMANDS.COMPOSE_IMAGE, resultImages.length);
1482
1517
  activeTasks.delete(userId);
1483
1518
  } catch (error) {
1484
1519
  activeTasks.delete(userId);
@@ -1705,6 +1740,22 @@ Prompt: ${prompt}`);
1705
1740
  return "获取功能列表失败,请稍后重试";
1706
1741
  }
1707
1742
  });
1743
+ ctx.command(COMMANDS.IMAGE_COMMANDS, "查看图像生成指令列表").action(async ({ session }) => {
1744
+ if (!session?.userId) return "会话无效";
1745
+ const globalConfig = ctx.root.config;
1746
+ const prefixConfig = globalConfig.prefix;
1747
+ let prefix = "";
1748
+ if (Array.isArray(prefixConfig) && prefixConfig.length > 0) {
1749
+ prefix = prefixConfig[0];
1750
+ } else if (typeof prefixConfig === "string") {
1751
+ prefix = prefixConfig;
1752
+ }
1753
+ const lines = ["🎨 图像生成指令列表:\n"];
1754
+ commandRegistry.userCommands.forEach((cmd) => {
1755
+ lines.push(`${prefix}${cmd.name} - ${cmd.description}`);
1756
+ });
1757
+ return lines.join("\n");
1758
+ });
1708
1759
  const providerLabel = config.provider === "gptgod" ? "GPTGod" : "云雾 Gemini 2.5 Flash Image";
1709
1760
  logger.info(`aka-ai-generator 插件已启动 (${providerLabel})`);
1710
1761
  }
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.2.12",
4
+ "version": "0.2.14",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [