Compare commits

...

1473 Commits

Author SHA1 Message Date
Soulter
291d65bb3e release: v3.5.5 2025-04-21 11:09:18 +08:00
Soulter
bd3ad03da6 Merge pull request #1361 from AstrBotDevs/hotfix/webui-mcp
fix: 修复 MCP 页面的一些问题
2025-04-21 10:54:19 +08:00
Soulter
5fa6788357 chore: properly storing interval ID for cleanup. 2025-04-21 10:54:06 +08:00
Soulter
c5c5a98ac4 🐛 fix: 修复 MCP 页面的一些问题 2025-04-21 10:51:01 +08:00
Soulter
a1151143cf Merge pull request #1357 from Raven95676/hotfix/gemini-functool
fix: 修复get_func_desc_google_genai_style未正确转换函数调用的问题
2025-04-21 10:26:44 +08:00
Raven95676
f5024984f7 perf: 移除冗余判断 2025-04-21 00:55:20 +08:00
Raven95676
f4880fd90d fix: 修复get_func_desc_google_genai_style未正确转换函数调用的问题 2025-04-21 00:11:31 +08:00
Soulter
da546cfe7f 🎈 perf(telegram): 弱化无法注册指令的日志级别 2025-04-20 18:08:52 +08:00
Soulter
a211933e83 📦 release: v3.5.4 2025-04-20 18:01:37 +08:00
Soulter
1d40b5a821 feat(updator): 替换为采用 Semver 语义化版本来比较版本 2025-04-20 17:30:01 +08:00
Soulter
33836daeb7 Merge pull request #1327 from YOOkoishi/tts-feat-branck
TTS : add text output alongside voice (Fix #1085)
2025-04-20 16:07:06 +08:00
Soulter
0de6d0e046 Merge pull request #1256 from Raven95676/better-stream
perf: 为不支持流式输出的平台提供fallback。
2025-04-20 15:24:31 +08:00
Soulter
9fedaa9f77 🎈perf(webui): 优化了 MCP 页面的效果 2025-04-20 11:26:53 +08:00
Soulter
bf4c2ecd33 feat: MCP 支持 SSE 传输协议连接到服务器 2025-04-20 11:02:28 +08:00
Soulter
f8c18cc1e0 Merge pull request #1341 from AstrBotDevs/fix-dashscope-error-1330
fix: 修复阿里云百炼 TTS 只能发送一次语音,第二次就会报错
2025-04-20 01:17:32 +08:00
Soulter
458b900412 Merge pull request #1340 from AstrBotDevs/perf-wecom-split-long-text
feature: 企业微信添加长文本分割功能以支持发送超过 2048 字符的消息
2025-04-20 01:15:48 +08:00
Soulter
192c776e0b 🐛 fix: 修复阿里云百炼 TTS 只能发送一次语音,第二次就会报错
fixes: #1330
2025-04-20 00:58:37 +08:00
anka
5cdec18863 improvement: 对标点符号分割而不是直接切分 2025-04-19 16:52:30 +00:00
Soulter
15f856f951 perf(wecom): 企业微信添加长文本分割功能以支持发送超过 2048 字符的消息
fixes: #564
2025-04-20 00:27:04 +08:00
Raven95676
01d52cef74 perf: 支持更多参数 2025-04-20 00:12:14 +08:00
YOO_koishi
31d8c40eca tts : add text output alongside voice (Fix #1085) 2025-04-19 14:44:02 +08:00
渡鸦95676
56001ed272 Merge pull request #1326 from Raven95676/session_waiter
perf: 修改默认会话过滤器标识符为umo
2025-04-19 13:45:06 +08:00
Raven95676
cfae655068 perf: 修改默认会话过滤器标识符为umo 2025-04-19 11:57:22 +08:00
Raven95676
5596565ec4 fix: 若启用Gemini原生工具,构建Content列表时忽略工具调用 2025-04-18 23:36:12 +08:00
Raven95676
e98c3d8393 fix: Gemini保证工具间的互斥 2025-04-18 16:19:36 +08:00
渡鸦95676
6687b816f0 Merge pull request #1303 from Raven95676/master
feat: 添加对Gemini原生搜索功能的支持
2025-04-17 20:48:02 +08:00
Raven95676
ea8035e854 feat: 添加对Gemini原生搜索功能的支持 2025-04-17 20:36:22 +08:00
Soulter
54b0171d49 Merge pull request #1296 from AstrBotDevs/feat-mcp-servers-market
[WIP] MCP 服务器市场
2025-04-17 16:26:41 +08:00
Soulter
676d4277b9 chore: 优化样式 2025-04-17 16:26:27 +08:00
Soulter
a4b1da3ca2 perf: 警告 2025-04-17 16:24:50 +08:00
Soulter
9e9c16e770 Merge pull request #1295 from EdelweissHuirh/master
修改分段回复的分割逻辑
2025-04-17 16:11:08 +08:00
Soulter
dc87006fed feat: 分页 2025-04-17 16:07:13 +08:00
Soulter
b9b260f26a perf: 弱化显示 2025-04-17 14:02:40 +08:00
Soulter
33fd6a5016 perf: 优化 MCP 服务器的日志回显 2025-04-17 13:59:10 +08:00
Soulter
97cbccc2ba feat: mcp 服务器市场 2025-04-17 00:41:04 +08:00
Raven95676
1ee4685d5d perf: 允许行级别锚点匹配以保持一致性 2025-04-16 22:13:38 +08:00
Soulter
aba18232b1 perf: docker 镜像自带 node 环境
fixes: #1290
2025-04-16 21:53:27 +08:00
huirh
0a02441b75 修改分段回复逻辑 2025-04-16 21:52:42 +08:00
Raven95676
1be5b4c7ff fix: 兼容旧版本google-genai sdk 2025-04-16 00:34:08 +08:00
Raven95676
a0ce0cf18a fix: 增加更多Gemini不支持多模态输出的情况 2025-04-16 00:11:46 +08:00
Soulter
7c54e5d093 perf: 优化已安装的插件页
fixes: #934
2025-04-15 22:53:40 +08:00
Soulter
b825e51dab chore: clean useless logs 2025-04-15 21:56:23 +08:00
Soulter
589855c393 feat: 支持开关是否忽略自身发送的消息
某些平台如 gewechat 会将自身账号在其他 APP 端发送的消息也当做消息事件下发导致给自己发消息时唤醒机器人

fixes: #890
2025-04-15 21:55:21 +08:00
渡鸦95676
4c546f2f53 Merge branch 'master' into better-stream 2025-04-15 21:22:08 +08:00
Raven95676
3753fce912 perf: 为发送流式消息的Fallback可选 2025-04-15 21:21:02 +08:00
Soulter
4c02857ec5 🐛 fix: 修复 aiocqhttp 无法发图片
fixes: #1275
2025-04-15 21:15:39 +08:00
Soulter
33f87ff7d7 🎈 perf: enhance metrics tracking with installation ID and sender ID hashing 2025-04-15 21:08:45 +08:00
Soulter
784dcf2a9a Merge pull request #1228 from Raven95676/gemini
refactor: 使用Google官方SDK重构gemini_source
2025-04-15 20:04:20 +08:00
Soulter
43ee943acb 🐛 fix: 多轮函数调用的报错 2025-04-15 10:59:16 +08:00
Soulter
a769fd7d13 chore: add google-genai dependency to project 2025-04-15 10:40:42 +08:00
渡鸦95676
2c4fd00b16 Merge pull request #1276 from Raven95676/master
fix: 移除TG注册命令时的调试信息,注册命令时添加合法性校验
2025-04-14 22:04:11 +08:00
Raven95676
264771fe98 fix: 移除注册时的调试信息,注册命令时添加合法性校验 2025-04-14 21:55:34 +08:00
Soulter
ecd92dafef Merge pull request #1274 from AstrBotDevs/fix-1121
🐛 fix: 修复上下文带图的情况下,对话数据库页无法查看对话详情的问题
2025-04-14 21:35:54 +08:00
Soulter
c8b6e4bea3 🐛 fix: 修复上下文带图的情况下,对话数据库页无法查看对话详情的问题
fixes: 1121
2025-04-14 21:34:11 +08:00
Soulter
3756cb766e 🎈 perf: 支持自定义 PyPI 软件仓库地址
fixes: #1165
2025-04-14 21:19:36 +08:00
Soulter
068d9ca60b Update README.md 2025-04-14 19:57:04 +08:00
Soulter
93f632d8b8 Update README.md 2025-04-14 19:56:32 +08:00
Soulter
bb44ce7e74 Update README.md 2025-04-14 10:30:12 +08:00
Raven95676
6986c8d8f7 fix: clean code,处理Gemini流式输出最后一部分概率性为None的情况 2025-04-13 18:34:57 +08:00
Raven95676
fe95506db4 perf: 添加日志过滤器以抑制非文本部分警告信息 2025-04-13 17:50:44 +08:00
Raven95676
310ed76b18 fix: 仅在确实包含图片模态时降级 2025-04-13 17:28:34 +08:00
Raven95676
98830d147f fix: 限速增加到1.5秒 2025-04-13 17:14:51 +08:00
Raven95676
19c9177d7b chore: 移除对dingtalk、lark、wecom的fallback 2025-04-13 17:03:06 +08:00
渡鸦95676
f41c5f97f6 Merge branch 'master' into better-stream 2025-04-13 16:47:56 +08:00
Raven95676
648c125697 refactor: 提取缓冲处理逻辑到astr_message_event 2025-04-13 15:37:22 +08:00
Soulter
0dc2b89897 Merge pull request #1257 from KimigaiiWuyi/master
🐛 修复飞书适配器转换消息过程中无法正确转化Base64图片
2025-04-13 15:33:02 +08:00
Soulter
83745f83a5 🐛 fix: 对飞书适配器 base64 格式数据先保存到本地 2025-04-13 15:29:56 +08:00
Soulter
2f91fe4535 Merge pull request #1244 from Rail1bc/master
修复:dequeue_context_length的配置项的实际行为与描述不一致;调用函数工具可能导致400错误
2025-04-13 14:41:16 +08:00
Raven95676
739f09059e feat: 为Gemini原生代码执行器提供有限支持 2025-04-13 12:43:25 +08:00
渡鸦95676
c86f9f0f5f Merge pull request #1261 from Raven95676/master
fix: 修复文件不存在的情况
2025-04-13 11:40:33 +08:00
Raven95676
9470ca6bc5 fix: 修复文件不存在的情况 2025-04-13 11:36:06 +08:00
Raven95676
2a92c4d5de fix: 修复导入 2025-04-13 11:22:27 +08:00
Raven95676
bb6e892657 feat: 重构发送流以提高代码可读性 2025-04-13 11:19:40 +08:00
KimigaiiWuyi
c9079b9299 🐛 修复飞书适配器转换消息过程中无法正确转化Base64图片 2025-04-13 06:06:02 +08:00
Raven95676
b6963c1bf9 perf: 为不支持流式输出的平台提供fallback。 2025-04-13 02:21:42 +08:00
Raven95676
9c29df47bb fix: 更新流式输出逻辑,禁用图片模态并添加日志警告。 2025-04-13 01:09:42 +08:00
Soulter
fc146d3d00 Merge pull request #1245 from AstrBotDevs/perf-mcpserver
perf: 适配 MCP 配置文件带 mcpServers 的情况(Cursor)
2025-04-12 23:06:39 +08:00
Soulter
1bf5a21678 Merge pull request #1158 from Jackxwb/master
文件发送时支持路径映射
2025-04-12 21:01:25 +08:00
Soulter
011542dc2b Merge pull request #1247 from Raven95676/shared_preferences
perf: shared_preferences加载失败时自动删除无效文件
2025-04-12 20:04:19 +08:00
Raven95676
489784104e perf: shared_preferences加载失败时自动删除无效文件 2025-04-12 19:31:45 +08:00
Raven95676
3860634fd2 fix: 修复了多模态输出支持判断问题并对只输出图片的情况进行处理。 2025-04-12 19:15:39 +08:00
Soulter
709c324e18 🐛 fix: 修复 MCP 服务器配置处理逻辑,确保正确处理空 mcpServers 情况并优化代码可读性 2025-04-12 18:19:06 +08:00
Soulter
b75d24d92c 🎈 perf: 适配 MCP 配置文件带 mcpServers 的情况(Cursor)
🐛 fix: 关闭/删除 MCP 服务器后 Tools 没有清除的问题
2025-04-12 17:56:23 +08:00
Raila23
ed80e9424c Merge branch 'master' of https://github.com/AstrBotDevs/AstrBot 2025-04-12 16:28:14 +08:00
Raila23
2fe1f2060a 修复:调用函数工具或其他未知情况,可能导致400 BadRequestError 2025-04-12 16:26:02 +08:00
Raila23
c6df820164 修复:每次清除的消息,比实际上期望的多1条 2025-04-12 15:34:35 +08:00
Soulter
d6239822db release: v3.5.3.2 2025-04-12 15:27:33 +08:00
Soulter
bced9ffff9 🐛 fix: 修复zhipu工具调用问题 2025-04-12 15:24:37 +08:00
Soulter
d7d1c1544a 🐛 fix: 修复重启bot时可能发生报错的问题
在 gewechat, wecom 等消息平台没启动成功的情况下重启bot会报错
2025-04-12 15:01:38 +08:00
Soulter
e3b0ca8ef6 🐛 fix: 改进版本号比较逻辑以支持任意长度的版本号 2025-04-12 10:00:25 +08:00
Soulter
9e266eb6d5 release: v3.5.3.1 2025-04-12 09:48:49 +08:00
Soulter
7231403e16 🐛 fix: xai missing field parameters 2025-04-12 09:47:11 +08:00
Soulter
344a486fd7 fix: entites 前向兼容 2025-04-12 09:10:54 +08:00
Soulter
4fd831875d Merge pull request #1237 from AstrBotDevs/release/v3.5.3
📦 release: v3.5.3
2025-04-12 01:04:31 +08:00
Soulter
0988d067ea 📦 release: v3.5.3 2025-04-12 00:58:45 +08:00
Raven95676
44dbe475af refactor: 拆分方法以提高代码可读性 2025-04-12 00:23:57 +08:00
Raven95676
bd24cf3ea4 feat: 初步完成原生流式请求逻辑 2025-04-11 23:45:30 +08:00
Raven95676
b493a808fe fix: 处理更多多模态不支持错误 2025-04-11 20:25:20 +08:00
Raven95676
54035d108d Merge branch 'gemini' of https://github.com/Raven95676/AstrBot-Rdev into gemini 2025-04-11 18:57:55 +08:00
Raven95676
c5e8bc7e20 fix: 修复模型生成内容的重试机制。 2025-04-11 18:55:46 +08:00
渡鸦95676
3bbb4779a3 Merge branch 'master' into gemini 2025-04-11 18:15:44 +08:00
Raven95676
1b3963ebea fix: 更新类型提示,简化代码并修复潜在的空值问题。 2025-04-11 18:07:00 +08:00
Soulter
3b6dd7e15a 🐛 fix: 修复 dify 下删除对话的报错问题
fixes: #1226
2025-04-11 17:27:29 +08:00
Soulter
757d2a3947 🐛 fix: 更新 Dify API 类型提示,增加对 Chatflow 应用类型的说明 2025-04-11 17:23:26 +08:00
Soulter
61b71143f2 Merge pull request #1223 from MR-pofeng/tag-msg-seq
feat:为QQ官方接口需要msg_seq的playload添加随机msg_seq
2025-04-11 16:25:46 +08:00
Soulter
1b343a36c9 Merge pull request #1174 from anka-afk/anka-dev
对关闭的#1167提供完整修复, 修复gemini请求content为空的情况, 增加上下文中验证toolcall逻辑
2025-04-11 16:20:30 +08:00
Soulter
8e94937060 🐛 fix: 修复使用 gemini 时,函数数工具调用会重复调用已经在过去会话中调用过的工具
fixes: #863 #1150
2025-04-11 15:50:36 +08:00
Raven95676
e8ffebc006 fix: 修复消息处理流程中可能出现的空消息 2025-04-11 15:01:20 +08:00
Raven95676
2ca95eaa9f fix: 在设置新key后重新初始化Gemini客户端 2025-04-11 14:42:24 +08:00
Raven95676
0dc5b4cdfc perf: 增加对RECITATION完成原因的处理,提取内容处理逻辑到独立方法 2025-04-11 12:25:44 +08:00
Raven95676
cc6cd96d8e fix: 修复潜在的空消息 2025-04-11 11:03:17 +08:00
Raven95676
4244d37625 chore: 格式化代码,禁用gemini source debug输出 2025-04-11 01:06:20 +08:00
Raven95676
0b766095d4 refactor: 初步完成gemini_source的重写 2025-04-11 01:03:16 +08:00
Soulter
a4f212a18f 🐛 fix: 修复使用 OneAPI + Gemini(openai) 传递空参数函数工具时可能报错的问题
fixes: #1060
2025-04-11 00:20:08 +08:00
Soulter
caafb73190 🐛 fix: 修复函数调用的一些bug 2025-04-10 23:28:51 +08:00
kuangfeng
09482799c9 feat:为需要msg_seq的playload添加随机msg_seq 2025-04-10 21:43:12 +08:00
Soulter
37f93d1760 Merge pull request #1175 from Raven95676/telegram
feat: 自动注册指令到Telegram
2025-04-10 20:26:54 +08:00
Soulter
725f2e5204 Merge pull request #1212 from AstrBotDevs/feat-lark-active-message
 feat: 支持飞书平台下主动消息发送
2025-04-10 17:14:37 +08:00
Soulter
967198fae0 feat: 支持飞书平台下主动消息发送
fixes: #1177

WARNING:
这个修复会导致开启对话隔离下飞书群组的对话记录丢失(但没有被删除)。
2025-04-10 17:12:26 +08:00
Soulter
43d57f6dcb 🎈 perf: Add type validation for configuration items in validate_config function 2025-04-10 15:56:14 +08:00
Soulter
6afa4db577 Merge pull request #1208 from Rail1bc/fix_begin_dialogs
fix:使 begin_dialogs ,预设对话,不会多次插入
2025-04-10 15:32:10 +08:00
Soulter
3b8c3fb29a Merge pull request #1207 from zsbai/patch-1
修复了 `event.get_sender_id()` 返回值与函数注释不一致的问题
2025-04-10 15:27:14 +08:00
Soulter
921c3b0627 Merge pull request #1203 from Rail1bc/master
将一项优化插件的简单逻辑,适配到Core中
2025-04-10 15:25:00 +08:00
Raila23
c0fadb45ab 添加更详细的描述 2025-04-10 15:20:56 +08:00
Raven95676
a1481fb179 群聊场景命令特殊处理 2025-04-10 14:54:25 +08:00
Soulter
987cd972d3 Merge pull request #1180 from Raven95676/reload
perf: 确保完整处理插件所有模块。
2025-04-10 14:45:28 +08:00
anka
bdf25976a3 fix: 少打一个字 2025-04-10 11:28:47 +08:00
anka
87c3aff4ce perf: 简化llm_request工具调用消息成对验证逻辑, 合并两处验证逻辑到一个函数 2025-04-10 11:25:03 +08:00
anka
99350a957a Merge remote-tracking branch 'origin/HEAD' into anka-dev 2025-04-10 11:16:49 +08:00
Soulter
319068dc7e Merge pull request #1179 from zhx8702/feat-platform-plugin-control
feat: 添加插件能针对不同消息平台开启关闭的功能
2025-04-10 11:02:09 +08:00
Soulter
cd18806c39 perf: improve platform compatibility checks 2025-04-10 11:01:04 +08:00
Raila23
95b08b2023 fix:使 begin_dialogs ,预设对话,不会多次插入 2025-04-10 09:18:58 +08:00
baiiylu
0e70f76c86 fix: wrong type of sender_id returned in event.get_sender_id() 2025-04-10 08:03:38 +08:00
Raila23
4d414a2994 增加dequeue_context_length的值的判断,只能在1到max_context_length之间 2025-04-09 22:28:33 +08:00
Raila23
3d22772d4e 新增配置项,允许配置:超出最多携带对话数量 时,一次性丢弃多少条旧消息 2025-04-09 22:12:02 +08:00
Raila23
0b381e2570 新增配置项,允许配置:超出最多携带对话数量 时,一次性丢弃多少条旧消息 2025-04-09 22:10:56 +08:00
Raven95676
f2cc4311c5 fix: optional value 2025-04-09 18:55:20 +08:00
Raven95676
e349671fdf format 2025-04-09 18:45:40 +08:00
Raven95676
01c02d5efa perf: 提取模块清理逻辑到 _purge_modules 方法 2025-04-09 18:11:35 +08:00
zhx
b62b1f3870 feat: 添加插件能针对不同消息平台开启关闭的功能
Squashed:

chore: merge master branch

chore: merge from master branch

chore: rename updateAllPlatformCompatibility to update_all_platform_compatibility for consistency

Reviewed by:

@Raven95676 @Soulter
2025-04-09 17:27:44 +08:00
Soulter
8844830859 Merge pull request #1194 from Raven95676/tools
feat: StarTools添加数据目录获取接口
2025-04-09 16:53:22 +08:00
Soulter
0c51ee4b64 chore: 依赖顺序 2025-04-09 16:53:06 +08:00
Soulter
11920d5e31 docs: add a badge to show plugins num 2025-04-09 16:41:32 +08:00
Raven95676
848ea1eb63 提升健壮性 2025-04-09 16:37:19 +08:00
渡鸦95676
a216519486 Merge branch 'AstrBotDevs:master' into tools 2025-04-09 16:16:26 +08:00
Raven95676
b04606c38e 新增获取数据目录的StarTool 2025-04-09 16:13:48 +08:00
Soulter
38072beea7 🎈 perf: 优化插件市场显示 2025-04-09 15:47:44 +08:00
Soulter
b843f1fa03 Update PULL_REQUEST_TEMPLATE.md 2025-04-09 15:28:18 +08:00
Soulter
560d40e571 Merge pull request #1184 from kterna/master
feat:查看本地插件readme和市场插件star数
2025-04-09 15:23:50 +08:00
Soulter
5f0b8161b7 perf: 优化 WebUI Chat 的流式传输性能 2025-04-09 15:22:35 +08:00
kterna
062d482917 fix 2025-04-09 08:43:16 +08:00
Soulter
39693a27e3 Merge branch 'master' into master 2025-04-09 00:30:51 +08:00
anka
7cd1eeac30 fix: 直接把空字符串改为" "一条消息的content是空字符串 2025-04-08 15:57:38 +00:00
Soulter
bafa473c8e Merge pull request #1157 from AstrBotDevs/feat-streaming
feature: 支持流式输出
2025-04-08 22:53:38 +08:00
Soulter
750cf46b2e 🎈 perf: better ChatPage UI 2025-04-08 17:33:46 +08:00
kterna
68885a4bbc Update astrbot/dashboard/routes/plugin.py
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-04-08 16:30:36 +08:00
Soulter
bcc99a8904 🐛 fix: 修复 permission 过滤算子的 raise_error 参数失效的问题 2025-04-08 14:42:05 +08:00
kterna
59fbd98db3 1 2025-04-08 14:31:35 +08:00
kterna
b70ed425f1 Merge branch 'master' of https://github.com/kterna/AstrBot 2025-04-08 14:05:43 +08:00
kterna
45ef5811c8 1 2025-04-08 14:02:59 +08:00
kterna
3b137ac762 插件管理中查看本地插件的readme 2025-04-08 14:01:14 +08:00
kterna
1ddb0caf73 star显示 2025-04-08 10:47:59 +08:00
Raven95676
ae4c6fe2dd 优化,确保完整处理插件所有模块。为核心方法添加文档。 2025-04-08 10:41:47 +08:00
Jackxwb
b03fe438d0 Merge branch 'master' of https://github.com/AstrBotDevs/AstrBot 2025-04-07 22:50:03 +08:00
Raven95676
db257af58e 提升代码可读性 2025-04-07 22:29:50 +08:00
Raven95676
735368c71b 保证变量名可读性 2025-04-07 22:16:02 +08:00
Raven95676
9e04e3679b 保证内置插件指令被注册 2025-04-07 22:08:29 +08:00
Raven95676
43b8414727 初步实现指令注册 2025-04-07 21:51:41 +08:00
anka
5a00187147 fix: 对历史记录的toolcall验证是否成对, 参考:
https://github.com/run-llama/llama_index/issues/13715
https://github.com/run-llama/llama_index/pull/16214
2025-04-07 18:14:30 +08:00
Raven95676
cb525c7c84 更新下hint( 2025-04-07 17:56:10 +08:00
anka
d88420dd03 fix: 修改获取人类可读的上下文的逻辑, 区分函数调用(无contents)和一般消息 2025-04-07 17:55:12 +08:00
anka
b9a983f8e0 fix: 为函数调用历史记录增加标记, 不读取入上下文 2025-04-07 17:45:35 +08:00
Raven95676
42431ea7db 统一text_chat_stream fallback 2025-04-07 17:43:35 +08:00
Raven95676
f9459e4abb 修复无法通过yield发送消息的问题 2025-04-07 17:38:23 +08:00
anka
72f917d611 fix: gemini只在content不为空的时候加入上下文 2025-04-07 17:31:57 +08:00
Raven95676
9fd1d19e93 分离流式与非流式响应处理 2025-04-07 11:52:29 +08:00
Soulter
062af1ac08 🎈 perf: 优化 WebUI 日志错误处理 2025-04-07 10:38:03 +08:00
Raven95676
41bd76e091 tg适配器最后一次编辑转换markdown 2025-04-07 00:47:52 +08:00
Raven95676
cfd3f4b199 流式输出完成后,将完整的LLM响应设置为事件结果 2025-04-07 00:17:53 +08:00
Soulter
79d38f9597 📦release: v3.5.2 2025-04-06 22:36:31 +08:00
Soulter
b3866559e1 📦release: v3.5.2 2025-04-06 22:35:10 +08:00
Soulter
4d186baa35 Merge pull request #1128 from anka-afk/anka-dev
feature: 实现了 #1127 还有 #1133 还有 #1143
2025-04-06 22:22:01 +08:00
anka
8ed3d5f3db fix: 将openai_source的结果消息链的构造方式和其他统一 2025-04-06 09:12:52 +00:00
anka
f0c8f39b6d 对tg的通过编辑消息的流式传输完善错误捕获 2025-04-06 08:57:18 +00:00
anka
431db8fc9b 对流式输出做错误捕获 2025-04-06 08:47:17 +00:00
anka
ba252c5356 fix: 修正一个偶然发现的命名错误() 2025-04-06 08:12:00 +00:00
Raven95676
a2812c39c0 修正文档注释 2025-04-06 16:05:21 +08:00
Raven95676
0490758820 替换原地修改和删除索引的旧逻辑 2025-04-06 15:36:05 +08:00
Jackxwb
7f56824b42 🐛 修复: 移除路径映射函数中的多余日志记录 2025-04-06 14:52:34 +08:00
Jackxwb
627da3a2bc 分离path_Mapping函数 2025-04-06 14:50:15 +08:00
Soulter
9b36a5c8a6 feat: 增加全平台对流式输出的处理逻辑 2025-04-06 13:43:23 +08:00
Soulter
c1cf2be533 feat: 完善流式处理 2025-04-06 11:56:06 +08:00
Jackxwb
e6b69042de 文件发送时支持路径映射 2025-04-06 01:06:51 +08:00
Soulter
109650faf3 feat: 支持流式输出 2025-04-06 00:56:33 +08:00
Raven95676
e54eaab842 将验证器字典移到类级别,避免重复创建 2025-04-05 21:19:53 +08:00
Raven95676
43b6297b5d reminder将时区设置移入try块,统一为self.timezone 2025-04-05 21:08:52 +08:00
Raven95676
c20f4f5adf 删除默认值,调整logger逻辑 2025-04-05 21:03:02 +08:00
Soulter
dc1f222cd2 fix: 使用 zoneinfo 替代 tzinfo; 默认不设置时区(使用系统默认时区) 2025-04-05 17:27:46 +08:00
Soulter
c2b687212c cleanup 2025-04-05 16:51:06 +08:00
Soulter
849913276d 🎈 perf: 钉钉支持 Markdown 渲染输出
fixes: #1104
2025-04-05 16:29:14 +08:00
Soulter
23579c1e4a 🐛 fix: 阿里百炼应用无法多轮会话
fixes: #1123
2025-04-05 16:21:41 +08:00
Soulter
e031161fd4 🐛 修复: 移除文本输入框的 auto-grow 属性
fixes: #1038
2025-04-05 15:58:17 +08:00
Soulter
4800ee6c0a Merge pull request #1152 from AstrBotDevs/feat-log-filter
 feat: 更新日志发布机制,支持日志级别和内容的字典格式,增加日志筛选功能
2025-04-05 15:49:09 +08:00
Soulter
d3a7fef9b0 🐛 修复: 移除多余的 console 语句 2025-04-05 15:46:45 +08:00
Soulter
40822fe77a feat: 更新日志发布机制,支持日志级别和内容的字典格式,增加日志筛选功能
fixes: #1010
2025-04-05 15:43:40 +08:00
Soulter
837b670213 feat(webui): 支持修改列表项
fixes: #1086
2025-04-05 15:10:44 +08:00
Soulter
57ce69f3fb feat: WebChat 支持语音输出
fixes: #1087
2025-04-05 15:02:34 +08:00
anka
be022c4894 fix: add StarTools to api 2025-04-05 11:55:25 +08:00
anka
8a366964bb feature: 增加时区设置支持 2025-04-05 11:52:51 +08:00
anka
ee86b68470 fix: 漏加classmethod了! 2025-04-05 01:15:56 +08:00
anka
60352307aa fix: 重生之我要苦读设计模式, 终于知道怎么整了哈哈哈: 使用静态类实现工具集合, 并且正确初始化 2025-04-05 01:11:10 +08:00
anka
3ebd2f746f feature: 添加插件工具类, 暂时这么多 2025-04-05 00:51:52 +08:00
anka
1c1a65b637 fix: 全部消息段的检验弄好了! 2025-04-05 00:21:28 +08:00
anka
010e60d029 Merge remote-tracking branch 'origin/HEAD' into anka-dev 2025-04-04 23:13:43 +08:00
Soulter
7a25568861 Merge pull request #1131 from AliveGh0st/feature/gemini-safety-settings
feature:增加对Gemini系列模型的安全设置参数支持
2025-04-04 21:22:58 +08:00
AliveGh0st
5f4f913661 feat: 增加对 Gemini 系列模型的输入安全设置参数支持
fixes: #216

Squashed:

Update astrbot/core/config/default.py

描述更正.

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

🎨 style: clean up

🐛 fix: 修复安全设置参数的默认值为列表
2025-04-04 21:12:51 +08:00
Soulter
ccd0e34a53 Merge pull request #1145 from AstrBotDevs/feat-telegram-markdownv2
 feat: 支持 Telegram MarkdownV2 渲染
2025-04-04 20:54:04 +08:00
Soulter
72f1ffccd3 feat: 支持 Telegram MarkdownV2 渲染
fixes: #649 #907
2025-04-04 20:52:22 +08:00
Soulter
ea7a52945f Merge pull request #1132 from Captain-Slacker-OwO/dify-md
docs: 更新 Dify 平台链接为官方域名
2025-04-04 01:12:19 +08:00
Soulter
89d4d1351a Merge pull request #1135 from AstrBotDevs/feat-dashscope-tts
feat: 支持阿里云百炼 TTS
2025-04-04 01:03:36 +08:00
Soulter
b757c91d93 🐛 fix: 修复无法识别到函数调用异常的问题 2025-04-04 01:02:39 +08:00
Soulter
27203d7a4d 🐛 fix: update voice key name 2025-04-04 00:47:50 +08:00
Soulter
9ad4e18ac5 feat: 支持阿里云百炼 TTS 2025-04-04 00:32:37 +08:00
anka
fcdc8f3ce7 Merge remote-tracking branch 'origin/HEAD' into anka-dev 2025-04-03 21:57:24 +08:00
Captain-Slacker-OwO
78b994b84a docs: 更新 Dify 平台链接为官方域名
将 README 文件中的 Dify 平台链接从旧域名更新为官方域名 dify.ai,确保文档的准确性和权威性。
2025-04-03 19:00:44 +08:00
Soulter
58bfc677e2 🐛 fix: dify error Arg user must be provided
fixes #1073
2025-04-03 16:49:05 +08:00
Soulter
7d17285a0c 🐛 fix: ensure whitelist entries are stripped of whitespace and converted to strings 2025-04-03 16:44:37 +08:00
Soulter
e9eb00a0d4 feat: 插件市场帮助按钮 2025-04-03 16:19:01 +08:00
anka
48d07af574 feature(fix?): 在发送消息之前统一检查消息内容是否为空, 不允许发送空消息, 以解决该消息内容不支持查看以及gemini返回<empty content>问题 2025-04-03 11:50:12 +08:00
Soulter
2fc62efd88 Merge pull request #1116 from AstrBotDevs/feat-log-sse
🏗 refactor: log 通信使用 SSE 替代 Websockets
2025-04-02 21:07:40 +08:00
Soulter
be516d75bd 🐛 fix: upadte method name 2025-04-02 21:06:59 +08:00
Soulter
951d5fde85 🏗 refactor: log 通信使用 SSE 替代 Websockets 2025-04-02 20:59:25 +08:00
Soulter
1389abc052 Merge pull request #1112 from AstrBotDevs/fix-aiocqhttp-empty-plain
修复 aiocqhttp 适配器下空白 plain 导致的报错
2025-04-02 16:27:12 +08:00
Soulter
19ad67a77f 🐛 fix: 修复 aiocqhttp 适配器下空白 plain 导致的 the object is not a proper segment chain 报错问题 2025-04-02 16:24:36 +08:00
Soulter
641f308344 Update README.md 2025-04-01 11:35:56 +08:00
Soulter
9f097fa4d5 Update README.md 2025-04-01 11:33:38 +08:00
Soulter
5ad362c52b Merge pull request #1081 from anka-afk/anka-dev
fix #1074 and add some comment
2025-04-01 10:57:40 +08:00
Soulter
614f238a61 Merge pull request #1072 from zhx8702/feat-add-plugin-md-dialog
feat: 安装完插件后自动弹出插件仓库 README 对话框
2025-04-01 10:56:24 +08:00
zhx
dec91950bc feat: 安装完插件后自动弹出插件仓库 README 对话框 2025-04-01 10:04:04 +08:00
anka
6cef9c23f0 bug fix: #1074 修改最多携带对话数量时出现bug 2025-03-31 22:41:23 +08:00
anka
3f568bf136 Merge remote-tracking branch 'origin/HEAD' into anka-dev 2025-03-31 22:32:40 +08:00
anka
5484b421ce perf: 增加部分注释 2025-03-31 22:30:43 +08:00
Soulter
02f21e07d3 📦 release: v3.5.1 2025-03-31 10:59:32 +08:00
Soulter
fff1f23a83 Update README.md 2025-03-31 00:57:23 +08:00
Soulter
a056ec0d38 Merge pull request #1065 from AstrBotDevs/perf-openai-source-balance
🎈 perf: OpenAI sources supports api key load balance(random)
2025-03-30 22:53:27 +08:00
Soulter
2eb9e5dde3 perf: 添加重试等待 2025-03-30 22:51:34 +08:00
渡鸦95676
627d2a4701 新增重试间隔 2025-03-30 22:33:21 +08:00
Soulter
76895fe86d chore: improve variable names 2025-03-30 22:12:34 +08:00
Soulter
64c3c85780 Merge pull request #1056 from Raven95676/master
perf: 优化无对话情况下设置人格的反馈;若禁用提供商,自动切换到另一个可用的提供商
2025-03-30 22:10:23 +08:00
Soulter
7288348857 🎈 perf: OpenAI sources supports api key load balance(random) 2025-03-30 22:00:45 +08:00
Soulter
62e73299b1 🐛 fix: forcely write shared preference data
Note: this is a fast fix for recent feedbacks, we'll improve its performance.
2025-03-30 21:33:41 +08:00
Raven95676
fe76c41ed8 perf: 若禁用提供商,自动切换到另一个可用的提供商 2025-03-30 15:18:48 +08:00
Raven95676
1a92edf8be perf: 优化无对话情况下设置人格的反馈 2025-03-30 14:38:40 +08:00
Soulter
b63b606a4e docs: 推荐使用 uv 进行手动部署 2025-03-30 10:39:14 +08:00
Soulter
8e2ef3d22b Merge pull request #1050 from advent259141/master
回复空@功能的修复
2025-03-30 00:15:26 +08:00
Gao Jinzhe
c6c4a32283 Add files via upload 2025-03-29 22:37:18 +08:00
Soulter
b70b3b158e feat: 支持 gemini-2.0-flash-exp-image-generation 对图片模态的输入 #1017 2025-03-29 20:51:27 +08:00
Soulter
3d59ab8108 fix: conversation and tool use page refresh 404 2025-03-29 19:17:56 +08:00
Soulter
b6c3089510 🎈 perf: 优化空 at 回复 2025-03-29 19:09:35 +08:00
Soulter
bd92aac280 feat: 支持 /llm 指令快捷启停 LLM 功能 #296 2025-03-29 18:31:07 +08:00
Soulter
5299e802e9 Merge pull request #1046 from AstrBotDevs/feat-docker-embedded-ffmpeg
docker 镜像提供内置 ffmpeg
2025-03-29 17:53:40 +08:00
Soulter
8e5a57d7dd Merge pull request #1045 from Raven95676/master
在lifecycle新增插件资源清理逻辑
2025-03-29 17:53:16 +08:00
Soulter
beaa324fb6 Merge pull request #1012 from Zhenyi-Wang/master
feat: gewechat client增加获取通讯录列表接口
2025-03-29 17:51:35 +08:00
Soulter
79e64fe206 Merge pull request #1011 from left666/left666
feat(core): 在 MessageChain 类中添加 at 和 at_all 方法
2025-03-29 17:50:55 +08:00
Soulter
93f525e3fe 🎈 perf: edge tts 支持使用代理;移除了一些不需要的方法 2025-03-29 17:48:22 +08:00
Soulter
aacb803c64 Merge pull request #999 from Futureppo/master
部分api获取不到model导致key泄露,使用正则表达式过滤掉key内容
2025-03-29 17:43:10 +08:00
Soulter
8a0665b222 🎈 feat: 更新 Dockerfile,添加 Node.js 支持并优化依赖安装 2025-03-29 17:42:31 +08:00
Soulter
20e41a7f73 🐛 fix: newgroup 指令名显示错误 2025-03-29 17:42:31 +08:00
Soulter
93a1699a35 Update README.md 2025-03-29 17:42:31 +08:00
Soulter
c33c07e4af Update README.md 2025-03-29 17:42:31 +08:00
Soulter
c7484d0cc9 Update README.md 2025-03-29 17:42:31 +08:00
Soulter
fb85a7bb35 feat: add demo mode 2025-03-29 17:42:31 +08:00
Soulter
42ff9a4d34 Update README.md 2025-03-29 17:42:31 +08:00
Soulter
005e9eae7c 🐛 fix: 插件更新时没有正确应用加速地址 2025-03-29 17:42:31 +08:00
Soulter
3e325debcc Update README.md 2025-03-29 17:42:31 +08:00
Soulter
a221de9a2b 🐛 fix: 修复 LLM 响应后事件钩子无法生效的问题 2025-03-29 17:42:31 +08:00
Soulter
32b0cc1865 Update README.md 2025-03-29 17:42:31 +08:00
Soulter
bbf85f8a12 🐛 fix: remove error logging for empty result and refresh extensions after upload 2025-03-29 17:42:31 +08:00
Soulter
67a0172b28 📦 release: v3.5.0 2025-03-29 17:42:31 +08:00
zhx
fb19d4d45b fix: install_plugin_from_file 方法load传参数改为文件名 2025-03-29 17:42:31 +08:00
Soulter
a156b1af14 feat: 支持通过指令下载插件 /plugin get 2025-03-29 17:42:31 +08:00
Soulter
a604b4943c 🎈 perf: 优化新版本时的信息显示 2025-03-29 17:42:31 +08:00
pre-commit-ci[bot]
3f0b6435d9 🎈 auto fixes by pre-commit hooks 2025-03-29 17:42:31 +08:00
Gao Jinzhe
e0f029e2cb Add files via upload 2025-03-29 17:42:31 +08:00
Soulter
89d3fd5fab 🎈 perf: 优化 WebUI 对话数据库中文历史检索 2025-03-29 17:42:31 +08:00
Soulter
a38b00be6b 🐛 fix: 修复部分可能形成 SQL 注入的风险 2025-03-29 17:42:31 +08:00
Futureppo
0e8d52b591 :ballon: feat: 使用正则表达式过滤掉 /model 可能暴露的 api_key
Squashed:

更新正则表达式

🎈 auto fixes by pre-commit hooks

Update main.py

Update main.py

chore: bugfixes
2025-03-29 17:40:48 +08:00
Soulter
298c77740d feat: docker 镜像提供内置 ffmpeg #979 2025-03-29 17:26:57 +08:00
Raven95676
c681aae8ee 修复日志问题 2025-03-29 17:25:38 +08:00
Raven95676
faef98b089 在lifecycle新增插件资源清理逻辑 2025-03-29 17:07:12 +08:00
Soulter
84a3e0a30b 🎈 feat: 更新 Dockerfile,添加 Node.js 支持并优化依赖安装 2025-03-29 16:36:02 +08:00
Soulter
69bd553ce0 Merge pull request #1035 from AstrBotDevs/fix-1034-bug
🐛 fix: groupnew 指令名显示错误
2025-03-28 23:46:30 +08:00
Soulter
fd0c0f8975 🐛 fix: newgroup 指令名显示错误 2025-03-28 23:45:19 +08:00
Zhenyi-Wang
860ceb06b4 Merge branch 'Soulter:master' into master 2025-03-28 21:27:25 +08:00
anka
ecf501bf72 Merge remote-tracking branch 'origin/HEAD' into anka-dev 2025-03-28 19:04:35 +08:00
Soulter
81a2ed1e25 Update README.md 2025-03-28 18:20:33 +08:00
Soulter
76ab28338a Update README.md 2025-03-28 13:24:41 +08:00
Soulter
9a56c9630f Update README.md 2025-03-28 13:23:29 +08:00
anka
53b9497c18 perf: 增加部分注释 2025-03-27 21:32:38 +08:00
Soulter
750b16b6ee feat: add demo mode 2025-03-27 15:54:23 +08:00
anka
0ee3e0779a Merge remote-tracking branch 'origin/HEAD' into anka-dev 2025-03-27 15:21:04 +08:00
pre-commit-ci[bot]
333c2d9299 🎈 auto fixes by pre-commit hooks 2025-03-27 03:21:43 +00:00
Zhenyi Wang
ad37ff5048 feat: gewechat client增加获取通讯录列表接口 2025-03-27 11:17:52 +08:00
pre-commit-ci[bot]
33f86f3bde 🎈 auto fixes by pre-commit hooks 2025-03-27 02:56:55 +00:00
Soulter
8acb969a49 Update README.md 2025-03-27 10:39:18 +08:00
left666
b74b5933b8 feat(core): 在 MessageChain 类中添加 at 和 at_all 方法
- 新增 at 方法,用于添加 At 消息到消息链中
- 新增 at_all 方法,用于添加 AtAll 消息到消息链中
2025-03-27 10:30:19 +08:00
Soulter
681c556b7e 🐛 fix: 插件更新时没有正确应用加速地址 2025-03-27 10:04:40 +08:00
anka
1746684e52 perf: 修改部分注释 2025-03-26 23:52:03 +08:00
Soulter
0b93d06555 Update README.md 2025-03-26 20:51:53 +08:00
anka
8a8b8c7c27 Merge remote-tracking branch 'origin/master' into anka-dev 2025-03-26 17:59:53 +08:00
anka
6b6577006d perf: 格式化 2025-03-26 17:59:30 +08:00
Soulter
23ee5e81c9 🐛 fix: 修复 LLM 响应后事件钩子无法生效的问题 2025-03-26 17:56:55 +08:00
Soulter
483f55e4b1 Update README.md 2025-03-26 16:16:03 +08:00
Soulter
1bb1bc2553 🐛 fix: remove error logging for empty result and refresh extensions after upload 2025-03-26 15:43:56 +08:00
Soulter
a4e4e36f94 📦 release: v3.5.0 2025-03-26 15:30:09 +08:00
Soulter
6849415812 Merge pull request #996 from zhx8702/fix-star-manager
fix: install_plugin_from_file 方法load传参数改为文件名
2025-03-26 15:26:53 +08:00
zhx
86f6cb038e fix: install_plugin_from_file 方法load传参数改为文件名 2025-03-26 15:06:33 +08:00
Soulter
7480a1d6ce feat: 支持通过指令下载插件 /plugin get 2025-03-26 14:33:45 +08:00
Soulter
3cd10117dd 🎈 perf: 优化新版本时的信息显示 2025-03-26 14:14:01 +08:00
Soulter
0caf19d390 Merge pull request #937 from advent259141/master
将对只有一个 @ 的消息内容的处理改成调用llm回复
2025-03-26 13:54:43 +08:00
anka
5c14ebb049 Merge remote-tracking branch 'origin/master' into anka-dev 2025-03-26 13:53:21 +08:00
anka
9717a736b1 perf: 更新部分描述 2025-03-26 13:50:54 +08:00
Soulter
9c9ab50d1a 🎈 perf: 优化 WebUI 对话数据库中文历史检索 2025-03-26 13:50:11 +08:00
Soulter
d4bcb8174e 🐛 fix: 修复部分可能形成 SQL 注入的风险 2025-03-26 13:41:18 +08:00
anka
9e7fe773bd perf: 更新部分注释 2025-03-26 11:14:46 +08:00
Soulter
aca18fab0f feat: 优化配置文件中的提示信息,增强可读性 2025-03-26 00:56:51 +08:00
Soulter
691de01b79 feat: 支持设置最多携带对话数量 2025-03-26 00:46:15 +08:00
Soulter
3383f15142 Merge pull request #988 from Soulter/NiceAir/master
 feat: Update UI elements and improve layout in various components
2025-03-25 23:17:11 +08:00
Soulter
84c1593889 feat: Update UI elements and improve layout in various components 2025-03-25 21:52:15 +08:00
Soulter
3c80fa1e33 Update README.md 2025-03-25 21:31:23 +08:00
Soulter
06b16a1deb Merge pull request #983 from Soulter/feat-conversation-webui-mgr
 支持 WebUI 对话管理
2025-03-25 21:26:00 +08:00
Soulter
4c4246fb09 Merge pull request #982 from NiceAir/master
添加对gewe的表情包、引用消息、视频的支持
2025-03-25 21:25:00 +08:00
Soulter
364be1e9f6 🐛 fix: Handle missing defusedxml dependency for Gewechat message parsing 2025-03-25 21:21:38 +08:00
NiceAir
f959ed71aa feat: Gewechat 支持表情包、引用消息、视频
Co-authored-by: Soulter <905617992@qq.com>
2025-03-25 21:00:12 +08:00
anka
5c4326c302 perf: 部分详细注释, 符合PEP8标准 2025-03-25 20:53:23 +08:00
Soulter
125fc3a622 feat: 支持 WebUI 对话管理 2025-03-25 19:44:46 +08:00
Soulter
6b9e785db3 Merge pull request #968 from Soulter/pre-commit-ci-update-config
🎈 pre-commit autoupdate
2025-03-25 15:03:39 +08:00
Soulter
25d34e9a43 Merge pull request #974 from zhx8702/feat-webui-add-search-keys
feat: 插件市场列表卡片过滤条件提出变量保持一致
2025-03-25 15:03:09 +08:00
Soulter
457d4aa1dc Merge pull request #976 from Raven95676/master
Improves Telegram adapter termination
2025-03-25 15:01:04 +08:00
Raven95676
ff0c0992ff Improves Telegram adapter termination 2025-03-25 14:46:20 +08:00
Soulter
d379e012c4 🐛 fix: telegram /start issue #751 2025-03-25 14:03:46 +08:00
zhx
151fff26fd feat: 插件市场列表卡片过滤条件提出变量保持一致 2025-03-25 13:50:16 +08:00
Soulter
3d0d561215 Update compose.yml 2025-03-25 13:24:37 +08:00
Soulter
22d586ed7b Update compose.yml 2025-03-25 13:24:19 +08:00
Soulter
6dc19b29e8 🐛 fix: remove redundant validation call in config validation function #901 2025-03-25 12:56:48 +08:00
Soulter
50975a87d4 🐛 fix: handle message sending failures with error logging 2025-03-25 12:34:43 +08:00
Soulter
ce721d9f0f 🐛 fix: platform adapter server blocks ctrl+c 2025-03-25 11:31:46 +08:00
Soulter
20510a33f7 feat: improve pyproject and use uv as package mgr 2025-03-25 11:07:20 +08:00
pre-commit-ci[bot]
3abd9c8763 🎈 pre-commit autoupdate
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.11.0 → v0.11.2](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.0...v0.11.2)
2025-03-24 17:08:12 +00:00
Soulter
e9eff7420b feat: 更加完善和美观的 本地 Markdown 渲染 2025-03-25 00:56:19 +08:00
Soulter
64c250c9d8 🎈perf: 优化可能的 conversation 为 None 的问题 2025-03-25 00:06:25 +08:00
Soulter
8047f82bfd 🎈perf: 优化删除插件目录的逻辑,抛出异常细节;完善 mcp 未安装时的提示 2025-03-24 23:07:56 +08:00
Soulter
af6467fb3d Merge pull request #962 from zhx8702/feat-webui-add-double-confirm
feat: 删除插件添加二次确认,插件列表添加非空判断
2025-03-24 23:01:43 +08:00
zhx
3ff1664aec feat: 删除多余代码 2025-03-24 20:27:05 +08:00
zhx
34ea2b44b8 Merge remote-tracking branch 'upstream/master' into feat-webui-add-double-confirm 2025-03-24 19:42:47 +08:00
Soulter
6c8d851109 Merge pull request #955 from Raven95676/master
Telegram适配器消息处理功能增强
2025-03-24 18:10:51 +08:00
Soulter
d678299a74 Merge branch 'master' into master 2025-03-24 18:10:27 +08:00
Soulter
7aed0db2b6 Merge pull request #951 from IGCrystal/master
fix: fix SSLCertVerificationError
2025-03-24 18:05:49 +08:00
Soulter
0355524345 Merge branch 'master' into master 2025-03-24 17:58:00 +08:00
Soulter
0a43e4672e style: format codes 2025-03-24 17:57:28 +08:00
zhx
71e0ccdfec feat: 删除插件添加二次确认,插件列表添加非空判断 2025-03-24 16:41:54 +08:00
冰苷晶
1df33ac3c8 fix: fix error 2025-03-24 13:28:14 +08:00
pre-commit-ci[bot]
7334090ac1 🎈 auto fixes by pre-commit hooks 2025-03-24 05:20:37 +00:00
冰苷晶
6b0f044198 fix: fix other errors 2025-03-24 13:20:05 +08:00
pre-commit-ci[bot]
ddf54c9cf8 🎈 auto fixes by pre-commit hooks 2025-03-24 04:32:21 +00:00
IGCrystal
7c64e184e2 Merge branch 'Soulter:master' into master 2025-03-24 12:32:16 +08:00
渡鸦95676
a904db033c Merge branch 'Soulter:master' into master 2025-03-24 12:19:17 +08:00
渡鸦95676
b234856b02 Remove unused variable
移除以通过ruff检查
在Ubuntu24.04LTS中,移除未见对现有功能的影响
2025-03-24 11:36:46 +08:00
Soulter
89d51d2afc 🎈 perf: config UI 2025-03-24 11:36:38 +08:00
Soulter
37cb9678e9 Merge pull request #826 from XuYingJie-cmd/master
新增了关于gewe发送视频的功能
2025-03-24 11:25:24 +08:00
pre-commit-ci[bot]
0500ff333a 🎈 auto fixes by pre-commit hooks 2025-03-24 02:50:28 +00:00
Raven95676
08528510ef Fix incorrect handling of reply messages within topics 2025-03-24 10:41:33 +08:00
Raven95676
ddbd03dc1e Adds sticker handling in Telegram adapter 2025-03-24 10:40:20 +08:00
Soulter
ade87f378a 🎈 perf: UI 优化 2025-03-24 00:32:40 +08:00
冰苷晶
4db14b905f fix: fix error 2025-03-23 23:40:06 +08:00
pre-commit-ci[bot]
b669b31451 🎈 auto fixes by pre-commit hooks 2025-03-23 15:07:22 +00:00
冰苷晶
1cb2b62f81 fix: fix error 2025-03-23 23:02:34 +08:00
Soulter
e5828713cf 🎈 perf: improve ChatPage and ConfigPage UI 2025-03-23 22:57:02 +08:00
冰苷晶
d10cb84068 fix: fix SSLCertVerificationError 2025-03-23 22:55:07 +08:00
Soulter
4222f8516f Merge pull request #844 from AraragiEro/mcp_adapt
支持 MCP 服务并优化函数调用流程
2025-03-23 22:35:35 +08:00
Soulter
7f998c7611 chore: remove useless print output 2025-03-23 22:28:00 +08:00
Soulter
db46000337 🎨 style: format codes 2025-03-23 22:22:11 +08:00
Soulter
1aac8d8041 feat: 适配完整的 function-calling 流程 2025-03-23 22:21:47 +08:00
Soulter
c59c8e05f7 🐛 fix: tools result 2025-03-23 17:03:18 +08:00
Soulter
4942d0a629 feat: 在工具使用页面添加函数调用信息提示和链接功能 2025-03-23 17:00:38 +08:00
Soulter
873b7715f4 🎈 perf: 优化 MCP Client 异步 Event 管理 2025-03-23 16:51:28 +08:00
pre-commit-ci[bot]
98e7ed6920 🎈 auto fixes by pre-commit hooks 2025-03-23 08:34:05 +00:00
Soulter
046f5e645e feat: 完善 MCP 管理和实现 WebUI MCP 相关的页面 2025-03-23 16:33:44 +08:00
pre-commit-ci[bot]
f5e5a7094c 🎈 auto fixes by pre-commit hooks 2025-03-23 06:39:13 +00:00
Gao Jinzhe
154125fee6 Add files via upload 2025-03-23 14:35:44 +08:00
pre-commit-ci[bot]
9f8e960ebe 🎈 auto fixes by pre-commit hooks 2025-03-23 03:31:20 +00:00
Soulter
4179b0be0a chore: 优化注解格式和 requirements.txt 2025-03-23 11:31:10 +08:00
Soulter
28bafa38db Merge branch 'master' into mcp_adapt 2025-03-23 11:01:44 +08:00
Soulter
b07552565e Merge pull request #926 from Soulter/perf-graceful-shutdown
支持所有消息平台的优雅退出
2025-03-23 10:56:56 +08:00
Soulter
c4427471d2 🎨 style: format codes 2025-03-23 00:25:26 +08:00
Soulter
08f81c6784 🐛 fix: 修复图片没有被存储到上下文中的问题 2025-03-23 00:23:42 +08:00
Soulter
a471e98aca 🐛 fix: Telegram 下无法识别图片描述(Caption) #910 2025-03-23 00:23:01 +08:00
Soulter
75a8fcc8a0 🐛 fix: 修复 Telegram 下非默认群组话题引用消息异常 #906 2025-03-22 23:39:21 +08:00
Soulter
46ef76c168 feat: 支持消息平台的热重载 2025-03-22 19:54:54 +08:00
Soulter
66637446c9 Merge remote-tracking branch 'origin/master' into perf-graceful-shutdown 2025-03-22 19:26:35 +08:00
Soulter
21efeb888a Merge pull request #904 from LunarMeal/master
新增了newgroup指令
2025-03-22 19:18:06 +08:00
Soulter
a4ee8b5322 Merge remote-tracking branch 'origin/master' into LunarMeal/master 2025-03-22 19:17:12 +08:00
Soulter
36519ac47e 🐛 fix: groupnew 设置为管理员指令 2025-03-22 19:14:58 +08:00
Soulter
3f514fceca 🎨 style: format codes 2025-03-22 19:07:47 +08:00
pre-commit-ci[bot]
c2249fdfac 🎈 auto fixes by pre-commit hooks 2025-03-22 11:06:42 +00:00
Soulter
c610719a44 feat: 为各平台适配器支持优雅关闭 2025-03-22 19:02:49 +08:00
Soulter
36a6c2461a 🐛 fix: 修复 Telegram Topic 群组下LLM 上下文及主动消息混乱的问题 #908 2025-03-22 18:15:43 +08:00
Soulter
c29f22c39e Update PLUGIN_PUBLISH.yml 2025-03-22 15:51:35 +08:00
Soulter
30d3062944 🎈 perf: 优化钉钉在配置错误之后堵塞整个线程的问题 #885
a.k.a 帮钉钉擦屁股
2025-03-22 15:44:42 +08:00
Soulter
69ba75abf4 Update README.md 2025-03-22 01:26:03 +08:00
Soulter
e4d486fec5 docs: 宝塔面板部署方式 2025-03-22 00:42:04 +08:00
Soulter
f242144dcf 更新 README.md 2025-03-21 19:21:35 +08:00
Soulter
02dee2d664 🎈 perf: add error handling for missing pyffmpeg library in video sending functionality 2025-03-21 16:51:23 +08:00
Soulter
a3dd2c3069 Merge remote-tracking branch 'origin/master' into XuYingJie-cmd/master 2025-03-21 16:49:15 +08:00
Soulter
a23425e8aa Merge pull request #781 from Moyuyanli/master
添加gewe的群相关操作
2025-03-21 16:31:10 +08:00
Moyuyanli
be79ddc9a3 fix:去掉跟post_text功能相同的接口方法 2025-03-21 16:24:31 +08:00
Soulter
7d71015e8c Update README.md 2025-03-21 16:12:25 +08:00
Soulter
ad54549b51 Update README.md 2025-03-21 15:58:40 +08:00
Soulter
6cf032a164 Update compose.yml 2025-03-21 11:06:22 +08:00
Soulter
6390d796ac Update compose.yml 2025-03-21 11:05:44 +08:00
Soulter
98b8411905 Update compose.yml 2025-03-21 10:53:09 +08:00
LunarMeal
ddf1029afa Merge branch 'master' of https://github.com/LunarMeal/AstrBot 2025-03-20 22:53:29 +08:00
LunarMeal
1effbc5cc9 fix 2025-03-20 22:53:21 +08:00
pre-commit-ci[bot]
414b645e9f 🎈 auto fixes by pre-commit hooks 2025-03-20 14:42:37 +00:00
LunarMeal
398c76f496 新增了newgroup指令 2025-03-20 22:39:49 +08:00
Soulter
1bc456dd95 🎈 perf: 改善一些术语描述 2025-03-20 20:31:36 +08:00
Soulter
2e8421884e Merge pull request #864 from Soulter/pre-commit-ci-update-config
🎈 pre-commit autoupdate
2025-03-20 20:23:45 +08:00
Soulter
70d9b193ac 🐛 fix: 修复私聊下 get_group 的一些问题 2025-03-20 20:18:20 +08:00
Moyuyanli
b49c11004a fix:还原回原来的依赖信息 2025-03-20 19:57:35 +08:00
Soulter
34843eea90 🎨 style: format codes 2025-03-20 18:07:24 +08:00
pre-commit-ci[bot]
2d6d7f31e8 🎈 auto fixes by pre-commit hooks 2025-03-20 10:06:11 +00:00
Soulter
7a24cbff1c feat: 支持 aiocqhttp 适配器下的获取群消息 2025-03-20 18:05:44 +08:00
pre-commit-ci[bot]
1e7eb2cf1c 🎈 auto fixes by pre-commit hooks 2025-03-20 09:21:32 +00:00
Soulter
361256e016 chore: 添加了一些 gewechat client 的注释 2025-03-20 17:20:32 +08:00
Soulter
8838dbd003 🎨 style: format codes 2025-03-20 16:54:27 +08:00
pre-commit-ci[bot]
13a95e1f2b 🎈 auto fixes by pre-commit hooks 2025-03-20 08:42:40 +00:00
Soulter
1aaa451a3e Merge branch 'master' into Moyuyanli/master 2025-03-20 16:42:13 +08:00
Soulter
cbba81e54d 🐛 fix: 无法接收图片 aiocqhttp 2025-03-20 16:03:41 +08:00
Soulter
370868dfac 🎈 perf: 消息平台和配置提供商配置页中,自动更新旧的配置,添加新的配置项 2025-03-20 13:22:49 +08:00
Soulter
77f692aae2 🎈 perf: 配置项显示优化 2025-03-20 13:17:27 +08:00
Soulter
9318e205ea feat: 阿里云百炼应用支持 RAG 应用 #878 2025-03-20 13:17:06 +08:00
Soulter
ebcc717c19 🎈 perf: Dify 下支持更多类型的图片输入及提高代码复用性 #893
🐛 fix: 修复飞书下无法进行图片输入的问题
2025-03-20 11:21:45 +08:00
Soulter
4c16b564ee 🎈 perf: 忽略微信团队消息 #859 2025-03-19 01:09:01 +08:00
Soulter
e2283d1453 🐛 fix: 修复 dify 下某些修改了 LLM 响应的插件可能不生效的问题 #876 2025-03-19 01:05:28 +08:00
Soulter
d891801c5a v3.4.39 2025-03-18 22:43:35 +08:00
Soulter
de75386944 🎈 perf: 登录后检查默认密码和弹出修改警告 2025-03-18 22:41:33 +08:00
Soulter
82dc37de50 style: format codes 2025-03-18 22:21:47 +08:00
Soulter
b6fa7f62dc chore: 添加安全提示信息 2025-03-18 22:18:01 +08:00
Soulter
f9e0a95c5e chore: 默认地址改回 0.0.0.0 2025-03-18 22:15:22 +08:00
pre-commit-ci[bot]
b2c6e12647 🎈 auto fixes by pre-commit hooks 2025-03-17 17:10:06 +00:00
pre-commit-ci[bot]
caffb83780 🎈 pre-commit autoupdate
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.9.10 → v0.11.0](https://github.com/astral-sh/ruff-pre-commit/compare/v0.9.10...v0.11.0)
2025-03-17 17:09:59 +00:00
Soulter
8882cb5479 v3.4.38 2025-03-18 00:54:51 +08:00
Soulter
75dace2dee 🎈 perf: 优化配置页的显示 2025-03-18 00:16:47 +08:00
Soulter
ad6487d042 🐛 fix: 修复部分指令可能造成的配置类型问题 2025-03-17 23:44:04 +08:00
Soulter
a91604e8ab Merge pull request #853 from IGCrystal/master
🎈 perf: 优化了iframe窗口,新增跳转按钮
2025-03-17 23:25:26 +08:00
Soulter
c364f7c643 🎈 perf: Dify 下当只有图片输入时的默认 prompt #837 2025-03-17 23:17:07 +08:00
Soulter
53435ba184 🐛 fix: 修复 model_config 中自定义的配置项(如温度)类型自动变回 string #854 2025-03-17 23:11:57 +08:00
Soulter
25f8d5519b 🐛 fix: LLOnebot 合并消息转发错误 #842 2025-03-17 22:42:48 +08:00
Moyuyanli
2e4fef6c66 feat:添加消息记录器 2025-03-17 16:02:55 +08:00
冰苷晶
80b2b7dc00 🎈 perf: 优化了iframe窗口 2025-03-16 21:35:30 +08:00
Alero
8585cd8e21 修复codecheck 2025-03-15 20:26:17 +08:00
Alero
9fa2a7eeea 修复codecheck 2025-03-15 20:24:36 +08:00
pre-commit-ci[bot]
2d1f74228d 🎈 auto fixes by pre-commit hooks 2025-03-15 12:10:17 +00:00
Alero
3d6f7aa0e1 修复codecheck 2025-03-15 20:09:49 +08:00
pre-commit-ci[bot]
3dea60366a 🎈 auto fixes by pre-commit hooks 2025-03-15 11:54:09 +00:00
Alero
d4d9a1df4c feat:新增MCP服务支持并优化工具调用逻辑
引入MCP客户端支持,增加mcp_server.json配置样例,完善工具描述生成及调用逻辑以支持MCP服务工具功能。同时调整相关逻辑以区分本地工具与MCP工具的调用方式,提升扩展性和灵活性。
2025-03-15 19:47:06 +08:00
Soulter
7d6975fd31 Merge pull request #832 from IGCrystal/master
🎈 perf: 优化iframe窗口,加入了关闭按钮
2025-03-15 14:25:16 +08:00
IGCrystal
08be52ed17 Merge branch 'Soulter:master' into master 2025-03-15 12:05:27 +08:00
邹永赫
682a7700c2 Merge pull request #835 from zouyonghe/master
修改注册函数工具时的打印信息
2025-03-15 12:20:32 +09:00
pre-commit-ci[bot]
9d87009216 🎈 auto fixes by pre-commit hooks 2025-03-15 03:16:51 +00:00
邹永赫
ef86838f62 修改注册函数工具时的打印信息 2025-03-15 12:15:05 +09:00
Soulter
35468233f8 🎈 perf: supports for customizing webui host, wecom webhook server host, qq official webhook server host #821 2025-03-15 01:21:36 +08:00
Soulter
26e229867d 🐛fix: 可能的QQ平台回复消息带有末尾空白的问题 #822 2025-03-15 00:57:17 +08:00
Soulter
3a1578b3c6 feat: 支持 Dify 文件、图片、视频、音频输出。#819 2025-03-15 00:51:32 +08:00
冰苷晶
d5e3d2cbbc 🎈 perf: 优化iframe窗口,加入了关闭按钮 2025-03-14 20:23:15 +08:00
Moyuyanli
c095248176 Merge remote-tracking branch 'origin/master' 2025-03-14 18:30:42 +08:00
Moyuyanli
44601c8954 fix:修复gewe的ModContacts消息类型 2025-03-14 18:30:27 +08:00
Soulter
135dbb8f07 style: clean codes 2025-03-14 18:02:00 +08:00
pre-commit-ci[bot]
c95682a0c7 🎈 auto fixes by pre-commit hooks 2025-03-14 09:11:21 +00:00
Moyuyanli
d177b9f7fa feat:添加主动添加好友事件 2025-03-14 17:11:10 +08:00
徐英杰
9b57615d94 新增了关于gewe发送视频的功能 2025-03-14 16:19:41 +08:00
Soulter
c03f3eacd1 Update README.md 2025-03-13 23:03:36 +08:00
Soulter
a26e395932 Merge pull request #817 from Soulter/feat-parse-reply
[Feature] 添加了 LLM 对消息平台引用回复内容的感知
2025-03-13 21:06:44 +08:00
Soulter
0870b87c96 🐛 fix: 获取引用消息失败时没有将引用消息段加入消息链 2025-03-13 20:59:52 +08:00
Soulter
b52a44a7dd 🎨 stype: format codes 2025-03-13 20:44:08 +08:00
Soulter
0a290aafef Merge pull request #815 from diudiu62/perf-gewechat
微信有未处理的消息类型,导致控制台打印太多的日志
2025-03-13 20:39:39 +08:00
Soulter
9014d4c410 🎨 style: format codes 2025-03-13 20:36:41 +08:00
pre-commit-ci[bot]
60e58b4f5f 🎈 auto fixes by pre-commit hooks 2025-03-13 09:52:03 +00:00
Soulter
620e74a6aa Merge branch 'master' into feat-parse-reply 2025-03-13 17:51:12 +08:00
Soulter
efa287ed35 feat: 支持 LLM 对引用消息的感知 #783 2025-03-13 17:40:28 +08:00
Soulter
a24eb9d9b0 🏗 refactor: clean up AstrBotConfig component markup for improved readability 2025-03-13 17:02:58 +08:00
Soulter
bd3dab8aae 🐛 fix: 插件管理的插件简介太长 “帮助”“操作”图标不显示 #790 2025-03-13 17:02:58 +08:00
Soulter
4fe1ebaa5b 🏗 refactor: improve styling and layout of AstrBotConfig component for enhanced readability 2025-03-13 17:02:58 +08:00
Soulter
c5e944744b 🏗 refactor: enhance ConfigPage layout and styling for better user experience 2025-03-13 17:02:58 +08:00
Soulter
0c396181f7 🏗 refactor: 配置页样式重写 2025-03-13 17:02:58 +08:00
Soulter
0034474219 🐛 fix: sent message to wrong topic in topic group #801 2025-03-13 17:02:58 +08:00
shuiping233
8136ad8287 修复命令参数报错信息无法发送至qq官方机器人平台的bug 2025-03-13 17:02:58 +08:00
Soulter
681940d466 🐛 fix: 修复重载插件时函数工具可能多次家在的问题 2025-03-13 17:02:58 +08:00
Soulter
16488506e8 🐛 fix: 修复部分情况下文件无法上传到 Telegram 群组的问题 #601 2025-03-13 17:02:58 +08:00
邹永赫
122fccc041 修复无法发送非嵌套的转发消息的问题 2025-03-13 17:02:58 +08:00
邹永赫
9d0ad35403 支持嵌套转发,里层包含多条信息 2025-03-13 17:02:58 +08:00
邹永赫
f9ec97e026 支持嵌套转发 2025-03-13 17:02:58 +08:00
Soulter
95495a2647 🏗 refactor: clean up AstrBotConfig component markup for improved readability 2025-03-13 16:40:59 +08:00
Soulter
e3310a605c 🐛 fix: 插件管理的插件简介太长 “帮助”“操作”图标不显示 #790 2025-03-13 16:36:35 +08:00
Soulter
b55719bf28 🏗 refactor: improve styling and layout of AstrBotConfig component for enhanced readability 2025-03-13 15:59:20 +08:00
diudiu62
b957b51279 已知消息类型,没有业务处理,只是避免控制台打印太多的日志 2025-03-13 15:55:22 +08:00
Soulter
90bcfab369 🏗 refactor: enhance ConfigPage layout and styling for better user experience 2025-03-13 15:44:52 +08:00
Soulter
f8a8e30641 🏗 refactor: 配置页样式重写 2025-03-13 15:37:53 +08:00
Soulter
25cb98e7a7 🐛 fix: sent message to wrong topic in topic group #801 2025-03-13 13:02:22 +08:00
Soulter
03e1bb7cf9 Merge pull request #807 from shuiping233/fix-#806
修复命令参数报错信息无法发送至qq官方机器人平台的bug
2025-03-13 10:05:24 +08:00
Soulter
85dbb24f3a 🐛 fix: 修复重载插件时函数工具可能多次家在的问题 2025-03-12 23:37:24 +08:00
shuiping233
d817635782 修复命令参数报错信息无法发送至qq官方机器人平台的bug 2025-03-12 18:09:25 +08:00
Soulter
2f4f237810 🐛 fix: 修复部分情况下文件无法上传到 Telegram 群组的问题 #601 2025-03-12 14:14:45 +08:00
邹永赫
5ac94d810f Merge pull request #794 from zouyonghe/dev/nested-forward
修复无法发送非嵌套的转发消息的问题
2025-03-12 12:01:33 +09:00
邹永赫
39dc46dc25 修复无法发送非嵌套的转发消息的问题 2025-03-12 11:59:53 +09:00
邹永赫
0d9cf725f7 Merge pull request #792 from zouyonghe/dev/nested-forward
支持嵌套转发,里层包含多条信息
2025-03-12 11:17:16 +09:00
邹永赫
e55dbead5b 支持嵌套转发,里层包含多条信息 2025-03-12 11:14:54 +09:00
邹永赫
7d046e5b30 Merge pull request #788 from zouyonghe/dev/nested-forward
支持嵌套转发
2025-03-12 08:50:50 +09:00
邹永赫
8b4693cf66 支持嵌套转发 2025-03-12 08:39:54 +09:00
Soulter
a1172c9a82 feat: 支持解析回复消息 #783 2025-03-11 23:27:10 +08:00
Soulter
1ed2bd33f0 🐛 fix: 修复插件更新时显示未知更新的问题 2025-03-11 22:38:25 +08:00
Soulter
4c159bd0ba Merge pull request #785 from shuiping233/fix-qq-offical-image-upload-issue
修复了使用Image.fromBytes等包装的图片消息链无法通过qq官方机器人适配器发送的bug
2025-03-11 22:10:27 +08:00
Soulter
050654b2a9 🐛 fix: 修复 QQ 官方机器人适配器下发送base64图片消息段报错的问题。
Co-authored-by: shuiping233 <1944680304@qq.com>
2025-03-11 22:08:13 +08:00
Soulter
61b261e1b2 Merge pull request #780 from beat4ocean/master
fix: 修复gewechat平台用户本人发消息触发消息回复的bug
2025-03-11 21:55:44 +08:00
shuiping233
017b010206 修复了使用Image.fromBytes等包装的图片消息链无法通过qq官方机器人适配器发送的bug 2025-03-11 21:17:08 +08:00
pre-commit-ci[bot]
00f5189f58 🎈 auto fixes by pre-commit hooks 2025-03-11 09:16:43 +00:00
Moyuyanli
4a8309ed1f style:idea默认格式化了部分代码
feat:添加根据消息事件获取群信息的接口
2025-03-11 17:10:55 +08:00
Moyuyanli
76cfc31a1d feat:添加 Group 类型 2025-03-11 17:10:04 +08:00
Moyuyanli
d9ec434699 feat:gewe的client添加 添加好友接口
feat:gewe的client添加 获取群信息/群成员接口
feat:gewe的client添加 添加群成员为好友接口
2025-03-11 17:08:33 +08:00
Soulter
239f3c40be 🎈 perf: 优化 WebUI 边栏宽度 2025-03-11 16:11:34 +08:00
Soulter
09c8c6e670 🐛 fix: 修复 aiocqhttp 下可能的设置管理员无效的问题 2025-03-11 15:52:30 +08:00
beat4ocean
7e4ad01c94 Merge branch 'Soulter:master' into master 2025-03-11 15:52:23 +08:00
beat4ocean
ed98e269ef Merge remote-tracking branch 'origin/master' 2025-03-11 15:48:44 +08:00
beat4ocean
b47d63334f fix: 修复gewechat平台用户本人发消息触发消息回复的bug 2025-03-11 15:48:28 +08:00
Soulter
5e2a3a5aea fix: 修复部分情况下 EdgeTTS 无法使用的问题
Co-authored-by: 需要哦 <2687427560@qq.com>
2025-03-11 15:29:51 +08:00
Soulter
1a7eb21fc7 Revert "🐛 fix: 修复 gewechat 部分场景下下载图片报错 #700"
This reverts commit c38fa77ce6.
2025-03-11 14:54:41 +08:00
Soulter
834a51cdc9 🐛 fix: 修复 OpenAI TTS API TypeError 报错 #755 2025-03-11 14:30:59 +08:00
Soulter
1b69d99c06 🐛 fix: 修复更新插件后插件重载不完全的问题 2025-03-11 14:20:24 +08:00
Soulter
ad189933c6 Merge pull request #775 from roeseth/master
update compose.yml to mount system time and tz
2025-03-11 12:49:38 +08:00
Soulter
9d86ff32de Merge pull request #774 from Soulter/pre-commit-ci-update-config
🎈 pre-commit autoupdate
2025-03-11 11:40:57 +08:00
Soulter
278bb57a58 Merge pull request #772 from beat4ocean/master
fix: 修复个人微信非第一次登陆情况,已记录gewechat的appid失效设备不存在导致无法重新登陆个人微信的bug
2025-03-11 11:40:07 +08:00
pre-commit-ci[bot]
0ba494e0ba 🎈 auto fixes by pre-commit hooks 2025-03-11 02:11:25 +00:00
roeseth
8b247054bb update compose.yml to mount system time and tz 2025-03-10 19:07:45 -07:00
pre-commit-ci[bot]
7c5c8e4e0d 🎈 auto fixes by pre-commit hooks 2025-03-11 00:55:01 +00:00
beat4ocean
ad106a27f3 Merge branch 'Soulter:master' into master 2025-03-11 08:54:55 +08:00
beat4ocean
9d6f61b49e fix: 修复非第一次登陆情况,已记录的gewechat的appid失效设备不存在导致无法重新登陆的bug 2025-03-11 08:48:37 +08:00
pre-commit-ci[bot]
02368954a0 🎈 auto fixes by pre-commit hooks 2025-03-10 17:09:25 +00:00
pre-commit-ci[bot]
b477a35a01 🎈 pre-commit autoupdate
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.9.9 → v0.9.10](https://github.com/astral-sh/ruff-pre-commit/compare/v0.9.9...v0.9.10)
2025-03-10 17:09:18 +00:00
Soulter
16622887de perf: 在调用插件异常时更完整的报错信息 2025-03-11 00:47:37 +08:00
Soulter
9059d1fb17 feat: 支持在对话隔离情况下可以将群聊加入白名单 #746 2025-03-11 00:34:29 +08:00
Soulter
df2b008d82 Merge pull request #744 from roeseth/fix-local-timezone
Use system local time zone instead of hardcoded UTC+8
2025-03-11 00:21:43 +08:00
Soulter
0da871efd0 chore: 日志完善 2025-03-10 23:58:42 +08:00
Soulter
1c55349f81 fix: 钉钉 webui 文档 2025-03-10 23:58:42 +08:00
Soulter
9309fa1e81 修复fishaudio默认baseurl不可用的问题 2025-03-10 01:32:26 +08:00
Soulter
5996189f91 Update README.md 2025-03-09 22:25:45 +08:00
Soulter
bd2b984bfb v3.4.37 2025-03-09 22:14:23 +08:00
pre-commit-ci[bot]
194409a117 🎈 auto fixes by pre-commit hooks 2025-03-09 13:23:52 +00:00
roeseth
27978b216d use system local timezone instead of hardcoded UTC+8 2025-03-09 06:18:53 -07:00
Soulter
c38fa77ce6 🐛 fix: 修复 gewechat 部分场景下下载图片报错 #700 2025-03-09 18:10:38 +08:00
Soulter
3eb49f7422 feat: 支持设置私聊是否需要唤醒前缀唤醒 #735 2025-03-09 18:03:23 +08:00
Soulter
1989d615d2 🌈 style: format codes 2025-03-09 17:48:59 +08:00
Soulter
239412d265 feat: 支持接入钉钉 #643 2025-03-09 17:47:51 +08:00
Soulter
375a419a9e Merge pull request #732 from xiewoc/master
Update aiocqhttp_platform_adapter.py
2025-03-09 12:36:48 +08:00
Soulter
875c8ab424 ci: upate astrbot webui build cis 2025-03-09 11:31:10 +08:00
Soulter
c9bfc810ce ci: upload astrbot webui build ci 2025-03-09 11:26:10 +08:00
Soulter
46ecb16949 🐛 fix: 无法正常保存插件的 list 类型配置 #737 2025-03-09 11:12:24 +08:00
Soulter
f6dc16f17b style: format codes 2025-03-08 20:55:25 +08:00
Soulter
4eef42f730 refactor: 移除未使用的 defineEmits 导入 2025-03-08 20:53:43 +08:00
Soulter
8612d9a771 docs: update changelogs 2025-03-08 20:37:46 +08:00
Soulter
0caff054f5 feat: 会话控制器支持自定义会话ID算子 2025-03-08 20:29:42 +08:00
Soulter
4aa91ad599 feat: 支持当消息只有@bot时,下一条发送人的消息直接唤醒机器人 2025-03-08 19:55:24 +08:00
Soulter
7a0864f5c2 feat: 推荐插件页面 2025-03-08 18:58:50 +08:00
Soulter
73dc0dfcf6 perf: 插件市场支持显示插件 logo 2025-03-08 17:31:08 +08:00
Soulter
1ff9a69339 chore: plugin logo 2025-03-08 17:23:25 +08:00
Soulter
179eb5d847 feat: 优化了插件卡片的 UI,插件卡片支持显示 logo 2025-03-08 17:13:36 +08:00
Soulter
52c868828c perf: 插件更新、保存配置均支持热重载 2025-03-08 15:22:56 +08:00
Soulter
7eea4615b6 perf: 优化了日志显示 2025-03-08 15:22:22 +08:00
Soulter
d9b351df1a fix: 修复主动人格情况下人格失效的问题 #719 #712 2025-03-08 14:14:14 +08:00
pre-commit-ci[bot]
d6a785b645 🎈 auto fixes by pre-commit hooks 2025-03-08 04:33:19 +00:00
xiewoc
79db828a01 Update aiocqhttp_platform_adapter.py 2025-03-08 12:30:49 +08:00
Soulter
a5ffb0f8dc perf: 安装/更新插件后直接热重载而不重启;更新 plugin 指令 2025-03-08 00:20:48 +08:00
Soulter
9492fcde74 perf: 完善了插件的启用和禁用的生命周期管理 2025-03-07 23:44:07 +08:00
Soulter
d2456ce4cd Update README.md 2025-03-07 10:52:09 +08:00
Soulter
7de27abc8d 🐛 fix: Telegram适配器使用代理地址无法获取图片 #723 2025-03-07 09:05:00 +08:00
Soulter
d8155bc8eb 🐛 fix: Telegram适配器使用代理地址无法获取图片 #723 2025-03-07 00:42:15 +08:00
Soulter
cf08e52a92 style: cleanup 2025-03-06 23:52:15 +08:00
Soulter
768398b991 feat: 支持 gewechat 图片等更多类型的主动消息 #710 2025-03-06 22:26:58 +08:00
Soulter
24c20a19f1 feat: 支持插件会话控制 API 2025-03-06 22:13:14 +08:00
Soulter
8fbcbcd4c0 🐛 fix: webchat cannot send active image message #710 2025-03-05 22:34:37 +08:00
Soulter
e0da5bb943 chore: delete some files for project safety 2025-03-05 19:05:50 +08:00
Soulter
36fbc4fb82 Update README.md 2025-03-05 18:55:40 +08:00
Soulter
cb11051f42 Update README.md 2025-03-05 17:56:23 +08:00
Soulter
a824781d14 Update README.md 2025-03-05 17:55:06 +08:00
Soulter
600a2c6748 🐛 fix: context.get_platform() error 2025-03-05 13:28:55 +08:00
Soulter
77df64bfb5 🐛 fix: 修复插件在带了 __del__ 之后无法被禁用和重载的问题 2025-03-05 11:33:01 +08:00
Soulter
2d6e54903c Update README.md 2025-03-05 00:58:44 +08:00
Soulter
baa2b83df9 🐛 fix: telegram cannot handle /start #620 2025-03-05 00:40:38 +08:00
Soulter
1ff02446af 🐛 fix: 404 error after installing plugins 2025-03-04 23:39:01 +08:00
Soulter
b58c6ba762 feat: add template of lmstudio #691 2025-03-04 23:38:33 +08:00
Soulter
611a902000 v3.4.35(fix) 2025-03-04 13:07:21 +08:00
Soulter
c1b3f9dd29 fix: remove fixed imports of platform adapters 2025-03-04 13:04:48 +08:00
Soulter
7c5a88a6a6 Update PLUGIN_PUBLISH.yml 2025-03-04 11:07:46 +08:00
Soulter
be9abfef58 Update PLUGIN_PUBLISH.yml 2025-03-04 10:57:53 +08:00
Soulter
b549c9377e Create PLUGIN_PUBLISH.yml 2025-03-04 10:56:11 +08:00
Soulter
a5b00dbf74 fix: bugfixes 2025-03-04 06:32:19 +08:00
Soulter
90e2e14cd7 fix: circular import 2025-03-04 00:52:28 +08:00
Soulter
14bb245424 perf: 添加多个平台适配器并更新 get_client 方法的返回类型 2025-03-04 00:19:33 +08:00
Soulter
b63a0f3a45 v3.4.34 2025-03-03 23:28:54 +08:00
Soulter
e1f8842d7f feat: 代码执行器添加清理和列出用户上传文件的命令 2025-03-03 23:28:39 +08:00
Soulter
3dda5fb268 perf: 优化插件市场、更新项目的视觉反馈 2025-03-03 23:16:24 +08:00
Soulter
248e0c5240 fix: parse error in gewechat #682 #680 2025-03-03 22:38:53 +08:00
Soulter
0297a43de6 ‼️fix: 修复 wecom 加载失败的问题 #659 2025-03-03 22:34:18 +08:00
Soulter
2b4f66e0cf fix: gewechat 'TypeName' parse error #680 #682 2025-03-03 22:03:58 +08:00
Soulter
e622af2cc3 ‼️fix(telegram): mentioning anyone triggers bot #669 2025-03-03 20:00:25 +08:00
Soulter
f527b1b5a6 Merge pull request #673 from inori-3333/master
将Flask初始化时允许的最大文件体积设置为128MB
2025-03-03 17:12:37 +08:00
Soulter
c15b13a107 Merge branch 'master' into master 2025-03-03 17:11:49 +08:00
Soulter
bc06acdd25 chore: cleanup 2025-03-03 17:10:47 +08:00
Soulter
5252870733 style: cleanup 2025-03-03 15:17:42 +08:00
Soulter
3cac6a47a5 style: cleanup 2025-03-03 13:51:41 +08:00
Soulter
49bba9bf98 style: format codes 2025-03-03 13:30:55 +08:00
inori-333
f4d12e4e5e 将Flask初始化时允许的最大文件体积设置为128MB 2025-03-03 13:19:17 +08:00
Soulter
d305211a36 chore: update port mappings in compose.yml 2025-03-03 12:48:42 +08:00
Soulter
9ec44d6f97 perf: 添加插件安装反馈提示,优化平台和提供者卡片高度 2025-03-03 11:22:50 +08:00
Soulter
175bb3ee01 feat: 分离本地插件和插件市场,缓存插件市场数据,插件市场搜索同时支持对描述进行搜索 2025-03-03 11:13:08 +08:00
Soulter
036c78750f Update compose.yml 2025-03-03 00:09:32 +08:00
Soulter
a18de9de7d feat(plugin): 添加 AstrBot 启动完成时的事件钩子;添加获取制定平台适配器的接口 2025-03-02 20:56:18 +08:00
Soulter
59fbbd5987 fix: 优化 request_llm 2025-03-02 19:52:29 +08:00
Soulter
7e89fbc907 feat: 完善插件在禁用/重载时的逻辑,添加 terminate() Star 父类方法 2025-03-02 16:02:47 +08:00
Soulter
0956f240b3 Merge pull request #667 from Kx-Y/master
为switch_conv的index参数添加类型判断
2025-03-02 13:37:02 +08:00
Soulter
f9db97c6b0 Update main.py 2025-03-02 13:36:17 +08:00
高性能戦闘ロボ
a2443c4ac1 Update main.py 2025-03-02 13:30:34 +08:00
高性能戦闘ロボ
095bd95044 为switch_conv的index参数添加类型判断 2025-03-02 13:11:04 +08:00
Soulter
b569209647 perf: 切换provider时如果没有打开provider开关,自动打开 2025-03-02 12:41:26 +08:00
Soulter
9057cac2b9 refactor: 代码执行器使用指令来制定上传文件以更好适配全平台;telegram 支持发送文件和语音 2025-03-02 12:37:14 +08:00
Soulter
f9a6c685df ‼️fix: 修复插件 AsyncGenerator 在没有执行 yield 语句的情况下设置事件结果无法被处理的问题 2025-03-02 01:16:14 +08:00
Soulter
208eb4f454 feat: add hint for Edge TTS service requiring ffmpeg installation 2025-03-01 21:56:23 +08:00
Soulter
b3cb9e6714 Merge pull request #658 from Soulter/feat-tts-gsvi
feat: 添加 GSVI tts 支持 #545 #351
2025-03-01 15:38:55 +08:00
崔永亮
5f9233f9b7 fix: fomat 多余;api_base 格式问题 2025-03-01 15:33:31 +08:00
Soulter
16447ae597 Merge pull request #657 from CAICAIIs/master
docs(en): Update README with translated provider support details
2025-03-01 15:21:06 +08:00
崔永亮
103edd5260 feat: 添加 GSVI tts 支持 #545 #351 2025-03-01 14:21:07 +08:00
yxw
928089bf0f docs(en): Update README with translated provider support details 2025-03-01 14:05:33 +08:00
Soulter
e5bd74695a Update README.md 2025-03-01 11:28:58 +08:00
Soulter
f796969465 Update README.md 2025-03-01 11:09:55 +08:00
Soulter
10756175b7 perf: 群聊记忆增强只处理 image 和 plain 2025-03-01 10:43:27 +08:00
Soulter
5637a71486 Update bug-report.yml 2025-03-01 10:11:36 +08:00
Soulter
bcebd0fb62 v3.4.33 2025-02-28 22:13:08 +08:00
Soulter
3817d3ca87 fix: 不记忆历史的会话 #630 2025-02-28 22:00:49 +08:00
Soulter
4dd714e814 Merge pull request #648 from Soulter/feat-edge-tts
feat: 添加对于 edge-tts 支持 #471
2025-02-28 21:46:50 +08:00
Soulter
61e8bb49ec chore: Cleanup 2025-02-28 21:33:03 +08:00
Soulter
103dcd3761 Merge pull request #645 from Quirrel-zh/master
修复&优化
2025-02-28 21:24:54 +08:00
Soulter
54ac135fc8 Merge pull request #642 from CAICAIIs/fix_bug
fix bug #621
2025-02-28 21:12:37 +08:00
Soulter
86582809fc Merge pull request #641 from Soulter/perf-plugin-search
perf: 插件市场非列表视图能够正常搜索 #640
2025-02-28 21:11:43 +08:00
Soulter
974d648f19 Merge pull request #638 from Soulter/perf-record
perf: 优化网页录音 #283
2025-02-28 21:10:09 +08:00
崔永亮
a79afc9597 feat: 添加对于 edge-tts 支持 #471 2025-02-28 16:57:44 +08:00
quirrel-zh
e4883241d9 🐛fixed:
1、由于tooltip移入时会消失无法点击其中链接,更改为按钮出发
	2、修复了由于已安装插件与插件市场中name不一致或repo链接大小写不一致导致的检测不到是否安装或有更新的bug
2025-02-28 15:58:57 +08:00
yxw
babf223745 fix bug #621 2025-02-28 14:22:59 +08:00
崔永亮
c7d91730b6 perf: 插件市场非列表视图能够正常搜索 #640 2025-02-28 14:18:10 +08:00
Soulter
71246b65c9 Update README.md 2025-02-28 14:06:31 +08:00
Soulter
50076b647e Merge pull request #639 from CAICAIIs/master
docs: add English README
2025-02-28 14:06:00 +08:00
yxw
a1a788dce8 docs: add English README 2025-02-28 13:39:45 +08:00
崔永亮
a611b4f346 perf: 优化网页录音 #283
1. 为防止输入一大堆 k,改 k 键为 Ctrl 键;
2. 改为长按录音,松手结束;
3. 为防止误触改为只有点击输入框之后才会生效
2025-02-28 13:22:55 +08:00
Soulter
7f6ed674b4 ‼️🐛 fix: 修复钩子函数无法终止事件传播的问题;修复某些情况下终止事件传播后仍然会请求 LLM 的问题 2025-02-28 00:02:17 +08:00
Soulter
aa3cfd887a fix: correct STT model path and improve logging in provider manager and pip installer 2025-02-27 11:33:53 +08:00
Soulter
2649d46d8d chore: remove ts 2025-02-27 01:01:28 +08:00
Soulter
e23ffe6f02 chore: remove ts 2025-02-27 00:57:55 +08:00
Soulter
96f3c3729a v3.4.32 2025-02-27 00:44:23 +08:00
Soulter
11e9d47ce2 fix: dify active message error #616 2025-02-27 00:26:04 +08:00
Soulter
efbc8e4383 Merge pull request #614 from Raven95676/master
🐛 fix: 修复telegram适配器中未处理base64的问题
2025-02-27 00:03:38 +08:00
Soulter
bc7404409f Merge pull request #612 from diudiu62/feat-sensevoice
新增sensevoice语言识别能力
2025-02-26 23:56:03 +08:00
Soulter
8677d70baf feat: add sensevoice adapter 2025-02-26 23:55:00 +08:00
Soulter
f39253f0e1 Merge branch 'master' into feat-sensevoice 2025-02-26 23:27:04 +08:00
Soulter
68c1957267 chore: update gitignore 2025-02-26 23:21:28 +08:00
Raven95676
a275aa2e4d 🐛 fix: 修复telegram适配器中未处理base64的问题 2025-02-26 16:35:44 +08:00
Soulter
cadbac9948 🐛 fix: update 404 error message to reference FAQ for better user guidance 2025-02-26 11:56:40 +08:00
diudiu62
82673e8ddd 依赖放到了参数配置地方提醒,docker提前自行打包依赖 2025-02-26 09:46:30 +08:00
Soulter
bee51024b3 perf: 修复 wecom 配置项的空格问题,确保正确传递 #599 2025-02-26 00:57:54 +08:00
Soulter
3437cb73ec Merge pull request #605 from Soulter/feat-update-btn
feat: 添加面板下载按钮置灰
2025-02-25 22:26:12 +08:00
diudiu62
d01d1a8520 增加依赖 2025-02-25 18:03:29 +08:00
diudiu62
5aa842cf66 增加sensevoice配置 2025-02-25 14:15:22 +08:00
Soulter
03282dee0f 🐛 fix: handle message end and error events in Dify provider, improve logging and error reporting 2025-02-25 14:09:12 +08:00
Soulter
98e8ecb8e2 🐛 fix: add type check for completion response from API to ensure correct handling 2025-02-25 11:46:44 +08:00
Soulter
9451dc3fd4 🐛 fix: 修复某些情况下热重载 provider 时可能没有正确应用的问题 2025-02-25 11:46:44 +08:00
崔永亮
e1d3759f55 feat: 添加面板下载按钮置灰 2025-02-25 10:13:34 +08:00
diudiu62
0ec382c86b 尝试集成sensevoice 2025-02-25 09:05:24 +08:00
Soulter
756087c9f1 feat: 扩展 PlatformAdapterType,支持 Telegram、WeCom 和 Lark 适配器 #601 2025-02-25 01:39:34 +08:00
Soulter
3e7c47e873 feat: 在 Telegram 适配器中支持@功能,增强消息处理能力 2025-02-25 01:32:44 +08:00
Soulter
e3ffdbc308 feat: openai_source 支持传入任何自定义参数以适配 Ollama 和 FastGPT 等 2025-02-25 00:51:09 +08:00
Soulter
645cace4d6 feat: 添加企业微信适配器配置并优化默认配置格式 2025-02-24 23:00:41 +08:00
Soulter
0959d5986b feat: 将 astrbot_plugin_wecom 集成至 astrbot 2025-02-24 22:43:43 +08:00
Soulter
89605c29a7 🐛 fix: ping docker 后关闭 Docker 连接以避免资源泄漏 2025-02-24 22:26:46 +08:00
Soulter
e527f31213 feat: 集成 astrbot_plugin_telegram 至 astrbot 2025-02-24 22:26:23 +08:00
Soulter
a0dbd99928 feat: 在静态文件路由中添加新的URL路径以增强功能 2025-02-24 22:09:42 +08:00
Soulter
17d39c7a4a 🐛 fix: increase forward threshold from 200 to 1500 in default configuration 2025-02-24 15:38:22 +08:00
Soulter
54edaebbd9 🐛 fix: remove unnecessary verification flag for captcha handling in SimpleGewechatClient 2025-02-24 15:36:37 +08:00
Soulter
d587a6f64c feat: add draggable iframe for tutorial links and enhance platform configuration UI 2025-02-24 13:50:07 +08:00
Soulter
2371c32be5 Update LICENSE 2025-02-24 00:31:57 +08:00
Soulter
c9abb8352c Update LICENSE 2025-02-24 00:29:27 +08:00
Soulter
8995e62e73 🐛fix: 更新v-slot类型定义以增强类型安全性 2025-02-23 20:18:00 +08:00
Soulter
316147a8db v3.4.31 2025-02-23 20:11:39 +08:00
Soulter
1fdcfc7a30 Merge pull request #587 from Raven95676/master
🐛fix: 修复aiocqhttp_platform_adapter文件相关判断逻辑
2025-02-23 19:57:50 +08:00
Soulter
8e2c633cd4 feat: 前端支持以列表展示正式版和开发版的列表 2025-02-23 19:53:55 +08:00
渡鸦95676
786b0e4a54 Update aiocqhttp_platform_adapter.py
else尾随空格
2025-02-23 18:16:39 +08:00
Raven95676
c38c1c3c35 🐛fix: 修复aiocqhttp_platform_adapter文件相关判断逻辑 2025-02-23 18:05:45 +08:00
Soulter
7d856756f4 🐛 fix: 修复 gemini 请求时出现多次不支持函数工具调用最后 429 的问题 2025-02-23 17:24:37 +08:00
Soulter
f0d1d365e0 Merge branch 'refactor-hot-load' 2025-02-23 17:04:36 +08:00
Soulter
8e2d666ff8 feat: 优化关于页面和配置页面样式,添加重启按钮功能 2025-02-23 16:57:48 +08:00
Soulter
38d7be1d5f feat: 优化提示框样式并更新关于页面内容 2025-02-23 16:29:57 +08:00
Soulter
431e2fad72 feat: 支持插件禁止默认的llm调用 #579 2025-02-23 16:10:32 +08:00
Soulter
b3b63be8fc Merge pull request #584 from Soulter/refactor-hot-load
🍺 refactor: 支持更大范围的热重载以及管理面板将平台和提供商配置独立化
2025-02-23 15:56:04 +08:00
Soulter
071fc7d6ef feat: 调整适配器类型显示样式并添加API Base信息 2025-02-23 15:52:30 +08:00
Soulter
2a37f7edac feat: 在聊天页面添加粘贴图片的快捷键提示 2025-02-23 15:41:34 +08:00
Soulter
c656ad5e2c feat: 消息平台和服务提供商页面支持显示日志 2025-02-23 15:27:05 +08:00
Soulter
da14a89490 🍺 refactor: 支持更大范围的热重载以及管理面板将平台和提供商配置独立化 2025-02-23 12:54:25 +08:00
Soulter
cf22eae467 fix: save config 2025-02-22 23:20:25 +08:00
Soulter
b199bddb0b feat: 适配多节点的转发消息(OneBot V11) 2025-02-22 21:07:57 +08:00
崔永亮
2188ea82de feat: 支持 AstrBot 更新使用 Github 加速地址 2025-02-22 18:17:34 +08:00
Soulter
1fa13d0177 Merge pull request #577 from Soulter/perf-autoScroll-switch
perf: 添加控制台关闭自动滚动按钮
2025-02-22 17:16:52 +08:00
崔永亮
ed508af424 perf: 添加控制台关闭自动滚动按钮 2025-02-22 17:10:53 +08:00
Fridemn
5df26864d5 Merge pull request #574 from Soulter/perf-port-check
🎈 perf: 启动时检查端口占用
2025-02-22 17:01:53 +08:00
崔永亮
837111b17e perf: 填加具体占用进程显示 2025-02-22 16:23:50 +08:00
崔永亮
a6b363b433 🎈 perf: 启动时检查端口占用 2025-02-22 16:10:46 +08:00
Soulter
2807e1e892 feat: add template of FastGPT 2025-02-22 15:43:14 +08:00
Soulter
0a2abd8214 Merge pull request #572 from Soulter/feat-dashscope
支持阿里云百炼应用智能体、工作流
2025-02-22 15:04:46 +08:00
Soulter
8beb7acdb1 feat: 支持为 dify 和 dashscope 提供商设置默认固定变量 #552 2025-02-22 14:48:18 +08:00
Soulter
466c80b94d feat: 阿里云百炼应用工作流支持自定义动态变量 #552 2025-02-22 14:32:37 +08:00
Soulter
36c0cfc9a9 feat: 支持阿里云百炼应用智能体、工作流
#552
2025-02-22 14:08:51 +08:00
Soulter
35ba1b3345 fix: gewechat verify code 2025-02-22 11:37:34 +08:00
Soulter
d00821d1c7 Update README.md 2025-02-22 10:07:18 +08:00
Soulter
6c1b3f242b Merge pull request #568 from Raven95676/master
🐛 fix: 修复webchat未处理base64的问题
2025-02-22 01:07:20 +08:00
Raven95676
9f9da1e0c9 🐛 fix: 修复webchat未处理base64的问题 2025-02-21 23:39:53 +08:00
崔永亮
14fb4b70bd feat: 支持 gewechat 设置验证码 #448 2025-02-21 23:08:23 +08:00
崔永亮
b1049540a4 feat: claude 支持纯图片 2025-02-21 22:26:31 +08:00
Fridemn
5e2909df33 Merge pull request #559 from Rt39/feat-claude-api
添加对Anthropic Claude API的支持
2025-02-21 21:12:52 +08:00
崔永亮
c122dad21f feat: 添加自定义api base 2025-02-21 21:07:59 +08:00
Rt39
48ae686602 feat: add claude template 2025-02-20 23:58:10 -05:00
Rt39
bf2c3a1a81 fix: 根据Codacy Production / Codacy Static Code Analysis修改格式问题 2025-02-20 21:15:07 -05:00
Rt39
96e7a93886 feat: 添加对Claude API的支持 2025-02-20 19:59:16 -05:00
Soulter
dba1ed1e19 v3.4.30 2025-02-21 01:31:36 +08:00
Soulter
a24514876b fix: 修复 dify 无法使用事件钩子的问题以及出现 GeneratorExit 的问题 #533 #264 2025-02-21 01:14:13 +08:00
Soulter
466a1c1c41 🐛 fix: 修复某些情况下导致插件报错 AttributeError 的问题 #549 2025-02-21 00:38:08 +08:00
Soulter
a2d5e9f40f feat: add xAI template 2025-02-20 16:34:32 +08:00
Soulter
1bbff1d161 v3.4.29 2025-02-19 20:05:33 +08:00
Soulter
0948bae99b feat: 添加代码执行器 Docker 宿主机绝对路径配置及相关功能
Co-authored-by: Bocity <haolovej@vip.qq.com>
2025-02-19 19:56:31 +08:00
Soulter
850db41596 feat: gemini source 初步支持对 API Key 进行负载均衡请求 #534 2025-02-19 19:06:37 +08:00
Soulter
7bafc87e2b 🐛 fix: 修复部分单指令失效的问题 2025-02-19 19:04:23 +08:00
Soulter
1a0de02a15 fix: 尝试修复gewechat群聊用户名出现unknown 2025-02-19 17:07:11 +08:00
Soulter
6d5d278624 fix: 尝试修复 gewechat 微信群聊情况下可能导致 unknown 的问题 #537 2025-02-19 16:42:30 +08:00
Soulter
3b4cc48fa0 👌 perf: 开启对话隔离的群聊以及私聊下,非op可以可以使用 /del 和 /reset #519 2025-02-19 16:22:42 +08:00
Soulter
c908461088 Merge pull request #543 from Soulter/refactor-command-group
更换为预编译指令的方式处理指令组指令并且让事件钩子也支持 yield 的方式发送消息
2025-02-19 15:54:26 +08:00
Soulter
53d1398d30 fix: 修复子指令组不能被调用的问题 2025-02-19 15:53:01 +08:00
Soulter
782c0367d0 feat: 事件钩子支持 yield 方式发送消息 2025-02-19 15:29:10 +08:00
Soulter
4678222e9b 👌 refactor: 更换为预编译指令的方式处理指令组指令 2025-02-19 14:55:14 +08:00
Soulter
f71dc3e4be 🐛 fix: reminder time zone issue 2025-02-19 00:15:14 +08:00
Soulter
f6233893bd 🐛 fix: 修复 reminder rm失败 #529 2025-02-19 00:10:18 +08:00
Soulter
6427bcf130 👌perf: 查询模型列表时,可以显示当前使用的模型名称 #523 2025-02-17 22:35:45 +08:00
Soulter
8fa41b706c Merge pull request #522 from yuanxinlyx/fix-keyerror-ls-command
fix: resolve KeyError when current conversation is not in paginated list
2025-02-17 21:45:40 +08:00
YuanxinLu
4706c4438d fix: resolve KeyError when current conversation is not in paginated list 2025-02-17 03:15:59 +08:00
Soulter
0c8ebc2b06 chore: clean up 2025-02-16 16:52:13 +08:00
Soulter
b3b5ebc2ca v3.4.28 2025-02-16 16:19:03 +08:00
Soulter
b8aa23ccc5 🐛fix: 修复转发消息的字数阈值功能#510 2025-02-16 15:54:29 +08:00
Soulter
364843db29 Merge pull request #389 from Nothingness-Void/新增过滤掉正则表达式内容
新增过滤掉正则表达式内容
2025-02-16 15:28:51 +08:00
Soulter
aa56c8f7e6 Merge branch 'master' into 新增过滤掉正则表达式内容 2025-02-16 15:27:30 +08:00
Soulter
8e9fd27058 merge branch master 2025-02-16 15:17:44 +08:00
Soulter
b75908cb2a Merge pull request #517 from Cvandia/master
 feat: 添加命令和命令组的别名支持
2025-02-16 14:51:47 +08:00
Soulter
af6df49ce1 perf: 补充别名为可选参数以前向兼容 2025-02-16 14:50:49 +08:00
Cvandia
bd3bdb5769 feat: 添加命令和命令组的别名支持 2025-02-16 14:44:17 +08:00
Soulter
98fe193b21 Merge pull request #477 from AraragiEro/master
[Feature] 希望添加更为灵活的filter.permission_type使用方式,使用户能自定义权限类型
2025-02-16 13:53:07 +08:00
Soulter
26cbc9e8b1 chore: cleanup 2025-02-16 13:32:28 +08:00
Alero
ebb8c43fd0 bug: 尝试修复cleancode错误 2025-02-16 10:56:17 +08:00
Soulter
8c7344f1c4 👌perf(qq): supports to pass OneBot notice, request event 2025-02-16 01:04:08 +08:00
Soulter
5c32a17787 👌perf: 优化了分段回复和回复时at,引用都打开时的一些体验性问题 2025-02-15 19:29:34 +08:00
Soulter
aff520e69a fix: 修复 Dify 下无法主动回复的问题 #494 2025-02-15 18:31:21 +08:00
Alero
45e627c33c fix: a bug when add filter to root command group 2025-02-14 23:52:31 +08:00
Alero
7a1b158f83 fix: cleancode err 2025-02-14 22:46:22 +08:00
Alero
6374c5d49d fix: add & | operation to customfilter 2025-02-14 22:33:32 +08:00
Alero
fd460b19d4 fix: cleancode err 2025-02-14 20:43:54 +08:00
Alero
dff7cc4ca5 feat: when custom filter cant pass, won't raise error anymore.
and when you use a command group and dont have custom filter access, the return group tree wont contain the command that you dont have permisson.
2025-02-14 20:34:31 +08:00
Alero
d013320bec feat: more powerful CustomFilter 2025-02-14 19:15:19 +08:00
Soulter
fc6dcfaf21 🐛 fix: cannot search plugin 2025-02-14 18:45:56 +08:00
Soulter
a001270bd2 feat: webui supports to search plugin via name 2025-02-14 18:43:04 +08:00
Soulter
9e67883fbd 🐛 fix: add no_proxy env vars to support localhost requests, fix 502 error when use ollama #504 2025-02-14 16:51:02 +08:00
Soulter
f1a448708c 🐛 fix: segmented reply caused incomplete non-llm-response #503 2025-02-14 16:19:09 +08:00
Soulter
a4bfa96502 feat: 支持自定义 Dify 工作流文本输入变量名 #441 2025-02-14 15:41:02 +08:00
Soulter
595b83a256 🐛 FIX: cannot send file in private chat when turn on the reply with quote #262 2025-02-14 14:41:41 +08:00
Soulter
8d34f77321 v3.4.27 2025-02-14 01:53:26 +08:00
Soulter
67095f97b1 🐛 fix: delete conversation
 feat: supports active reply whitelist
2025-02-14 01:43:52 +08:00
Soulter
50740c94ab 🐛 fix: cannot input text before mention in gewechat #492 2025-02-14 01:09:48 +08:00
Soulter
4db4cfeda2 👌 perf: format datetime labels in MessageStat component #460 2025-02-14 00:30:34 +08:00
Soulter
ad13cef89c 👌perf: sort models by id when listing models #384 2025-02-14 00:08:12 +08:00
Soulter
855fc6fcd1 Display the Japanese translation entry 2025-02-13 23:36:50 +08:00
Soulter
8f12244e51 Merge pull request #491 from eltociear/add-japanese-readme
docs: add Japanese README
2025-02-13 22:56:21 +08:00
Ikko Eltociear Ashimine
fe0213465c docs: add Japanese README
I created Japanese translated README.
2025-02-13 14:45:52 +09:00
Soulter
f984047004 fix: unable to send c2c message using webhook qqofficial platform #484 2025-02-13 00:01:16 +08:00
Soulter
19e9e2d090 fix: fix dify cannot set/unset variables #482 2025-02-12 23:58:04 +08:00
Soulter
7fe3b97d00 fix: improve content safety check handling for at or wake commands 2025-02-12 23:42:32 +08:00
Soulter
9cd243da47 fix: handle empty content in gemini context 2025-02-12 23:39:41 +08:00
Soulter
e43208c2e9 fix: update session_id assignment logic for group messages 2025-02-12 14:04:55 +08:00
Soulter
dc016fc22f feat: update validate_config to return a tuple contains casted data 2025-02-12 13:50:24 +08:00
Alero
c6f037cae2 fix: a undefine mistake 2025-02-12 03:25:01 +08:00
Alero
f049830e28 Merge branch 'master' of github.com:AraragiEro/AstrBot 2025-02-12 03:06:23 +08:00
Alero
dd1995ae0b feat: add a way to define custom permission filter. 2025-02-12 03:05:51 +08:00
Soulter
23dc233569 chore: remove useless config items 2025-02-12 02:32:57 +08:00
Soulter
0977aa7d0d chore: fix the default port of qo webhook 2025-02-12 02:28:15 +08:00
Soulter
24862b0672 docs: update the comments of register_llm_tool 2025-02-12 02:27:39 +08:00
Soulter
f05a57efc3 chore: v3.4.26 2025-02-12 01:55:36 +08:00
Soulter
65331a9d7c feat: 支持基于对数函数的分段回复延时时间计算 2025-02-12 01:44:08 +08:00
Soulter
f7ae287e40 fix: ensure result is retrieved again to handle potential plugin chain replacements 2025-02-12 00:27:25 +08:00
Soulter
45f380b1f6 feat: add configuable port for dashboard and improve the method of getting local ip address 2025-02-11 23:00:24 +08:00
Soulter
9e6b329df4 Merge pull request #472 from Akuma-real/master
fix: correct dashboard update tooltip typo
2025-02-11 22:04:19 +08:00
Soulter
43cd34d94c feat: supports to check the content safety of LLM output #474 2025-02-11 22:03:44 +08:00
Soulter
9fa00aff9a 支持完善的 Dify Chat 模式对话管理 2025-02-11 21:30:17 +08:00
Soulter
9a56dcb1be fix: cannot reset conversation in dify chat mode #469 2025-02-11 21:29:28 +08:00
鬼鬼Sama
fdfe7bbe59 fix: correct dashboard update tooltip typo 2025-02-11 20:16:09 +08:00
Soulter
3a99a60792 perf: gewechat send all events to pipeline 2025-02-11 20:00:39 +08:00
Soulter
fa2b4e14df fix: gewechat cannot send message directly 2025-02-11 19:49:20 +08:00
Soulter
35322a6900 Merge pull request #465 from Soulter/feat-qo-webhook
支持 Webhook 方式接入 QQ 官方机器人平台
2025-02-11 18:10:14 +08:00
Soulter
2ccf29d61e Update README.md 2025-02-11 17:28:03 +08:00
Soulter
b068013343 perf: better handle in qq official send 2025-02-11 01:25:17 -05:00
Soulter
d839e72998 feat: 支持 Webhook 方式接入 QQ 官方机器人接口 2025-02-11 01:18:25 -05:00
Soulter
d7c9a8ed29 chore: webhook server, client 2025-02-11 11:19:50 +08:00
Soulter
6837d4d692 chore: update version 2025-02-11 02:05:06 +08:00
Soulter
8aba83735b Update README.md 2025-02-11 01:31:31 +08:00
Soulter
aa51187747 perf(core): change log level to debug for platform and provider adapter instantiation 2025-02-11 01:25:52 +08:00
Soulter
5f07a9ae95 perf(core): better handle in loading platforms 2025-02-11 01:23:50 +08:00
Soulter
a2ca767bf4 v3.4.25 2025-02-11 01:12:23 +08:00
Soulter
5806c74e7c chore(core): display the unsupported message segments 2025-02-11 01:10:17 +08:00
Soulter
0481e1d45e fix(core): github mirror not applied successfully 2025-02-11 01:10:17 +08:00
Soulter
3177b61421 feat(platform): support lark platform 2025-02-11 01:07:14 +08:00
Soulter
6009cf5dfa feat: 添加 moonshot 配置模板 #446 2025-02-10 18:54:59 +08:00
Soulter
0a970e8c31 feat: 支持gewechat文件输出 2025-02-10 18:46:54 +08:00
Soulter
aa276ca6af fix: 修复gewechat无法at人和发语音失败的问题 #447 #438 2025-02-10 18:11:22 +08:00
Soulter
9f02dd13ff fix: 修复qq在@和回复开启的情况下转发消息异常的问题 2025-02-10 13:07:09 +08:00
Soulter
609e723322 v3.4.24 2025-02-10 00:34:02 +08:00
Soulter
c564a1d53e fix: raw_completion 没有正确传递 #439 2025-02-10 00:26:53 +08:00
Soulter
a7fe31f28b fix: 修复指令不经过唤醒前缀也能生效的问题。在引用消息的时候无法使用前缀唤醒机器人 #444 2025-02-09 22:35:52 +08:00
Soulter
a84dc599d6 fix: 修复 /tts 指令 2025-02-09 22:14:10 +08:00
Soulter
8da029add9 feat: 支持 TTS, STT 提供商的显示和快捷切换 2025-02-09 22:08:51 +08:00
Soulter
ba45a2d270 feat: 支持设置GitHub反向代理地址 2025-02-09 18:51:53 +08:00
Soulter
cb56b22aea Update README.md 2025-02-09 16:49:00 +08:00
Soulter
23cc5b31ba perf: 从压缩包上传插件时,去除branch尾缀 2025-02-09 14:59:27 +08:00
Soulter
e8d99f0460 fix: 修复戳一戳消息报错 2025-02-09 13:57:33 +08:00
Soulter
6bcd10cd5c fix: gemini 报错时显示 apikey 2025-02-09 13:56:55 +08:00
Soulter
619fb20c5f fix: drun 不支持函数调用的报错 2025-02-09 01:20:11 +08:00
Soulter
386a312e96 fix: 修复一些typo 2025-02-08 22:52:24 +08:00
Soulter
2759d347e6 update: add socksio, echatpy, cryptography to dockerfile 2025-02-08 22:10:17 +08:00
Soulter
b6ec327b49 perf:完善主动会话 2025-02-08 22:04:36 +08:00
Soulter
ee02d622ba v3.4.23 2025-02-08 21:42:37 +08:00
Soulter
5c4a6083f5 Merge pull request #433 from Cvandia/master
支持 fishaudio tts 文字转语音
2025-02-08 21:20:03 +08:00
Soulter
49e63a3d3d perf: 优化报错显示 2025-02-08 21:19:25 +08:00
Soulter
6bae9dc9ed 👌 perf: 当响应头不为audio/wav时抛出报错 2025-02-08 21:16:09 +08:00
Cvandia
5fa1979a46 🐛 fix: 移除调试过程的不必要的文件写入操作 2025-02-08 20:49:37 +08:00
Cvandia
b40d4fa315 Merge remote-tracking branch 'upstream/master' 2025-02-08 20:45:49 +08:00
Soulter
4d2ff7cd5b fix: 修复 qq 回复别人的时候也会触发机器人, Onebot at 使用 string #330 2025-02-08 20:35:10 +08:00
Cvandia
d8ec0e64d0 Merge remote-tracking branch 'upstream/master' 2025-02-08 19:40:56 +08:00
Cvandia
82e979cc07 feat: 添加 FishAudio TTS API 支持,更新配置和依赖项 2025-02-08 19:37:43 +08:00
Soulter
8c132a51f5 fix: 修复子指令设置permission之后会导致其一定会被执行 #427 2025-02-08 18:51:30 +08:00
Soulter
40bd372cc1 fix: 重启gewe的时候机器人会疯狂发消息 #421 2025-02-08 18:02:42 +08:00
Soulter
212e114270 perf: 优化了一些提示 2025-02-08 15:55:46 +08:00
Soulter
b0e9de6951 perf: 增加DIFY超时时间 #422 2025-02-08 12:58:54 +08:00
Soulter
3489522bbb feat: 支持展示插件是否有更新 2025-02-08 12:22:36 +08:00
Soulter
96237abc03 fix: 当群聊自动回复时,不会带上人格的Prompt #419 2025-02-08 10:17:43 +08:00
Xu Void
7155b4f0ac Update default.py 2025-02-08 10:16:31 +08:00
Soulter
a8b2b09e0f v3.4.22 2025-02-08 00:01:47 +08:00
Soulter
6858b8c555 perf: 当图片数据为空时不加入上下文 #379 2025-02-07 23:57:25 +08:00
Soulter
0e493b1a0e Merge pull request #411 from zhaolj/fix-bug-#298
fix bug #298
2025-02-07 23:39:03 +08:00
Soulter
37d478f970 fix: 移除了分段回复llm提示词辅助 2025-02-07 23:21:05 +08:00
zhaolj
7d0d42a49f fix bug #298 2025-02-07 22:57:49 +08:00
Soulter
0eb1684ef1 fix: 修复 openai_source 尝试弹出最早的记录失败的问题 2025-02-07 22:38:04 +08:00
Soulter
9b0b723143 fix: 联网搜索失败,函数调用无返回值 #342 2025-02-07 22:07:56 +08:00
Soulter
532bc6e1e6 fix: Google Search 报 429 错误时,放宽 Exception 至其他搜索引擎 #405 2025-02-07 21:32:06 +08:00
Soulter
fe3ed4c454 fix: 自部署文转图不生效 #352 2025-02-07 20:24:11 +08:00
Soulter
b5ec89e586 fix: 插件错误信息点击关闭没反应 #394 2025-02-07 20:05:45 +08:00
Soulter
895e7397c2 remove: 移除了 put_history_to_prompt。当主动回复时,将群聊记录将自动放入prompt,当未主动回复但是开启群聊增强时,群聊记录将放入system prompt 2025-02-07 20:00:30 +08:00
Soulter
59b767957a fix: 400 Bad Request: The browser (or proxy) sent a request that this server could not understand. #396 2025-02-07 18:26:31 +08:00
Soulter
17d4bf8f22 perf: 管理面板优化新增列表项的提示 2025-02-06 20:19:53 +08:00
Soulter
836be3b097 update: changelogs 2025-02-06 18:51:47 +08:00
Soulter
310415bea9 feat: 聊天增强图像转述支持自定义 Provider id 2025-02-06 18:49:16 +08:00
Soulter
aafc1276a9 v3.4.21 2025-02-06 18:34:43 +08:00
Soulter
2993e794cc perf: hint 2025-02-06 17:45:15 +08:00
Soulter
58cb9cfb2d chore: clean code 2025-02-06 17:43:04 +08:00
Soulter
fbdf0901d5 fix: 修复reminder时区问题 2025-02-06 17:41:34 +08:00
Soulter
af8c81b621 feat: 支持重载插件 2025-02-06 17:27:53 +08:00
Soulter
06b5275e48 perf: 增加报错显示 2025-02-06 16:43:40 +08:00
Soulter
ad95572d5f perf: 更好的 list 可视化 2025-02-06 15:59:45 +08:00
Xu Void
0021cfc4bc 新增过滤掉正则表达式内容
Fixes #338

新增过滤掉正则表达式内容

---

For more details, open the [Copilot Workspace session](https://copilot-workspace.githubnext.com/Soulter/AstrBot/issues/338?shareId=XXXX-XXXX-XXXX-XXXX).
2025-02-06 15:28:28 +08:00
Soulter
aebc7850f4 fix: openrouter 报错 no endpoints found that support tool use #371 2025-02-06 15:25:15 +08:00
Soulter
1b7efbc607 支持列表展示插件市场 2025-02-06 15:18:11 +08:00
Soulter
3800e96d14 fix: 修复metadata不生效的问题
feat: 支持查看插件行为
2025-02-06 15:10:24 +08:00
Soulter
461f1bb07c feat: 支持插件handler优先级 2025-02-06 12:35:43 +08:00
Soulter
7d4c07e4f6 feat: 支持设置 timeout 2025-02-06 12:31:39 +08:00
Soulter
31b788f463 fix: 修复不支持图片的模型请求异常 2025-02-06 01:50:53 +08:00
Soulter
96ab761f73 fix: 修复reminder无法删除的问题 2025-02-05 22:45:02 +08:00
Soulter
2b3f05c039 update: 优化部分注释 2025-02-05 19:58:48 +08:00
Soulter
f2e8303b66 fix: KeyError _mood_imitation_dialogs_processed 2025-02-05 18:52:55 +08:00
Soulter
2a614b545b fix: 修复可能的 KeyError 2025-02-05 17:17:05 +08:00
Soulter
5c0ab21f68 fix: 修复 /model 异常 2025-02-05 17:05:47 +08:00
Soulter
689d109438 typo: myid -> sid 2025-02-05 16:59:21 +08:00
Soulter
2a6934b283 perf: 无对话状态的提示 2025-02-05 16:56:13 +08:00
Soulter
760cb94e9a v3.4.20 2025-02-05 16:06:52 +08:00
Soulter
2a6cff0013 feat: 支持重命名对话 2025-02-05 16:06:18 +08:00
Soulter
ce578f0417 feat: 支持使用 LLM 辅助分段回复 #338 2025-02-05 15:40:52 +08:00
Soulter
1745bdb9e2 perf: 优化一些问题 2025-02-05 15:39:59 +08:00
Soulter
3f90b89c3c 添加屏蔽无权限指令回复的功能 #361 2025-02-05 15:06:38 +08:00
Soulter
f343e40d15 Merge pull request #370 from Soulter/feat-conversation
feat: 更好的对话管理
2025-02-05 14:56:47 +08:00
Soulter
5cc4be9e65 perf: 优化部分显示问题 2025-02-05 14:51:40 +08:00
Soulter
da5aada002 fix: 修复指令组情况下可能造成多指令出触发的问题 2025-02-05 13:52:53 +08:00
Soulter
07f2ee9ad9 fix: 修复 /reset 指令 2025-02-05 13:33:36 +08:00
Soulter
12f4e1146f feat: 更好的对话管理 2025-02-05 13:26:53 +08:00
Soulter
92c57e5476 fix: 修复级联指令组时出现载入错误的问题 2025-02-05 11:11:04 +08:00
Soulter
a923baacd8 Update README.md 2025-02-05 01:56:09 +08:00
Soulter
999b094d55 Merge pull request #358 from eltociear/patch-1
chore: update main.py
2025-02-05 01:34:04 +08:00
Soulter
d4213f2352 perf: announcement plugin market 2025-02-05 01:19:54 +08:00
Ikko Eltociear Ashimine
3f65c9a066 chore: update main.py
occured -> occurred
2025-02-05 02:18:41 +09:00
Soulter
1d427e2645 perf: 优化插件页面 2025-02-05 01:10:53 +08:00
Soulter
36414c4b00 perf: 优化aiocqhttp适配器对用户非法输入的处理 2025-02-05 00:02:18 +08:00
Soulter
47e253d76c fix: 修复权限过滤算子导致的问题 #350 2025-02-04 23:31:46 +08:00
Soulter
b73cf84df0 v3.4.19 2025-02-04 16:37:15 +08:00
Soulter
a5b885a774 fix: schema 中 object hint 不显示 #290
feat: 优化插件市场的访问
2025-02-04 16:36:00 +08:00
Soulter
0c785413da chore: clean code 2025-02-04 15:51:26 +08:00
Soulter
482d7ef5f7 v3.4.19 2025-02-04 15:47:24 +08:00
Soulter
9f9073c0ff feat: 支持设置所有指令的权限
feat: 插件指令支持设置指令描述
feat: plugin 指令支持查看插件的指令
2025-02-04 15:41:45 +08:00
Soulter
ef05ff4abd fix: 管理员指令 /reset /persona 2025-02-04 13:50:23 +08:00
Soulter
5848aae435 Update README.md 2025-02-04 13:44:02 +08:00
Soulter
fb06f33de0 Update README.md 2025-02-04 12:51:17 +08:00
Soulter
0d7ddb149e fix: 修复请求 gemini 推理模型出现 candidates 错误的问题 #333 2025-02-04 00:30:23 +08:00
Soulter
4f2d7b9c4e feat: 适配 Azure OpenAI #332 2025-02-03 23:59:04 +08:00
Soulter
c02ed96f6f perf: gewechat 服务端回调接口默认暴露在所有地址 2025-02-03 18:51:19 +08:00
Soulter
3b2ac891b2 fix: 修复限流器不可用的问题 #263 2025-02-03 18:51:19 +08:00
Soulter
ef0108881b Update Dockerfile 2025-02-03 17:48:17 +08:00
Soulter
af48975a6b chore: v3.4.18 2025-02-03 16:14:27 +08:00
Soulter
6441b149ab fix: 修复主动概率回复关闭后仍然回复的问题 #317 2025-02-03 14:33:53 +08:00
Soulter
f8892881f8 fix: 尝试修复 gewechat 群聊收不到 at 的回复 #294 2025-02-03 14:28:14 +08:00
Soulter
228aec5401 perf: 移除了默认人格 2025-02-03 14:17:45 +08:00
Soulter
68ad48ff55 fix: 修复HTTP代理删除后不生效 #319 2025-02-03 14:11:50 +08:00
Soulter
541ba64032 fix: 调用Gemini API输出多余空行问题 #318 2025-02-03 13:27:56 +08:00
Soulter
2d870b798c feat: 添加硅基流动模版 2025-02-03 13:24:22 +08:00
Soulter
0f1fe1ab63 fix: 硅基流动 not a vlm 和 tool calling not supported 报错 #305 # 291
perf: 安装和更新插件后全量重启避免奇奇怪怪的bug
feat: 支持 /tool off_all 停用所有函数工具
2025-02-03 13:20:49 +08:00
Soulter
73cc86ddb1 perf: 回复时艾特发送者之后添加空格或换行 #312 2025-02-03 12:04:26 +08:00
Soulter
23128f4be2 perf: 主动回复不支持 qq_official 的 hint 2025-02-03 12:00:05 +08:00
Soulter
92200d0e82 fix: docker容器内时区不对 2025-02-03 01:15:09 +08:00
Soulter
d6e8655792 fix: 抱错时首先移除 tool 2025-02-02 23:17:59 +08:00
Soulter
37076d7920 perf: siliconcloud 不支持 tool 的模型 2025-02-02 23:05:36 +08:00
Soulter
78347ec91b perf: 当人格长度为1时设置默认人格
feat: 支持取消人格
2025-02-02 22:36:50 +08:00
Soulter
9ded102a0a chore: v3.4.17 2025-02-02 20:39:26 +08:00
Soulter
59b7d8b8cb chore: clean code 2025-02-02 20:15:57 +08:00
Soulter
f5b97f6762 perf: 优化 404 提示 2025-02-02 19:55:32 +08:00
Soulter
d47da241af feat: openai tts 更换模型 #300 2025-02-02 19:47:39 +08:00
Soulter
4611ce15eb feat: [beta] 支持群聊内基于概率的主动回复 2025-02-02 19:23:46 +08:00
Soulter
aa8c56a688 fix: 相同type的provider共享了记忆 2025-02-02 19:13:47 +08:00
Soulter
ef44d4471a feat: 增加模型响应后的插件钩子
remove: 移除了默认的r1过滤
2025-02-02 16:42:21 +08:00
Soulter
5581eae957 fix: deepseek-r1模型存在遗留“</think>”的问题 #279
Open
2025-02-02 14:59:17 +08:00
Soulter
ec46dfaac9 perf: 人格情景在发现格式不对时仍然加载而不是跳过 #282 2025-02-02 14:59:17 +08:00
Soulter
6042a047bd 修复Gemini函数调用时,parameters为空对象导致的错误
fix: 修复Gemini函数调用时,parameters为空对象导致的错误
2025-02-02 14:50:34 +08:00
Soulter
6ca9e2a753 perf: websearch 可选配置引用链接 #287 2025-02-02 14:42:13 +08:00
Camreishi
618eabfe5c fix: 修复Gemini函数调用时,parameters为空对象导致的错误
Closes #288
2025-02-02 13:25:08 +08:00
Soulter
bb5db2e9d0 fix: 修复弹出记录报错的问题 #272 2025-02-02 13:24:05 +08:00
Soulter
97e4d169b3 perf: 未启用模型提供商时的异常处理 2025-02-02 11:23:33 +08:00
Soulter
50e44b1473 perf: 移除默认人格 2025-02-02 11:12:17 +08:00
Soulter
38588dd3fa update compose 2025-02-02 00:11:55 +08:00
Soulter
d183388347 perf: 去除gewechat默认配置 2025-02-01 23:20:25 +08:00
Soulter
1e69d59384 fix: 配置提示 typo 2025-02-01 22:56:34 +08:00
Soulter
00f008f94d Update compose.yml 2025-02-01 21:31:24 +08:00
Soulter
3c28001a74 v3.4.16 2025-02-01 19:31:59 +08:00
Soulter
76a6218be6 fix: 修复webui无法从本地上传插件的问题 2025-02-01 19:31:29 +08:00
Soulter
6c1de1bbd6 Update README.md 2025-02-01 16:19:01 +08:00
Soulter
d7678081da perf: Provider 重复时不直接报错闪退 #265 2025-02-01 14:36:41 +08:00
Soulter
5e4ba563cb perf: 弱化更新报错 #267 2025-02-01 14:29:39 +08:00
Soulter
8afbe77b0a Update README.md 2025-02-01 12:11:58 +08:00
Soulter
2ef139b59a fix: 修复每次启动astrbot都需要微信扫码的问题 2025-01-31 01:28:49 +08:00
Soulter
1f0d2d9b89 fix: QQ官方机器人开启 reply with metion 和 reply with quote 后,无法正常回复消息 #244 2025-01-30 01:36:25 +08:00
Soulter
37a1f144ab chore: update changelog of 3.4.15 2025-01-30 00:32:50 +08:00
Soulter
9a7a654596 perf: 插件处于禁用状态时其所属的函数调用工具不可被启用 #254 2025-01-30 00:27:10 +08:00
Soulter
9abccd63cf chore: remove stt.py 2025-01-29 23:47:50 +08:00
Soulter
93fea77182 chore: bump to v3.4.15 2025-01-29 23:43:09 +08:00
Soulter
19797243f6 perf: 增加插件链接 2025-01-29 19:56:09 +08:00
Soulter
c9c733d925 Merge branch 'dev' 2025-01-29 19:43:52 +08:00
Soulter
a7d7678c78 fix: 修复白名单为空时依然终止事件 #259 2025-01-29 17:17:27 +08:00
Soulter
c0911921c7 feat: 配置Schema以及插件支持配置 2025-01-29 16:54:57 +08:00
Soulter
4a4241d57a Update README.md 2025-01-29 13:26:51 +08:00
Soulter
c9426bb6eb config 2025-01-29 12:25:54 +08:00
Soulter
db4abd169a fix: 优化分段回复 2025-01-28 14:42:15 +08:00
Soulter
80b6958599 fix: 修复 config validator 不起效的问题 2025-01-28 14:18:21 +08:00
Soulter
80058c781a fix: 修复r1思考标签问题和分段回复间隔时间问题 2025-01-28 14:03:10 +08:00
Soulter
44bd2e36f3 Update README.md 2025-01-28 02:15:11 +08:00
Soulter
3589a5e5be perf: 强化ltm异常处理 2025-01-27 21:47:35 +08:00
Soulter
13ef033f0e fix: 群聊增强的参数类型转换 2025-01-27 21:40:20 +08:00
Soulter
3f8c68bbca fix: f-string expression part cannot include a backslash
long_term_memory.py, line 69
2025-01-27 21:01:50 +08:00
Soulter
4275cea82b chore: v3.4.14 2025-01-27 20:09:03 +08:00
Soulter
a0bcb5339a perf: 自动删除 deepseek-r1 模型自带的 think 标签 2025-01-27 20:04:39 +08:00
Soulter
43deec4a4b Merge pull request #255 from Soulter/feat-ltm
支持记录非唤醒状态下群聊历史记录
2025-01-27 20:02:43 +08:00
Soulter
2bc433a30b feat: 支持记录非唤醒状态下群聊历史记录 2025-01-27 20:00:32 +08:00
Soulter
eb2b395932 perf: /t2i 即时生效 2025-01-27 19:33:38 +08:00
Soulter
2bfd1c0bf2 perf: 自动移除 ollama 不支持 tool 的模型的 tool 请求 2025-01-27 19:25:28 +08:00
Soulter
7228c4b13f fix: 修复 TTS 部分变量名错误导致请求失败 2025-01-27 18:45:34 +08:00
Soulter
9351d7471f perf: 优化 gewechat 消息下发异常处理 2025-01-27 18:11:31 +08:00
Soulter
1cf49998bc Update README.md 2025-01-27 11:34:27 +08:00
Soulter
6ae86597e8 chore: v3.4.13 2025-01-26 16:51:13 +08:00
Soulter
c578ff25bd fix: stt_enabled 未初始化 #252 2025-01-26 16:51:02 +08:00
Soulter
2934a3e3be chore: logo 2025-01-26 15:18:23 +08:00
Soulter
ceaa69da75 feat: 支持消息分段回复 2025-01-26 13:45:32 +08:00
Soulter
fa8e731576 Update README.md 2025-01-25 22:45:47 +08:00
Soulter
685c0a106a perf: use pysilk instead of pilk 避免构建问题 2025-01-25 20:18:40 +08:00
Soulter
7f539090dd perf: 更新项目时连带更新依赖 2025-01-25 20:04:28 +08:00
Soulter
2089273f95 Merge pull request #251 from Soulter/feat-tts
适配 OpenAI TTS API,并支持 Napcat,Gewechat,Lagrange 的语音输出
2025-01-25 19:51:22 +08:00
Soulter
838bb4c7ad chore: remove duration 2025-01-25 19:49:53 +08:00
Soulter
637acd1a12 feat: 适配 OpenAI TTS API,并支持 Napcat,Gewechat,Lagrange 的语音输出 2025-01-25 19:46:00 +08:00
Soulter
03fa9a847f feat: gewechat 支持语音、图片 2025-01-25 16:34:40 +08:00
Soulter
d488c88e78 feat: 支持路径映射,解决docker部署两端文件系统不一致导致的富媒体文件路径不存在问题 2025-01-24 14:08:08 +08:00
Soulter
baae842210 fix: napcat 下语音消息接收异常 2025-01-24 13:41:13 +08:00
Soulter
ec1fb838b6 perf: notice 2025-01-22 21:38:05 +08:00
Soulter
13281179df perf: notice 2025-01-22 21:36:28 +08:00
Soulter
276a42c9a1 Bump to 3.4.11 2025-01-22 21:16:24 +08:00
Soulter
7a70a730ba perf: 任务报错后的优雅报错输出 2025-01-22 21:14:26 +08:00
Soulter
d0fe59631c perf: 优化更新项目时重启可能会导致Address already in use的问题 2025-01-22 20:57:15 +08:00
Soulter
106892e933 fix: 修复appid保存的问题和部分群聊at失效的问题和群聊@的sender username显示异常的问题 2025-01-22 20:34:52 +08:00
Soulter
19543a41b3 Update README.md 2025-01-22 19:56:07 +08:00
Soulter
b172b760ab feat: 为平台和提供商适配器添加默认 ID 配置 #248 2025-01-22 16:52:34 +08:00
Soulter
4b5d49cb41 Bump to 3.4.10 2025-01-22 00:19:20 +08:00
Soulter
3fd35b6058 feat: 管理面板更新面板按钮 #245 2025-01-22 00:17:43 +08:00
Soulter
5f86c4ab99 perf: 增强 LLM 请求错误处理 #243 2025-01-21 16:29:19 +08:00
Soulter
c94a7f6629 perf: 针对 api_base 的明显提示,修改 ollama 模板的api_base #247 2025-01-21 16:15:04 +08:00
Soulter
7d6beb4141 fix: QQ 图片发送不了 #246 2025-01-21 16:12:10 +08:00
Soulter
e2117e690a feat: 支持登出gewechat 2025-01-21 13:12:09 +08:00
Soulter
fb791290e2 fix: 添加gewechat适配器过滤器 2025-01-21 12:39:57 +08:00
Soulter
5dd1488b5d perf: 优化webui和主程序更新的协调
fix: 修复某些请求不能正确应用代理的问题
2025-01-21 01:08:15 +08:00
Soulter
529cd64d82 perf: help显示AstrBot和webui版本 2025-01-21 00:10:59 +08:00
Soulter
d2bd3e8da8 bump to v3.4.9 2025-01-20 23:35:34 +08:00
Soulter
e42ce7dd86 perf: 优化了用户体验 2025-01-20 23:27:13 +08:00
Soulter
40709462ee chore: bump domain to astrbot.app 2025-01-20 19:02:54 +08:00
Soulter
2ad6c01a4d Update README.md 2025-01-20 15:48:39 +08:00
Soulter
70c12e788e feat: LLM额外唤醒词与机器人唤醒词冲突时的处理 2025-01-20 10:22:25 +08:00
Soulter
1713791c90 docs: update webui demo 2025-01-20 00:46:29 +08:00
Soulter
9aa23fd412 Update README.md 2025-01-19 21:32:42 +08:00
Soulter
e4ba09cd93 chore: remove package-lock.json 2025-01-19 18:20:40 +08:00
Soulter
171fdf1fbc fix: 消息链无元素时仍然插入了@和回复 2025-01-18 23:25:42 +08:00
Soulter
01f4e0b961 feat: gewechat 主动消息 2025-01-18 22:31:17 +08:00
Soulter
be2d5a91c7 chore: bump to v3.4.8 2025-01-18 22:19:35 +08:00
Soulter
a1d89d9478 Merge pull request #242 from Soulter/feat-gewechat
初步接入 gewechat 文字交互
2025-01-18 22:16:53 +08:00
Soulter
98d1dc3b65 feat: 初步接入 gewechat 文字交互 2025-01-18 22:01:36 +08:00
Soulter
b80eb3acc0 feat: 支持回复时 At 和引用发送者 #241 2025-01-18 17:31:11 +08:00
Soulter
05ccc1995b fix: 清除残留的 personalities 2025-01-18 17:31:11 +08:00
Soulter
0de244889e chore: gitsponsors 2025-01-18 10:54:37 +08:00
Soulter
e6c5c3a493 chore: bump to v3.4.7 2025-01-16 11:26:05 +08:00
Soulter
164aa2ccd2 Merge pull request #240 from Soulter/feat-better-persona
feat: 更好的人格情景管理
2025-01-16 11:20:28 +08:00
Soulter
f1599e26b3 perf: webchat 主动信息 2025-01-16 11:19:02 +08:00
Soulter
ed64a4d32d chore: 整理hint 2025-01-16 11:11:30 +08:00
Soulter
2ee4b431d4 fix: 无tool导致的报错 #239 2025-01-15 11:16:31 +08:00
Soulter
cd8a73ed19 feat: 更好的人格情景管理和管理面板支持删除列表默认模版项 2025-01-14 21:08:57 +08:00
Soulter
e6c985ce4e feat: 优化WebChat长连接的逻辑 2025-01-13 12:42:32 +08:00
Soulter
a20446aeb9 🎉 chore: bump to v3.4.6 2025-01-13 02:17:23 +08:00
Soulter
7b23d76559 feat: 支持并完善服务提供商默认配置模板接口 2025-01-13 02:05:57 +08:00
Soulter
8315cf5818 perf: 面板文件更新检查和引导提示和AboutPage 2025-01-12 13:01:40 +08:00
Soulter
ed16265bde fix: 更新官方文档链接并优化管理面板版本检查日志 2025-01-12 12:23:27 +08:00
Soulter
dff205faf6 feat: 添加聊天功能路由和更新管理面板命令 2025-01-12 12:18:19 +08:00
Soulter
9aae8aee0c Update README.md 2025-01-12 11:45:39 +08:00
Soulter
7c818ced2b perf: 文件和语音功能适配 Lagrange 2025-01-12 11:44:33 +08:00
Soulter
218e887558 fix: download_file 修复 SSL 连接错误处理 2025-01-12 11:44:33 +08:00
Soulter
a68860b35a chore: compress the banner 2025-01-12 10:52:17 +08:00
Soulter
82d4d43383 🎉 Bump to v3.4.5 2025-01-11 23:35:22 +08:00
Soulter
94618e8feb feat: 添加 aiodocker 依赖 2025-01-11 22:02:15 +08:00
Soulter
55de7d4494 🎉 Bump to v3.4.5 2025-01-11 21:40:48 +08:00
Soulter
7ed639f741 🎉 bump to v3.4.5 2025-01-11 21:06:06 +08:00
Soulter
41f2870c29 Merge pull request #236 from Soulter/feat-stt
支持 Speech To Text,并适配腾讯修改过的 Silk 语音格式
2025-01-11 21:00:04 +08:00
Soulter
ba198490fa feat: 支持自部署 Whisper 模型 2025-01-11 20:31:21 +08:00
Soulter
0f9ab082ab perf: 优化webchat,没有结果返回时的反馈 2025-01-11 19:45:42 +08:00
Soulter
97b58965f2 feat: webchat可显示Provider状态 2025-01-11 19:31:56 +08:00
Soulter
f2566c68e3 feat: 按 K 语音 2025-01-11 19:07:26 +08:00
Soulter
a456bf5449 fix: 初始化reminder时的一些问题 2025-01-11 18:55:18 +08:00
Soulter
a09998f910 feat: webchat 支持语音输入 2025-01-11 18:54:40 +08:00
Soulter
be662b913c feat: 支持 Whisper STT,并适配 Tencent 语音格式 2025-01-11 17:19:28 +08:00
Soulter
e7ddc8448d perf: 代码执行器在成功执行后清空文件buffer 2025-01-11 11:31:56 +08:00
Soulter
29374f8d8a fix: 修复 /dashbord_update 指令 2025-01-11 00:25:02 +08:00
Soulter
359b971103 Merge pull request #235 from Soulter/feat-webchat
WebChat 支持
2025-01-11 00:17:18 +08:00
Soulter
fbdb1ae208 chore: bump to v3.4.4 2025-01-11 00:14:08 +08:00
Soulter
22c13c1eff perf: webchat支持传图 2025-01-11 00:06:19 +08:00
Soulter
5fc63aeaf1 perf: ui 2025-01-10 22:45:14 +08:00
Soulter
d4f32673ab fix: 修复持久化问题 2025-01-10 22:08:43 +08:00
Soulter
480dffb51b feat: 初步实现 webchat 页面 2025-01-10 21:48:15 +08:00
Soulter
966df00124 feat: 支持从管理面板(控制台页)手动安装 pip 库 2025-01-10 15:35:57 +08:00
Soulter
3e2b4bc727 feat: 支持动态设置会话变量以适用 Dify 输入变量 2025-01-10 12:32:20 +08:00
Soulter
5929a8d42b Update README.md 2025-01-09 23:11:11 +08:00
Soulter
f8ab40eb39 chore: 上传管理面板package.json 2025-01-09 22:25:46 +08:00
Soulter
55e9233b93 docs: v3.4.3 changelog 2025-01-09 22:19:11 +08:00
Soulter
b7277b51fd feat: 管理面板支持显示不在metadata中的配置 2025-01-09 22:03:53 +08:00
Soulter
1fa9111b2b perf: 进一步防止llm递归调用 2025-01-09 22:03:22 +08:00
Soulter
90a9e496d9 feat: 适配器类插件支持设置默认配置模板 2025-01-09 19:45:18 +08:00
Soulter
2a7dce1eb0 chore: clean code 2025-01-09 16:34:39 +08:00
Soulter
0c0841cc03 fix: websearch 在 cmd_config 中失效的问题 2025-01-09 16:33:58 +08:00
Soulter
4c9fe016bf fix: test_pipeline 2025-01-09 16:00:43 +08:00
Soulter
acc90f140c chore: bump dashboard_release_url 2025-01-09 15:50:24 +08:00
Soulter
68a7bc3930 Merge pull request #232 from Soulter/feat-python-interpreter
初步实现代码执行器
2025-01-09 15:43:40 +08:00
Soulter
12ea64be0e fix: dashboard command bug 2025-01-09 15:42:04 +08:00
Soulter
7f30a673f7 fix: 修复 qq_official 无法发图 2025-01-09 15:20:54 +08:00
Soulter
897e100c32 Merge pull request #234 from Soulter/233-gemini-native-support
支持通过 Google GenAI 访问 Gemini 模型
2025-01-09 14:23:44 +08:00
Soulter
0d4ad5cb31 fix: 修复 APScheduler 任务错过后不执行的问题 2025-01-09 14:23:07 +08:00
Soulter
b124bd0d0e feat: 支持通过 Google GenAI 访问 Gemini 模型 2025-01-09 14:05:48 +08:00
Soulter
6bc2f84602 Update README.md
qingcloud 在新网的账户余额不足导致原域名无法续费
2025-01-09 10:35:02 +08:00
Soulter
d787a28c40 feat: 支持使用 /dashboard update 更新管理面板 2025-01-09 00:59:28 +08:00
Soulter
6b078a5731 cd: build dashboard files automatically 2025-01-09 00:57:48 +08:00
Soulter
17dddbfe21 chore: 禁用插件 2025-01-08 23:34:54 +08:00
Soulter
3ff3c9e144 perf: 检测到docker不可用时自动禁用本插件 2025-01-08 23:32:49 +08:00
Soulter
f5a37d82cc Merge branch 'master' into feat-python-interpreter 2025-01-08 23:13:52 +08:00
Soulter
d3d428dc9d fix: 管理面板支持禁用/启用插件 2025-01-08 23:04:03 +08:00
Soulter
8dc8c5b5dc feat: 支持对插件禁用/启用 2025-01-08 22:28:20 +08:00
Soulter
e6b06f914b perf: provider 偏好项记忆 2025-01-08 20:46:34 +08:00
Soulter
4dc502a8b6 fix: 修复事件监听器会让wakestage失效的问题 2025-01-08 20:24:01 +08:00
Soulter
b1d1a13d5f perf: 支持图片输入 2025-01-08 19:56:03 +08:00
Soulter
75cc4cac5a perf: 代码执行器添加部分控制指令,添加更多可用库 2025-01-08 13:26:16 +08:00
Soulter
1b7e4fbbdc perf: 退出时关闭 aiohttp client session 2025-01-08 12:43:34 +08:00
Soulter
9789e2f6c1 perf: 代码执行器请求llm不持久化历史记录 2025-01-08 02:12:35 +08:00
Soulter
b8fb0bee24 feat: 初步实现代码执行器 #210 2025-01-08 02:10:27 +08:00
Soulter
419f77e245 Update README.md 2025-01-07 20:56:25 +08:00
Soulter
59b1c3473b Merge pull request #230 from Soulter/feat-dify
接入 Dify
2025-01-07 20:14:33 +08:00
Soulter
6db58ca375 perf: 优化在prompt为空的情况下不请求provider 2025-01-07 20:01:47 +08:00
Soulter
4832b342b0 Merge branch 'master' into feat-dify 2025-01-07 19:59:54 +08:00
Soulter
6cec542402 feat: 初步接入 Dify 2025-01-07 19:56:18 +08:00
Soulter
9644791783 feat: kdb 2024-12-30 18:06:09 +08:00
Soulter
5031c307d1 update: readme 2024-12-26 23:39:29 +08:00
Soulter
aa49539e3e chore: fix test 2024-12-26 23:33:40 +08:00
Soulter
7b4118493b chore: fix test 2024-12-26 23:15:10 +08:00
Soulter
d1cc9ba4ce chore: update test workflow 2024-12-26 23:09:11 +08:00
Soulter
e0e92139d7 fix: test workflow 2024-12-26 23:07:50 +08:00
Soulter
62039392bb chore: fix test workflow 2024-12-26 23:06:30 +08:00
Soulter
b72c69892e test: dashboard test 2024-12-26 22:59:17 +08:00
Soulter
e6205e9aad ci: update workflow 2024-12-25 17:18:29 +08:00
Soulter
b8a6fb1720 chore: update tests 2024-12-25 12:50:29 +08:00
Soulter
7c06d82f27 perf: plugin manager 重复 reload 释放资源 2024-12-25 12:50:29 +08:00
Soulter
d92cb0f500 perf: 当没有provider时直接返回 2024-12-25 12:50:29 +08:00
Soulter
7fa72f2fe9 perf: adapt glm-4v-flash 2024-12-24 14:08:20 +08:00
Soulter
21d480a3b5 bugfixes 2024-12-22 05:31:29 +08:00
Soulter
771c045844 feat: 可配置是否启用白名单 2024-12-22 05:18:27 +08:00
Soulter
e6ce484c15 perf: 不加载已经outdated的reminder 2024-12-22 05:06:15 +08:00
Soulter
102a92f62d perf: 移动对 prompt 的内置修改的逻辑 2024-12-21 18:39:10 +08:00
Soulter
6c7ac70701 Bump version to v3.4.2 2024-12-21 16:40:04 +08:00
Soulter
9d8372289f fix: fstring format error #226 2024-12-21 16:38:53 +08:00
Soulter
766f6a1ba2 perf: use request_llm 2024-12-21 16:35:16 +08:00
Soulter
193ff24f4c feat: 添加发送消息后的事件钩子 2024-12-20 16:31:36 +08:00
Soulter
c675017374 feat: 新增LLM请求事件钩子和装饰消息结果钩子 2024-12-19 21:33:03 +08:00
Soulter
86cb852507 perf: llm-tuner adapter 检查路径 2024-12-18 21:25:04 +08:00
Soulter
73494e0d7d perf: 使用 astrbot-registry 下载面板静态资源 2024-12-18 21:24:39 +08:00
Soulter
ec61aa1b6f Merge pull request #224 from Soulter/dashboard
迁移 AstrBot Dashboard 源代码至 AstrBot
2024-12-17 23:45:13 +08:00
Soulter
6df0e78b22 upload: dashboard from Soulter/AstrBot-Dashboard 2024-12-17 23:40:32 +08:00
Soulter
63c604359b fix: update 2024-12-16 22:53:23 +08:00
Soulter
08212588a0 chore: update docker ci/cd workflow 2024-12-16 21:12:02 +08:00
Soulter
c8518ce827 feat: auto release 2024-12-16 21:00:38 +08:00
Soulter
94434e3fc0 chore: changelogs 2024-12-16 21:00:31 +08:00
Soulter
9f3af95198 fix: websearch 2024-12-16 20:26:07 +08:00
Soulter
acb3af8ab8 feat: reminder 2024-12-16 20:02:50 +08:00
Soulter
9c50889371 feat: backend market api 2024-12-15 13:04:18 +08:00
Soulter
8c03c90708 fix: 修复 event loop closed 2024-12-15 13:03:00 +08:00
Soulter
91cc21e729 fix: 修复未找到适用于qq_official的平台适配器 #223 2024-12-15 11:54:08 +08:00
Soulter
dd29199c9b fix: unable to open database file when launching initially #222 2024-12-15 02:30:04 +08:00
Soulter
9156629d72 Merge pull request #220 from Soulter/ver/3.4.0
v3.4.0
2024-12-14 23:28:38 +08:00
Soulter
002aa61dd9 update: README.md 2024-12-14 23:27:18 +08:00
Soulter
401747a7a3 perf: hint 2024-12-14 23:24:18 +08:00
Soulter
990390218c perf: 优化向后兼容性 2024-12-14 22:26:08 +08:00
Soulter
69a4d6ac83 perf: more simple api 2024-12-14 20:50:21 +08:00
Soulter
3a67492680 perf: 插件报错直接终止事件;支持生成器发送信息 2024-12-14 20:11:28 +08:00
Soulter
d58b9edf78 Update README.md 2024-12-12 22:19:16 +08:00
Soulter
5144dd09f1 perf: Star 插件类优化 2024-12-12 19:07:04 +08:00
Soulter
6a5f3720a2 update: docker compose 2024-12-12 13:14:14 +08:00
Soulter
d814d3537c fix: chat 唤醒前缀 2024-12-12 11:58:38 +08:00
Soulter
85380ade6a feat: 支持 llmtuner
perf: 优化流水线
2024-12-11 23:53:10 +08:00
Soulter
86f53deade perf: 优化配置文件 Metadata 2024-12-11 20:07:29 +08:00
Soulter
c3357dc0e2 feat: 插件帮助 2024-12-11 16:09:16 +08:00
Soulter
97e14dd294 feat: 可选启动系统时间提示 2024-12-11 15:48:33 +08:00
Soulter
e45c48b998 perf: 群聊第一个消息段是 At 消息,但不是 At 机器人或 At 全体成员,则不唤醒 2024-12-11 15:23:38 +08:00
Soulter
0b53eae4ad feat: 添加 websearch 2024-12-11 15:02:29 +08:00
Soulter
92aa3123ec refactor: 支持llm tool 2024-12-11 13:21:01 +08:00
Soulter
e9e789da20 fix: 修复 vchat 适配器路径错误和一些其他优化 2024-12-11 00:55:39 +08:00
Soulter
c6bdac8835 format: code lint
(ruff, uv 是个好东西)
2024-12-10 22:09:53 +08:00
Soulter
90df679a77 remove: 删除 python3.10 不兼容的 f-string 语法
Co-authored-by: Soulter <905617992@qq.com>
Co-authored-by: QodiCat <1357016290@qq.com>
2024-12-10 20:56:16 +08:00
Soulter
b25a422fd6 fix: 修复旧版本插件在管理面板找不到的问题 2024-12-10 20:21:13 +08:00
Soulter
47e70bd086 feat: 过滤器支持限定权限组;支持指定白名单是否忽略管理 2024-12-10 20:14:13 +08:00
Soulter
f963194124 perf: 私聊下白名单不拦截管理员 2024-12-10 16:32:54 +08:00
Soulter
bdfc77d349 refactor: im so tired :) 2024-12-09 22:38:42 +08:00
Soulter
7abe90f2ac feat: 使用 jwt 用于管理面板鉴权 2024-12-03 19:35:07 +08:00
Soulter
4a52779d09 perf: 优化在多人聊天时的context管理 2024-12-02 23:13:56 +08:00
Soulter
a01e865042 feat: 本地指标收集到数据库 2024-12-02 22:20:24 +08:00
Soulter
446c50da80 fix: 修复 ATRI 模块导入位置,确保在需要时正确加载 2024-12-02 19:34:15 +08:00
Soulter
750a93a1aa remove: 移除了 nakuru-project 库
但仍然使用其对 OneBot 的数据格式封装。
2024-12-02 19:31:33 +08:00
Soulter
ba12d65792 perf: 不再内嵌管理面板构建文件 2024-11-29 17:09:04 +08:00
Soulter
bd40404f58 feat: LLM 提供商模板 2024-11-29 15:25:49 +08:00
Soulter
4d8d9ecfc2 feat: 接入绿泡泡消息平台 2024-11-28 21:39:35 +08:00
Soulter
f2efa022b4 feat: aipcqhttp 支持设置白名单 2024-11-27 23:45:23 +08:00
Soulter
fc28f34ec6 feat: metrics 采用 Tickstats 2024-11-27 21:41:54 +08:00
Soulter
b740cc467d chore: 删除遗留文件 2024-11-27 15:09:56 +08:00
Soulter
6ab8114eee feat: v3.4.0 2024-11-27 15:04:30 +08:00
Soulter
cd3f90917f Update README.md 2024-11-22 23:16:59 +08:00
Soulter
2219547a8b Update README.md 2024-11-22 19:23:17 +08:00
Soulter
017426501c fix: test 2024-11-22 11:20:55 +08:00
Soulter
ca19754a30 fix(message handler): llm tools 2024-11-19 13:46:48 +08:00
Soulter
4623f2f12a chore: 去除一些不必要的注释 2024-11-17 16:01:13 +08:00
Soulter
c14813c0b2 perf: 1. 仪表盘安装插件时日志回显
2. log 生产消费优化
3. llm metrics 优化
4. 优化 provider 指令,显示为 llm.id 配置项
2024-11-15 17:58:52 +08:00
Soulter
9d8308ace0 fix: 修复仪表盘在检测重启的时候的问题 2024-11-13 17:36:57 +08:00
Soulter
4976e81ea4 feat: 1. 增加可选插件仓库镜像配置;
2. 仪表盘更新;
2024-11-13 17:20:07 +08:00
Soulter
f59de87a31 fix: 本地插件上传报错 2024-11-12 20:03:26 +08:00
Soulter
53dbebb503 fix: 修复文转图模式下短文本报错的问题 #215 2024-10-14 18:01:51 +08:00
Soulter
52df91eb60 fix: 修复插件配置更新失败 2024-10-11 16:45:08 +08:00
Soulter
a9a758d715 perf: 更换更新插件依赖的方式 2024-10-10 22:53:00 +08:00
Soulter
0226fa7a25 Update README.md 2024-10-10 14:07:15 +08:00
Soulter
a4f47da35c feat: 支持插件市场 2024-10-07 16:26:39 +08:00
Soulter
29364000e2 chore: update version 2024-10-07 15:19:50 +08:00
Soulter
ceecca44a4 fix: active message 2024-10-07 15:18:08 +08:00
Soulter
50f62e66b0 perf: 文转图渲染失败时发送纯文本 2024-10-06 00:20:42 +08:00
Soulter
ab39dfd254 Merge pull request #214 from Soulter/dev
通过 Commit Hash 更新和仪表盘 UI 优化
2024-10-05 10:52:43 +08:00
Soulter
708fad18b6 Merge pull request #213 from lumenmai/identifier
添加可识别群员身份功能
2024-10-05 10:46:05 +08:00
Soulter
526ba34d87 remove: .idea 2024-10-05 10:35:03 +08:00
lumenmai
5d4882dee9 添加可识别群员身份功能 2024-10-05 00:28:20 +08:00
Soulter
48c4361d37 feat: 支持通过 commit hash 更新到指定 commit
perf: 仪表盘顶部导航栏优化
2024-10-04 15:09:07 +08:00
Soulter
c1d070186e fix: test 2024-10-04 00:17:55 +08:00
Soulter
1a39fd9172 fix: 修复消息计数 2024-10-04 00:11:04 +08:00
Soulter
0c1ab4158e chore: 更新部分配置项解释 2024-10-04 00:05:41 +08:00
Soulter
5221566335 refactor: dashboard backend, frontend
fix: 仪表盘部分配置不显示
2024-10-04 00:04:34 +08:00
Soulter
2291c2d9ba fix: metrics 没被正常消费 2024-09-29 12:30:50 +08:00
Soulter
0de14c4c8b perf: 配置项默认值 2024-09-23 11:07:52 -04:00
Soulter
51de0159fb chore: update version to 3.3.15 2024-09-23 10:51:02 -04:00
Soulter
37a756aeb3 fix: turn off openai api streaming mode 2024-09-23 10:49:57 -04:00
Soulter
353b6ed761 feat: 支持自定义文转图服务地址 2024-09-22 10:50:47 -04:00
Soulter
90815b1ac5 chore: update version to 3.3.14 2024-09-22 10:25:26 -04:00
Soulter
8a50786e61 feat: 支持设置控制台日志级别;
refactor: 重写了后端与仪表盘的日志通信
2024-09-22 10:23:26 -04:00
Soulter
3b77df0556 fix: 修复下载更新后压缩包不解压的问题 2024-09-21 12:37:05 -04:00
Soulter
1fa11062de fix: /plugin u 指令异常 2024-09-21 12:33:00 -04:00
Soulter
6883de0f1c feat: partially test http server api 2024-09-21 12:19:49 -04:00
Soulter
bdde0fe094 refactor: HTTP 请求全部异步化,移除了 baidu_aip, request 依赖 2024-09-21 11:36:02 -04:00
Soulter
ab22b8103e Merge pull request #208 from Soulter/fix-issue-207
fix: 修复仪表盘保存配置递归校验失效的问题
2024-09-21 22:42:16 +08:00
Soulter
641d5cd67b fix: 修复仪表盘保存配置递归校验失效的问题 2024-09-21 10:40:32 -04:00
Soulter
9fe941e457 fix(dashboard): 修复配置页不显示模型配置的问题 2024-09-20 05:10:47 -04:00
Soulter
78060c9985 refactor: moveplugins and temp folder to data/ 2024-09-20 04:41:44 -04:00
Soulter
5bd6af3400 Merge pull request #202 from Soulter/feat-middleware
支持插件注册消息中间件
2024-09-18 13:29:48 +08:00
Soulter
4ecd78d6a8 perf: remove error raise when command handler return an unexpected value 2024-09-17 04:49:49 -04:00
Soulter
7e9f54ed2c fix: change_password api 2024-09-17 03:33:18 -04:00
Soulter
7dd29c707f perf: 优化部分配置项的显示 2024-09-15 10:28:23 -04:00
Soulter
a1489fb1f9 Merge pull request #203 from Soulter/feat-custom-t2i-tmpl
自定义文转图 HTML 模板
2024-09-14 20:38:50 +08:00
Soulter
5f0f5398e8 fix: custom t2i 2024-09-14 08:21:34 -04:00
Soulter
e3b2396f32 feat: custom t2i tmpl 2024-09-14 19:59:30 +08:00
Soulter
6fd70ed26a fix: call middleware 2024-09-11 04:59:49 -04:00
Soulter
a93e6ff01a feat: middleware 2024-09-11 16:47:44 +08:00
Soulter
6db8c38c58 chore: remove agent function of helloworld plugin 2024-09-11 15:38:08 +08:00
Soulter
d3d3ff7970 Update .codecov.yml 2024-09-11 12:34:49 +08:00
Soulter
c5b2b30f79 Merge pull request #200 from Soulter/config-refactor
Update dashboard
2024-09-10 11:43:58 +00:00
Soulter
ac2144d65b chore(dashboard): update dashboard 2024-09-10 07:40:39 -04:00
Soulter
c620b4f919 Merge pull request #184 from Soulter/config-refactor
更易读的配置格式和平台、LLM多实例
2024-09-10 11:01:42 +00:00
Soulter
292a3a43ba perf: 完善覆盖率测试 2024-09-10 03:56:44 -04:00
Soulter
5fc4693b9c remove: .coverage 2024-09-10 01:57:51 -04:00
Soulter
6dfbaf1b88 bugfixes 2024-09-10 01:57:13 -04:00
Soulter
14c6e56287 Merge branch 'master' into config-refactor 2024-09-10 13:17:04 +08:00
Soulter
7e48514f67 Update README.md 2024-09-08 21:06:20 +08:00
Soulter
d8e70c4d7f perf: 优化 llm tool 返回值处理 2024-09-08 08:41:26 -04:00
Soulter
fb52989d62 Merge pull request #199 from Soulter/dev
解耦合 LLM Tool Use 注册并暴露插件接口
2024-09-08 12:24:34 +00:00
Soulter
5b72ebaad5 delete: remove deprecated files 2024-09-08 08:23:43 -04:00
Soulter
98863ab901 feat: customized tool-use 2024-09-08 08:16:36 -04:00
Soulter
b5cb5eb969 feat: customized tool-use 2024-09-08 19:41:00 +08:00
Soulter
7f4f96f77b Merge branch 'master' into dev 2024-09-08 19:39:26 +08:00
Soulter
3b3f75f03e fix: 增大超时时间 2024-08-18 04:00:45 -04:00
Soulter
a5db4d4e47 fix: 修复异端情况下主动信息发送带有本地图片url的消息时报错的问题 2024-08-18 03:55:11 -04:00
Soulter
d3b0f25cfe refactor: Update ProviderOpenAIOfficial to skip test message when TEST_MODE=on
This commit updates the `ProviderOpenAIOfficial` class to skip returning the test message when the environment variable `TEST_MODE` is set to "on". This change ensures that the test message is only returned when both `TEST_LLM` and `TEST_MODE` are set to "on".
2024-08-17 06:19:08 -04:00
Soulter
a9c6a68c5f Update README.md 2024-08-17 17:59:59 +08:00
Soulter
c27f172452 Merge pull request #190 from Soulter/feat-test
[Feature] 添加自动化测试
2024-08-17 17:56:43 +08:00
Soulter
2eeb5822c1 chore: add codecov.yml 2024-08-17 05:54:38 -04:00
Soulter
743046d48f chore: Create necessary directories for data and temp in coverage test workflow 2024-08-17 05:29:52 -04:00
Soulter
d3a5205bde refactor: Update coverage test workflow to properly create command configuration file 2024-08-17 05:27:33 -04:00
Soulter
ae6dd8929a refactor: Update coverage test workflow to create command configuration file properly 2024-08-17 05:25:45 -04:00
Soulter
dcf96896ef chore: Update coverage test workflow to install dependencies from requirements.txt 2024-08-17 05:10:05 -04:00
Soulter
67792100bb refactor: Fix command configuration file creation in coverage test workflow 2024-08-17 05:08:08 -04:00
Soulter
48c1263417 chore: add coverage test workflow 2024-08-17 05:02:34 -04:00
Soulter
12d37381fe perf: request llm api when only TEST_LLM=on 2024-08-17 04:49:43 -04:00
Soulter
dcec3f5f84 feat: unit test
perf: func call improvement
2024-08-17 04:46:23 -04:00
Soulter
32e2a7830a feat: Add timeout parameter to QQOfficial bot client initialization 2024-08-17 03:20:08 -04:00
Soulter
6992249e53 refactor: Update image downloading method in ProviderOpenAIOfficial 2024-08-17 15:06:13 +08:00
Soulter
107214ac53 fix: Handle errors in AstrBotBootstrap gracefully 2024-08-17 15:01:55 +08:00
Soulter
8a58772911 perf: fill the missing metric record 2024-08-17 14:58:43 +08:00
Soulter
e21736b470 perf: remove message reply when rate limit occur 2024-08-17 14:54:11 +08:00
Soulter
e8679f8984 Create codeql.yml 2024-08-17 14:34:02 +08:00
Soulter
970fe02027 fix: 修复QQ官方机器人API聊天时不能找到平台的问题 #189 2024-08-17 14:30:35 +08:00
Soulter
12216853c5 chore: issue and pr template 2024-08-17 11:20:36 +08:00
Soulter
33ec92258d Update config.py 2024-08-13 15:05:16 +08:00
Soulter
a578edf137 fix: metrics
perf: aiocqhttp image url
2024-08-12 02:50:31 -04:00
Soulter
f8949ebead perf: reboot after installing plugin 2024-08-11 23:24:37 -04:00
Soulter
141c91301f perf: Improve sleep time handling in QQOfficial and ProviderOpenAIOfficial 2024-08-11 23:24:37 -04:00
Soulter
8d95e67b5a Update README.md 2024-08-11 17:13:49 +08:00
Soulter
0633e7f25f perf: improve the effects of local function-calling 2024-08-11 03:55:31 -04:00
Soulter
266da0a9d8 fix: 修复重启时 aiocqhttp 没有正常退出导致端口占用的问题 2024-08-11 02:30:49 -04:00
Soulter
121c40f273 perf: raise error when badrequest 2024-08-11 01:49:33 -04:00
Soulter
a876efb95f fix: 更新后覆盖文件路径错误 2024-08-10 04:35:07 -04:00
Soulter
95a8cc9498 fix: 修复部分字段未更新导致的错误 2024-08-10 04:13:24 -04:00
Soulter
f02731055e fix: 修复插件启用忽略前缀之后可能的逻辑冲突 2024-08-10 03:25:50 -04:00
Soulter
1df83addfc update: add gcc 2024-08-10 14:59:00 +08:00
Soulter
9db43ac5e6 feat: 注册指令支持忽略指令前缀;快捷主动回复 2024-08-10 02:35:54 -04:00
Soulter
0f470cf96f Update README.md 2024-08-09 12:26:00 +08:00
Soulter
da3fcb7b86 Merge pull request #186 from itgpt-com/master
优化 docker build
2024-08-08 22:15:48 +08:00
Soulter
73dd4703b9 Update .dockerignore 2024-08-08 22:15:05 +08:00
itgpt
0c679a0151 添加 .dockerignore 过滤 docker cp 不必要文件。缩小镜像 2024-08-08 16:21:30 +08:00
itgpt
1d6ea2dbe6 添加端口输出 2024-08-08 16:16:55 +08:00
itgpt
933df57654 优化 docker build 2024-08-08 15:53:44 +08:00
Soulter
a7c87642b4 refactor: Update configuration format and handling 2024-08-06 23:21:18 -04:00
Soulter
cbe761fc33 Update README.md 2024-08-07 00:49:00 +08:00
Soulter
f8aef78d25 feat: 重构配置格式
perf: 优化配置处理过程和呈现方式
2024-08-06 04:58:29 -04:00
Soulter
14dbdb2d83 feat: 插件支持正则匹配 2024-08-05 12:12:00 -04:00
Soulter
abda226d63 Merge pull request #183 from irorange27/master
fix: fix logo syntax warning
2024-08-05 23:37:57 +08:00
niina
a2dc6f0a49 fix: fix logo syntax warning 2024-08-05 22:53:45 +08:00
Soulter
7a94c26333 fix: 修复 wake 唤醒词无法触发 command 的问题 2024-08-05 05:02:57 -04:00
Soulter
9b1ffb384b perf: 优化aiocqhttp适配器的异常处理 2024-08-05 04:46:12 -04:00
Soulter
9566bfe122 workaround for issue #181 2024-08-03 17:03:38 +08:00
Soulter
89ff103bda chore: Add mimetypes workaround for issue #188 2024-08-03 17:02:45 +08:00
Soulter
6c788db53a Merge remote-tracking branch 'refs/remotes/origin/master' 2024-08-03 16:17:25 +08:00
Soulter
344b5fa419 fix: f-string eror 2024-08-03 16:17:04 +08:00
Soulter
c6d161b837 Update README.md 2024-08-03 15:04:20 +08:00
Soulter
2065ba0c60 Update README.md 2024-08-03 01:05:27 +08:00
Soulter
a481fd1a3e fix: Strip leading and trailing whitespace from llm_wake_prefix 2024-08-02 23:17:35 +08:00
Soulter
c50bcdbdb9 fix: Register command only if plugin is found 2024-08-02 22:48:04 +08:00
Soulter
36a2a7632c fix: 优化初始化、消息处理时的配置读取过程,减少性能损耗 2024-07-31 23:38:31 +08:00
Soulter
e77b7014e6 fix: 修复更新、卸载插件时的报错 2024-07-30 09:15:45 +08:00
Soulter
d57fd0f827 fix: metadata is not seralizable 2024-07-29 09:47:42 +08:00
Soulter
6a83d2a62a update version 2024-07-28 12:11:07 +08:00
Soulter
2d29726c18 fix: 修复带空格路径导致的重启失败 2024-07-28 11:55:57 +08:00
Soulter
b241b0f954 update version 2024-07-27 12:31:15 -04:00
Soulter
171dd1dc02 feat: qq 官方机器人接口支持C2C 2024-07-27 12:30:09 -04:00
Soulter
af62d969d7 perf: 更改 send_msg 接口 2024-07-27 11:26:02 -04:00
Soulter
c4fd9a66c6 update version to 3.3.3 2024-07-27 11:08:51 -04:00
Soulter
d191997a39 feat: aiocqhttp 适配器适配主动发送消息接口 2024-07-27 11:07:26 -04:00
Soulter
853ac4c104 fix: 优化 update 提示 2024-07-27 04:58:15 -04:00
Soulter
ed053acad6 update: version 2024-07-27 04:47:57 -04:00
Soulter
f147634e51 fix: 修复update异常 2024-07-27 04:43:53 -04:00
Soulter
e3b2a68341 Merge pull request #179 from Soulter/refactor-v3.3.0
feat: 新增 Provider 注册接口;新增 provider 指令
2024-07-27 16:31:03 +08:00
Soulter
84c450aef9 feat: 新增 Provider 注册接口;新增 provider 指令 2024-07-27 04:25:27 -04:00
Soulter
f52a0eb43a fix: 修复默认配置迁移问题 2024-07-27 08:58:26 +08:00
Soulter
6ed7559518 Merge pull request #174 from Soulter/refactor-v3.3.0
重写工程,提高稳定性
2024-07-26 18:24:33 +08:00
Soulter
d977dbe9a7 update version 2024-07-26 06:24:11 -04:00
Soulter
17fc761c61 chore: update default plugin 2024-07-26 05:15:41 -04:00
Soulter
af878f2ed3 fix: 修复 aiocqhttp 运行导致 ctrl+c 无法退出 bot 的问题
perf: 支持通过context注册task
2024-07-26 05:02:29 -04:00
Soulter
bb2164c324 perf: 在 context 中添加message_handler 2024-07-25 12:58:45 -04:00
Soulter
0496becc50 perf: 增强 aiocqhttp 发图的稳定性 2024-07-25 12:33:31 -04:00
Soulter
618f8aa7d2 fix: 修复一些指令的bug 2024-07-25 10:44:17 -04:00
Soulter
c57f711c48 update: metrics refactoring 2024-07-24 09:48:25 -04:00
Soulter
4edd11f2f7 fix: 修复了一些bug。 2024-07-24 09:19:43 -04:00
Soulter
a2cf058951 update: refactor codes 2024-07-24 18:40:08 +08:00
Soulter
d52eb10ddd chore: remove large font files to shrink the source code size 2024-07-07 21:37:44 +08:00
Soulter
4b6dae71fc update: 更新默认插件 helloworld 2024-07-07 21:00:18 +08:00
Soulter
ddad30c22e feat: 支持本地上传插件 2024-07-07 20:59:12 +08:00
Soulter
77067c545c feat: 使用压缩包文件的更新方式 2024-07-07 18:26:58 +08:00
Soulter
465d283cad Update README.md 2024-06-23 11:23:17 +08:00
Soulter
05071144fb fix: 修复文转图相关问题 2024-06-09 08:56:52 -04:00
Soulter
a4e7904953 chore: clean codes 2024-06-03 20:40:18 -04:00
Soulter
986a8c7554 Update README.md 2024-06-03 21:18:53 +08:00
Soulter
9272843b77 Update README.md 2024-06-03 21:18:00 +08:00
Soulter
542d4bc703 typo: fix t2i typo 2024-06-03 08:47:51 -04:00
Soulter
e3640fdac9 perf: 优化update、help等指令的输出效果 2024-06-03 08:33:17 -04:00
Soulter
f64ab4b190 chore: 移除了一些过时的方法 2024-06-03 05:54:40 -04:00
Soulter
bd571e1577 feat: 提供新的文本转图片样式 2024-06-03 05:51:44 -04:00
Soulter
e4a5cbd893 prof: 改善加载插件时的稳定性 2024-06-03 00:20:56 -04:00
Soulter
7a9fd7fd1e fix: 修复报配置文件未找到的问题 2024-06-02 23:14:48 -04:00
Soulter
d9b60108db Update README.md 2024-05-30 18:11:57 +08:00
Soulter
8455c8b4ed Update README.md 2024-05-30 18:03:59 +08:00
Soulter
5c2e7099fc Update README.md 2024-05-26 21:38:32 +08:00
Soulter
1fd1d55895 Update config.py 2024-05-26 21:31:26 +08:00
Soulter
5ce4137e75 fix: 修复model指令 2024-05-26 21:15:33 +08:00
Soulter
d49179541e feat: 给插件的init方法传入 ctx 2024-05-26 21:10:19 +08:00
Soulter
676f258981 perf: 重启后终止子进程 2024-05-26 21:09:23 +08:00
Soulter
fa44749240 fix: 修复相对路径导致的windows启动器无法安装依赖的问题 2024-05-26 18:15:25 +08:00
Soulter
6c856f9da2 fix(typo): 修复插件注册器的一个typo导致无法注册消息平台插件的问题 2024-05-26 18:07:07 +08:00
Soulter
e8773cea7f fix: 修复配置文件没有有效迁移的问题 2024-05-25 20:59:37 +08:00
Soulter
4d36ffcb08 fix: 优化插件的结果处理 2024-05-25 18:46:38 +08:00
Soulter
c653e492c4 Merge pull request #164 from Soulter/stat-upload-perf
/models 指令优化
2024-05-25 18:35:56 +08:00
Soulter
f08de1f404 perf: 添加 models 指令到帮助中 2024-05-25 18:34:08 +08:00
Soulter
1218691b61 perf: model 指令放宽限制,支持输入自定义模型。设置模型后持久化保存。 2024-05-25 18:29:01 +08:00
Soulter
61fc27ff79 Merge pull request #163 from Soulter/stat-upload-perf
优化统计记录数据结构
2024-05-25 18:28:08 +08:00
Soulter
123ee24f7e fix: stat perf 2024-05-25 18:01:16 +08:00
Soulter
52c9045a28 feat: 优化了统计信息数据结构 2024-05-25 17:47:41 +08:00
Soulter
f00f1e8933 fix: 画图报错 2024-05-24 13:33:02 +08:00
Soulter
8da4433e57 chore: 更改相关字段 2024-05-21 08:44:05 +08:00
Soulter
7babb87934 perf: 更改库的加载顺序 2024-05-21 08:41:46 +08:00
Soulter
f67b171385 perf: 数据库迁移至 data 目录下 2024-05-19 17:10:11 +08:00
Soulter
1780d1355d perf: 将内部pip全部更换为阿里云镜像; 插件依赖更新逻辑优化 2024-05-19 16:45:08 +08:00
Soulter
5a3390e4f3 fix: force update 2024-05-19 16:06:47 +08:00
Soulter
337d96b41d Merge pull request #160 from Soulter/dev_default_openai_refactor
优化自带的 OpenAI LLM 交互, 人格, 网页搜索
2024-05-19 15:23:19 +08:00
Soulter
38a1dfea98 fix: web content scraper add proxy 2024-05-19 15:08:22 +08:00
Soulter
fbef73aeec fix: websearch encoding set to utf-8 2024-05-19 14:42:28 +08:00
Soulter
d6214c2b7c fix: web search 2024-05-19 12:55:54 +08:00
Soulter
d58c86f6fc perf: websearch 优化;项目结构调整 2024-05-19 12:46:07 +08:00
Soulter
ea34c20198 perf: 优化人格和LVM的处理过程 2024-05-18 10:34:35 +08:00
Soulter
934ca94e62 refactor: 重写 LLM OpenAI 模块 2024-05-17 22:56:44 +08:00
Soulter
1775327c2e chore: refact openai official 2024-05-17 09:07:11 +08:00
Soulter
707fcad8b4 feat: gpt 模型列表查看指令 models 2024-05-17 00:06:49 +08:00
Soulter
f143c5afc6 fix: 修复 plugin v 子指令报错的问题 2024-05-16 23:11:07 +08:00
Soulter
99f94b2611 fix: 修复无法调用某些指令的问题 2024-05-16 23:04:47 +08:00
Soulter
e39c1f9116 remove: 移除自动更换多模态模型的功能 2024-05-16 22:46:50 +08:00
Soulter
235e0b9b8f fix: gocq logging 2024-05-09 13:24:31 +08:00
Soulter
d5a9bed8a4 fix(updator): IterableList object has no
attribute origin
2024-05-08 19:18:21 +08:00
Soulter
d7dc8a7612 chore: 添加一些日志;更新版本 2024-05-08 19:12:23 +08:00
Soulter
08cd3ca40c perf: 更好的日志输出;
fix: 修复可视化面板刷新404
2024-05-08 19:01:36 +08:00
Soulter
a13562dcea fix: 修复启动器启动加载带有配置的插件时提示配置文件缺失的问题 2024-05-08 16:28:30 +08:00
Soulter
d7a0c0d1d0 Update requirements.txt 2024-05-07 15:58:51 +08:00
Soulter
c0729b2d29 fix: 修复插件重载相关问题 2024-04-22 19:04:15 +08:00
Soulter
a80f474290 fix: 修复更新插件时的报错 2024-04-22 18:36:56 +08:00
Soulter
699207dd54 update: version 2024-04-21 22:41:48 +08:00
Soulter
e7708010c9 fix: 修复 gocq 平台下无法回复消息的问题 2024-04-21 22:39:09 +08:00
Soulter
f66091e08f 🎨: clean codes 2024-04-21 22:20:23 +08:00
Soulter
03bb932f8f fix: 修复可视化面板报错 2024-04-21 22:16:42 +08:00
Soulter
fbf8b349e0 update: helloworld 2024-04-21 22:13:27 +08:00
Soulter
e9278fce6a !! delete: 移除对逆向 ChatGPT 的所有支持。 2024-04-21 22:12:09 +08:00
Soulter
9a7db956d5 fix: 修复 3.10.x readibility 依赖导致的报错 2024-04-21 16:40:02 +08:00
Soulter
13196dd667 perf: 修改包路径 2024-03-15 14:49:44 +08:00
Soulter
52b80e24d2 Merge remote-tracking branch 'refs/remotes/origin/master' 2024-03-15 14:29:48 +08:00
Soulter
7dff87e65d fix: 修复无法更新到指定版本的问题 2024-03-15 14:29:28 +08:00
Soulter
31ee64d1b2 Update docker-image.yml 2024-03-15 14:11:57 +08:00
Soulter
8e865b6918 fix: 修复无LLM情况下update不管用的问题 2024-03-15 14:05:16 +08:00
Soulter
66f91e5832 update: 更新版本号 2024-03-15 13:50:57 +08:00
Soulter
cd2d368f9c fix: 修复可视化面板指定版本更新失效的问题 2024-03-15 13:48:14 +08:00
Soulter
7736c1c9bd feat: QQ机器人官方API 支持可选择是否接收Q群消息 2024-03-15 13:44:18 +08:00
Soulter
6728c0b7b5 chore: 改变包名 2024-03-15 13:37:51 +08:00
Soulter
344f92e0e7 perf: 将内部基本消息对象统一为 AstrBotMessage
feat: 支持官方qq接口的Q群消息
2024-03-14 13:56:32 +08:00
Soulter
fdabfef6a7 update: version 2024-03-13 21:28:18 +08:00
Soulter
6c5718f134 fix: 修复画图的报错 2024-03-13 21:27:48 +08:00
Soulter
edfde51434 fix: 修复频道平台下未找到平台 qqchan 的实例的错误 2024-03-13 19:53:36 +08:00
Soulter
3fc1347bba fix: plugin register management 2024-03-12 20:00:02 +08:00
Soulter
e643eea365 perf: 结构化插件的表示格式; 优化插件开发接口 2024-03-12 18:50:50 +08:00
Soulter
1af481f5f9 fix: function call with newer version 2024-03-07 17:35:21 +08:00
Soulter
317d1c4c41 fix: onebot protocol connection error 2024-03-05 14:03:46 +08:00
Soulter
a703860512 fix: plugin call 2024-03-05 13:52:44 +08:00
Soulter
1cd1c8ea0d feat: 异步重写
perf: 优化网页搜索回答规范
2024-03-03 18:54:50 +08:00
Soulter
53ef3bbf4f fix: 修复修改cqhttp端口后仍检测失败的问题 2024-02-19 19:04:40 +08:00
Soulter
ab7b8aad7c chore: delete llms 2024-02-12 23:28:12 +08:00
Soulter
c49213282b Merge remote-tracking branch 'refs/remotes/origin/master' 2024-02-12 23:18:11 +08:00
Soulter
3c87fc5b31 perf: clean codes; 将 keyword 功能转移至 helloworld 插件下 2024-02-12 23:17:55 +08:00
Soulter
9684508e1d Update README.md 2024-02-11 13:47:09 +08:00
Soulter
bb0edae200 Update README.md 2024-02-08 00:40:48 +08:00
Soulter
acb68a4a1e chore: 更新版本标识 2024-02-08 00:31:08 +08:00
Soulter
46dd6f3243 fix: 1. 修复可视化面板无法保存配置的问题;修复help指令无法生成图片的问题
feat: 支持更多插件标准接口
2024-02-08 00:29:37 +08:00
Soulter
ecab072890 chore: 改变版本号;清理一些无用变量 2024-02-07 17:41:10 +08:00
Soulter
148534d3c2 Merge remote-tracking branch 'refs/remotes/origin/master' 2024-02-07 16:45:11 +08:00
Soulter
1278f16973 feat: 可视化面板完整支持插件配置 2024-02-07 16:44:38 +08:00
Soulter
7d9b3c6c5c Update docker-image.yml 2024-02-07 13:05:52 +08:00
Soulter
83dcb5165c perf: 优化可视化面板配置显示;
feat: 新增面向插件的配置接口
2024-02-07 12:19:52 +08:00
Soulter
30862bb82f perf: 优化更新速度和更新流程 2024-02-06 19:18:53 +08:00
Soulter
6c0bda8feb Update README.md 2024-02-06 18:30:56 +08:00
Soulter
e14dece206 perf: 优化项目更新逻辑 2024-02-06 17:45:02 +08:00
Soulter
680593d636 fix: 修复web指令前缀失效问题 2024-02-06 15:42:29 +08:00
Soulter
144440214f fix: 修复画图报错的问题 2024-02-06 12:56:41 +08:00
Soulter
6667b58a3f fix: 修复容器出现的一些问题 2024-02-06 12:48:57 +08:00
Soulter
b55d9533be chore: 清理了一些无用代码 2024-02-05 23:46:46 +08:00
Soulter
3484fc60e6 fix: dashboard fake dead 2024-02-05 14:51:19 +08:00
Soulter
eac0265522 fix: 修复频道私聊且独立会话下的报错 2024-02-05 14:45:32 +08:00
Soulter
ac74431633 fix: 修复远程连接可视化面板下控制台不能正常显示的问题 2024-02-05 14:12:38 +08:00
Soulter
4c098200be fix: 修复docker环境下ws server的报错 2024-02-05 13:54:45 +08:00
Soulter
2cf18972f3 fix: 修复面板保存配置时报错的问题;修复频道私聊报错的问题
perf: 改善日志
2024-02-05 13:18:34 +08:00
Soulter
d522d2a6a9 Merge remote-tracking branch 'refs/remotes/origin/master' 2024-02-04 21:29:58 +08:00
Soulter
7079ce096f feat: 可视化面板支持日志显示
chore: 减少了一些日志输出
2024-02-04 21:28:03 +08:00
Soulter
5e8c5067b1 Update README.md 2024-01-16 00:32:07 +08:00
Soulter
570ff4e8b6 perf: 优化bing网页搜索 2024-01-10 16:48:46 +08:00
Soulter
e2f1362a1f fix: 修复myid指令在gocq平台上不可用的情况 2024-01-09 22:25:52 +08:00
Soulter
3519e38211 perf: 移除默认prompt 2024-01-07 14:51:43 +08:00
Soulter
08734250f7 feat: 支持频道上的文字转图片 2024-01-05 18:59:02 +08:00
Soulter
e8407f6449 feat: 添加逆向语言模型服务相关配置到面板 2024-01-05 17:13:52 +08:00
Soulter
04f3400f83 perf: 改善插件搜集流程 2024-01-03 20:19:31 +08:00
Soulter
89c8b3e7fc fix: 修复 gocq 环境下 at 机器人报错的问题 2024-01-03 16:32:08 +08:00
Soulter
66294100ec fix: typo fix 2024-01-03 16:26:58 +08:00
Soulter
8ed8a23c8b fix: 修复 gocq 环境下消息响应的一些问题 2024-01-03 16:15:37 +08:00
Soulter
449b0b03b5 fix: 修复报错 nick_qq 的问题 2024-01-03 16:00:51 +08:00
Soulter
d93754bf1d Update cmd_config.py 2024-01-03 15:46:11 +08:00
Soulter
a007a61ecc Update docker-image.yml 2024-01-02 16:44:58 +08:00
Soulter
e481377317 fix: 修复 update 的一些问题 2024-01-01 12:46:22 +08:00
Soulter
4c5831c7b4 remove: 删除 simhei 字体资源 2024-01-01 12:03:21 +08:00
Soulter
fc54b5237f feat: 支持设置 llm 唤醒词 2024-01-01 11:48:55 +08:00
Soulter
f8f42678d1 fix: 修复 消息 send() 不能够正常使用的问题 2024-01-01 11:34:56 +08:00
Soulter
38b1f4128c Merge pull request #145 from Soulter/dev_platform_refact
重构与消息平台有关的部分代码
2024-01-01 11:07:13 +08:00
Soulter
04fb4f88ad feat: 重构代码 2023-12-30 20:08:28 +08:00
Soulter
4675f5df08 Create stale.yml 2023-12-28 14:12:25 +08:00
Soulter
34ee358d40 Update README.md 2023-12-28 14:01:53 +08:00
Soulter
c4cfd1a3e2 Update README.md 2023-12-28 13:18:47 +08:00
Soulter
f5857aaa0c Merge branch 'master' into dev 2023-12-02 16:26:25 +08:00
Soulter
f4222e0923 bugfixes 2023-11-21 22:37:35 +08:00
Soulter
f0caea9026 feat: 针对 OneBot 和 NoneBot 的消息兼容层和插件的初步适配 2023-11-21 14:23:47 +08:00
389 changed files with 41822 additions and 7108 deletions

23
.dockerignore Normal file
View File

@@ -0,0 +1,23 @@
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# github acions
.github/
.*ignore
.git/
# User-specific stuff
.idea/
# Byte-compiled / optimized / DLL files
__pycache__/
# Environments
.env
.venv
env/
venv*/
ENV/
.conda/
README*.md
dashboard/
data/
changelogs/
tests/
.ruff_cache/

View File

@@ -0,0 +1,40 @@
name: '🥳 发布插件'
title: "[Plugin] 插件名"
description: 提交插件到插件市场
labels: [ "plugin-publish" ]
body:
- type: markdown
attributes:
value: |
欢迎发布插件到插件市场!请确保您的插件经过**完整的**测试。
- type: textarea
attributes:
label: 插件仓库
description: 插件的 GitHub 仓库链接
placeholder: >
如 https://github.com/Soulter/astrbot-github-cards
- type: textarea
attributes:
label: 描述
value: |
插件名:
插件作者:
插件简介:
支持的消息平台:(必填,如 QQ、微信、飞书)
标签:(可选)
社交链接:(可选, 将会在插件市场作者名称上作为可点击的链接)
description: 必填。请以列表的字段按顺序将插件名、插件作者、插件简介放在这里。如果您不知道支持哪些消息平台,请填写测试过的消息平台。
- type: checkboxes
attributes:
label: Code of Conduct
options:
- label: >
我已阅读并同意遵守该项目的 [行为准则](https://docs.github.com/zh/site-policy/github-terms/github-community-code-of-conduct)。
required: true
- type: markdown
attributes:
value: "❤️"

82
.github/ISSUE_TEMPLATE/bug-report.yml vendored Normal file
View File

@@ -0,0 +1,82 @@
name: '🐛 报告 Bug'
title: '[Bug]'
description: 提交报告帮助我们改进。
labels: [ 'bug' ]
body:
- type: markdown
attributes:
value: |
感谢您抽出时间报告问题!请准确解释您的问题。如果可能,请提供一个可复现的片段(这有助于更快地解决问题)。
- type: textarea
attributes:
label: 发生了什么
description: 描述你遇到的异常
placeholder: >
一个清晰且具体的描述这个异常是什么。
validations:
required: true
- type: textarea
attributes:
label: 如何复现?
description: >
复现该问题的步骤
placeholder: >
如: 1. 打开 '...'
validations:
required: true
- type: textarea
attributes:
label: AstrBot 版本、部署方式(如 Windows Docker Desktop 部署)、使用的提供商、使用的消息平台适配器
description: >
请提供您的 AstrBot 版本和部署方式。
placeholder: >
如: 3.1.8 Docker, 3.1.7 Windows启动器
validations:
required: true
- type: dropdown
attributes:
label: 操作系统
description: |
你在哪个操作系统上遇到了这个问题?
multiple: false
options:
- 'Windows'
- 'macOS'
- 'Linux'
- 'Other'
- 'Not sure'
validations:
required: true
- type: textarea
attributes:
label: 报错日志
description: >
如报错日志、截图等。请提供完整的 Debug 级别的日志,不要介意它很长!
placeholder: >
请提供完整的报错日志或截图。
validations:
required: true
- type: checkboxes
attributes:
label: 你愿意提交 PR 吗?
description: >
这不是必需的,但我们很乐意在贡献过程中为您提供指导特别是如果你已经很好地理解了如何实现修复。
options:
- label: 是的,我愿意提交 PR!
- type: checkboxes
attributes:
label: Code of Conduct
options:
- label: >
我已阅读并同意遵守该项目的 [行为准则](https://docs.github.com/zh/site-policy/github-terms/github-community-code-of-conduct)。
required: true
- type: markdown
attributes:
value: "感谢您填写我们的表单!"

View File

@@ -0,0 +1,42 @@
name: '🎉 功能建议'
title: "[Feature]"
description: 提交建议帮助我们改进。
labels: [ "enhancement" ]
body:
- type: markdown
attributes:
value: |
感谢您抽出时间提出新功能建议,请准确解释您的想法。
- type: textarea
attributes:
label: 描述
description: 简短描述您的功能建议。
- type: textarea
attributes:
label: 使用场景
description: 你想要发生什么?
placeholder: >
一个清晰且具体的描述这个功能的使用场景。
- type: checkboxes
attributes:
label: 你愿意提交PR吗?
description: >
这不是必须的,但我们欢迎您的贡献。
options:
- label: 是的, 我愿意提交PR!
- type: checkboxes
attributes:
label: Code of Conduct
options:
- label: >
我已阅读并同意遵守该项目的 [行为准则](https://docs.github.com/zh/site-policy/github-terms/github-community-code-of-conduct)。
required: true
- type: markdown
attributes:
value: "感谢您填写我们的表单!"

14
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,14 @@
<!-- 如果有的话,指定这个 PR 要解决的 ISSUE -->
修复了 #XYZ
### Motivation
<!--解释为什么要改动-->
### Modifications
<!--简单解释你的改动-->
### Check
- [ ] 我的 Commit Message 符合良好的[规范](https://www.conventionalcommits.org/en/v1.0.0/#summary)
- [ ] 我新增/修复/优化的功能经过良好的测试

35
.github/workflows/auto_release.yml vendored Normal file
View File

@@ -0,0 +1,35 @@
on:
push:
tags:
- 'v*'
workflow_dispatch:
name: Auto Release
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Dashboard Build
run: |
cd dashboard
npm install
npm run build
echo "COMMIT_SHA=$(git rev-parse HEAD)" >> $GITHUB_ENV
echo ${{ github.ref_name }} > dist/assets/version
zip -r dist.zip dist
- name: Fetch Changelog
run: |
echo "changelog=changelogs/${{github.ref_name}}.md" >> "$GITHUB_ENV"
- name: Create Release
uses: ncipollo/release-action@v1
with:
bodyFile: ${{ env.changelog }}
artifacts: "dashboard/dist.zip"

93
.github/workflows/codeql.yml vendored Normal file
View File

@@ -0,0 +1,93 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
schedule:
- cron: '21 15 * * 5'
jobs:
analyze:
name: Analyze (${{ matrix.language }})
# Runner size impacts CodeQL analysis time. To learn more, please see:
# - https://gh.io/recommended-hardware-resources-for-running-codeql
# - https://gh.io/supported-runners-and-hardware-resources
# - https://gh.io/using-larger-runners (GitHub.com only)
# Consider using larger runners or machines with greater resources for possible analysis time improvements.
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }}
permissions:
# required for all workflows
security-events: write
# required to fetch internal or private CodeQL packs
packages: read
# only required for workflows in private repositories
actions: read
contents: read
strategy:
fail-fast: false
matrix:
include:
- language: python
build-mode: none
# CodeQL supports the following values keywords for 'language': 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift'
# Use `c-cpp` to analyze code written in C, C++ or both
# Use 'java-kotlin' to analyze code written in Java, Kotlin or both
# Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both
# To learn more about changing the languages that are analyzed or customizing the build mode for your analysis,
# see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning.
# If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
steps:
- name: Checkout repository
uses: actions/checkout@v4
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
build-mode: ${{ matrix.build-mode }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality
# If the analyze step fails for one of the languages you are analyzing with
# "We were unable to automatically build your code", modify the matrix above
# to set the build mode to "manual" for that language. Then modify this step
# to build your code.
# Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
- if: matrix.build-mode == 'manual'
shell: bash
run: |
echo 'If you are using a "manual" build mode for one or more of the' \
'languages you are analyzing, replace this with the commands to build' \
'your code, for example:'
echo ' make bootstrap'
echo ' make release'
exit 1
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
with:
category: "/language:${{matrix.language}}"

45
.github/workflows/coverage_test.yml vendored Normal file
View File

@@ -0,0 +1,45 @@
name: Run tests and upload coverage
on:
push:
branches:
- master
paths-ignore:
- 'README.md'
- 'changelogs/**'
- 'dashboard/**'
workflow_dispatch:
jobs:
test:
name: Run tests and collect coverage
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v4
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install pytest pytest-cov pytest-asyncio
- name: Run tests
run: |
mkdir data
mkdir data/plugins
mkdir data/config
mkdir data/temp
export TESTING=true
export ZHIPU_API_KEY=${{ secrets.OPENAI_API_KEY }}
PYTHONPATH=./ pytest --cov=. tests/ -v -o log_cli=true -o log_level=DEBUG
- name: Upload results to Codecov
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}

31
.github/workflows/dashboard_ci.yml vendored Normal file
View File

@@ -0,0 +1,31 @@
name: AstrBot Dashboard CI
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: npm install, build
run: |
cd dashboard
npm install
npm run build
- name: Inject Commit SHA
id: get_sha
run: |
echo "COMMIT_SHA=$(git rev-parse HEAD)" >> $GITHUB_ENV
mkdir -p dashboard/dist/assets
echo $COMMIT_SHA > dashboard/dist/assets/version
- name: Archive production artifacts
uses: actions/upload-artifact@v4
with:
name: dist-without-markdown
path: |
dashboard/dist
!dist/**/*.md

View File

@@ -2,24 +2,42 @@ name: Docker Image CI/CD
on:
push:
branches:
- master
- dev_dashboard
paths-ignore:
- '**/*.md'
tags:
- 'v*'
workflow_dispatch:
jobs:
publish-latest-docker-image:
publish-docker:
runs-on: ubuntu-latest
name: Build and publish docker image
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Build image
run: |
docker build -t ${{ secrets.DOCKER_HUB_USERNAME }}/astrbot:v1 .
- name: Publish image
run: |
docker login -u ${{ secrets.DOCKER_HUB_USERNAME }} -p ${{ secrets.DOCKER_HUB_PASSWORD }}
docker push ${{ secrets.DOCKER_HUB_USERNAME }}/astrbot:v1
- name: 拉取源码
uses: actions/checkout@v3
with:
fetch-depth: 1
- name: 设置 QEMU
uses: docker/setup-qemu-action@v3
- name: 设置 Docker Buildx
uses: docker/setup-buildx-action@v3
- name: 登录到 DockerHub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
- name: 构建和推送 Docker hub
uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: |
${{ secrets.DOCKER_HUB_USERNAME }}/astrbot:latest
${{ secrets.DOCKER_HUB_USERNAME }}/astrbot:${{ github.ref_name }}
- name: Post build notifications
run: echo "Docker image has been built and pushed successfully"

27
.github/workflows/stale.yml vendored Normal file
View File

@@ -0,0 +1,27 @@
# This workflow warns and then closes issues and PRs that have had no activity for a specified amount of time.
#
# You can adjust the behavior by modifying this file.
# For more information, see:
# https://github.com/actions/stale
name: Mark stale issues and pull requests
on:
schedule:
- cron: '21 23 * * *'
jobs:
stale:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/stale@v5
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-issue-message: 'Stale issue message'
stale-pr-message: 'Stale pull request message'
stale-issue-label: 'no-issue-activity'
stale-pr-label: 'no-pr-activity'

26
.gitignore vendored
View File

@@ -1,8 +1,32 @@
__pycache__
botpy.log
.vscode
data.db
.venv*
.idea
data_v2.db
data_v3.db
configs/session
configs/config.yaml
**/.DS_Store
temp
cmd_config.json
data
cookies.json
logs/
addons/plugins
.coverage
tests/astrbot_plugin_openai
chroma
dashboard/node_modules/
dashboard/dist/
.DS_Store
package-lock.json
package.json
venv/*
packages/python_interpreter/workplace
.venv/*
.conda/
.idea
pytest.ini

13
.pre-commit-config.yaml Normal file
View File

@@ -0,0 +1,13 @@
default_install_hook_types: [pre-commit, prepare-commit-msg]
ci:
autofix_commit_msg: ":balloon: auto fixes by pre-commit hooks"
autofix_prs: true
autoupdate_branch: master
autoupdate_schedule: weekly
autoupdate_commit_msg: ":balloon: pre-commit autoupdate"
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.11.2
hooks:
- id: ruff
- id: ruff-format

View File

@@ -1,8 +1,35 @@
FROM python:3.10.13-bullseye
FROM python:3.10-slim
WORKDIR /AstrBot
COPY . /AstrBot/
RUN python -m pip install -r requirements.txt
RUN apt-get update && apt-get install -y --no-install-recommends \
nodejs \
npm \
gcc \
build-essential \
python3-dev \
libffi-dev \
libssl-dev \
ca-certificates \
bash \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
RUN python -m pip install uv
RUN uv pip install -r requirements.txt --no-cache-dir --system
RUN uv pip install socksio uv pyffmpeg pilk --no-cache-dir --system
# 释出 ffmpeg
RUN python -c "from pyffmpeg import FFmpeg; ff = FFmpeg();"
# add /root/.pyffmpeg/bin/ffmpeg to PATH, inorder to use ffmpeg
RUN echo 'export PATH=$PATH:/root/.pyffmpeg/bin' >> ~/.bashrc
EXPOSE 6185
EXPOSE 6186
CMD [ "python", "main.py" ]

35
Dockerfile_with_node Normal file
View File

@@ -0,0 +1,35 @@
FROM python:3.10-slim
WORKDIR /AstrBot
COPY . /AstrBot/
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc \
build-essential \
python3-dev \
libffi-dev \
libssl-dev \
curl \
unzip \
ca-certificates \
bash \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# Installation of Node.js
ENV NVM_DIR="/root/.nvm"
RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.2/install.sh | bash && \
. "$NVM_DIR/nvm.sh" && \
nvm install 22 && \
nvm use 22
RUN /bin/bash -c ". \"$NVM_DIR/nvm.sh\" && node -v && npm -v"
RUN python -m pip install uv
RUN uv pip install -r requirements.txt --no-cache-dir --system
RUN uv pip install socksio uv pyffmpeg --no-cache-dir --system
EXPOSE 6185
EXPOSE 6186
CMD ["python", "main.py"]

View File

@@ -629,8 +629,8 @@ to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
AstrBot is a llm-powered chatbot and develop framework.
Copyright (C) 2022-2099 Soulter
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published

346
README.md
View File

@@ -1,185 +1,203 @@
<p align="center">
<img src="https://github.com/Soulter/AstrBot/assets/37870767/b1686114-f3aa-4963-b07f-28bf83dc0a10" alt="QQChannelChatGPT" width="200" />
![yjtp](https://github.com/user-attachments/assets/dcc74009-c57e-4b66-9ae3-0a81fc001255)
</p>
<div align="center">
# AstrBot
_✨ 易上手的多平台 LLM 聊天机器人及开发框架 ✨_
*✨ 2024 - 希望成为一个跨平台、极易上手、稳定安全的机器人项目。✨*
<a href="https://trendshift.io/repositories/12875" target="_blank"><img src="https://trendshift.io/api/badge/repositories/12875" alt="Soulter%2FAstrBot | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
[![GitHub release (latest by date)](https://img.shields.io/github/v/release/Soulter/AstrBot)](https://github.com/Soulter/AstrBot/releases/latest)
<img src="https://wakatime.com/badge/user/915e5316-99c6-4563-a483-ef186cf000c9/project/34412545-2e37-400f-bedc-42348713ac1f.svg" alt="wakatime">
<img src="https://img.shields.io/badge/python-3.9+-blue.svg" alt="python">
<a href="https://qm.qq.com/cgi-bin/qm/qr?k=EYGsuUTfe00_iOu9JTXS7_TEpMkXOvwv&jump_from=webapi&authKey=uUEMKCROfsseS+8IzqPjzV3y1tzy4AkykwTib2jNkOFdzezF9s9XknqnIaf3CDft">
<img alt="Static Badge" src="https://img.shields.io/badge/QQ群-322154837-purple">
</a>
<img alt="Static Badge" src="https://img.shields.io/badge/频道-x42d56aki2-purple">
[![GitHub release (latest by date)](https://img.shields.io/github/v/release/Soulter/AstrBot?style=for-the-badge&color=76bad9)](https://github.com/Soulter/AstrBot/releases/latest)
<img src="https://img.shields.io/badge/python-3.10+-blue.svg?style=for-the-badge&color=76bad9" alt="python">
<a href="https://hub.docker.com/r/soulter/astrbot"><img alt="Docker pull" src="https://img.shields.io/docker/pulls/soulter/astrbot.svg?style=for-the-badge&color=76bad9"/></a>
<a href="https://qm.qq.com/cgi-bin/qm/qr?k=wtbaNx7EioxeaqS9z7RQWVXPIxg2zYr7&jump_from=webapi&authKey=vlqnv/AV2DbJEvGIcxdlNSpfxVy+8vVqijgreRdnVKOaydpc+YSw4MctmEbr0k5"><img alt="QQ_community" src="https://img.shields.io/badge/QQ群-775869627-purple?style=for-the-badge&color=76bad9"></a>
<a href="https://t.me/+hAsD2Ebl5as3NmY1"><img alt="Telegram_community" src="https://img.shields.io/badge/Telegram-AstrBot-purple?style=for-the-badge&color=76bad9"></a>
[![wakatime](https://wakatime.com/badge/user/915e5316-99c6-4563-a483-ef186cf000c9/project/018e705a-a1a7-409a-a849-3013485e6c8e.svg?style=for-the-badge&color=76bad9)](https://wakatime.com/badge/user/915e5316-99c6-4563-a483-ef186cf000c9/project/018e705a-a1a7-409a-a849-3013485e6c8e)
![Dynamic JSON Badge](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fapi.soulter.top%2Fastrbot%2Fstats&query=v&label=7%E6%97%A5%E6%B4%BB%E8%B7%83%E9%87%8F&cacheSeconds=3600&style=for-the-badge&color=3b618e)
![Dynamic JSON Badge](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fapi.soulter.top%2Fastrbot%2Fplugin-num&query=%24.result&suffix=%E4%B8%AA&style=for-the-badge&label=%E6%8F%92%E4%BB%B6%E5%B8%82%E5%9C%BA&cacheSeconds=3600)
<a href="https://astrbot.soulter.top/center">项目主页(开发中)</a>
<a href="https://github.com/Soulter/QQChannelChatGPT/wiki">部署文档</a>
<a href="https://github.com/Soulter/QQChannelChatGPT/issues">问题提交</a>
<a href="https://astrbot.soulter.top/center/docs/%E5%BC%80%E5%8F%91/%E6%8F%92%E4%BB%B6%E5%BC%80%E5%8F%91">插件开发(最少只需 25 行,真不难!)</a>
<a href="https://github.com/Soulter/AstrBot/blob/master/README_en.md">English</a>
<a href="https://github.com/Soulter/AstrBot/blob/master/README_ja.md">日本語</a>
<a href="https://astrbot.app/">查看文档</a>
<a href="https://github.com/Soulter/AstrBot/issues">问题提交</a>
</div>
AstrBot 是一个松耦合、异步、支持多消息平台部署、具有易用的插件系统和完善的大语言模型LLM接入功能的聊天机器人及开发框架。
[![star](https://gitcode.com/Soulter/AstrBot/star/badge.svg?style=for-the-badge)](https://gitcode.com/Soulter/AstrBot)
<!-- [![codecov](https://img.shields.io/codecov/c/github/soulter/astrbot?style=for-the-badge)](https://codecov.io/gh/Soulter/AstrBot)
-->
## ✨ 近期更新
1. AstrBot 现已支持接入 [MCP](https://modelcontextprotocol.io/) 服务器!
## ✨ 主要功能
> [!NOTE]
> 🪧 我们正基于前沿科研成果,设计并实现适用于角色扮演和情感陪伴的长短期记忆模型及情绪控制模型,旨在提升对话的真实性与情感表达能力。敬请期待 `v3.6.0` 版本!
1. **大语言模型对话**。支持各种大语言模型,包括 OpenAI API、Google Gemini、Llama、Deepseek、ChatGLM 等,支持接入本地部署的大模型,通过 Ollama、LLMTuner。具有多轮对话、人格情境、多模态能力支持图片理解、语音转文字Whisper
2. **多消息平台接入**。支持接入 QQOneBot、QQ 频道、微信Gewechat、飞书、Telegram。后续将支持钉钉、Discord、WhatsApp、小爱音响。支持速率限制、白名单、关键词过滤、百度内容审核。
3. **Agent**。原生支持部分 Agent 能力,如代码执行器、自然语言待办、网页搜索。对接 [Dify 平台](https://dify.ai/),便捷接入 Dify 智能助手、知识库和 Dify 工作流。
4. **插件扩展**。深度优化的插件机制,支持[开发插件](https://astrbot.app/dev/plugin.html)扩展功能,极简开发。已支持安装多个插件。
5. **可视化管理面板**。支持可视化修改配置、插件管理、日志查看等功能,降低配置难度。集成 WebChat可在面板上与大模型对话。
6. **高稳定性、高模块化**。基于事件总线和流水线的架构设计,高度模块化,低耦合。
> [!TIP]
> WebUI 在线体验 Demo: [https://demo.astrbot.app/](https://demo.astrbot.app/)
>
> 用户名: `astrbot`, 密码: `astrbot`。
## ✨ 使用方式
#### Docker 部署
请参阅官方文档 [使用 Docker 部署 AstrBot](https://astrbot.app/deploy/astrbot/docker.html#%E4%BD%BF%E7%94%A8-docker-%E9%83%A8%E7%BD%B2-astrbot) 。
#### Windows 一键安装器部署
请参阅官方文档 [使用 Windows 一键安装器部署 AstrBot](https://astrbot.app/deploy/astrbot/windows.html) 。
#### 宝塔面板部署
请参阅官方文档 [宝塔面板部署](https://astrbot.app/deploy/astrbot/btpanel.html) 。
#### CasaOS 部署
社区贡献的部署方式。
请参阅官方文档 [CasaOS 部署](https://astrbot.app/deploy/astrbot/casaos.html) 。
#### 手动部署
推荐使用 `uv`
```bash
git clone https://github.com/AstrBotDevs/AstrBot && cd AstrBot
pip install uv
uv run main.py
```
或者请参阅官方文档 [通过源码部署 AstrBot](https://astrbot.app/deploy/astrbot/cli.html) 。
#### Replit 部署
[![Run on Repl.it](https://repl.it/badge/github/Soulter/AstrBot)](https://repl.it/github/Soulter/AstrBot)
## ⚡ 消息平台支持情况
| 平台 | 支持性 | 详情 | 消息类型 |
| -------- | ------- | ------- | ------ |
| QQ(官方机器人接口) | ✔ | 私聊、群聊QQ 频道私聊、群聊 | 文字、图片 |
| QQ(OneBot) | ✔ | 私聊、群聊 | 文字、图片、语音 |
| 微信(个人号) | ✔ | 微信个人号私聊、群聊 | 文字、图片、语音 |
| [Telegram](https://github.com/Soulter/astrbot_plugin_telegram) | ✔ | 私聊、群聊 | 文字、图片 |
| [微信(企业微信)](https://github.com/Soulter/astrbot_plugin_wecom) | ✔ | 私聊 | 文字、图片、语音 |
| 飞书 | ✔ | 私聊、群聊 | 文字、图片 |
| 钉钉 | ✔ | 私聊、群聊 | 文字、图片 |
| 微信对话开放平台 | 🚧 | 计划内 | - |
| Discord | 🚧 | 计划内 | - |
| WhatsApp | 🚧 | 计划内 | - |
| 小爱音响 | 🚧 | 计划内 | - |
## ⚡ 提供商支持情况
| 名称 | 支持性 | 类型 | 备注 |
| -------- | ------- | ------- | ------- |
| OpenAI API | ✔ | 文本生成 | 也支持 DeepSeek、Google Gemini、GLM、Kimi、硅基流动、xAI 等兼容 OpenAI API 的服务 |
| Claude API | ✔ | 文本生成 | |
| Google Gemini API | ✔ | 文本生成 | |
| Dify | ✔ | LLMOps | |
| DashScope(阿里云百炼应用) | ✔ | LLMOps | |
| Ollama | ✔ | 模型加载器 | 本地部署 DeepSeek、Llama 等开源语言模型 |
| LM Studio | ✔ | 模型加载器 | 本地部署 DeepSeek、Llama 等开源语言模型 |
| LLMTuner | ✔ | 模型加载器 | 本地加载 lora 等微调模型 |
| OneAPI | ✔ | LLM 分发系统 | |
| Whisper | ✔ | 语音转文本 | 支持 API、本地部署 |
| SenseVoice | ✔ | 语音转文本 | 本地部署 |
| OpenAI TTS API | ✔ | 文本转语音 | |
| GSVI | ✔ | 文本转语音 | GPT-Sovits-Inference |
| Fishaudio | ✔ | 文本转语音 | GPT-Sovits 作者参与的项目 |
| Edge-TTS | ✔ | 文本转语音 | Edge 浏览器的免费 TTS |
## ❤️ 贡献
欢迎任何 Issues/Pull Requests只需要将你的更改提交到此项目 )
### 如何贡献
你可以通过查看问题或帮助审核 PR拉取请求来贡献。任何问题或 PR 都欢迎参与,以促进社区贡献。当然,这些只是建议,你可以以任何方式进行贡献。对于新功能的添加,请先通过 Issue 讨论。
### 开发环境
AstrBot 使用 `ruff` 进行代码格式化和检查。
```bash
git clone https://github.com/Soulter/AstrBot
pip install pre-commit
pre-commit install
```
## 🌟 支持
- Star 这个项目!
- 在[爱发电](https://afdian.com/a/soulter)支持我!
- 在[微信](https://drive.soulter.top/f/pYfA/d903f4fa49a496fda3f16d2be9e023b5.png)支持我~
## ✨ Demo
<div align='center'>
<img src="https://github.com/user-attachments/assets/4ee688d9-467d-45c8-99d6-368f9a8a92d8" width="600">
_✨基于 Docker 的沙箱化代码执行器Beta 测试✨_
<img src="https://github.com/user-attachments/assets/0378f407-6079-4f64-ae4c-e97ab20611d2" height=500>
_✨ 多模态、网页搜索、长文本转图片(可配置) ✨_
<img src="https://github.com/user-attachments/assets/e137a9e1-340a-4bf2-bb2b-771132780735" height=150>
<img src="https://github.com/user-attachments/assets/480f5e82-cf6a-4955-a869-0d73137aa6e1" height=150>
_✨ 插件系统——部分插件展示 ✨_
<img src="https://github.com/user-attachments/assets/0cdbf564-2f59-4da5-b524-ce0e7ef3d978" width=600>
_✨ WebUI ✨_
</div>
## ❤️ Special Thanks
特别感谢所有 Contributors 和插件开发者对 AstrBot 的贡献 ❤️
<a href="https://github.com/AstrBotDevs/AstrBot/graphs/contributors">
<img src="https://contrib.rocks/image?repo=AstrBotDevs/AstrBot" />
</a>
## 🤔您可能想了解的
- **如何部署?** [帮助文档](https://github.com/Soulter/QQChannelChatGPT/wiki) (部署不成功欢迎进群捞人解决<3)
- **go-cqhttp启动不成功报登录失败** [在这里搜索解决方法](https://github.com/Mrs4s/go-cqhttp/issues)
- **程序闪退/机器人启动不成功** [提交issue或加群反馈](https://github.com/Soulter/QQChannelChatGPT/issues)
- **如何开启ChatGPTBardClaude等语言模型** [查看帮助](https://github.com/Soulter/QQChannelChatGPT/wiki/%E8%A1%A5%E5%85%85%EF%BC%9A%E5%A6%82%E4%BD%95%E5%BC%80%E5%90%AFChatGPT%E3%80%81Bard%E3%80%81Claude%E7%AD%89%E8%AF%AD%E8%A8%80%E6%A8%A1%E5%9E%8B%EF%BC%9F)
## ⭐ Star History
## 🧩功能:
> [!TIP]
> 如果本项目对您的生活 / 工作产生了帮助,或者您关注本项目的未来发展,请给项目 Star这是我维护这个开源项目的动力 <3
最近功能
1. 支持切换代码分支输入`/update checkout <分支名>`即可切换代码分支
2. 正在测试可视化面板输入`/update checkout dev_dashboard`后根据提示即可体验
<div align="center">
🌍支持的AI语言模型一览
[![Star History Chart](https://api.star-history.com/svg?repos=soulter/astrbot&type=Date)](https://star-history.com/#soulter/astrbot&Date)
**文字模型/图片理解**
</div>
- OpenAI GPT-3原生支持
- OpenAI GPT-3.5原生支持
- OpenAI GPT-4原生支持
- Claude免费[LLMs插件](https://github.com/Soulter/llms)支持
- HuggingChat免费[LLMs插件](https://github.com/Soulter/llms)支持
## Disclaimer
**图片生成**
1. The project is protected under the `AGPL-v3` opensource license.
2. The deployment of WeChat (personal account) utilizes [Gewechat](https://github.com/Devo919/Gewechat) service. AstrBot only guarantees connectivity with Gewechat and recommends using a WeChat account that is not frequently used. In the event of account risk control, the author of this project shall not bear any responsibility.
3. Please ensure compliance with local laws and regulations when using this project.
- NovelAI/Naifu (免费[AIDraw插件](https://github.com/Soulter/aidraw)支持)
_私は、高性能ですから!_
🌍机器人支持的能力一览
- 可视化面板beta
- 同时部署机器人到 QQ QQ 频道
- 大模型对话
- 大模型网页搜索能力 **(目前仅支持OpenAI系模型最新版本下使用 web on 指令打开)**
- 插件在QQ或QQ频道聊天框内输入 `plugin` 了解详情
- 回复文字图片渲染以图片markdown格式回复**大幅度降低被风控概率**需手动在`cmd_config.json`内开启qq_pic_mode
- 人格设置
- 关键词回复
- 热更新更新本项目时**仅需**在QQ或QQ频道聊天框内输入`update latest r`
- Windows一键部署 https://github.com/Soulter/QQChatGPTLauncher/releases/latest
<!--
### 基本功能
<details>
<summary>✅ 回复符合上下文</summary>
- 程序向API发送近多次对话内容模型根据上下文生成回复
- 你可在`configs/config.yaml`中修改`total_token_limit`来近似控制缓存大小。
</details>
<details>
<summary>✅ 超额自动切换</summary>
- 超额时程序自动切换openai的key方便快捷
</details>
<details>
<summary>✅ 支持统计频道、消息数量等信息</summary>
- 实现了简单的统计功能
</details>
<details>
<summary>✅ 多并发处理,回复速度快</summary>
- 使用了协程理论最高可以支持每个子频道每秒回复5条信息
</details>
<details>
<summary>✅ 持久化转储历史记录,重启不丢失</summary>
- 使用内置的sqlite数据库存储历史记录到本地
- 方式为定时转储,可在`config.yaml`下修改`dump_history_interval`来修改间隔时间,单位为分钟。
</details>
<details>
<summary>✅ 支持多种指令控制</summary>
- 详见下方`指令功能`
</details>
<details>
<summary>✅ 官方API稳定</summary>
- 不使用ChatGPT逆向接口而使用官方API接口稳定方便。
- QQ频道机器人框架为QQ官方开源的框架稳定。
</details> -->
<!-- > 关于tokentoken就相当于是AI中的单词数但是不等于单词数`text-davinci-003`模型中最大可以支持`4097`个token。在发送信息时这个机器人会将用户的历史聊天记录打包发送给ChatGPT因此`token`也会相应的累加为了保证聊天的上下文的逻辑性就有了缓存token。 -->
### 🛠️ 插件支持
本项目支持接入插件。
> 使用`plugin i 插件GitHub链接`即可安装。
插件开发教程https://github.com/Soulter/QQChannelChatGPT/wiki/%E5%9B%9B%E3%80%81%E5%BC%80%E5%8F%91%E6%8F%92%E4%BB%B6
部分插件:
- `LLMS`: https://github.com/Soulter/llms | Claude, HuggingChat 大语言模型接入。
- `GoodPlugins`: https://github.com/Soulter/goodplugins | 随机动漫图片、搜番、喜报生成器等等
- `sysstat`: https://github.com/Soulter/sysstatqcbot | 查看系统状态
- `BiliMonitor`: https://github.com/Soulter/BiliMonitor | 订阅B站动态
- `liferestart`: https://github.com/Soulter/liferestart | 人生重开模拟器
<img width="900" alt="image" src="https://github.com/Soulter/AstrBot/assets/37870767/824d1ff3-7b85-481c-b795-8e62dedb9fd7">
<!--
### 指令
#### OpenAI官方API
在频道内需要先`@`机器人之后再输入指令在QQ中暂时需要在消息前加上`ai `,不需要@
- `/reset`重置prompt
- `/his`查看历史记录(每个用户都有独立的会话)
- `/his [页码数]`查看不同页码的历史记录。例如`/his 2`查看第2页
- `/token`查看当前缓存的总token数
- `/count` 查看统计
- `/status` 查看chatGPT的配置
- `/help` 查看帮助
- `/key` 动态添加key
- `/set` 人格设置面板
- `/keyword nihao 你好` 设置关键词回复。nihao->你好
- `/bing` 切换为bing
- `/revgpt` 切换为ChatGPT逆向库
- `/画` 画画
#### 逆向ChatGPT库语言模型
- `/gpt` 切换为OpenAI官方API
- `/bing` 切换为bing
* 切换模型指令支持临时回复。如`/bing 你好`将会临时使用一次bing模型 -->
<!--
## 🙇‍感谢
本项目使用了一下项目:
[ChatGPT by acheong08](https://github.com/acheong08/ChatGPT)
[EdgeGPT by acheong08](https://github.com/acheong08/EdgeGPT)
[go-cqhttp by Mrs4s](https://github.com/Mrs4s/go-cqhttp)
[nakuru-project by Lxns-Network](https://github.com/Lxns-Network/nakuru-project) -->

182
README_en.md Normal file
View File

@@ -0,0 +1,182 @@
<p align="center">
![6e1279651f16d7fdf4727558b72bbaf1](https://github.com/user-attachments/assets/ead4c551-fc3c-48f7-a6f7-afbfdb820512)
</p>
<div align="center">
_✨ Easy-to-use Multi-platform LLM Chatbot & Development Framework ✨_
<a href="https://trendshift.io/repositories/12875" target="_blank"><img src="https://trendshift.io/api/badge/repositories/12875" alt="Soulter%2FAstrBot | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
[![GitHub release (latest by date)](https://img.shields.io/github/v/release/Soulter/AstrBot)](https://github.com/Soulter/AstrBot/releases/latest)
<img src="https://img.shields.io/badge/python-3.10+-blue.svg" alt="python">
<a href="https://hub.docker.com/r/soulter/astrbot"><img alt="Docker pull" src="https://img.shields.io/docker/pulls/soulter/astrbot.svg"/></a>
<a href="https://qm.qq.com/cgi-bin/qm/qr?k=wtbaNx7EioxeaqS9z7RQWVXPIxg2zYr7&jump_from=webapi&authKey=vlqnv/AV2DbJEvGIcxdlNSpfxVy+8vVqijgreRdnVKOaydpc+YSw4MctmEbr0k5"><img alt="Static Badge" src="https://img.shields.io/badge/QQ群-630166526-purple"></a>
[![wakatime](https://wakatime.com/badge/user/915e5316-99c6-4563-a483-ef186cf000c9/project/018e705a-a1a7-409a-a849-3013485e6c8e.svg)](https://wakatime.com/badge/user/915e5316-99c6-4563-a483-ef186cf000c9/project/018e705a-a1a7-409a-a849-3013485e6c8e)
![Dynamic JSON Badge](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fapi.soulter.top%2Fastrbot%2Fstats&query=v&label=7%E6%97%A5%E6%B6%88%E6%81%AF%E4%B8%8A%E8%A1%8C%E9%87%8F&cacheSeconds=3600)
[![codecov](https://codecov.io/gh/Soulter/AstrBot/graph/badge.svg?token=FF3P5967B8)](https://codecov.io/gh/Soulter/AstrBot)
<a href="https://astrbot.app/">Documentation</a>
<a href="https://github.com/Soulter/AstrBot/issues">Issue Tracking</a>
</div>
AstrBot is a loosely coupled, asynchronous chatbot and development framework that supports multi-platform deployment, featuring an easy-to-use plugin system and comprehensive Large Language Model (LLM) integration capabilities.
## ✨ Key Features
1. **LLM Conversations** - Supports various LLMs including OpenAI API, Google Gemini, Llama, Deepseek, ChatGLM, etc. Enables local model deployment via Ollama/LLMTuner. Features multi-turn dialogues, personality contexts, multimodal capabilities (image understanding), and speech-to-text (Whisper).
2. **Multi-platform Integration** - Supports QQ (OneBot), QQ Channels, WeChat (Gewechat), Feishu, and Telegram. Planned support for DingTalk, Discord, WhatsApp, and Xiaomi Smart Speakers. Includes rate limiting, whitelisting, keyword filtering, and Baidu content moderation.
3. **Agent Capabilities** - Native support for code execution, natural language TODO lists, web search. Integrates with [Dify Platform](https://dify.ai/) for easy access to Dify assistants/knowledge bases/workflows.
4. **Plugin System** - Optimized plugin mechanism with minimal development effort. Supports multiple installed plugins.
5. **Web Dashboard** - Visual configuration management, plugin controls, logging, and WebChat interface for direct LLM interaction.
6. **High Stability & Modularity** - Event bus and pipeline architecture ensures high modularization and loose coupling.
> [!TIP]
> Dashboard Demo: [https://demo.astrbot.app/](https://demo.astrbot.app/)
> Username: `astrbot`, Password: `astrbot` (LLM not configured for chat page)
## ✨ Deployment
#### Docker Deployment
See docs: [Deploy with Docker](https://astrbot.app/deploy/astrbot/docker.html#docker-deployment)
#### Windows Installer
Requires Python (>3.10). See docs: [Windows Installer Guide](https://astrbot.app/deploy/astrbot/windows.html)
#### Replit Deployment
[![Run on Repl.it](https://repl.it/badge/github/Soulter/AstrBot)](https://repl.it/github/Soulter/AstrBot)
#### CasaOS Deployment
Community-contributed method.
See docs: [CasaOS Deployment](https://astrbot.app/deploy/astrbot/casaos.html)
#### Manual Deployment
See docs: [Source Code Deployment](https://astrbot.app/deploy/astrbot/cli.html)
## ⚡ Platform Support
| Platform | Status | Details | Message Types |
| -------------------------------------------------------------- | ------ | ------------------- | ------------------- |
| QQ (Official Bot) | ✔ | Private/Group chats | Text, Images |
| QQ (OneBot) | ✔ | Private/Group chats | Text, Images, Voice |
| WeChat (Personal) | ✔ | Private/Group chats | Text, Images, Voice |
| [Telegram](https://github.com/Soulter/astrbot_plugin_telegram) | ✔ | Private/Group chats | Text, Images |
| [WeChat Work](https://github.com/Soulter/astrbot_plugin_wecom) | ✔ | Private chats | Text, Images, Voice |
| Feishu | ✔ | Group chats | Text, Images |
| WeChat Open Platform | 🚧 | Planned | - |
| Discord | 🚧 | Planned | - |
| WhatsApp | 🚧 | Planned | - |
| Xiaomi Speakers | 🚧 | Planned | - |
## Provider Support Status
| Name | Support | Type | Notes |
|---------------------------|---------|------------------------|-----------------------------------------------------------------------|
| OpenAI API | ✔ | Text Generation | Supports all OpenAI API-compatible services including DeepSeek, Google Gemini, GLM, Moonshot, Alibaba Cloud Bailian, Silicon Flow, xAI, etc. |
| Claude API | ✔ | Text Generation | |
| Google Gemini API | ✔ | Text Generation | |
| Dify | ✔ | LLMOps | |
| DashScope (Alibaba Cloud) | ✔ | LLMOps | |
| Ollama | ✔ | Model Loader | Local deployment for open-source LLMs (DeepSeek, Llama, etc.) |
| LM Studio | ✔ | Model Loader | Local deployment for open-source LLMs (DeepSeek, Llama, etc.) |
| LLMTuner | ✔ | Model Loader | Local loading of fine-tuned models (e.g. LoRA) |
| OneAPI | ✔ | LLM Distribution | |
| Whisper | ✔ | Speech-to-Text | Supports API and local deployment |
| SenseVoice | ✔ | Speech-to-Text | Local deployment |
| OpenAI TTS API | ✔ | Text-to-Speech | |
| Fishaudio | ✔ | Text-to-Speech | Project involving GPT-Sovits author |
# 🦌 Roadmap
> [!TIP]
> Suggestions welcome via Issues <3
- [ ] Ensure feature parity across all platform adapters
- [ ] Optimize plugin APIs
- [ ] Add default TTS services (e.g., GPT-Sovits)
- [ ] Enhance chat features with persistent memory
- [ ] i18n Planning
## ❤️ Contributions
All Issues/PRs welcome! Simply submit your changes to this project :)
For major features, please discuss via Issues first.
## 🌟 Support
- Star this project!
- Support via [Afdian](https://afdian.com/a/soulter)
- WeChat support: [QR Code](https://drive.soulter.top/f/pYfA/d903f4fa49a496fda3f16d2be9e023b5.png)
## ✨ Demos
> [!NOTE]
> Code executor file I/O currently tested with Napcat(QQ)/Lagrange(QQ)
<div align='center'>
<img src="https://github.com/user-attachments/assets/4ee688d9-467d-45c8-99d6-368f9a8a92d8" width="600">
_✨ Docker-based Sandboxed Code Executor (Beta) ✨_
<img src="https://github.com/user-attachments/assets/0378f407-6079-4f64-ae4c-e97ab20611d2" height=500>
_✨ Multimodal Input, Web Search, Text-to-Image ✨_
<img src="https://github.com/user-attachments/assets/8ec12797-e70f-460a-959e-48eca39ca2bb" height=100>
_✨ Natural Language TODO Lists ✨_
<img src="https://github.com/user-attachments/assets/e137a9e1-340a-4bf2-bb2b-771132780735" height=150>
<img src="https://github.com/user-attachments/assets/480f5e82-cf6a-4955-a869-0d73137aa6e1" height=150>
_✨ Plugin System Showcase ✨_
<img src="https://github.com/user-attachments/assets/592a8630-14c7-4e06-b496-9c0386e4f36c" width=600>
_✨ Web Dashboard ✨_
![webchat](https://drive.soulter.top/f/vlsA/ezgif-5-fb044b2542.gif)
_✨ Built-in Web Chat Interface ✨_
</div>
## ⭐ Star History
> [!TIP]
> If this project helps you, please give it a star <3
<div align="center">
[![Star History Chart](https://api.star-history.com/svg?repos=soulter/astrbot&type=Date)](https://star-history.com/#soulter/astrbot&Date)
</div>
## Disclaimer
1. Licensed under `AGPL-v3`.
2. WeChat integration uses [Gewechat](https://github.com/Devo919/Gewechat). Use at your own risk with non-critical accounts.
3. Users must comply with local laws and regulations.
<!-- ## ✨ ATRI [Beta]
Available as plugin: [astrbot_plugin_atri](https://github.com/Soulter/astrbot_plugin_atri)
1. Qwen1.5-7B-Chat Lora model fine-tuned with ATRI character data
2. Long-term memory
3. Meme understanding & responses
4. TTS integration
-->
_私は、高性能ですから!_

170
README_ja.md Normal file
View File

@@ -0,0 +1,170 @@
<p align="center">
![6e1279651f16d7fdf4727558b72bbaf1](https://github.com/user-attachments/assets/ead4c551-fc3c-48f7-a6f7-afbfdb820512)
</p>
<div align="center">
_✨ 簡単に使えるマルチプラットフォーム LLM チャットボットおよび開発フレームワーク ✨_
<a href="https://trendshift.io/repositories/12875" target="_blank"><img src="https://trendshift.io/api/badge/repositories/12875" alt="Soulter%2FAstrBot | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
[![GitHub release (latest by date)](https://img.shields.io/github/v/release/Soulter/AstrBot)](https://github.com/Soulter/AstrBot/releases/latest)
<img src="https://img.shields.io/badge/python-3.10+-blue.svg" alt="python">
<a href="https://hub.docker.com/r/soulter/astrbot"><img alt="Docker pull" src="https://img.shields.io/docker/pulls/soulter/astrbot.svg"/></a>
<img alt="Static Badge" src="https://img.shields.io/badge/QQ群-630166526-purple">
[![wakatime](https://wakatime.com/badge/user/915e5316-99c6-4563-a483-ef186cf000c9/project/018e705a-a1a7-409a-a849-3013485e6c8e.svg)](https://wakatime.com/badge/user/915e5316-99c6-4563-a483-ef186cf000c9/project/018e705a-a1a7-409a-a849-3013485e6c8e)
![Dynamic JSON Badge](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fapi.soulter.top%2Fastrbot%2Fstats&query=v&label=7%E6%97%A5%E6%B6%88%E6%81%AF%E4%B8%8A%E8%A1%8C%E9%87%8F&cacheSeconds=3600)
[![codecov](https://codecov.io/gh/Soulter/AstrBot/graph/badge.svg?token=FF3P5967B8)](https://codecov.io/gh/Soulter/AstrBot)
<a href="https://astrbot.app/">ドキュメントを見る</a>
<a href="https://github.com/Soulter/AstrBot/issues">問題を報告する</a>
</div>
AstrBot は、疎結合、非同期、複数のメッセージプラットフォームに対応したデプロイ、使いやすいプラグインシステム、および包括的な大規模言語モデルLLM接続機能を備えたチャットボットおよび開発フレームワークです。
## ✨ 主な機能
1. **大規模言語モデルの対話**。OpenAI API、Google Gemini、Llama、Deepseek、ChatGLM など、さまざまな大規模言語モデルをサポートし、Ollama、LLMTuner を介してローカルにデプロイされた大規模モデルをサポートします。多輪対話、人格シナリオ、多モーダル機能を備え、画像理解、音声からテキストへの変換Whisperをサポートします。
2. **複数のメッセージプラットフォームの接続**。QQOneBot、QQ チャンネル、WeChatGewechat、Feishu、Telegram への接続をサポートします。今後、DingTalk、Discord、WhatsApp、Xiaoai 音響をサポートする予定です。レート制限、ホワイトリスト、キーワードフィルタリング、Baidu コンテンツ監査をサポートします。
3. **エージェント**。一部のエージェント機能をネイティブにサポートし、コードエグゼキューター、自然言語タスク、ウェブ検索などを提供します。[Dify プラットフォーム](https://dify.ai/)と連携し、Dify スマートアシスタント、ナレッジベース、Dify ワークフローを簡単に接続できます。
4. **プラグインの拡張**。深く最適化されたプラグインメカニズムを備え、[プラグインの開発](https://astrbot.app/dev/plugin.html)をサポートし、機能を拡張できます。複数のプラグインのインストールをサポートします。
5. **ビジュアル管理パネル**。設定の視覚的な変更、プラグイン管理、ログの表示などをサポートし、設定の難易度を低減します。WebChat を統合し、パネル上で大規模モデルと対話できます。
6. **高い安定性と高いモジュール性**。イベントバスとパイプラインに基づくアーキテクチャ設計により、高度にモジュール化され、低結合です。
> [!TIP]
> 管理パネルのオンラインデモを体験する: [https://demo.astrbot.app/](https://demo.astrbot.app/)
>
> ユーザー名: `astrbot`, パスワード: `astrbot`。LLM が設定されていないため、チャットページで大規模モデルを使用することはできません。(デモのログインパスワードを変更しないでください 😭)
## ✨ 使用方法
#### Docker デプロイ
公式ドキュメント [Docker を使用して AstrBot をデプロイする](https://astrbot.app/deploy/astrbot/docker.html#%E4%BD%BF%E7%94%A8-docker-%E9%83%A8%E7%BD%B2-astrbot) を参照してください。
#### Windows ワンクリックインストーラーのデプロイ
コンピュータに Python>3.10)がインストールされている必要があります。公式ドキュメント [Windows ワンクリックインストーラーを使用して AstrBot をデプロイする](https://astrbot.app/deploy/astrbot/windows.html) を参照してください。
#### Replit デプロイ
[![Run on Repl.it](https://repl.it/badge/github/Soulter/AstrBot)](https://repl.it/github/Soulter/AstrBot)
#### CasaOS デプロイ
コミュニティが提供するデプロイ方法です。
公式ドキュメント [ソースコードを使用して AstrBot をデプロイする](https://astrbot.app/deploy/astrbot/casaos.html) を参照してください。
#### 手動デプロイ
公式ドキュメント [ソースコードを使用して AstrBot をデプロイする](https://astrbot.app/deploy/astrbot/cli.html) を参照してください。
## ⚡ メッセージプラットフォームのサポート状況
| プラットフォーム | サポート状況 | 詳細 | メッセージタイプ |
| -------- | ------- | ------- | ------ |
| QQ(公式ロボットインターフェース) | ✔ | プライベートチャット、グループチャット、QQ チャンネルプライベートチャット、グループチャット | テキスト、画像 |
| QQ(OneBot) | ✔ | プライベートチャット、グループチャット | テキスト、画像、音声 |
| WeChat(個人アカウント) | ✔ | WeChat 個人アカウントのプライベートチャット、グループチャット | テキスト、画像、音声 |
| [Telegram](https://github.com/Soulter/astrbot_plugin_telegram) | ✔ | プライベートチャット、グループチャット | テキスト、画像 |
| [WeChat(企業 WeChat)](https://github.com/Soulter/astrbot_plugin_wecom) | ✔ | プライベートチャット | テキスト、画像、音声 |
| Feishu | ✔ | グループチャット | テキスト、画像 |
| WeChat 対話オープンプラットフォーム | 🚧 | 計画中 | - |
| Discord | 🚧 | 計画中 | - |
| WhatsApp | 🚧 | 計画中 | - |
| Xiaoai 音響 | 🚧 | 計画中 | - |
# 🦌 今後のロードマップ
> [!TIP]
> Issue でさらに多くの提案を歓迎します <3
- [ ] 現在のすべてのプラットフォームアダプターの機能の一貫性を確保し、改善する
- [ ] プラグインインターフェースの最適化
- [ ] GPT-Sovits などの TTS サービスをデフォルトでサポート
- [ ] "チャット強化" 部分を完成させ、永続的な記憶をサポート
- [ ] i18n の計画
## ❤️ 貢献
Issue や Pull Request を歓迎します!このプロジェクトに変更を加えるだけです :)
新機能の追加については、まず Issue で議論してください。
## 🌟 サポート
- このプロジェクトに Star を付けてください!
- [愛発電](https://afdian.com/a/soulter)で私をサポートしてください!
- [WeChat](https://drive.soulter.top/f/pYfA/d903f4fa49a496fda3f16d2be9e023b5.png)で私をサポートしてください~
## ✨ デモ
> [!NOTE]
> コードエグゼキューターのファイル入力/出力は現在 Napcat(QQ)、Lagrange(QQ) でのみテストされています
<div align='center'>
<img src="https://github.com/user-attachments/assets/4ee688d9-467d-45c8-99d6-368f9a8a92d8" width="600">
_✨ Docker ベースのサンドボックス化されたコードエグゼキューターベータテスト中✨_
<img src="https://github.com/user-attachments/assets/0378f407-6079-4f64-ae4c-e97ab20611d2" height=500>
_✨ 多モーダル、ウェブ検索、長文の画像変換設定可能✨_
<img src="https://github.com/user-attachments/assets/8ec12797-e70f-460a-959e-48eca39ca2bb" height=100>
_✨ 自然言語タスク ✨_
<img src="https://github.com/user-attachments/assets/e137a9e1-340a-4bf2-bb2b-771132780735" height=150>
<img src="https://github.com/user-attachments/assets/480f5e82-cf6a-4955-a869-0d73137aa6e1" height=150>
_✨ プラグインシステム - 一部のプラグインの展示 ✨_
<img src="https://github.com/user-attachments/assets/592a8630-14c7-4e06-b496-9c0386e4f36c" width="600">
_✨ 管理パネル ✨_
![webchat](https://drive.soulter.top/f/vlsA/ezgif-5-fb044b2542.gif)
_✨ 内蔵 Web Chat、オンラインでボットと対話 ✨_
</div>
## ⭐ Star History
> [!TIP]
> このプロジェクトがあなたの生活や仕事に役立った場合、またはこのプロジェクトの将来の発展に関心がある場合は、プロジェクトに Star を付けてください。これはこのオープンソースプロジェクトを維持するためのモチベーションです <3
<div align="center">
[![Star History Chart](https://api.star-history.com/svg?repos=soulter/astrbot&type=Date)](https://star-history.com/#soulter/astrbot&Date)
</div>
## スポンサー
[<img src="https://api.gitsponsors.com/api/badge/img?id=575865240" height="20">](https://api.gitsponsors.com/api/badge/link?p=XEpbdGxlitw/RbcwiTX93UMzNK/jgDYC8NiSzamIPMoKvG2lBFmyXhSS/b0hFoWlBBMX2L5X5CxTDsUdyvcIEHTOfnkXz47UNOZvMwyt5CzbYpq0SEzsSV1OJF1cCo90qC/ZyYKYOWedal3MhZ3ikw==)
## 免責事項
1. このプロジェクトは `AGPL-v3` オープンソースライセンスの下で保護されています。
2. WeChat個人アカウントのデプロイメントには [Gewechat](https://github.com/Devo919/Gewechat) サービスを利用しています。AstrBot は Gewechat との接続を保証するだけであり、アカウントのリスク管理に関しては、このプロジェクトの著者は一切の責任を負いません。
3. このプロジェクトを使用する際は、現地の法律および規制を遵守してください。
<!-- ## ✨ ATRI [ベータテスト]
この機能はプラグインとしてロードされます。プラグインリポジトリのアドレス:[astrbot_plugin_atri](https://github.com/Soulter/astrbot_plugin_atri)
1. 《ATRI ~ My Dear Moments》の主人公 ATRI のキャラクターセリフを微調整データセットとして使用した `Qwen1.5-7B-Chat Lora` 微調整モデル。
2. 長期記憶
3. ミームの理解と返信
4. TTS
-->
_私は、高性能ですから!_

View File

@@ -1,26 +0,0 @@
from aip import AipContentCensor
class BaiduJudge:
def __init__(self, baidu_configs) -> None:
if 'app_id' in baidu_configs and 'api_key' in baidu_configs and 'secret_key' in baidu_configs:
self.app_id = str(baidu_configs['app_id'])
self.api_key = baidu_configs['api_key']
self.secret_key = baidu_configs['secret_key']
self.client = AipContentCensor(self.app_id, self.api_key, self.secret_key)
else:
raise ValueError("Baidu configs error! 请填写百度内容审核服务相关配置!")
def judge(self, text):
res = self.client.textCensorUserDefined(text)
if 'conclusionType' not in res:
return False, "百度审核服务未知错误"
if res['conclusionType'] == 1:
return True, "合规"
else:
if 'data' not in res:
return False, "百度审核服务未知错误"
count = len(res['data'])
info = f"百度审核服务发现 {count} 处违规:\n"
for i in res['data']:
info += f"{i['msg']}\n"
info += "\n判断结果:"+res['conclusion']
return False, info

View File

@@ -1 +0,0 @@
.page-breadcrumb .v-toolbar{background:transparent}

View File

@@ -1 +0,0 @@
import{x as i,o as l,c as _,w as s,a as e,f as a,J as m,V as r,b as t,t as u,ab as p,B as n,ac as o,j as f}from"./index-7c8bc001.js";const b={class:"text-h3"},h={class:"d-flex align-center"},g={class:"d-flex align-center"},V=i({__name:"BaseBreadcrumb",props:{title:String,breadcrumbs:Array,icon:String},setup(d){const c=d;return(x,B)=>(l(),_(r,{class:"page-breadcrumb mb-1 mt-1"},{default:s(()=>[e(a,{cols:"12",md:"12"},{default:s(()=>[e(m,{variant:"outlined",elevation:"0",class:"px-4 py-3 withbg"},{default:s(()=>[e(r,{"no-gutters":"",class:"align-center"},{default:s(()=>[e(a,{md:"5"},{default:s(()=>[t("h3",b,u(c.title),1)]),_:1}),e(a,{md:"7",sm:"12",cols:"12"},{default:s(()=>[e(p,{items:c.breadcrumbs,class:"text-h5 justify-md-end pa-1"},{divider:s(()=>[t("div",h,[e(n(o),{size:"17"})])]),prepend:s(()=>[e(f,{size:"small",icon:"mdi-home",class:"text-secondary mr-2"}),t("div",g,[e(n(o),{size:"17"})])]),_:1},8,["items"])]),_:1})]),_:1})]),_:1})]),_:1})]),_:1}))}});export{V as _};

View File

@@ -1 +0,0 @@
import{x as e,o as a,c as t,w as o,a as s,B as n,X as r,T as c}from"./index-7c8bc001.js";const f=e({__name:"BlankLayout",setup(p){return(u,_)=>(a(),t(c,null,{default:o(()=>[s(n(r))]),_:1}))}});export{f as default};

View File

@@ -1 +0,0 @@
import{_ as m}from"./BaseBreadcrumb.vue_vue_type_style_index_0_lang-89ca5198.js";import{_}from"./UiParentCard.vue_vue_type_script_setup_true_lang-03a5c441.js";import{x as p,D as a,o as r,s,a as e,w as t,f as o,V as i,F as n,u as g,c as h,_ as b,e as x,t as y}from"./index-7c8bc001.js";const P=p({__name:"ColorPage",setup(C){const c=a({title:"Colors Page"}),d=a([{title:"Utilities",disabled:!1,href:"#"},{title:"Colors",disabled:!0,href:"#"}]),u=a(["primary","lightprimary","secondary","lightsecondary","info","success","accent","warning","error","darkText","lightText","borderLight","inputBorder","containerBg"]);return(V,k)=>(r(),s(n,null,[e(m,{title:c.value.title,breadcrumbs:d.value},null,8,["title","breadcrumbs"]),e(i,null,{default:t(()=>[e(o,{cols:"12",md:"12"},{default:t(()=>[e(_,{title:"Color Palette"},{default:t(()=>[e(i,null,{default:t(()=>[(r(!0),s(n,null,g(u.value,(l,f)=>(r(),h(o,{md:"3",cols:"12",key:f},{default:t(()=>[e(b,{rounded:"md",class:"align-center justify-center d-flex",height:"100",width:"100%",color:l},{default:t(()=>[x("class: "+y(l),1)]),_:2},1032,["color"])]),_:2},1024))),128))]),_:1})]),_:1})]),_:1})]),_:1})],64))}});export{P as default};

View File

@@ -1 +0,0 @@
import{_ as h}from"./UiParentCard.vue_vue_type_script_setup_true_lang-03a5c441.js";import{o as a,s as t,a as n,w as i,f as b,F as d,u as g,V as C,d as U,e as x,t as c,a8 as B,R as _,c as r,a9 as w,O as v,b as V,aa as N,i as F,q as P,k as f,A as S}from"./index-7c8bc001.js";const D={name:"ConfigPage",components:{UiParentCard:h},data(){return{config_data:{data:[]},save_message_snack:!1,save_message:"",save_message_success:""}},mounted(){this.getConfig()},methods:{getConfig(){_.get("/api/configs").then(o=>{this.config_data=o.data.data,console.log(this.config_data)})},updateConfig(){_.post("/api/configs",this.config_data).then(o=>{console.log(this.config_data),o.data.status==="success"?(this.save_message=o.data.message,this.save_message_snack=!0,this.save_message_success="success"):(this.save_message=o.data.message,this.save_message_snack=!0,this.save_message_success="error")})}}},$=Object.assign(D,{setup(o){return(s,m)=>(a(),t(d,null,[n(C,null,{default:i(()=>[n(b,{cols:"12",md:"12"},{default:i(()=>[(a(!0),t(d,null,g(s.config_data.data,u=>(a(),r(h,{key:u.name,title:u.name,style:{"margin-bottom":"16px"}},{default:i(()=>[(a(!0),t(d,null,g(u.body,e=>(a(),t(d,null,[e.config_type==="item"?(a(),t(d,{key:0},[e.val_type==="bool"?(a(),r(w,{key:0,modelValue:e.value,"onUpdate:modelValue":l=>e.value=l,label:e.name,hint:e.description,color:"primary",inset:""},null,8,["modelValue","onUpdate:modelValue","label","hint"])):e.val_type==="string"?(a(),r(v,{key:1,modelValue:e.value,"onUpdate:modelValue":l=>e.value=l,label:e.name,hint:e.description,style:{"margin-bottom":"8px"},variant:"outlined"},null,8,["modelValue","onUpdate:modelValue","label","hint"])):e.val_type==="int"?(a(),r(v,{key:2,modelValue:e.value,"onUpdate:modelValue":l=>e.value=l,label:e.name,hint:e.description,style:{"margin-bottom":"8px"},variant:"outlined"},null,8,["modelValue","onUpdate:modelValue","label","hint"])):e.val_type==="list"?(a(),t(d,{key:3},[V("span",null,c(e.name),1),n(N,{modelValue:e.value,"onUpdate:modelValue":l=>e.value=l,chips:"",clearable:"",label:"请添加",multiple:"","prepend-icon":"mdi-tag-multiple-outline"},{selection:i(({attrs:l,item:p,select:k,selected:y})=>[n(F,P(l,{"model-value":y,closable:"",onClick:k,"onClick:close":O=>s.remove(p)}),{default:i(()=>[V("strong",null,c(p),1)]),_:2},1040,["model-value","onClick","onClick:close"])]),_:2},1032,["modelValue","onUpdate:modelValue"])],64)):f("",!0)],64)):e.config_type==="divider"?(a(),r(S,{key:1,style:{"margin-top":"8px","margin-bottom":"8px"}})):f("",!0)],64))),256))]),_:2},1032,["title"]))),128))]),_:1})]),_:1}),n(U,{icon:"mdi-content-save",size:"x-large",style:{position:"fixed",right:"52px",bottom:"52px"},color:"darkprimary",onClick:s.updateConfig},null,8,["onClick"]),n(B,{timeout:2e3,elevation:"24",color:s.save_message_success,modelValue:s.save_message_snack,"onUpdate:modelValue":m[0]||(m[0]=u=>s.save_message_snack=u)},{default:i(()=>[x(c(s.save_message),1)]),_:1},8,["color","modelValue"])],64))}});export{$ as default};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
.CardMediaWrapper{max-width:720px;margin:0 auto;position:relative}.CardMediaBuild{position:absolute;top:0;left:0;width:100%;animation:5s bounce ease-in-out infinite}.CardMediaParts{position:absolute;top:0;left:0;width:100%;animation:10s blink ease-in-out infinite}

View File

@@ -1 +0,0 @@
import{_ as t}from"./_plugin-vue_export-helper-c27b6911.js";import{o,c,w as s,V as i,a as r,b as e,d as l,e as a,f as d}from"./index-7c8bc001.js";const n="/assets/img-error-bg-ab6474a0.svg",_="/assets/img-error-blue-2675a7a9.svg",m="/assets/img-error-text-a6aebfa0.svg",g="/assets/img-error-purple-edee3fbc.svg";const p={},u={class:"text-center"},f=e("div",{class:"CardMediaWrapper"},[e("img",{src:n,alt:"grid",class:"w-100"}),e("img",{src:_,alt:"grid",class:"CardMediaParts"}),e("img",{src:m,alt:"build",class:"CardMediaBuild"}),e("img",{src:g,alt:"build",class:"CardMediaBuild"})],-1),h=e("h1",{class:"text-h1"},"Something is wrong",-1),v=e("p",null,[e("small",null,[a("The page you are looking was moved, removed, "),e("br"),a("renamed, or might never exist! ")])],-1);function x(b,V){return o(),c(i,{"no-gutters":"",class:"h-100vh"},{default:s(()=>[r(d,{class:"d-flex align-center justify-center"},{default:s(()=>[e("div",u,[f,h,v,r(l,{variant:"flat",color:"primary",class:"mt-4",to:"/","prepend-icon":"mdi-home"},{default:s(()=>[a(" Home")]),_:1})])]),_:1})]),_:1})}const C=t(p,[["render",x]]);export{C as default};

View File

@@ -1 +0,0 @@
import{x as b,o as d,c as h,w as e,a,a6 as C,b as i,K as x,e as o,t as u,G as m,d as r,A as E,L as V,a7 as y,J as w,s as p,f as c,F as f,u as $,V as k,q as S,N as B,O as N,P as T,H as j,a8 as D,R as g,j as F}from"./index-7c8bc001.js";const G={class:"d-sm-flex align-center justify-space-between"},v=b({__name:"ExtensionCard",props:{title:String,link:String},setup(n){const s=n,l=t=>{window.open(t,"_blank")};return(t,_)=>(d(),h(w,{variant:"outlined",elevation:"0",class:"withbg"},{default:e(()=>[a(C,{style:{padding:"10px 20px"}},{default:e(()=>[i("div",G,[a(x,null,{default:e(()=>[o(u(s.title),1)]),_:1}),a(m),a(r,{icon:"mdi-link",variant:"plain",onClick:_[0]||(_[0]=z=>l(s.link))})])]),_:1}),a(E),a(V,null,{default:e(()=>[y(t.$slots,"default")]),_:3})]),_:3}))}}),P=i("div",{style:{"background-color":"white",width:"100%",padding:"16px","border-radius":"10px"}},[i("h3",null,"🧩 已安装的插件")],-1),U={style:{"min-height":"180px","max-height":"180px",overflow:"hidden"}},q={class:"d-flex align-center gap-3"},A=i("div",{style:{"background-color":"white",width:"100%",padding:"16px","border-radius":"10px"}},[i("h3",null,"🧩 插件市场 [待开发]")],-1),I=i("span",{class:"text-h5"},"从 Git 仓库链接安装插件",-1),L=i("small",null,"github, gitee, gitlab 等公开的仓库都行。",-1),O=i("br",null,null,-1),R={name:"ExtensionPage",components:{ExtensionCard:v},data(){return{extension_data:{data:[]},save_message_snack:!1,save_message:"",save_message_success:"",extension_url:"",status:"",dialog:!1,snack_message:"",snack_show:!1,snack_success:"success",install_loading:!1,uninstall_loading:!1}},mounted(){this.getExtensions()},methods:{getExtensions(){g.get("/api/extensions").then(n=>{this.extension_data.data=n.data.data,console.log(this.extension_data)})},newExtension(){this.install_loading=!0,console.log(this.install_loading),g.post("/api/extensions/install",{url:this.extension_url}).then(n=>{if(this.install_loading=!1,n.data.status==="error"){this.snack_message=n.data.message,this.snack_show=!0,this.snack_success="error";return}this.extension_data.data=n.data.data,console.log(this.extension_data),this.extension_url="",this.snack_message=n.data.message,this.snack_show=!0,this.snack_success="success",this.dialog=!1,this.getExtensions()}).catch(n=>{this.install_loading=!1,this.snack_message=n,this.snack_show=!0,this.snack_success="error"})},uninstallExtension(n){this.uninstall_loading=!0,g.post("/api/extensions/uninstall",{name:n}).then(s=>{if(this.uninstall_loading=!1,s.data.status==="error"){this.snack_message=s.data.message,this.snack_show=!0,this.snack_success="error";return}this.extension_data.data=s.data.data,console.log(this.extension_data),this.snack_message=s.data.message,this.snack_show=!0,this.snack_success="success",this.dialog=!1,this.getExtensions()}).catch(s=>{this.uninstall_loading=!1,this.snack_message=s,this.snack_show=!0,this.snack_success="error"})}}},J=Object.assign(R,{setup(n){return(s,l)=>(d(),p(f,null,[a(k,null,{default:e(()=>[a(c,{cols:"12",md:"12"},{default:e(()=>[P]),_:1}),(d(!0),p(f,null,$(s.extension_data.data,t=>(d(),h(c,{cols:"12",md:"6",lg:"4"},{default:e(()=>[(d(),h(v,{key:t.name,title:t.name,link:t.repo,style:{"margin-bottom":"16px"}},{default:e(()=>[i("p",U,u(t.desc),1),i("div",q,[a(F,null,{default:e(()=>[o("mdi-account")]),_:1}),i("span",null,u(t.author),1),a(m),a(r,{variant:"plain",onClick:_=>s.uninstallExtension(t.name),loading:s.uninstall_loading},{default:e(()=>[o("卸 载")]),_:2},1032,["onClick","loading"])])]),_:2},1032,["title","link"]))]),_:2},1024))),256)),a(c,{cols:"12",md:"12"},{default:e(()=>[A]),_:1})]),_:1}),a(j,{modelValue:s.dialog,"onUpdate:modelValue":l[3]||(l[3]=t=>s.dialog=t),persistent:"",width:"700"},{activator:e(({props:t})=>[a(r,S(t,{icon:"mdi-plus",size:"x-large",style:{position:"fixed",right:"52px",bottom:"52px"},color:"darkprimary"}),null,16)]),default:e(()=>[a(w,null,{default:e(()=>[a(x,null,{default:e(()=>[I]),_:1}),a(V,null,{default:e(()=>[a(B,null,{default:e(()=>[a(k,null,{default:e(()=>[a(c,{cols:"12"},{default:e(()=>[a(N,{label:"Git 库链接",modelValue:s.extension_url,"onUpdate:modelValue":l[0]||(l[0]=t=>s.extension_url=t),required:""},null,8,["modelValue"])]),_:1})]),_:1})]),_:1}),L,O,i("small",null,u(s.status),1)]),_:1}),a(T,null,{default:e(()=>[a(m),a(r,{color:"blue-darken-1",variant:"text",onClick:l[1]||(l[1]=t=>s.dialog=!1)},{default:e(()=>[o(" 关闭 ")]),_:1}),a(r,{color:"blue-darken-1",variant:"text",loading:s.install_loading,onClick:l[2]||(l[2]=t=>s.newExtension(s.extension_url))},{default:e(()=>[o(" 安装 ")]),_:1},8,["loading"])]),_:1})]),_:1})]),_:1},8,["modelValue"]),a(D,{timeout:2e3,elevation:"24",color:s.snack_success,modelValue:s.snack_show,"onUpdate:modelValue":l[4]||(l[4]=t=>s.snack_show=t)},{default:e(()=>[o(u(s.snack_message),1)]),_:1},8,["color","modelValue"])],64))}});export{J as default};

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
.custom-devider{border-color:#00000014!important}.googleBtn{border-color:#00000014;margin:30px 0 20px}.outlinedInput .v-field{border:1px solid rgba(0,0,0,.08);box-shadow:none}.orbtn{padding:2px 40px;border-color:#00000014;margin:20px 15px}.pwdInput{position:relative}.pwdInput .v-input__append{position:absolute;right:10px;top:50%;transform:translateY(-50%)}.loginForm .v-text-field .v-field--active input{font-weight:500}.loginBox{max-width:475px;margin:0 auto}

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
import{at as _,x as d,D as n,o as c,s as m,a as f,w as p,au as r,b as a,av as o,B as t,aw as h}from"./index-7c8bc001.js";const s={Sidebar_drawer:!0,Customizer_drawer:!1,mini_sidebar:!1,fontTheme:"Roboto",inputBg:!1},l=_({id:"customizer",state:()=>({Sidebar_drawer:s.Sidebar_drawer,Customizer_drawer:s.Customizer_drawer,mini_sidebar:s.mini_sidebar,fontTheme:"Poppins",inputBg:s.inputBg}),getters:{},actions:{SET_SIDEBAR_DRAWER(){this.Sidebar_drawer=!this.Sidebar_drawer},SET_MINI_SIDEBAR(e){this.mini_sidebar=e},SET_FONT(e){this.fontTheme=e}}}),u={class:"logo",style:{display:"flex","align-items":"center"}},b={style:{"font-size":"24px","font-weight":"1000"}},w={style:{"font-size":"20px","font-weight":"1000"}},S={style:{"font-size":"20px"}},z=d({__name:"LogoDark",setup(e){n("rgb(var(--v-theme-primary))"),n("rgb(var(--v-theme-secondary))");const i=l();return(g,B)=>(c(),m("div",u,[f(t(h),{to:"/",style:{"text-decoration":"none",color:"black"}},{default:p(()=>[r(a("span",b,"AstrBot 仪表盘",512),[[o,!t(i).mini_sidebar]]),r(a("span",w,"Astr",512),[[o,t(i).mini_sidebar]]),r(a("span",S,"Bot",512),[[o,t(i).mini_sidebar]])]),_:1})]))}});export{z as _,l as u};

View File

@@ -1 +0,0 @@
import{_ as o}from"./BaseBreadcrumb.vue_vue_type_style_index_0_lang-89ca5198.js";import{_ as i}from"./UiParentCard.vue_vue_type_script_setup_true_lang-03a5c441.js";import{x as n,D as a,o as c,s as m,a as e,w as t,f as d,b as f,V as _,F as u}from"./index-7c8bc001.js";const p=["innerHTML"],v=n({__name:"MaterialIcons",setup(b){const s=a({title:"Material Icons"}),r=a('<iframe src="https://materialdesignicons.com/" frameborder="0" width="100%" height="1000"></iframe>'),l=a([{title:"Icons",disabled:!1,href:"#"},{title:"Material Icons",disabled:!0,href:"#"}]);return(h,M)=>(c(),m(u,null,[e(o,{title:s.value.title,breadcrumbs:l.value},null,8,["title","breadcrumbs"]),e(_,null,{default:t(()=>[e(d,{cols:"12",md:"12"},{default:t(()=>[e(i,{title:"Material Icons"},{default:t(()=>[f("div",{innerHTML:r.value},null,8,p)]),_:1})]),_:1})]),_:1})],64))}});export{v as default};

View File

@@ -1 +0,0 @@
import{_ as B}from"./LogoDark.vue_vue_type_script_setup_true_lang-4faa128a.js";import{x as y,D as o,o as b,s as U,a as e,w as a,b as n,B as $,d as u,f as d,A as _,e as f,V as r,O as m,an as A,as as E,F,c as T,N as q,J as V,L as P}from"./index-7c8bc001.js";const z="/assets/social-google-a359a253.svg",N=["src"],S=n("span",{class:"ml-2"},"Sign up with Google",-1),D=n("h5",{class:"text-h5 text-center my-4 mb-8"},"Sign up with Email address",-1),G={class:"d-sm-inline-flex align-center mt-2 mb-7 mb-sm-0 font-weight-bold"},L=n("a",{href:"#",class:"ml-1 text-lightText"},"Terms and Condition",-1),O={class:"mt-5 text-right"},j=y({__name:"AuthRegister",setup(w){const c=o(!1),i=o(!1),p=o(""),v=o(""),g=o(),h=o(""),x=o(""),k=o([s=>!!s||"Password is required",s=>s&&s.length<=10||"Password must be less than 10 characters"]),C=o([s=>!!s||"E-mail is required",s=>/.+@.+\..+/.test(s)||"E-mail must be valid"]);function R(){g.value.validate()}return(s,l)=>(b(),U(F,null,[e(u,{block:"",color:"primary",variant:"outlined",class:"text-lightText googleBtn"},{default:a(()=>[n("img",{src:$(z),alt:"google"},null,8,N),S]),_:1}),e(r,null,{default:a(()=>[e(d,{class:"d-flex align-center"},{default:a(()=>[e(_,{class:"custom-devider"}),e(u,{variant:"outlined",class:"orbtn",rounded:"md",size:"small"},{default:a(()=>[f("OR")]),_:1}),e(_,{class:"custom-devider"})]),_:1})]),_:1}),D,e(E,{ref_key:"Regform",ref:g,"lazy-validation":"",action:"/dashboards/analytical",class:"mt-7 loginForm"},{default:a(()=>[e(r,null,{default:a(()=>[e(d,{cols:"12",sm:"6"},{default:a(()=>[e(m,{modelValue:h.value,"onUpdate:modelValue":l[0]||(l[0]=t=>h.value=t),density:"comfortable","hide-details":"auto",variant:"outlined",color:"primary",label:"Firstname"},null,8,["modelValue"])]),_:1}),e(d,{cols:"12",sm:"6"},{default:a(()=>[e(m,{modelValue:x.value,"onUpdate:modelValue":l[1]||(l[1]=t=>x.value=t),density:"comfortable","hide-details":"auto",variant:"outlined",color:"primary",label:"Lastname"},null,8,["modelValue"])]),_:1})]),_:1}),e(m,{modelValue:v.value,"onUpdate:modelValue":l[2]||(l[2]=t=>v.value=t),rules:C.value,label:"Email Address / Username",class:"mt-4 mb-4",required:"",density:"comfortable","hide-details":"auto",variant:"outlined",color:"primary"},null,8,["modelValue","rules"]),e(m,{modelValue:p.value,"onUpdate:modelValue":l[3]||(l[3]=t=>p.value=t),rules:k.value,label:"Password",required:"",density:"comfortable",variant:"outlined",color:"primary","hide-details":"auto","append-icon":i.value?"mdi-eye":"mdi-eye-off",type:i.value?"text":"password","onClick:append":l[4]||(l[4]=t=>i.value=!i.value),class:"pwdInput"},null,8,["modelValue","rules","append-icon","type"]),n("div",G,[e(A,{modelValue:c.value,"onUpdate:modelValue":l[5]||(l[5]=t=>c.value=t),rules:[t=>!!t||"You must agree to continue!"],label:"Agree with?",required:"",color:"primary",class:"ms-n2","hide-details":""},null,8,["modelValue","rules"]),L]),e(u,{color:"secondary",block:"",class:"mt-2",variant:"flat",size:"large",onClick:l[6]||(l[6]=t=>R())},{default:a(()=>[f("Sign Up")]),_:1})]),_:1},512),n("div",O,[e(_),e(u,{variant:"plain",to:"/auth/login",class:"mt-2 text-capitalize mr-n2"},{default:a(()=>[f("Already have an account?")]),_:1})])],64))}});const I={class:"pa-7 pa-sm-12"},J=n("h2",{class:"text-secondary text-h2 mt-8"},"Sign up",-1),Y=n("h4",{class:"text-disabled text-h4 mt-3"},"Enter credentials to continue",-1),M=y({__name:"RegisterPage",setup(w){return(c,i)=>(b(),T(r,{class:"h-100vh","no-gutters":""},{default:a(()=>[e(d,{cols:"12",class:"d-flex align-center bg-lightprimary"},{default:a(()=>[e(q,null,{default:a(()=>[n("div",I,[e(r,{justify:"center"},{default:a(()=>[e(d,{cols:"12",lg:"10",xl:"6",md:"7"},{default:a(()=>[e(V,{elevation:"0",class:"loginBox"},{default:a(()=>[e(V,{variant:"outlined"},{default:a(()=>[e(P,{class:"pa-9"},{default:a(()=>[e(r,null,{default:a(()=>[e(d,{cols:"12",class:"text-center"},{default:a(()=>[e(B),J,Y]),_:1})]),_:1}),e(j)]),_:1})]),_:1})]),_:1})]),_:1})]),_:1})])]),_:1})]),_:1})]),_:1}))}});export{M as default};

View File

@@ -1 +0,0 @@
.custom-devider{border-color:#00000014!important}.googleBtn{border-color:#00000014;margin:30px 0 20px}.outlinedInput .v-field{border:1px solid rgba(0,0,0,.08);box-shadow:none}.orbtn{padding:2px 40px;border-color:#00000014;margin:20px 15px}.pwdInput{position:relative}.pwdInput .v-input__append{position:absolute;right:10px;top:50%;transform:translateY(-50%)}.loginBox{max-width:475px;margin:0 auto}

View File

@@ -1 +0,0 @@
import{_ as c}from"./BaseBreadcrumb.vue_vue_type_style_index_0_lang-89ca5198.js";import{_ as f}from"./UiParentCard.vue_vue_type_script_setup_true_lang-03a5c441.js";import{x as m,D as s,o as l,s as r,a as e,w as a,f as i,V as o,F as d,u as _,J as p,U as b,b as h,t as g}from"./index-7c8bc001.js";const v=m({__name:"ShadowPage",setup(w){const n=s({title:"Shadow Page"}),u=s([{title:"Utilities",disabled:!1,href:"#"},{title:"Shadow",disabled:!0,href:"#"}]);return(V,x)=>(l(),r(d,null,[e(c,{title:n.value.title,breadcrumbs:u.value},null,8,["title","breadcrumbs"]),e(o,null,{default:a(()=>[e(i,{cols:"12",md:"12"},{default:a(()=>[e(f,{title:"Basic Shadow"},{default:a(()=>[e(o,{justify:"center"},{default:a(()=>[(l(),r(d,null,_(25,t=>e(i,{key:t,cols:"auto"},{default:a(()=>[e(p,{height:"100",width:"100",class:b(["mb-5",["d-flex justify-center align-center bg-primary",`elevation-${t}`]])},{default:a(()=>[h("div",null,g(t-1),1)]),_:2},1032,["class"])]),_:2},1024)),64))]),_:1})]),_:1})]),_:1})]),_:1})],64))}});export{v as default};

View File

@@ -1 +0,0 @@
import{_ as o}from"./BaseBreadcrumb.vue_vue_type_style_index_0_lang-89ca5198.js";import{_ as n}from"./UiParentCard.vue_vue_type_script_setup_true_lang-03a5c441.js";import{x as c,D as a,o as i,s as m,a as e,w as t,f as d,b as f,V as _,F as u}from"./index-7c8bc001.js";const b=["innerHTML"],w=c({__name:"TablerIcons",setup(p){const s=a({title:"Tabler Icons"}),r=a('<iframe src="https://tablericons.com/" frameborder="0" width="100%" height="600"></iframe>'),l=a([{title:"Icons",disabled:!1,href:"#"},{title:"Tabler Icons",disabled:!0,href:"#"}]);return(h,T)=>(i(),m(u,null,[e(o,{title:s.value.title,breadcrumbs:l.value},null,8,["title","breadcrumbs"]),e(_,null,{default:t(()=>[e(d,{cols:"12",md:"12"},{default:t(()=>[e(n,{title:"Tabler Icons"},{default:t(()=>[f("div",{innerHTML:r.value},null,8,b)]),_:1})]),_:1})]),_:1})],64))}});export{w as default};

View File

@@ -1 +0,0 @@
import{_ as m}from"./BaseBreadcrumb.vue_vue_type_style_index_0_lang-89ca5198.js";import{_ as v}from"./UiParentCard.vue_vue_type_script_setup_true_lang-03a5c441.js";import{x as f,o as i,c as g,w as e,a,a6 as y,K as b,e as w,t as d,A as C,L as V,a7 as L,J as _,D as o,s as h,f as k,b as t,F as x,u as B,U as H,V as T}from"./index-7c8bc001.js";const s=f({__name:"UiChildCard",props:{title:String},setup(r){const l=r;return(n,c)=>(i(),g(_,{variant:"outlined"},{default:e(()=>[a(y,{class:"py-3"},{default:e(()=>[a(b,{class:"text-h5"},{default:e(()=>[w(d(l.title),1)]),_:1})]),_:1}),a(C),a(V,null,{default:e(()=>[L(n.$slots,"default")]),_:3})]),_:3}))}}),D={class:"d-flex flex-column gap-1"},S={class:"text-caption pa-2 bg-lightprimary"},z=t("div",{class:"text-grey"},"Class",-1),N={class:"font-weight-medium"},U=t("div",null,[t("p",{class:"text-left"},"Left aligned on all viewport sizes."),t("p",{class:"text-center"},"Center aligned on all viewport sizes."),t("p",{class:"text-right"},"Right aligned on all viewport sizes."),t("p",{class:"text-sm-left"},"Left aligned on viewports SM (small) or wider."),t("p",{class:"text-right text-md-left"},"Left aligned on viewports MD (medium) or wider."),t("p",{class:"text-right text-lg-left"},"Left aligned on viewports LG (large) or wider."),t("p",{class:"text-right text-xl-left"},"Left aligned on viewports XL (extra-large) or wider.")],-1),$=t("div",{class:"d-flex justify-space-between flex-row"},[t("a",{href:"#",class:"text-decoration-none"},"Non-underlined link"),t("div",{class:"text-decoration-line-through"},"Line-through text"),t("div",{class:"text-decoration-overline"},"Overline text"),t("div",{class:"text-decoration-underline"},"Underline text")],-1),M=t("div",null,[t("p",{class:"text-high-emphasis"},"High-emphasis has an opacity of 87% in light theme and 100% in dark."),t("p",{class:"text-medium-emphasis"},"Medium-emphasis text and hint text have opacities of 60% in light theme and 70% in dark."),t("p",{class:"text-disabled"},"Disabled text has an opacity of 38% in light theme and 50% in dark.")],-1),A=f({__name:"TypographyPage",setup(r){const l=o({title:"Typography Page"}),n=o([["Heading 1","text-h1"],["Heading 2","text-h2"],["Heading 3","text-h3"],["Heading 4","text-h4"],["Heading 5","text-h5"],["Heading 6","text-h6"],["Subtitle 1","text-subtitle-1"],["Subtitle 2","text-subtitle-2"],["Body 1","text-body-1"],["Body 2","text-body-2"],["Button","text-button"],["Caption","text-caption"],["Overline","text-overline"]]),c=o([{title:"Utilities",disabled:!1,href:"#"},{title:"Typography",disabled:!0,href:"#"}]);return(O,F)=>(i(),h(x,null,[a(m,{title:l.value.title,breadcrumbs:c.value},null,8,["title","breadcrumbs"]),a(T,null,{default:e(()=>[a(k,{cols:"12",md:"12"},{default:e(()=>[a(v,{title:"Basic Typography"},{default:e(()=>[a(s,{title:"Heading"},{default:e(()=>[t("div",D,[(i(!0),h(x,null,B(n.value,([p,u])=>(i(),g(_,{variant:"outlined",key:p,class:"my-4"},{default:e(()=>[t("div",{class:H([u,"pa-2"])},d(p),3),t("div",S,[z,t("div",N,d(u),1)])]),_:2},1024))),128))])]),_:1}),a(s,{title:"Text-alignment",class:"mt-8"},{default:e(()=>[U]),_:1}),a(s,{title:"Decoration",class:"mt-8"},{default:e(()=>[$]),_:1}),a(s,{title:"Opacity",class:"mt-8"},{default:e(()=>[M]),_:1})]),_:1})]),_:1})]),_:1})],64))}});export{A as default};

View File

@@ -1 +0,0 @@
import{x as n,o,c as i,w as e,a,a6 as d,b as c,K as u,e as p,t as _,a7 as s,A as f,L as V,J as m}from"./index-7c8bc001.js";const C={class:"d-sm-flex align-center justify-space-between"},h=n({__name:"UiParentCard",props:{title:String},setup(l){const r=l;return(t,x)=>(o(),i(m,{variant:"outlined",elevation:"0",class:"withbg"},{default:e(()=>[a(d,null,{default:e(()=>[c("div",C,[a(u,null,{default:e(()=>[p(_(r.title),1)]),_:1}),s(t.$slots,"action")])]),_:3}),a(f),a(V,null,{default:e(()=>[s(t.$slots,"default")]),_:3})]),_:3}))}});export{h as _};

View File

@@ -1 +0,0 @@
const s=(t,r)=>{const o=t.__vccOpts||t;for(const[c,e]of r)o[c]=e;return o};export{s as _};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,526 +0,0 @@
from addons.dashboard.server import AstrBotDashBoard, DashBoardData
from pydantic import BaseModel
from typing import Union, Optional
import uuid
from util import general_utils as gu
from util.cmd_config import CmdConfig
from dataclasses import dataclass
import sys
import os
import threading
import time
def shutdown_bot(delay_s: int):
time.sleep(delay_s)
py = sys.executable
os.execl(py, py, *sys.argv)
@dataclass
class DashBoardConfig():
config_type: str
name: Optional[str] = None
description: Optional[str] = None
path: Optional[str] = None # 仅 item 才需要
body: Optional[list['DashBoardConfig']] = None # 仅 group 才需要
value: Optional[Union[list, dict, str, int, bool]] = None # 仅 item 才需要
val_type: Optional[str] = None # 仅 item 才需要
class DashBoardHelper():
def __init__(self, dashboard_data: DashBoardData, config: dict):
dashboard_data.configs = {
"data": []
}
self.parse_default_config(dashboard_data, config)
self.dashboard_data: DashBoardData = dashboard_data
self.dashboard = AstrBotDashBoard(self.dashboard_data)
self.key_map = {} # key: uuid, value: config key name
self.cc = CmdConfig()
@self.dashboard.register("post_configs")
def on_post_configs(post_configs: dict):
try:
gu.log(f"收到配置更新请求", gu.LEVEL_INFO, tag="可视化面板")
self.save_config(post_configs)
self.parse_default_config(self.dashboard_data, self.cc.get_all())
# 重启
threading.Thread(target=shutdown_bot, args=(2,), daemon=True).start()
except Exception as e:
gu.log(f"在保存配置时发生错误:{e}", gu.LEVEL_ERROR, tag="可视化面板")
raise e
# 将 config.yaml、 中的配置解析到 dashboard_data.configs 中
def parse_default_config(self, dashboard_data: DashBoardData, config: dict):
try:
bot_platform_group = DashBoardConfig(
config_type="group",
name="机器人平台配置",
description="机器人平台配置描述",
body=[
DashBoardConfig(
config_type="item",
val_type="bool",
name="启用 QQ 频道平台",
description="就是你想到的那个 QQ 频道平台。详见 q.qq.com",
value=config['qqbot']['enable'],
path="qqbot.enable",
),
DashBoardConfig(
config_type="item",
val_type="string",
name="QQ机器人APPID",
description="详见 q.qq.com",
value=config['qqbot']['appid'],
path="qqbot.appid",
),
DashBoardConfig(
config_type="item",
val_type="string",
name="QQ机器人令牌",
description="详见 q.qq.com",
value=config['qqbot']['token'],
path="qqbot.token",
),
DashBoardConfig(
config_type="item",
val_type="string",
name="QQ机器人 Secret",
description="详见 q.qq.com",
value=config['qqbot_secret'],
path="qqbot_secret",
),
DashBoardConfig(
config_type="divider"
),
DashBoardConfig(
config_type="item",
val_type="bool",
name="启用 GO-CQHTTP 平台",
description="gocq 是一个基于 HTTP 协议的 CQHTTP 协议的实现。详见 github.com/Mrs4s/go-cqhttp",
value=config['gocqbot']['enable'],
path="gocqbot.enable",
)
]
)
proxy_group = DashBoardConfig(
config_type="group",
name="代理配置",
description="代理配置描述",
body=[
DashBoardConfig(
config_type="item",
val_type="string",
name="HTTP 代理地址",
description="建议上下一致",
value=config['http_proxy'],
path="proxy",
),
DashBoardConfig(
config_type="item",
val_type="string",
name="HTTPS 代理地址",
description="建议上下一致",
value=config['https_proxy'],
path="proxy",
)
]
)
general_platform_detail_group = DashBoardConfig(
config_type="group",
name="通用平台配置",
description="",
body=[
DashBoardConfig(
config_type="item",
val_type="bool",
name="启动消息文字转图片",
description="启动后,机器人会将消息转换为图片发送,以降低风控风险。",
value=config['qq_pic_mode'],
path="qq_pic_mode",
),
DashBoardConfig(
config_type="item",
val_type="int",
name="消息限制时间",
description="在此时间内,机器人不会回复同一个用户的消息。单位:秒",
value=config['limit']['time'],
path="limit.time",
),
DashBoardConfig(
config_type="item",
val_type="int",
name="消息限制次数",
description="在上面的时间内,如果用户发送消息超过此次数,则机器人不会回复。单位:次",
value=config['limit']['count'],
path="limit.count",
),
DashBoardConfig(
config_type="item",
val_type="string",
name="回复前缀",
description="[xxxx] 你好! 其中xxxx是你可以填写的前缀。如果为空则不显示。",
value=config['reply_prefix'],
path="reply_prefix",
),
DashBoardConfig(
config_type="item",
val_type="string",
name="管理员用户 ID",
description="对机器人 !myid 即可获得。如果此功能不可用,请加群 322154837",
value=config['gocq_qqchan_admin'],
path="gocq_qqchan_admin",
),
DashBoardConfig(
config_type="item",
val_type="list",
name="通用管理员用户 ID同上此项支持多个管理员",
description="",
value=config['other_admins'],
path="other_admins",
),
DashBoardConfig(
config_type="item",
val_type="bool",
name="独立会话",
description="是否启用独立会话模式,即 1 个用户自然账号 1 个会话。",
value=config['uniqueSessionMode'],
path="uniqueSessionMode",
),
DashBoardConfig(
config_type="item",
val_type="bool",
name="是否允许 QQ 频道私聊",
description="仅针对 QQ 频道 SDK而非 GO-CQHTTP。如果启用那么机器人会响应私聊消息。",
value=config['direct_message_mode'],
path="direct_message_mode",
),
]
)
gocq_platform_detail_group = DashBoardConfig(
config_type="group",
name="GO-CQHTTP 平台配置",
description="",
body=[
DashBoardConfig(
config_type="item",
val_type="string",
name="HTTP 服务器地址",
description="",
value=config['gocq_host'],
path="gocq_host",
),
DashBoardConfig(
config_type="item",
val_type="int",
name="HTTP 服务器端口",
description="",
value=config['gocq_http_port'],
path="gocq_http_port",
),
DashBoardConfig(
config_type="item",
val_type="int",
name="WebSocket 服务器端口",
description="",
value=config['gocq_websocket_port'],
path="gocq_websocket_port",
),
DashBoardConfig(
config_type="item",
val_type="bool",
name="是否响应群消息",
description="",
value=config['gocq_react_group'],
path="gocq_react_group",
),
DashBoardConfig(
config_type="item",
val_type="bool",
name="是否响应私聊消息",
description="",
value=config['gocq_react_friend'],
path="gocq_react_friend",
),
DashBoardConfig(
config_type="item",
val_type="bool",
name="是否响应群成员增加消息",
description="",
value=config['gocq_react_group_increase'],
path="gocq_react_group_increase",
),
DashBoardConfig(
config_type="item",
val_type="bool",
name="是否响应频道消息",
description="",
value=config['gocq_react_guild'],
path="gocq_react_guild",
),
DashBoardConfig(
config_type="item",
val_type="int",
name="转发阈值(字符数)",
description="机器人回复的消息长度超出这个值后,会被折叠成转发卡片发出以减少刷屏。",
value=config['qq_forward_threshold'],
path="qq_forward_threshold",
),
]
)
llm_group = DashBoardConfig(
config_type="group",
name="LLM 配置",
description="",
body=[
DashBoardConfig(
config_type="item",
val_type="list",
name="OpenAI API KEY",
description="OpenAI API 的 KEY。支持使用非官方但是兼容的 API。",
value=config['openai']['key'],
path="openai.key",
),
DashBoardConfig(
config_type="item",
val_type="string",
name="OpenAI API 节点地址",
description="OpenAI API 的节点地址,配合非官方 API 使用。如果不想填写,那么请填写 none",
value=config['openai']['api_base'],
path="openai.api_base",
),
DashBoardConfig(
config_type="item",
val_type="string",
name="OpenAI 模型",
description="OpenAI 模型。详见 https://platform.openai.com/docs/api-reference/chat",
value=config['openai']['chatGPTConfigs']['model'],
path="openai.chatGPTConfigs.model",
),
DashBoardConfig(
config_type="item",
val_type="int",
name="OpenAI 最大生成长度",
description="OpenAI 最大生成长度。详见 https://platform.openai.com/docs/api-reference/chat",
value=config['openai']['chatGPTConfigs']['max_tokens'],
path="openai.chatGPTConfigs.max_tokens",
),
DashBoardConfig(
config_type="item",
val_type="float",
name="OpenAI 温度",
description="OpenAI 温度。详见 https://platform.openai.com/docs/api-reference/chat",
value=config['openai']['chatGPTConfigs']['temperature'],
path="openai.chatGPTConfigs.temperature",
),
DashBoardConfig(
config_type="item",
val_type="float",
name="OpenAI top_p",
description="OpenAI top_p。详见 https://platform.openai.com/docs/api-reference/chat",
value=config['openai']['chatGPTConfigs']['top_p'],
path="openai.chatGPTConfigs.top_p",
),
DashBoardConfig(
config_type="item",
val_type="float",
name="OpenAI frequency_penalty",
description="OpenAI frequency_penalty。详见 https://platform.openai.com/docs/api-reference/chat",
value=config['openai']['chatGPTConfigs']['frequency_penalty'],
path="openai.chatGPTConfigs.frequency_penalty",
),
DashBoardConfig(
config_type="item",
val_type="float",
name="OpenAI presence_penalty",
description="OpenAI presence_penalty。详见 https://platform.openai.com/docs/api-reference/chat",
value=config['openai']['chatGPTConfigs']['presence_penalty'],
path="openai.chatGPTConfigs.presence_penalty",
),
DashBoardConfig(
config_type="item",
val_type="int",
name="OpenAI 总生成长度限制",
description="OpenAI 总生成长度限制。详见 https://platform.openai.com/docs/api-reference/chat",
value=config['openai']['total_tokens_limit'],
path="openai.total_tokens_limit",
),
DashBoardConfig(
config_type="item",
val_type="string",
name="OpenAI 图像生成模型",
description="OpenAI 图像生成模型。",
value=config['openai_image_generate']['model'],
path="openai_image_generate.model",
),
DashBoardConfig(
config_type="item",
val_type="string",
name="OpenAI 图像生成大小",
description="OpenAI 图像生成大小。",
value=config['openai_image_generate']['size'],
path="openai_image_generate.size",
),
DashBoardConfig(
config_type="item",
val_type="string",
name="OpenAI 图像生成风格",
description="OpenAI 图像生成风格。修改前请参考 OpenAI 官方文档",
value=config['openai_image_generate']['style'],
path="openai_image_generate.style",
),
DashBoardConfig(
config_type="item",
val_type="string",
name="OpenAI 图像生成质量",
description="OpenAI 图像生成质量。修改前请参考 OpenAI 官方文档",
value=config['openai_image_generate']['quality'],
path="openai_image_generate.quality",
),
DashBoardConfig(
config_type="item",
val_type="string",
name="大语言模型问题题首提示词",
description="如果填写了此项,在每个对大语言模型的请求中,都会在问题前加上此提示词。",
value=config['llm_env_prompt'],
path="llm_env_prompt",
),
]
)
baidu_aip_group = DashBoardConfig(
config_type="group",
name="百度内容审核",
description="需要去申请",
body=[
DashBoardConfig(
config_type="item",
val_type="bool",
name="启动百度内容审核服务",
description="",
value=config['baidu_aip']['enable'],
path="baidu_aip.enable"
),
# "app_id": null,
# "api_key": null,
# "secret_key": null
DashBoardConfig(
config_type="item",
val_type="string",
name="APP ID",
description="",
value=config['baidu_aip']['app_id'],
path="baidu_aip.app_id"
),
DashBoardConfig(
config_type="item",
val_type="string",
name="API KEY",
description="",
value=config['baidu_aip']['api_key'],
path="baidu_aip.api_key"
),
DashBoardConfig(
config_type="item",
val_type="string",
name="SECRET KEY",
description="",
value=config['baidu_aip']['secret_key'],
path="baidu_aip.secret_key"
)
]
)
other_group = DashBoardConfig(
config_type="group",
name="其他配置",
description="其他配置描述",
body=[
# 人格
DashBoardConfig(
config_type="item",
val_type="string",
name="默认人格文本",
description="默认人格文本",
value=config['default_personality_str'],
path="default_personality_str",
),
DashBoardConfig(
config_type="item",
val_type="string",
name="面板用户名",
description="是的,就是你理解的这个面板的用户名",
value=config['dashboard_username'],
path="dashboard_username",
),
]
)
dashboard_data.configs['data'] = [
bot_platform_group,
general_platform_detail_group,
gocq_platform_detail_group,
proxy_group,
llm_group,
other_group,
baidu_aip_group
]
except Exception as e:
gu.log(f"配置文件解析错误:{e}", gu.LEVEL_ERROR)
raise e
def save_config(self, post_config: dict):
'''
根据 path 解析并保存配置
'''
queue = []
for config in post_config['data']:
queue.append(config)
while len(queue) > 0:
config = queue.pop(0)
if config['config_type'] == "group":
for item in config['body']:
queue.append(item)
elif config['config_type'] == "item":
if config['path'] is None or config['path'] == "":
continue
path = config['path'].split('.')
if len(path) == 0:
continue
if config['val_type'] == "bool":
self.cc.put_by_dot_str(config['path'], config['value'])
elif config['val_type'] == "string":
self.cc.put_by_dot_str(config['path'], config['value'])
elif config['val_type'] == "int":
try:
self.cc.put_by_dot_str(config['path'], int(config['value']))
except:
raise ValueError(f"配置项 {config['name']} 的值必须是整数")
elif config['val_type'] == "float":
try:
self.cc.put_by_dot_str(config['path'], float(config['value']))
except:
raise ValueError(f"配置项 {config['name']} 的值必须是浮点数")
elif config['val_type'] == "list":
if config['value'] is None:
self.cc.put_by_dot_str(config['path'], [])
elif not isinstance(config['value'], list):
raise ValueError(f"配置项 {config['name']} 的值必须是列表")
self.cc.put_by_dot_str(config['path'], config['value'])
else:
raise NotImplementedError(f"未知或者未实现的的配置项类型:{config['val_type']}")
def run(self):
self.dashboard.run()

View File

@@ -1,233 +0,0 @@
from flask import Flask, request
from flask.logging import default_handler
from werkzeug.serving import make_server
import datetime
from util import general_utils as gu
from dataclasses import dataclass
import logging
from cores.database.conn import dbConn
from util.cmd_config import CmdConfig
import util.plugin_util as putil
@dataclass
class DashBoardData():
stats: dict
configs: dict
logs: dict
plugins: list[dict]
@dataclass
class Response():
status: str
message: str
data: dict
class AstrBotDashBoard():
def __init__(self, dashboard_data: DashBoardData):
self.dashboard_data = dashboard_data
self.dashboard_be = Flask(__name__, static_folder="dist", static_url_path="/")
log = logging.getLogger('werkzeug')
log.setLevel(logging.ERROR)
self.funcs = {}
self.cc = CmdConfig()
@self.dashboard_be.get("/")
def index():
# 返回页面
return self.dashboard_be.send_static_file("index.html")
@self.dashboard_be.post("/api/authenticate")
def authenticate():
username = self.cc.get("dashboard_username", "")
password = self.cc.get("dashboard_password", "")
# 获得请求体
post_data = request.json
if post_data["username"] == username and post_data["password"] == password:
return Response(
status="success",
message="登录成功。",
data={
"token": "astrbot-test-token",
"username": username
}
).__dict__
else:
return Response(
status="error",
message="用户名或密码错误。",
data=None
).__dict__
@self.dashboard_be.post("/api/change_password")
def change_password():
password = self.cc.get("dashboard_password", "")
# 获得请求体
post_data = request.json
if post_data["password"] == password:
self.cc.put("dashboard_password", post_data["new_password"])
return Response(
status="success",
message="修改成功。",
data=None
).__dict__
else:
return Response(
status="error",
message="原密码错误。",
data=None
).__dict__
@self.dashboard_be.get("/api/stats")
def get_stats():
db_inst = dbConn()
all_session = db_inst.get_all_stat_session()
last_24_message = db_inst.get_last_24h_stat_message()
# last_24_platform = db_inst.get_last_24h_stat_platform()
platforms = db_inst.get_platform_cnt_total()
self.dashboard_data.stats["session"] = []
self.dashboard_data.stats["session_total"] = db_inst.get_session_cnt_total()
self.dashboard_data.stats["message"] = last_24_message
self.dashboard_data.stats["message_total"] = db_inst.get_message_cnt_total()
self.dashboard_data.stats["platform"] = platforms
return Response(
status="success",
message="",
data=self.dashboard_data.stats
).__dict__
@self.dashboard_be.get("/api/configs")
def get_configs():
return Response(
status="success",
message="",
data=self.dashboard_data.configs
).__dict__
@self.dashboard_be.post("/api/configs")
def post_configs():
post_configs = request.json
try:
self.funcs["post_configs"](post_configs)
return Response(
status="success",
message="保存成功~ 机器人将在 2 秒内重启以应用新的配置。",
data=None
).__dict__
except Exception as e:
return Response(
status="error",
message=e.__str__(),
data=self.dashboard_data.configs
).__dict__
@self.dashboard_be.get("/api/logs")
def get_logs():
return Response(
status="success",
message="",
data=self.dashboard_data.logs
).__dict__
@self.dashboard_be.get("/api/extensions")
def get_plugins():
"""
{
"name": "GoodPlugins",
"repo": "https://gitee.com/soulter/goodplugins",
"author": "soulter",
"desc": "一些好用的插件",
"version": "1.0"
}
"""
_plugin_resp = []
for plugin in self.dashboard_data.plugins:
_p = self.dashboard_data.plugins[plugin]
_t = {
"name": _p["info"]["name"],
"repo": '' if "repo" not in _p["info"] else _p["info"]["repo"],
"author": _p["info"]["author"],
"desc": _p["info"]["desc"],
"version": _p["info"]["version"]
}
_plugin_resp.append(_t)
return Response(
status="success",
message="",
data=_plugin_resp
).__dict__
@self.dashboard_be.post("/api/extensions/install")
def install_plugin():
post_data = request.json
repo_url = post_data["url"]
try:
gu.log(f"正在安装插件 {repo_url}", tag="可视化面板")
putil.install_plugin(repo_url, self.dashboard_data.plugins)
gu.log(f"安装插件 {repo_url} 成功", tag="可视化面板")
return Response(
status="success",
message="安装成功~",
data=None
).__dict__
except Exception as e:
return Response(
status="error",
message=e.__str__(),
data=None
).__dict__
@self.dashboard_be.post("/api/extensions/uninstall")
def uninstall_plugin():
post_data = request.json
plugin_name = post_data["name"]
try:
gu.log(f"正在卸载插件 {plugin_name}", tag="可视化面板")
putil.uninstall_plugin(plugin_name, self.dashboard_data.plugins)
gu.log(f"卸载插件 {plugin_name} 成功", tag="可视化面板")
return Response(
status="success",
message="卸载成功~",
data=None
).__dict__
except Exception as e:
return Response(
status="error",
message=e.__str__(),
data=None
).__dict__
@self.dashboard_be.post("/api/extensions/update")
def update_plugin():
post_data = request.json
plugin_name = post_data["name"]
try:
gu.log(f"正在更新插件 {plugin_name}", tag="可视化面板")
putil.update_plugin(plugin_name, self.dashboard_data.plugins)
gu.log(f"更新插件 {plugin_name} 成功", tag="可视化面板")
return Response(
status="success",
message="更新成功~",
data=None
).__dict__
except Exception as e:
return Response(
status="error",
message=e.__str__(),
data=None
).__dict__
def register(self, name: str):
def decorator(func):
self.funcs[name] = func
return func
return decorator
def run(self):
ip_address = gu.get_local_ip_addresses()
ip_str = f"http://{ip_address}:6185\n\thttp://localhost:6185"
gu.log(f"\n\n==================\n您可以访问:\n\n\t{ip_str}\n\n来登录可视化面板。\n注意: 所有配置项现已全量迁移至 cmd_config.json 文件下。您可以登录可视化面板在线修改配置。\n==================\n\n", tag="可视化面板")
# self.dashboard_be.run(host="0.0.0.0", port=6185)
http_server = make_server('0.0.0.0', 6185, self.dashboard_be)
http_server.serve_forever()

View File

@@ -1,5 +0,0 @@
# helloworld
QQChannelChatGPT项目的测试插件
A test plugin for QQChannelChatGPT plugin feature

View File

@@ -1,66 +0,0 @@
from nakuru.entities.components import *
from nakuru import (
GroupMessage,
FriendMessage
)
from botpy.message import Message, DirectMessage
from model.platform.qq import QQ
from cores.qqbot.global_object import (
AstrMessageEvent,
CommandResult
)
'''
注意改插件名噢格式XXXPlugin 或 Main
小提示:把此模板仓库 fork 之后 clone 到机器人文件夹下的 addons/plugins/ 目录下,然后用 Pycharm/VSC 等工具打开可获更棒的编程体验(自动补全等)
'''
class HelloWorldPlugin:
"""
初始化函数, 可以选择直接pass
"""
def __init__(self) -> None:
print("hello, world!")
"""
机器人程序会调用此函数。
返回规范: bool: 插件是否响应该消息 (所有的消息均会调用每一个载入的插件, 如果不响应, 则应返回 False)
Tuple: Non e或者长度为 3 的元组。如果不响应, 返回 None 如果响应, 第 1 个参数为指令是否调用成功, 第 2 个参数为返回的消息链列表, 第 3 个参数为指令名称
例子:一个名为"yuanshen"的插件;当接收到消息为“原神 可莉”, 如果不想要处理此消息则返回False, None如果想要处理但是执行失败了返回True, tuple([False, "请求失败。", "yuanshen"]) 执行成功了返回True, tuple([True, "结果文本", "yuanshen"])
"""
def run(self, ame: AstrMessageEvent):
if ame.message_str == "helloworld":
# return True, tuple([True, "Hello World!!", "helloworld"])
return CommandResult(
hit=True,
success=True,
message_chain=[Plain("Hello World!!")],
command_name="helloworld"
)
else:
return CommandResult(
hit=False,
success=False,
message_chain=None,
command_name=None
)
"""
插件元信息。
当用户输入 plugin v 插件名称 时,会调用此函数,返回帮助信息。
返回参数要求(必填)dict{
"name": str, # 插件名称
"desc": str, # 插件简短描述
"help": str, # 插件帮助信息
"version": str, # 插件版本
"author": str, # 插件作者
"repo": str, # 插件仓库地址 [ 可选 ]
"homepage": str, # 插件主页 [ 可选 ]
}
"""
def info(self):
return {
"name": "helloworld",
"desc": "测试插件",
"help": "测试插件, 回复 helloworld 即可触发",
"version": "v1.2",
"author": "Soulter"
}

3
astrbot/__init__.py Normal file
View File

@@ -0,0 +1,3 @@
from .core.log import LogManager
logger = LogManager.GetLogger(log_name="astrbot")

7
astrbot/api/__init__.py Normal file
View File

@@ -0,0 +1,7 @@
from astrbot.core.config.astrbot_config import AstrBotConfig
from astrbot import logger
from astrbot.core import html_renderer
from astrbot.core import sp
from astrbot.core.star.register import register_llm_tool as llm_tool
__all__ = ["AstrBotConfig", "logger", "html_renderer", "llm_tool", "sp"]

53
astrbot/api/all.py Normal file
View File

@@ -0,0 +1,53 @@
from astrbot.core.config.astrbot_config import AstrBotConfig
from astrbot import logger
from astrbot.core import html_renderer
from astrbot.core.star.register import register_llm_tool as llm_tool
# event
from astrbot.core.message.message_event_result import (
MessageEventResult,
MessageChain,
CommandResult,
EventResultType,
)
from astrbot.core.platform import AstrMessageEvent
# star register
from astrbot.core.star.register import (
register_command as command,
register_command_group as command_group,
register_event_message_type as event_message_type,
register_regex as regex,
register_platform_adapter_type as platform_adapter_type,
)
from astrbot.core.star.filter.event_message_type import (
EventMessageTypeFilter,
EventMessageType,
)
from astrbot.core.star.filter.platform_adapter_type import (
PlatformAdapterTypeFilter,
PlatformAdapterType,
)
from astrbot.core.star.register import (
register_star as register, # 注册插件Star
)
from astrbot.core.star import Context, Star
from astrbot.core.star.config import *
# provider
from astrbot.core.provider import Provider, Personality, ProviderMetaData
# platform
from astrbot.core.platform import (
AstrMessageEvent,
Platform,
AstrBotMessage,
MessageMember,
MessageType,
PlatformMetadata,
)
from astrbot.core.platform.register import register_platform_adapter
from .message_components import *

View File

@@ -0,0 +1,18 @@
from astrbot.core.message.message_event_result import (
MessageEventResult,
MessageChain,
CommandResult,
EventResultType,
ResultContentType,
)
from astrbot.core.platform import AstrMessageEvent
__all__ = [
"MessageEventResult",
"MessageChain",
"CommandResult",
"EventResultType",
"AstrMessageEvent",
"ResultContentType",
]

View File

@@ -0,0 +1,49 @@
from astrbot.core.star.register import (
register_command as command,
register_command_group as command_group,
register_event_message_type as event_message_type,
register_regex as regex,
register_platform_adapter_type as platform_adapter_type,
register_permission_type as permission_type,
register_custom_filter as custom_filter,
register_on_astrbot_loaded as on_astrbot_loaded,
register_on_llm_request as on_llm_request,
register_on_llm_response as on_llm_response,
register_llm_tool as llm_tool,
register_on_decorating_result as on_decorating_result,
register_after_message_sent as after_message_sent,
)
from astrbot.core.star.filter.event_message_type import (
EventMessageTypeFilter,
EventMessageType,
)
from astrbot.core.star.filter.platform_adapter_type import (
PlatformAdapterTypeFilter,
PlatformAdapterType,
)
from astrbot.core.star.filter.permission import PermissionTypeFilter, PermissionType
from astrbot.core.star.filter.custom_filter import CustomFilter
__all__ = [
"command",
"command_group",
"event_message_type",
"regex",
"platform_adapter_type",
"permission_type",
"EventMessageTypeFilter",
"EventMessageType",
"PlatformAdapterTypeFilter",
"PlatformAdapterType",
"PermissionTypeFilter",
"CustomFilter",
"custom_filter",
"PermissionType",
"on_astrbot_loaded",
"on_llm_request",
"llm_tool",
"on_decorating_result",
"after_message_sent",
"on_llm_response",
]

View File

@@ -0,0 +1 @@
from astrbot.core.message.components import *

View File

@@ -0,0 +1,23 @@
from astrbot.core.platform import (
AstrMessageEvent,
Platform,
AstrBotMessage,
MessageMember,
MessageType,
PlatformMetadata,
Group,
)
from astrbot.core.platform.register import register_platform_adapter
from astrbot.core.message.components import *
__all__ = [
"AstrMessageEvent",
"Platform",
"AstrBotMessage",
"MessageMember",
"MessageType",
"PlatformMetadata",
"register_platform_adapter",
"Group",
]

View File

@@ -0,0 +1,17 @@
from astrbot.core.provider import Provider, STTProvider, Personality
from astrbot.core.provider.entities import (
ProviderRequest,
ProviderType,
ProviderMetaData,
LLMResponse,
)
__all__ = [
"Provider",
"STTProvider",
"Personality",
"ProviderRequest",
"ProviderType",
"ProviderMetaData",
"LLMResponse",
]

View File

@@ -0,0 +1,8 @@
from astrbot.core.star.register import (
register_star as register, # 注册插件Star
)
from astrbot.core.star import Context, Star, StarTools
from astrbot.core.star.config import *
__all__ = ["register", "Context", "Star", "StarTools"]

View File

@@ -0,0 +1,7 @@
from astrbot.core.utils.session_waiter import (
SessionWaiter,
SessionController,
session_waiter,
)
__all__ = ["SessionWaiter", "SessionController", "session_waiter"]

33
astrbot/core/__init__.py Normal file
View File

@@ -0,0 +1,33 @@
import os
import asyncio
from .log import LogManager, LogBroker # noqa
from astrbot.core.utils.t2i.renderer import HtmlRenderer
from astrbot.core.utils.shared_preferences import SharedPreferences
from astrbot.core.utils.pip_installer import PipInstaller
from astrbot.core.db.sqlite import SQLiteDatabase
from astrbot.core.config.default import DB_PATH
from astrbot.core.config import AstrBotConfig
# 初始化数据存储文件夹
os.makedirs("data", exist_ok=True)
astrbot_config = AstrBotConfig()
t2i_base_url = astrbot_config.get("t2i_endpoint", "https://t2i.soulter.top/text2img")
html_renderer = HtmlRenderer(t2i_base_url)
logger = LogManager.GetLogger(log_name="astrbot")
if os.environ.get("TESTING", ""):
logger.setLevel("DEBUG")
db_helper = SQLiteDatabase(DB_PATH)
sp = (
SharedPreferences()
) # 简单的偏好设置存储, 这里后续应该存储到数据库中, 一些部分可以存储到配置中
pip_installer = PipInstaller(
astrbot_config.get("pip_install_arg", ""),
astrbot_config.get("pypi_index_url", None),
)
web_chat_queue = asyncio.Queue(maxsize=32)
web_chat_back_queue = asyncio.Queue(maxsize=32)
WEBUI_SK = "Advanced_System_for_Text_Response_and_Bot_Operations_Tool"
DEMO_MODE = os.getenv("DEMO_MODE", False)

View File

@@ -0,0 +1,9 @@
from .default import DEFAULT_CONFIG, VERSION, DB_PATH
from .astrbot_config import *
__all__ = [
"DEFAULT_CONFIG",
"VERSION",
"DB_PATH",
"AstrBotConfig",
]

View File

@@ -0,0 +1,132 @@
import os
import json
import logging
import enum
from .default import DEFAULT_CONFIG, DEFAULT_VALUE_MAP
from typing import Dict
ASTRBOT_CONFIG_PATH = "data/cmd_config.json"
logger = logging.getLogger("astrbot")
class RateLimitStrategy(enum.Enum):
STALL = "stall"
DISCARD = "discard"
class AstrBotConfig(dict):
"""从配置文件中加载的配置,支持直接通过点号操作符访问根配置项。
- 初始化时会将传入的 default_config 与配置文件进行比对,如果配置文件中缺少配置项则会自动插入默认值并进行一次写入操作。会递归检查配置项。
- 如果配置文件路径对应的文件不存在,则会自动创建并写入默认配置。
- 如果传入了 schema将会通过 schema 解析出 default_config此时传入的 default_config 会被忽略。
"""
def __init__(
self,
config_path: str = ASTRBOT_CONFIG_PATH,
default_config: dict = DEFAULT_CONFIG,
schema: dict = None,
):
super().__init__()
# 调用父类的 __setattr__ 方法,防止保存配置时将此属性写入配置文件
object.__setattr__(self, "config_path", config_path)
object.__setattr__(self, "default_config", default_config)
object.__setattr__(self, "schema", schema)
if schema:
default_config = self._config_schema_to_default_config(schema)
if not self.check_exist():
"""不存在时载入默认配置"""
with open(config_path, "w", encoding="utf-8-sig") as f:
json.dump(default_config, f, indent=4, ensure_ascii=False)
with open(config_path, "r", encoding="utf-8-sig") as f:
conf_str = f.read()
if conf_str.startswith("/ufeff"): # remove BOM
conf_str = conf_str.encode("utf8")[3:].decode("utf8")
conf = json.loads(conf_str)
# 检查配置完整性,并插入
has_new = self.check_config_integrity(default_config, conf)
self.update(conf)
if has_new:
self.save_config()
self.update(conf)
def _config_schema_to_default_config(self, schema: dict) -> dict:
"""将 Schema 转换成 Config"""
conf = {}
def _parse_schema(schema: dict, conf: dict):
for k, v in schema.items():
if v["type"] not in DEFAULT_VALUE_MAP:
raise TypeError(
f"不受支持的配置类型 {v['type']}。支持的类型有:{DEFAULT_VALUE_MAP.keys()}"
)
if "default" in v:
default = v["default"]
else:
default = DEFAULT_VALUE_MAP[v["type"]]
if v["type"] == "object":
conf[k] = {}
_parse_schema(v["items"], conf[k])
else:
conf[k] = default
_parse_schema(schema, conf)
return conf
def check_config_integrity(self, refer_conf: Dict, conf: Dict, path=""):
"""检查配置完整性,如果有新的配置项则返回 True"""
has_new = False
for key, value in refer_conf.items():
if key not in conf:
# logger.info(f"检查到配置项 {path + "." + key if path else key} 不存在,已插入默认值 {value}")
path_ = path + "." + key if path else key
logger.info(f"检查到配置项 {path_} 不存在,已插入默认值 {value}")
conf[key] = value
has_new = True
else:
if conf[key] is None:
conf[key] = value
has_new = True
elif isinstance(value, dict):
has_new |= self.check_config_integrity(
value, conf[key], path + "." + key if path else key
)
return has_new
def save_config(self, replace_config: Dict = None):
"""将配置写入文件
如果传入 replace_config则将配置替换为 replace_config
"""
if replace_config:
self.update(replace_config)
with open(self.config_path, "w", encoding="utf-8-sig") as f:
json.dump(self, f, indent=2, ensure_ascii=False)
def __getattr__(self, item):
try:
return self[item]
except KeyError:
return None
def __delattr__(self, key):
try:
del self[key]
self.save_config()
except KeyError:
raise AttributeError(f"没有找到 Key: '{key}'")
def __setattr__(self, key, value):
self[key] = value
def check_exist(self) -> bool:
return os.path.exists(self.config_path)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,199 @@
"""
AstrBot 会话-对话管理器, 维护两个本地存储, 其中一个是 json 格式的shared_preferences, 另外一个是数据库
在 AstrBot 中, 会话和对话是独立的, 会话用于标记对话窗口, 例如群聊"123456789"可以建立一个会话,
在一个会话中可以建立多个对话, 并且支持对话的切换和删除
"""
import uuid
import json
import asyncio
from astrbot.core import sp
from typing import Dict, List
from astrbot.core.db import BaseDatabase
from astrbot.core.db.po import Conversation
class ConversationManager:
"""负责管理会话与 LLM 的对话,某个会话当前正在用哪个对话。"""
def __init__(self, db_helper: BaseDatabase):
# session_conversations 字典记录会话ID-对话ID 映射关系
self.session_conversations: Dict[str, str] = sp.get("session_conversation", {})
self.db = db_helper
self.save_interval = 60 # 每 60 秒保存一次
self._start_periodic_save()
def _start_periodic_save(self):
"""启动定时保存任务"""
asyncio.create_task(self._periodic_save())
async def _periodic_save(self):
"""定时保存会话对话映射关系到存储中"""
while True:
await asyncio.sleep(self.save_interval)
self._save_to_storage()
def _save_to_storage(self):
"""保存会话对话映射关系到存储中"""
sp.put("session_conversation", self.session_conversations)
async def new_conversation(self, unified_msg_origin: str) -> str:
"""新建对话,并将当前会话的对话转移到新对话
Args:
unified_msg_origin (str): 统一的消息来源字符串。格式为 platform_name:message_type:session_id
Returns:
conversation_id (str): 对话 ID, 是 uuid 格式的字符串
"""
conversation_id = str(uuid.uuid4())
self.db.new_conversation(user_id=unified_msg_origin, cid=conversation_id)
self.session_conversations[unified_msg_origin] = conversation_id
sp.put("session_conversation", self.session_conversations)
return conversation_id
async def switch_conversation(self, unified_msg_origin: str, conversation_id: str):
"""切换会话的对话
Args:
unified_msg_origin (str): 统一的消息来源字符串。格式为 platform_name:message_type:session_id
conversation_id (str): 对话 ID, 是 uuid 格式的字符串
"""
self.session_conversations[unified_msg_origin] = conversation_id
sp.put("session_conversation", self.session_conversations)
async def delete_conversation(
self, unified_msg_origin: str, conversation_id: str = None
):
"""删除会话的对话,当 conversation_id 为 None 时删除会话当前的对话
Args:
unified_msg_origin (str): 统一的消息来源字符串。格式为 platform_name:message_type:session_id
conversation_id (str): 对话 ID, 是 uuid 格式的字符串
"""
conversation_id = self.session_conversations.get(unified_msg_origin)
if conversation_id:
self.db.delete_conversation(user_id=unified_msg_origin, cid=conversation_id)
del self.session_conversations[unified_msg_origin]
sp.put("session_conversation", self.session_conversations)
async def get_curr_conversation_id(self, unified_msg_origin: str) -> str:
"""获取会话当前的对话 ID
Args:
unified_msg_origin (str): 统一的消息来源字符串。格式为 platform_name:message_type:session_id
Returns:
conversation_id (str): 对话 ID, 是 uuid 格式的字符串
"""
return self.session_conversations.get(unified_msg_origin, None)
async def get_conversation(
self, unified_msg_origin: str, conversation_id: str
) -> Conversation:
"""获取会话的对话
Args:
unified_msg_origin (str): 统一的消息来源字符串。格式为 platform_name:message_type:session_id
conversation_id (str): 对话 ID, 是 uuid 格式的字符串
Returns:
conversation (Conversation): 对话对象
"""
return self.db.get_conversation_by_user_id(unified_msg_origin, conversation_id)
async def get_conversations(self, unified_msg_origin: str) -> List[Conversation]:
"""获取会话的所有对话
Args:
unified_msg_origin (str): 统一的消息来源字符串。格式为 platform_name:message_type:session_id
Returns:
conversations (List[Conversation]): 对话对象列表
"""
return self.db.get_conversations(unified_msg_origin)
async def update_conversation(
self, unified_msg_origin: str, conversation_id: str, history: List[Dict]
):
"""更新会话的对话
Args:
unified_msg_origin (str): 统一的消息来源字符串。格式为 platform_name:message_type:session_id
conversation_id (str): 对话 ID, 是 uuid 格式的字符串
history (List[Dict]): 对话历史记录, 是一个字典列表, 每个字典包含 role 和 content 字段
"""
if conversation_id:
self.db.update_conversation(
user_id=unified_msg_origin,
cid=conversation_id,
history=json.dumps(history),
)
async def update_conversation_title(self, unified_msg_origin: str, title: str):
"""更新会话的对话标题
Args:
unified_msg_origin (str): 统一的消息来源字符串。格式为 platform_name:message_type:session_id
title (str): 对话标题
"""
conversation_id = self.session_conversations.get(unified_msg_origin)
if conversation_id:
self.db.update_conversation_title(
user_id=unified_msg_origin, cid=conversation_id, title=title
)
async def update_conversation_persona_id(
self, unified_msg_origin: str, persona_id: str
):
"""更新会话的对话 Persona ID
Args:
unified_msg_origin (str): 统一的消息来源字符串。格式为 platform_name:message_type:session_id
persona_id (str): 对话 Persona ID
"""
conversation_id = self.session_conversations.get(unified_msg_origin)
if conversation_id:
self.db.update_conversation_persona_id(
user_id=unified_msg_origin, cid=conversation_id, persona_id=persona_id
)
async def get_human_readable_context(
self, unified_msg_origin, conversation_id, page=1, page_size=10
):
"""获取人类可读的上下文
Args:
unified_msg_origin (str): 统一的消息来源字符串。格式为 platform_name:message_type:session_id
conversation_id (str): 对话 ID, 是 uuid 格式的字符串
page (int): 页码
page_size (int): 每页大小
"""
conversation = await self.get_conversation(unified_msg_origin, conversation_id)
history = json.loads(conversation.history)
contexts = []
temp_contexts = []
for record in history:
if record["role"] == "user":
temp_contexts.append(f"User: {record['content']}")
elif record["role"] == "assistant":
if "content" in record and record["content"]:
temp_contexts.append(f"Assistant: {record['content']}")
elif "tool_calls" in record:
tool_calls_str = json.dumps(
record["tool_calls"], ensure_ascii=False
)
temp_contexts.append(f"Assistant: [函数调用] {tool_calls_str}")
else:
temp_contexts.append("Assistant: [未知的内容]")
contexts.insert(0, temp_contexts)
temp_contexts = []
# 展平 contexts 列表
contexts = [item for sublist in contexts for item in sublist]
# 计算分页
paged_contexts = contexts[(page - 1) * page_size : page * page_size]
total_pages = len(contexts) // page_size
if len(contexts) % page_size != 0:
total_pages += 1
return paged_contexts, total_pages

View File

@@ -0,0 +1,231 @@
"""
Astrbot 核心生命周期管理类, 负责管理 AstrBot 的启动、停止、重启等操作。
该类负责初始化各个组件, 包括 ProviderManager、PlatformManager、KnowledgeDBManager、ConversationManager、PluginManager、PipelineScheduler、EventBus等。
该类还负责加载和执行插件, 以及处理事件总线的分发。
工作流程:
1. 初始化所有组件
2. 启动事件总线和任务, 所有任务都在这里运行
3. 执行启动完成事件钩子
"""
import traceback
import asyncio
import time
import threading
import os
from .event_bus import EventBus
from . import astrbot_config
from asyncio import Queue
from typing import List
from astrbot.core.pipeline.scheduler import PipelineScheduler, PipelineContext
from astrbot.core.star import PluginManager
from astrbot.core.platform.manager import PlatformManager
from astrbot.core.star.context import Context
from astrbot.core.provider.manager import ProviderManager
from astrbot.core import LogBroker
from astrbot.core.db import BaseDatabase
from astrbot.core.updator import AstrBotUpdator
from astrbot.core import logger
from astrbot.core.config.default import VERSION
from astrbot.core.rag.knowledge_db_mgr import KnowledgeDBManager
from astrbot.core.conversation_mgr import ConversationManager
from astrbot.core.star.star_handler import star_handlers_registry, EventType
from astrbot.core.star.star_handler import star_map
class AstrBotCoreLifecycle:
"""
AstrBot 核心生命周期管理类, 负责管理 AstrBot 的启动、停止、重启等操作。
该类负责初始化各个组件, 包括 ProviderManager、PlatformManager、KnowledgeDBManager、ConversationManager、PluginManager、PipelineScheduler、
EventBus 等。
该类还负责加载和执行插件, 以及处理事件总线的分发。
"""
def __init__(self, log_broker: LogBroker, db: BaseDatabase):
self.log_broker = log_broker # 初始化日志代理
self.astrbot_config = astrbot_config # 初始化配置
self.db = db # 初始化数据库
# 根据环境变量设置代理
os.environ["https_proxy"] = self.astrbot_config["http_proxy"]
os.environ["http_proxy"] = self.astrbot_config["http_proxy"]
os.environ["no_proxy"] = "localhost"
async def initialize(self):
"""
初始化 AstrBot 核心生命周期管理类, 负责初始化各个组件, 包括 ProviderManager、PlatformManager、KnowledgeDBManager、ConversationManager、PluginManager、PipelineScheduler、EventBus、AstrBotUpdator等。
"""
# 初始化日志代理
logger.info("AstrBot v" + VERSION)
if os.environ.get("TESTING", ""):
logger.setLevel("DEBUG") # 测试模式下设置日志级别为 DEBUG
else:
logger.setLevel(self.astrbot_config["log_level"]) # 设置日志级别
# 初始化事件队列
self.event_queue = Queue()
# 初始化供应商管理器
self.provider_manager = ProviderManager(self.astrbot_config, self.db)
# 初始化平台管理器
self.platform_manager = PlatformManager(self.astrbot_config, self.event_queue)
# 初始化知识库管理器
self.knowledge_db_manager = KnowledgeDBManager(self.astrbot_config)
# 初始化对话管理器
self.conversation_manager = ConversationManager(self.db)
# 初始化提供给插件的上下文
self.star_context = Context(
self.event_queue,
self.astrbot_config,
self.db,
self.provider_manager,
self.platform_manager,
self.conversation_manager,
self.knowledge_db_manager,
)
# 初始化插件管理器
self.plugin_manager = PluginManager(self.star_context, self.astrbot_config)
# 扫描、注册插件、实例化插件类
await self.plugin_manager.reload()
# 根据配置实例化各个 Provider
await self.provider_manager.initialize()
# 初始化消息事件流水线调度器
self.pipeline_scheduler = PipelineScheduler(
PipelineContext(self.astrbot_config, self.plugin_manager)
)
await self.pipeline_scheduler.initialize()
# 初始化更新器
self.astrbot_updator = AstrBotUpdator()
# 初始化事件总线
self.event_bus = EventBus(self.event_queue, self.pipeline_scheduler)
# 记录启动时间
self.start_time = int(time.time())
# 初始化当前任务列表
self.curr_tasks: List[asyncio.Task] = []
# 根据配置实例化各个平台适配器
await self.platform_manager.initialize()
# 初始化关闭控制面板的事件
self.dashboard_shutdown_event = asyncio.Event()
def _load(self):
"""加载事件总线和任务并初始化"""
# 创建一个异步任务来执行事件总线的 dispatch() 方法
# dispatch是一个无限循环的协程, 从事件队列中获取事件并处理
event_bus_task = asyncio.create_task(
self.event_bus.dispatch(), name="event_bus"
)
# 把插件中注册的所有协程函数注册到事件总线中并执行
extra_tasks = []
for task in self.star_context._register_tasks:
extra_tasks.append(asyncio.create_task(task, name=task.__name__))
tasks_ = [event_bus_task, *extra_tasks]
for task in tasks_:
self.curr_tasks.append(
asyncio.create_task(self._task_wrapper(task), name=task.get_name())
)
self.start_time = int(time.time())
async def _task_wrapper(self, task: asyncio.Task):
"""异步任务包装器, 用于处理异步任务执行中出现的各种异常
Args:
task (asyncio.Task): 要执行的异步任务
"""
try:
await task
except asyncio.CancelledError:
pass # 任务被取消, 静默处理
except Exception as e:
# 获取完整的异常堆栈信息, 按行分割并记录到日志中
logger.error(f"------- 任务 {task.get_name()} 发生错误: {e}")
for line in traceback.format_exc().split("\n"):
logger.error(f"| {line}")
logger.error("-------")
async def start(self):
"""启动 AstrBot 核心生命周期管理类, 用load加载事件总线和任务并初始化, 执行启动完成事件钩子"""
self._load()
logger.info("AstrBot 启动完成。")
# 执行启动完成事件钩子
handlers = star_handlers_registry.get_handlers_by_event_type(
EventType.OnAstrBotLoadedEvent
)
for handler in handlers:
try:
logger.info(
f"hook(on_astrbot_loaded) -> {star_map[handler.handler_module_path].name} - {handler.handler_name}"
)
await handler.handler()
except BaseException:
logger.error(traceback.format_exc())
# 同时运行curr_tasks中的所有任务
await asyncio.gather(*self.curr_tasks, return_exceptions=True)
async def stop(self):
"""停止 AstrBot 核心生命周期管理类, 取消所有当前任务并终止各个管理器"""
# 请求停止所有正在运行的异步任务
for task in self.curr_tasks:
task.cancel()
for plugin in self.plugin_manager.context.get_all_stars():
try:
await self.plugin_manager._terminate_plugin(plugin)
except Exception as e:
logger.warning(traceback.format_exc())
logger.warning(
f"插件 {plugin.name} 未被正常终止 {e!s}, 可能会导致资源泄露等问题。"
)
await self.provider_manager.terminate()
await self.platform_manager.terminate()
self.dashboard_shutdown_event.set()
# 再次遍历curr_tasks等待每个任务真正结束
for task in self.curr_tasks:
try:
await task
except asyncio.CancelledError:
pass
except Exception as e:
logger.error(f"任务 {task.get_name()} 发生错误: {e}")
async def restart(self):
"""重启 AstrBot 核心生命周期管理类, 终止各个管理器并重新加载平台实例"""
await self.provider_manager.terminate()
await self.platform_manager.terminate()
self.dashboard_shutdown_event.set()
threading.Thread(
target=self.astrbot_updator._reboot, name="restart", daemon=True
).start()
def load_platform(self) -> List[asyncio.Task]:
"""加载平台实例并返回所有平台实例的异步任务列表"""
tasks = []
platform_insts = self.platform_manager.get_insts()
for platform_inst in platform_insts:
tasks.append(
asyncio.create_task(platform_inst.run(), name=platform_inst.meta().name)
)
return tasks

161
astrbot/core/db/__init__.py Normal file
View File

@@ -0,0 +1,161 @@
import abc
from dataclasses import dataclass
from typing import List, Dict, Any, Tuple
from astrbot.core.db.po import Stats, LLMHistory, ATRIVision, Conversation
@dataclass
class BaseDatabase(abc.ABC):
"""
数据库基类
"""
def __init__(self) -> None:
pass
def insert_base_metrics(self, metrics: dict):
"""插入基础指标数据"""
self.insert_platform_metrics(metrics["platform_stats"])
self.insert_plugin_metrics(metrics["plugin_stats"])
self.insert_command_metrics(metrics["command_stats"])
self.insert_llm_metrics(metrics["llm_stats"])
@abc.abstractmethod
def insert_platform_metrics(self, metrics: dict):
"""插入平台指标数据"""
raise NotImplementedError
@abc.abstractmethod
def insert_plugin_metrics(self, metrics: dict):
"""插入插件指标数据"""
raise NotImplementedError
@abc.abstractmethod
def insert_command_metrics(self, metrics: dict):
"""插入指令指标数据"""
raise NotImplementedError
@abc.abstractmethod
def insert_llm_metrics(self, metrics: dict):
"""插入 LLM 指标数据"""
raise NotImplementedError
@abc.abstractmethod
def update_llm_history(self, session_id: str, content: str, provider_type: str):
"""更新 LLM 历史记录。当不存在 session_id 时插入"""
raise NotImplementedError
@abc.abstractmethod
def get_llm_history(
self, session_id: str = None, provider_type: str = None
) -> List[LLMHistory]:
"""获取 LLM 历史记录, 如果 session_id 为 None, 返回所有"""
raise NotImplementedError
@abc.abstractmethod
def get_base_stats(self, offset_sec: int = 86400) -> Stats:
"""获取基础统计数据"""
raise NotImplementedError
@abc.abstractmethod
def get_total_message_count(self) -> int:
"""获取总消息数"""
raise NotImplementedError
@abc.abstractmethod
def get_grouped_base_stats(self, offset_sec: int = 86400) -> Stats:
"""获取基础统计数据(合并)"""
raise NotImplementedError
@abc.abstractmethod
def insert_atri_vision_data(self, vision_data: ATRIVision):
"""插入 ATRI 视觉数据"""
raise NotImplementedError
@abc.abstractmethod
def get_atri_vision_data(self) -> List[ATRIVision]:
"""获取 ATRI 视觉数据"""
raise NotImplementedError
@abc.abstractmethod
def get_atri_vision_data_by_path_or_id(
self, url_or_path: str, id: str
) -> ATRIVision:
"""通过 url 或 path 获取 ATRI 视觉数据"""
raise NotImplementedError
@abc.abstractmethod
def get_conversation_by_user_id(self, user_id: str, cid: str) -> Conversation:
"""通过 user_id 和 cid 获取 Conversation"""
raise NotImplementedError
@abc.abstractmethod
def new_conversation(self, user_id: str, cid: str):
"""新建 Conversation"""
raise NotImplementedError
@abc.abstractmethod
def get_conversations(self, user_id: str) -> List[Conversation]:
raise NotImplementedError
@abc.abstractmethod
def update_conversation(self, user_id: str, cid: str, history: str):
"""更新 Conversation"""
raise NotImplementedError
@abc.abstractmethod
def delete_conversation(self, user_id: str, cid: str):
"""删除 Conversation"""
raise NotImplementedError
@abc.abstractmethod
def update_conversation_title(self, user_id: str, cid: str, title: str):
"""更新 Conversation 标题"""
raise NotImplementedError
@abc.abstractmethod
def update_conversation_persona_id(self, user_id: str, cid: str, persona_id: str):
"""更新 Conversation Persona ID"""
raise NotImplementedError
@abc.abstractmethod
def get_all_conversations(
self, page: int = 1, page_size: int = 20
) -> Tuple[List[Dict[str, Any]], int]:
"""获取所有对话,支持分页
Args:
page: 页码从1开始
page_size: 每页数量
Returns:
Tuple[List[Dict[str, Any]], int]: 返回一个元组,包含对话列表和总对话数
"""
raise NotImplementedError
@abc.abstractmethod
def get_filtered_conversations(
self,
page: int = 1,
page_size: int = 20,
platforms: List[str] = None,
message_types: List[str] = None,
search_query: str = None,
exclude_ids: List[str] = None,
exclude_platforms: List[str] = None,
) -> Tuple[List[Dict[str, Any]], int]:
"""获取筛选后的对话列表
Args:
page: 页码
page_size: 每页数量
platforms: 平台筛选列表
message_types: 消息类型筛选列表
search_query: 搜索关键词
exclude_ids: 排除的用户ID列表
exclude_platforms: 排除的平台列表
Returns:
Tuple[List[Dict[str, Any]], int]: 返回一个元组,包含对话列表和总对话数
"""
raise NotImplementedError

View File

@@ -0,0 +1,112 @@
import json
import aiosqlite
import os
from typing import Any
from .plugin_storage import PluginStorage
DBPATH = "data/plugin_data/sqlite/plugin_data.db"
class SQLitePluginStorage(PluginStorage):
"""插件数据的 SQLite 存储实现类。
该类提供异步方式将插件数据存储到 SQLite 数据库中,支持数据的增删改查操作。
所有数据以 (plugin, key) 作为复合主键进行索引。
"""
_instance = None # Standalone instance of the class
_db_conn = None
db_path = None
def __new__(cls):
"""
创建或获取 SQLitePluginStorage 的单例实例。
如果实例已存在,则返回现有实例;否则创建一个新实例。
数据在 `data/plugin_data/sqlite/plugin_data.db` 下。
"""
os.makedirs(os.path.dirname(DBPATH), exist_ok=True)
if cls._instance is None:
cls._instance = super(SQLitePluginStorage, cls).__new__(cls)
cls._instance.db_path = DBPATH
return cls._instance
async def _init_db(self):
"""初始化数据库连接(只执行一次)"""
if SQLitePluginStorage._db_conn is None:
SQLitePluginStorage._db_conn = await aiosqlite.connect(self.db_path)
await self._setup_db()
async def _setup_db(self):
"""
异步初始化数据库。
创建插件数据表,如果表不存在则创建,表结构包含 plugin、key 和 value 字段,
其中 plugin 和 key 组合作为主键。
"""
await self._db_conn.execute("""
CREATE TABLE IF NOT EXISTS plugin_data (
plugin TEXT,
key TEXT,
value TEXT,
PRIMARY KEY (plugin, key)
)
""")
await self._db_conn.commit()
async def set(self, plugin: str, key: str, value: Any):
"""
异步存储数据。
将指定插件的键值对存入数据库,如果键已存在则更新值。
值会被序列化为 JSON 字符串后存储。
Args:
plugin: 插件标识符
key: 数据键名
value: 要存储的数据值(任意类型,将被 JSON 序列化)
"""
await self._init_db()
await self._db_conn.execute(
"INSERT INTO plugin_data (plugin, key, value) VALUES (?, ?, ?) "
"ON CONFLICT(plugin, key) DO UPDATE SET value = excluded.value",
(plugin, key, json.dumps(value)),
)
await self._db_conn.commit()
async def get(self, plugin: str, key: str) -> Any:
"""
异步获取数据。
从数据库中获取指定插件和键名对应的值,
返回的值会从 JSON 字符串反序列化为原始数据类型。
Args:
plugin: 插件标识符
key: 数据键名
Returns:
Any: 存储的数据值,如果未找到则返回 None
"""
await self._init_db()
async with self._db_conn.execute(
"SELECT value FROM plugin_data WHERE plugin = ? AND key = ?",
(plugin, key),
) as cursor:
row = await cursor.fetchone()
return json.loads(row[0]) if row else None
async def delete(self, plugin: str, key: str):
"""
异步删除数据。
从数据库中删除指定插件和键名对应的数据项。
Args:
plugin: 插件标识符
key: 要删除的数据键名
"""
await self._init_db()
await self._db_conn.execute(
"DELETE FROM plugin_data WHERE plugin = ? AND key = ?", (plugin, key)
)
await self._db_conn.commit()

89
astrbot/core/db/po.py Normal file
View File

@@ -0,0 +1,89 @@
"""指标数据"""
from dataclasses import dataclass, field
from typing import List
@dataclass
class Platform:
"""平台使用统计数据"""
name: str
count: int
timestamp: int
@dataclass
class Provider:
"""供应商使用统计数据"""
name: str
count: int
timestamp: int
@dataclass
class Plugin:
"""插件使用统计数据"""
name: str
count: int
timestamp: int
@dataclass
class Command:
"""命令使用统计数据"""
name: str
count: int
timestamp: int
@dataclass
class Stats:
platform: List[Platform] = field(default_factory=list)
command: List[Command] = field(default_factory=list)
llm: List[Provider] = field(default_factory=list)
@dataclass
class LLMHistory:
"""LLM 聊天时持久化的信息"""
provider_type: str
session_id: str
content: str
@dataclass
class ATRIVision:
"""Deprecated"""
id: str
url_or_path: str
caption: str
is_meme: bool
keywords: List[str]
platform_name: str
session_id: str
sender_nickname: str
timestamp: int = -1
@dataclass
class Conversation:
"""LLM 对话存储
对于网页聊天history 存储了包括指令、回复、图片等在内的所有消息。
对于其他平台的聊天,不存储非 LLM 的回复(因为考虑到已经存储在各自的平台上)。
"""
user_id: str
cid: str
history: str = ""
"""字符串格式的列表。"""
created_at: int = 0
updated_at: int = 0
title: str = ""
persona_id: str = ""

565
astrbot/core/db/sqlite.py Normal file
View File

@@ -0,0 +1,565 @@
import sqlite3
import os
import time
from astrbot.core.db.po import Platform, Stats, LLMHistory, ATRIVision, Conversation
from . import BaseDatabase
from typing import Tuple, List, Dict, Any
class SQLiteDatabase(BaseDatabase):
def __init__(self, db_path: str) -> None:
super().__init__()
self.db_path = db_path
with open(os.path.dirname(__file__) + "/sqlite_init.sql", "r") as f:
sql = f.read()
# 初始化数据库
self.conn = self._get_conn(self.db_path)
c = self.conn.cursor()
c.executescript(sql)
self.conn.commit()
# 检查 webchat_conversation 的 title 字段是否存在
c.execute(
"""
PRAGMA table_info(webchat_conversation)
"""
)
res = c.fetchall()
has_title = False
has_persona_id = False
for row in res:
if row[1] == "title":
has_title = True
if row[1] == "persona_id":
has_persona_id = True
if not has_title:
c.execute(
"""
ALTER TABLE webchat_conversation ADD COLUMN title TEXT;
"""
)
self.conn.commit()
if not has_persona_id:
c.execute(
"""
ALTER TABLE webchat_conversation ADD COLUMN persona_id TEXT;
"""
)
self.conn.commit()
c.close()
def _get_conn(self, db_path: str) -> sqlite3.Connection:
conn = sqlite3.connect(self.db_path)
conn.text_factory = str
return conn
def _exec_sql(self, sql: str, params: Tuple = None):
conn = self.conn
try:
c = self.conn.cursor()
except sqlite3.ProgrammingError:
conn = self._get_conn(self.db_path)
c = conn.cursor()
if params:
c.execute(sql, params)
c.close()
else:
c.execute(sql)
c.close()
conn.commit()
def insert_platform_metrics(self, metrics: dict):
for k, v in metrics.items():
self._exec_sql(
"""
INSERT INTO platform(name, count, timestamp) VALUES (?, ?, ?)
""",
(k, v, int(time.time())),
)
def insert_plugin_metrics(self, metrics: dict):
pass
def insert_command_metrics(self, metrics: dict):
for k, v in metrics.items():
self._exec_sql(
"""
INSERT INTO command(name, count, timestamp) VALUES (?, ?, ?)
""",
(k, v, int(time.time())),
)
def insert_llm_metrics(self, metrics: dict):
for k, v in metrics.items():
self._exec_sql(
"""
INSERT INTO llm(name, count, timestamp) VALUES (?, ?, ?)
""",
(k, v, int(time.time())),
)
def update_llm_history(self, session_id: str, content: str, provider_type: str):
res = self.get_llm_history(session_id, provider_type)
if res:
self._exec_sql(
"""
UPDATE llm_history SET content = ? WHERE session_id = ? AND provider_type = ?
""",
(content, session_id, provider_type),
)
else:
self._exec_sql(
"""
INSERT INTO llm_history(provider_type, session_id, content) VALUES (?, ?, ?)
""",
(provider_type, session_id, content),
)
def get_llm_history(
self, session_id: str = None, provider_type: str = None
) -> Tuple:
try:
c = self.conn.cursor()
except sqlite3.ProgrammingError:
c = self._get_conn(self.db_path).cursor()
conditions = []
params = []
if session_id:
conditions.append("session_id = ?")
params.append(session_id)
if provider_type:
conditions.append("provider_type = ?")
params.append(provider_type)
sql = "SELECT * FROM llm_history"
if conditions:
sql += " WHERE " + " AND ".join(conditions)
c.execute(sql, params)
res = c.fetchall()
histories = []
for row in res:
histories.append(LLMHistory(*row))
c.close()
return histories
def get_base_stats(self, offset_sec: int = 86400) -> Stats:
"""获取 offset_sec 秒前到现在的基础统计数据"""
where_clause = f" WHERE timestamp >= {int(time.time()) - offset_sec}"
try:
c = self.conn.cursor()
except sqlite3.ProgrammingError:
c = self._get_conn(self.db_path).cursor()
c.execute(
"""
SELECT * FROM platform
"""
+ where_clause
)
platform = []
for row in c.fetchall():
platform.append(Platform(*row))
# c.execute(
# '''
# SELECT * FROM command
# ''' + where_clause
# )
# command = []
# for row in c.fetchall():
# command.append(Command(*row))
# c.execute(
# '''
# SELECT * FROM llm
# ''' + where_clause
# )
# llm = []
# for row in c.fetchall():
# llm.append(Provider(*row))
c.close()
return Stats(platform, [], [])
def get_total_message_count(self) -> int:
try:
c = self.conn.cursor()
except sqlite3.ProgrammingError:
c = self._get_conn(self.db_path).cursor()
c.execute(
"""
SELECT SUM(count) FROM platform
"""
)
res = c.fetchone()
c.close()
return res[0]
def get_grouped_base_stats(self, offset_sec: int = 86400) -> Stats:
"""获取 offset_sec 秒前到现在的基础统计数据(合并)"""
where_clause = f" WHERE timestamp >= {int(time.time()) - offset_sec}"
try:
c = self.conn.cursor()
except sqlite3.ProgrammingError:
c = self._get_conn(self.db_path).cursor()
c.execute(
"""
SELECT name, SUM(count), timestamp FROM platform
"""
+ where_clause
+ " GROUP BY name"
)
platform = []
for row in c.fetchall():
platform.append(Platform(*row))
c.close()
return Stats(platform, [], [])
def get_conversation_by_user_id(self, user_id: str, cid: str) -> Conversation:
try:
c = self.conn.cursor()
except sqlite3.ProgrammingError:
c = self._get_conn(self.db_path).cursor()
c.execute(
"""
SELECT * FROM webchat_conversation WHERE user_id = ? AND cid = ?
""",
(user_id, cid),
)
res = c.fetchone()
c.close()
if not res:
return
return Conversation(*res)
def new_conversation(self, user_id: str, cid: str):
history = "[]"
updated_at = int(time.time())
created_at = updated_at
self._exec_sql(
"""
INSERT INTO webchat_conversation(user_id, cid, history, updated_at, created_at) VALUES (?, ?, ?, ?, ?)
""",
(user_id, cid, history, updated_at, created_at),
)
def get_conversations(self, user_id: str) -> Tuple:
try:
c = self.conn.cursor()
except sqlite3.ProgrammingError:
c = self._get_conn(self.db_path).cursor()
c.execute(
"""
SELECT cid, created_at, updated_at, title, persona_id FROM webchat_conversation WHERE user_id = ? ORDER BY updated_at DESC
""",
(user_id,),
)
res = c.fetchall()
c.close()
conversations = []
for row in res:
cid = row[0]
created_at = row[1]
updated_at = row[2]
title = row[3]
persona_id = row[4]
conversations.append(
Conversation("", cid, "[]", created_at, updated_at, title, persona_id)
)
return conversations
def update_conversation(self, user_id: str, cid: str, history: str):
"""更新对话,并且同时更新时间"""
updated_at = int(time.time())
self._exec_sql(
"""
UPDATE webchat_conversation SET history = ?, updated_at = ? WHERE user_id = ? AND cid = ?
""",
(history, updated_at, user_id, cid),
)
def update_conversation_title(self, user_id: str, cid: str, title: str):
self._exec_sql(
"""
UPDATE webchat_conversation SET title = ? WHERE user_id = ? AND cid = ?
""",
(title, user_id, cid),
)
def update_conversation_persona_id(self, user_id: str, cid: str, persona_id: str):
self._exec_sql(
"""
UPDATE webchat_conversation SET persona_id = ? WHERE user_id = ? AND cid = ?
""",
(persona_id, user_id, cid),
)
def delete_conversation(self, user_id: str, cid: str):
self._exec_sql(
"""
DELETE FROM webchat_conversation WHERE user_id = ? AND cid = ?
""",
(user_id, cid),
)
def insert_atri_vision_data(self, vision: ATRIVision):
ts = int(time.time())
keywords = ",".join(vision.keywords)
self._exec_sql(
"""
INSERT INTO atri_vision(id, url_or_path, caption, is_meme, keywords, platform_name, session_id, sender_nickname, timestamp) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
""",
(
vision.id,
vision.url_or_path,
vision.caption,
vision.is_meme,
keywords,
vision.platform_name,
vision.session_id,
vision.sender_nickname,
ts,
),
)
def get_atri_vision_data(self) -> Tuple:
try:
c = self.conn.cursor()
except sqlite3.ProgrammingError:
c = self._get_conn(self.db_path).cursor()
c.execute(
"""
SELECT * FROM atri_vision
"""
)
res = c.fetchall()
visions = []
for row in res:
visions.append(ATRIVision(*row))
c.close()
return visions
def get_atri_vision_data_by_path_or_id(
self, url_or_path: str, id: str
) -> ATRIVision:
try:
c = self.conn.cursor()
except sqlite3.ProgrammingError:
c = self._get_conn(self.db_path).cursor()
c.execute(
"""
SELECT * FROM atri_vision WHERE url_or_path = ? OR id = ?
""",
(url_or_path, id),
)
res = c.fetchone()
c.close()
if res:
return ATRIVision(*res)
return None
def get_all_conversations(
self, page: int = 1, page_size: int = 20
) -> Tuple[List[Dict[str, Any]], int]:
"""获取所有对话,支持分页,按更新时间降序排序"""
try:
c = self.conn.cursor()
except sqlite3.ProgrammingError:
c = self._get_conn(self.db_path).cursor()
try:
# 获取总记录数
c.execute("""
SELECT COUNT(*) FROM webchat_conversation
""")
total_count = c.fetchone()[0]
# 计算偏移量
offset = (page - 1) * page_size
# 获取分页数据,按更新时间降序排序
c.execute(
"""
SELECT user_id, cid, created_at, updated_at, title, persona_id
FROM webchat_conversation
ORDER BY updated_at DESC
LIMIT ? OFFSET ?
""",
(page_size, offset),
)
rows = c.fetchall()
conversations = []
for row in rows:
user_id, cid, created_at, updated_at, title, persona_id = row
# 确保 cid 是字符串类型且至少有8个字符否则使用一个默认值
safe_cid = str(cid) if cid else "unknown"
display_cid = safe_cid[:8] if len(safe_cid) >= 8 else safe_cid
conversations.append(
{
"user_id": user_id or "",
"cid": safe_cid,
"title": title or f"对话 {display_cid}",
"persona_id": persona_id or "",
"created_at": created_at or 0,
"updated_at": updated_at or 0,
}
)
return conversations, total_count
except Exception as _:
# 返回空列表和0确保即使出错也有有效的返回值
return [], 0
finally:
c.close()
def get_filtered_conversations(
self,
page: int = 1,
page_size: int = 20,
platforms: List[str] = None,
message_types: List[str] = None,
search_query: str = None,
exclude_ids: List[str] = None,
exclude_platforms: List[str] = None,
) -> Tuple[List[Dict[str, Any]], int]:
"""获取筛选后的对话列表"""
try:
c = self.conn.cursor()
except sqlite3.ProgrammingError:
c = self._get_conn(self.db_path).cursor()
try:
# 构建查询条件
where_clauses = []
params = []
# 平台筛选
if platforms and len(platforms) > 0:
platform_conditions = []
for platform in platforms:
platform_conditions.append("user_id LIKE ?")
params.append(f"{platform}:%")
if platform_conditions:
where_clauses.append(f"({' OR '.join(platform_conditions)})")
# 消息类型筛选
if message_types and len(message_types) > 0:
message_type_conditions = []
for msg_type in message_types:
message_type_conditions.append("user_id LIKE ?")
params.append(f"%:{msg_type}:%")
if message_type_conditions:
where_clauses.append(f"({' OR '.join(message_type_conditions)})")
# 搜索关键词
if search_query:
search_query = search_query.encode("unicode_escape").decode("utf-8")
where_clauses.append(
"(title LIKE ? OR user_id LIKE ? OR cid LIKE ? OR history LIKE ?)"
)
search_param = f"%{search_query}%"
params.extend([search_param, search_param, search_param, search_param])
# 排除特定用户ID
if exclude_ids and len(exclude_ids) > 0:
for exclude_id in exclude_ids:
where_clauses.append("user_id NOT LIKE ?")
params.append(f"{exclude_id}%")
# 排除特定平台
if exclude_platforms and len(exclude_platforms) > 0:
for exclude_platform in exclude_platforms:
where_clauses.append("user_id NOT LIKE ?")
params.append(f"{exclude_platform}:%")
# 构建完整的 WHERE 子句
where_sql = " WHERE " + " AND ".join(where_clauses) if where_clauses else ""
# 构建计数查询
count_sql = f"SELECT COUNT(*) FROM webchat_conversation{where_sql}"
# 获取总记录数
c.execute(count_sql, params)
total_count = c.fetchone()[0]
# 计算偏移量
offset = (page - 1) * page_size
# 构建分页数据查询
data_sql = f"""
SELECT user_id, cid, created_at, updated_at, title, persona_id
FROM webchat_conversation
{where_sql}
ORDER BY updated_at DESC
LIMIT ? OFFSET ?
"""
query_params = params + [page_size, offset]
# 获取分页数据
c.execute(data_sql, query_params)
rows = c.fetchall()
conversations = []
for row in rows:
user_id, cid, created_at, updated_at, title, persona_id = row
# 确保 cid 是字符串类型,否则使用一个默认值
safe_cid = str(cid) if cid else "unknown"
display_cid = safe_cid[:8] if len(safe_cid) >= 8 else safe_cid
conversations.append(
{
"user_id": user_id or "",
"cid": safe_cid,
"title": title or f"对话 {display_cid}",
"persona_id": persona_id or "",
"created_at": created_at or 0,
"updated_at": updated_at or 0,
}
)
return conversations, total_count
except Exception as _:
# 返回空列表和0确保即使出错也有有效的返回值
return [], 0
finally:
c.close()

View File

@@ -0,0 +1,50 @@
CREATE TABLE IF NOT EXISTS platform(
name VARCHAR(32),
count INTEGER,
timestamp INTEGER
);
CREATE TABLE IF NOT EXISTS llm(
name VARCHAR(32),
count INTEGER,
timestamp INTEGER
);
CREATE TABLE IF NOT EXISTS plugin(
name VARCHAR(32),
count INTEGER,
timestamp INTEGER
);
CREATE TABLE IF NOT EXISTS command(
name VARCHAR(32),
count INTEGER,
timestamp INTEGER
);
CREATE TABLE IF NOT EXISTS llm_history(
provider_type VARCHAR(32),
session_id VARCHAR(32),
content TEXT
);
-- ATRI
CREATE TABLE IF NOT EXISTS atri_vision(
id TEXT,
url_or_path TEXT,
caption TEXT,
is_meme BOOLEAN,
keywords TEXT,
platform_name VARCHAR(32),
session_id VARCHAR(32),
sender_nickname VARCHAR(32),
timestamp INTEGER
);
CREATE TABLE IF NOT EXISTS webchat_conversation(
user_id TEXT, -- 会话 id
cid TEXT, -- 对话 id
history TEXT,
created_at INTEGER,
updated_at INTEGER,
title TEXT,
persona_id TEXT
);
PRAGMA encoding = 'UTF-8';

57
astrbot/core/event_bus.py Normal file
View File

@@ -0,0 +1,57 @@
"""
事件总线, 用于处理事件的分发和处理
事件总线是一个异步队列, 用于接收各种消息事件, 并将其发送到Scheduler调度器进行处理
其中包含了一个无限循环的调度函数, 用于从事件队列中获取新的事件, 并创建一个新的异步任务来执行管道调度器的处理逻辑
class:
EventBus: 事件总线, 用于处理事件的分发和处理
工作流程:
1. 维护一个异步队列, 来接受各种消息事件
2. 无限循环的调度函数, 从事件队列中获取新的事件, 打印日志并创建一个新的异步任务来执行管道调度器的处理逻辑
"""
import asyncio
from asyncio import Queue
from astrbot.core.pipeline.scheduler import PipelineScheduler
from astrbot.core import logger
from .platform import AstrMessageEvent
class EventBus:
"""事件总线: 用于处理事件的分发和处理
维护一个异步队列, 来接受各种消息事件
"""
def __init__(self, event_queue: Queue, pipeline_scheduler: PipelineScheduler):
self.event_queue = event_queue # 事件队列
self.pipeline_scheduler = pipeline_scheduler # 管道调度器
async def dispatch(self):
"""无限循环的调度函数, 从事件队列中获取新的事件, 打印日志并创建一个新的异步任务来执行管道调度器的处理逻辑"""
while True:
event: AstrMessageEvent = (
await self.event_queue.get()
) # 从事件队列中获取新的事件
self._print_event(event) # 打印日志
asyncio.create_task(
self.pipeline_scheduler.execute(event)
) # 创建新的异步任务来执行管道调度器的处理逻辑
def _print_event(self, event: AstrMessageEvent):
"""用于记录事件信息
Args:
event (AstrMessageEvent): 事件对象
"""
# 如果有发送者名称: [平台名] 发送者名称/发送者ID: 消息概要
if event.get_sender_name():
logger.info(
f"[{event.get_platform_name()}] {event.get_sender_name()}/{event.get_sender_id()}: {event.get_message_outline()}"
)
# 没有发送者名称: [平台名] 发送者ID: 消息概要
else:
logger.info(
f"[{event.get_platform_name()}] {event.get_sender_id()}: {event.get_message_outline()}"
)

View File

@@ -0,0 +1,48 @@
"""
AstrBot 启动器,负责初始化和启动核心组件和仪表板服务器。
工作流程:
1. 初始化核心生命周期, 传递数据库和日志代理实例到核心生命周期
2. 运行核心生命周期任务和仪表板服务器
"""
import asyncio
import traceback
from astrbot.core import logger
from astrbot.core.core_lifecycle import AstrBotCoreLifecycle
from astrbot.core.db import BaseDatabase
from astrbot.core import LogBroker
from astrbot.dashboard.server import AstrBotDashboard
class InitialLoader:
"""AstrBot 启动器,负责初始化和启动核心组件和仪表板服务器。"""
def __init__(self, db: BaseDatabase, log_broker: LogBroker):
self.db = db
self.logger = logger
self.log_broker = log_broker
async def start(self):
core_lifecycle = AstrBotCoreLifecycle(self.log_broker, self.db)
core_task = []
try:
await core_lifecycle.initialize()
core_task = core_lifecycle.start()
except Exception as e:
logger.critical(traceback.format_exc())
logger.critical(f"😭 初始化 AstrBot 失败:{e} !!!")
self.dashboard_server = AstrBotDashboard(
core_lifecycle, self.db, core_lifecycle.dashboard_shutdown_event
)
task = asyncio.gather(
core_task, self.dashboard_server.run()
) # 启动核心任务和仪表板服务器
try:
await task # 整个AstrBot在这里运行
except asyncio.CancelledError:
logger.info("🌈 正在关闭 AstrBot...")
await core_lifecycle.stop()

248
astrbot/core/log.py Normal file
View File

@@ -0,0 +1,248 @@
"""
日志系统, 用于支持核心组件和插件的日志记录, 提供了日志订阅功能
const:
CACHED_SIZE: 日志缓存大小, 用于限制缓存的日志数量
log_color_config: 日志颜色配置, 定义了不同日志级别的颜色
class:
LogBroker: 日志代理类, 用于缓存和分发日志消息
LogQueueHandler: 日志处理器, 用于将日志消息发送到 LogBroker
LogManager: 日志管理器, 用于创建和配置日志记录器
function:
is_plugin_path: 检查文件路径是否来自插件目录
get_short_level_name: 将日志级别名称转换为四个字母的缩写
工作流程:
1. 通过 LogManager.GetLogger() 获取日志器, 配置了控制台输出和多个格式化过滤器
2. 通过 set_queue_handler() 设置日志处理器, 将日志消息发送到 LogBroker
3. logBroker 维护一个订阅者列表, 负责将日志分发给所有订阅者
4. 订阅者可以使用 register() 方法注册到 LogBroker, 订阅日志流
"""
import logging
import colorlog
import asyncio
import os
import sys
from collections import deque
from asyncio import Queue
from typing import List
# 日志缓存大小
CACHED_SIZE = 200
# 日志颜色配置
log_color_config = {
"DEBUG": "green",
"INFO": "bold_cyan",
"WARNING": "bold_yellow",
"ERROR": "red",
"CRITICAL": "bold_red",
"RESET": "reset",
"asctime": "green",
}
def is_plugin_path(pathname):
"""检查文件路径是否来自插件目录
Args:
pathname (str): 文件路径
Returns:
bool: 如果路径来自插件目录,则返回 True否则返回 False
"""
if not pathname:
return False
norm_path = os.path.normpath(pathname)
return ("data/plugins" in norm_path) or ("packages/" in norm_path)
def get_short_level_name(level_name):
"""将日志级别名称转换为四个字母的缩写
Args:
level_name (str): 日志级别名称, 如 "DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"
Returns:
str: 四个字母的日志级别缩写
"""
level_map = {
"DEBUG": "DBUG",
"INFO": "INFO",
"WARNING": "WARN",
"ERROR": "ERRO",
"CRITICAL": "CRIT",
}
return level_map.get(level_name, level_name[:4].upper())
class LogBroker:
"""日志代理类, 用于缓存和分发日志消息
发布-订阅模式
"""
def __init__(self):
self.log_cache = deque(maxlen=CACHED_SIZE) # 环形缓冲区, 保存最近的日志
self.subscribers: List[Queue] = [] # 订阅者列表
def register(self) -> Queue:
"""注册新的订阅者, 并给每个订阅者返回一个带有日志缓存的队列
Returns:
Queue: 订阅者的队列, 可用于接收日志消息
"""
q = Queue(maxsize=CACHED_SIZE + 10)
for log in self.log_cache:
q.put_nowait(log)
self.subscribers.append(q)
return q
def unregister(self, q: Queue):
"""取消订阅
Args:
q (Queue): 需要取消订阅的队列
"""
self.subscribers.remove(q)
def publish(self, log_entry: dict):
"""发布新日志到所有订阅者, 使用非阻塞方式投递, 避免一个订阅者阻塞整个系统
Args:
log_entry (dict): 日志消息, 包含日志级别和日志内容.
example: {"level": "INFO", "data": "This is a log message.", "time": "2023-10-01 12:00:00"}
"""
self.log_cache.append(log_entry)
for q in self.subscribers:
try:
q.put_nowait(log_entry)
except asyncio.QueueFull:
pass
class LogQueueHandler(logging.Handler):
"""日志处理器, 用于将日志消息发送到 LogBroker
继承自 logging.Handler
"""
def __init__(self, log_broker: LogBroker):
super().__init__()
self.log_broker = log_broker
def emit(self, record):
"""日志处理的入口方法, 接受一个日志记录, 转换为字符串后由 LogBroker 发布
这个方法会在每次日志记录时被调用
Args:
record (logging.LogRecord): 日志记录对象, 包含日志信息
"""
log_entry = self.format(record)
self.log_broker.publish(
{
"level": record.levelname,
"time": record.asctime,
"data": log_entry,
}
)
class LogManager:
"""日志管理器, 用于创建和配置日志记录器
提供了获取默认日志记录器logger和设置队列处理器的方法
"""
@classmethod
def GetLogger(cls, log_name: str = "default"):
"""获取指定名称的日志记录器logger
Args:
log_name (str): 日志记录器的名称, 默认为 "default"
Returns:
logging.Logger: 返回配置好的日志记录器
"""
logger = logging.getLogger(log_name)
# 检查该logger或父级logger是否已经有处理器, 如果已经有处理器, 直接返回该logger, 避免重复配置
if logger.hasHandlers():
return logger
# 如果logger没有处理器
console_handler = logging.StreamHandler(
sys.stdout
) # 创建一个StreamHandler用于控制台输出
console_handler.setLevel(
logging.DEBUG
) # 将日志级别设置为DEBUG(最低级别, 显示所有日志), *如果插件没有设置级别, 默认为DEBUG
# 创建彩色日志格式化器, 输出日志格式为: [时间] [插件标签] [日志级别] [文件名:行号]: 日志消息
console_formatter = colorlog.ColoredFormatter(
fmt="%(log_color)s [%(asctime)s] %(plugin_tag)s [%(short_levelname)-4s] [%(filename)s:%(lineno)d]: %(message)s %(reset)s",
datefmt="%H:%M:%S",
log_colors=log_color_config,
)
class PluginFilter(logging.Filter):
"""插件过滤器类, 用于标记日志来源是插件还是核心组件"""
def filter(self, record):
record.plugin_tag = (
"[Plug]" if is_plugin_path(record.pathname) else "[Core]"
)
return True
class FileNameFilter(logging.Filter):
"""文件名过滤器类, 用于修改日志记录的文件名格式
例如: 将文件路径 /path/to/file.py 转换为 file.<file> 格式"""
# 获取这个文件和父文件夹的名字:<folder>.<file> 并且去除 .py
def filter(self, record):
dirname = os.path.dirname(record.pathname)
record.filename = (
os.path.basename(dirname)
+ "."
+ os.path.basename(record.pathname).replace(".py", "")
)
return True
class LevelNameFilter(logging.Filter):
"""短日志级别名称过滤器类, 用于将日志级别名称转换为四个字母的缩写"""
# 添加短日志级别名称
def filter(self, record):
record.short_levelname = get_short_level_name(record.levelname)
return True
console_handler.setFormatter(console_formatter) # 设置处理器的格式化器
logger.addFilter(PluginFilter()) # 添加插件过滤器
logger.addFilter(FileNameFilter()) # 添加文件名过滤器
logger.addFilter(LevelNameFilter()) # 添加级别名称过滤器
logger.setLevel(logging.DEBUG) # 设置日志级别为DEBUG
logger.addHandler(console_handler) # 添加处理器到logger
return logger
@classmethod
def set_queue_handler(cls, logger: logging.Logger, log_broker: LogBroker):
"""设置队列处理器, 用于将日志消息发送到 LogBroker
Args:
logger (logging.Logger): 日志记录器
log_broker (LogBroker): 日志代理类, 用于缓存和分发日志消息
"""
handler = LogQueueHandler(log_broker)
handler.setLevel(logging.DEBUG)
if logger.handlers:
handler.setFormatter(logger.handlers[0].formatter)
else:
# 为队列处理器设置相同格式的formatter
handler.setFormatter(
logging.Formatter(
"[%(asctime)s] [%(short_levelname)s] %(plugin_tag)s[%(filename)s:%(lineno)d]: %(message)s"
)
)
logger.addHandler(handler)

View File

@@ -0,0 +1,607 @@
"""
MIT License
Copyright (c) 2021 Lxns-Network
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""
import base64
import json
import os
import uuid
import typing as T
from enum import Enum
from pydantic.v1 import BaseModel
from astrbot.core.utils.io import download_image_by_url, file_to_base64
class ComponentType(Enum):
Plain = "Plain" # 纯文本消息
Face = "Face" # QQ表情
Record = "Record" # 语音
Video = "Video" # 视频
At = "At" # At
Node = "Node" # 转发消息的一个节点
Nodes = "Nodes" # 转发消息的多个节点
Poke = "Poke" # QQ 戳一戳
Image = "Image" # 图片
Reply = "Reply" # 回复
Forward = "Forward" # 转发消息
File = "File" # 文件
RPS = "RPS" # TODO
Dice = "Dice" # TODO
Shake = "Shake" # TODO
Anonymous = "Anonymous" # TODO
Share = "Share"
Contact = "Contact" # TODO
Location = "Location" # TODO
Music = "Music"
RedBag = "RedBag"
Xml = "Xml"
Json = "Json"
CardImage = "CardImage"
TTS = "TTS"
Unknown = "Unknown"
WechatEmoji = "WechatEmoji" # Wechat 下的 emoji 表情包
class BaseMessageComponent(BaseModel):
type: ComponentType
def toString(self):
output = f"[CQ:{self.type.lower()}"
for k, v in self.__dict__.items():
if k == "type" or v is None:
continue
if k == "_type":
k = "type"
if isinstance(v, bool):
v = 1 if v else 0
output += ",%s=%s" % (
k,
str(v)
.replace("&", "&amp;")
.replace(",", "&#44;")
.replace("[", "&#91;")
.replace("]", "&#93;"),
)
output += "]"
return output
def toDict(self):
data = {}
for k, v in self.__dict__.items():
if k == "type" or v is None:
continue
if k == "_type":
k = "type"
data[k] = v
return {"type": self.type.lower(), "data": data}
class Plain(BaseMessageComponent):
type: ComponentType = "Plain"
text: str
convert: T.Optional[bool] = True # 若为 False 则直接发送未转换 CQ 码的消息
def __init__(self, text: str, convert: bool = True, **_):
super().__init__(text=text, convert=convert, **_)
def toString(self): # 没有 [CQ:plain] 这种东西,所以直接导出纯文本
if not self.convert:
return self.text
return (
self.text.replace("&", "&amp;").replace("[", "&#91;").replace("]", "&#93;")
)
class Face(BaseMessageComponent):
type: ComponentType = "Face"
id: int
def __init__(self, **_):
super().__init__(**_)
class Record(BaseMessageComponent):
type: ComponentType = "Record"
file: T.Optional[str] = ""
magic: T.Optional[bool] = False
url: T.Optional[str] = ""
cache: T.Optional[bool] = True
proxy: T.Optional[bool] = True
timeout: T.Optional[int] = 0
# 额外
path: T.Optional[str]
def __init__(self, file: T.Optional[str], **_):
for k in _.keys():
if k == "url":
pass
# Protocol.warn(f"go-cqhttp doesn't support send {self.type} by {k}")
super().__init__(file=file, **_)
@staticmethod
def fromFileSystem(path, **_):
return Record(file=f"file:///{os.path.abspath(path)}", path=path, **_)
@staticmethod
def fromURL(url: str, **_):
if url.startswith("http://") or url.startswith("https://"):
return Record(file=url, **_)
raise Exception("not a valid url")
async def convert_to_file_path(self) -> str:
"""将这个语音统一转换为本地文件路径。这个方法避免了手动判断语音数据类型,直接返回语音数据的本地路径(如果是网络 URL, 则会自动进行下载)。
Returns:
str: 语音的本地路径,以绝对路径表示。
"""
if self.file and self.file.startswith("file:///"):
file_path = self.file[8:]
return file_path
elif self.file and self.file.startswith("http"):
file_path = await download_image_by_url(self.file)
return os.path.abspath(file_path)
elif self.file and self.file.startswith("base64://"):
bs64_data = self.file.removeprefix("base64://")
image_bytes = base64.b64decode(bs64_data)
file_path = f"data/temp/{uuid.uuid4()}.jpg"
with open(file_path, "wb") as f:
f.write(image_bytes)
return os.path.abspath(file_path)
elif os.path.exists(self.file):
file_path = self.file
return os.path.abspath(file_path)
else:
raise Exception(f"not a valid file: {self.file}")
async def convert_to_base64(self) -> str:
"""将语音统一转换为 base64 编码。这个方法避免了手动判断语音数据类型,直接返回语音数据的 base64 编码。
Returns:
str: 语音的 base64 编码,不以 base64:// 或者 data:image/jpeg;base64, 开头。
"""
# convert to base64
if self.file and self.file.startswith("file:///"):
bs64_data = file_to_base64(self.file[8:])
elif self.file and self.file.startswith("http"):
file_path = await download_image_by_url(self.file)
bs64_data = file_to_base64(file_path)
elif self.file and self.file.startswith("base64://"):
bs64_data = self.file
elif os.path.exists(self.file):
bs64_data = file_to_base64(self.file)
else:
raise Exception(f"not a valid file: {self.file}")
bs64_data = bs64_data.removeprefix("base64://")
return bs64_data
class Video(BaseMessageComponent):
type: ComponentType = "Video"
file: str
cover: T.Optional[str] = ""
c: T.Optional[int] = 2
# 额外
path: T.Optional[str] = ""
def __init__(self, file: str, **_):
# for k in _.keys():
# if k == "c" and _[k] not in [2, 3]:
# logger.warn(f"Protocol: {k}={_[k]} doesn't match values")
super().__init__(file=file, **_)
@staticmethod
def fromFileSystem(path, **_):
return Video(file=f"file:///{os.path.abspath(path)}", path=path, **_)
@staticmethod
def fromURL(url: str, **_):
if url.startswith("http://") or url.startswith("https://"):
return Video(file=url, **_)
raise Exception("not a valid url")
class At(BaseMessageComponent):
type: ComponentType = "At"
qq: T.Union[int, str] # 此处str为all时代表所有人
name: T.Optional[str] = ""
def __init__(self, **_):
super().__init__(**_)
class AtAll(At):
qq: str = "all"
def __init__(self, **_):
super().__init__(**_)
class RPS(BaseMessageComponent): # TODO
type: ComponentType = "RPS"
def __init__(self, **_):
super().__init__(**_)
class Dice(BaseMessageComponent): # TODO
type: ComponentType = "Dice"
def __init__(self, **_):
super().__init__(**_)
class Shake(BaseMessageComponent): # TODO
type: ComponentType = "Shake"
def __init__(self, **_):
super().__init__(**_)
class Anonymous(BaseMessageComponent): # TODO
type: ComponentType = "Anonymous"
ignore: T.Optional[bool] = False
def __init__(self, **_):
super().__init__(**_)
class Share(BaseMessageComponent):
type: ComponentType = "Share"
url: str
title: str
content: T.Optional[str] = ""
image: T.Optional[str] = ""
def __init__(self, **_):
super().__init__(**_)
class Contact(BaseMessageComponent): # TODO
type: ComponentType = "Contact"
_type: str # type 字段冲突
id: T.Optional[int] = 0
def __init__(self, **_):
super().__init__(**_)
class Location(BaseMessageComponent): # TODO
type: ComponentType = "Location"
lat: float
lon: float
title: T.Optional[str] = ""
content: T.Optional[str] = ""
def __init__(self, **_):
super().__init__(**_)
class Music(BaseMessageComponent):
type: ComponentType = "Music"
_type: str
id: T.Optional[int] = 0
url: T.Optional[str] = ""
audio: T.Optional[str] = ""
title: T.Optional[str] = ""
content: T.Optional[str] = ""
image: T.Optional[str] = ""
def __init__(self, **_):
# for k in _.keys():
# if k == "_type" and _[k] not in ["qq", "163", "xm", "custom"]:
# logger.warn(f"Protocol: {k}={_[k]} doesn't match values")
super().__init__(**_)
class Image(BaseMessageComponent):
type: ComponentType = "Image"
file: T.Optional[str] = ""
_type: T.Optional[str] = ""
subType: T.Optional[int] = 0
url: T.Optional[str] = ""
cache: T.Optional[bool] = True
id: T.Optional[int] = 40000
c: T.Optional[int] = 2
# 额外
path: T.Optional[str] = ""
file_unique: T.Optional[str] = "" # 某些平台可能有图片缓存的唯一标识
def __init__(self, file: T.Optional[str], **_):
super().__init__(file=file, **_)
@staticmethod
def fromURL(url: str, **_):
if url.startswith("http://") or url.startswith("https://"):
return Image(file=url, **_)
raise Exception("not a valid url")
@staticmethod
def fromFileSystem(path, **_):
return Image(file=f"file:///{os.path.abspath(path)}", path=path, **_)
@staticmethod
def fromBase64(base64: str, **_):
return Image(f"base64://{base64}", **_)
@staticmethod
def fromBytes(byte: bytes):
return Image.fromBase64(base64.b64encode(byte).decode())
@staticmethod
def fromIO(IO):
return Image.fromBytes(IO.read())
async def convert_to_file_path(self) -> str:
"""将这个图片统一转换为本地文件路径。这个方法避免了手动判断图片数据类型,直接返回图片数据的本地路径(如果是网络 URL, 则会自动进行下载)。
Returns:
str: 图片的本地路径,以绝对路径表示。
"""
url = self.url if self.url else self.file
if url and url.startswith("file:///"):
image_file_path = url[8:]
return image_file_path
elif url and url.startswith("http"):
image_file_path = await download_image_by_url(url)
return os.path.abspath(image_file_path)
elif url and url.startswith("base64://"):
bs64_data = url.removeprefix("base64://")
image_bytes = base64.b64decode(bs64_data)
image_file_path = f"data/temp/{uuid.uuid4()}.jpg"
with open(image_file_path, "wb") as f:
f.write(image_bytes)
return os.path.abspath(image_file_path)
elif os.path.exists(url):
image_file_path = url
return os.path.abspath(image_file_path)
else:
raise Exception(f"not a valid file: {url}")
async def convert_to_base64(self) -> str:
"""将这个图片统一转换为 base64 编码。这个方法避免了手动判断图片数据类型,直接返回图片数据的 base64 编码。
Returns:
str: 图片的 base64 编码,不以 base64:// 或者 data:image/jpeg;base64, 开头。
"""
# convert to base64
url = self.url if self.url else self.file
if url and url.startswith("file:///"):
bs64_data = file_to_base64(url[8:])
elif url and url.startswith("http"):
image_file_path = await download_image_by_url(url)
bs64_data = file_to_base64(image_file_path)
elif url and url.startswith("base64://"):
bs64_data = url
elif os.path.exists(url):
bs64_data = file_to_base64(url)
else:
raise Exception(f"not a valid file: {url}")
bs64_data = bs64_data.removeprefix("base64://")
return bs64_data
class Reply(BaseMessageComponent):
type: ComponentType = "Reply"
id: T.Union[str, int]
"""所引用的消息 ID"""
chain: T.Optional[T.List["BaseMessageComponent"]] = []
"""引用的消息段列表"""
sender_id: T.Optional[int] | T.Optional[str] = 0
"""引用的消息发送者 ID"""
sender_nickname: T.Optional[str] = ""
"""引用的消息发送者昵称"""
time: T.Optional[int] = 0
"""引用的消息发送时间"""
message_str: T.Optional[str] = ""
"""解析后的纯文本消息字符串"""
sender_str: T.Optional[str] = ""
"""被引用的消息纯文本"""
text: T.Optional[str] = ""
"""deprecated"""
qq: T.Optional[int] = 0
"""deprecated"""
seq: T.Optional[int] = 0
"""deprecated"""
def __init__(self, **_):
super().__init__(**_)
class RedBag(BaseMessageComponent):
type: ComponentType = "RedBag"
title: str
def __init__(self, **_):
super().__init__(**_)
class Poke(BaseMessageComponent):
type: str = ""
id: T.Optional[int] = 0
qq: T.Optional[int] = 0
def __init__(self, type: str, **_):
type = f"Poke:{type}"
super().__init__(type=type, **_)
class Forward(BaseMessageComponent):
type: ComponentType = "Forward"
id: str
def __init__(self, **_):
super().__init__(**_)
class Node(BaseMessageComponent):
"""群合并转发消息"""
type: ComponentType = "Node"
id: T.Optional[int] = 0 # 忽略
name: T.Optional[str] = "" # qq昵称
uin: T.Optional[int] = 0 # qq号
content: T.Optional[T.Union[str, list, dict]] = "" # 子消息段列表
seq: T.Optional[T.Union[str, list]] = "" # 忽略
time: T.Optional[int] = 0
def __init__(self, content: T.Union[str, list, dict, "Node", T.List["Node"]], **_):
if isinstance(content, list):
_content = None
if all(isinstance(item, Node) for item in content):
_content = [node.toDict() for node in content]
else:
_content = ""
for chain in content:
_content += chain.toString()
content = _content
elif isinstance(content, Node):
content = content.toDict()
super().__init__(content=content, **_)
def toString(self):
# logger.warn("Protocol: node doesn't support stringify")
return ""
class Nodes(BaseMessageComponent):
type: ComponentType = "Nodes"
nodes: T.List[Node]
def __init__(self, nodes: T.List[Node], **_):
super().__init__(nodes=nodes, **_)
def toDict(self):
return {"messages": [node.toDict() for node in self.nodes]}
class Xml(BaseMessageComponent):
type: ComponentType = "Xml"
data: str
resid: T.Optional[int] = 0
def __init__(self, **_):
super().__init__(**_)
class Json(BaseMessageComponent):
type: ComponentType = "Json"
data: T.Union[str, dict]
resid: T.Optional[int] = 0
def __init__(self, data, **_):
if isinstance(data, dict):
data = json.dumps(data)
super().__init__(data=data, **_)
class CardImage(BaseMessageComponent):
type: ComponentType = "CardImage"
file: str
cache: T.Optional[bool] = True
minwidth: T.Optional[int] = 400
minheight: T.Optional[int] = 400
maxwidth: T.Optional[int] = 500
maxheight: T.Optional[int] = 500
source: T.Optional[str] = ""
icon: T.Optional[str] = ""
def __init__(self, **_):
super().__init__(**_)
@staticmethod
def fromFileSystem(path, **_):
return CardImage(file=f"file:///{os.path.abspath(path)}", **_)
class TTS(BaseMessageComponent):
type: ComponentType = "TTS"
text: str
def __init__(self, **_):
super().__init__(**_)
class Unknown(BaseMessageComponent):
type: ComponentType = "Unknown"
text: str
def toString(self):
return ""
class File(BaseMessageComponent):
"""
目前此消息段只适配了 Napcat。
"""
type: ComponentType = "File"
name: T.Optional[str] = "" # 名字
file: T.Optional[str] = "" # url本地路径
def __init__(self, name: str, file: str):
super().__init__(name=name, file=file)
class WechatEmoji(BaseMessageComponent):
type: ComponentType = "WechatEmoji"
md5: T.Optional[str] = ""
md5_len: T.Optional[int] = 0
cdnurl: T.Optional[str] = ""
def __init__(self, **_):
super().__init__(**_)
ComponentTypes = {
"plain": Plain,
"text": Plain,
"face": Face,
"record": Record,
"video": Video,
"at": At,
"rps": RPS,
"dice": Dice,
"shake": Shake,
"anonymous": Anonymous,
"share": Share,
"contact": Contact,
"location": Location,
"music": Music,
"image": Image,
"reply": Reply,
"redbag": RedBag,
"poke": Poke,
"forward": Forward,
"node": Node,
"nodes": Nodes,
"xml": Xml,
"json": Json,
"cardimage": CardImage,
"tts": TTS,
"unknown": Unknown,
"file": File,
"WechatEmoji": WechatEmoji,
}

View File

@@ -0,0 +1,222 @@
import enum
from typing import List, Optional, Union, AsyncGenerator
from dataclasses import dataclass, field
from astrbot.core.message.components import (
BaseMessageComponent,
Plain,
Image,
At,
AtAll,
)
from typing_extensions import deprecated
@dataclass
class MessageChain:
"""MessageChain 描述了一整条消息中带有的所有组件。
现代消息平台的一条富文本消息中可能由多个组件构成如文本、图片、At 等,并且保留了顺序。
Attributes:
`chain` (list): 用于顺序存储各个组件。
`use_t2i_` (bool): 用于标记是否使用文本转图片服务。默认为 None即跟随用户的设置。当设置为 True 时,将会使用文本转图片服务。
"""
chain: List[BaseMessageComponent] = field(default_factory=list)
use_t2i_: Optional[bool] = None # None 为跟随用户设置
def message(self, message: str):
"""添加一条文本消息到消息链 `chain` 中。
Example:
CommandResult().message("Hello ").message("world!")
# 输出 Hello world!
"""
self.chain.append(Plain(message))
return self
def at(self, name: str, qq: Union[str, int]):
"""添加一条 At 消息到消息链 `chain` 中。
Example:
CommandResult().at("张三", "12345678910")
# 输出 @张三
"""
self.chain.append(At(name=name, qq=qq))
return self
def at_all(self):
"""添加一条 AtAll 消息到消息链 `chain` 中。
Example:
CommandResult().at_all()
# 输出 @所有人
"""
self.chain.append(AtAll())
return self
@deprecated("请使用 message 方法代替。")
def error(self, message: str):
"""添加一条错误消息到消息链 `chain` 中
Example:
CommandResult().error("解析失败")
"""
self.chain.append(Plain(message))
return self
def url_image(self, url: str):
"""添加一条图片消息https 链接)到消息链 `chain` 中。
Note:
如果需要发送本地图片,请使用 `file_image` 方法。
Example:
CommandResult().image("https://example.com/image.jpg")
"""
self.chain.append(Image.fromURL(url))
return self
def file_image(self, path: str):
"""添加一条图片消息(本地文件路径)到消息链 `chain` 中。
Note:
如果需要发送网络图片,请使用 `url_image` 方法。
CommandResult().image("image.jpg")
"""
self.chain.append(Image.fromFileSystem(path))
return self
def use_t2i(self, use_t2i: bool):
"""设置是否使用文本转图片服务。
Args:
use_t2i (bool): 是否使用文本转图片服务。默认为 None即跟随用户的设置。当设置为 True 时,将会使用文本转图片服务。
"""
self.use_t2i_ = use_t2i
return self
def get_plain_text(self) -> str:
"""获取纯文本消息。这个方法将获取 chain 中所有 Plain 组件的文本并拼接成一条消息。空格分隔。"""
return " ".join([comp.text for comp in self.chain if isinstance(comp, Plain)])
def squash_plain(self):
"""将消息链中的所有 Plain 消息段聚合到第一个 Plain 消息段中。"""
if not self.chain:
return
new_chain = []
first_plain = None
plain_texts = []
for comp in self.chain:
if isinstance(comp, Plain):
if first_plain is None:
first_plain = comp
new_chain.append(comp)
plain_texts.append(comp.text)
else:
new_chain.append(comp)
if first_plain is not None:
first_plain.text = "".join(plain_texts)
self.chain = new_chain
return self
class EventResultType(enum.Enum):
"""用于描述事件处理的结果类型。
Attributes:
CONTINUE: 事件将会继续传播
STOP: 事件将会终止传播
"""
CONTINUE = enum.auto()
STOP = enum.auto()
class ResultContentType(enum.Enum):
"""用于描述事件结果的内容的类型。"""
LLM_RESULT = enum.auto()
"""调用 LLM 产生的结果"""
GENERAL_RESULT = enum.auto()
"""普通的消息结果"""
STREAMING_RESULT = enum.auto()
"""调用 LLM 产生的流式结果"""
STREAMING_FINISH= enum.auto()
"""流式输出完成"""
@dataclass
class MessageEventResult(MessageChain):
"""MessageEventResult 描述了一整条消息中带有的所有组件以及事件处理的结果。
现代消息平台的一条富文本消息中可能由多个组件构成如文本、图片、At 等,并且保留了顺序。
Attributes:
`chain` (list): 用于顺序存储各个组件。
`use_t2i_` (bool): 用于标记是否使用文本转图片服务。默认为 None即跟随用户的设置。当设置为 True 时,将会使用文本转图片服务。
`result_type` (EventResultType): 事件处理的结果类型。
"""
result_type: Optional[EventResultType] = field(
default_factory=lambda: EventResultType.CONTINUE
)
result_content_type: Optional[ResultContentType] = field(
default_factory=lambda: ResultContentType.GENERAL_RESULT
)
async_stream: Optional[AsyncGenerator] = None
"""异步流"""
def stop_event(self) -> "MessageEventResult":
"""终止事件传播。"""
self.result_type = EventResultType.STOP
return self
def continue_event(self) -> "MessageEventResult":
"""继续事件传播。"""
self.result_type = EventResultType.CONTINUE
return self
def is_stopped(self) -> bool:
"""
是否终止事件传播。
"""
return self.result_type == EventResultType.STOP
def set_async_stream(self, stream: AsyncGenerator) -> "MessageEventResult":
"""设置异步流。"""
self.async_stream = stream
return self
def set_result_content_type(self, typ: ResultContentType) -> "MessageEventResult":
"""设置事件处理的结果类型。
Args:
result_type (EventResultType): 事件处理的结果类型。
"""
self.result_content_type = typ
return self
def is_llm_result(self) -> bool:
"""是否为 LLM 结果。"""
return self.result_content_type == ResultContentType.LLM_RESULT
# 为了兼容旧版代码,保留 CommandResult 的别名
CommandResult = MessageEventResult

View File

@@ -0,0 +1,41 @@
from astrbot.core.message.message_event_result import (
MessageEventResult,
EventResultType,
)
from .waking_check.stage import WakingCheckStage
from .whitelist_check.stage import WhitelistCheckStage
from .rate_limit_check.stage import RateLimitStage
from .content_safety_check.stage import ContentSafetyCheckStage
from .platform_compatibility.stage import PlatformCompatibilityStage
from .preprocess_stage.stage import PreProcessStage
from .process_stage.stage import ProcessStage
from .result_decorate.stage import ResultDecorateStage
from .respond.stage import RespondStage
# 管道阶段顺序
STAGES_ORDER = [
"WakingCheckStage", # 检查是否需要唤醒
"WhitelistCheckStage", # 检查是否在群聊/私聊白名单
"RateLimitStage", # 检查会话是否超过频率限制
"ContentSafetyCheckStage", # 检查内容安全
"PlatformCompatibilityStage", # 检查所有处理器的平台兼容性
"PreProcessStage", # 预处理
"ProcessStage", # 交由 Stars 处理a.k.a 插件),或者 LLM 调用
"ResultDecorateStage", # 处理结果比如添加回复前缀、t2i、转换为语音 等
"RespondStage", # 发送消息
]
__all__ = [
"WakingCheckStage",
"WhitelistCheckStage",
"RateLimitStage",
"ContentSafetyCheckStage",
"PlatformCompatibilityStage",
"PreProcessStage",
"ProcessStage",
"ResultDecorateStage",
"RespondStage",
"MessageEventResult",
"EventResultType",
]

View File

@@ -0,0 +1,37 @@
from typing import Union, AsyncGenerator
from ..stage import Stage, register_stage
from ..context import PipelineContext
from astrbot.core.platform.astr_message_event import AstrMessageEvent
from astrbot.core.message.message_event_result import MessageEventResult
from astrbot.core import logger
from .strategies.strategy import StrategySelector
@register_stage
class ContentSafetyCheckStage(Stage):
"""检查内容安全
当前只会检查文本的。
"""
async def initialize(self, ctx: PipelineContext):
config = ctx.astrbot_config["content_safety"]
self.strategy_selector = StrategySelector(config)
async def process(
self, event: AstrMessageEvent, check_text: str = None
) -> Union[None, AsyncGenerator[None, None]]:
"""检查内容安全"""
text = check_text if check_text else event.get_message_str()
ok, info = self.strategy_selector.check(text)
if not ok:
if event.is_at_or_wake_command:
event.set_result(
MessageEventResult().message(
"你的消息或者大模型的响应中包含不适当的内容,已被屏蔽。"
)
)
yield
event.stop_event()
logger.info(f"内容安全检查不通过,原因:{info}")
return

View File

@@ -0,0 +1,8 @@
import abc
from typing import Tuple
class ContentSafetyStrategy(abc.ABC):
@abc.abstractmethod
def check(self, content: str) -> Tuple[bool, str]:
raise NotImplementedError

View File

@@ -0,0 +1,30 @@
"""
使用此功能应该先 pip install baidu-aip
"""
from . import ContentSafetyStrategy
from aip import AipContentCensor
class BaiduAipStrategy(ContentSafetyStrategy):
def __init__(self, appid: str, ak: str, sk: str) -> None:
self.app_id = appid
self.api_key = ak
self.secret_key = sk
self.client = AipContentCensor(self.app_id, self.api_key, self.secret_key)
def check(self, content: str):
res = self.client.textCensorUserDefined(content)
if "conclusionType" not in res:
return False, ""
if res["conclusionType"] == 1:
return True, ""
else:
if "data" not in res:
return False, ""
count = len(res["data"])
info = f"百度审核服务发现 {count} 处违规:\n"
for i in res["data"]:
info += f"{i['msg']}\n"
info += "\n判断结果:" + res["conclusion"]
return False, info

View File

@@ -0,0 +1,23 @@
import re
from . import ContentSafetyStrategy
class KeywordsStrategy(ContentSafetyStrategy):
def __init__(self, extra_keywords: list) -> None:
self.keywords = []
if extra_keywords is None:
extra_keywords = []
self.keywords.extend(extra_keywords)
# keywords_path = os.path.join(os.path.dirname(__file__), "unfit_words")
# internal keywords
# if os.path.exists(keywords_path):
# with open(keywords_path, "r", encoding="utf-8") as f:
# self.keywords.extend(
# json.loads(base64.b64decode(f.read()).decode("utf-8"))["keywords"]
# )
def check(self, content: str) -> bool:
for keyword in self.keywords:
if re.search(keyword, content):
return False, "内容安全检查不通过,匹配到敏感词。"
return True, ""

View File

@@ -0,0 +1,34 @@
from . import ContentSafetyStrategy
from typing import List, Tuple
from astrbot import logger
class StrategySelector:
def __init__(self, config: dict) -> None:
self.enabled_strategies: List[ContentSafetyStrategy] = []
if config["internal_keywords"]["enable"]:
from .keywords import KeywordsStrategy
self.enabled_strategies.append(
KeywordsStrategy(config["internal_keywords"]["extra_keywords"])
)
if config["baidu_aip"]["enable"]:
try:
from .baidu_aip import BaiduAipStrategy
except ImportError:
logger.warning("使用百度内容审核应该先 pip install baidu-aip")
return
self.enabled_strategies.append(
BaiduAipStrategy(
config["baidu_aip"]["app_id"],
config["baidu_aip"]["api_key"],
config["baidu_aip"]["secret_key"],
)
)
def check(self, content: str) -> Tuple[bool, str]:
for strategy in self.enabled_strategies:
ok, info = strategy.check(content)
if not ok:
return False, info
return True, ""

View File

@@ -0,0 +1,11 @@
from dataclasses import dataclass
from astrbot.core.config.astrbot_config import AstrBotConfig
from astrbot.core.star import PluginManager
@dataclass
class PipelineContext:
"""上下文对象,包含管道执行所需的上下文信息"""
astrbot_config: AstrBotConfig # AstrBot 配置对象
plugin_manager: PluginManager # 插件管理器对象

View File

@@ -0,0 +1,56 @@
from ..stage import Stage, register_stage
from ..context import PipelineContext
from typing import Union, AsyncGenerator
from astrbot.core.platform.astr_message_event import AstrMessageEvent
from astrbot.core.star.star import star_map
from astrbot.core.star.star_handler import StarHandlerMetadata
from astrbot.core import logger
@register_stage
class PlatformCompatibilityStage(Stage):
"""检查所有处理器的平台兼容性。
这个阶段会检查所有处理器是否在当前平台启用如果未启用则设置platform_compatible属性为False。
"""
async def initialize(self, ctx: PipelineContext) -> None:
"""初始化平台兼容性检查阶段
Args:
ctx (PipelineContext): 消息管道上下文对象, 包括配置和插件管理器
"""
self.ctx = ctx
async def process(
self, event: AstrMessageEvent
) -> Union[None, AsyncGenerator[None, None]]:
# 获取当前平台ID
platform_id = event.get_platform_id()
# 获取已激活的处理器
activated_handlers = event.get_extra("activated_handlers")
if activated_handlers is None:
activated_handlers = []
# 标记不兼容的处理器
for handler in activated_handlers:
if not isinstance(handler, StarHandlerMetadata):
continue
# 检查处理器是否在当前平台启用
enabled = handler.is_enabled_for_platform(platform_id)
if not enabled:
if handler.handler_module_path in star_map:
plugin_name = star_map[handler.handler_module_path].name
logger.debug(
f"[PlatformCompatibilityStage] 插件 {plugin_name} 在平台 {platform_id} 未启用,标记处理器 {handler.handler_name} 为平台不兼容"
)
# 设置处理器为平台不兼容状态
# TODO: 更好的标记方式
handler.platform_compatible = False
else:
# 确保处理器为平台兼容状态
handler.platform_compatible = True
# 更新已激活的处理器列表
event.set_extra("activated_handlers", activated_handlers)

View File

@@ -0,0 +1,73 @@
import traceback
import asyncio
from typing import Union, AsyncGenerator
from ..stage import Stage, register_stage
from ..context import PipelineContext
from astrbot.core.platform.astr_message_event import AstrMessageEvent
from astrbot.core import logger
from astrbot.core.message.components import Plain, Record, Image
@register_stage
class PreProcessStage(Stage):
async def initialize(self, ctx: PipelineContext) -> None:
self.ctx = ctx
self.config = ctx.astrbot_config
self.plugin_manager = ctx.plugin_manager
self.stt_settings: dict = self.config.get("provider_stt_settings", {})
self.platform_settings: dict = self.config.get("platform_settings", {})
async def process(
self, event: AstrMessageEvent
) -> Union[None, AsyncGenerator[None, None]]:
"""在处理事件之前的预处理"""
# 路径映射
if mappings := self.platform_settings.get("path_mapping", []):
# 支持 RecordImage 消息段的路径映射。
message_chain = event.get_messages()
for idx, component in enumerate(message_chain):
if isinstance(component, (Record, Image)) and component.url:
for mapping in mappings:
from_, to_ = mapping.split(":")
from_ = from_.removesuffix("/")
to_ = to_.removesuffix("/")
url = component.url.removeprefix("file://")
if url.startswith(from_):
component.url = url.replace(from_, to_, 1)
logger.debug(f"路径映射: {url} -> {component.url}")
message_chain[idx] = component
# STT
if self.stt_settings.get("enable", False):
# TODO: 独立
stt_provider = (
self.plugin_manager.context.provider_manager.curr_stt_provider_inst
)
if stt_provider:
message_chain = event.get_messages()
for idx, component in enumerate(message_chain):
if isinstance(component, Record) and component.url:
path = component.url.removeprefix("file://")
retry = 5
for i in range(retry):
try:
result = await stt_provider.get_text(audio_url=path)
if result:
logger.info("语音转文本结果: " + result)
message_chain[idx] = Plain(result)
event.message_str += result
event.message_obj.message_str += result
break
except FileNotFoundError as e:
# napcat workaround
logger.warning(e)
logger.warning(f"重试中: {i + 1}/{retry}")
await asyncio.sleep(0.5)
continue
except BaseException as e:
logger.error(traceback.format_exc())
logger.error(f"语音转文本失败: {e}")
break

View File

@@ -0,0 +1,539 @@
"""
本地 Agent 模式的 LLM 调用 Stage
"""
import traceback
import asyncio
import json
from typing import Union, AsyncGenerator
from ...context import PipelineContext
from ..stage import Stage
from astrbot.core.platform.astr_message_event import AstrMessageEvent
from astrbot.core.message.message_event_result import (
MessageEventResult,
ResultContentType,
MessageChain,
)
from astrbot.core.message.components import Image
from astrbot.core import logger
from astrbot.core.utils.metrics import Metric
from astrbot.core.provider.entities import (
ProviderRequest,
LLMResponse,
ToolCallMessageSegment,
AssistantMessageSegment,
ToolCallsResult,
)
from astrbot.core.star.star_handler import star_handlers_registry, EventType
from astrbot.core.star.star import star_map
class LLMRequestSubStage(Stage):
async def initialize(self, ctx: PipelineContext) -> None:
self.ctx = ctx
self.bot_wake_prefixs = ctx.astrbot_config["wake_prefix"] # list
self.provider_wake_prefix = ctx.astrbot_config["provider_settings"][
"wake_prefix"
] # str
self.max_context_length = ctx.astrbot_config["provider_settings"][
"max_context_length"
] # int
self.dequeue_context_length = min(
max(1, ctx.astrbot_config["provider_settings"]["dequeue_context_length"]),
self.max_context_length - 1,
) # int
self.streaming_response = ctx.astrbot_config["provider_settings"][
"streaming_response"
] # bool
for bwp in self.bot_wake_prefixs:
if self.provider_wake_prefix.startswith(bwp):
logger.info(
f"识别 LLM 聊天额外唤醒前缀 {self.provider_wake_prefix} 以机器人唤醒前缀 {bwp} 开头,已自动去除。"
)
self.provider_wake_prefix = self.provider_wake_prefix[len(bwp) :]
self.conv_manager = ctx.plugin_manager.context.conversation_manager
async def process(
self, event: AstrMessageEvent, _nested: bool = False
) -> Union[None, AsyncGenerator[None, None]]:
req: ProviderRequest = None
provider = self.ctx.plugin_manager.context.get_using_provider()
if provider is None:
return
if event.get_extra("provider_request"):
req = event.get_extra("provider_request")
assert isinstance(
req, ProviderRequest
), "provider_request 必须是 ProviderRequest 类型。"
if req.conversation:
all_contexts = json.loads(req.conversation.history)
req.contexts = self._process_tool_message_pairs(
all_contexts, remove_tags=True
)
else:
req = ProviderRequest(prompt="", image_urls=[])
if self.provider_wake_prefix:
if not event.message_str.startswith(self.provider_wake_prefix):
return
req.prompt = event.message_str[len(self.provider_wake_prefix) :]
req.func_tool = self.ctx.plugin_manager.context.get_llm_tool_manager()
for comp in event.message_obj.message:
if isinstance(comp, Image):
image_path = await comp.convert_to_file_path()
req.image_urls.append(image_path)
# 获取对话上下文
conversation_id = await self.conv_manager.get_curr_conversation_id(
event.unified_msg_origin
)
if not conversation_id:
conversation_id = await self.conv_manager.new_conversation(
event.unified_msg_origin
)
conversation = await self.conv_manager.get_conversation(
event.unified_msg_origin, conversation_id
)
if not conversation:
conversation_id = await self.conv_manager.new_conversation(
event.unified_msg_origin
)
conversation = await self.conv_manager.get_conversation(
event.unified_msg_origin, conversation_id
)
req.conversation = conversation
req.contexts = json.loads(conversation.history)
event.set_extra("provider_request", req)
if not req.prompt and not req.image_urls:
return
# 执行请求 LLM 前事件钩子。
# 装饰 system_prompt 等功能
# 获取当前平台ID
platform_id = event.get_platform_id()
handlers = star_handlers_registry.get_handlers_by_event_type(
EventType.OnLLMRequestEvent, platform_id=platform_id
)
for handler in handlers:
try:
logger.debug(
f"hook(on_llm_request) -> {star_map[handler.handler_module_path].name} - {handler.handler_name}"
)
await handler.handler(event, req)
except BaseException:
logger.error(traceback.format_exc())
if event.is_stopped():
logger.info(
f"{star_map[handler.handler_module_path].name} - {handler.handler_name} 终止了事件传播。"
)
return
if isinstance(req.contexts, str):
req.contexts = json.loads(req.contexts)
# max context length
if (
self.max_context_length != -1 # -1 为不限制
and len(req.contexts) // 2 > self.max_context_length
):
logger.debug("上下文长度超过限制,将截断。")
req.contexts = req.contexts[
-(self.max_context_length - self.dequeue_context_length + 1) * 2 :
]
# 找到第一个role 为 user 的索引,确保上下文格式正确
index = next((i for i, item in enumerate(req.contexts) if item.get("role") == "user"), None)
if index is not None and index > 0:
req.contexts = req.contexts[index:]
# session_id
if not req.session_id:
req.session_id = event.unified_msg_origin
async def requesting(req: ProviderRequest):
try:
need_loop = True
while need_loop:
need_loop = False
logger.debug(f"提供商请求 Payload: {req}")
final_llm_response = None
if self.streaming_response:
stream = provider.text_chat_stream(**req.__dict__)
async for llm_response in stream:
if llm_response.is_chunk:
if llm_response.result_chain:
yield llm_response.result_chain # MessageChain
else:
yield MessageChain().message(
llm_response.completion_text
)
else:
final_llm_response = llm_response
else:
final_llm_response = await provider.text_chat(
**req.__dict__
) # 请求 LLM
if not final_llm_response:
raise Exception("LLM response is None.")
# 执行 LLM 响应后的事件钩子。
handlers = star_handlers_registry.get_handlers_by_event_type(
EventType.OnLLMResponseEvent
)
for handler in handlers:
try:
logger.debug(
f"hook(on_llm_response) -> {star_map[handler.handler_module_path].name} - {handler.handler_name}"
)
await handler.handler(event, final_llm_response)
except BaseException:
logger.error(traceback.format_exc())
if event.is_stopped():
logger.info(
f"{star_map[handler.handler_module_path].name} - {handler.handler_name} 终止了事件传播。"
)
return
if self.streaming_response:
# 流式输出的处理
async for result in self._handle_llm_stream_response(
event, req, final_llm_response
):
if isinstance(result, ProviderRequest):
# 有函数工具调用并且返回了结果,我们需要再次请求 LLM
req = result
need_loop = True
else:
yield
else:
# 非流式输出的处理
async for result in self._handle_llm_response(
event, req, final_llm_response
):
if isinstance(result, ProviderRequest):
# 有函数工具调用并且返回了结果,我们需要再次请求 LLM
req = result
need_loop = True
else:
yield
asyncio.create_task(
Metric.upload(
llm_tick=1,
model_name=provider.get_model(),
provider_type=provider.meta().type,
)
)
# 保存到历史记录
await self._save_to_history(event, req, final_llm_response)
except BaseException as e:
logger.error(traceback.format_exc())
event.set_result(
MessageEventResult().message(
f"AstrBot 请求失败。\n错误类型: {type(e).__name__}\n错误信息: {str(e)}"
)
)
if not self.streaming_response:
event.set_extra("tool_call_result", None)
async for _ in requesting(req):
yield
else:
event.set_result(
MessageEventResult()
.set_result_content_type(ResultContentType.STREAMING_RESULT)
.set_async_stream(requesting(req))
)
# 这里使用yield来暂停当前阶段等待流式输出完成后继续处理
yield
if event.get_extra("tool_call_result"):
event.set_result(event.get_extra("tool_call_result"))
event.set_extra("tool_call_result", None)
yield
async def _handle_llm_response(
self,
event: AstrMessageEvent,
req: ProviderRequest,
llm_response: LLMResponse,
) -> AsyncGenerator[Union[None, ProviderRequest], None]:
"""处理非流式 LLM 响应。
Returns:
AsyncGenerator[Union[None, ProviderRequest], None]: 如果返回 ProviderRequest表示需要再次调用 LLM
Yields:
Iterator[Union[None, ProviderRequest]]: 将 event 交付给下一个 stage 或者返回 ProviderRequest 表示需要再次调用 LLM
"""
if llm_response.role == "assistant":
# text completion
if llm_response.result_chain:
event.set_result(
MessageEventResult(
chain=llm_response.result_chain.chain
).set_result_content_type(ResultContentType.LLM_RESULT)
)
else:
event.set_result(
MessageEventResult()
.message(llm_response.completion_text)
.set_result_content_type(ResultContentType.LLM_RESULT)
)
elif llm_response.role == "err":
event.set_result(
MessageEventResult().message(
f"AstrBot 请求失败。\n错误信息: {llm_response.completion_text}"
)
)
elif llm_response.role == "tool":
# 处理函数工具调用
async for result in self._handle_function_tools(event, req, llm_response):
yield result
async def _handle_llm_stream_response(
self,
event: AstrMessageEvent,
req: ProviderRequest,
llm_response: LLMResponse,
) -> AsyncGenerator[Union[None, ProviderRequest], None]:
"""处理流式 LLM 响应。
专门用于处理流式输出完成后的响应,与非流式响应处理分离。
Returns:
AsyncGenerator[Union[None, ProviderRequest], None]: 如果返回 ProviderRequest表示需要再次调用 LLM
Yields:
Iterator[Union[None, ProviderRequest]]: 将 event 交付给下一个 stage 或者返回 ProviderRequest 表示需要再次调用 LLM
"""
if llm_response.role == "assistant":
# text completion
if llm_response.result_chain:
event.set_result(
MessageEventResult(
chain=llm_response.result_chain.chain
).set_result_content_type(ResultContentType.STREAMING_FINISH)
)
else:
event.set_result(
MessageEventResult()
.message(llm_response.completion_text)
.set_result_content_type(ResultContentType.STREAMING_FINISH)
)
elif llm_response.role == "err":
event.set_result(
MessageEventResult().message(
f"AstrBot 请求失败。\n错误信息: {llm_response.completion_text}"
)
)
elif llm_response.role == "tool":
# 处理函数工具调用
async for result in self._handle_function_tools(event, req, llm_response):
yield result
async def _handle_function_tools(
self,
event: AstrMessageEvent,
req: ProviderRequest,
llm_response: LLMResponse,
) -> AsyncGenerator[Union[None, ProviderRequest], None]:
"""处理函数工具调用。
Returns:
AsyncGenerator[Union[None, ProviderRequest], None]: 如果返回 ProviderRequest表示需要再次调用 LLM
"""
# function calling
tool_call_result: list[ToolCallMessageSegment] = []
logger.info(
f"触发 {len(llm_response.tools_call_name)} 个函数调用: {llm_response.tools_call_name}"
)
for func_tool_name, func_tool_args, func_tool_id in zip(
llm_response.tools_call_name,
llm_response.tools_call_args,
llm_response.tools_call_ids,
):
try:
func_tool = req.func_tool.get_func(func_tool_name)
if func_tool.origin == "mcp":
logger.info(
f"从 MCP 服务 {func_tool.mcp_server_name} 调用工具函数:{func_tool.name},参数:{func_tool_args}"
)
client = req.func_tool.mcp_client_dict[func_tool.mcp_server_name]
res = await client.session.call_tool(func_tool.name, func_tool_args)
if res:
# TODO content的类型可能包括list[TextContent | ImageContent | EmbeddedResource]这里只处理了TextContent。
tool_call_result.append(
ToolCallMessageSegment(
role="tool",
tool_call_id=func_tool_id,
content=res.content[0].text,
)
)
else:
# 获取处理器,过滤掉平台不兼容的处理器
platform_id = event.get_platform_id()
star_md = star_map.get(func_tool.handler_module_path)
if (
star_md and
platform_id in star_md.supported_platforms
and not star_md.supported_platforms[platform_id]
):
logger.debug(
f"处理器 {func_tool_name}({star_md.name}) 在当前平台不兼容或者被禁用,跳过执行"
)
# 直接跳过不添加任何消息到tool_call_result
continue
logger.info(
f"调用工具函数:{func_tool_name},参数:{func_tool_args}"
)
# 尝试调用工具函数
wrapper = self._call_handler(
self.ctx, event, func_tool.handler, **func_tool_args
)
async for resp in wrapper:
if resp is not None: # 有 return 返回
tool_call_result.append(
ToolCallMessageSegment(
role="tool",
tool_call_id=func_tool_id,
content=resp,
)
)
else:
res = event.get_result()
if res and res.chain:
event.set_extra("tool_call_result", res)
yield # 有生成器返回
event.clear_result() # 清除上一个 handler 的结果
except BaseException as e:
logger.warning(traceback.format_exc())
tool_call_result.append(
ToolCallMessageSegment(
role="tool",
tool_call_id=func_tool_id,
content=f"error: {str(e)}",
)
)
if tool_call_result:
# 函数调用结果
req.func_tool = None # 暂时不支持递归工具调用
assistant_msg_seg = AssistantMessageSegment(
role="assistant", tool_calls=llm_response.to_openai_tool_calls()
)
# 在多轮 Tool 调用的情况下,这里始终保持最新的 Tool 调用结果,减少上下文长度。
req.tool_calls_result = ToolCallsResult(
tool_calls_info=assistant_msg_seg,
tool_calls_result=tool_call_result,
)
yield req # 再次执行 LLM 请求
else:
if llm_response.completion_text:
event.set_result(
MessageEventResult().message(llm_response.completion_text)
)
async def _save_to_history(
self, event: AstrMessageEvent, req: ProviderRequest, llm_response: LLMResponse
):
if not req or not req.conversation or not llm_response:
return
if llm_response.role == "assistant":
# 文本回复
contexts = req.contexts.copy()
contexts.append(await req.assemble_context())
# 记录并标记函数调用结果
if req.tool_calls_result:
tool_calls_messages = req.tool_calls_result.to_openai_messages()
# 添加标记
for message in tool_calls_messages:
message["_tool_call_history"] = True
processed_tool_messages = self._process_tool_message_pairs(
tool_calls_messages, remove_tags=False
)
contexts.extend(processed_tool_messages)
contexts.append(
{"role": "assistant", "content": llm_response.completion_text}
)
contexts_to_save = list(
filter(lambda item: "_no_save" not in item, contexts)
)
await self.conv_manager.update_conversation(
event.unified_msg_origin, req.conversation.cid, history=contexts_to_save
)
def _process_tool_message_pairs(self, messages, remove_tags=True):
"""处理工具调用消息确保assistant和tool消息成对出现
Args:
messages (list): 消息列表
remove_tags (bool): 是否移除_tool_call_history标记
Returns:
list: 处理后的消息列表保证了assistant和对应tool消息的成对出现
"""
result = []
i = 0
while i < len(messages):
current_msg = messages[i]
# 普通消息直接添加
if "_tool_call_history" not in current_msg:
result.append(current_msg.copy() if remove_tags else current_msg)
i += 1
continue
# 工具调用消息成对处理
if current_msg.get("role") == "assistant" and "tool_calls" in current_msg:
assistant_msg = current_msg.copy()
if remove_tags and "_tool_call_history" in assistant_msg:
del assistant_msg["_tool_call_history"]
related_tools = []
j = i + 1
while (
j < len(messages)
and messages[j].get("role") == "tool"
and "_tool_call_history" in messages[j]
):
tool_msg = messages[j].copy()
if remove_tags:
del tool_msg["_tool_call_history"]
related_tools.append(tool_msg)
j += 1
# 成对的时候添加到结果
if related_tools:
result.append(assistant_msg)
result.extend(related_tools)
i = j # 跳过已处理
else:
# 单独的tool消息
i += 1
return result

View File

@@ -0,0 +1,67 @@
"""
本地 Agent 模式的 AstrBot 插件调用 Stage
"""
from ...context import PipelineContext
from ..stage import Stage
from typing import Dict, Any, List, AsyncGenerator, Union
from astrbot.core.platform.astr_message_event import AstrMessageEvent
from astrbot.core.message.message_event_result import MessageEventResult
from astrbot.core import logger
from astrbot.core.star.star_handler import StarHandlerMetadata
from astrbot.core.star.star import star_map
import traceback
class StarRequestSubStage(Stage):
async def initialize(self, ctx: PipelineContext) -> None:
self.curr_provider = ctx.plugin_manager.context.get_using_provider()
self.prompt_prefix = ctx.astrbot_config["provider_settings"]["prompt_prefix"]
self.identifier = ctx.astrbot_config["provider_settings"]["identifier"]
self.ctx = ctx
async def process(
self, event: AstrMessageEvent
) -> Union[None, AsyncGenerator[None, None]]:
activated_handlers: List[StarHandlerMetadata] = event.get_extra(
"activated_handlers"
)
handlers_parsed_params: Dict[str, Dict[str, Any]] = event.get_extra(
"handlers_parsed_params"
)
if not handlers_parsed_params:
handlers_parsed_params = {}
for handler in activated_handlers:
# 检查处理器是否在当前平台兼容
if (
hasattr(handler, "platform_compatible")
and handler.platform_compatible is False
):
logger.debug(
f"处理器 {handler.handler_name} 在当前平台不兼容,跳过执行"
)
continue
params = handlers_parsed_params.get(handler.handler_full_name, {})
try:
if handler.handler_module_path not in star_map:
continue
logger.debug(
f"plugin -> {star_map.get(handler.handler_module_path).name} - {handler.handler_name}"
)
wrapper = self._call_handler(self.ctx, event, handler.handler, **params)
async for ret in wrapper:
yield ret
event.clear_result() # 清除上一个 handler 的结果
except Exception as e:
logger.error(traceback.format_exc())
logger.error(f"Star {handler.handler_full_name} handle error: {e}")
if event.is_at_or_wake_command:
ret = f":(\n\n在调用插件 {star_map.get(handler.handler_module_path).name} 的处理函数 {handler.handler_name} 时出现异常:{e}"
event.set_result(MessageEventResult().message(ret))
yield
event.clear_result()
event.stop_event()

View File

@@ -0,0 +1,68 @@
from typing import List, Union, AsyncGenerator
from ..stage import Stage, register_stage
from ..context import PipelineContext
from .method.llm_request import LLMRequestSubStage
from .method.star_request import StarRequestSubStage
from astrbot.core.platform.astr_message_event import AstrMessageEvent
from astrbot.core.star.star_handler import StarHandlerMetadata
from astrbot.core.provider.entities import ProviderRequest
from astrbot.core import logger
@register_stage
class ProcessStage(Stage):
async def initialize(self, ctx: PipelineContext) -> None:
self.ctx = ctx
self.config = ctx.astrbot_config
self.plugin_manager = ctx.plugin_manager
self.llm_request_sub_stage = LLMRequestSubStage()
await self.llm_request_sub_stage.initialize(ctx)
self.star_request_sub_stage = StarRequestSubStage()
await self.star_request_sub_stage.initialize(ctx)
async def process(
self, event: AstrMessageEvent
) -> Union[None, AsyncGenerator[None, None]]:
"""处理事件"""
activated_handlers: List[StarHandlerMetadata] = event.get_extra(
"activated_handlers"
)
# 有插件 Handler 被激活
if activated_handlers:
async for resp in self.star_request_sub_stage.process(event):
# 生成器返回值处理
if isinstance(resp, ProviderRequest):
# Handler 的 LLM 请求
event.set_extra("provider_request", resp)
_t = False
async for _ in self.llm_request_sub_stage.process(event):
_t = True
yield
if not _t:
yield
else:
yield
# 调用 LLM 相关请求
if not self.ctx.astrbot_config["provider_settings"].get("enable", True):
return
if (
not event._has_send_oper
and event.is_at_or_wake_command
and not event.call_llm
):
# 是否有过发送操作 and 是否是被 @ 或者通过唤醒前缀
if (
event.get_result() and not event.get_result().is_stopped()
) or not event.get_result():
# 事件没有终止传播
provider = self.ctx.plugin_manager.context.get_using_provider()
if not provider:
logger.info("未找到可用的 LLM 提供商,请先前往配置服务提供商。")
return
async for _ in self.llm_request_sub_stage.process(event):
yield

View File

@@ -0,0 +1,101 @@
import asyncio
from datetime import datetime, timedelta
from collections import defaultdict, deque
from typing import DefaultDict, Deque, Union, AsyncGenerator
from ..stage import Stage, register_stage
from ..context import PipelineContext
from astrbot.core.platform.astr_message_event import AstrMessageEvent
from astrbot.core import logger
from astrbot.core.config.astrbot_config import RateLimitStrategy
@register_stage
class RateLimitStage(Stage):
"""
检查是否需要限制消息发送的限流器。
使用 Fixed Window 算法。
如果触发限流,将 stall 流水线,直到下一个时间窗口来临时自动唤醒。
"""
def __init__(self):
# 存储每个会话的请求时间队列
self.event_timestamps: DefaultDict[str, Deque[datetime]] = defaultdict(deque)
# 为每个会话设置一个锁,避免并发冲突
self.locks: DefaultDict[str, asyncio.Lock] = defaultdict(asyncio.Lock)
# 限流参数
self.rate_limit_count: int = 0
self.rate_limit_time: timedelta = timedelta(0)
async def initialize(self, ctx: PipelineContext) -> None:
"""
初始化限流器,根据配置设置限流参数。
"""
self.rate_limit_count = ctx.astrbot_config["platform_settings"]["rate_limit"][
"count"
]
self.rate_limit_time = timedelta(
seconds=ctx.astrbot_config["platform_settings"]["rate_limit"]["time"]
)
self.rl_strategy = ctx.astrbot_config["platform_settings"]["rate_limit"][
"strategy"
] # stall or discard
async def process(
self, event: AstrMessageEvent
) -> Union[None, AsyncGenerator[None, None]]:
"""
检查并处理限流逻辑。如果触发限流,流水线会 stall 并在窗口期后自动恢复。
Args:
event (AstrMessageEvent): 当前消息事件。
ctx (PipelineContext): 流水线上下文。
Returns:
MessageEventResult: 继续或停止事件处理的结果。
"""
session_id = event.session_id
now = datetime.now()
async with self.locks[session_id]: # 确保同一会话不会并发修改队列
timestamps = self.event_timestamps[session_id]
self._remove_expired_timestamps(timestamps, now)
if len(timestamps) >= self.rate_limit_count:
# 达到限流阈值,计算下一个窗口的时间
next_window_time = timestamps[0] + self.rate_limit_time
stall_duration = (next_window_time - now).total_seconds()
match self.rl_strategy:
case RateLimitStrategy.STALL.value:
logger.info(
f"会话 {session_id} 被限流。根据限流策略,此会话处理将被暂停 {stall_duration:.2f} 秒。"
)
await asyncio.sleep(stall_duration)
case RateLimitStrategy.DISCARD.value:
# event.set_result(MessageEventResult().message(f"会话 {session_id} 被限流。根据限流策略,此请求已被丢弃,直到您的限额于 {stall_duration:.2f} 秒后重置。"))
logger.info(
f"会话 {session_id} 被限流。根据限流策略,此请求已被丢弃,直到限额于 {stall_duration:.2f} 秒后重置。"
)
return event.stop_event()
self._remove_expired_timestamps(
timestamps, now + timedelta(seconds=stall_duration)
)
timestamps.append(now)
def _remove_expired_timestamps(
self, timestamps: Deque[datetime], now: datetime
) -> None:
"""
移除时间窗口外的时间戳。
Args:
timestamps (Deque[datetime]): 当前会话的时间戳队列。
now (datetime): 当前时间,用于计算过期时间。
"""
expiry_threshold: datetime = now - self.rate_limit_time
while timestamps and timestamps[0] < expiry_threshold:
timestamps.popleft()

View File

@@ -0,0 +1,233 @@
import random
import asyncio
import math
import traceback
import astrbot.core.message.components as Comp
from typing import Union, AsyncGenerator
from ..stage import register_stage, Stage
from ..context import PipelineContext
from astrbot.core.platform.astr_message_event import AstrMessageEvent
from astrbot.core.message.message_event_result import MessageChain, ResultContentType
from astrbot.core import logger
from astrbot.core.message.message_event_result import BaseMessageComponent
from astrbot.core.star.star_handler import star_handlers_registry, EventType
from astrbot.core.star.star import star_map
from astrbot.core.utils.path_util import path_Mapping
@register_stage
class RespondStage(Stage):
# 组件类型到其非空判断函数的映射
_component_validators = {
Comp.Plain: lambda comp: bool(
comp.text and comp.text.strip()
), # 纯文本消息需要strip
Comp.Face: lambda comp: comp.id is not None, # QQ表情
Comp.Record: lambda comp: bool(comp.file), # 语音
Comp.Video: lambda comp: bool(comp.file), # 视频
Comp.At: lambda comp: bool(comp.qq) or bool(comp.name), # @
Comp.AtAll: lambda comp: True, # @所有人
Comp.RPS: lambda comp: True, # 不知道是啥(未完成)
Comp.Dice: lambda comp: True, # 骰子(未完成)
Comp.Shake: lambda comp: True, # 摇一摇(未完成)
Comp.Anonymous: lambda comp: True, # 匿名(未完成)
Comp.Share: lambda comp: bool(comp.url) and bool(comp.title), # 分享
Comp.Contact: lambda comp: True, # 联系人(未完成)
Comp.Location: lambda comp: bool(comp.lat and comp.lon), # 位置
Comp.Music: lambda comp: bool(comp._type)
and bool(comp.url)
and bool(comp.audio), # 音乐
Comp.Image: lambda comp: bool(comp.file), # 图片
Comp.Reply: lambda comp: bool(comp.id) and comp.sender_id is not None, # 回复
Comp.RedBag: lambda comp: bool(comp.title), # 红包
Comp.Poke: lambda comp: comp.id != 0 and comp.qq != 0, # 戳一戳
Comp.Forward: lambda comp: bool(comp.id and comp.id.strip()), # 转发
Comp.Node: lambda comp: bool(comp.name)
and comp.uin != 0
and bool(comp.content), # 一个转发节点
Comp.Nodes: lambda comp: bool(comp.nodes), # 多个转发节点
Comp.Xml: lambda comp: bool(comp.data and comp.data.strip()), # XML
Comp.Json: lambda comp: bool(comp.data), # JSON
Comp.CardImage: lambda comp: bool(comp.file), # 卡片图片
Comp.TTS: lambda comp: bool(comp.text and comp.text.strip()), # 语音合成
Comp.Unknown: lambda comp: bool(comp.text and comp.text.strip()), # 未知消息
Comp.File: lambda comp: bool(comp.file), # 文件
Comp.WechatEmoji: lambda comp: bool(comp.md5), # 微信表情
}
async def initialize(self, ctx: PipelineContext):
self.ctx = ctx
self.config = ctx.astrbot_config
self.platform_settings: dict = self.config.get("platform_settings", {})
self.reply_with_mention = ctx.astrbot_config["platform_settings"][
"reply_with_mention"
]
self.reply_with_quote = ctx.astrbot_config["platform_settings"][
"reply_with_quote"
]
# 分段回复
self.enable_seg: bool = ctx.astrbot_config["platform_settings"][
"segmented_reply"
]["enable"]
self.only_llm_result = ctx.astrbot_config["platform_settings"][
"segmented_reply"
]["only_llm_result"]
self.interval_method = ctx.astrbot_config["platform_settings"][
"segmented_reply"
]["interval_method"]
self.log_base = float(
ctx.astrbot_config["platform_settings"]["segmented_reply"]["log_base"]
)
interval_str: str = ctx.astrbot_config["platform_settings"]["segmented_reply"][
"interval"
]
interval_str_ls = interval_str.replace(" ", "").split(",")
try:
self.interval = [float(t) for t in interval_str_ls]
except BaseException as e:
logger.error(f"解析分段回复的间隔时间失败。{e}")
self.interval = [1.5, 3.5]
logger.info(f"分段回复间隔时间:{self.interval}")
async def _word_cnt(self, text: str) -> int:
"""分段回复 统计字数"""
if all(ord(c) < 128 for c in text):
word_count = len(text.split())
else:
word_count = len([c for c in text if c.isalnum()])
return word_count
async def _calc_comp_interval(self, comp: BaseMessageComponent) -> float:
"""分段回复 计算间隔时间"""
if self.interval_method == "log":
if isinstance(comp, Comp.Plain):
wc = await self._word_cnt(comp.text)
i = math.log(wc + 1, self.log_base)
return random.uniform(i, i + 0.5)
else:
return random.uniform(1, 1.75)
else:
# random
return random.uniform(self.interval[0], self.interval[1])
async def _is_empty_message_chain(self, chain: list[BaseMessageComponent]):
"""检查消息链是否为空
Args:
chain (list[BaseMessageComponent]): 包含消息对象的列表
"""
if not chain:
return True
for comp in chain:
comp_type = type(comp)
# 检查组件类型是否在字典中
if comp_type in self._component_validators:
if self._component_validators[comp_type](comp):
return False
else:
logger.info(f"空内容检查: 无法识别的组件类型: {comp_type.__name__}")
# 如果所有组件都为空
return True
async def process(
self, event: AstrMessageEvent
) -> Union[None, AsyncGenerator[None, None]]:
result = event.get_result()
if result is None:
return
if result.result_content_type == ResultContentType.STREAMING_FINISH:
return
if result.result_content_type == ResultContentType.STREAMING_RESULT:
# 流式结果直接交付平台适配器处理
use_fallback = self.config.get("provider_settings", {}).get(
"streaming_segmented", False
)
logger.info(f"应用流式输出({event.get_platform_name()})")
await event._pre_send()
await event.send_streaming(result.async_stream, use_fallback)
await event._post_send()
return
elif len(result.chain) > 0:
# 检查路径映射
if mappings := self.platform_settings.get("path_mapping", []):
for idx, component in enumerate(result.chain):
if isinstance(component, Comp.File) and component.file:
# 支持 File 消息段的路径映射。
component.file = path_Mapping(mappings, component.file)
event.get_result().chain[idx] = component
await event._pre_send()
# 检查消息链是否为空
try:
if await self._is_empty_message_chain(result.chain):
logger.info("消息为空,跳过发送阶段")
event.clear_result()
event.stop_event()
return
except Exception as e:
logger.warning(f"空内容检查异常: {e}")
if self.enable_seg and (
(self.only_llm_result and result.is_llm_result())
or not self.only_llm_result
):
decorated_comps = []
if self.reply_with_mention:
for comp in result.chain:
if isinstance(comp, Comp.At):
decorated_comps.append(comp)
result.chain.remove(comp)
break
if self.reply_with_quote:
for comp in result.chain:
if isinstance(comp, Comp.Reply):
decorated_comps.append(comp)
result.chain.remove(comp)
break
# 分段回复
for comp in result.chain:
i = await self._calc_comp_interval(comp)
await asyncio.sleep(i)
try:
await event.send(MessageChain([*decorated_comps, comp]))
except Exception as e:
logger.error(f"发送消息失败: {e} chain: {result.chain}")
break
else:
try:
await event.send(result)
except Exception as e:
logger.error(traceback.format_exc())
logger.error(f"发送消息失败: {e} chain: {result.chain}")
await event._post_send()
logger.info(
f"AstrBot -> {event.get_sender_name()}/{event.get_sender_id()}: {event._outline_chain(result.chain)}"
)
handlers = star_handlers_registry.get_handlers_by_event_type(
EventType.OnAfterMessageSentEvent, platform_id=event.get_platform_id()
)
for handler in handlers:
try:
logger.debug(
f"hook(on_after_message_sent) -> {star_map[handler.handler_module_path].name} - {handler.handler_name}"
)
await handler.handler(event)
except BaseException:
logger.error(traceback.format_exc())
if event.is_stopped():
logger.info(
f"{star_map[handler.handler_module_path].name} - {handler.handler_name} 终止了事件传播。"
)
return
event.clear_result()

View File

@@ -0,0 +1,260 @@
import time
import re
import traceback
from typing import Union, AsyncGenerator
from ..stage import Stage, register_stage, registered_stages
from ..context import PipelineContext
from astrbot.core.platform.astr_message_event import AstrMessageEvent
from astrbot.core.message.message_event_result import ResultContentType
from astrbot.core.platform.message_type import MessageType
from astrbot.core import logger
from astrbot.core.message.components import Plain, Image, At, Reply, Record, File, Node
from astrbot.core import html_renderer
from astrbot.core.star.star_handler import star_handlers_registry, EventType
from astrbot.core.star.star import star_map
@register_stage
class ResultDecorateStage(Stage):
async def initialize(self, ctx: PipelineContext):
self.ctx = ctx
self.reply_prefix = ctx.astrbot_config["platform_settings"]["reply_prefix"]
self.reply_with_mention = ctx.astrbot_config["platform_settings"][
"reply_with_mention"
]
self.reply_with_quote = ctx.astrbot_config["platform_settings"][
"reply_with_quote"
]
self.t2i_word_threshold = ctx.astrbot_config["t2i_word_threshold"]
try:
self.t2i_word_threshold = int(self.t2i_word_threshold)
if self.t2i_word_threshold < 50:
self.t2i_word_threshold = 50
except BaseException:
self.t2i_word_threshold = 150
self.t2i_strategy = ctx.astrbot_config["t2i_strategy"]
self.t2i_use_network = self.t2i_strategy == "remote"
self.forward_threshold = ctx.astrbot_config["platform_settings"][
"forward_threshold"
]
# 分段回复
self.words_count_threshold = int(
ctx.astrbot_config["platform_settings"]["segmented_reply"][
"words_count_threshold"
]
)
self.enable_segmented_reply = ctx.astrbot_config["platform_settings"][
"segmented_reply"
]["enable"]
self.only_llm_result = ctx.astrbot_config["platform_settings"][
"segmented_reply"
]["only_llm_result"]
self.regex = ctx.astrbot_config["platform_settings"]["segmented_reply"]["regex"]
self.content_cleanup_rule = ctx.astrbot_config["platform_settings"][
"segmented_reply"
]["content_cleanup_rule"]
# exception
self.content_safe_check_reply = ctx.astrbot_config["content_safety"][
"also_use_in_response"
]
self.content_safe_check_stage = None
if self.content_safe_check_reply:
for stage in registered_stages:
if stage.__class__.__name__ == "ContentSafetyCheckStage":
self.content_safe_check_stage = stage
async def process(
self, event: AstrMessageEvent
) -> Union[None, AsyncGenerator[None, None]]:
result = event.get_result()
if result is None or not result.chain:
return
if result.result_content_type == ResultContentType.STREAMING_RESULT:
return
is_stream = result.result_content_type == ResultContentType.STREAMING_FINISH
# 回复时检查内容安全
if (
self.content_safe_check_reply
and self.content_safe_check_stage
and result.is_llm_result()
and not is_stream # 流式输出不检查内容安全
):
text = ""
for comp in result.chain:
if isinstance(comp, Plain):
text += comp.text
async for _ in self.content_safe_check_stage.process(
event, check_text=text
):
yield
# 发送消息前事件钩子
handlers = star_handlers_registry.get_handlers_by_event_type(
EventType.OnDecoratingResultEvent, platform_id=event.get_platform_id()
)
for handler in handlers:
try:
logger.debug(
f"hook(on_decorating_result) -> {star_map[handler.handler_module_path].name} - {handler.handler_name}"
)
if is_stream:
logger.warning(
"启用流式输出时,依赖发送消息前事件钩子的插件可能无法正常工作"
)
await handler.handler(event)
if event.get_result() is None or not event.get_result().chain:
logger.debug(
f"hook(on_decorating_result) -> {star_map[handler.handler_module_path].name} - {handler.handler_name} 将消息结果清空。"
)
except BaseException:
logger.error(traceback.format_exc())
if event.is_stopped():
logger.info(
f"{star_map[handler.handler_module_path].name} - {handler.handler_name} 终止了事件传播。"
)
return
# 流式输出不执行下面的逻辑
if is_stream:
logger.info("流式输出已启用,跳过结果装饰阶段")
return
# 需要再获取一次。插件可能直接对 chain 进行了替换。
result = event.get_result()
if result is None:
return
if len(result.chain) > 0:
# 回复前缀
if self.reply_prefix:
for comp in result.chain:
if isinstance(comp, Plain):
comp.text = self.reply_prefix + comp.text
break
# 分段回复
if self.enable_segmented_reply:
if (
self.only_llm_result and result.is_llm_result()
) or not self.only_llm_result:
new_chain = []
for comp in result.chain:
if isinstance(comp, Plain):
if len(comp.text) > self.words_count_threshold:
# 不分段回复
new_chain.append(comp)
continue
split_response = re.findall(
self.regex, comp.text, re.DOTALL | re.MULTILINE
)
if not split_response:
new_chain.append(comp)
continue
for seg in split_response:
if self.content_cleanup_rule:
seg = re.sub(self.content_cleanup_rule, "", seg)
if seg.strip():
new_chain.append(Plain(seg))
else:
# 非 Plain 类型的消息段不分段
new_chain.append(comp)
result.chain = new_chain
# TTS
if (
self.ctx.astrbot_config["provider_tts_settings"]["enable"]
and result.is_llm_result()
):
tts_provider = self.ctx.plugin_manager.context.provider_manager.curr_tts_provider_inst
new_chain = []
for comp in result.chain:
if isinstance(comp, Plain) and len(comp.text) > 1:
try:
logger.info("TTS 请求: " + comp.text)
audio_path = await tts_provider.get_audio(comp.text)
logger.info("TTS 结果: " + audio_path)
if audio_path:
new_chain.append(
Record(file=audio_path, url=audio_path)
)
if(self.ctx.astrbot_config["provider_tts_settings"]["dual_output"]):
new_chain.append(comp)
else:
logger.error(
f"由于 TTS 音频文件没找到,消息段转语音失败: {comp.text}"
)
new_chain.append(comp)
except BaseException:
logger.error(traceback.format_exc())
logger.error("TTS 失败,使用文本发送。")
new_chain.append(comp)
else:
new_chain.append(comp)
result.chain = new_chain
# 文本转图片
elif (
result.use_t2i_ is None and self.ctx.astrbot_config["t2i"]
) or result.use_t2i_:
plain_str = ""
for comp in result.chain:
if isinstance(comp, Plain):
plain_str += "\n\n" + comp.text
else:
break
if plain_str and len(plain_str) > self.t2i_word_threshold:
render_start = time.time()
try:
url = await html_renderer.render_t2i(
plain_str, return_url=True, use_network=self.t2i_use_network
)
except BaseException:
logger.error("文本转图片失败,使用文本发送。")
return
if time.time() - render_start > 3:
logger.warning(
"文本转图片耗时超过了 3 秒,如果觉得很慢可以使用 /t2i 关闭文本转图片模式。"
)
if url:
if url.startswith("http"):
result.chain = [Image.fromURL(url)]
else:
result.chain = [Image.fromFileSystem(url)]
# 触发转发消息
has_forwarded = False
if event.get_platform_name() == "aiocqhttp":
word_cnt = 0
for comp in result.chain:
if isinstance(comp, Plain):
word_cnt += len(comp.text)
if word_cnt > self.forward_threshold:
node = Node(
uin=event.get_self_id(), name="AstrBot", content=[*result.chain]
)
result.chain = [node]
has_forwarded = True
if not has_forwarded:
# at 回复
if (
self.reply_with_mention
and event.get_message_type() != MessageType.FRIEND_MESSAGE
):
result.chain.insert(
0, At(qq=event.get_sender_id(), name=event.get_sender_name())
)
if len(result.chain) > 1 and isinstance(result.chain[1], Plain):
result.chain[1].text = "\n" + result.chain[1].text
# 引用回复
if self.reply_with_quote:
if not any(isinstance(item, File) for item in result.chain):
result.chain.insert(0, Reply(id=event.message_obj.message_id))

View File

@@ -0,0 +1,79 @@
from . import STAGES_ORDER
from .stage import registered_stages
from .context import PipelineContext
from typing import AsyncGenerator
from astrbot.core.platform import AstrMessageEvent
from astrbot.core import logger
class PipelineScheduler:
"""管道调度器,负责调度各个阶段的执行"""
def __init__(self, context: PipelineContext):
registered_stages.sort(
key=lambda x: STAGES_ORDER.index(x.__class__.__name__)
) # 按照顺序排序
self.ctx = context # 上下文对象
async def initialize(self):
"""初始化管道调度器时, 初始化所有阶段"""
for stage in registered_stages:
# logger.debug(f"初始化阶段 {stage.__class__ .__name__}")
await stage.initialize(self.ctx)
async def _process_stages(self, event: AstrMessageEvent, from_stage=0):
"""依次执行各个阶段
Args:
event (AstrMessageEvent): 事件对象
from_stage (int): 从第几个阶段开始执行, 默认从0开始
"""
for i in range(from_stage, len(registered_stages)):
stage = registered_stages[i] # 获取当前要执行的阶段
# logger.debug(f"执行阶段 {stage.__class__ .__name__}")
coroutine = stage.process(
event
) # 调用阶段的process方法, 返回协程或者异步生成器
if isinstance(coroutine, AsyncGenerator):
# 如果返回的是异步生成器, 实现洋葱模型的核心
async for _ in coroutine:
# 此处是前置处理完成后的暂停点(yield), 下面开始执行后续阶段
if event.is_stopped():
logger.debug(
f"阶段 {stage.__class__.__name__} 已终止事件传播。"
)
break
# 递归调用, 处理所有后续阶段
await self._process_stages(event, i + 1)
# 此处是后续所有阶段处理完毕后返回的点, 执行后置处理
if event.is_stopped():
logger.debug(
f"阶段 {stage.__class__.__name__} 已终止事件传播。"
)
break
else:
# 如果返回的是普通协程(不含yield的async函数), 则不进入下一层(基线条件)
# 简单地等待它执行完成, 然后继续执行下一个阶段
await coroutine
if event.is_stopped():
logger.debug(f"阶段 {stage.__class__.__name__} 已终止事件传播。")
break
async def execute(self, event: AstrMessageEvent):
"""执行 pipeline
Args:
event (AstrMessageEvent): 事件对象
"""
await self._process_stages(event)
# 如果没有发送操作, 则发送一个空消息, 以便于后续的处理
if not event._has_send_oper and event.get_platform_name() == "webchat":
await event.send(None)
logger.debug("pipeline 执行完毕。")

View File

@@ -0,0 +1,110 @@
from __future__ import annotations
import abc
import inspect
import traceback
from astrbot.api import logger
from typing import List, AsyncGenerator, Union, Awaitable
from astrbot.core.platform.astr_message_event import AstrMessageEvent
from .context import PipelineContext
from astrbot.core.message.message_event_result import MessageEventResult, CommandResult
registered_stages: List[Stage] = [] # 维护了所有已注册的 Stage 实现类
def register_stage(cls):
"""一个简单的装饰器,用于注册 pipeline 包下的 Stage 实现类"""
registered_stages.append(cls())
return cls
class Stage(abc.ABC):
"""描述一个 Pipeline 的某个阶段"""
@abc.abstractmethod
async def initialize(self, ctx: PipelineContext) -> None:
"""初始化阶段
Args:
ctx (PipelineContext): 消息管道上下文对象, 包括配置和插件管理器
"""
raise NotImplementedError
@abc.abstractmethod
async def process(
self, event: AstrMessageEvent
) -> Union[None, AsyncGenerator[None, None]]:
"""处理事件
Args:
event (AstrMessageEvent): 事件对象,包含事件的相关信息
Returns:
Union[None, AsyncGenerator[None, None]]: 处理结果,可能是 None 或者异步生成器, 如果为 None 则表示不需要继续处理, 如果为异步生成器则表示需要继续处理(进入下一个阶段)
"""
raise NotImplementedError
async def _call_handler(
self,
ctx: PipelineContext,
event: AstrMessageEvent,
handler: Awaitable,
*args,
**kwargs,
) -> AsyncGenerator[None, None]:
"""执行事件处理函数并处理其返回结果
该方法负责调用处理函数并处理不同类型的返回值。它支持两种类型的处理函数:
1. 异步生成器: 实现洋葱模型每次yield都会将控制权交回上层
2. 协程: 执行一次并处理返回值
Args:
ctx (PipelineContext): 消息管道上下文对象
event (AstrMessageEvent): 待处理的事件对象
handler (Awaitable): 事件处理函数
*args: 传递给handler的位置参数
**kwargs: 传递给handler的关键字参数
Returns:
AsyncGenerator[None, None]: 异步生成器,用于在管道中传递控制流
"""
ready_to_call = None # 一个协程或者异步生成器(async def)
trace_ = None
try:
ready_to_call = handler(event, *args, **kwargs)
except TypeError as _:
# 向下兼容
trace_ = traceback.format_exc()
# 以前的handler会额外传入一个参数, 但是context对象实际上在插件实例中有一份
ready_to_call = handler(event, ctx.plugin_manager.context, *args, **kwargs)
if isinstance(ready_to_call, AsyncGenerator):
# 如果是一个异步生成器, 进入洋葱模型
_has_yielded = False # 是否返回过值
try:
async for ret in ready_to_call:
# 这里逐步执行异步生成器, 对于每个yield返回的ret, 执行下面的代码
# 返回值只能是 MessageEventResult 或者 None无返回值
_has_yielded = True
if isinstance(ret, (MessageEventResult, CommandResult)):
# 如果返回值是 MessageEventResult, 设置结果并继续
event.set_result(ret)
yield # 传递控制权给上一层的process函数
else:
# 如果返回值是 None, 则不设置结果并继续
# 继续执行后续阶段
yield ret # 传递控制权给上一层的process函数
if not _has_yielded:
# 如果这个异步生成器没有执行到yield分支
yield
except Exception as e:
logger.error(f"Previous Error: {trace_}")
raise e
elif inspect.iscoroutine(ready_to_call):
# 如果只是一个协程, 直接执行
ret = await ready_to_call
if isinstance(ret, (MessageEventResult, CommandResult)):
event.set_result(ret)
yield # 传递控制权给上一层的process函数
else:
yield ret # 传递控制权给上一层的process函数

View File

@@ -0,0 +1,165 @@
from ..stage import Stage, register_stage
from ..context import PipelineContext
from astrbot import logger
from typing import Union, AsyncGenerator
from astrbot.core.platform.astr_message_event import AstrMessageEvent
from astrbot.core.message.message_event_result import MessageEventResult, MessageChain
from astrbot.core.message.components import At
from astrbot.core.star.star_handler import star_handlers_registry, EventType
from astrbot.core.star.star import star_map
from astrbot.core.star.filter.permission import PermissionTypeFilter
@register_stage
class WakingCheckStage(Stage):
"""检查是否需要唤醒。唤醒机器人有如下几点条件:
1. 机器人被 @ 了
2. 机器人的消息被提到了
3. 以 wake_prefix 前缀开头,并且消息没有以 At 消息段开头
4. 插件Star的 handler filter 通过
5. 私聊情况下,位于 admins_id 列表中的管理员的消息(在白名单阶段中)
"""
async def initialize(self, ctx: PipelineContext) -> None:
"""初始化唤醒检查阶段
Args:
ctx (PipelineContext): 消息管道上下文对象, 包括配置和插件管理器
"""
self.ctx = ctx
self.no_permission_reply = self.ctx.astrbot_config["platform_settings"].get(
"no_permission_reply", True
)
# 私聊是否需要 wake_prefix 才能唤醒机器人
self.friend_message_needs_wake_prefix = self.ctx.astrbot_config[
"platform_settings"
].get("friend_message_needs_wake_prefix", False)
# 是否忽略机器人自己发送的消息
self.ignore_bot_self_message = self.ctx.astrbot_config["platform_settings"].get(
"ignore_bot_self_message", False
)
async def process(
self, event: AstrMessageEvent
) -> Union[None, AsyncGenerator[None, None]]:
if (
self.ignore_bot_self_message
and event.get_self_id() == event.get_sender_id()
):
# 忽略机器人自己发送的消息
event.stop_event()
return
# 设置 sender 身份
event.message_str = event.message_str.strip()
for admin_id in self.ctx.astrbot_config["admins_id"]:
if str(event.get_sender_id()) == admin_id:
event.role = "admin"
break
# 检查 wake
wake_prefixes = self.ctx.astrbot_config["wake_prefix"]
messages = event.get_messages()
is_wake = False
for wake_prefix in wake_prefixes:
if event.message_str.startswith(wake_prefix):
if (
not event.is_private_chat()
and isinstance(messages[0], At)
and str(messages[0].qq) != str(event.get_self_id())
and str(messages[0].qq) != "all"
):
# 如果是群聊,且第一个消息段是 At 消息,但不是 At 机器人或 At 全体成员,则不唤醒
break
is_wake = True
event.is_at_or_wake_command = True
event.is_wake = True
event.message_str = event.message_str[len(wake_prefix) :].strip()
break
if not is_wake:
# 检查是否有 at 消息
for message in messages:
if isinstance(message, At) and (
str(message.qq) == str(event.get_self_id())
or str(message.qq) == "all"
):
is_wake = True
event.is_wake = True
wake_prefix = ""
event.is_at_or_wake_command = True
break
# 检查是否是私聊
if event.is_private_chat() and not self.friend_message_needs_wake_prefix:
is_wake = True
event.is_wake = True
event.is_at_or_wake_command = True
wake_prefix = ""
# 检查插件的 handler filter
activated_handlers = []
handlers_parsed_params = {} # 注册了指令的 handler
for handler in star_handlers_registry.get_handlers_by_event_type(
EventType.AdapterMessageEvent
):
# filter 需满足 AND 逻辑关系
passed = True
permission_not_pass = False
permission_filter_raise_error = False
if len(handler.event_filters) == 0:
continue
for filter in handler.event_filters:
try:
if isinstance(filter, PermissionTypeFilter):
if not filter.filter(event, self.ctx.astrbot_config):
permission_not_pass = True
permission_filter_raise_error = filter.raise_error
else:
if not filter.filter(event, self.ctx.astrbot_config):
passed = False
break
except Exception as e:
await event.send(
MessageEventResult().message(
f"插件 {star_map[handler.handler_module_path].name}: {e}"
)
)
await event._post_send()
event.stop_event()
passed = False
break
if passed:
if permission_not_pass:
if not permission_filter_raise_error:
# 跳过
continue
if self.no_permission_reply:
await event.send(
MessageChain().message(
f"ID {event.get_sender_id()} 权限不足。通过 /sid 获取 ID 并请管理员添加。"
)
)
await event._post_send()
logger.info(
f"触发 {star_map[handler.handler_module_path].name} 时, 用户(ID={event.get_sender_id()}) 权限不足。"
)
event.stop_event()
return
is_wake = True
event.is_wake = True
activated_handlers.append(handler)
if "parsed_params" in event.get_extra():
handlers_parsed_params[handler.handler_full_name] = event.get_extra(
"parsed_params"
)
event.clear_extra()
event.set_extra("activated_handlers", activated_handlers)
event.set_extra("handlers_parsed_params", handlers_parsed_params)
if not is_wake:
event.stop_event()

View File

@@ -0,0 +1,65 @@
from ..stage import Stage, register_stage
from ..context import PipelineContext
from typing import AsyncGenerator, Union
from astrbot.core.platform.astr_message_event import AstrMessageEvent
from astrbot.core.platform.message_type import MessageType
from astrbot.core import logger
@register_stage
class WhitelistCheckStage(Stage):
"""检查是否在群聊/私聊白名单"""
async def initialize(self, ctx: PipelineContext) -> None:
self.enable_whitelist_check = ctx.astrbot_config["platform_settings"][
"enable_id_white_list"
]
self.whitelist = ctx.astrbot_config["platform_settings"]["id_whitelist"]
self.whitelist = [
str(i).strip() for i in self.whitelist if str(i).strip() != ""
]
self.wl_ignore_admin_on_group = ctx.astrbot_config["platform_settings"][
"wl_ignore_admin_on_group"
]
self.wl_ignore_admin_on_friend = ctx.astrbot_config["platform_settings"][
"wl_ignore_admin_on_friend"
]
self.wl_log = ctx.astrbot_config["platform_settings"]["id_whitelist_log"]
async def process(
self, event: AstrMessageEvent
) -> Union[None, AsyncGenerator[None, None]]:
if not self.enable_whitelist_check:
# 白名单检查未启用
return
if len(self.whitelist) == 0:
# 白名单为空,不检查
return
if event.get_platform_name() == "webchat":
# WebChat 豁免
return
# 检查是否在白名单
if self.wl_ignore_admin_on_group:
if (
event.role == "admin"
and event.get_message_type() == MessageType.GROUP_MESSAGE
):
return
if self.wl_ignore_admin_on_friend:
if (
event.role == "admin"
and event.get_message_type() == MessageType.FRIEND_MESSAGE
):
return
if (
event.unified_msg_origin not in self.whitelist
and str(event.get_group_id()).strip() not in self.whitelist
):
if self.wl_log:
logger.info(
f"会话 ID {event.unified_msg_origin} 不在会话白名单中,已终止事件传播。请在配置文件中添加该会话 ID 到白名单。"
)
event.stop_event()

View File

@@ -0,0 +1,14 @@
from .platform import Platform
from .astr_message_event import AstrMessageEvent
from .platform_metadata import PlatformMetadata
from .astrbot_message import AstrBotMessage, MessageMember, MessageType, Group
__all__ = [
"Platform",
"AstrMessageEvent",
"PlatformMetadata",
"AstrBotMessage",
"MessageMember",
"MessageType",
"Group",
]

Some files were not shown because too many files have changed in this diff Show More