alemonjs-aichat 1.0.24 → 1.0.26

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.
@@ -161,7 +161,7 @@ const tools = [
161
161
  {
162
162
  type: "function",
163
163
  function: {
164
- name: "RunCommand",
164
+ name: "exec",
165
165
  description: "执行终端命令, 请勿执行高危险性指令, 例如rm -rf /等, 否则可能会导致服务器数据丢失或系统崩溃. 你可以使用这个工具来执行一些简单的命令, 例如查看当前目录下的文件ls, 查看系统状态top等. 当你想要执行一个命令时, 请提供具体的命令文本, 例如'ls -la'或'top -n 1'. 执行结果将会返回给你, 但请注意, 由于安全限制, 某些命令可能无法执行或返回受限的结果",
166
166
  parameters: {
167
167
  type: "object",
@@ -606,10 +606,10 @@ const availableTools = {
606
606
  },
607
607
  /**
608
608
  * 运行cmd命令
609
- * @param command 命令字符串
609
+ * @param exec 命令字符串
610
610
  * @returns 命令执行结果
611
611
  */
612
- RunCommand: async ({ command }) => {
612
+ exec: async ({ command }) => {
613
613
  const { exec } = await import('child_process');
614
614
  return new Promise((resolve) => {
615
615
  exec(command, (error, stdout, stderr) => {
@@ -51,7 +51,7 @@ const loadSkills = () => {
51
51
  // 根据name和description去重
52
52
  const uniqueSkills = allSkills.filter((skill, index, self) => index ===
53
53
  self.findIndex((s) => s.name === skill.name && s.description === skill.description));
54
- console.log(`加载技能: ${uniqueSkills.length}个`);
54
+ // console.log(`加载技能: ${uniqueSkills.length}个`);
55
55
  return uniqueSkills;
56
56
  };
57
57
  // 获取某个技能的详情, 通过技能名称匹配对应文件夹下的所有md文件,将内容一并返回
@@ -109,12 +109,12 @@ const loadExternalSkills = () => {
109
109
  const allSkills = value.skills_dir.flatMap((dir) => {
110
110
  return loadSkillsFromPath(dir);
111
111
  });
112
- console.log(`加载外部技能: ${allSkills.length}个`);
112
+ // console.log(`加载外部技能: ${allSkills.length}个`);
113
113
  return allSkills;
114
114
  }
115
115
  const skillsDir = value.skills_dir || path.join(currentDir, "../../skills");
116
116
  const skills = loadSkillsFromPath(skillsDir);
117
- console.log(`加载外部技能: ${skills.length}个`);
117
+ // console.log(`加载外部技能: ${skills.length}个`);
118
118
  return skills;
119
119
  };
120
120
 
@@ -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}.absolute{position:absolute}.relative{position:relative}.-left-4{left:-1rem}.-top-4{top:-1rem}.-z-0{z-index:0}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-4{margin-bottom:1rem}.mb-6{margin-bottom:1.5rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.box-border{box-sizing:border-box}.flex{display:flex}.grid{display:grid}.hidden{display:none}.h-0\.5{height:.125rem}.h-1\.5{height:.375rem}.h-24{height:6rem}.min-h-\[120px\]{min-height:120px}.w-1\.5{width:.375rem}.w-1\/3{width:33.333333%}.w-12{width:3rem}.w-20{width:5rem}.w-24{width:6rem}.auto-rows-auto{grid-auto-rows:auto}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-between{justify-content:space-between}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(.75rem*var(--tw-space-y-reverse));margin-top:calc(.75rem*(1 - var(--tw-space-y-reverse)))}.self-start{align-self:flex-start}.overflow-hidden{overflow:hidden}.rounded-2xl{border-radius:1rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-amber-400\/20{border-color:rgba(251,191,36,.2)}.border-amber-400\/30{border-color:rgba(251,191,36,.3)}.border-blue-500\/30{border-color:rgba(59,130,246,.3)}.border-green-500\/30{border-color:rgba(34,197,94,.3)}.border-purple-400\/30{border-color:rgba(192,132,252,.3)}.border-red-500\/20{border-color:rgba(239,68,68,.2)}.border-white\/10{border-color:hsla(0,0%,100%,.1)}.border-white\/20{border-color:hsla(0,0%,100%,.2)}.border-yellow-500\/30{border-color:rgba(234,179,8,.3)}.bg-amber-400{--tw-bg-opacity:1;background-color:rgb(251 191 36/var(--tw-bg-opacity,1))}.bg-amber-500\/10{background-color:rgba(245,158,11,.1)}.bg-amber-500\/20{background-color:rgba(245,158,11,.2)}.bg-black\/30{background-color:rgba(0,0,0,.3)}.bg-black\/60{background-color:rgba(0,0,0,.6)}.bg-blue-400{--tw-bg-opacity:1;background-color:rgb(96 165 250/var(--tw-bg-opacity,1))}.bg-blue-500\/20{background-color:rgba(59,130,246,.2)}.bg-green-500\/20{background-color:rgba(34,197,94,.2)}.bg-purple-400\/20{background-color:rgba(192,132,252,.2)}.bg-red-500\/10{background-color:rgba(239,68,68,.1)}.bg-white\/30{background-color:hsla(0,0%,100%,.3)}.bg-white\/5{background-color:hsla(0,0%,100%,.05)}.bg-yellow-500\/20{background-color:rgba(234,179,8,.2)}.bg-gradient-to-br{background-image:linear-gradient(to bottom right,var(--tw-gradient-stops))}.bg-gradient-to-r{background-image:linear-gradient(to right,var(--tw-gradient-stops))}.from-amber-300{--tw-gradient-from:#fcd34d var(--tw-gradient-from-position);--tw-gradient-to:rgba(252,211,77,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-amber-400{--tw-gradient-from:#fbbf24 var(--tw-gradient-from-position);--tw-gradient-to:rgba(251,191,36,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-gray-900\/90{--tw-gradient-from:rgba(17,24,39,.9) var(--tw-gradient-from-position);--tw-gradient-to:rgba(17,24,39,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-purple-500\/20{--tw-gradient-from:rgba(168,85,247,.2) var(--tw-gradient-from-position);--tw-gradient-to:rgba(168,85,247,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.via-yellow-200{--tw-gradient-to:hsla(53,98%,77%,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),#fef08a var(--tw-gradient-via-position),var(--tw-gradient-to)}.to-amber-300{--tw-gradient-to:#fcd34d var(--tw-gradient-to-position)}.to-black\/80{--tw-gradient-to:rgba(0,0,0,.8) var(--tw-gradient-to-position)}.to-pink-500\/20{--tw-gradient-to:rgba(236,72,153,.2) var(--tw-gradient-to-position)}.to-purple-400{--tw-gradient-to:#c084fc var(--tw-gradient-to-position)}.bg-cover{background-size:cover}.bg-clip-text{background-clip:text}.p-2{padding:.5rem}.p-4{padding:1rem}.p-5{padding:1.25rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-2\.5{padding-left:.625rem;padding-right:.625rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.py-0\.5{padding-bottom:.125rem;padding-top:.125rem}.py-1{padding-bottom:.25rem;padding-top:.25rem}.py-2{padding-bottom:.5rem;padding-top:.5rem}.py-3{padding-bottom:.75rem;padding-top:.75rem}.pl-1{padding-left:.25rem}.pt-2{padding-top:.5rem}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.italic{font-style:italic}.leading-relaxed{line-height:1.625}.tracking-wide{letter-spacing:.025em}.text-\[\#B8AE8E\]{--tw-text-opacity:1;color:rgb(184 174 142/var(--tw-text-opacity,1))}.text-amber-100{--tw-text-opacity:1;color:rgb(254 243 199/var(--tw-text-opacity,1))}.text-amber-200{--tw-text-opacity:1;color:rgb(253 230 138/var(--tw-text-opacity,1))}.text-amber-300{--tw-text-opacity:1;color:rgb(252 211 77/var(--tw-text-opacity,1))}.text-blue-300{--tw-text-opacity:1;color:rgb(147 197 253/var(--tw-text-opacity,1))}.text-green-300{--tw-text-opacity:1;color:rgb(134 239 172/var(--tw-text-opacity,1))}.text-purple-200{--tw-text-opacity:1;color:rgb(233 213 255/var(--tw-text-opacity,1))}.text-red-300{--tw-text-opacity:1;color:rgb(252 165 165/var(--tw-text-opacity,1))}.text-transparent{color:transparent}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.text-white\/40{color:hsla(0,0%,100%,.4)}.text-white\/50{color:hsla(0,0%,100%,.5)}.text-white\/60{color:hsla(0,0%,100%,.6)}.text-white\/70{color:hsla(0,0%,100%,.7)}.text-white\/80{color:hsla(0,0%,100%,.8)}.text-yellow-300{--tw-text-opacity:1;color:rgb(253 224 71/var(--tw-text-opacity,1))}.shadow-xl{--tw-shadow:0 20px 25px -5px rgba(0,0,0,.1),0 8px 10px -6px rgba(0,0,0,.1);--tw-shadow-colored:0 20px 25px -5px var(--tw-shadow-color),0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.blur-2xl{--tw-blur:blur(40px)}.blur-2xl,.drop-shadow-lg{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)}.drop-shadow-lg{--tw-drop-shadow:drop-shadow(0 10px 8px rgba(0,0,0,.04)) drop-shadow(0 4px 3px rgba(0,0,0,.1))}.backdrop-blur-md{--tw-backdrop-blur:blur(12px)}.backdrop-blur-md,.backdrop-blur-sm{-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.backdrop-blur-sm{--tw-backdrop-blur:blur(4px)}.transition-colors{transition-duration:.15s;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1)}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}.hover\:bg-white\/5:hover{background-color:hsla(0,0%,100%,.05)}@media (min-width:640px){.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (min-width:768px){.md\:block{display:block}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:border-l{border-left-width:1px}.md\:border-r-0{border-right-width:0}}
@@ -1,4 +1,4 @@
1
1
  const reg = ['win32'].includes(process.platform) ? /^file:\/\/\// : /^file:\/\// ;
2
- const fileUrl = new URL('main.css-CXQ9gyIo.css', import.meta.url).href.replace(reg, '');
2
+ const fileUrl = new URL('main.css-JBKqe5f4.css', import.meta.url).href.replace(reg, '');
3
3
 
4
4
  export { fileUrl as default };
package/lib/config.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import Redis from 'ioredis';
2
2
  import { getConfigValue } from 'alemonjs';
3
+ import { tools } from './api/aitools.js';
3
4
 
4
5
  class db {
5
6
  redis;
@@ -123,7 +124,7 @@ class db {
123
124
  async switchAI(guid, name) {
124
125
  const aiConfigStr = await this.redis.get(`ai:list:${name}`);
125
126
  if (!aiConfigStr) {
126
- console.log("切换失败, 无此模型");
127
+ console.log("切换失败, 无此AI");
127
128
  await this.setAIConfig(guid, db.defaultAIConfig);
128
129
  return null;
129
130
  }
@@ -131,13 +132,19 @@ class db {
131
132
  const currentConfig = await this.getAIConfig(guid);
132
133
  aiConfig.systemPrompt = currentConfig.systemPrompt;
133
134
  await this.setAIConfig(guid, aiConfig);
135
+ // 记录当前切换的ai名字
136
+ await this.redis.set(`ai:currentAI:${guid}`, name);
134
137
  return aiConfig;
135
138
  }
139
+ /** 获取当前使用的AI名称 */
140
+ async getCurrentAI(guid) {
141
+ return (await this.redis.get(`ai:currentAI:${guid}`)) || "";
142
+ }
136
143
  /** 切换AI的RAPI模式 */
137
144
  async switchModelRAPI(aiName, enable) {
138
145
  const aiConfigStr = await this.redis.get(`ai:list:${aiName}`);
139
146
  if (!aiConfigStr) {
140
- console.log("切换失败, 无此模型");
147
+ console.log("切换失败, 无此AI");
141
148
  return;
142
149
  }
143
150
  const aiConfig = JSON.parse(aiConfigStr);
@@ -215,6 +222,14 @@ class db {
215
222
  }
216
223
  await this.redis.del(`ai:history:${guid}`);
217
224
  }
225
+ /** 清理最后的N条聊天记录 */
226
+ async clearLastNChatHistory(guid, n) {
227
+ const history = await this.getAIChatHistory(guid);
228
+ while (history.length > n) {
229
+ history.shift();
230
+ }
231
+ await this.setAIChatHistory(guid, history);
232
+ }
218
233
  /** 获取好感度 */
219
234
  async getAffectionLevel(guid, userKey) {
220
235
  const levelStr = await this.redis.get(`ai:affection:${guid}:${userKey}`);
@@ -266,21 +281,13 @@ class db {
266
281
  }
267
282
  /** 设置语音回复开关 */
268
283
  async setTTSResponseSwitch(guid, enable) {
269
- await this.redis.set(`chat:tts:response:${guid}`, enable ? "1" : "0");
284
+ await this.redis.set(`ai:tts:response:${guid}`, enable ? "1" : "0");
270
285
  }
271
286
  /** 获取语音回复开关 */
272
287
  async getTTSResponseSwitch(guid) {
273
- const switchStr = (await this.redis.get(`chat:tts:response:${guid}`)) || "0";
288
+ const switchStr = (await this.redis.get(`ai:tts:response:${guid}`)) || "0";
274
289
  return switchStr;
275
290
  }
276
- /** 设置 bot id */
277
- async setBotID(id) {
278
- await this.redis.set("bot:id", id);
279
- }
280
- /** 获取 bot id */
281
- async getBotID() {
282
- return await this.redis.get("bot:id");
283
- }
284
291
  /** 添加图片URL */
285
292
  async addImgUrl(guid, url) {
286
293
  const id = guid ?? "global";
@@ -302,9 +309,72 @@ class db {
302
309
  async getDeepThoughtSwitch(guid) {
303
310
  return (await this.redis.get(`ai:deep_thought:switch:${guid}`)) || "0";
304
311
  }
312
+ /** 设置深度思考开关 */
305
313
  async setDeepThoughtSwitch(guid, enable) {
306
314
  await this.redis.set(`ai:deep_thought:switch:${guid}`, enable ? "1" : "0");
307
315
  }
316
+ /** 仅艾特触发 */
317
+ async getAtTriggerSwitch(guid) {
318
+ return (await this.redis.get(`ai:at_trigger:switch:${guid}`)) || "1";
319
+ }
320
+ /** 设置仅艾特触发开关 */
321
+ async setAtTriggerSwitch(guid, enable) {
322
+ await this.redis.set(`ai:at_trigger:switch:${guid}`, enable ? "1" : "0");
323
+ }
324
+ /** 禁用工具 */
325
+ async disableTool(guid, toolName) {
326
+ await this.redis.set(`ai:tool:disable:${guid}:${toolName}`, "1");
327
+ }
328
+ /** 启用工具 */
329
+ async enableTool(guid, toolName) {
330
+ await this.redis.del(`ai:tool:disable:${guid}:${toolName}`);
331
+ }
332
+ /** 获取工具状态 */
333
+ async getToolStatus(guid, toolName) {
334
+ const status = (await this.redis.get(`ai:tool:disable:${guid}:${toolName}`)) || "0";
335
+ return status;
336
+ }
337
+ /** 获取所有工具状态 */
338
+ async getAllToolStatus(guid) {
339
+ const status = {};
340
+ for (const tool of tools) {
341
+ const toolName = tool.type === "function" ? tool.function.name : "";
342
+ status[toolName] = await this.getToolStatus(guid, toolName);
343
+ }
344
+ return status;
345
+ }
346
+ // 获取所有可用工具,返回完整工具
347
+ async getAvailableTools(guid) {
348
+ const toolStatus = await this.getAllToolStatus(guid);
349
+ const availableTools = tools.filter((tool) => {
350
+ const toolName = tool.type === "function" ? tool.function.name : "";
351
+ return !toolStatus[toolName] || toolStatus[toolName] === "0";
352
+ });
353
+ return availableTools;
354
+ }
355
+ /** 装载工具 */
356
+ async loadTools(guid) {
357
+ const toolStatus = await this.getAllToolStatus(guid);
358
+ const allTools = tools.map((tool) => tool.type === "function" ? tool.function.name : "");
359
+ // 默认禁用的危险工具
360
+ const defaultDisabledTools = ["exec", "EvalCode"];
361
+ // 给工具添加默认状态
362
+ const toolsWithStatus = allTools.map((tool) => ({
363
+ name: tool,
364
+ disabled: (toolStatus[tool] && toolStatus[tool] === "1") ||
365
+ (!toolStatus[tool] && defaultDisabledTools.includes(tool)),
366
+ }));
367
+ // 存储工具状态
368
+ for (const tool of toolsWithStatus) {
369
+ if (tool.disabled) {
370
+ await this.disableTool(guid, tool.name);
371
+ }
372
+ else {
373
+ await this.enableTool(guid, tool.name);
374
+ }
375
+ }
376
+ return toolsWithStatus.map((tool) => tool.name);
377
+ }
308
378
  }
309
379
  const configValue = getConfigValue();
310
380
  const aiChatConfig = configValue.aiChat || {};
@@ -1,6 +1,6 @@
1
1
  var title = "IA聊天插件";
2
- var name = "aichat";
3
- var version = "v1.0.22";
2
+ var name = "alemonjs-aichat";
3
+ var version = "v1.0.25";
4
4
  var by = "AlemonJS";
5
5
  var list = [
6
6
  {
@@ -80,17 +80,21 @@ var list = [
80
80
  cmd: "/[开启|关闭]主动搭话",
81
81
  desc: "开启或关闭主动搭话功能。"
82
82
  },
83
+ {
84
+ cmd: "/[开启|关闭]仅艾特触发",
85
+ desc: "开启或关闭仅艾特触发功能, 避免群内有多个AI时互相干扰。"
86
+ },
83
87
  {
84
88
  cmd: "/[开启|关闭]好感度",
85
89
  desc: "开启或关闭好感度功能。"
86
90
  },
87
91
  {
88
92
  cmd: "/[开启|关闭]语音回复",
89
- desc: "[限QQ平台]开启或关闭语音回复功能。"
93
+ desc: "[限QQ平台][需部署gpt-sovits]开启或关闭语音回复功能。"
90
94
  },
91
95
  {
92
- cmd: "/[开启|关闭]MCP工具",
93
- desc: "开启或关闭MCP工具功能。部分AI模型可能不支持该功能。"
96
+ cmd: "/[开启|关闭]工具",
97
+ desc: "开启或关闭工具功能。部分AI模型可能不支持该功能。"
94
98
  },
95
99
  {
96
100
  cmd: "/[开启|关闭]复杂回复",
@@ -163,6 +167,14 @@ var list = [
163
167
  {
164
168
  cmd: "/[可以|不可以]涩涩",
165
169
  desc: "开启或关闭涩涩功能, 开启后AI可能会生成一些R18的内容, 请谨慎使用。"
170
+ },
171
+ {
172
+ cmd: "/[开启|关闭]工具[工具名]",
173
+ desc: "开启或关闭指定工具功能,部分工具可能会有安全风险,请谨慎使用。"
174
+ },
175
+ {
176
+ cmd: "/工具列表",
177
+ desc: "查看当前所有工具的状态。"
166
178
  }
167
179
  ]
168
180
  }
@@ -0,0 +1,99 @@
1
+ import React from 'react';
2
+ import fileUrl from '../../assets/main.css.js';
3
+ import help from '../../data/help.json.js';
4
+ import { LinkStyleSheet } from 'jsxp';
5
+ import fileUrl$1 from '../../assets/113574575_p0_master1200.jpg.js';
6
+
7
+ const cmds = [
8
+ { cmd: "/切换AI <AI名称>", desc: "切换当前使用的AI模型" },
9
+ { cmd: "/切换模型 <模型名称>", desc: "切换当前使用的AI模型" },
10
+ { cmd: "/<开启|关闭>仅艾特触发", desc: "开启或关闭仅艾特触发功能" },
11
+ { cmd: "/<开启|关闭>工具", desc: "开启或关闭AI工具" },
12
+ { cmd: "/<开启|关闭>复杂输出", desc: "开启或关闭复杂输出" },
13
+ { cmd: "/<开启|关闭>好感度", desc: "开启或关闭好感度系统" },
14
+ { cmd: "/清空对话", desc: "清空当前对话历史" },
15
+ ];
16
+ const StatusItem = ({ label, value, isDeep, }) => {
17
+ const getStatusStyle = () => {
18
+ if (isDeep && value === "默认") {
19
+ return "bg-yellow-500/20 text-yellow-300 border-yellow-500/30";
20
+ }
21
+ if (value) {
22
+ return "bg-green-500/20 text-green-300 border-green-500/30";
23
+ }
24
+ return "bg-red-500/10 text-red-300 border-red-500/20";
25
+ };
26
+ return (React.createElement("div", { className: "flex items-center justify-between py-2 px-2 rounded-lg bg-white/5" },
27
+ React.createElement("span", { className: " text-white/70" }, label),
28
+ React.createElement("span", { className: `px-3 py-0.5 rounded-full text-xs font-mono font-medium border ${getStatusStyle()}` }, isDeep && value === "默认" ? "默认" : value ? "开启" : "关闭")));
29
+ };
30
+ /**
31
+ * @param param0
32
+ * @returns
33
+ */
34
+ function App(data) {
35
+ return (React.createElement("html", null,
36
+ React.createElement("head", null,
37
+ React.createElement(LinkStyleSheet, { src: fileUrl })),
38
+ React.createElement("body", { className: "bg-cover p-4", style: { backgroundImage: `url(${fileUrl$1})` } },
39
+ React.createElement("div", { className: "relative mb-6" },
40
+ React.createElement("div", { className: "absolute -left-4 -top-4 w-24 h-24 bg-purple-400/20 rounded-full blur-2xl -z-0" }),
41
+ React.createElement("h2", { className: "relative text-3xl font-bold bg-gradient-to-r from-amber-300 via-yellow-200 to-amber-300 bg-clip-text text-transparent drop-shadow-lg tracking-wide" }, "AI\u914D\u7F6E\u8BE6\u60C5"),
42
+ React.createElement("div", { className: "mt-2 flex items-center gap-2" },
43
+ React.createElement("div", { className: "h-0.5 w-12 bg-gradient-to-r from-amber-400 to-purple-400 rounded-full" }),
44
+ React.createElement("div", { className: "h-0.5 w-20 bg-white/30 rounded-full" })),
45
+ React.createElement("div", { className: "flex items-center gap-2 mt-3 " },
46
+ React.createElement("span", { className: "px-2 py-0.5 bg-amber-500/20 rounded-md text-amber-200 font-mono text-xs border border-amber-400/30" }, help.name),
47
+ React.createElement("span", { className: "text-amber-300 text-xs font-mono font-medium" }, help.version),
48
+ React.createElement("span", { className: "text-white/50 text-xs" }, "\u2022"),
49
+ React.createElement("span", { className: "text-white/60 text-xs" }, "by"),
50
+ React.createElement("span", { className: "text-amber-200 text-xs font-medium tracking-wide" }, help.by))),
51
+ React.createElement("div", { className: "bg-gradient-to-br from-gray-900/90 to-black/80 backdrop-blur-md rounded-2xl border border-white/20 shadow-xl overflow-hidden mb-4 text-white" },
52
+ React.createElement("div", { className: "px-5 py-3 bg-white/5 border-b border-white/10 backdrop-blur-sm" },
53
+ React.createElement("p", { className: "text-base font-medium tracking-wide flex items-center gap-2" },
54
+ React.createElement("span", { className: "w-1.5 h-1.5 bg-blue-400 rounded-full" }),
55
+ "\u5F53\u524DAI\u914D\u7F6E")),
56
+ React.createElement("div", { className: "p-5 space-y-3" },
57
+ React.createElement("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-3" },
58
+ React.createElement(StatusItem, { label: "\u603B\u5F00\u5173", value: data.switch }),
59
+ React.createElement(StatusItem, { label: "\u5DE5\u5177", value: data.tools }),
60
+ React.createElement(StatusItem, { label: "\u590D\u6742\u8F93\u51FA", value: data.complex }),
61
+ React.createElement(StatusItem, { label: "\u597D\u611F\u5EA6\u5F00\u5173", value: data.affection }),
62
+ React.createElement(StatusItem, { label: "\u8BED\u97F3\u56DE\u590D\u5F00\u5173", value: data.tts }),
63
+ React.createElement(StatusItem, { label: "\u6DF1\u5EA6\u601D\u8003", value: data.deep, isDeep: true }),
64
+ React.createElement(StatusItem, { label: "\u4EC5\u827E\u7279\u89E6\u53D1", value: data.atTrigger })),
65
+ React.createElement("div", { className: "pt-2 border-t border-white/10" },
66
+ React.createElement("div", { className: "flex items-center justify-between py-2" },
67
+ React.createElement("span", { className: " text-white/60" }, "\u5F53\u524D\u4F7F\u7528\u6A21\u578B"),
68
+ React.createElement("span", { className: "px-3 py-1 bg-blue-500/20 rounded-full font-mono text-blue-300 border border-blue-500/30" },
69
+ data.currentAI || "未设置",
70
+ " / ",
71
+ data.model || "未设置")),
72
+ React.createElement("div", { className: "py-2" },
73
+ React.createElement("div", { className: " text-white/60 mb-1" }, "\u63D0\u793A\u8BCD"),
74
+ React.createElement("div", { className: "px-3 py-2 bg-white/5 rounded-lg text-white/80 border border-white/10" }, data.prompt || "未设置")),
75
+ React.createElement("div", { className: "py-2" },
76
+ React.createElement("div", { className: " text-white/60 mb-2" }, "AI\u5217\u8868"),
77
+ React.createElement("div", { className: "flex flex-wrap gap-2" }, data.aiList.length ? (data.aiList.map((ai, idx) => (React.createElement("span", { key: idx, className: "px-2.5 py-1 text-xs rounded-full bg-gradient-to-r from-purple-500/20 to-pink-500/20 text-purple-200 border border-purple-400/30 backdrop-blur-sm font-medium" }, ai)))) : (React.createElement("span", { className: " text-white/40 italic" }, "\u6682\u65E0AI\u914D\u7F6E"))))))),
78
+ React.createElement("div", { className: "bg-gradient-to-br from-gray-900/90 to-black/80 backdrop-blur-md rounded-2xl border border-white/20 shadow-xl overflow-hidden mb-4" },
79
+ React.createElement("div", { className: "px-5 py-3 bg-white/5 border-b border-white/10 backdrop-blur-sm" },
80
+ React.createElement("p", { className: "text-base font-semibold tracking-wide flex items-center gap-2" },
81
+ React.createElement("span", { className: "w-1.5 h-1.5 bg-amber-400 rounded-full shadow-glow" }),
82
+ React.createElement("span", { className: "text-amber-100" }, "\u63A8\u8350\u6307\u4EE4"))),
83
+ React.createElement("div", { className: "p-5" },
84
+ React.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 auto-rows-auto" },
85
+ cmds.map((item, idx) => {
86
+ const isLastInRow = (idx + 1) % 2 === 0 || idx === cmds.length - 1;
87
+ const isSecondCol = idx % 2 === 1;
88
+ return (React.createElement("div", { key: idx, className: `relative flex flex-col gap-1.5 p-4 transition-colors hover:bg-white/5
89
+ ${isSecondCol ? "md:border-l border-white/10" : ""}
90
+ ${idx >= 2 ? "border-t border-white/10" : ""}
91
+ ${isLastInRow && idx < cmds.length - 1 ? "md:border-r-0" : ""}
92
+ ` },
93
+ React.createElement("code", { className: "text-amber-300 font-mono font-semibold tracking-wide bg-amber-500/10 px-2 py-0.5 rounded-md self-start border border-amber-400/20" }, item.cmd),
94
+ React.createElement("span", { className: "text-white/80 leading-relaxed pl-1" }, item.desc)));
95
+ }),
96
+ cmds.length % 2 !== 0 && (React.createElement("div", { className: "hidden md:block relative min-h-[120px] p-4 border-t border-white/10" }))))))));
97
+ }
98
+
99
+ export { App as default };
@@ -13,16 +13,18 @@ function App() {
13
13
  React.createElement("head", null,
14
14
  React.createElement(LinkStyleSheet, { src: fileUrl })),
15
15
  React.createElement("body", { className: "bg-cover p-4", style: { backgroundImage: `url(${fileUrl$1})` } },
16
- React.createElement("h2", { className: "text-2xl text-white font-bold italic drop-shadow-md" }, help.title),
17
- React.createElement("div", { className: "flex gap-2 mb-4 text-white italic drop-shadow-md" },
18
- React.createElement("span", { className: "text-[#e8deba] " },
19
- help.name,
20
- " ",
21
- help.version),
22
- " ",
23
- React.createElement("span", { className: "text-gray-400" }, "by"),
24
- " ",
25
- React.createElement("span", { className: "text-[#e8deba] " }, help.by)),
16
+ React.createElement("div", { className: "relative mb-6" },
17
+ React.createElement("div", { className: "absolute -left-4 -top-4 w-24 h-24 bg-purple-400/20 rounded-full blur-2xl -z-0" }),
18
+ React.createElement("h2", { className: "relative text-3xl font-bold bg-gradient-to-r from-amber-300 via-yellow-200 to-amber-300 bg-clip-text text-transparent drop-shadow-lg tracking-wide" }, help.title),
19
+ React.createElement("div", { className: "mt-2 flex items-center gap-2" },
20
+ React.createElement("div", { className: "h-0.5 w-12 bg-gradient-to-r from-amber-400 to-purple-400 rounded-full" }),
21
+ React.createElement("div", { className: "h-0.5 w-20 bg-white/30 rounded-full" })),
22
+ React.createElement("div", { className: "flex items-center gap-2 mt-3 text-sm" },
23
+ React.createElement("span", { className: "px-2 py-0.5 bg-amber-500/20 rounded-md text-amber-200 font-mono text-xs border border-amber-400/30" }, help.name),
24
+ React.createElement("span", { className: "text-amber-300 text-xs font-mono font-medium" }, help.version),
25
+ React.createElement("span", { className: "text-white/50 text-xs" }, "\u2022"),
26
+ React.createElement("span", { className: "text-white/60 text-xs" }, "by"),
27
+ React.createElement("span", { className: "text-amber-200 text-xs font-medium tracking-wide" }, help.by))),
26
28
  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
29
  React.createElement("div", { className: "p-4 bg-black/30 border-b border-white/20" },
28
30
  React.createElement("p", { className: "text-lg font-semibold" }, item.group.title),
package/lib/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { redis } from './redis.js';
1
2
  import commands from './routes/commands.js';
2
3
  import middleware from './routes/middleware.js';
3
4
 
@@ -12,6 +13,20 @@ var index = defineChildren({
12
13
  logger.info({
13
14
  message: "本地测试启动",
14
15
  });
16
+ redis
17
+ .get("ai:list")
18
+ .then((value) => {
19
+ logger.info({
20
+ message: "Redis连接成功,测试键值已获取",
21
+ value: value,
22
+ });
23
+ })
24
+ .catch((error) => {
25
+ logger.error({
26
+ message: "Redis连接失败",
27
+ error: error.message,
28
+ });
29
+ });
15
30
  },
16
31
  });
17
32
 
@@ -1,13 +1,12 @@
1
1
  import { getConfigValue, useClient } from 'alemonjs';
2
2
  import { API } from '@alemonjs/onebot';
3
- import { API as API$1 } from '@alemonjs/bubble';
3
+ import redisClient from '../config.js';
4
4
 
5
- // import { API as ScbbsAPI } from "@alemonjs/scbbs";
6
5
  const selects = onSelects(["message.create", "private.message.create"]);
7
6
  // 中间件
8
7
  var mw = onMiddleware(selects, async (event, next) => {
9
8
  getConfigValue().aiChat || {};
10
- console.log("原始事件:", event.value);
9
+ // console.log("原始事件:", event.value);
11
10
  // 新增字段
12
11
  event["user_id"] = event.UserId; // 用户QQ号
13
12
  event["msg"] = event.MessageText; // 消息内容
@@ -135,55 +134,56 @@ var mw = onMiddleware(selects, async (event, next) => {
135
134
  }
136
135
  }
137
136
  }
138
- // if (event.Platform == "scbbs") {
139
- // const [client] = useClient(event, ScbbsAPI);
140
- // event["msg"] = `${event["msg"]}`; // 消息内容包含at机器人昵称
141
- // event["self_id"] = event.value.toUser.id; // 机器人ID
142
- // event["originalMsg"] = event.value.content; // 原始消息内容
143
- // event["atBot"] =
144
- // event.name === "private.message.create" && event.Platform == "scbbs";
145
- // // 处理图片消息
146
- // event["img"] = (event.value.content || [])
147
- // .filter((item: { type: string }) => item.type === "image")
148
- // .map((item: { data: { url: string } }) =>
149
- // item.data.url.replace("!/fwfh/368x238", ""),
150
- // );
151
- // // 判断是否为群管理员
152
- // event["isAdmin"] =
153
- // event.value.author.roles?.filter(
154
- // (role: { id: number }) => role.id == 1 || role.id == 2,
155
- // ).length > 0;
156
- // event["bot"] = {
157
- // nickname: event.value.toUser.nickname,
158
- // user_id: event.value.toUser.id,
159
- // }; // 机器人信息
160
- // event["raw_message"] = event.value.content
161
- // .map((item: { type: string; data: any }) => {
162
- // if (item.type === "text") {
163
- // return item.data.text;
164
- // } else if (item.type === "image") {
165
- // return `[CQ:image,${item.data.url}]`;
166
- // } else if (item.type === "at") {
167
- // return `[CQ:at,qq=${item.data.id}]`;
168
- // } else {
169
- // return `[${item.type.toUpperCase()}]`;
170
- // }
171
- // })
172
- // .join("");
173
- // if (event.replyId && event.replyId !== "-1") {
174
- // const replyMsg = await client.getMessage(event.replyId);
175
- // console.log("获取回复", replyMsg);
176
- // if (replyMsg) {
177
- // event["reply"] = replyMsg[0].data;
178
- // if (replyMsg[0].data.fromUserId == event.self_id) {
179
- // event["atBot"] = true;
180
- // }
181
- // }
182
- // }
183
- // }
137
+ if (event.Platform == "scbbs") {
138
+ const ScbbsAPI = await import('@alemonjs/scbbs');
139
+ const [client] = useClient(event, ScbbsAPI);
140
+ event["msg"] = `${event["msg"]}`; // 消息内容包含at机器人昵称
141
+ event["self_id"] = event.value.toUser.id; // 机器人ID
142
+ event["originalMsg"] = event.value.content; // 原始消息内容
143
+ event["atBot"] =
144
+ event.name === "private.message.create" && event.Platform == "scbbs";
145
+ // 处理图片消息
146
+ event["img"] = (event.value.content || [])
147
+ .filter((item) => item.type === "image")
148
+ .map((item) => item.data.url.replace("!/fwfh/368x238", ""));
149
+ // 判断是否为群管理员
150
+ event["isAdmin"] =
151
+ event.value.author.roles?.filter((role) => role.id == 1 || role.id == 2).length > 0;
152
+ event["bot"] = {
153
+ nickname: event.value.toUser.nickname,
154
+ user_id: event.value.toUser.id,
155
+ }; // 机器人信息
156
+ event["raw_message"] = event.value.content
157
+ .map((item) => {
158
+ if (item.type === "text") {
159
+ return item.data.text;
160
+ }
161
+ else if (item.type === "image") {
162
+ return `[CQ:image,${item.data.url}]`;
163
+ }
164
+ else if (item.type === "at") {
165
+ return `[CQ:at,qq=${item.data.id}]`;
166
+ }
167
+ else {
168
+ return `[${item.type.toUpperCase()}]`;
169
+ }
170
+ })
171
+ .join("");
172
+ if (event.replyId && event.replyId !== "-1") {
173
+ const replyMsg = await client.getMessage(event.replyId);
174
+ console.log("获取回复", replyMsg);
175
+ if (replyMsg) {
176
+ event["reply"] = replyMsg[0].data;
177
+ if (replyMsg[0].data.fromUserId == event.self_id) {
178
+ event["atBot"] = true;
179
+ }
180
+ }
181
+ }
182
+ }
184
183
  if (event.Platform == "bubble") {
184
+ const BubbleAPI = await import('@alemonjs/bubble');
185
185
  const CDN_URL = "https://bubble-oss-files.alemonjs.com/";
186
- const [client] = useClient(event, API$1);
186
+ const [client] = useClient(event, BubbleAPI);
187
187
  event["uidkey"] = `${event["guid"]}:${event.value.author}(${event.UserId})`; // 唯一标识
188
188
  event["nickname"] =
189
189
  `${event.name == "private.message.create" ? "[私信]" : ""}${event.value.author}(${event.UserId})`; // 昵称(用户名+QQ号)
@@ -258,8 +258,12 @@ var mw = onMiddleware(selects, async (event, next) => {
258
258
  event["reply"] = replyData;
259
259
  }
260
260
  }
261
- // console.log("处理后的事件", JSON.stringify(event, null, 2));
262
- // 常用于兼容其他框架或增强event功能
261
+ const atTriggerSwitch = await redisClient.getAtTriggerSwitch(event.ChannelId ?? event.UserId);
262
+ if (atTriggerSwitch === "1" && event.Platform !== "testone") {
263
+ if (!event["atBot"] && event.name !== "private.message.create") {
264
+ return;
265
+ }
266
+ }
263
267
  next();
264
268
  });
265
269
 
package/lib/redis.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { getConfigValue } from 'alemonjs';
2
2
  import Redis from 'ioredis';
3
3
 
4
- const value = getConfigValue().aiChat || {};
4
+ const value = getConfigValue().aiChat || getConfigValue() || {};
5
5
  const redisConfig = {
6
6
  host: value.redis?.host ?? "localhost",
7
7
  port: value.redis?.port ?? 6379,
@@ -1,5 +1,7 @@
1
1
  import redisClient from '../../config.js';
2
- import { useMessage, Text } from 'alemonjs';
2
+ import App from '../../image/conponent/AiConfig.js';
3
+ import { useMessage, Image, Text } from 'alemonjs';
4
+ import { renderComponentIsHtmlToBuffer } from 'jsxp';
3
5
 
4
6
  const selects = onSelects(["message.create", "private.message.create"]);
5
7
  // inline regex used directly in condition checks
@@ -15,9 +17,29 @@ var res = onResponse(selects, async (e, next) => {
15
17
  const isOpenTTSReply = await redisClient.getTTSResponseSwitch(e.guid); // TTS回复开关
16
18
  const toolsIsOpen = await redisClient.getToolSwitch(e.guid); // 工具开关
17
19
  const deepThinkingIsOpen = await redisClient.getDeepThoughtSwitch(e.guid); // 深度思考开关
20
+ const atTriggerSwitch = await redisClient.getAtTriggerSwitch(e.guid); // 仅艾特触发
21
+ const currentAI = await redisClient.getCurrentAI(e.guid); // 当前AI名称
18
22
  const aiList = await redisClient.getAIList();
19
23
  // 发送消息
20
- message.send(format(Text(`当前AI配置:\n`), Text(`总开关: ${config.model != "" ? "开启" : "关闭"}\n`), Text(`工具: ${toolsIsOpen == "1" ? "开启" : "关闭"}\n`), Text(`复杂输出: ${complexResponse == "1" ? "开启" : "关闭"}\n`), Text(`好感度开关: ${affectionIsOpen == "1" ? "开启" : "关闭"}\n`), Text(`TTS回复开关: ${isOpenTTSReply == "1" ? "开启" : "关闭"}\n`), Text(`深度思考: ${deepThinkingIsOpen == "1" ? "默认" : "关闭"}\n`), Text(`模型: ${config.model || "未设置"}\n`), Text(`提示词: ${config.systemPrompt || "未设置"}\n`), Text(`AI数量: ${aiList.length ? aiList.length : "暂无AI配置"}`)));
24
+ const img = await renderComponentIsHtmlToBuffer(App, {
25
+ switch: config.model != "",
26
+ tools: toolsIsOpen == "1",
27
+ complex: complexResponse == "1",
28
+ affection: affectionIsOpen == "1",
29
+ tts: isOpenTTSReply == "1",
30
+ deep: deepThinkingIsOpen == "1",
31
+ model: config.model,
32
+ prompt: config.systemPrompt,
33
+ atTrigger: atTriggerSwitch == "1",
34
+ aiList: aiList,
35
+ currentAI: currentAI,
36
+ });
37
+ if (img) {
38
+ message.send(format(Image(img)));
39
+ }
40
+ else {
41
+ message.send(format(Text(`当前AI配置:\n`), Text(`总开关: ${config.model != "" ? "开启" : "关闭"}\n`), Text(`工具: ${toolsIsOpen == "1" ? "开启" : "关闭"}\n`), Text(`复杂输出: ${complexResponse == "1" ? "开启" : "关闭"}\n`), Text(`好感度开关: ${affectionIsOpen == "1" ? "开启" : "关闭"}\n`), Text(`TTS回复开关: ${isOpenTTSReply == "1" ? "开启" : "关闭"}\n`), Text(`深度思考: ${deepThinkingIsOpen == "1" ? "默认" : "关闭"}\n`), Text(`仅艾特触发: ${atTriggerSwitch == "1" ? "开启" : "关闭"}\n`), Text(`模型: ${config.model || "未设置"}\n`), Text(`提示词: ${config.systemPrompt || "未设置"}\n`), Text(`当前AI: ${currentAI || "未设置"}\n`), Text(`AI数量: ${aiList.length ? aiList.length : "暂无AI配置"}`)));
42
+ }
21
43
  }
22
44
  // 查看AI列表
23
45
  if (/^(\/|#)ai列表$/i.test(e.msg)) {
@@ -1,13 +1,13 @@
1
1
  import { useMessage, Image, Text } from 'alemonjs';
2
- import { renderComponentToBuffer } from 'jsxp';
3
- import App from '../../image/conponent/help.js';
2
+ import { renderComponentIsHtmlToBuffer } from 'jsxp';
3
+ import App from '../../image/conponent/Help.js';
4
4
  import redisClient from '../../config.js';
5
5
 
6
6
  const selects = onSelects(["message.create", "private.message.create"]);
7
7
  var res = onResponse(selects, async (e) => {
8
8
  // 创建
9
9
  const [message] = useMessage(e);
10
- const img = await renderComponentToBuffer("help", App, {});
10
+ const img = await renderComponentIsHtmlToBuffer(App, {});
11
11
  if (img)
12
12
  message.send(format(Image(img)));
13
13
  const config = await redisClient.getAIConfig(e.guid);
@@ -1,10 +1,8 @@
1
1
  import { availableTools } from '../../api/aitools.js';
2
- import { loadSkills } from '../../api/loadSkill.js';
3
2
  import redisClient from '../../config.js';
4
3
  import { useMessage, Text, getConfigValue } from 'alemonjs';
5
- import { log } from 'console';
6
4
 
7
- getConfigValue().aiChat || {};
5
+ const value = getConfigValue().aiChat || {};
8
6
  const selects = onSelects(["message.create", "private.message.create"]);
9
7
  // inline regex checks are used in-place; keep `test` as shorthand
10
8
  const test = /(\/|#)测试$/i;
@@ -13,11 +11,11 @@ var res = onResponse(selects, async (e, next) => {
13
11
  const [message] = useMessage(e);
14
12
  // 测试
15
13
  if (test.test(e.msg)) {
16
- // 1. 获取技能列表(简要信息)
17
- const skills = loadSkills();
18
- console.log(skills);
19
- log(JSON.stringify(e, null, 2));
20
14
  message.send(format(Text("测试消息")));
15
+ // 1. 获取技能列表(简要信息)
16
+ // const skills = loadSkills();
17
+ // console.log(skills);
18
+ // log(JSON.stringify(e, null, 2));
21
19
  // let img =
22
20
  // "https://imgen.x.ai/xai-imgen/xai-tmp-imgen-597ead9b-8db9-4f43-a501-ee4f32d9b4d0.jpeg";
23
21
  // if (img.startsWith("https://imgen.x.ai")) {
@@ -285,8 +283,11 @@ var res = onResponse(selects, async (e, next) => {
285
283
  return;
286
284
  }
287
285
  const [, , action] = match;
288
- console.log("涩涩action", action);
289
286
  const enable = /(关闭|禁止|不可以|不允许)/.test(action) ? false : true;
287
+ if (!value.baidu?.ak && !value.baidu?.sk && !enable) {
288
+ message.send(format(Text("未配置百度AI审核接口,无法停止涩涩!请在配置文件中添加百度AI的AK和SK。")));
289
+ return;
290
+ }
290
291
  await redisClient.setR18Switch(e.guid, enable);
291
292
  message.send(format(Text(`已${enable ? "允许" : "禁止"}涩涩功能 !`)));
292
293
  return;
@@ -342,7 +343,51 @@ var res = onResponse(selects, async (e, next) => {
342
343
  message.send(format(Text(`已${enable ? "开启" : "关闭"} AI ${aiName.trim()} 的 RAPI 模式 !`)));
343
344
  }
344
345
  }
345
- // 无匹配则继续下一个响应器
346
+ // 设置仅艾特触发
347
+ if (/(\/|#)(开启|关闭)仅艾特触发$/i.test(e.msg)) {
348
+ const match = e.msg.match(/(\/|#)(开启|关闭)仅艾特触发$/i);
349
+ if (!match) {
350
+ message.send(format(Text("格式错误,请按照 格式:/开启仅艾特触发 或 /关闭仅艾特触发 进行设置")));
351
+ return;
352
+ }
353
+ const [, , action] = match;
354
+ const enable = action === "开启";
355
+ await redisClient.setAtTriggerSwitch(e.guid, enable);
356
+ message.send(format(Text(`已${enable ? "开启" : "关闭"}仅艾特触发功能 !`)));
357
+ return;
358
+ }
359
+ // 获取所有ai工具列表
360
+ if (/(\/|#)工具列表$/i.test(e.msg)) {
361
+ const tools = await redisClient.getAllToolStatus(e.guid);
362
+ const toolList = Object.entries(tools)
363
+ .map(([tool, status]) => `${tool}: ${status === "0" ? "开启" : "关闭"}`)
364
+ .join("\n");
365
+ message.send(format(Text(`当前工具列表及状态:\n${toolList}`)));
366
+ return;
367
+ }
368
+ // 控制工具开关
369
+ if (/(\/|#)(开启|关闭)工具 (.*)$/i.test(e.msg)) {
370
+ if (!e.IsMaster || String(e.UserId) != "3501869534") {
371
+ return;
372
+ }
373
+ const match = e.msg.match(/(\/|#)(开启|关闭)工具 (.*)$/i);
374
+ if (!match) {
375
+ message.send(format(Text("格式错误,请按照 格式:/开启工具 <工具名称> 或 /关闭工具 <工具名称> 进行设置")));
376
+ return;
377
+ }
378
+ const [, , action, toolName] = match;
379
+ const enable = action === "开启";
380
+ if (enable) {
381
+ redisClient.enableTool(e.guid, toolName.trim());
382
+ }
383
+ else {
384
+ {
385
+ redisClient.disableTool(e.guid, toolName.trim());
386
+ }
387
+ }
388
+ message.send(format(Text(`已${enable ? "开启" : "关闭"}工具 ${toolName.trim()} !`)));
389
+ return;
390
+ }
346
391
  next();
347
392
  });
348
393
 
@@ -6,7 +6,7 @@ import { log } from 'console';
6
6
  import OpenAi from 'openai';
7
7
  import { TTSClient } from '../../api/tts.js';
8
8
  import { getTimeString } from '../../api/format.js';
9
- import { tools, availableTools } from '../../api/aitools.js';
9
+ import { availableTools, tools } from '../../api/aitools.js';
10
10
  import { loadSkills } from '../../api/loadSkill.js';
11
11
  import redisClient from '../../config.js';
12
12
  import fileUrl from '../../assets/error.png.js';
@@ -272,7 +272,7 @@ ${e.ClientError ? `当前错误信息:${e.ClientError}` : ""}
272
272
  messages: messages,
273
273
  };
274
274
  if (isTool === "1") {
275
- createParams["tools"] = tools;
275
+ createParams["tools"] = await redisClient.getAvailableTools(e.guid);
276
276
  createParams["tool_choice"] = "auto";
277
277
  }
278
278
  if (deepThoughtSwitch === "0") {
@@ -364,8 +364,16 @@ ${e.ClientError ? `当前错误信息:${e.ClientError}` : ""}
364
364
  }
365
365
  else {
366
366
  try {
367
- // 执行本地函数
368
- functionResult = await functionToCall(functionArgs);
367
+ if ((await redisClient.getToolStatus(e.guid, functionName)) === "1") {
368
+ console.error(`工具被禁用:${functionName}`);
369
+ functionResult = {
370
+ ok: false,
371
+ error: `工具被禁用:${functionName}`,
372
+ };
373
+ }
374
+ else {
375
+ functionResult = await functionToCall(functionArgs);
376
+ }
369
377
  }
370
378
  catch (err) {
371
379
  console.error(`工具执行失败:${functionName}`, err);
@@ -512,7 +520,9 @@ ${e.ClientError ? `当前错误信息:${e.ClientError}` : ""}
512
520
  }
513
521
  catch (error) {
514
522
  console.error("AI回复出错", error);
515
- message.send(format(Text("AI回复出错了呢~\n" + error)));
523
+ // 清理聊天记录
524
+ await redisClient.clearLastNChatHistory(e.guid, 5);
525
+ message.send(format(Text("AI回复出错了, 已清理聊天记录~\n" + error)));
516
526
  next();
517
527
  }
518
528
  });
@@ -20,7 +20,7 @@ var commands = defineRouter([
20
20
  },
21
21
  // setting (包含大量子命令)
22
22
  {
23
- regular: Regular.or(/(\/|#)添加ai$/i, /(\/|#)添加ai\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)/i, /(\/|#)(切换)(ai|配置) ?(.*)/i, /(\/|#)添加提示词$/i, /(\/|#)添加提示词 (\S+) ([\s\S]+)/i, /(\/|#)(删除)(提示词|提示词列表) ?(.*)/i, /(\/|#)(切换|设置)提示词 ?(.*)$/i, /(\/|#)清空提示词$/i, /(\/|#)删除ai\s(\S+)$/i, /(\/|#)(开启|启用|关闭|禁用)好感度$/i, /(\/|#)(开启|关闭)(全部)?聊天$/i, /(\/|#)(开启|关闭)语音回复$/i, /(\/|#)(开启|关闭)(MCP)?工具$/i, /(\/|#)切换图床(.*)$/i, /(\/|#)修改 ?(.+) ?模型 ?(.+)$/i, /(\/|#)(关闭|开启)复杂(输出|回复)$/i, /(\/|#)(关闭|禁止|不可以|不允许|开启|可以|允许)(涩涩|色色)$/i, /(\/|#)切换模型(.*)$/i, /(\/|#)(关闭|开启)主动(搭话|回复)$/i, /(\/|#)(开启|关闭)深度思考$/i, /(\/|#)(设置|修改)(ai)?(.*)rapi模式(开启|关闭)$/i, /(\/|#)测试$/i),
23
+ regular: Regular.or(/(\/|#)添加ai$/i, /(\/|#)添加ai\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)/i, /(\/|#)(切换)(ai|配置) ?(.*)/i, /(\/|#)添加提示词$/i, /(\/|#)添加提示词 (\S+) ([\s\S]+)/i, /(\/|#)(删除)(提示词|提示词列表) ?(.*)/i, /(\/|#)(切换|设置)提示词 ?(.*)$/i, /(\/|#)清空提示词$/i, /(\/|#)删除ai\s(\S+)$/i, /(\/|#)(开启|启用|关闭|禁用)好感度$/i, /(\/|#)(开启|关闭)(全部)?聊天$/i, /(\/|#)(开启|关闭)语音回复$/i, /(\/|#)(开启|关闭)(MCP)?工具$/i, /(\/|#)切换图床(.*)$/i, /(\/|#)修改 ?(.+) ?模型 ?(.+)$/i, /(\/|#)(关闭|开启)复杂(输出|回复)$/i, /(\/|#)(关闭|禁止|不可以|不允许|开启|可以|允许)(涩涩|色色)$/i, /(\/|#)切换模型(.*)$/i, /(\/|#)(关闭|开启)主动(搭话|回复)$/i, /(\/|#)(开启|关闭)深度思考$/i, /(\/|#)(设置|修改)(ai)?(.*)rapi模式(开启|关闭)$/i, /(\/|#)(开启|关闭)仅艾特触发$/i, /(\/|#)工具列表$/i, /(\/|#)(开启|关闭)工具 (.*)$/i, /(\/|#)测试$/i),
24
24
  handler: lazy(() => import('../response/setting/res.js')),
25
25
  },
26
26
  // affection
@@ -38,11 +38,6 @@ var commands = defineRouter([
38
38
  regular: /.*/,
39
39
  handler: lazy(() => import('../response/zreply/res.js')),
40
40
  },
41
- // fallback: zreply - 最低优先级,处理普通对话
42
- // {
43
- // regular: /.*/,
44
- // handler: lazy(() => import("../response/zreply/rapi.res")),
45
- // },
46
41
  ]);
47
42
 
48
43
  export { commands as default };
package/lib/s3.js CHANGED
@@ -2,7 +2,6 @@ import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
2
2
  import fs from 'fs/promises';
3
3
  import path from 'path';
4
4
  import mime from 'mime-types';
5
- import { redis } from './redis.js';
6
5
  import sharp from 'sharp';
7
6
  import { getConfigValue } from 'alemonjs';
8
7
 
@@ -180,23 +179,13 @@ const uploadImageToR2 = async (source, key, processOptions = {}) => {
180
179
  const { buffer: originalBuffer, contentType: originalContentType, fileName: originalFileName, } = await getImageBuffer(source);
181
180
  // 2. 预处理图片(转换格式、压缩、处理动图)
182
181
  const { buffer: processedBuffer, contentType, format, wasAnimated, } = await preprocessImage(originalBuffer, originalContentType, processOptions);
183
- // 3. 获取R2配置
184
- const config = (await redis.get(`chat:image:bed`)) || "R2";
185
- const R2_CONFIG = config == "R2"
186
- ? {
187
- accessKeyId: "f8f0d360d3156a1667c527c5ea4b18d7",
188
- secretAccessKey: "365f44cf6079054ad99923dd32966d0aba4e4d2d24726cadcd0102a7c2e7bc08",
189
- bucket: "images",
190
- publicDomain: "https://r2img.ccxk.top",
191
- endpoint: "https://b24a196dc2ee578845bb555d53812508.r2.cloudflarestorage.com",
192
- }
193
- : {
194
- accessKeyId: "pCLV51yDLbntzsfUkgBSjo5fJSor3nr5",
195
- secretAccessKey: "5672e62135be31808f1afbd35862839e",
196
- bucket: "schub-image",
197
- publicDomain: "https://s3img.suancaixianyu.cn",
198
- endpoint: "https://s3.api.upyun.com",
199
- };
182
+ const R2_CONFIG = {
183
+ accessKeyId: "f8f0d360d3156a1667c527c5ea4b18d7",
184
+ secretAccessKey: "365f44cf6079054ad99923dd32966d0aba4e4d2d24726cadcd0102a7c2e7bc08",
185
+ bucket: "images",
186
+ publicDomain: "https://r2img.ccxk.top",
187
+ endpoint: "https://b24a196dc2ee578845bb555d53812508.r2.cloudflarestorage.com",
188
+ };
200
189
  // 4. 创建S3客户端
201
190
  const s3Client = new S3Client({
202
191
  region: "auto",
package/package.json CHANGED
@@ -1,63 +1,59 @@
1
- {
2
- "name": "alemonjs-aichat",
3
- "version": "1.0.24",
4
- "description": "alemonjs-aichat",
5
- "author": "suancaixianyu",
6
- "license": "MIT",
7
- "main": "lib/index.js",
8
- "type": "module",
9
- "scripts": {
10
- "dev": "npx lvy dev",
11
- "build": "npx lvy build",
12
- "review": "npx alemonc start",
13
- "start": "npx pm2 startOrRestart pm2.config.cjs",
14
- "stop": "npx pm2 stop pm2.config.cjs",
15
- "delete": "npx pm2 delete pm2.config.cjs",
16
- "update": "npm publish"
17
- },
18
- "devDependencies": {
19
- "@alemonjs/bubble": "^2.1.8",
20
- "@alemonjs/db": "^0.0.16",
21
- "@alemonjs/onebot": "^2.1.14",
22
- "@types/node": "^22",
23
- "alemonjs": "2.1.56",
24
- "cssnano": "^7",
25
- "jsxp": "^1.3.0",
26
- "lvyjs": "^0.2.25",
27
- "pm2": "^5",
28
- "tailwindcss": "3"
29
- },
30
- "export": {
31
- ".": "./lib/index.js",
32
- "./package": "./package.json",
33
- "./desktop": "./lib/desktop.js"
34
- },
35
- "exports": {
36
- ".": "./lib/index.js",
37
- "./package": "./package.json",
38
- "./desktop": "./lib/desktop.js"
39
- },
40
- "publishConfig": {
41
- "registry": "https://registry.npmjs.org",
42
- "access": "public"
43
- },
44
- "repository": {
45
- "type": "gitee",
46
- "url": "https://gitee.com/suancaixianyu/alemonjs-aichat.git"
47
- },
48
- "keywords": [
49
- "alemonjs"
50
- ],
51
- "bugs": "https://gitee.com/suancaixianyu/alemonjs-aichat/issues",
52
- "dependencies": {
53
- "@ai-sdk/xai": "^3.0.60",
54
- "@aws-sdk/client-s3": "^3.975.0",
55
- "@aws-sdk/s3-request-presigner": "^3.975.0",
56
- "ai": "^6.0.105",
57
- "alemonjs-aichat": "^1.0.23",
58
- "mime-types": "^3.0.2",
59
- "openai": "^6.34.0",
60
- "sharp": "^0.34.5",
61
- "zod": "^4.3.6"
62
- }
1
+ {
2
+ "name": "alemonjs-aichat",
3
+ "version": "1.0.26",
4
+ "description": "alemonjs-aichat",
5
+ "author": "suancaixianyu",
6
+ "license": "MIT",
7
+ "main": "lib/index.js",
8
+ "type": "module",
9
+ "scripts": {
10
+ "dev": "npx lvy dev",
11
+ "build": "npx lvy build",
12
+ "review": "npx alemonc start",
13
+ "start": "npx pm2 startOrRestart pm2.config.cjs",
14
+ "stop": "npx pm2 stop pm2.config.cjs",
15
+ "delete": "npx pm2 delete pm2.config.cjs",
16
+ "update": "npm publish"
17
+ },
18
+ "devDependencies": {
19
+ "@alemonjs/bubble": "^2.1.10",
20
+ "@alemonjs/db": "^0.0.16",
21
+ "@alemonjs/onebot": "^2.1.14",
22
+ "@types/node": "^22",
23
+ "alemonjs": "2.1.57",
24
+ "cssnano": "^7",
25
+ "jsxp": "^1.3.0",
26
+ "lvyjs": "^0.2.25",
27
+ "pm2": "^5",
28
+ "tailwindcss": "3"
29
+ },
30
+ "export": {
31
+ ".": "./lib/index.js",
32
+ "./package": "./package.json",
33
+ "./desktop": "./lib/desktop.js"
34
+ },
35
+ "exports": {
36
+ ".": "./lib/index.js",
37
+ "./package": "./package.json",
38
+ "./desktop": "./lib/desktop.js"
39
+ },
40
+ "publishConfig": {
41
+ "registry": "https://registry.npmjs.org",
42
+ "access": "public"
43
+ },
44
+ "workspaces": [
45
+ "packages/*"
46
+ ],
47
+ "dependencies": {
48
+ "@ai-sdk/xai": "^3.0.60",
49
+ "@aws-sdk/client-s3": "^3.975.0",
50
+ "@aws-sdk/s3-request-presigner": "^3.975.0",
51
+ "ai": "^6.0.105",
52
+ "alemonjs-aichat": "^1.0.25",
53
+ "mime-types": "^3.0.2",
54
+ "node-fetch": "^3.3.2",
55
+ "openai": "^6.34.0",
56
+ "sharp": "^0.34.5",
57
+ "zod": "^4.3.6"
58
+ }
63
59
  }