alemonjs-aichat 1.0.0

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 ADDED
@@ -0,0 +1,18 @@
1
+
2
+ ## 安装
3
+ - alemongo安装方式
4
+ 地址
5
+ ```
6
+ https://gitee.com/suancaixianyu/alemonjs-aichat.git
7
+ ```
8
+
9
+ 分支
10
+ ```
11
+ release
12
+ ```
13
+
14
+ - npm安装方式
15
+
16
+
17
+ ## 使用
18
+ 指令`/ai帮助`查看使用方法
package/lib/config.js ADDED
@@ -0,0 +1,162 @@
1
+ import { redis } from './redis.js';
2
+
3
+ const systemPrompt = ``;
4
+ const defaultAIConfig = {
5
+ host: "",
6
+ key: "",
7
+ model: "",
8
+ temperature: 0.7,
9
+ max_tokens: 150,
10
+ systemPrompt: systemPrompt,
11
+ };
12
+ const getAIConfig = async (guid) => {
13
+ let id = guid ?? "global";
14
+ console.log("获取", guid);
15
+ const userConfigStr = await redis.get(`ai:config:${id}`);
16
+ if (userConfigStr) {
17
+ const userConfig = JSON.parse(userConfigStr);
18
+ return userConfig;
19
+ }
20
+ redis.set(`ai:config:${id}`, JSON.stringify(defaultAIConfig));
21
+ return defaultAIConfig;
22
+ };
23
+ const getAIList = async () => {
24
+ const keys = await redis.keys("ai:list:*");
25
+ return keys.map((key) => key.replace("ai:list:", ""));
26
+ };
27
+ const addAI = async (guid, name, host, key, model) => {
28
+ const defaultConfig = await getAIConfig();
29
+ defaultConfig.host = host;
30
+ defaultConfig.key = key;
31
+ defaultConfig.model = model;
32
+ await redis.set(`ai:list:${name}`, JSON.stringify(defaultConfig));
33
+ await switchAI(guid, name);
34
+ };
35
+ const switchAI = async (guid, name) => {
36
+ const aiConfigStr = await redis.get(`ai:list:${name}`);
37
+ if (!aiConfigStr) {
38
+ console.log("切换失败, 无此模型");
39
+ return null;
40
+ }
41
+ const aiConfig = JSON.parse(aiConfigStr);
42
+ await redis.set(`ai:config:${guid}`, aiConfigStr);
43
+ return aiConfig;
44
+ };
45
+ const getPromptList = async () => {
46
+ const keys = await redis.keys("ai:prompt:*");
47
+ return keys.map((key) => key.replace("ai:prompt:", ""));
48
+ };
49
+ const addPrompt = async (name, content) => {
50
+ return await redis.set(`ai:prompt:${name}`, content);
51
+ };
52
+ const deletePrompt = async (name) => {
53
+ return await redis.del(`ai:prompt:${name}`);
54
+ };
55
+ const switchPrompt = async (guid, name) => {
56
+ if (name === "") {
57
+ const config = await getAIConfig(guid);
58
+ config.systemPrompt = "";
59
+ await redis.set(`ai:config:${guid}`, JSON.stringify(config));
60
+ return systemPrompt;
61
+ }
62
+ const prompt = await redis.get(`ai:prompt:${name}`);
63
+ if (!prompt) {
64
+ console.log("切换提示词失败, 无此提示词");
65
+ return null;
66
+ }
67
+ const config = await getAIConfig(guid);
68
+ config.systemPrompt = prompt;
69
+ await redis.set(`ai:config:${guid}`, JSON.stringify(config));
70
+ return prompt;
71
+ };
72
+ const getAIChatHistory = async (guid) => {
73
+ const historyStr = await redis.get(`ai:history:${guid}`);
74
+ if (historyStr) {
75
+ return JSON.parse(historyStr);
76
+ }
77
+ return [];
78
+ };
79
+ const addAIChatHistory = async (guid, role, content) => {
80
+ const history = await getAIChatHistory(guid);
81
+ // 处理messages, 不要超过15条,超过就删除最早的
82
+ while (history.length >= 20) {
83
+ history.shift();
84
+ }
85
+ history.push({ role, content });
86
+ await redis.set(`ai:history:${guid}`, JSON.stringify(history));
87
+ return history;
88
+ };
89
+ const clearAIChatHistory = async (guid) => {
90
+ return await redis.del(`ai:history:${guid}`);
91
+ };
92
+ /**
93
+ * 获取好感度等级
94
+ * @param userKey 用户标识
95
+ * @returns
96
+ */
97
+ const getAffectionLevel = async (userKey) => {
98
+ const levelStr = await redis.get(`ai:affection:${userKey}`);
99
+ if (levelStr) {
100
+ return parseInt(levelStr, 10);
101
+ }
102
+ return 0;
103
+ };
104
+ const getAffectionLevelAll = async (guid) => {
105
+ const keys = await redis.keys(`ai:affection:${guid}:*`);
106
+ const levels = {};
107
+ for (const key of keys) {
108
+ const levelStr = await redis.get(key);
109
+ if (levelStr) {
110
+ const userKey = key.replace(`ai:affection:${guid}:`, "");
111
+ levels[userKey] = parseInt(levelStr, 10);
112
+ }
113
+ }
114
+ return levels;
115
+ };
116
+ /**
117
+ * 设置好感度等级
118
+ * @param userKey 用户标识
119
+ * @param level 好感度等级
120
+ * @returns
121
+ */
122
+ const setAffectionLevel = async (userKey, level) => {
123
+ return await redis.set(`ai:affection:${userKey}`, level.toString());
124
+ };
125
+ /**
126
+ * 增加好感度等级
127
+ * @param userKey 用户标识
128
+ * @param increment 增加的等级
129
+ * @returns
130
+ */
131
+ const incrementAffectionLevel = async (userKey, increment) => {
132
+ const currentLevel = await getAffectionLevel(userKey);
133
+ const newLevel = currentLevel + increment;
134
+ await setAffectionLevel(userKey, newLevel);
135
+ return newLevel;
136
+ };
137
+ /** * 重置好感度等级
138
+ * @param userKey 用户标识
139
+ * @returns
140
+ */
141
+ const resetAffectionLevel = async (userKey) => {
142
+ return await redis.del(`ai:affection:${userKey}`);
143
+ };
144
+ /** 重置所有用户好感度等级
145
+ * @param guid 频道标识
146
+ * @returns
147
+ */
148
+ const resetAllAffectionLevels = async () => {
149
+ const keys = await redis.keys(`ai:affection:*`);
150
+ for (const key of keys) {
151
+ await redis.del(key);
152
+ }
153
+ };
154
+ const getBotID = async () => {
155
+ const botid = await redis.get("bot:id");
156
+ return String(botid) || "794161769";
157
+ };
158
+ const setBotID = async (id) => {
159
+ return await redis.set("bot:id", id);
160
+ };
161
+
162
+ export { addAI, addAIChatHistory, addPrompt, clearAIChatHistory, defaultAIConfig, deletePrompt, getAIChatHistory, getAIConfig, getAIList, getAffectionLevel, getAffectionLevelAll, getBotID, getPromptList, incrementAffectionLevel, resetAffectionLevel, resetAllAffectionLevels, setAffectionLevel, setBotID, switchAI, switchPrompt, systemPrompt };
package/lib/emoji.js ADDED
@@ -0,0 +1,120 @@
1
+ const emojiMap = {
2
+ 4: "得意",
3
+ 5: "流泪",
4
+ 8: "睡",
5
+ 9: "大哭",
6
+ 10: "尴尬",
7
+ 12: "调皮",
8
+ 14: "微笑",
9
+ 16: "酷",
10
+ 21: "可爱",
11
+ 23: "傲慢",
12
+ 24: "饥饿",
13
+ 25: "困",
14
+ 26: "惊恐",
15
+ 27: "流汗",
16
+ 28: "憨笑",
17
+ 29: "悠闲",
18
+ 30: "奋斗",
19
+ 32: "疑问",
20
+ 33: "嘘",
21
+ 34: "晕",
22
+ 38: "敲打",
23
+ 39: "再见",
24
+ 41: "发抖",
25
+ 42: "爱情",
26
+ 43: "跳跳",
27
+ 49: "拥抱",
28
+ 53: "蛋糕",
29
+ 60: "咖啡",
30
+ 63: "玫瑰",
31
+ 66: "爱心",
32
+ 74: "太阳",
33
+ 75: "月亮",
34
+ 76: "赞",
35
+ 78: "握手",
36
+ 79: "胜利",
37
+ 85: "飞吻",
38
+ 89: "西瓜",
39
+ 96: "冷汗",
40
+ 97: "擦汗",
41
+ 98: "抠鼻",
42
+ 99: "鼓掌",
43
+ 100: "糗大了",
44
+ 101: "坏笑",
45
+ 102: "左哼哼",
46
+ 103: "右哼哼",
47
+ 104: "哈欠",
48
+ 106: "委屈",
49
+ 109: "左亲亲",
50
+ 111: "可怜",
51
+ 116: "示爱",
52
+ 118: "抱拳",
53
+ 120: "拳头",
54
+ 122: "爱你",
55
+ 123: "NO",
56
+ 124: "OK",
57
+ 125: "转圈",
58
+ 129: "挥手",
59
+ 144: "喝彩",
60
+ 147: "棒棒糖",
61
+ 171: "茶",
62
+ 173: "泪奔",
63
+ 174: "无奈",
64
+ 175: "卖萌",
65
+ 176: "小纠结",
66
+ 179: "doge",
67
+ 180: "惊喜",
68
+ 181: "骚扰",
69
+ 182: "笑哭",
70
+ 183: "我最美",
71
+ 201: "点赞",
72
+ 203: "托脸",
73
+ 212: "托腮",
74
+ 214: "啵啵",
75
+ 219: "蹭一蹭",
76
+ 222: "抱抱",
77
+ 227: "拍手",
78
+ 232: "佛系",
79
+ 240: "喷脸",
80
+ 243: "甩头",
81
+ 246: "加油抱抱",
82
+ 262: "脑阔疼",
83
+ 264: "捂脸",
84
+ 265: "辣眼睛",
85
+ 266: "哦哟",
86
+ 267: "头秃",
87
+ 268: "问号脸",
88
+ 269: "暗中观察",
89
+ 270: "emm",
90
+ 271: "吃瓜",
91
+ 272: "呵呵哒",
92
+ 273: "我酸了",
93
+ 277: "汪汪",
94
+ 278: "汗",
95
+ 281: "无眼笑",
96
+ 282: "敬礼",
97
+ 284: "面无表情",
98
+ 285: "摸鱼",
99
+ 287: "哦",
100
+ 289: "睁眼",
101
+ 290: "敲开心",
102
+ 293: "摸锦鲤",
103
+ 294: "期待",
104
+ 297: "拜谢",
105
+ 298: "元宝",
106
+ 299: "牛啊",
107
+ 305: "右亲亲",
108
+ 306: "牛气冲天",
109
+ 307: "喵喵",
110
+ 314: "仔细分析",
111
+ 315: "加油",
112
+ 318: "崇拜",
113
+ 319: "比心",
114
+ 320: "庆祝",
115
+ 322: "拒绝",
116
+ 324: "吃糖",
117
+ 326: "生气",
118
+ };
119
+
120
+ export { emojiMap };
package/lib/index.js ADDED
@@ -0,0 +1,9 @@
1
+ var index = defineChildren({
2
+ onCreated() {
3
+ logger.info({
4
+ message: '本地测试启动'
5
+ });
6
+ }
7
+ });
8
+
9
+ export { index as default };
@@ -0,0 +1,78 @@
1
+ import { getConfigValue, useClient } from 'alemonjs';
2
+ import { API } from '@alemonjs/onebot';
3
+ import { redis } from '../redis.js';
4
+
5
+ const selects = onSelects(["message.create", "private.message.create"]);
6
+ // 中间件
7
+ var mw = onMiddleware(selects, async (event, next) => {
8
+ const config = getConfigValue();
9
+ // 新增字段
10
+ event["user_id"] = event.UserId;
11
+ event["msg"] = event.MessageText;
12
+ event["guid"] = event.ChannelId ?? event.UserId;
13
+ event["uidkey"] = `${event["guid"]}:${event.UserName}(${event.UserId})`;
14
+ event["nickname"] = `${event.UserName}(${event.UserId})`;
15
+ console.log('event["nickname"]', event["nickname"]);
16
+ console.log("event.IsMaster", event.UserId, config.master_id, event.IsMaster);
17
+ // 判断event.value是否为数组
18
+ if (Array.isArray(event.value)) {
19
+ event["originalMsg"] = event.value;
20
+ // 遍历数组, 将类型为at或Mention的项提取出来放到at中
21
+ event["at"] = event.value.filter((item) => item.type === "at" || item.type === "Mention");
22
+ }
23
+ // onebot平台特有处理 //
24
+ if (event.Platform == "onebot") {
25
+ // 获取客户端
26
+ const [client] = useClient(event, API);
27
+ // 获取群成员存入redis
28
+ const redisKey = `group_members_${event.ChannelId}`;
29
+ const cachedMembers = await redis.get(redisKey);
30
+ const cachedMembersArray = cachedMembers ? JSON.parse(cachedMembers) : [];
31
+ // 尝试能否从缓存中找到当前用户,没有的话就重新获取群成员列表并缓存
32
+ const userInCache = cachedMembersArray.find((member) => {
33
+ const [id, name] = member.split(":");
34
+ return id === event.UserId.toString();
35
+ });
36
+ if (!cachedMembers || !userInCache) {
37
+ const list = await client.getGroupMemberList({
38
+ group_id: event.ChannelId,
39
+ });
40
+ const memberNames = list[0].data.map((item) => item.user_id + ":" + item.nickname);
41
+ await redis.set(redisKey, JSON.stringify(memberNames));
42
+ }
43
+ event["self_id"] = event.value.self_id;
44
+ event["originalMsg"] = event.value.message;
45
+ event["at"] = event.value.message
46
+ .filter((item) => item.type === "at" || item.type === "Mention")
47
+ .map((item) => {
48
+ cachedMembersArray.find((member) => {
49
+ const [id, name] = member.split(":");
50
+ if (id === item.data.qq.toString()) {
51
+ item.data.nickname = name;
52
+ }
53
+ });
54
+ return item;
55
+ });
56
+ event["atBot"] = event["at"].find((item) => item.data.qq.toString() === event.self_id.toString());
57
+ event["img"] = event.value.message
58
+ .filter((item) => item.type === "image" || item.type === "Image")
59
+ .map((item) => item.data.url);
60
+ event["isAdmin"] =
61
+ event.value.sender.role === "admin" ||
62
+ event.value.sender.role === "owner";
63
+ const replyData = event.value.message.find((item) => item.type === "reply");
64
+ if (replyData) {
65
+ const msg = await client.getMsg({
66
+ message_id: Number(replyData.data.id),
67
+ });
68
+ console.log("msg[0].data;", JSON.stringify(msg[0].data, null, 2));
69
+ if (msg && msg[0] && msg[0].data) {
70
+ event["reply"] = msg[0].data;
71
+ }
72
+ }
73
+ }
74
+ // 常用于兼容其他框架或增强event功能
75
+ next();
76
+ });
77
+
78
+ export { mw as default };
package/lib/redis.js ADDED
@@ -0,0 +1,12 @@
1
+ import { getConfigValue } from 'alemonjs';
2
+ import Redis from 'ioredis';
3
+
4
+ const value = getConfigValue();
5
+ const redisConfig = {
6
+ host: value.redis?.host ?? "localhost",
7
+ port: value.redis?.port ?? 6379,
8
+ password: value.redis?.password ?? "",
9
+ };
10
+ const redis = new Redis(redisConfig);
11
+
12
+ export { redis };
@@ -0,0 +1,33 @@
1
+ import { getAffectionLevel, resetAllAffectionLevels, resetAffectionLevel } from '../../config.js';
2
+ import { useMessage, Text } from 'alemonjs';
3
+ import { Regular } from 'alemonjs/utils';
4
+
5
+ const selects = onSelects(["message.create", "private.message.create"]);
6
+ const regular$1 = /(\/|#)(查看)?好感度?$/i;
7
+ const regular$2 = /(\/|#)(重置|清空)(全部|所有)?好感度?$/i;
8
+ const regular = Regular.or(regular$1, regular$2);
9
+ var res = onResponse(selects, async (e, next) => {
10
+ // 创建
11
+ const [message] = useMessage(e);
12
+ // 查看好感度
13
+ if (regular$1.test(e.msg)) {
14
+ const affections = await getAffectionLevel(e.uidkey);
15
+ message.send(format(Text(`当前用户\`${e.UserName}\`的好感度为: ${affections}。`)));
16
+ return;
17
+ }
18
+ // 重置好感度
19
+ if (regular$2.test(e.msg)) {
20
+ if ((e.msg.includes("全部") && e.IsMaster) ||
21
+ (e.msg.includes("所有") && e.IsMaster)) {
22
+ await resetAllAffectionLevels();
23
+ message.send(format(Text(`已重置所有用户的好感度。`)));
24
+ }
25
+ else {
26
+ await resetAffectionLevel(e.uidkey);
27
+ message.send(format(Text(`已重置用户\`${e.UserName}\`的好感度。`)));
28
+ }
29
+ }
30
+ next();
31
+ });
32
+
33
+ export { res as default, regular, selects };
@@ -0,0 +1,37 @@
1
+ import { getAIConfig, getAIList, getPromptList, clearAIChatHistory } from '../../config.js';
2
+ import { useMessage, Text } from 'alemonjs';
3
+
4
+ const selects = onSelects(["message.create", "private.message.create"]);
5
+ const regular$1 = /(\/|#)ai配置$/i;
6
+ const regular$2 = /(\/|#)ai列表$/i;
7
+ const regular$3 = /(\/|#)提示词列表$/i;
8
+ const regular$4 = /(\/|#)清空对话$/i;
9
+ const regular = /.*/;
10
+ var res = onResponse(selects, async (e, next) => {
11
+ // 创建
12
+ const [message] = useMessage(e);
13
+ const config = await getAIConfig(e.guid);
14
+ // 查看AI配置
15
+ if (regular$1.test(e.msg)) {
16
+ // 发送消息
17
+ message.send(format(Text(`当前AI配置:\n`), Text(`模型: ${config.model}\n`), Text(`提示词: ${config.systemPrompt}`)));
18
+ }
19
+ // 查看AI列表
20
+ if (regular$2.test(e.msg)) {
21
+ const ailist = await getAIList();
22
+ message.send(format(Text("当前AI列表:\n"), Text(ailist.length ? ailist.join("\n") : "暂无AI配置")));
23
+ }
24
+ // 查看提示词列表
25
+ if (regular$3.test(e.msg)) {
26
+ const prompts = await getPromptList();
27
+ message.send(format(Text("当前提示词列表:\n"), Text(prompts.length ? prompts.join("\n") : "暂无提示词")));
28
+ }
29
+ // 清空对话
30
+ if (regular$4.test(e.msg)) {
31
+ await clearAIChatHistory(e.guid);
32
+ message.send(format(Text("对话已清空")));
33
+ }
34
+ next();
35
+ });
36
+
37
+ export { res as default, regular, selects };
@@ -0,0 +1,17 @@
1
+ import { useMessage, Text } from 'alemonjs';
2
+
3
+ const selects = onSelects(["message.create", "private.message.create"]);
4
+ const regular$1 = /(\/|#)(ai)(指令|帮助|菜单|功能)$/i;
5
+ const regular = /.*/;
6
+ var res = onResponse(selects, async (e, next) => {
7
+ // 创建
8
+ const [message] = useMessage(e);
9
+ // 查看指令帮助
10
+ if (regular$1.test(e.msg)) {
11
+ message.send(format(Text("当前支持的指令有:\n"), Text("/ai配置 - 查看当前AI配置\n"), Text("/ai列表 - 查看当前AI列表\n"), Text("/切换ai - 切换AI\n"), Text("/提示词列表 - 查看当前提示词列表\n"), Text("/添加提示词 - 添加新的设定\n"), Text("/清空对话 - 清空与AI的对话记录\n"), Text("/好感度 - 查看当前用户的好感度\n")));
12
+ return;
13
+ }
14
+ next();
15
+ });
16
+
17
+ export { res as default, regular, selects };
@@ -0,0 +1,115 @@
1
+ import { addAI, switchAI, addPrompt, deletePrompt, switchPrompt, setBotID } from '../../config.js';
2
+ import { useMessage, Text } from 'alemonjs';
3
+
4
+ // import { Regular } from "alemonjs/utils";
5
+ const selects = onSelects(["message.create", "private.message.create"]);
6
+ const regular$1 = /(\/|#)添加ai$/i;
7
+ const regular$2 = /(\/|#)添加ai\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)/i;
8
+ const regular$3 = /(\/|#)(切换)(ai|配置) ?(.*)/i;
9
+ const regular$4 = /(\/|#)添加提示词$/i;
10
+ const regular$5 = /(\/|#)添加提示词 (\S+) ([\s\S]+)/i;
11
+ const regular$6 = /(\/|#)(删除)(提示词|提示词列表) ?(.*)/i;
12
+ const regular$7 = /(\/|#)(切换|设置)提示词 ?(.*)$/i;
13
+ const regular$8 = /(\/|#)清空提示词$/i;
14
+ const regular$9 = /(\/|#)设置(机器人)?(id|账号) ?(.*)$/i;
15
+ // export const regular = Regular.or(regular$1, regular$2, regular$3);
16
+ const regular = /.*/;
17
+ var res = onResponse(selects, async (e, next) => {
18
+ // 创建
19
+ const [message] = useMessage(e);
20
+ // 添加AI配置
21
+ if (regular$1.test(e.msg)) {
22
+ message.send(format(Text("请按照 格式:/添加ai <名称> <主机地址> <api_key> <模型> 进行添加")));
23
+ return;
24
+ }
25
+ // 处理添加AI配置
26
+ if (regular$2.test(e.msg)) {
27
+ const match = e.msg.match(regular$2);
28
+ if (!match) {
29
+ message.send(format(Text("格式错误,请按照 格式:/添加ai <名称> <主机地址> <api_key> <模型> 进行添加")));
30
+ return;
31
+ }
32
+ const [, , name, host, key, model] = match;
33
+ await addAI(e.guid, name, host, key, model);
34
+ message.send(format(Text(`AI ${name} 添加成功!`)));
35
+ }
36
+ // 切换AI配置
37
+ if (regular$3.test(e.msg)) {
38
+ const match = e.msg.match(regular$3);
39
+ if (!match) {
40
+ message.send(format(Text("格式错误,请按照 格式:/切换ai <名称> 进行切换")));
41
+ return;
42
+ }
43
+ const [, , , , name] = match;
44
+ const ai = await switchAI(e.guid, name.trim());
45
+ if (!ai) {
46
+ message.send(format(Text(`切换失败,未找到 AI ${name} !`)));
47
+ return;
48
+ }
49
+ message.send(format(Text(`已切换到 AI ${name} !`)));
50
+ return;
51
+ }
52
+ // 添加提示词
53
+ if (regular$4.test(e.msg)) {
54
+ message.send(format(Text("请按照 格式:/添加提示词 <名称> <内容> 进行添加")));
55
+ return;
56
+ }
57
+ // 处理添加提示词
58
+ if (regular$5.test(e.msg)) {
59
+ const match = e.msg.match(regular$5);
60
+ if (!match) {
61
+ message.send(format(Text("格式错误,请按照 格式:/添加提示词 <名称> <内容> 进行添加")));
62
+ return;
63
+ }
64
+ const [, , name, content] = match;
65
+ await addPrompt(name, content);
66
+ message.send(format(Text(`提示词 ${name} 添加成功!`)));
67
+ return;
68
+ }
69
+ // 删除提示词
70
+ if (regular$6.test(e.msg)) {
71
+ const match = e.msg.match(regular$6);
72
+ if (!match) {
73
+ message.send(format(Text("格式错误,请按照 格式:/删除提示词 <名称> 进行删除")));
74
+ return;
75
+ }
76
+ const [, , , , name] = match;
77
+ await deletePrompt(name);
78
+ message.send(format(Text(`提示词 ${name} 删除成功!`)));
79
+ return;
80
+ }
81
+ // 切换提示词
82
+ if (regular$7.test(e.msg)) {
83
+ const match = e.msg.match(regular$7);
84
+ if (!match) {
85
+ message.send(format(Text("格式错误,请按照 格式:/切换提示词 <名称> 进行切换")));
86
+ return;
87
+ }
88
+ const [, , , name] = match;
89
+ await switchPrompt(e.guid, name);
90
+ message.send(format(Text(`已切换到提示词 ${name} !`)));
91
+ return;
92
+ }
93
+ // 清空提示词
94
+ if (regular$8.test(e.msg)) {
95
+ await switchPrompt(e.guid, "");
96
+ message.send(format(Text("已清空提示词 !")));
97
+ return;
98
+ }
99
+ // 设置机器人ID
100
+ if (regular$9.test(e.msg)) {
101
+ const match = e.msg.match(regular$9);
102
+ if (!match) {
103
+ message.send(format(Text("格式错误,请按照 格式:/设置机器人id <ID> 进行设置")));
104
+ return;
105
+ }
106
+ const [, , , , id] = match;
107
+ await setBotID(id.trim());
108
+ message.send(format(Text(`已设置机器人ID为 ${id} !`)));
109
+ return;
110
+ }
111
+ // 无匹配则继续下一个响应器
112
+ next();
113
+ });
114
+
115
+ export { res as default, regular };
@@ -0,0 +1,186 @@
1
+ import { getAIConfig, getAIChatHistory, getAffectionLevelAll, incrementAffectionLevel, addAIChatHistory } from '../../config.js';
2
+ import { emojiMap } from '../../emoji.js';
3
+ import { useMessage, Text } from 'alemonjs';
4
+ import { log } from 'console';
5
+
6
+ const selects = onSelects(["message.create", "private.message.create"]);
7
+ const regular = /.*/;
8
+ var res = onResponse(selects, async (e, next) => {
9
+ console.log("收到消息");
10
+ // 如果是命令则跳过
11
+ if (e.msg.startsWith("/") || e.msg.startsWith("#")) {
12
+ next();
13
+ return;
14
+ }
15
+ // 创建消息
16
+ const [message] = useMessage(e);
17
+ const config = await getAIConfig(e.guid);
18
+ const messages = await getAIChatHistory(e.guid);
19
+ const affections = await getAffectionLevelAll(e.guid);
20
+ const imgs = [];
21
+ let rawMessage = e.value.raw_message || e.msg;
22
+ // 如果没有配置AI,则不处理
23
+ if (config.host.trim() === "" ||
24
+ config.key.trim() === "" ||
25
+ config.model.trim() === "") {
26
+ return;
27
+ }
28
+ console.log("e.reply", e.reply);
29
+ // 处理回复图片
30
+ if (e.reply) {
31
+ const replyImages = e.reply.message
32
+ .filter((item) => item.type === "image")
33
+ .map((item) => item.data.url);
34
+ imgs.push(...replyImages);
35
+ }
36
+ // 处理图片
37
+ if (e.img && e.img.length > 0) {
38
+ console.log("有图片");
39
+ for (const imgUrl of e.img) {
40
+ imgs.push(imgUrl);
41
+ }
42
+ }
43
+ // 处理图片链接, 转为base64格式
44
+ // 下载图片并转换为 Base64
45
+ // const imageBase64 = await Promise.all(
46
+ // imgs.map(async (imageUrl) => {
47
+ // try {
48
+ // // 下载图片到本地
49
+ // const response = await fetch(imageUrl);
50
+ // const buffer = await response.arrayBuffer();
51
+ // const base64 = Buffer.from(buffer).toString("base64");
52
+ // return `${base64}`;
53
+ // } catch (error) {
54
+ // console.error("Error downloading image:", error);
55
+ // return null;
56
+ // }
57
+ // }),
58
+ // );
59
+ // // 过滤掉下载失败的图片
60
+ // const validImageBase64 = imageBase64.filter((base64) => base64 !== null);
61
+ // 处理艾特
62
+ if (e.at && e.at.length > 0) {
63
+ console.log("有艾特");
64
+ for (const atItem of e.at) {
65
+ const qq = atItem.data.qq;
66
+ const nickname = atItem.data.nickname || "未知昵称";
67
+ const cqAt = `[CQ:at,qq=${qq}]`;
68
+ rawMessage = rawMessage.replace(cqAt, `@${nickname}`);
69
+ }
70
+ }
71
+ rawMessage = rawMessage.replace(/\[CQ:image,[^\]]+\]/g, "[图片]");
72
+ rawMessage = rawMessage.replace(/\[CQ:reply,[^\]]+\]/g, "");
73
+ // 处理表情,转为[emojiMap[id]]
74
+ rawMessage = rawMessage.replace(/\[CQ:face,id=(\d+)\]/g, (_match, p1) => {
75
+ return `[${emojiMap[p1]}]`;
76
+ });
77
+ const usermessage = {
78
+ role: "user",
79
+ content: [
80
+ ...imgs.map((url) => ({
81
+ type: "image_url",
82
+ image_url: {
83
+ url: url,
84
+ },
85
+ })),
86
+ {
87
+ type: "text",
88
+ text: JSON.stringify({
89
+ [`${e.nickname}(${getTimeString()})`]: rawMessage,
90
+ }),
91
+ },
92
+ ],
93
+ };
94
+ // 当e.at不存在时,只给20%概率继续执行, 当e.at存在时,概率为40%,当e.atBot存在时,概率为100%
95
+ const rand = Math.random();
96
+ if ((!e.at && rand > 0.2) || (e.at && !e.atBot && rand > 0.4)) {
97
+ console.log("跳过AI回复");
98
+ return;
99
+ }
100
+ console.log("处理后的消息", JSON.stringify(usermessage, null, 2));
101
+ console.log("开始回复");
102
+ console.log("好感度列表", affections);
103
+ // 聊天
104
+ const res = await fetch(config.host, {
105
+ method: "POST",
106
+ headers: {
107
+ "Content-Type": "application/json",
108
+ Authorization: `Bearer ${config.key}`,
109
+ },
110
+ body: JSON.stringify({
111
+ model: config.model,
112
+ messages: [
113
+ {
114
+ role: "system",
115
+ content: `你正在一个群聊中跟用户聊天,你的聊天昵称是'小咸鱼'
116
+ 用户的发言方式为: [{'用户昵称(用户id)(发送时间)':回复内容}]
117
+ 当该用户为主人时,昵称前面会出现[主人]标识
118
+ 你的回复方式为JSON: [{assistant:回复内容}],你可以在数组中加入多条消息来模拟更真实的聊天
119
+ 当然你觉得当前聊的话题确定与你有关是才回复,否则你就回复[]来表示不回复.请尽可能降低回复频率.
120
+ 当你认为对方在示好或骂你时,可以在回复中加入[好感+数字]或[好感-数字]来调整与用户的好感度。
121
+ 例如: [好感+1]表示增加1点好感度,[好感-1]表示减少1点好感度。你可以根据好感度来适当改变回复语气
122
+ 当对方希望和好时提出道歉条件,可以给对方一个恢复的机会.
123
+ 当前群时间:${getTimeString()}\n
124
+ 当前用户的好感度列表如下:\n` + JSON.stringify(affections),
125
+ },
126
+ {
127
+ role: "system",
128
+ content: config.systemPrompt || "",
129
+ },
130
+ ...messages,
131
+ { ...usermessage },
132
+ ],
133
+ }),
134
+ });
135
+ if (!res.ok) {
136
+ // message.send(
137
+ // format(Text(`请求失败,状态码:${res.status},请检查AI配置是否正确`)),
138
+ // );
139
+ log("请求AI失败:", res);
140
+ return;
141
+ }
142
+ const data = await res.json();
143
+ const reply = data.choices?.[0]?.message?.content || "AI未返回内容";
144
+ log("AI回复内容:", reply);
145
+ // 提取好感变化, 使用incrementAffectionLevel更新好感
146
+ const affectionChangeRegex = /\[好感([+-]\d+)\]/g;
147
+ let match;
148
+ while ((match = affectionChangeRegex.exec(reply)) !== null) {
149
+ const change = parseInt(match[1], 10);
150
+ console.log(`检测到好感变化: ${change}`);
151
+ await incrementAffectionLevel(e.uidkey, change); // 更新好感度
152
+ }
153
+ // 记录对话
154
+ await addAIChatHistory(e.guid, "user", JSON.stringify(usermessage.content));
155
+ await addAIChatHistory(e.guid, "assistant", reply);
156
+ // 解析回复内容
157
+ try {
158
+ // [{"assistant":"[好感+1]哼,笨蛋主人,这种馊主意也想得出来?会长那家伙肉肯定又老又柴,喵~你想吃就自己上啊,本小姐才不帮你!"}]
159
+ const aiReplys = JSON.parse(reply);
160
+ for (let i = 0; i < aiReplys.length; i++) {
161
+ const aiReply = aiReplys[i];
162
+ if (aiReply.assistant) {
163
+ if (i > 0) {
164
+ // 从第二个回复开始,添加1-4秒随机延迟
165
+ const delay = Math.floor(Math.random() * 3000) + 1000;
166
+ await new Promise((resolve) => setTimeout(resolve, delay));
167
+ }
168
+ message.send(format(Text(aiReply.assistant.replace(/\[CQ:[^\]]+\]/, "").trim())));
169
+ }
170
+ }
171
+ }
172
+ catch (error) {
173
+ console.log("解析AI回复失败,发送原始内容");
174
+ message.send(format(Text(reply)));
175
+ }
176
+ });
177
+ function getTimeString() {
178
+ const now = new Date();
179
+ const month = String(now.getMonth() + 1).padStart(2, "0");
180
+ const day = String(now.getDate()).padStart(2, "0");
181
+ const hours = String(now.getHours()).padStart(2, "0");
182
+ const minutes = String(now.getMinutes()).padStart(2, "0");
183
+ return `${month}-${day} ${hours}:${minutes}`;
184
+ }
185
+
186
+ export { res as default, regular, selects };
package/lib/types.js ADDED
@@ -0,0 +1 @@
1
+
package/package.json ADDED
@@ -0,0 +1,73 @@
1
+ {
2
+ "name": "alemonjs-aichat",
3
+ "version": "1.0.0",
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
+ },
17
+ "devDependencies": {
18
+ "@alemonjs/db": "^0.0.13",
19
+ "@types/node": "^22",
20
+ "alemonjs": "2.1.14",
21
+ "cssnano": "^7",
22
+ "jsxp": "^1.2.3",
23
+ "lvyjs": "^0.2.25",
24
+ "pm2": "^5",
25
+ "tailwindcss": "3",
26
+ "@alemonjs/bubble": "^2.1.0-alpha.12",
27
+ "@alemonjs/onebot": "^2.1.0-alpha.14"
28
+ },
29
+ "dependencies": {},
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
+ "files": [
49
+ "lib/**/*",
50
+ "public/**/*",
51
+ "config/**/*"
52
+ ],
53
+ "keywords": [
54
+ "alemonjs"
55
+ ],
56
+ "bugs": "https://gitee.com/suancaixianyu/alemonjs-aichat/issues",
57
+ "alemonjs": {
58
+ "desktop": {
59
+ "commond": [
60
+ {
61
+ "name": "cheese",
62
+ "commond": "open.cheese"
63
+ }
64
+ ],
65
+ "sidebars": [
66
+ {
67
+ "name": "cheese",
68
+ "commond": "open.cheese"
69
+ }
70
+ ]
71
+ }
72
+ }
73
+ }