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 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
- ### alemongo安装方式
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
- release
26
+
27
+ ```sh
28
+ ## 运行
29
+ yarn dev
30
+
31
+ ## 打包
32
+ yarn build
20
33
  ```
21
34
 
22
- ## 2.配置`alemon.config.yaml`
23
- 在配置中添加该插件, 使用`ALemonDesk`只需要启用插件即可
35
+ ## 2.配置
36
+
37
+ #### 配置app
38
+ 在配置中添加该插件, 才能正常触发指令
39
+
40
+ 如果使用的是git拉取项目的方式, 就不需要配置这个
24
41
  ``` yaml
25
42
  app:
26
43
  - 'alemonjs-aichat'
27
44
  ```
28
45
 
29
- ## 3.配置redis
46
+ #### 配置redis
30
47
  `alemon.config.yaml`
31
48
  ``` yaml
32
49
  redis:
33
- host: '127.0.0.1'
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
- ...item,
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, uploadImage };
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}
@@ -1,4 +1,4 @@
1
1
  const reg = ['win32'].includes(process.platform) ? /^file:\/\/\// : /^file:\/\// ;
2
- const fileUrl = new URL('main.css-DlHMK5k-.css', import.meta.url).href.replace(reg, '');
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
- type: "function",
214
- function: {
215
- name: "StableDiffusionImageToPrompt",
216
- description: "使用 Stable Diffusion 将图片转换为提示词, 仅供绘图时使用, 其他场景请勿使用",
217
- parameters: {
218
- type: "object",
219
- properties: {
220
- image_url: {
221
- type: "string",
222
- description: "图片的URL地址,必须是你所收到的图片地址",
223
- },
224
- },
225
- required: ["image_url"],
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 };
@@ -1,11 +1,12 @@
1
1
  var title = "IA聊天插件";
2
2
  var name = "aichat";
3
- var version = "v1.0.11";
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: "添加新的AI聊天配置。"
45
+ desc: "添加新的提示词配置。"
40
46
  },
41
47
  {
42
48
  cmd: "/删除提示词 [名称]",
43
- desc: "删除已有的AI聊天配置。"
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("h2", { className: "p-4 text-lg font-semibold bg-black/30 border-b border-white/20" }, item.group.title),
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) => {
@@ -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
- // const config = getConfigValue();
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.uidkey);
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.uidkey);
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
- // uploadImageToR2("./public/image_out/1769259547338.jpg").then((url) => {
30
- // console.log("上传成功,图片URL:", url);
31
- // });
32
- message.send(format(Text("测试消息")));
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
  });