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
package/README.md
CHANGED
|
@@ -1,39 +1,95 @@
|
|
|
1
1
|
|
|
2
2
|
## 1.安装
|
|
3
3
|
|
|
4
|
+
安装中遇到问题可访问alemonjs官网查询[https://alemonjs.com/docs](https://alemonjs.com/docs)
|
|
5
|
+
|
|
4
6
|
### yarn安装方式(推荐)
|
|
7
|
+
前置条件:
|
|
8
|
+
安装alemonjs: [alemonjs官网](https://alemonjs.com)
|
|
5
9
|
|
|
6
|
-
```
|
|
10
|
+
``` sh
|
|
7
11
|
yarn add alemonjs-aichat
|
|
8
12
|
```
|
|
9
13
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
```
|
|
14
|
-
https://gitee.com/suancaixianyu/alemonjs-aichat.git
|
|
14
|
+
```sh
|
|
15
|
+
## 运行
|
|
16
|
+
yarn start
|
|
15
17
|
```
|
|
16
18
|
|
|
17
|
-
|
|
19
|
+
### git拉取项目安装
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
git clone git@gitee.com:suancaixianyu/alemonjs-aichat.git
|
|
23
|
+
cd alemonjs-aichat
|
|
24
|
+
yarn
|
|
18
25
|
```
|
|
19
|
-
|
|
26
|
+
|
|
27
|
+
```sh
|
|
28
|
+
## 运行
|
|
29
|
+
yarn dev
|
|
30
|
+
|
|
31
|
+
## 打包
|
|
32
|
+
yarn build
|
|
20
33
|
```
|
|
21
34
|
|
|
22
|
-
## 2
|
|
23
|
-
|
|
35
|
+
## 2.配置
|
|
36
|
+
|
|
37
|
+
#### 配置app
|
|
38
|
+
在配置中添加该插件, 才能正常触发指令
|
|
39
|
+
|
|
40
|
+
如果使用的是git拉取项目的方式, 就不需要配置这个
|
|
24
41
|
``` yaml
|
|
25
42
|
app:
|
|
26
43
|
- 'alemonjs-aichat'
|
|
27
44
|
```
|
|
28
45
|
|
|
29
|
-
|
|
46
|
+
#### 配置redis
|
|
30
47
|
`alemon.config.yaml`
|
|
31
48
|
``` yaml
|
|
32
49
|
redis:
|
|
33
|
-
host
|
|
50
|
+
# host默认 127.0.0.1
|
|
51
|
+
host: '127.0.0.1'
|
|
52
|
+
# 端口号默认 6379
|
|
34
53
|
port: 6379
|
|
54
|
+
# 无密码则留空或者不写, 默认无密码
|
|
35
55
|
password: ''
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
#### 配置AI修图
|
|
59
|
+
修图基于xai的grok接口实现, 该接口返回的图片也是xai的, 在特定平台中可能无法发送此图片,这时候可以用自己写个代理将图片转为可访问状态
|
|
60
|
+
|
|
61
|
+
已知无需转换的平台: `QQ`,`Bubble`
|
|
62
|
+
`alemon.config.yaml`
|
|
63
|
+
``` yaml
|
|
64
|
+
# 代理默认可不填
|
|
65
|
+
xaiImgProxy: 'http://127.0.0.1:3002/ximgen'
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
ai修图依赖于xai, 需要使用该功能必须添加一个grok模型, 在启动机器人后, 使用以下指令添加
|
|
69
|
+
```
|
|
70
|
+
#添加ai grok https://api.x.ai/v1 <你的apiKey> grok-4-1-fast-non-reasoning
|
|
71
|
+
```
|
|
72
|
+
通常情况下nodejs不会使用本地的代理, 因此你可能需要自己解决一下无法访问ai的问题, 例如另写一个脚本, 脚本已经预备好了, 下载项目中的`daili.js`然后运行它即可(Clash的默认端口一般是7890, 可能需要修改一下daili.js中的端口)
|
|
73
|
+
``` sh
|
|
74
|
+
node daili
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
如果你使用的是`daili.js`, 那么地址需要改为`http://127.0.0.1:3002/x/v1`
|
|
36
78
|
|
|
79
|
+
#### 配置搜索工具
|
|
80
|
+
网页搜索功能基于[https://serpapi.com](https://serpapi.com)的接口制作
|
|
81
|
+
需要前往他们的官网获取key
|
|
82
|
+
`alemon.config.yaml`
|
|
83
|
+
``` yaml
|
|
84
|
+
# 默认无, 可能无法使用网页搜索
|
|
85
|
+
searchApiKey: ''
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
#### 配置管理员
|
|
89
|
+
`alemon.config.yaml`
|
|
90
|
+
``` yaml
|
|
91
|
+
# 默认无, 小部分功能需要管理员权限才能使用
|
|
92
|
+
master_id: ['3501869534']
|
|
37
93
|
```
|
|
38
94
|
|
|
39
95
|
## 4.使用
|
package/lib/api.js
CHANGED
|
@@ -1,42 +1,9 @@
|
|
|
1
1
|
import { redis } from './redis.js';
|
|
2
2
|
import { getConfigValue } from 'alemonjs';
|
|
3
3
|
import fs from 'fs';
|
|
4
|
+
import { uploadImageToR2 } from './s3.js';
|
|
4
5
|
|
|
5
6
|
const value = getConfigValue();
|
|
6
|
-
const uploadImage = async (base64) => {
|
|
7
|
-
const apiUrl = "https://img.scdn.io/api/v1.php";
|
|
8
|
-
const uploadSingleImage = async (imageBase64) => {
|
|
9
|
-
const singleFormData = new FormData();
|
|
10
|
-
const blob = await (await fetch(imageBase64)).blob();
|
|
11
|
-
singleFormData.append("image", blob, "image.jpg");
|
|
12
|
-
const controller = new AbortController();
|
|
13
|
-
const timeoutId = setTimeout(() => controller.abort(), 30000);
|
|
14
|
-
const response = await fetch(apiUrl, {
|
|
15
|
-
method: "POST",
|
|
16
|
-
body: singleFormData,
|
|
17
|
-
signal: controller.signal,
|
|
18
|
-
});
|
|
19
|
-
clearTimeout(timeoutId);
|
|
20
|
-
if (!response.ok) {
|
|
21
|
-
return "";
|
|
22
|
-
}
|
|
23
|
-
const result = await response.json();
|
|
24
|
-
return result.url || "";
|
|
25
|
-
};
|
|
26
|
-
if (Array.isArray(base64)) {
|
|
27
|
-
const results = [];
|
|
28
|
-
for (const item of base64) {
|
|
29
|
-
const result = await uploadSingleImage(item);
|
|
30
|
-
if (result) {
|
|
31
|
-
results.push(result);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
return results;
|
|
35
|
-
}
|
|
36
|
-
else {
|
|
37
|
-
return [await uploadSingleImage(base64)];
|
|
38
|
-
}
|
|
39
|
-
};
|
|
40
7
|
const getTimeString = () => {
|
|
41
8
|
const now = new Date();
|
|
42
9
|
const month = String(now.getMonth() + 1).padStart(2, "0");
|
|
@@ -175,9 +142,10 @@ const availableTools = {
|
|
|
175
142
|
* @param {string} prompt - 正向提示词
|
|
176
143
|
* @param {string} [bad_prompt] - 反向提示词
|
|
177
144
|
* @param {string} [direction] - 方向: landscape, portrait, square
|
|
145
|
+
* @param {boolean} [base64] - 是否返回base64格式的图片
|
|
178
146
|
* @returns {Promise<{type: "image", url: string}[]>} 图片列表
|
|
179
147
|
*/
|
|
180
|
-
StableDiffusionGenerateImage: async ({ prompt, bad_prompt = "(easynegative:1.1), (verybadimagenegative_v1.3:1), (low quality:1.2), (worst quality:1.2)", direction = "portrait", }) => {
|
|
148
|
+
StableDiffusionGenerateImage: async ({ prompt, bad_prompt = "(easynegative:1.1), (verybadimagenegative_v1.3:1), (low quality:1.2), (worst quality:1.2)", direction = "portrait", base64 = false, }) => {
|
|
181
149
|
const images = [];
|
|
182
150
|
const sizeMap = {
|
|
183
151
|
landscape: { width: 768, height: 512 },
|
|
@@ -222,6 +190,9 @@ const availableTools = {
|
|
|
222
190
|
const base64Data = data.images[0].replace(/^data:image\/\w+;base64,/, "");
|
|
223
191
|
const buffer = Buffer.from(base64Data, "base64");
|
|
224
192
|
fs.writeFileSync(path, buffer);
|
|
193
|
+
if (base64) {
|
|
194
|
+
return data.images;
|
|
195
|
+
}
|
|
225
196
|
images.push({ type: "image", url: path });
|
|
226
197
|
return images;
|
|
227
198
|
}
|
|
@@ -299,7 +270,21 @@ const availableTools = {
|
|
|
299
270
|
const data = await response.json();
|
|
300
271
|
return data.data.list.map((item) => ({
|
|
301
272
|
viewUrl: `https://test.suancaixianyu.cn/#/postDetails/${item.id}`,
|
|
302
|
-
|
|
273
|
+
title: item.title,
|
|
274
|
+
content: item.content,
|
|
275
|
+
userName: item.creator.nickname,
|
|
276
|
+
userPage: `https://test.suancaixianyu.cn/#/user/${item.creatorId}`,
|
|
277
|
+
createdAt: item.createdAt,
|
|
278
|
+
plate: item.plate.name,
|
|
279
|
+
postVersions: item.postVersions.map((version) => ({
|
|
280
|
+
version: version.version,
|
|
281
|
+
versionName: version.title,
|
|
282
|
+
createdAt: version.createdAt,
|
|
283
|
+
files: version.files.map((file) => ({
|
|
284
|
+
name: file.name,
|
|
285
|
+
url: file.url,
|
|
286
|
+
})),
|
|
287
|
+
})),
|
|
303
288
|
}));
|
|
304
289
|
}
|
|
305
290
|
catch (error) {
|
|
@@ -327,6 +312,69 @@ const availableTools = {
|
|
|
327
312
|
return [];
|
|
328
313
|
}
|
|
329
314
|
},
|
|
315
|
+
AIPS: async ({ image_url, instruction }) => {
|
|
316
|
+
// 从aiconfig中查询grok配置
|
|
317
|
+
const aiConfigStr = await redis.get(`ai:list:grok`);
|
|
318
|
+
if (!aiConfigStr) {
|
|
319
|
+
return "未找到AI配置";
|
|
320
|
+
}
|
|
321
|
+
const aiConfig = JSON.parse(aiConfigStr);
|
|
322
|
+
// 处理单个或多个图片
|
|
323
|
+
const imageUrls = Array.isArray(image_url) ? image_url : [image_url];
|
|
324
|
+
const imgBase64Array = [];
|
|
325
|
+
for (const url of imageUrls) {
|
|
326
|
+
let imgBase64 = "";
|
|
327
|
+
if (url.startsWith("https://imgen.x.ai")) {
|
|
328
|
+
imgBase64 = url;
|
|
329
|
+
}
|
|
330
|
+
else {
|
|
331
|
+
imgBase64 = await uploadImageToR2(url);
|
|
332
|
+
}
|
|
333
|
+
imgBase64Array.push(imgBase64);
|
|
334
|
+
}
|
|
335
|
+
try {
|
|
336
|
+
const requestBody = Array.isArray(image_url)
|
|
337
|
+
? {
|
|
338
|
+
model: "grok-imagine-image",
|
|
339
|
+
images: imgBase64Array.map((base64) => ({
|
|
340
|
+
url: base64,
|
|
341
|
+
type: "image_url",
|
|
342
|
+
})),
|
|
343
|
+
prompt: instruction,
|
|
344
|
+
n: 1,
|
|
345
|
+
}
|
|
346
|
+
: {
|
|
347
|
+
model: "grok-imagine-image",
|
|
348
|
+
image: {
|
|
349
|
+
url: imgBase64Array[0],
|
|
350
|
+
type: "image_url",
|
|
351
|
+
},
|
|
352
|
+
prompt: instruction,
|
|
353
|
+
n: 1,
|
|
354
|
+
};
|
|
355
|
+
console.log("最终请求体", requestBody);
|
|
356
|
+
const res = await fetch(aiConfig.host + "/images/edits", {
|
|
357
|
+
method: "POST",
|
|
358
|
+
headers: {
|
|
359
|
+
"Content-Type": "application/json",
|
|
360
|
+
Authorization: `Bearer ${aiConfig.key}`,
|
|
361
|
+
},
|
|
362
|
+
body: JSON.stringify(requestBody),
|
|
363
|
+
});
|
|
364
|
+
if (!res.ok) {
|
|
365
|
+
const err = await res.text();
|
|
366
|
+
console.log(err);
|
|
367
|
+
return err;
|
|
368
|
+
}
|
|
369
|
+
const image = await res.json();
|
|
370
|
+
console.log("画好了", image);
|
|
371
|
+
return image.data;
|
|
372
|
+
}
|
|
373
|
+
catch (error) {
|
|
374
|
+
console.error("Error calling AIPS:", error);
|
|
375
|
+
return "调用接口失败" + error;
|
|
376
|
+
}
|
|
377
|
+
},
|
|
330
378
|
};
|
|
331
379
|
|
|
332
|
-
export { TTSClient, availableTools, getTimeString
|
|
380
|
+
export { TTSClient, availableTools, getTimeString };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }/*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/*,:after,:before{border:0 solid #e5e7eb;box-sizing:border-box}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-moz-tab-size:4;tab-size:4;-webkit-tap-highlight-color:transparent}body{line-height:inherit}hr{border-top-width:1px;color:inherit;height:0}abbr:where([title]){text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-size:1em;font-variation-settings:normal}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{border-collapse:collapse;border-color:inherit;text-indent:0}button,input,optgroup,select,textarea{color:inherit;font-family:inherit;font-feature-settings:inherit;font-size:100%;font-variation-settings:inherit;font-weight:inherit;letter-spacing:inherit;line-height:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::placeholder,textarea::placeholder{color:#9ca3af;opacity:1}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{height:auto;max-width:100%}[hidden]:where(:not([hidden=until-found])){display:none}.mb-4{margin-bottom:1rem}.mt-1{margin-top:.25rem}.box-border{box-sizing:border-box}.flex{display:flex}.w-1\/3{width:33.333333%}.flex-wrap{flex-wrap:wrap}.gap-2{gap:.5rem}.overflow-hidden{overflow:hidden}.rounded-lg{border-radius:.5rem}.border-b{border-bottom-width:1px}.border-r{border-right-width:1px}.border-white\/20{border-color:hsla(0,0%,100%,.2)}.bg-black\/30{background-color:rgba(0,0,0,.3)}.bg-black\/60{background-color:rgba(0,0,0,.6)}.bg-cover{background-size:cover}.p-2{padding:.5rem}.p-4{padding:1rem}.text-2xl{font-size:1.5rem;line-height:2rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.font-bold{font-weight:700}.font-semibold{font-weight:600}.italic{font-style:italic}.text-\[\#B8AE8E\]{--tw-text-opacity:1;color:rgb(184 174 142/var(--tw-text-opacity,1))}.text-\[\#e8deba\]{--tw-text-opacity:1;color:rgb(232 222 186/var(--tw-text-opacity,1))}.text-gray-400{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity,1))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.drop-shadow-md{--tw-drop-shadow:drop-shadow(0 4px 3px rgba(0,0,0,.07)) drop-shadow(0 2px 2px rgba(0,0,0,.06));filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}body{display:flex;flex-direction:column;margin:0;padding:0}.backdrop-blur-xs{backdrop-filter:blur(4px)}.last\:border-0:last-child{border-width:0}
|
package/lib/assets/main.css.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
const reg = ['win32'].includes(process.platform) ? /^file:\/\/\// : /^file:\/\// ;
|
|
2
|
-
const fileUrl = new URL('main.css-
|
|
2
|
+
const fileUrl = new URL('main.css-CXQ9gyIo.css', import.meta.url).href.replace(reg, '');
|
|
3
3
|
|
|
4
4
|
export { fileUrl as default };
|
package/lib/config.js
CHANGED
|
@@ -105,8 +105,8 @@ const clearAIChatHistory = async (guid) => {
|
|
|
105
105
|
* @param userKey 用户标识
|
|
106
106
|
* @returns
|
|
107
107
|
*/
|
|
108
|
-
const getAffectionLevel = async (userKey) => {
|
|
109
|
-
const levelStr = await redis.get(`ai:affection:${userKey}`);
|
|
108
|
+
const getAffectionLevel = async (guid, userKey) => {
|
|
109
|
+
const levelStr = await redis.get(`ai:affection:${guid}:${userKey}`);
|
|
110
110
|
if (levelStr) {
|
|
111
111
|
return parseInt(levelStr, 10);
|
|
112
112
|
}
|
|
@@ -130,8 +130,8 @@ const getAffectionLevelAll = async (guid) => {
|
|
|
130
130
|
* @param level 好感度等级
|
|
131
131
|
* @returns
|
|
132
132
|
*/
|
|
133
|
-
const setAffectionLevel = async (userKey, level) => {
|
|
134
|
-
return await redis.set(`ai:affection:${userKey}`, level.toString());
|
|
133
|
+
const setAffectionLevel = async (guid, userKey, level) => {
|
|
134
|
+
return await redis.set(`ai:affection:${guid}:${userKey}`, level.toString());
|
|
135
135
|
};
|
|
136
136
|
/**
|
|
137
137
|
* 增加好感度等级
|
|
@@ -139,10 +139,10 @@ const setAffectionLevel = async (userKey, level) => {
|
|
|
139
139
|
* @param increment 增加的等级
|
|
140
140
|
* @returns
|
|
141
141
|
*/
|
|
142
|
-
const incrementAffectionLevel = async (userKey, increment) => {
|
|
143
|
-
const currentLevel = await getAffectionLevel(userKey);
|
|
142
|
+
const incrementAffectionLevel = async (guid, userKey, increment) => {
|
|
143
|
+
const currentLevel = await getAffectionLevel(guid, userKey);
|
|
144
144
|
const newLevel = currentLevel + increment;
|
|
145
|
-
await setAffectionLevel(userKey, newLevel);
|
|
145
|
+
await setAffectionLevel(guid, userKey, newLevel);
|
|
146
146
|
return newLevel;
|
|
147
147
|
};
|
|
148
148
|
/** * 重置好感度等级
|
|
@@ -180,12 +180,26 @@ const deleteAI = async (name) => {
|
|
|
180
180
|
const affectionSwitch = async (guid, enable) => {
|
|
181
181
|
return await redis.set(`ai:affection:switch:${guid}`, enable ? "1" : "0");
|
|
182
182
|
};
|
|
183
|
+
/**
|
|
184
|
+
* 获取好感度开关状态
|
|
185
|
+
*/
|
|
186
|
+
const getAffectionSwitch = async (guid) => {
|
|
187
|
+
const switchStr = (await redis.get(`ai:affection:switch:${guid}`)) || "1";
|
|
188
|
+
return switchStr;
|
|
189
|
+
};
|
|
190
|
+
/**
|
|
191
|
+
* 获取tts回复开关状态
|
|
192
|
+
*/
|
|
193
|
+
const getTTSResponseSwitch = async (guid) => {
|
|
194
|
+
const switchStr = (await redis.get(`chat:tts:response:${guid}`)) || "0";
|
|
195
|
+
return switchStr;
|
|
196
|
+
};
|
|
183
197
|
const tools = [
|
|
184
198
|
{
|
|
185
199
|
type: "function",
|
|
186
200
|
function: {
|
|
187
201
|
name: "StableDiffusionGenerateImage",
|
|
188
|
-
description: "使用 Stable Diffusion
|
|
202
|
+
description: "使用 Stable Diffusion 模型生成图像, 该工具只擅长生成动漫风格的图片, 文本生成图像, 如需修改图像请使用AIPS工具",
|
|
189
203
|
parameters: {
|
|
190
204
|
type: "object",
|
|
191
205
|
properties: {
|
|
@@ -209,23 +223,24 @@ const tools = [
|
|
|
209
223
|
},
|
|
210
224
|
},
|
|
211
225
|
},
|
|
212
|
-
{
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
},
|
|
226
|
+
// {
|
|
227
|
+
// type: "function",
|
|
228
|
+
// function: {
|
|
229
|
+
// name: "StableDiffusionImageToPrompt",
|
|
230
|
+
// description:
|
|
231
|
+
// "使用 Stable Diffusion 将图片转换为提示词, 仅供绘图时使用, 其他场景请勿使用",
|
|
232
|
+
// parameters: {
|
|
233
|
+
// type: "object",
|
|
234
|
+
// properties: {
|
|
235
|
+
// image_url: {
|
|
236
|
+
// type: "string",
|
|
237
|
+
// description: "图片的URL地址,必须是你所收到的图片地址",
|
|
238
|
+
// },
|
|
239
|
+
// },
|
|
240
|
+
// required: ["image_url"],
|
|
241
|
+
// },
|
|
242
|
+
// },
|
|
243
|
+
// },
|
|
229
244
|
{
|
|
230
245
|
type: "function",
|
|
231
246
|
function: {
|
|
@@ -277,6 +292,27 @@ const tools = [
|
|
|
277
292
|
},
|
|
278
293
|
},
|
|
279
294
|
},
|
|
295
|
+
{
|
|
296
|
+
type: "function",
|
|
297
|
+
function: {
|
|
298
|
+
name: "AIPS",
|
|
299
|
+
description: "AI修图工具, 可以对图片进行任何处理, 只需提供需要修改的图片url和修改需求即可. 当遇到审核拦截的问题时, 请不要再次尝试提交相同的修改需求, 这可能会导致账号被封禁. 请修改你的修改需求文本描述, 使其更具体和详细, 以避免触发审核拦截. ",
|
|
300
|
+
parameters: {
|
|
301
|
+
type: "object",
|
|
302
|
+
properties: {
|
|
303
|
+
image_url: {
|
|
304
|
+
type: "string",
|
|
305
|
+
description: "需要修改的图片URL地址,必须是你所收到的图片地址",
|
|
306
|
+
},
|
|
307
|
+
instruction: {
|
|
308
|
+
type: "string",
|
|
309
|
+
description: "修改需求的文本描述,尽量详细和具体,例如:'请将图片中的人物换成一个穿着宇航服的宇航员,背景改成月球表面,保持其他元素不变'",
|
|
310
|
+
},
|
|
311
|
+
},
|
|
312
|
+
required: ["image_url", "instruction"],
|
|
313
|
+
},
|
|
314
|
+
},
|
|
315
|
+
},
|
|
280
316
|
];
|
|
281
317
|
|
|
282
|
-
export { addAI, addAIChatHistory, addPrompt, affectionSwitch, clearAIChatHistory, defaultAIConfig, deleteAI, deletePrompt, getAIChatHistory, getAIConfig, getAIList, getAffectionLevel, getAffectionLevelAll, getPromptList, incrementAffectionLevel, resetAffectionLevel, resetAllAffectionLevels, setAffectionLevel, setBotID, switchAI, switchPrompt, systemPrompt, tools };
|
|
318
|
+
export { addAI, addAIChatHistory, addPrompt, affectionSwitch, clearAIChatHistory, defaultAIConfig, deleteAI, deletePrompt, getAIChatHistory, getAIConfig, getAIList, getAffectionLevel, getAffectionLevelAll, getAffectionSwitch, getPromptList, getTTSResponseSwitch, incrementAffectionLevel, resetAffectionLevel, resetAllAffectionLevels, setAffectionLevel, setBotID, switchAI, switchPrompt, systemPrompt, tools };
|
package/lib/data/help.json.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
var title = "IA聊天插件";
|
|
2
2
|
var name = "aichat";
|
|
3
|
-
var version = "v1.0.
|
|
3
|
+
var version = "v1.0.14";
|
|
4
4
|
var by = "AlemonJS";
|
|
5
5
|
var list = [
|
|
6
6
|
{
|
|
7
7
|
group: {
|
|
8
8
|
title: "配置操作",
|
|
9
|
+
desc: "在新的聊天窗口中需要先配置ai才能使用",
|
|
9
10
|
items: [
|
|
10
11
|
{
|
|
11
12
|
cmd: "/添加ai",
|
|
@@ -26,6 +27,10 @@ var list = [
|
|
|
26
27
|
{
|
|
27
28
|
cmd: "/切换ai [名称]",
|
|
28
29
|
desc: "切换到指定名称的ai配置。"
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
cmd: "/修改 [AI名称] 模型 [模型名称]",
|
|
33
|
+
desc: "修改指定AI配置的模型。"
|
|
29
34
|
}
|
|
30
35
|
]
|
|
31
36
|
}
|
|
@@ -33,14 +38,15 @@ var list = [
|
|
|
33
38
|
{
|
|
34
39
|
group: {
|
|
35
40
|
title: "提示词操作",
|
|
41
|
+
desc: "提示词功能可以让你为AI聊天配置添加特定的提示词,以引导AI的回复风格和内容。",
|
|
36
42
|
items: [
|
|
37
43
|
{
|
|
38
44
|
cmd: "/添加提示词 [名称] [内容]",
|
|
39
|
-
desc: "
|
|
45
|
+
desc: "添加新的提示词配置。"
|
|
40
46
|
},
|
|
41
47
|
{
|
|
42
48
|
cmd: "/删除提示词 [名称]",
|
|
43
|
-
desc: "
|
|
49
|
+
desc: "删除已有的提示词配置。"
|
|
44
50
|
},
|
|
45
51
|
{
|
|
46
52
|
cmd: "/提示词列表",
|
|
@@ -60,6 +66,7 @@ var list = [
|
|
|
60
66
|
{
|
|
61
67
|
group: {
|
|
62
68
|
title: "聊天",
|
|
69
|
+
desc: "以下功能的可用性取决于所使用的AI模型是否支持相关工具。",
|
|
63
70
|
items: [
|
|
64
71
|
{
|
|
65
72
|
cmd: "/[开启|关闭]聊天",
|
|
@@ -77,6 +84,10 @@ var list = [
|
|
|
77
84
|
cmd: "/[开启|关闭]MCP工具",
|
|
78
85
|
desc: "开启或关闭MCP工具功能。部分AI模型可能不支持该功能。"
|
|
79
86
|
},
|
|
87
|
+
{
|
|
88
|
+
cmd: "/[开启|关闭]复杂回复",
|
|
89
|
+
desc: "当模型不支持json输出时关闭该功能可以让AI以纯文本形式回复,关闭后无法识别图片 使用工具 好感度等功能,适合本地部署的ai"
|
|
90
|
+
},
|
|
80
91
|
{
|
|
81
92
|
cmd: "/好感度",
|
|
82
93
|
desc: "查看当前好感度状态。"
|
|
@@ -92,9 +103,26 @@ var list = [
|
|
|
92
103
|
]
|
|
93
104
|
}
|
|
94
105
|
},
|
|
106
|
+
{
|
|
107
|
+
group: {
|
|
108
|
+
title: "AI功能",
|
|
109
|
+
desc: "这里的功能基于特定ai制作, 需要先配置grok模型才能使用ps功能",
|
|
110
|
+
items: [
|
|
111
|
+
{
|
|
112
|
+
cmd: "/ps [提示词]",
|
|
113
|
+
desc: "使用ai修图功能对图片进行处理。提示词可以包含对图片的描述和修改要求。"
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
cmd: "/画图 [提示词]",
|
|
117
|
+
desc: "使用ai画图功能根据提示词生成图片。提示词可以包含对图片的描述和要求。"
|
|
118
|
+
}
|
|
119
|
+
]
|
|
120
|
+
}
|
|
121
|
+
},
|
|
95
122
|
{
|
|
96
123
|
group: {
|
|
97
124
|
title: "管理员设置",
|
|
125
|
+
desc: "以下功能仅管理员可用,用于管理所有用户的对话记录和好感度数据。",
|
|
98
126
|
items: [
|
|
99
127
|
{
|
|
100
128
|
cmd: "/清空所有对话",
|
|
@@ -24,7 +24,9 @@ function App() {
|
|
|
24
24
|
" ",
|
|
25
25
|
React.createElement("span", { className: "text-[#e8deba] " }, help.by)),
|
|
26
26
|
help.list.map((item, index) => (React.createElement("div", { key: index, className: " border-b last:border-0 bg-black/60 rounded-lg overflow-hidden mb-4 text-white backdrop-blur-xs" },
|
|
27
|
-
React.createElement("
|
|
27
|
+
React.createElement("div", { className: "p-4 bg-black/30 border-b border-white/20" },
|
|
28
|
+
React.createElement("p", { className: "text-lg font-semibold" }, item.group.title),
|
|
29
|
+
React.createElement("p", { className: "text-md mt-1" }, item.group.desc)),
|
|
28
30
|
React.createElement("div", null, Array.from({
|
|
29
31
|
length: Math.ceil(item.group.items.length / 3),
|
|
30
32
|
}).map((_, rowIndex) => {
|
package/lib/middleware/mw.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { useClient } from 'alemonjs';
|
|
1
|
+
import { getConfigValue, useClient } from 'alemonjs';
|
|
2
2
|
import { API } from '@alemonjs/onebot';
|
|
3
|
+
import { API as API$1 } from '@alemonjs/bubble';
|
|
3
4
|
|
|
4
5
|
const selects = onSelects(["message.create", "private.message.create"]);
|
|
5
6
|
// 中间件
|
|
6
7
|
var mw = onMiddleware(selects, async (event, next) => {
|
|
7
|
-
|
|
8
|
+
const config = getConfigValue();
|
|
9
|
+
console.log("原始事件:", event.value);
|
|
8
10
|
// 新增字段
|
|
9
11
|
event["user_id"] = event.UserId; // 用户QQ号
|
|
10
12
|
event["msg"] = event.MessageText; // 消息内容
|
|
@@ -115,6 +117,58 @@ var mw = onMiddleware(selects, async (event, next) => {
|
|
|
115
117
|
})
|
|
116
118
|
.join("");
|
|
117
119
|
}
|
|
120
|
+
if (event.Platform == "bubble") {
|
|
121
|
+
const bubbleConfig = config.bubble || {};
|
|
122
|
+
const [client] = useClient(event, API$1);
|
|
123
|
+
event["uidkey"] = `${event["guid"]}:${event.value.author}(${event.UserId})`; // 唯一标识
|
|
124
|
+
event["nickname"] =
|
|
125
|
+
`${event.name == "private.message.create" ? "[私信]" : ""}${event.value.author}(${event.UserId})`; // 昵称(用户名+QQ号)
|
|
126
|
+
event["user_id"] = event.UserId;
|
|
127
|
+
event["guid"] = event.ChannelId ?? event.UserId;
|
|
128
|
+
const botInfo = await client.getMe();
|
|
129
|
+
event["bot"] = {
|
|
130
|
+
nickname: botInfo[0].data.botUser.name,
|
|
131
|
+
user_id: botInfo[0].data.botUser.id,
|
|
132
|
+
};
|
|
133
|
+
event["at"] =
|
|
134
|
+
event.value.content.match(/<@!?(\d+)>/g)?.map((mention) => {
|
|
135
|
+
return {
|
|
136
|
+
type: "at",
|
|
137
|
+
data: {
|
|
138
|
+
qq: mention.match(/<@!?(\d+)>/)[1],
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
}) || [];
|
|
142
|
+
event["atBot"] = event["at"].find((item) => item.data.qq === event["bot"].id);
|
|
143
|
+
event["originalMsg"] = event.value.content; // 原始消息内容
|
|
144
|
+
event["img"] =
|
|
145
|
+
event.value.fileMeta &&
|
|
146
|
+
event.value.fileMeta.contentType?.startsWith("image/")
|
|
147
|
+
? [
|
|
148
|
+
bubbleConfig.CDN_URL ??
|
|
149
|
+
"https://bubble-oss-files.alemonjs.com/" +
|
|
150
|
+
event.value.fileMeta.url,
|
|
151
|
+
]
|
|
152
|
+
: [];
|
|
153
|
+
// if (event.value.replyToId) {
|
|
154
|
+
// const channelMessages = await client.request({
|
|
155
|
+
// method: "GET",
|
|
156
|
+
// url: `${bubbleConfig.API_URL ?? "https://api.bubble.alemonjs.com"}/channels/${event.ChannelId}/messages`,
|
|
157
|
+
// headers: {
|
|
158
|
+
// Authorization: `Bearer ${bubbleConfig.token}`,
|
|
159
|
+
// "Content-Type": "application/json",
|
|
160
|
+
// },
|
|
161
|
+
// });
|
|
162
|
+
// const replyMsg = channelMessages.find(
|
|
163
|
+
// (msg: { id: string | number }) =>
|
|
164
|
+
// msg.id.toString() === event.value.replyToId,
|
|
165
|
+
// );
|
|
166
|
+
// console.log("获取到的回复消息", replyMsg);
|
|
167
|
+
// if (replyMsg) {
|
|
168
|
+
// event["reply"] = replyMsg;
|
|
169
|
+
// }
|
|
170
|
+
// }
|
|
171
|
+
}
|
|
118
172
|
console.log("处理后的事件", JSON.stringify(event, null, 2));
|
|
119
173
|
// 常用于兼容其他框架或增强event功能
|
|
120
174
|
next();
|
|
@@ -11,7 +11,7 @@ var res = onResponse(selects, async (e, next) => {
|
|
|
11
11
|
const [message] = useMessage(e);
|
|
12
12
|
// 查看好感度
|
|
13
13
|
if (regular$1.test(e.msg)) {
|
|
14
|
-
const affections = await getAffectionLevel(e.
|
|
14
|
+
const affections = await getAffectionLevel(e.guid, e.UserName);
|
|
15
15
|
message.send(format(Text(`当前用户\`${e.UserName}\`的好感度为: ${affections}。`)));
|
|
16
16
|
return;
|
|
17
17
|
}
|
|
@@ -23,7 +23,7 @@ var res = onResponse(selects, async (e, next) => {
|
|
|
23
23
|
message.send(format(Text(`已重置所有用户的好感度。`)));
|
|
24
24
|
}
|
|
25
25
|
else {
|
|
26
|
-
await resetAffectionLevel(e.
|
|
26
|
+
await resetAffectionLevel(e.UserName);
|
|
27
27
|
message.send(format(Text(`已重置用户\`${e.UserName}\`的好感度。`)));
|
|
28
28
|
}
|
|
29
29
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { addAI, switchAI, addPrompt, deletePrompt, switchPrompt, deleteAI, affectionSwitch, getAIList, getAIConfig } from '../../config.js';
|
|
2
2
|
import { redis } from '../../redis.js';
|
|
3
|
-
import { useMessage, Text } from 'alemonjs';
|
|
3
|
+
import { getConfigValue, useMessage, Text, ImageURL } from 'alemonjs';
|
|
4
4
|
import { Regular } from 'alemonjs/utils';
|
|
5
5
|
|
|
6
|
+
const value = getConfigValue();
|
|
6
7
|
const selects = onSelects(["message.create", "private.message.create"]);
|
|
7
8
|
const regular$1 = /(\/|#)添加ai$/i;
|
|
8
9
|
const regular$2 = /(\/|#)添加ai\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)/i;
|
|
@@ -19,17 +20,19 @@ const regular$14 = /(\/|#)(开启|关闭)语音回复$/i;
|
|
|
19
20
|
const regular$15 = /(\/|#)(开启|关闭)(MCP)?工具$/i;
|
|
20
21
|
const regular$16 = /(\/|#)切换图床(.*)$/i;
|
|
21
22
|
const regular$17 = /(\/|#)修改 ?(.+) ?模型 ?(.+)$/i;
|
|
23
|
+
const regular$18 = /(\/|#)(关闭|开启)复杂(输出|回复)$/i;
|
|
22
24
|
const test = /(\/|#)测试$/i;
|
|
23
|
-
const regular = Regular.or(regular$1, regular$2, regular$3, regular$4, regular$5, regular$6, regular$7, regular$8, regular$10, regular$11, regular$13, regular$14, regular$15, regular$16, regular$17, test);
|
|
25
|
+
const regular = Regular.or(regular$1, regular$2, regular$3, regular$4, regular$5, regular$6, regular$7, regular$8, regular$10, regular$11, regular$13, regular$14, regular$15, regular$16, regular$17, regular$18, test);
|
|
24
26
|
var res = onResponse(selects, async (e, next) => {
|
|
25
27
|
// 创建
|
|
26
28
|
const [message] = useMessage(e);
|
|
27
29
|
// 测试
|
|
28
30
|
if (test.test(e.msg)) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
let img = "https://imgen.x.ai/xai-imgen/xai-tmp-imgen-597ead9b-8db9-4f43-a501-ee4f32d9b4d0.jpeg";
|
|
32
|
+
if (img.startsWith("https://imgen.x.ai")) {
|
|
33
|
+
img = img.replace("https://imgen.x.ai", value.xaiImgProxy);
|
|
34
|
+
}
|
|
35
|
+
message.send(format(Text("测试消息"), ImageURL(img)));
|
|
33
36
|
// const a = await availableTools.StableDiffusionGenerateImage({
|
|
34
37
|
// prompt: "a cat playing with a ball",
|
|
35
38
|
// });
|
|
@@ -242,6 +245,19 @@ var res = onResponse(selects, async (e, next) => {
|
|
|
242
245
|
message.send(format(Text(`已将 AI ${aiName.trim()} 的模型修改为 ${modelName.trim()} !`)));
|
|
243
246
|
return;
|
|
244
247
|
}
|
|
248
|
+
// 设置复杂输出开关
|
|
249
|
+
if (regular$18.test(e.msg)) {
|
|
250
|
+
const match = e.msg.match(regular$18);
|
|
251
|
+
if (!match) {
|
|
252
|
+
message.send(format(Text("格式错误,请按照 格式:/开启复杂输出 或 /关闭复杂输出 进行设置")));
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
const [, , action] = match;
|
|
256
|
+
const enable = action === "开启";
|
|
257
|
+
await redis.set(`chat:complex:response:${e.guid}`, enable ? "1" : "0");
|
|
258
|
+
message.send(format(Text(`已${enable ? "开启" : "关闭"}复杂输出功能 !`)));
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
245
261
|
// 无匹配则继续下一个响应器
|
|
246
262
|
next();
|
|
247
263
|
});
|