koishi-plugin-aka-ai-generator 0.2.13 → 0.2.16

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 CHANGED
@@ -9,6 +9,7 @@ export interface ModelMappingConfig {
9
9
  export interface StyleConfig {
10
10
  commandName: string;
11
11
  prompt: string;
12
+ mode?: 'single' | 'multiple';
12
13
  }
13
14
  export interface StyleGroupConfig {
14
15
  prompts: StyleConfig[];
package/lib/index.js CHANGED
@@ -599,11 +599,16 @@ 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("命令名称(不含前缀斜杠)"),
606
- prompt: import_koishi.Schema.string().role("textarea", { rows: 4 }).required().description("生成 prompt")
607
+ prompt: import_koishi.Schema.string().role("textarea", { rows: 4 }).required().description("生成 prompt"),
608
+ mode: import_koishi.Schema.union([
609
+ import_koishi.Schema.const("single").description("单图模式"),
610
+ import_koishi.Schema.const("multiple").description("多图模式")
611
+ ]).default("single").description("图片输入模式")
607
612
  });
608
613
  var Config = import_koishi.Schema.intersect([
609
614
  import_koishi.Schema.object({
@@ -645,11 +650,13 @@ var Config = import_koishi.Schema.intersect([
645
650
  styles: import_koishi.Schema.array(StyleItemSchema).role("table").default([
646
651
  {
647
652
  commandName: "变手办",
648
- prompt: "将这张照片变成手办模型。在它后面放置一个印有图像主体的盒子,桌子上有一台电脑显示Blender建模过程。在盒子前面添加一个圆形塑料底座,角色手办站在上面。如果可能的话,将场景设置在室内"
653
+ prompt: "将这张照片变成手办模型。在它后面放置一个印有图像主体的盒子,桌子上有一台电脑显示Blender建模过程。在盒子前面添加一个圆形塑料底座,角色手办站在上面。如果可能的话,将场景设置在室内",
654
+ mode: "single"
649
655
  },
650
656
  {
651
657
  commandName: "变写实",
652
- prompt: "请根据用户提供的图片,在严格保持主体身份、外观特征与姿态不变的前提下,生成一张照片级真实感的超写实摄影作品。要求:1. 采用专业相机拍摄(如佳能EOS R5),使用85mm f/1.4人像镜头,呈现柯达Portra 400胶片质感,8K超高清画质,HDR高动态范围,电影级打光效果;2. 画面应具有照片级真实感、超现实主义风格和高细节表现,确保光影、皮肤质感、服饰纹理与背景环境都贴近真实世界;3. 使用自然光影营造真实氛围,呈现raw and natural的原始自然感,具有authentic film snapshot的真实胶片质感;4. 整体需具备tactile feel触感质感和simulated texture模拟纹理细节,可以适度优化噪点与瑕疵,但不要改变主体特征或添加额外元素;5. 整体效果需像专业摄影棚拍摄的真实照片,具有电影级画质;6. 如果主体是人物脸部,脸部生成效果应参考欧美混血白人精致美丽帅气英俊的外观特征进行生成,保持精致立体的五官轮廓、健康光泽的肌肤质感、优雅的气质和自然的表情,确保面部特征协调美观。"
658
+ prompt: "请根据用户提供的图片,在严格保持主体身份、外观特征与姿态不变的前提下,生成一张照片级真实感的超写实摄影作品。要求:1. 采用专业相机拍摄(如佳能EOS R5),使用85mm f/1.4人像镜头,呈现柯达Portra 400胶片质感,8K超高清画质,HDR高动态范围,电影级打光效果;2. 画面应具有照片级真实感、超现实主义风格和高细节表现,确保光影、皮肤质感、服饰纹理与背景环境都贴近真实世界;3. 使用自然光影营造真实氛围,呈现raw and natural的原始自然感,具有authentic film snapshot的真实胶片质感;4. 整体需具备tactile feel触感质感和simulated texture模拟纹理细节,可以适度优化噪点与瑕疵,但不要改变主体特征或添加额外元素;5. 整体效果需像专业摄影棚拍摄的真实照片,具有电影级画质;6. 如果主体是人物脸部,脸部生成效果应参考欧美混血白人精致美丽帅气英俊的外观特征进行生成,保持精致立体的五官轮廓、健康光泽的肌肤质感、优雅的气质和自然的表情,确保面部特征协调美观。",
659
+ mode: "single"
653
660
  }
654
661
  ]).description("自定义风格命令配置")
655
662
  }),
@@ -1110,55 +1117,72 @@ function apply(ctx, config) {
1110
1117
  });
1111
1118
  }
1112
1119
  __name(recordUserUsage, "recordUserUsage");
1113
- async function getImageUrl(img, session) {
1114
- let url = null;
1115
- if (img) {
1116
- url = img.attrs?.src || null;
1117
- if (url) {
1118
- if (config.logLevel === "debug") {
1119
- logger.debug("从命令参数获取图片", { url });
1120
+ async function getInputData(session, imgParam, mode) {
1121
+ const collectedImages = [];
1122
+ let collectedText = "";
1123
+ if (imgParam) {
1124
+ if (typeof imgParam === "object" && imgParam.attrs?.src) {
1125
+ collectedImages.push(imgParam.attrs.src);
1126
+ } else if (typeof imgParam === "string") {
1127
+ if (imgParam.startsWith("http") || imgParam.startsWith("data:")) {
1128
+ collectedImages.push(imgParam);
1120
1129
  }
1121
- return url;
1122
1130
  }
1123
1131
  }
1124
- let elements = session.quote?.elements;
1125
- if (elements) {
1126
- const images2 = import_koishi.h.select(elements, "img");
1127
- if (images2.length > 0) {
1128
- if (images2.length > 1) {
1129
- await session.send('本功能仅支持处理一张图片,检测到多张图片。如需合成多张图片请使用"合成图像"命令');
1130
- return null;
1132
+ if (session.quote?.elements) {
1133
+ const quoteImages = import_koishi.h.select(session.quote.elements, "img");
1134
+ for (const img of quoteImages) {
1135
+ if (img.attrs.src) collectedImages.push(img.attrs.src);
1136
+ }
1137
+ }
1138
+ if (collectedImages.length > 0) {
1139
+ if (mode === "single") {
1140
+ if (collectedImages.length > 1) {
1141
+ return { error: '本功能仅支持处理一张图片,检测到多张图片。如需合成多张图片请使用"合成图像"命令' };
1142
+ }
1143
+ return { images: collectedImages };
1144
+ }
1145
+ return { images: collectedImages };
1146
+ }
1147
+ const promptMsg = mode === "single" ? "请在30秒内发送一张图片" : "请发送图片(发送纯文字结束,至少需要2张)";
1148
+ await session.send(promptMsg);
1149
+ while (true) {
1150
+ const msg = await session.prompt(mode === "multiple" ? 6e4 : 3e4);
1151
+ if (!msg) return { error: "等待超时" };
1152
+ const elements = import_koishi.h.parse(msg);
1153
+ const images = import_koishi.h.select(elements, "img");
1154
+ const textElements = import_koishi.h.select(elements, "text");
1155
+ const text = textElements.map((el) => el.attrs.content).join(" ").trim();
1156
+ if (images.length > 0) {
1157
+ for (const img of images) {
1158
+ collectedImages.push(img.attrs.src);
1159
+ }
1160
+ if (mode === "single") {
1161
+ if (collectedImages.length > 1) {
1162
+ return { error: "本功能仅支持处理一张图片,检测到多张图片" };
1163
+ }
1164
+ if (text) collectedText = text;
1165
+ break;
1131
1166
  }
1132
- url = images2[0].attrs.src;
1133
- if (config.logLevel === "debug") {
1134
- logger.debug("从引用消息获取图片", { url });
1167
+ if (text) {
1168
+ collectedText = text;
1169
+ break;
1135
1170
  }
1136
- return url;
1171
+ await session.send(`已收到 ${collectedImages.length} 张图片,继续发送或输入文字结束`);
1172
+ continue;
1173
+ }
1174
+ if (text) {
1175
+ if (collectedImages.length === 0) {
1176
+ await session.send("未检测到图片,请先发送图片");
1177
+ continue;
1178
+ }
1179
+ collectedText = text;
1180
+ break;
1137
1181
  }
1138
1182
  }
1139
- await session.send("请在30秒内发送一张图片");
1140
- const msg = await session.prompt(3e4);
1141
- if (!msg) {
1142
- await session.send("等待超时");
1143
- return null;
1144
- }
1145
- elements = import_koishi.h.parse(msg);
1146
- const images = import_koishi.h.select(elements, "img");
1147
- if (images.length === 0) {
1148
- await session.send("未检测到图片,请重试");
1149
- return null;
1150
- }
1151
- if (images.length > 1) {
1152
- await session.send('本功能仅支持处理一张图片,检测到多张图片。如需合成多张图片请使用"合成图像"命令');
1153
- return null;
1154
- }
1155
- url = images[0].attrs.src;
1156
- if (config.logLevel === "debug") {
1157
- logger.debug("从用户输入获取图片", { url });
1158
- }
1159
- return url;
1183
+ return { images: collectedImages, text: collectedText };
1160
1184
  }
1161
- __name(getImageUrl, "getImageUrl");
1185
+ __name(getInputData, "getInputData");
1162
1186
  async function requestProviderImages(prompt, imageUrls, numImages, requestContext) {
1163
1187
  const providerType = requestContext?.provider || config.provider;
1164
1188
  const targetModelId = requestContext?.modelId;
@@ -1173,9 +1197,9 @@ function apply(ctx, config) {
1173
1197
  return await providerInstance.generateImages(prompt, imageUrls, numImages);
1174
1198
  }
1175
1199
  __name(requestProviderImages, "requestProviderImages");
1176
- async function processImageWithTimeout(session, img, prompt, styleName, requestContext, displayInfo) {
1200
+ async function processImageWithTimeout(session, img, prompt, styleName, requestContext, displayInfo, mode = "single") {
1177
1201
  return Promise.race([
1178
- processImage(session, img, prompt, styleName, requestContext, displayInfo),
1202
+ processImage(session, img, prompt, styleName, requestContext, displayInfo, mode),
1179
1203
  new Promise(
1180
1204
  (_, reject) => setTimeout(() => reject(new Error("命令执行超时")), config.commandTimeout * 1e3)
1181
1205
  )
@@ -1183,11 +1207,11 @@ function apply(ctx, config) {
1183
1207
  const userId = session.userId;
1184
1208
  if (userId) activeTasks.delete(userId);
1185
1209
  logger.error("图像处理超时或失败", { userId, error });
1186
- return error.message === "命令执行超时" ? "图像处理超时,请重试" : "图像处理失败,请稍后重试";
1210
+ return error.message === "命令执行超时" ? "图像处理超时,请重试" : `图像处理失败:${error.message}`;
1187
1211
  });
1188
1212
  }
1189
1213
  __name(processImageWithTimeout, "processImageWithTimeout");
1190
- async function processImage(session, img, prompt, styleName, requestContext, displayInfo) {
1214
+ async function processImage(session, img, prompt, styleName, requestContext, displayInfo, mode = "single") {
1191
1215
  const userId = session.userId;
1192
1216
  if (activeTasks.has(userId)) {
1193
1217
  return "您有一个图像处理任务正在进行中,请等待完成";
@@ -1196,17 +1220,22 @@ function apply(ctx, config) {
1196
1220
  if (imageCount < 1 || imageCount > 4) {
1197
1221
  return "生成数量必须在 1-4 之间";
1198
1222
  }
1199
- const imageUrl = await getImageUrl(img, session);
1200
- if (!imageUrl) {
1201
- return;
1223
+ const inputResult = await getInputData(session, img, mode);
1224
+ if ("error" in inputResult) {
1225
+ return inputResult.error;
1226
+ }
1227
+ const { images: imageUrls, text: extraText } = inputResult;
1228
+ let finalPrompt = prompt;
1229
+ if (extraText) {
1230
+ finalPrompt += " " + extraText;
1202
1231
  }
1203
1232
  const providerType = requestContext?.provider || config.provider;
1204
1233
  const providerModelId = requestContext?.modelId || (providerType === "yunwu" ? config.yunwuModelId : config.gptgodModelId);
1205
1234
  logger.info("开始图像处理", {
1206
1235
  userId,
1207
- imageUrl,
1236
+ imageUrls,
1208
1237
  styleName,
1209
- prompt,
1238
+ prompt: finalPrompt,
1210
1239
  numImages: imageCount,
1211
1240
  provider: providerType,
1212
1241
  modelId: providerModelId
@@ -1228,7 +1257,7 @@ ${infoParts.join("\n")}`;
1228
1257
  await session.send(statusMessage);
1229
1258
  try {
1230
1259
  activeTasks.set(userId, "processing");
1231
- const images = await requestProviderImages(prompt, imageUrl, imageCount, requestContext);
1260
+ const images = await requestProviderImages(finalPrompt, imageUrls, imageCount, requestContext);
1232
1261
  if (images.length === 0) {
1233
1262
  activeTasks.delete(userId);
1234
1263
  return "图像处理失败:未能生成图片";
@@ -1245,8 +1274,8 @@ ${infoParts.join("\n")}`;
1245
1274
  } catch (error) {
1246
1275
  activeTasks.delete(userId);
1247
1276
  logger.error("图像处理失败", { userId, error });
1248
- if (error?.message && (error.message.includes("内容被安全策略拦截") || error.message.includes("生成失败") || error.message.includes("处理失败"))) {
1249
- return error.message;
1277
+ if (error?.message) {
1278
+ return `图像处理失败:${error.message}`;
1250
1279
  }
1251
1280
  return "图像处理失败,请稍后重试";
1252
1281
  }
@@ -1306,7 +1335,8 @@ ${infoParts.join("\n")}`;
1306
1335
  displayInfo.modelId = modifiers.modelMapping.modelId;
1307
1336
  displayInfo.modelDescription = modifiers.modelMapping.suffix || modifiers.modelMapping.modelId;
1308
1337
  }
1309
- return processImageWithTimeout(session, img, mergedPrompt, style.commandName, requestContext, displayInfo);
1338
+ const mode = style.mode || "single";
1339
+ return processImageWithTimeout(session, img, mergedPrompt, style.commandName, requestContext, displayInfo, mode);
1310
1340
  });
1311
1341
  logger.info(`已注册命令: ${style.commandName}`);
1312
1342
  }
@@ -1411,8 +1441,8 @@ Prompt: ${prompt}`);
1411
1441
  } catch (error) {
1412
1442
  activeTasks.delete(userId);
1413
1443
  logger.error("自定义图像处理失败", { userId, error });
1414
- if (error?.message && (error.message.includes("内容被安全策略拦截") || error.message.includes("生成失败") || error.message.includes("处理失败"))) {
1415
- return error.message;
1444
+ if (error?.message) {
1445
+ return `图像处理失败:${error.message}`;
1416
1446
  }
1417
1447
  return "图像处理失败,请稍后重试";
1418
1448
  }
@@ -1424,7 +1454,7 @@ Prompt: ${prompt}`);
1424
1454
  const userId = session.userId;
1425
1455
  if (userId) activeTasks.delete(userId);
1426
1456
  logger.error("自定义图像处理超时或失败", { userId, error });
1427
- return error.message === "命令执行超时" ? "图像处理超时,请重试" : "图像处理失败,请稍后重试";
1457
+ return error.message === "命令执行超时" ? "图像处理超时,请重试" : `图像处理失败:${error.message}`;
1428
1458
  });
1429
1459
  });
1430
1460
  ctx.command(COMMANDS.COMPOSE_IMAGE, "合成多张图片,使用自定义prompt控制合成效果").option("num", "-n <num:number> 生成图片数量 (1-4)").action(async ({ session, options }) => {
@@ -1517,8 +1547,8 @@ Prompt: ${prompt}`);
1517
1547
  } catch (error) {
1518
1548
  activeTasks.delete(userId);
1519
1549
  logger.error("图片合成失败", { userId, error });
1520
- if (error?.message && (error.message.includes("内容被安全策略拦截") || error.message.includes("生成失败") || error.message.includes("处理失败"))) {
1521
- return error.message;
1550
+ if (error?.message) {
1551
+ return `图片合成失败:${error.message}`;
1522
1552
  }
1523
1553
  return "图片合成失败,请稍后重试";
1524
1554
  }
@@ -1530,7 +1560,7 @@ Prompt: ${prompt}`);
1530
1560
  const userId = session.userId;
1531
1561
  if (userId) activeTasks.delete(userId);
1532
1562
  logger.error("图片合成超时或失败", { userId, error });
1533
- return error.message === "命令执行超时" ? "图片合成超时,请重试" : "图片合成失败,请稍后重试";
1563
+ return error.message === "命令执行超时" ? "图片合成超时,请重试" : `图片合成失败:${error.message}`;
1534
1564
  });
1535
1565
  });
1536
1566
  ctx.command(`${COMMANDS.RECHARGE} [content:text]`, "为用户充值次数(仅管理员)").action(async ({ session }, content) => {
@@ -1739,6 +1769,22 @@ Prompt: ${prompt}`);
1739
1769
  return "获取功能列表失败,请稍后重试";
1740
1770
  }
1741
1771
  });
1772
+ ctx.command(COMMANDS.IMAGE_COMMANDS, "查看图像生成指令列表").action(async ({ session }) => {
1773
+ if (!session?.userId) return "会话无效";
1774
+ const globalConfig = ctx.root.config;
1775
+ const prefixConfig = globalConfig.prefix;
1776
+ let prefix = "";
1777
+ if (Array.isArray(prefixConfig) && prefixConfig.length > 0) {
1778
+ prefix = prefixConfig[0];
1779
+ } else if (typeof prefixConfig === "string") {
1780
+ prefix = prefixConfig;
1781
+ }
1782
+ const lines = ["🎨 图像生成指令列表:\n"];
1783
+ commandRegistry.userCommands.forEach((cmd) => {
1784
+ lines.push(`${prefix}${cmd.name} - ${cmd.description}`);
1785
+ });
1786
+ return lines.join("\n");
1787
+ });
1742
1788
  const providerLabel = config.provider === "gptgod" ? "GPTGod" : "云雾 Gemini 2.5 Flash Image";
1743
1789
  logger.info(`aka-ai-generator 插件已启动 (${providerLabel})`);
1744
1790
  }
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.13",
4
+ "version": "0.2.16",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [