koishi-plugin-smmcat-nai-diffusion 0.0.4 → 0.0.6
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 +3 -0
- package/lib/index.js +221 -6
- package/package.json +1 -1
package/lib/index.d.ts
CHANGED
|
@@ -7,6 +7,9 @@ export interface Config {
|
|
|
7
7
|
useGuildId: string[];
|
|
8
8
|
useGuildUser: string[];
|
|
9
9
|
negative_prompt: string;
|
|
10
|
+
gemimiBaseUrl: string;
|
|
11
|
+
gemimiToken: string;
|
|
12
|
+
gemimiType: string;
|
|
10
13
|
}
|
|
11
14
|
export declare const inject: string[];
|
|
12
15
|
export declare const Config: Schema<Config>;
|
package/lib/index.js
CHANGED
|
@@ -123,7 +123,10 @@ var Config = import_koishi.Schema.object({
|
|
|
123
123
|
negative_prompt: import_koishi.Schema.string().default("lowres, bad anatomy, bad hands, text, error, missing fingers, extra digit, fewer digits, cropped, worst quality, low quality, normal quality, jpeg artifacts, signature, watermark, username, blurry, ugly, deformed, poorly drawn hands, poorly drawn face, mutation, deformed, bad proportions, gross proportions, long neck, missing arms, missing legs, extra arms, extra legs, fused fingers, too many fingers, unclear eyes, bad body, out of focus, chromatic aberration").description("默认反向词条"),
|
|
124
124
|
token: import_koishi.Schema.string().default("").description("[tuercha](https://api.tuercha.com/register) 绘图站提供的令牌"),
|
|
125
125
|
useGuildId: import_koishi.Schema.array(String).role("table").default([]).description("指定可用群"),
|
|
126
|
-
useGuildUser: import_koishi.Schema.array(String).role("table").default([]).description("指定可用人(无视可用群规则)")
|
|
126
|
+
useGuildUser: import_koishi.Schema.array(String).role("table").default([]).description("指定可用人(无视可用群规则)"),
|
|
127
|
+
gemimiBaseUrl: import_koishi.Schema.string().default("https://api.futureppo.top").description("Gemimi 接口基地址(用于图生词,描述生词)"),
|
|
128
|
+
gemimiToken: import_koishi.Schema.string().default("").description("Gemimi 图生词提供的令牌(用于图生词,描述生词)"),
|
|
129
|
+
gemimiType: import_koishi.Schema.string().default("gemini-3-flash-preview").description("Gemimi 模型版本名(用于图生词,描述生词)")
|
|
127
130
|
});
|
|
128
131
|
function apply(ctx, config) {
|
|
129
132
|
ctx.on("ready", () => {
|
|
@@ -175,7 +178,7 @@ function apply(ctx, config) {
|
|
|
175
178
|
async getDrawByPicAndEntry(text, imgUrl, fn) {
|
|
176
179
|
const base64Img = await naiDiffusion.imageUrlToBase64(imgUrl);
|
|
177
180
|
if (base64Img.err) {
|
|
178
|
-
await fn(base64Img.err);
|
|
181
|
+
fn && await fn(base64Img.err);
|
|
179
182
|
return null;
|
|
180
183
|
}
|
|
181
184
|
const drawParams = {
|
|
@@ -221,18 +224,130 @@ function apply(ctx, config) {
|
|
|
221
224
|
}
|
|
222
225
|
return imgList;
|
|
223
226
|
} catch (error) {
|
|
227
|
+
fn && await fn("请求失败");
|
|
224
228
|
console.log(error.response);
|
|
225
229
|
return null;
|
|
226
230
|
}
|
|
227
231
|
},
|
|
232
|
+
/** 通图片和提示词生成词条 */
|
|
233
|
+
async getEntryByGemimiForPicAndText(imgUrl, morePrompt, fn) {
|
|
234
|
+
try {
|
|
235
|
+
const imgInfo = await naiDiffusion.imageUrlToBase64(imgUrl, true);
|
|
236
|
+
if (imgInfo.err) {
|
|
237
|
+
throw new Error(imgInfo.err);
|
|
238
|
+
}
|
|
239
|
+
const body = {
|
|
240
|
+
model: config.gemimiType,
|
|
241
|
+
messages: [
|
|
242
|
+
{
|
|
243
|
+
role: "user",
|
|
244
|
+
content: [
|
|
245
|
+
{
|
|
246
|
+
type: "text",
|
|
247
|
+
text: "我需要你通过图生成 nai-diffusion-4-5-full 的词条,只需要发送词条信息即可,并以逗号结尾"
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
type: "image_url",
|
|
251
|
+
image_url: {
|
|
252
|
+
url: imgInfo.base64
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
]
|
|
256
|
+
}
|
|
257
|
+
],
|
|
258
|
+
"temperature": 0.7,
|
|
259
|
+
"max_tokens": 1024
|
|
260
|
+
};
|
|
261
|
+
const res = await ctx.http.post(config.gemimiBaseUrl + "/v1/chat/completions", body, {
|
|
262
|
+
headers: {
|
|
263
|
+
Authorization: config.gemimiToken
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
if (!Array.isArray(res.choices)) {
|
|
267
|
+
fn && await fn("未得到任何提示词");
|
|
268
|
+
return null;
|
|
269
|
+
}
|
|
270
|
+
if (!res.choices[0]?.message?.content) {
|
|
271
|
+
fn && await fn("未得到任何提示词");
|
|
272
|
+
return null;
|
|
273
|
+
}
|
|
274
|
+
if (naiDiffusion.containsForbiddenChars(res.choices[0].message.content)) {
|
|
275
|
+
fn && await fn("Gemimi接口返回的数据词条有误");
|
|
276
|
+
return null;
|
|
277
|
+
}
|
|
278
|
+
const text = res.choices[0].message.content + (morePrompt ? morePrompt.split(",").map((item) => {
|
|
279
|
+
if (item.trim()) {
|
|
280
|
+
const tag = item.trim().replace(/ /g, "_");
|
|
281
|
+
return `(${tag}:1.5)`;
|
|
282
|
+
} else {
|
|
283
|
+
return null;
|
|
284
|
+
}
|
|
285
|
+
}).filter((i) => i).join(", ") : "");
|
|
286
|
+
config.deBug && console.log(text);
|
|
287
|
+
return text;
|
|
288
|
+
} catch (error) {
|
|
289
|
+
console.log(error.response?.data || error);
|
|
290
|
+
fn && await fn("请求失败,请等待一会再请求 gemimi");
|
|
291
|
+
return null;
|
|
292
|
+
}
|
|
293
|
+
},
|
|
294
|
+
/** 通图片和提示词生成词条 */
|
|
295
|
+
async getEntryByGemimiForText(prompt, fn) {
|
|
296
|
+
try {
|
|
297
|
+
const body = {
|
|
298
|
+
model: config.gemimiType,
|
|
299
|
+
messages: [
|
|
300
|
+
{
|
|
301
|
+
role: "user",
|
|
302
|
+
content: [
|
|
303
|
+
{
|
|
304
|
+
type: "text",
|
|
305
|
+
text: "我需要你通过我的描述生成 nai-diffusion-4-5-full 的对应词条,只需要发送词条信息即可,最终以逗号结尾"
|
|
306
|
+
},
|
|
307
|
+
{
|
|
308
|
+
type: "text",
|
|
309
|
+
text: prompt
|
|
310
|
+
}
|
|
311
|
+
]
|
|
312
|
+
}
|
|
313
|
+
],
|
|
314
|
+
"temperature": 0.7,
|
|
315
|
+
"max_tokens": 1024
|
|
316
|
+
};
|
|
317
|
+
const res = await ctx.http.post(config.gemimiBaseUrl + "/v1/chat/completions", body, {
|
|
318
|
+
headers: {
|
|
319
|
+
Authorization: config.gemimiToken
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
if (!Array.isArray(res.choices)) {
|
|
323
|
+
fn && await fn("未得到任何提示词");
|
|
324
|
+
return null;
|
|
325
|
+
}
|
|
326
|
+
if (!res.choices[0]?.message?.content) {
|
|
327
|
+
fn && await fn("未得到任何提示词");
|
|
328
|
+
return null;
|
|
329
|
+
}
|
|
330
|
+
if (naiDiffusion.containsForbiddenChars(res.choices[0].message.content)) {
|
|
331
|
+
fn && await fn("gemimi接口返回的数据词条有误");
|
|
332
|
+
return null;
|
|
333
|
+
}
|
|
334
|
+
const text = res.choices[0].message.content;
|
|
335
|
+
config.deBug && console.log(text);
|
|
336
|
+
return text;
|
|
337
|
+
} catch (error) {
|
|
338
|
+
console.log(error.response?.data || error);
|
|
339
|
+
fn && await fn("请求失败,请等待一会再请求 gemimi");
|
|
340
|
+
return null;
|
|
341
|
+
}
|
|
342
|
+
},
|
|
228
343
|
/** 图片网络地址转 base64 */
|
|
229
|
-
async imageUrlToBase64(url) {
|
|
344
|
+
async imageUrlToBase64(url, ignoreSize = false) {
|
|
230
345
|
if (!url) return { err: "请传入图片", base64: ``, size: [0, 0] };
|
|
231
346
|
const useImg = await ctx.canvas.loadImage(url);
|
|
232
347
|
const h2 = useImg.naturalHeight || useImg.height || 0;
|
|
233
348
|
const w = useImg.naturalWidth || useImg.width || 0;
|
|
234
|
-
console.log(h2, w);
|
|
235
|
-
if (!(h2 % 64 == 0 && w % 64 == 0)) {
|
|
349
|
+
config.deBug && console.log("图片宽高为:", h2, w);
|
|
350
|
+
if (!ignoreSize && !(h2 % 64 == 0 && w % 64 == 0)) {
|
|
236
351
|
return {
|
|
237
352
|
base64: ``,
|
|
238
353
|
size: [w, h2],
|
|
@@ -329,6 +444,102 @@ ${imgs.map((item) => {
|
|
|
329
444
|
use.clear(session);
|
|
330
445
|
}
|
|
331
446
|
});
|
|
447
|
+
if (config.gemimiToken) {
|
|
448
|
+
ctx.command("参考生图 <prompt:text>").action(async ({ session }, prompt) => {
|
|
449
|
+
const { userId, guildId } = session;
|
|
450
|
+
if (!config.useGuildUser.includes(userId)) {
|
|
451
|
+
if (!(guildId && config.useGuildId.includes(guildId))) {
|
|
452
|
+
return `该群暂时不提供该功能...`;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
const imgList = import_koishi.h.select(prompt, "img").map((i) => i.attrs.src);
|
|
456
|
+
const text = import_koishi.h.select(prompt, "text")[0]?.attrs.content || null;
|
|
457
|
+
if (!imgList.length) return "请传参考图!";
|
|
458
|
+
if (use.isUse(session)) return `正在进行上一个绘画,请等待...`;
|
|
459
|
+
if (prompt && naiDiffusion.containsForbiddenChars(prompt)) return `输入的词条包含问题,请检查!`;
|
|
460
|
+
try {
|
|
461
|
+
use.start(session);
|
|
462
|
+
await Chat.send(session, `<qqbot-at-user id="${userId}" /> 稍等,Gemimi酱 正通过图片信息和文本获取词条...`);
|
|
463
|
+
const prompt2 = await naiDiffusion.getEntryByGemimiForPicAndText(imgList[0], text, async (err) => {
|
|
464
|
+
await session.send(err);
|
|
465
|
+
});
|
|
466
|
+
if (!prompt2) return use.clear(session);
|
|
467
|
+
if (config.useMd) {
|
|
468
|
+
await Chat.send(session, `<qqbot-at-user id="${userId}" /> 获取词条成功,正在根据词条信息给你画...`);
|
|
469
|
+
} else {
|
|
470
|
+
await Chat.send(session, `获取词条成功,正在根据词条信息给你画...`);
|
|
471
|
+
}
|
|
472
|
+
const imgs = await naiDiffusion.getDrawByEntry(prompt2);
|
|
473
|
+
if (!(imgs && imgs.length)) {
|
|
474
|
+
await Chat.send(session, "生图失败...");
|
|
475
|
+
use.clear(session);
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
478
|
+
if (config.useMd) {
|
|
479
|
+
const md = `<qqbot-at-user id="${userId}" /> 画完了,请点击下方查看!
|
|
480
|
+
|
|
481
|
+
${imgs.map((item) => {
|
|
482
|
+
return `[查看图片](${item})`;
|
|
483
|
+
}).join("\n")}`;
|
|
484
|
+
await Chat.send(session, md);
|
|
485
|
+
} else {
|
|
486
|
+
await Chat.send(session, imgs.map((item) => {
|
|
487
|
+
return import_koishi.h.image(item);
|
|
488
|
+
}).join("\n"));
|
|
489
|
+
}
|
|
490
|
+
use.clear(session);
|
|
491
|
+
} catch (error) {
|
|
492
|
+
console.log(error);
|
|
493
|
+
use.clear(session);
|
|
494
|
+
}
|
|
495
|
+
});
|
|
496
|
+
ctx.command("描述生图 <prompt:text>").action(async ({ session }, prompt) => {
|
|
497
|
+
const { userId, guildId } = session;
|
|
498
|
+
if (!config.useGuildUser.includes(userId)) {
|
|
499
|
+
if (!(guildId && config.useGuildId.includes(guildId))) {
|
|
500
|
+
return `该群暂时不提供该功能...`;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
const text = import_koishi.h.select(prompt, "text")[0]?.attrs.content || null;
|
|
504
|
+
if (!text) return "请传描述!";
|
|
505
|
+
if (use.isUse(session)) return `正在进行上一个绘画,请等待...`;
|
|
506
|
+
try {
|
|
507
|
+
use.start(session);
|
|
508
|
+
await Chat.send(session, `<qqbot-at-user id="${userId}" /> 稍等,Gemimi酱 正通过描述去获取词条...`);
|
|
509
|
+
const prompt2 = await naiDiffusion.getEntryByGemimiForText(text, async (err) => {
|
|
510
|
+
await session.send(err);
|
|
511
|
+
});
|
|
512
|
+
if (!prompt2) return use.clear(session);
|
|
513
|
+
if (config.useMd) {
|
|
514
|
+
await Chat.send(session, `<qqbot-at-user id="${userId}" /> 获取词条成功,正在根据词条信息给你画...`);
|
|
515
|
+
} else {
|
|
516
|
+
await Chat.send(session, `获取词条成功,正在根据词条信息给你画...`);
|
|
517
|
+
}
|
|
518
|
+
const imgs = await naiDiffusion.getDrawByEntry(prompt2);
|
|
519
|
+
if (!(imgs && imgs.length)) {
|
|
520
|
+
await Chat.send(session, "生图失败...");
|
|
521
|
+
use.clear(session);
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
524
|
+
if (config.useMd) {
|
|
525
|
+
const md = `<qqbot-at-user id="${userId}" /> 画完了,请点击下方查看!
|
|
526
|
+
|
|
527
|
+
${imgs.map((item) => {
|
|
528
|
+
return `[查看图片](${item})`;
|
|
529
|
+
}).join("\n")}`;
|
|
530
|
+
await Chat.send(session, md);
|
|
531
|
+
} else {
|
|
532
|
+
await Chat.send(session, imgs.map((item) => {
|
|
533
|
+
return import_koishi.h.image(item);
|
|
534
|
+
}).join("\n"));
|
|
535
|
+
}
|
|
536
|
+
use.clear(session);
|
|
537
|
+
} catch (error) {
|
|
538
|
+
console.log(error);
|
|
539
|
+
use.clear(session);
|
|
540
|
+
}
|
|
541
|
+
});
|
|
542
|
+
}
|
|
332
543
|
ctx.command("转图 <prompt:text>").action(async ({ session }, prompt) => {
|
|
333
544
|
const { userId, guildId } = session;
|
|
334
545
|
if (!config.useGuildUser.includes(userId)) {
|
|
@@ -344,7 +555,11 @@ ${imgs.map((item) => {
|
|
|
344
555
|
if (use.isUse(session)) return `正在进行上一个绘画,请等待...`;
|
|
345
556
|
try {
|
|
346
557
|
use.start(session);
|
|
347
|
-
|
|
558
|
+
if (config.useMd) {
|
|
559
|
+
await Chat.send(session, `<qqbot-at-user id="${userId}" /> 稍等,Nai酱 正在给你画...`);
|
|
560
|
+
} else {
|
|
561
|
+
await Chat.send(session, `稍等,正在给你画...`);
|
|
562
|
+
}
|
|
348
563
|
const imgs = await naiDiffusion.getDrawByPicAndEntry(text, imgList[0], async (err) => {
|
|
349
564
|
await session.send(err);
|
|
350
565
|
});
|