alemonjs-aichat 1.0.13 → 1.0.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.
- package/README.md +68 -12
- package/lib/api.js +85 -37
- package/lib/assets/main.css-CXQ9gyIo.css +1 -0
- package/lib/assets/main.css.js +1 -1
- package/lib/config.js +62 -26
- package/lib/data/help.json.js +31 -3
- package/lib/image/conponent/help.js +3 -1
- package/lib/middleware/mw.js +56 -2
- package/lib/response/affection/res.js +2 -2
- package/lib/response/setting/res.js +22 -6
- package/lib/response/tools/res.js +110 -0
- package/lib/response/zreply/res.js +170 -121
- package/lib/s3.js +229 -49
- package/package.json +8 -4
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { availableTools } from '../../api.js';
|
|
2
|
+
import { getConfigValue, useMessage, Text, ImageURL, Image } from 'alemonjs';
|
|
3
|
+
import { Regular } from 'alemonjs/utils';
|
|
4
|
+
import OpenAi from 'openai';
|
|
5
|
+
import { getAIConfig } from '../../config.js';
|
|
6
|
+
|
|
7
|
+
const selects = onSelects(["message.create", "private.message.create"]);
|
|
8
|
+
const regular$1 = /(\/|#)(ai)?(pt|p图|ps|修图)(.*)$/i;
|
|
9
|
+
const regular$2 = /(\/|#)(ai)?(画图)(.*)$/i;
|
|
10
|
+
const regular = Regular.or(regular$1, regular$2);
|
|
11
|
+
var res = onResponse(selects, async (e) => {
|
|
12
|
+
getConfigValue();
|
|
13
|
+
// 创建
|
|
14
|
+
const [message] = useMessage(e);
|
|
15
|
+
if (regular$1.test(e.msg)) {
|
|
16
|
+
const match = e.msg.match(regular$1);
|
|
17
|
+
const [, , , , content] = match;
|
|
18
|
+
if (!content.trim()) {
|
|
19
|
+
message.send(format(Text("请提供修图内容,例如:\n#修图 换个背景")));
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const imgs = [];
|
|
23
|
+
if (e.reply) {
|
|
24
|
+
const replyImages = e.reply.message
|
|
25
|
+
.filter((item) => item.type === "image")
|
|
26
|
+
.map((item) => item.data.url);
|
|
27
|
+
imgs.push(...replyImages);
|
|
28
|
+
}
|
|
29
|
+
// 处理图片
|
|
30
|
+
if (e.img && e.img.length > 0) {
|
|
31
|
+
console.log("有图片");
|
|
32
|
+
for (const imgUrl of e.img) {
|
|
33
|
+
imgs.push(imgUrl);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
console.log("imgs", imgs);
|
|
37
|
+
message.send(format(Text("开始修图喵~")));
|
|
38
|
+
const res = await availableTools.AIPS({
|
|
39
|
+
image_url: imgs,
|
|
40
|
+
instruction: content,
|
|
41
|
+
});
|
|
42
|
+
if (res && typeof res === "object" && res.length > 0) {
|
|
43
|
+
message.send(format(Text("修图完成喵~"), ImageURL(res[0].url)));
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
try {
|
|
47
|
+
const json = JSON.parse(res);
|
|
48
|
+
console.log("res", json);
|
|
49
|
+
message.send(format(Text(json.error)));
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
message.send(format(Text("图片过于色情被和谐了喵~")));
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (regular$2.test(e.msg)) {
|
|
57
|
+
const match = e.msg.match(regular$2);
|
|
58
|
+
const [, , , , content] = match;
|
|
59
|
+
if (!content.trim()) {
|
|
60
|
+
message.send(format(Text("请提供画图内容,例如:\n#画图 画一只猫")));
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
message.send(format(Text("开始画图喵~")));
|
|
64
|
+
const aiConfig = await getAIConfig(e.guid);
|
|
65
|
+
if (!aiConfig) {
|
|
66
|
+
const res = await availableTools.StableDiffusionGenerateImage({
|
|
67
|
+
prompt: content,
|
|
68
|
+
base64: true,
|
|
69
|
+
});
|
|
70
|
+
if (res && typeof res === "object" && res.length > 0) {
|
|
71
|
+
message.send(format(Text("画图完成喵~"), Image(res[0])));
|
|
72
|
+
}
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const openai = new OpenAi({
|
|
76
|
+
baseURL: aiConfig.host,
|
|
77
|
+
apiKey: aiConfig.key,
|
|
78
|
+
timeout: 300000,
|
|
79
|
+
});
|
|
80
|
+
try {
|
|
81
|
+
const response = await openai.chat.completions.create({
|
|
82
|
+
model: aiConfig.model,
|
|
83
|
+
messages: [
|
|
84
|
+
{
|
|
85
|
+
role: "system",
|
|
86
|
+
content: "你是一个sd画图助手,协助用户将文字描述转换为提示词。描述中会出现一些动漫或游戏角色的名字, 请使用他们的英文名作为提示词, 请直接输出翻译后的提示词",
|
|
87
|
+
},
|
|
88
|
+
{ role: "user", content: content },
|
|
89
|
+
],
|
|
90
|
+
});
|
|
91
|
+
const prompt = response.choices[0].message?.content?.trim();
|
|
92
|
+
console.log("prompt", prompt);
|
|
93
|
+
if (prompt) {
|
|
94
|
+
const res = await availableTools.StableDiffusionGenerateImage({
|
|
95
|
+
prompt,
|
|
96
|
+
base64: true,
|
|
97
|
+
});
|
|
98
|
+
if (res && typeof res === "object" && res.length > 0) {
|
|
99
|
+
message.send(format(Text("画图完成喵~"), Image(res[0])));
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
console.error("Error generating image:", error);
|
|
105
|
+
message.send(format(Text("画图失败了喵~")));
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
export { res as default, regular, selects };
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { API } from '@alemonjs/onebot';
|
|
2
2
|
import { getTimeString, availableTools, TTSClient } from '../../api.js';
|
|
3
|
-
import { getAIConfig, getAIChatHistory, getAffectionLevelAll,
|
|
3
|
+
import { getAIConfig, getAIChatHistory, getAffectionLevelAll, getAffectionSwitch, addAIChatHistory, tools, incrementAffectionLevel, getTTSResponseSwitch } from '../../config.js';
|
|
4
4
|
import { emojiMap } from '../../emoji.js';
|
|
5
5
|
import { redis } from '../../redis.js';
|
|
6
6
|
import { uploadImageToR2 } from '../../s3.js';
|
|
7
|
-
import { useMessage, Text, ImageFile, useClient } from 'alemonjs';
|
|
7
|
+
import { getConfigValue, useMessage, Text, Mention, ImageURL, ImageFile, useClient } from 'alemonjs';
|
|
8
8
|
import { log } from 'console';
|
|
9
9
|
import OpenAi from 'openai';
|
|
10
10
|
|
|
11
|
+
const value = getConfigValue();
|
|
11
12
|
const selects = onSelects(["message.create", "private.message.create"]);
|
|
12
13
|
const regular = /.*/;
|
|
13
14
|
let ttsClient;
|
|
@@ -21,6 +22,7 @@ var res = onResponse(selects, async (e, next) => {
|
|
|
21
22
|
next();
|
|
22
23
|
return;
|
|
23
24
|
}
|
|
25
|
+
const complexOutputIsOpen = (await redis.get(`chat:complex:response:${e.guid}`)) || "1"; // 复杂输出开关
|
|
24
26
|
// 创建消息
|
|
25
27
|
const [message] = useMessage(e);
|
|
26
28
|
const config = await getAIConfig(e.guid);
|
|
@@ -29,27 +31,57 @@ var res = onResponse(selects, async (e, next) => {
|
|
|
29
31
|
const imgs = [];
|
|
30
32
|
let rawMessage = e.value.raw_message || e.raw_message || e.msg;
|
|
31
33
|
// 好感度开关
|
|
32
|
-
const affectionIsOpen =
|
|
34
|
+
const affectionIsOpen = await getAffectionSwitch(e.guid);
|
|
33
35
|
const botName = e.bot.nickname || "小咸鱼";
|
|
34
|
-
const systemPrompt =
|
|
35
|
-
关于回复格式:
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
36
|
+
const systemPrompt = `
|
|
37
|
+
## 关于回复格式:
|
|
38
|
+
### 用户发言
|
|
39
|
+
- 格式: [私聊]用户昵称(用户id)(发送时间):消息内容
|
|
40
|
+
当场景为私聊时, 用户昵称前方会出现[私聊]标识
|
|
41
|
+
### 你的回复格式
|
|
42
|
+
格式:
|
|
43
|
+
${botName}:消息内容
|
|
44
|
+
|
|
45
|
+
消息叠加:
|
|
46
|
+
你可以使用多个叠加, 来模拟发送多条消息, 让聊天显得更真实, 例如:
|
|
47
|
+
${botName}:早上好
|
|
48
|
+
${botName}:吃过早饭了吗
|
|
49
|
+
|
|
50
|
+
${complexOutputIsOpen === "1"
|
|
51
|
+
? `
|
|
52
|
+
发送图片:
|
|
53
|
+
你可以使用<img=图片链接>的方式来发送图片, 允许发送多张图, 例如:
|
|
54
|
+
${botName}:画好了<img=https://example.com/landmarks.jpg>
|
|
55
|
+
支持发送网络图片和相对路径图片, 例如:
|
|
56
|
+
${botName}:这是本地图片<img=./localimage.jpg>
|
|
57
|
+
${botName}:这是网络图片<img=https://example.com/image.jpg>
|
|
58
|
+
`
|
|
59
|
+
: ""}
|
|
60
|
+
|
|
61
|
+
${affectionIsOpen === "1" && complexOutputIsOpen === "1"
|
|
62
|
+
? `好感度系统:
|
|
63
|
+
好感度主要影响回复该用户的语气, 对低好感度的用户破口大骂, 对高好感度的用户温柔以待
|
|
64
|
+
例如增加:
|
|
65
|
+
${botName}:主人你对我真好<好感+1>
|
|
66
|
+
例如减少:
|
|
67
|
+
${botName}:滚一边去吧,傻b<好感-2>
|
|
68
|
+
|
|
69
|
+
其他情况, 例如指定修改对某个用户的好感:
|
|
70
|
+
${botName}:原来ta是这样的人, 人家以后再也不理ta了<用户昵称好感-1>
|
|
71
|
+
假设对方昵称为'哈基米':
|
|
72
|
+
${botName}:原来ta是这样的人, 人家以后再也不理ta了<哈基米好感-1>`
|
|
73
|
+
: ""}
|
|
74
|
+
|
|
75
|
+
拒绝回复:
|
|
76
|
+
在讨论中如果你觉得不适合回复或觉得与你无关又或者不感兴趣, 可以直接回复"[]"来拒绝本次回复, 例如:
|
|
77
|
+
${botName}:[]
|
|
78
|
+
|
|
79
|
+
基础状态信息:
|
|
80
|
+
这里的信息会实时变化,根据需要进行获取使用
|
|
81
|
+
当前群时间:${getTimeString()}
|
|
82
|
+
当前群号:${e.guid}
|
|
83
|
+
当前群名称:${e.GroupName || "无"}
|
|
84
|
+
`;
|
|
53
85
|
// 如果没有配置AI,则不处理
|
|
54
86
|
if (config.host.trim() === "" ||
|
|
55
87
|
config.key.trim() === "" ||
|
|
@@ -57,6 +89,10 @@ var res = onResponse(selects, async (e, next) => {
|
|
|
57
89
|
return;
|
|
58
90
|
}
|
|
59
91
|
console.log("e.reply", e.reply);
|
|
92
|
+
// 发消息的人是机器人自己, 则不处理
|
|
93
|
+
if (e.UserId == e.bot.user_id) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
60
96
|
// 当e.at不存在时,只给20%概率继续执行, 当e.at存在时,概率为40%,当e.atBot存在时,概率为100%
|
|
61
97
|
const rand = Math.random();
|
|
62
98
|
if (e.Name !== "private.message.create") {
|
|
@@ -67,27 +103,46 @@ var res = onResponse(selects, async (e, next) => {
|
|
|
67
103
|
}
|
|
68
104
|
// if (e.UserId != "3501869534") return;
|
|
69
105
|
// 处理回复图片
|
|
70
|
-
if (
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
106
|
+
if (complexOutputIsOpen === "1") {
|
|
107
|
+
if (e.reply) {
|
|
108
|
+
const replyImages = e.reply.message
|
|
109
|
+
.filter((item) => item.type === "image")
|
|
110
|
+
.map((item) => item.data.url);
|
|
111
|
+
imgs.push(...replyImages);
|
|
112
|
+
const replyAt = e.reply.message
|
|
113
|
+
.filter((item) => item.type === "at")
|
|
114
|
+
.map((item) => item.data);
|
|
115
|
+
const replyContent = e.reply.message
|
|
116
|
+
.filter((item) => item.type === "text")
|
|
117
|
+
.map((item) => item.data.text)
|
|
118
|
+
.join("");
|
|
119
|
+
if (replyAt.length > 0) {
|
|
120
|
+
for (const atItem of replyAt) {
|
|
121
|
+
const qq = atItem.qq;
|
|
122
|
+
const nickname = atItem.nickname || "未知昵称";
|
|
123
|
+
const cqAt = `[CQ:at,qq=${qq}]`;
|
|
124
|
+
rawMessage = rawMessage.replace(cqAt, `@${nickname}`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
rawMessage = rawMessage.replace(/\[CQ:image,[^\]]+\]/g, "[图片]");
|
|
128
|
+
rawMessage = `[回复:${replyContent}]` + "\n" + rawMessage;
|
|
129
|
+
}
|
|
130
|
+
// 处理图片
|
|
131
|
+
if (e.img && e.img.length > 0) {
|
|
132
|
+
console.log("有图片");
|
|
133
|
+
for (const imgUrl of e.img) {
|
|
134
|
+
imgs.push(imgUrl);
|
|
135
|
+
}
|
|
81
136
|
}
|
|
82
137
|
}
|
|
83
138
|
console.log("开始回复");
|
|
84
|
-
// 处理图片链接, 转为base64格式
|
|
85
139
|
// 下载图片并转换为 Base64
|
|
86
140
|
const imageurls = await Promise.all(imgs.map(async (imageUrl) => {
|
|
87
141
|
return await uploadImageToR2(imageUrl);
|
|
88
142
|
}));
|
|
89
143
|
// 过滤掉下载失败的图片
|
|
90
144
|
const validImageUrls = imageurls.filter((img) => img !== null);
|
|
145
|
+
console.log("validImageUrls", validImageUrls);
|
|
91
146
|
// 处理艾特
|
|
92
147
|
if (e.at && e.at.length > 0) {
|
|
93
148
|
console.log("有艾特");
|
|
@@ -104,11 +159,17 @@ var res = onResponse(selects, async (e, next) => {
|
|
|
104
159
|
index++; // 每次匹配后索引加1
|
|
105
160
|
return imageUrl ? `` : "[图片]";
|
|
106
161
|
});
|
|
107
|
-
|
|
162
|
+
if (/\[CQ:reply,[^\]]+\]/.test(rawMessage)) {
|
|
163
|
+
rawMessage = rawMessage.replace(/\[CQ:reply,[^\]]+\]/g, "");
|
|
164
|
+
validImageUrls.map((url) => {
|
|
165
|
+
rawMessage += `\n`;
|
|
166
|
+
});
|
|
167
|
+
}
|
|
108
168
|
// 处理表情,转为[emojiMap[id]]
|
|
109
|
-
rawMessage = rawMessage.replace(/\[CQ:face,id=(\d+)
|
|
169
|
+
rawMessage = rawMessage.replace(/\[CQ:face,id=(\d+),[^\]]+\]/g, (_match, p1) => {
|
|
110
170
|
return `[${emojiMap[p1]}]`;
|
|
111
171
|
});
|
|
172
|
+
console.log("处理后的消息内容:\n", rawMessage);
|
|
112
173
|
const usermessage = {
|
|
113
174
|
role: "user",
|
|
114
175
|
content: [
|
|
@@ -120,9 +181,11 @@ var res = onResponse(selects, async (e, next) => {
|
|
|
120
181
|
})),
|
|
121
182
|
{
|
|
122
183
|
type: "text",
|
|
123
|
-
text:
|
|
124
|
-
|
|
125
|
-
|
|
184
|
+
text: complexOutputIsOpen === "1"
|
|
185
|
+
? JSON.stringify({
|
|
186
|
+
[`${e.nickname}(${getTimeString()})`]: rawMessage,
|
|
187
|
+
})
|
|
188
|
+
: `${e.nickname}(${getTimeString()}): ${rawMessage}`,
|
|
126
189
|
},
|
|
127
190
|
],
|
|
128
191
|
};
|
|
@@ -145,13 +208,16 @@ var res = onResponse(selects, async (e, next) => {
|
|
|
145
208
|
...historyMessages,
|
|
146
209
|
{ ...usermessage },
|
|
147
210
|
];
|
|
148
|
-
|
|
211
|
+
const isTool = await redis.get(`chat:tools:response:${e.guid}`);
|
|
212
|
+
const createParams = {
|
|
149
213
|
model: config.model,
|
|
150
|
-
tools,
|
|
151
|
-
tool_choice: "auto",
|
|
152
|
-
// reasoning_effort: "low",
|
|
153
214
|
messages: messages,
|
|
154
|
-
}
|
|
215
|
+
};
|
|
216
|
+
if (isTool === "1") {
|
|
217
|
+
createParams["tools"] = tools;
|
|
218
|
+
createParams["tool_choice"] = "auto";
|
|
219
|
+
}
|
|
220
|
+
let res = await openai.chat.completions.create(createParams);
|
|
155
221
|
if (!res.choices || res.choices.length === 0) {
|
|
156
222
|
log("AI未返回内容");
|
|
157
223
|
return;
|
|
@@ -170,6 +236,9 @@ var res = onResponse(selects, async (e, next) => {
|
|
|
170
236
|
if (responseMessage.tool_calls.find((tc) => tc.function.name === "StableDiffusionGenerateImage")) {
|
|
171
237
|
message.send(format(Text("好的, 我这就去画图~稍等一会儿哦~")));
|
|
172
238
|
}
|
|
239
|
+
if (responseMessage.tool_calls.find((tc) => tc.function.name === "AIPS")) {
|
|
240
|
+
message.send(format(Text("开始修图中~稍等一会儿哦~")));
|
|
241
|
+
}
|
|
173
242
|
console.log("模型决定调用工具:", responseMessage.tool_calls);
|
|
174
243
|
for (const toolCall of responseMessage.tool_calls) {
|
|
175
244
|
const functionName = toolCall.function.name;
|
|
@@ -210,99 +279,79 @@ var res = onResponse(selects, async (e, next) => {
|
|
|
210
279
|
}
|
|
211
280
|
}
|
|
212
281
|
// 如果没有工具调用,处理最终回复
|
|
213
|
-
let reply = res.choices?.[0]?.message?.content?.trim()
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
catch (error) {
|
|
234
|
-
console.log("解析AI好感变化失败:", error);
|
|
282
|
+
let reply = res.choices?.[0]?.message?.content?.trim() || `${botName}:[]`;
|
|
283
|
+
if (reply === `${botName}:[]`) {
|
|
284
|
+
log("AI选择不回复");
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
console.log("AI回复:\n", reply);
|
|
288
|
+
// 处理消息
|
|
289
|
+
// 提取ai发送的消息有多少条
|
|
290
|
+
const replyMessages = reply.split(new RegExp(`${botName}:`, "g")).slice(1);
|
|
291
|
+
const replymessages = [];
|
|
292
|
+
for (const replyMessage of replyMessages) {
|
|
293
|
+
// 从消息中提取图片
|
|
294
|
+
const imageRegex = /<img=(.*?)>/g;
|
|
295
|
+
let match;
|
|
296
|
+
const imgUrls = [];
|
|
297
|
+
while ((match = imageRegex.exec(replyMessage)) !== null) {
|
|
298
|
+
imgUrls.push(match[1]);
|
|
235
299
|
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
300
|
+
// 处理好感度变化(通用和特定用户)
|
|
301
|
+
const affectionRegex = /<([^<>]*?)好感([+-]\d+)>/g;
|
|
302
|
+
let affectionMatch;
|
|
303
|
+
while ((affectionMatch = affectionRegex.exec(replyMessage)) !== null) {
|
|
304
|
+
const nickname = affectionMatch[1] || e.UserName;
|
|
305
|
+
const change = parseInt(affectionMatch[2], 10);
|
|
306
|
+
if (!isNaN(change) && change !== 0) {
|
|
307
|
+
console.log(`检测到对${nickname}的好感变化: ${change}`);
|
|
308
|
+
await incrementAffectionLevel(e.guid, nickname, change);
|
|
245
309
|
}
|
|
246
310
|
}
|
|
311
|
+
// 组合消息,添加到replymessages数组中
|
|
312
|
+
replymessages.push({
|
|
313
|
+
text: replyMessage.replace(imageRegex, "").trim(),
|
|
314
|
+
images: imgUrls,
|
|
315
|
+
});
|
|
247
316
|
}
|
|
248
317
|
// 记录对话
|
|
249
318
|
await addAIChatHistory(e.guid, res.choices?.[0].message);
|
|
250
|
-
|
|
319
|
+
// 发送消息
|
|
320
|
+
const isOpenTTSReply = await getTTSResponseSwitch(e.guid);
|
|
251
321
|
if (isOpenTTSReply === "1") {
|
|
322
|
+
// 提前发送语音消息避免等待时间过长
|
|
252
323
|
ttsClient = new TTSClient();
|
|
253
|
-
|
|
324
|
+
let msg = replymessages.map((item) => item.text).join("\n");
|
|
325
|
+
msg = msg.replace(/\[CQ:[^\]]+\]/g, "");
|
|
326
|
+
sendTTSMessage(msg, e, ttsClient);
|
|
254
327
|
}
|
|
255
|
-
//
|
|
256
|
-
|
|
257
|
-
const
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
267
|
-
}
|
|
268
|
-
const msg = aiReply.assistant.replace(/\[CQ:[^\]]+\]/, "").trim();
|
|
269
|
-
if (aiReply.image_url) {
|
|
270
|
-
const replyRes = await message.send(format(Text(msg), ImageFile(aiReply.image_url)));
|
|
271
|
-
if (replyRes[0].code !== 2000) {
|
|
272
|
-
console.log("发送消息失败:", replyRes);
|
|
273
|
-
await message.send(format(Text(msg)));
|
|
274
|
-
await message.send(format(ImageFile(aiReply.image_url)));
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
else {
|
|
278
|
-
message.send(format(Text(msg)));
|
|
279
|
-
}
|
|
280
|
-
if (isOpenTTSReply === "1") {
|
|
281
|
-
sendTTSMessage(msg, e, ttsClient);
|
|
328
|
+
// 处理消息
|
|
329
|
+
for (const replyMessage of replymessages) {
|
|
330
|
+
const { text, images } = replyMessage;
|
|
331
|
+
const components = [Mention(e.UserId), Text(text)];
|
|
332
|
+
for (const img of images) {
|
|
333
|
+
const isNetworkImage = /^https?:\/\//.test(img);
|
|
334
|
+
if (e.Platform == "scbbs" && isNetworkImage) {
|
|
335
|
+
// scbbs平台不支持网络图片, 需要先下载再上传
|
|
336
|
+
const uploadedUrl = await uploadImageToR2(img.replace("https://imgen.x.ai", value.xaiImgProxy || "https://imgen.x.ai"));
|
|
337
|
+
if (uploadedUrl) {
|
|
338
|
+
components.push(ImageURL(uploadedUrl));
|
|
282
339
|
}
|
|
283
340
|
}
|
|
284
341
|
else {
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
}
|
|
342
|
+
const imageComponent = isNetworkImage ? ImageURL(img) : ImageFile(img);
|
|
343
|
+
components.push(imageComponent);
|
|
288
344
|
}
|
|
289
345
|
}
|
|
346
|
+
// 添加随机1-5秒延迟
|
|
347
|
+
const delay = Math.random() * 4000 + 1000;
|
|
348
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
349
|
+
await message.send(format(...components));
|
|
290
350
|
}
|
|
291
|
-
|
|
292
|
-
console.log("解析AI回复失败,发送原始内容");
|
|
293
|
-
console.log(error);
|
|
294
|
-
const msg = reply
|
|
295
|
-
.replace(/\[CQ:[^\]]+\]/, "")
|
|
296
|
-
.replace(/"}]/, "")
|
|
297
|
-
.replace(/\[{"assistant":"/, "")
|
|
298
|
-
.trim();
|
|
299
|
-
message.send(format(Text(reply)));
|
|
300
|
-
if (isOpenTTSReply === "1") {
|
|
301
|
-
sendTTSMessage(msg, e, ttsClient);
|
|
302
|
-
}
|
|
303
|
-
}
|
|
351
|
+
return;
|
|
304
352
|
});
|
|
305
|
-
function sendTTSMessage(message, e, ttsClient) {
|
|
353
|
+
async function sendTTSMessage(message, e, ttsClient) {
|
|
354
|
+
await ttsClient.setModel("派蒙", "默认");
|
|
306
355
|
// 发送TTS语音
|
|
307
356
|
ttsClient.getAudio(message).then(async (audio) => {
|
|
308
357
|
console.log("audio", audio);
|