mirror of https://github.com/Mai-with-u/MaiBot.git
解决与上游debug分支的冲突,优化output_manager实现
commit
e98c32a5e1
|
|
@ -193,9 +193,8 @@ cython_debug/
|
||||||
# jieba
|
# jieba
|
||||||
jieba.cache
|
jieba.cache
|
||||||
|
|
||||||
|
# .vscode
|
||||||
# vscode
|
!.vscode/settings.json
|
||||||
/.vscode
|
|
||||||
|
|
||||||
# direnv
|
# direnv
|
||||||
/.direnv
|
/.direnv
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"editor.formatOnSave": true
|
||||||
|
}
|
||||||
16
README.md
16
README.md
|
|
@ -29,16 +29,17 @@
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
> ⚠️ **注意事项**
|
> [!WARNING]
|
||||||
> - 项目处于活跃开发阶段,代码可能随时更改
|
> - 项目处于活跃开发阶段,代码可能随时更改
|
||||||
> - 文档未完善,有问题可以提交 Issue 或者 Discussion
|
> - 文档未完善,有问题可以提交 Issue 或者 Discussion
|
||||||
> - QQ机器人存在被限制风险,请自行了解,谨慎使用
|
> - QQ机器人存在被限制风险,请自行了解,谨慎使用
|
||||||
> - 由于持续迭代,可能存在一些已知或未知的bug
|
> - 由于持续迭代,可能存在一些已知或未知的bug
|
||||||
> - 由于开发中,可能消耗较多token
|
> - 由于开发中,可能消耗较多token
|
||||||
|
|
||||||
**交流群**: 766798517 一群人较多,建议加下面的(开发和建议相关讨论)不一定有空回复,会优先写文档和代码
|
## 💬交流群
|
||||||
**交流群**: 571780722 另一个群(开发和建议相关讨论)不一定有空回复,会优先写文档和代码
|
- [一群](https://qm.qq.com/q/VQ3XZrWgMs) 766798517 ,建议加下面的(开发和建议相关讨论)不一定有空回复,会优先写文档和代码
|
||||||
**交流群**: 1035228475 另一个群(开发和建议相关讨论)不一定有空回复,会优先写文档和代码
|
- [二群](https://qm.qq.com/q/RzmCiRtHEW) 571780722 (开发和建议相关讨论)不一定有空回复,会优先写文档和代码
|
||||||
|
- [三群](https://qm.qq.com/q/wlH5eT8OmQ) 1035228475(开发和建议相关讨论)不一定有空回复,会优先写文档和代码
|
||||||
|
|
||||||
**其他平台版本**
|
**其他平台版本**
|
||||||
|
|
||||||
|
|
@ -51,7 +52,7 @@
|
||||||
|
|
||||||
### 部署方式
|
### 部署方式
|
||||||
|
|
||||||
- 📦 **Windows 一键傻瓜式部署**:请运行项目根目录中的 ```run.bat```,部署完成后请参照后续配置指南进行配置
|
- 📦 **Windows 一键傻瓜式部署**:请运行项目根目录中的 `run.bat`,部署完成后请参照后续配置指南进行配置
|
||||||
|
|
||||||
- [📦 Windows 手动部署指南 ](docs/manual_deploy_windows.md)
|
- [📦 Windows 手动部署指南 ](docs/manual_deploy_windows.md)
|
||||||
|
|
||||||
|
|
@ -139,9 +140,10 @@
|
||||||
|
|
||||||
|
|
||||||
## 📌 注意事项
|
## 📌 注意事项
|
||||||
SengokuCola已得到大脑升级
|
|
||||||
|
|
||||||
> ⚠️ **警告**:本应用生成内容来自人工智能模型,由 AI 生成,请仔细甄别,请勿用于违反法律的用途,AI生成内容不代表本人观点和立场。
|
SengokuCola~~纯编程外行,面向cursor编程,很多代码写得不好多多包涵~~已得到大脑升级
|
||||||
|
> [!WARNING]
|
||||||
|
> 本应用生成内容来自人工智能模型,由 AI 生成,请仔细甄别,请勿用于违反法律的用途,AI生成内容不代表本人观点和立场。
|
||||||
|
|
||||||
## 致谢
|
## 致谢
|
||||||
[nonebot2](https://github.com/nonebot/nonebot2): 跨平台 Python 异步聊天机器人框架
|
[nonebot2](https://github.com/nonebot/nonebot2): 跨平台 Python 异步聊天机器人框架
|
||||||
|
|
|
||||||
69
bot.py
69
bot.py
|
|
@ -1,20 +1,23 @@
|
||||||
|
import asyncio
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import nonebot
|
import nonebot
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
import uvicorn
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from nonebot.adapters.onebot.v11 import Adapter
|
from nonebot.adapters.onebot.v11 import Adapter
|
||||||
import platform
|
import platform
|
||||||
from src.common.output_manager import OutputManager
|
from src.common.output_manager import output_manager
|
||||||
|
|
||||||
# 获取没有加载env时的环境变量
|
# 获取没有加载env时的环境变量
|
||||||
env_mask = {key: os.getenv(key) for key in os.environ}
|
env_mask = {key: os.getenv(key) for key in os.environ}
|
||||||
|
|
||||||
# 初始化输出管理器
|
# 初始化输出管理器和uvicorn服务器
|
||||||
output_manager = OutputManager()
|
uvicorn_server = None
|
||||||
|
|
||||||
|
|
||||||
def easter_egg():
|
def easter_egg():
|
||||||
|
|
@ -120,7 +123,8 @@ def load_logger():
|
||||||
"#777777>|</> <cyan>{name:.<8}</cyan>:<cyan>{function:.<8}</cyan>:<cyan>{line: >4}</cyan> <fg "
|
"#777777>|</> <cyan>{name:.<8}</cyan>:<cyan>{function:.<8}</cyan>:<cyan>{line: >4}</cyan> <fg "
|
||||||
"#777777>-</> <level>{message}</level>",
|
"#777777>-</> <level>{message}</level>",
|
||||||
colorize=True,
|
colorize=True,
|
||||||
level=os.getenv("LOG_LEVEL", "DEBUG"), # 根据环境设置日志级别,默认为INFO
|
level=os.getenv("LOG_LEVEL", "INFO"), # 根据环境设置日志级别,默认为INFO
|
||||||
|
filter=lambda record: "nonebot" not in record["name"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -157,7 +161,39 @@ def scan_provider(env_config: dict):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
async def graceful_shutdown():
|
||||||
|
try:
|
||||||
|
global uvicorn_server
|
||||||
|
if uvicorn_server:
|
||||||
|
uvicorn_server.force_exit = True # 强制退出
|
||||||
|
await uvicorn_server.shutdown()
|
||||||
|
|
||||||
|
tasks = [t for t in asyncio.all_tasks() if t is not asyncio.current_task()]
|
||||||
|
for task in tasks:
|
||||||
|
task.cancel()
|
||||||
|
await asyncio.gather(*tasks, return_exceptions=True)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"麦麦关闭失败: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
async def uvicorn_main():
|
||||||
|
global uvicorn_server
|
||||||
|
config = uvicorn.Config(
|
||||||
|
app="__main__:app",
|
||||||
|
host=os.getenv("HOST", "127.0.0.1"),
|
||||||
|
port=int(os.getenv("PORT", 8080)),
|
||||||
|
reload=os.getenv("ENVIRONMENT") == "dev",
|
||||||
|
timeout_graceful_shutdown=5,
|
||||||
|
log_config=None,
|
||||||
|
access_log=False,
|
||||||
|
)
|
||||||
|
server = uvicorn.Server(config)
|
||||||
|
uvicorn_server = server
|
||||||
|
await server.serve()
|
||||||
|
|
||||||
|
|
||||||
|
def raw_main():
|
||||||
# 利用 TZ 环境变量设定程序工作的时区
|
# 利用 TZ 环境变量设定程序工作的时区
|
||||||
# 仅保证行为一致,不依赖 localtime(),实际对生产环境几乎没有作用
|
# 仅保证行为一致,不依赖 localtime(),实际对生产环境几乎没有作用
|
||||||
if platform.system().lower() != "windows":
|
if platform.system().lower() != "windows":
|
||||||
|
|
@ -190,6 +226,7 @@ if __name__ == "__main__":
|
||||||
nonebot.init(**base_config, **env_config)
|
nonebot.init(**base_config, **env_config)
|
||||||
|
|
||||||
# 注册适配器
|
# 注册适配器
|
||||||
|
global driver
|
||||||
driver = nonebot.get_driver()
|
driver = nonebot.get_driver()
|
||||||
driver.register_adapter(Adapter)
|
driver.register_adapter(Adapter)
|
||||||
|
|
||||||
|
|
@ -206,7 +243,27 @@ if __name__ == "__main__":
|
||||||
atexit.register(cleanup)
|
atexit.register(cleanup)
|
||||||
|
|
||||||
# 启动
|
# 启动
|
||||||
nonebot.run()
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
|
||||||
|
try:
|
||||||
|
raw_main()
|
||||||
|
|
||||||
|
global app
|
||||||
|
app = nonebot.get_asgi()
|
||||||
|
|
||||||
|
loop = asyncio.new_event_loop()
|
||||||
|
asyncio.set_event_loop(loop)
|
||||||
|
loop.run_until_complete(uvicorn_main())
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
logger.warning("麦麦会努力做的更好的!正在停止中......")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"主程序异常: {e}")
|
||||||
|
finally:
|
||||||
|
loop.run_until_complete(graceful_shutdown())
|
||||||
|
loop.close()
|
||||||
|
logger.info("进程终止完毕,麦麦开始休眠......下次再见哦!")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,12 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [0.0.5] - 2025-3-11
|
||||||
|
### Added
|
||||||
|
- 新增了 `alias_names` 配置项,用于指定麦麦的别名。
|
||||||
|
|
||||||
## [0.0.4] - 2025-3-9
|
## [0.0.4] - 2025-3-9
|
||||||
### Added
|
### Added
|
||||||
- 新增了 `memory_ban_words` 配置项,用于指定不希望记忆的词汇。
|
- 新增了 `memory_ban_words` 配置项,用于指定不希望记忆的词汇。
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,6 @@ services:
|
||||||
- NAPCAT_UID=${NAPCAT_UID}
|
- NAPCAT_UID=${NAPCAT_UID}
|
||||||
- NAPCAT_GID=${NAPCAT_GID} # 让 NapCat 获取当前用户 GID,UID,防止权限问题
|
- NAPCAT_GID=${NAPCAT_GID} # 让 NapCat 获取当前用户 GID,UID,防止权限问题
|
||||||
ports:
|
ports:
|
||||||
- 3000:3000
|
|
||||||
- 3001:3001
|
|
||||||
- 6099:6099
|
- 6099:6099
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
|
|
@ -19,7 +17,7 @@ services:
|
||||||
mongodb:
|
mongodb:
|
||||||
container_name: mongodb
|
container_name: mongodb
|
||||||
environment:
|
environment:
|
||||||
- tz=Asia/Shanghai
|
- TZ=Asia/Shanghai
|
||||||
# - MONGO_INITDB_ROOT_USERNAME=your_username
|
# - MONGO_INITDB_ROOT_USERNAME=your_username
|
||||||
# - MONGO_INITDB_ROOT_PASSWORD=your_password
|
# - MONGO_INITDB_ROOT_PASSWORD=your_password
|
||||||
expose:
|
expose:
|
||||||
|
|
|
||||||
|
|
@ -1,67 +1,97 @@
|
||||||
# 🐳 Docker 部署指南
|
# 🐳 Docker 部署指南
|
||||||
|
|
||||||
## 部署步骤(推荐,但不一定是最新)
|
## 部署步骤 (推荐,但不一定是最新)
|
||||||
|
|
||||||
|
**"更新镜像与容器"部分在本文档 [Part 6](#6-更新镜像与容器)**
|
||||||
|
|
||||||
|
### 0. 前提说明
|
||||||
|
|
||||||
|
**本文假设读者已具备一定的 Docker 基础知识。若您对 Docker 不熟悉,建议先参考相关教程或文档进行学习,或选择使用 [📦Linux手动部署指南](./manual_deploy_linux.md) 或 [📦Windows手动部署指南](./manual_deploy_windows.md) 。**
|
||||||
|
|
||||||
|
|
||||||
### 1. 获取Docker配置文件:
|
### 1. 获取Docker配置文件
|
||||||
|
|
||||||
|
- 建议先单独创建好一个文件夹并进入,作为工作目录
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
wget https://raw.githubusercontent.com/SengokuCola/MaiMBot/main/docker-compose.yml -O docker-compose.yml
|
wget https://raw.githubusercontent.com/SengokuCola/MaiMBot/main/docker-compose.yml -O docker-compose.yml
|
||||||
```
|
```
|
||||||
|
|
||||||
- 若需要启用MongoDB数据库的用户名和密码,可进入docker-compose.yml,取消MongoDB处的注释并修改变量`=`后方的值为你的用户名和密码\
|
- 若需要启用MongoDB数据库的用户名和密码,可进入docker-compose.yml,取消MongoDB处的注释并修改变量旁 `=` 后方的值为你的用户名和密码\
|
||||||
修改后请注意在之后配置`.env.prod`文件时指定MongoDB数据库的用户名密码
|
修改后请注意在之后配置 `.env.prod` 文件时指定MongoDB数据库的用户名密码
|
||||||
|
|
||||||
|
|
||||||
### 2. 启动服务:
|
### 2. 启动服务
|
||||||
|
|
||||||
- **!!! 请在第一次启动前确保当前工作目录下`.env.prod`与`bot_config.toml`文件存在 !!!**\
|
- **!!! 请在第一次启动前确保当前工作目录下 `.env.prod` 与 `bot_config.toml` 文件存在 !!!**\
|
||||||
由于Docker文件映射行为的特殊性,若宿主机的映射路径不存在,可能导致意外的目录创建,而不会创建文件,由于此处需要文件映射到文件,需提前确保文件存在且路径正确,可使用如下命令:
|
由于Docker文件映射行为的特殊性,若宿主机的映射路径不存在,可能导致意外的目录创建,而不会创建文件,由于此处需要文件映射到文件,需提前确保文件存在且路径正确,可使用如下命令:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
touch .env.prod
|
touch .env.prod
|
||||||
touch bot_config.toml
|
touch bot_config.toml
|
||||||
```
|
```
|
||||||
|
|
||||||
- 启动Docker容器:
|
- 启动Docker容器:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
NAPCAT_UID=$(id -u) NAPCAT_GID=$(id -g) docker compose up -d
|
NAPCAT_UID=$(id -u) NAPCAT_GID=$(id -g) docker compose up -d
|
||||||
|
# 旧版Docker中可能找不到docker compose,请使用docker-compose工具替代
|
||||||
|
NAPCAT_UID=$(id -u) NAPCAT_GID=$(id -g) docker-compose up -d
|
||||||
```
|
```
|
||||||
|
|
||||||
- 旧版Docker中可能找不到docker compose,请使用docker-compose工具替代
|
|
||||||
|
|
||||||
|
### 3. 修改配置并重启Docker
|
||||||
|
|
||||||
### 3. 修改配置并重启Docker:
|
- 请前往 [🎀新手配置指南](./installation_cute.md) 或 [⚙️标准配置指南](./installation_standard.md) 完成 `.env.prod` 与 `bot_config.toml` 配置文件的编写\
|
||||||
|
**需要注意 `.env.prod` 中HOST处IP的填写,Docker中部署和系统中直接安装的配置会有所不同**
|
||||||
- 请前往 [🎀 新手配置指南](docs/installation_cute.md) 或 [⚙️ 标准配置指南](docs/installation_standard.md) 完成`.env.prod`与`bot_config.toml`配置文件的编写\
|
|
||||||
**需要注意`.env.prod`中HOST处IP的填写,Docker中部署和系统中直接安装的配置会有所不同**
|
|
||||||
|
|
||||||
- 重启Docker容器:
|
- 重启Docker容器:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker restart maimbot # 若修改过容器名称则替换maimbot为你自定的名臣
|
docker restart maimbot # 若修改过容器名称则替换maimbot为你自定的名称
|
||||||
```
|
```
|
||||||
|
|
||||||
- 下方命令可以但不推荐,只是同时重启NapCat、MongoDB、MaiMBot三个服务
|
- 下方命令可以但不推荐,只是同时重启NapCat、MongoDB、MaiMBot三个服务
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
NAPCAT_UID=$(id -u) NAPCAT_GID=$(id -g) docker compose restart
|
NAPCAT_UID=$(id -u) NAPCAT_GID=$(id -g) docker compose restart
|
||||||
|
# 旧版Docker中可能找不到docker compose,请使用docker-compose工具替代
|
||||||
|
NAPCAT_UID=$(id -u) NAPCAT_GID=$(id -g) docker-compose restart
|
||||||
```
|
```
|
||||||
|
|
||||||
- 旧版Docker中可能找不到docker compose,请使用docker-compose工具替代
|
|
||||||
|
|
||||||
|
|
||||||
### 4. 登入NapCat管理页添加反向WebSocket
|
### 4. 登入NapCat管理页添加反向WebSocket
|
||||||
|
|
||||||
- 在浏览器地址栏输入`http://<宿主机IP>:6099/`进入NapCat的管理Web页,添加一个Websocket客户端
|
- 在浏览器地址栏输入 `http://<宿主机IP>:6099/` 进入NapCat的管理Web页,添加一个Websocket客户端
|
||||||
|
|
||||||
> 网络配置 -> 新建 -> Websocket客户端
|
> 网络配置 -> 新建 -> Websocket客户端
|
||||||
|
|
||||||
- Websocket客户端的名称自定,URL栏填入`ws://maimbot:8080/onebot/v11/ws`,启用并保存即可\
|
- Websocket客户端的名称自定,URL栏填入 `ws://maimbot:8080/onebot/v11/ws`,启用并保存即可\
|
||||||
(若修改过容器名称则替换maimbot为你自定的名称)
|
(若修改过容器名称则替换maimbot为你自定的名称)
|
||||||
|
|
||||||
|
|
||||||
### 5. 愉快地和麦麦对话吧!
|
### 5. 部署完成,愉快地和麦麦对话吧!
|
||||||
|
|
||||||
|
|
||||||
|
### 6. 更新镜像与容器
|
||||||
|
|
||||||
|
- 拉取最新镜像
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker-compose pull
|
||||||
|
```
|
||||||
|
|
||||||
|
- 执行启动容器指令,该指令会自动重建镜像有更新的容器并启动
|
||||||
|
|
||||||
|
```bash
|
||||||
|
NAPCAT_UID=$(id -u) NAPCAT_GID=$(id -g) docker compose up -d
|
||||||
|
# 旧版Docker中可能找不到docker compose,请使用docker-compose工具替代
|
||||||
|
NAPCAT_UID=$(id -u) NAPCAT_GID=$(id -g) docker-compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
## ⚠️ 注意事项
|
## ⚠️ 注意事项
|
||||||
|
|
||||||
- 目前部署方案仍在测试中,可能存在未知问题
|
- 目前部署方案仍在测试中,可能存在未知问题
|
||||||
- 配置文件中的API密钥请妥善保管,不要泄露
|
- 配置文件中的API密钥请妥善保管,不要泄露
|
||||||
- 建议先在测试环境中运行,确认无误后再部署到生产环境
|
- 建议先在测试环境中运行,确认无误后再部署到生产环境
|
||||||
|
|
@ -110,7 +110,8 @@ PLUGINS=["src2.plugins.chat"] # 这里是机器人的插件列表呢
|
||||||
```toml
|
```toml
|
||||||
[bot]
|
[bot]
|
||||||
qq = "把这里改成你的机器人QQ号喵" # 填写你的机器人QQ号
|
qq = "把这里改成你的机器人QQ号喵" # 填写你的机器人QQ号
|
||||||
nickname = "麦麦" # 机器人的名字,你可以改成你喜欢的任何名字哦
|
nickname = "麦麦" # 机器人的名字,你可以改成你喜欢的任何名字哦,建议和机器人QQ名称/群昵称一样哦
|
||||||
|
alias_names = ["小麦", "阿麦"] # 也可以用这个招呼机器人,可以不设置呢
|
||||||
|
|
||||||
[personality]
|
[personality]
|
||||||
# 这里可以设置机器人的性格呢,让它更有趣一些喵
|
# 这里可以设置机器人的性格呢,让它更有趣一些喵
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
## API配置说明
|
## API配置说明
|
||||||
|
|
||||||
`.env.prod`和`bot_config.toml`中的API配置关系如下:
|
`.env.prod` 和 `bot_config.toml` 中的API配置关系如下:
|
||||||
|
|
||||||
### 在.env.prod中定义API凭证:
|
### 在.env.prod中定义API凭证:
|
||||||
```ini
|
```ini
|
||||||
|
|
@ -72,6 +72,9 @@ PLUGINS=["src2.plugins.chat"]
|
||||||
[bot]
|
[bot]
|
||||||
qq = "机器人QQ号" # 必填
|
qq = "机器人QQ号" # 必填
|
||||||
nickname = "麦麦" # 机器人昵称
|
nickname = "麦麦" # 机器人昵称
|
||||||
|
# alias_names: 配置机器人可使用的别名。当机器人在群聊或对话中被调用时,别名可以作为直接命令或提及机器人的关键字使用。
|
||||||
|
# 该配置项为字符串数组。例如: ["小麦", "阿麦"]
|
||||||
|
alias_names = ["小麦", "阿麦"] # 机器人别名
|
||||||
|
|
||||||
[personality]
|
[personality]
|
||||||
prompt_personality = [
|
prompt_personality = [
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ pip install -r requirements.txt
|
||||||
|
|
||||||
## 数据库配置
|
## 数据库配置
|
||||||
### 3️⃣ **安装并启动MongoDB**
|
### 3️⃣ **安装并启动MongoDB**
|
||||||
- 安装与启动:Debian参考[官方文档](https://docs.mongodb.com/manual/tutorial/install-mongodb-on-debian/),Ubuntu参考[官方文档](https://docs.mongodb.com/manual/tutorial/install-mongodb-on-ubuntu/)
|
- 安装与启动: Debian参考[官方文档](https://docs.mongodb.com/manual/tutorial/install-mongodb-on-debian/),Ubuntu参考[官方文档](https://docs.mongodb.com/manual/tutorial/install-mongodb-on-ubuntu/)
|
||||||
|
|
||||||
- 默认连接本地27017端口
|
- 默认连接本地27017端口
|
||||||
---
|
---
|
||||||
|
|
@ -76,15 +76,14 @@ pip install -r requirements.txt
|
||||||
|
|
||||||
- 参考[NapCat官方文档](https://www.napcat.wiki/guide/boot/Shell#napcat-installer-linux%E4%B8%80%E9%94%AE%E4%BD%BF%E7%94%A8%E8%84%9A%E6%9C%AC-%E6%94%AF%E6%8C%81ubuntu-20-debian-10-centos9)安装
|
- 参考[NapCat官方文档](https://www.napcat.wiki/guide/boot/Shell#napcat-installer-linux%E4%B8%80%E9%94%AE%E4%BD%BF%E7%94%A8%E8%84%9A%E6%9C%AC-%E6%94%AF%E6%8C%81ubuntu-20-debian-10-centos9)安装
|
||||||
|
|
||||||
- 使用QQ小号登录,添加反向WS地址:
|
- 使用QQ小号登录,添加反向WS地址: `ws://127.0.0.1:8080/onebot/v11/ws`
|
||||||
`ws://127.0.0.1:8080/onebot/v11/ws`
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 配置文件设置
|
## 配置文件设置
|
||||||
### 5️⃣ **配置文件设置,让麦麦Bot正常工作**
|
### 5️⃣ **配置文件设置,让麦麦Bot正常工作**
|
||||||
- 修改环境配置文件:`.env.prod`
|
- 修改环境配置文件: `.env.prod`
|
||||||
- 修改机器人配置文件:`bot_config.toml`
|
- 修改机器人配置文件: `bot_config.toml`
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
@ -107,9 +106,9 @@ python3 bot.py
|
||||||
---
|
---
|
||||||
|
|
||||||
## 常见问题
|
## 常见问题
|
||||||
🔧 权限问题:在命令前加`sudo`
|
🔧 权限问题: 在命令前加 `sudo`
|
||||||
🔌 端口占用:使用`sudo lsof -i :8080`查看端口占用
|
🔌 端口占用: 使用 `sudo lsof -i :8080` 查看端口占用
|
||||||
🛡️ 防火墙:确保8080/27017端口开放
|
🛡️ 防火墙: 确保8080/27017端口开放
|
||||||
```bash
|
```bash
|
||||||
sudo ufw allow 8080/tcp
|
sudo ufw allow 8080/tcp
|
||||||
sudo ufw allow 27017/tcp
|
sudo ufw allow 27017/tcp
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@
|
||||||
|
|
||||||
在创建虚拟环境之前,请确保你的电脑上安装了Python 3.9及以上版本。如果没有,可以按以下步骤安装:
|
在创建虚拟环境之前,请确保你的电脑上安装了Python 3.9及以上版本。如果没有,可以按以下步骤安装:
|
||||||
|
|
||||||
1. 访问Python官网下载页面:https://www.python.org/downloads/release/python-3913/
|
1. 访问Python官网下载页面: https://www.python.org/downloads/release/python-3913/
|
||||||
2. 下载Windows安装程序 (64-bit): `python-3.9.13-amd64.exe`
|
2. 下载Windows安装程序 (64-bit): `python-3.9.13-amd64.exe`
|
||||||
3. 运行安装程序,并确保勾选"Add Python 3.9 to PATH"选项
|
3. 运行安装程序,并确保勾选"Add Python 3.9 to PATH"选项
|
||||||
4. 点击"Install Now"开始安装
|
4. 点击"Install Now"开始安装
|
||||||
|
|
@ -79,11 +79,11 @@ pip install -r requirements.txt
|
||||||
|
|
||||||
### 3️⃣ **配置NapCat,让麦麦bot与qq取得联系**
|
### 3️⃣ **配置NapCat,让麦麦bot与qq取得联系**
|
||||||
- 安装并登录NapCat(用你的qq小号)
|
- 安装并登录NapCat(用你的qq小号)
|
||||||
- 添加反向WS:`ws://127.0.0.1:8080/onebot/v11/ws`
|
- 添加反向WS: `ws://127.0.0.1:8080/onebot/v11/ws`
|
||||||
|
|
||||||
### 4️⃣ **配置文件设置,让麦麦Bot正常工作**
|
### 4️⃣ **配置文件设置,让麦麦Bot正常工作**
|
||||||
- 修改环境配置文件:`.env.prod`
|
- 修改环境配置文件: `.env.prod`
|
||||||
- 修改机器人配置文件:`bot_config.toml`
|
- 修改机器人配置文件: `bot_config.toml`
|
||||||
|
|
||||||
### 5️⃣ **启动麦麦机器人**
|
### 5️⃣ **启动麦麦机器人**
|
||||||
- 打开命令行,cd到对应路径
|
- 打开命令行,cd到对应路径
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@
|
||||||
|
|
||||||
pythonEnv = pkgs.python3.withPackages (
|
pythonEnv = pkgs.python3.withPackages (
|
||||||
ps: with ps; [
|
ps: with ps; [
|
||||||
|
ruff
|
||||||
pymongo
|
pymongo
|
||||||
python-dotenv
|
python-dotenv
|
||||||
pydantic
|
pydantic
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,51 @@
|
||||||
[project]
|
[project]
|
||||||
name = "Megbot"
|
name = "MaiMaiBot"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
description = "New Bot Project"
|
description = "MaiMaiBot"
|
||||||
|
|
||||||
[tool.nonebot]
|
[tool.nonebot]
|
||||||
plugins = ["src.plugins.chat"]
|
plugins = ["src.plugins.chat"]
|
||||||
plugin_dirs = ["src/plugins"]
|
plugin_dirs = ["src/plugins"]
|
||||||
|
|
||||||
[tool.ruff]
|
[tool.ruff]
|
||||||
# 设置 Python 版本
|
|
||||||
target-version = "py39"
|
include = ["*.py"]
|
||||||
|
|
||||||
|
# 行长度设置
|
||||||
|
line-length = 120
|
||||||
|
|
||||||
|
[tool.ruff.lint]
|
||||||
|
fixable = ["ALL"]
|
||||||
|
unfixable = []
|
||||||
|
|
||||||
|
# 如果一个变量的名称以下划线开头,即使它未被使用,也不应该被视为错误或警告。
|
||||||
|
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
|
||||||
|
|
||||||
# 启用的规则
|
# 启用的规则
|
||||||
select = [
|
select = [
|
||||||
"E", # pycodestyle 错误
|
"E", # pycodestyle 错误
|
||||||
"F", # pyflakes
|
"F", # pyflakes
|
||||||
"I", # isort
|
"B", # flake8-bugbear
|
||||||
"B", # flake8-bugbear
|
|
||||||
]
|
]
|
||||||
|
|
||||||
# 行长度设置
|
ignore = ["E711"]
|
||||||
line-length = 88
|
|
||||||
|
[tool.ruff.format]
|
||||||
|
docstring-code-format = true
|
||||||
|
indent-style = "space"
|
||||||
|
|
||||||
|
|
||||||
|
# 使用双引号表示字符串
|
||||||
|
quote-style = "double"
|
||||||
|
|
||||||
|
# 尊重魔法尾随逗号
|
||||||
|
# 例如:
|
||||||
|
# items = [
|
||||||
|
# "apple",
|
||||||
|
# "banana",
|
||||||
|
# "cherry",
|
||||||
|
# ]
|
||||||
|
skip-magic-trailing-comma = false
|
||||||
|
|
||||||
|
# 自动检测合适的换行符
|
||||||
|
line-ending = "auto"
|
||||||
|
|
|
||||||
BIN
requirements.txt
BIN
requirements.txt
Binary file not shown.
|
|
@ -7,8 +7,8 @@ import websockets
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Dict, List, Any, Optional, Set, Callable
|
from typing import Dict, List, Any, Optional, Set, Callable
|
||||||
import logging
|
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
class OutputCapture:
|
class OutputCapture:
|
||||||
|
|
@ -45,9 +45,10 @@ class OutputCapture:
|
||||||
class WebSocketServer:
|
class WebSocketServer:
|
||||||
"""WebSocket服务器,用于将捕获的输出发送到前端"""
|
"""WebSocket服务器,用于将捕获的输出发送到前端"""
|
||||||
|
|
||||||
def __init__(self, host: str = "localhost", port: int = 8765):
|
def __init__(self, host: str = None, port: int = None):
|
||||||
self.host = host
|
# 从环境变量或配置获取主机和端口
|
||||||
self.port = port
|
self.host = host or os.getenv("WEBSOCKET_HOST", "localhost")
|
||||||
|
self.port = port or int(os.getenv("WEBSOCKET_PORT", "8765"))
|
||||||
self.server = None
|
self.server = None
|
||||||
self.clients: Set[websockets.WebSocketServerProtocol] = set()
|
self.clients: Set[websockets.WebSocketServerProtocol] = set()
|
||||||
self.running = False
|
self.running = False
|
||||||
|
|
@ -68,7 +69,7 @@ class WebSocketServer:
|
||||||
)
|
)
|
||||||
|
|
||||||
# 发送历史记录
|
# 发送历史记录
|
||||||
history = OutputManager().get_message_history()
|
history = output_manager.get_message_history()
|
||||||
await websocket.send(
|
await websocket.send(
|
||||||
json.dumps(
|
json.dumps(
|
||||||
{
|
{
|
||||||
|
|
@ -93,7 +94,9 @@ class WebSocketServer:
|
||||||
return
|
return
|
||||||
|
|
||||||
message_json = json.dumps(message)
|
message_json = json.dumps(message)
|
||||||
await asyncio.gather(*[client.send(message_json) for client in self.clients])
|
tasks = [client.send(message_json) for client in self.clients]
|
||||||
|
if tasks:
|
||||||
|
await asyncio.gather(*tasks)
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
"""启动WebSocket服务器"""
|
"""启动WebSocket服务器"""
|
||||||
|
|
@ -134,30 +137,25 @@ class WebSocketServer:
|
||||||
logger.info("WebSocket服务器已停止")
|
logger.info("WebSocket服务器已停止")
|
||||||
|
|
||||||
|
|
||||||
|
# 使用threading.Lock进行初始化保护
|
||||||
|
_output_manager_lock = threading.Lock()
|
||||||
|
_output_manager_initialized = False
|
||||||
|
|
||||||
|
|
||||||
class OutputManager:
|
class OutputManager:
|
||||||
"""单例模式的输出管理器"""
|
"""单例模式的输出管理器"""
|
||||||
|
|
||||||
_instance = None
|
|
||||||
_lock = threading.Lock()
|
|
||||||
|
|
||||||
def __new__(cls):
|
|
||||||
with cls._lock:
|
|
||||||
if cls._instance is None:
|
|
||||||
cls._instance = super(OutputManager, cls).__new__(cls)
|
|
||||||
cls._instance._initialized = False
|
|
||||||
return cls._instance
|
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
if self._initialized:
|
global _output_manager_initialized
|
||||||
return
|
with _output_manager_lock:
|
||||||
|
if not _output_manager_initialized:
|
||||||
# 初始化属性
|
# 初始化属性
|
||||||
self.stdout_capture = None
|
self.stdout_capture = None
|
||||||
self.stderr_capture = None
|
self.stderr_capture = None
|
||||||
self.websocket_server = WebSocketServer()
|
self.websocket_server = WebSocketServer()
|
||||||
self.message_history: List[Dict[str, Any]] = []
|
self.message_history: List[Dict[str, Any]] = []
|
||||||
self.max_history_size = 1000 # 最大历史记录数量
|
self.max_history_size = 1000 # 最大历史记录数量
|
||||||
self._initialized = True
|
_output_manager_initialized = True
|
||||||
|
|
||||||
def start_capture(self):
|
def start_capture(self):
|
||||||
"""开始捕获标准输出和错误输出"""
|
"""开始捕获标准输出和错误输出"""
|
||||||
|
|
@ -249,6 +247,10 @@ class OutputManager:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# 创建全局单例实例
|
||||||
|
output_manager = OutputManager()
|
||||||
|
|
||||||
|
|
||||||
# 示例用法
|
# 示例用法
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
# 启动输出管理器
|
# 启动输出管理器
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,7 @@ class ReasoningGUI:
|
||||||
self.db = Database.get_instance().db
|
self.db = Database.get_instance().db
|
||||||
logger.success("数据库初始化成功")
|
logger.success("数据库初始化成功")
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.exception(f"数据库初始化失败")
|
logger.exception("数据库初始化失败")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
# 存储群组数据
|
# 存储群组数据
|
||||||
|
|
@ -342,7 +342,7 @@ class ReasoningGUI:
|
||||||
'group_id': self.selected_group_id
|
'group_id': self.selected_group_id
|
||||||
})
|
})
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.exception(f"自动更新出错")
|
logger.exception("自动更新出错")
|
||||||
|
|
||||||
# 每5秒更新一次
|
# 每5秒更新一次
|
||||||
time.sleep(5)
|
time.sleep(5)
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import re
|
||||||
import time
|
import time
|
||||||
from random import random
|
from random import random
|
||||||
|
|
||||||
|
|
@ -75,6 +76,14 @@ class ChatBot:
|
||||||
logger.info(f"[过滤词识别]消息中含有{word},filtered")
|
logger.info(f"[过滤词识别]消息中含有{word},filtered")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# 正则表达式过滤
|
||||||
|
for pattern in global_config.ban_msgs_regex:
|
||||||
|
if re.search(pattern, message.raw_message):
|
||||||
|
logger.info(
|
||||||
|
f"[{message.group_name}]{message.user_nickname}:{message.raw_message}")
|
||||||
|
logger.info(f"[正则表达式过滤]消息匹配到{pattern},filtered")
|
||||||
|
return
|
||||||
|
|
||||||
current_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(message.time))
|
current_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(message.time))
|
||||||
|
|
||||||
# topic=await topic_identifier.identify_topic_llm(message.processed_plain_text)
|
# topic=await topic_identifier.identify_topic_llm(message.processed_plain_text)
|
||||||
|
|
@ -129,7 +138,7 @@ class ChatBot:
|
||||||
|
|
||||||
# 如果找不到思考消息,直接返回
|
# 如果找不到思考消息,直接返回
|
||||||
if not thinking_message:
|
if not thinking_message:
|
||||||
logger.warning(f"未找到对应的思考消息,可能已超时被移除")
|
logger.warning("未找到对应的思考消息,可能已超时被移除")
|
||||||
return
|
return
|
||||||
|
|
||||||
# 记录开始思考的时间,避免从思考到回复的时间太久
|
# 记录开始思考的时间,避免从思考到回复的时间太久
|
||||||
|
|
@ -178,7 +187,7 @@ class ChatBot:
|
||||||
|
|
||||||
# 检查是否 <没有找到> emoji
|
# 检查是否 <没有找到> emoji
|
||||||
if emoji_raw != None:
|
if emoji_raw != None:
|
||||||
emoji_path, discription = emoji_raw
|
emoji_path, description = emoji_raw
|
||||||
|
|
||||||
emoji_cq = CQCode.create_emoji_cq(emoji_path)
|
emoji_cq = CQCode.create_emoji_cq(emoji_path)
|
||||||
|
|
||||||
|
|
@ -194,7 +203,7 @@ class ChatBot:
|
||||||
raw_message=emoji_cq,
|
raw_message=emoji_cq,
|
||||||
plain_text=emoji_cq,
|
plain_text=emoji_cq,
|
||||||
processed_plain_text=emoji_cq,
|
processed_plain_text=emoji_cq,
|
||||||
detailed_plain_text=discription,
|
detailed_plain_text=description,
|
||||||
user_nickname=global_config.BOT_NICKNAME,
|
user_nickname=global_config.BOT_NICKNAME,
|
||||||
group_name=message.group_name,
|
group_name=message.group_name,
|
||||||
time=bot_response_time,
|
time=bot_response_time,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import os
|
import os
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from typing import Dict, Optional
|
from typing import Dict, List, Optional
|
||||||
|
|
||||||
import tomli
|
import tomli
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
@ -12,10 +12,12 @@ from packaging.specifiers import SpecifierSet, InvalidSpecifier
|
||||||
@dataclass
|
@dataclass
|
||||||
class BotConfig:
|
class BotConfig:
|
||||||
"""机器人配置类"""
|
"""机器人配置类"""
|
||||||
|
|
||||||
INNER_VERSION: Version = None
|
INNER_VERSION: Version = None
|
||||||
|
|
||||||
BOT_QQ: Optional[int] = 1
|
BOT_QQ: Optional[int] = 1
|
||||||
BOT_NICKNAME: Optional[str] = None
|
BOT_NICKNAME: Optional[str] = None
|
||||||
|
BOT_ALIAS_NAMES: List[str] = field(default_factory=list) # 别名,可以通过这个叫它
|
||||||
|
|
||||||
# 消息处理相关配置
|
# 消息处理相关配置
|
||||||
MIN_TEXT_LENGTH: int = 2 # 最小处理文本长度
|
MIN_TEXT_LENGTH: int = 2 # 最小处理文本长度
|
||||||
|
|
@ -43,6 +45,7 @@ class BotConfig:
|
||||||
EMOJI_CHECK_PROMPT: str = "符合公序良俗" # 表情包过滤要求
|
EMOJI_CHECK_PROMPT: str = "符合公序良俗" # 表情包过滤要求
|
||||||
|
|
||||||
ban_words = set()
|
ban_words = set()
|
||||||
|
ban_msgs_regex = set()
|
||||||
|
|
||||||
max_response_length: int = 1024 # 最大回复长度
|
max_response_length: int = 1024 # 最大回复长度
|
||||||
|
|
||||||
|
|
@ -81,23 +84,25 @@ class BotConfig:
|
||||||
PROMPT_PERSONALITY = [
|
PROMPT_PERSONALITY = [
|
||||||
"曾经是一个学习地质的女大学生,现在学习心理学和脑科学,你会刷贴吧",
|
"曾经是一个学习地质的女大学生,现在学习心理学和脑科学,你会刷贴吧",
|
||||||
"是一个女大学生,你有黑色头发,你会刷小红书",
|
"是一个女大学生,你有黑色头发,你会刷小红书",
|
||||||
"是一个女大学生,你会刷b站,对ACG文化感兴趣"
|
"是一个女大学生,你会刷b站,对ACG文化感兴趣",
|
||||||
]
|
]
|
||||||
|
|
||||||
PROMPT_SCHEDULE_GEN="一个曾经学习地质,现在学习心理学和脑科学的女大学生,喜欢刷qq,贴吧,知乎和小红书"
|
PROMPT_SCHEDULE_GEN = "一个曾经学习地质,现在学习心理学和脑科学的女大学生,喜欢刷qq,贴吧,知乎和小红书"
|
||||||
|
|
||||||
PERSONALITY_1: float = 0.6 # 第一种人格概率
|
PERSONALITY_1: float = 0.6 # 第一种人格概率
|
||||||
PERSONALITY_2: float = 0.3 # 第二种人格概率
|
PERSONALITY_2: float = 0.3 # 第二种人格概率
|
||||||
PERSONALITY_3: float = 0.1 # 第三种人格概率
|
PERSONALITY_3: float = 0.1 # 第三种人格概率
|
||||||
|
|
||||||
memory_ban_words: list = field(default_factory=lambda: ['表情包', '图片', '回复', '聊天记录']) # 添加新的配置项默认值
|
memory_ban_words: list = field(
|
||||||
|
default_factory=lambda: ["表情包", "图片", "回复", "聊天记录"]
|
||||||
|
) # 添加新的配置项默认值
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_config_dir() -> str:
|
def get_config_dir() -> str:
|
||||||
"""获取配置文件目录"""
|
"""获取配置文件目录"""
|
||||||
current_dir = os.path.dirname(os.path.abspath(__file__))
|
current_dir = os.path.dirname(os.path.abspath(__file__))
|
||||||
root_dir = os.path.abspath(os.path.join(current_dir, '..', '..', '..'))
|
root_dir = os.path.abspath(os.path.join(current_dir, "..", "..", ".."))
|
||||||
config_dir = os.path.join(root_dir, 'config')
|
config_dir = os.path.join(root_dir, "config")
|
||||||
if not os.path.exists(config_dir):
|
if not os.path.exists(config_dir):
|
||||||
os.makedirs(config_dir)
|
os.makedirs(config_dir)
|
||||||
return config_dir
|
return config_dir
|
||||||
|
|
@ -108,35 +113,32 @@ class BotConfig:
|
||||||
Args:
|
Args:
|
||||||
value[str]: 版本表达式(字符串)
|
value[str]: 版本表达式(字符串)
|
||||||
Returns:
|
Returns:
|
||||||
SpecifierSet
|
SpecifierSet
|
||||||
"""
|
"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
converted = SpecifierSet(value)
|
converted = SpecifierSet(value)
|
||||||
except InvalidSpecifier as e:
|
except InvalidSpecifier:
|
||||||
logger.error(
|
logger.error(f"{value} 分类使用了错误的版本约束表达式\n", "请阅读 https://semver.org/lang/zh-CN/ 修改代码")
|
||||||
f"{value} 分类使用了错误的版本约束表达式\n",
|
|
||||||
"请阅读 https://semver.org/lang/zh-CN/ 修改代码"
|
|
||||||
)
|
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
return converted
|
return converted
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_config_version(cls, toml: dict) -> Version:
|
def get_config_version(cls, toml: dict) -> Version:
|
||||||
"""提取配置文件的 SpecifierSet 版本数据
|
"""提取配置文件的 SpecifierSet 版本数据
|
||||||
Args:
|
Args:
|
||||||
toml[dict]: 输入的配置文件字典
|
toml[dict]: 输入的配置文件字典
|
||||||
Returns:
|
Returns:
|
||||||
Version
|
Version
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if 'inner' in toml:
|
if "inner" in toml:
|
||||||
try:
|
try:
|
||||||
config_version: str = toml["inner"]["version"]
|
config_version: str = toml["inner"]["version"]
|
||||||
except KeyError as e:
|
except KeyError as e:
|
||||||
logger.error(f"配置文件中 inner 段 不存在, 这是错误的配置文件")
|
logger.error("配置文件中 inner 段 不存在, 这是错误的配置文件")
|
||||||
raise KeyError(f"配置文件中 inner 段 不存在 {e}, 这是错误的配置文件")
|
raise KeyError(f"配置文件中 inner 段 不存在 {e}, 这是错误的配置文件") from e
|
||||||
else:
|
else:
|
||||||
toml["inner"] = {"version": "0.0.0"}
|
toml["inner"] = {"version": "0.0.0"}
|
||||||
config_version = toml["inner"]["version"]
|
config_version = toml["inner"]["version"]
|
||||||
|
|
@ -149,7 +151,7 @@ class BotConfig:
|
||||||
"请阅读 https://semver.org/lang/zh-CN/ 修改配置,并参考本项目指定的模板进行修改\n"
|
"请阅读 https://semver.org/lang/zh-CN/ 修改配置,并参考本项目指定的模板进行修改\n"
|
||||||
"本项目在不同的版本下有不同的模板,请注意识别"
|
"本项目在不同的版本下有不同的模板,请注意识别"
|
||||||
)
|
)
|
||||||
raise InvalidVersion("配置文件中 inner段 的 version 键是错误的版本描述\n")
|
raise InvalidVersion("配置文件中 inner段 的 version 键是错误的版本描述\n") from e
|
||||||
|
|
||||||
return ver
|
return ver
|
||||||
|
|
||||||
|
|
@ -159,26 +161,26 @@ class BotConfig:
|
||||||
config = cls()
|
config = cls()
|
||||||
|
|
||||||
def personality(parent: dict):
|
def personality(parent: dict):
|
||||||
personality_config = parent['personality']
|
personality_config = parent["personality"]
|
||||||
personality = personality_config.get('prompt_personality')
|
personality = personality_config.get("prompt_personality")
|
||||||
if len(personality) >= 2:
|
if len(personality) >= 2:
|
||||||
logger.debug(f"载入自定义人格:{personality}")
|
logger.debug(f"载入自定义人格:{personality}")
|
||||||
config.PROMPT_PERSONALITY = personality_config.get('prompt_personality', config.PROMPT_PERSONALITY)
|
config.PROMPT_PERSONALITY = personality_config.get("prompt_personality", config.PROMPT_PERSONALITY)
|
||||||
logger.info(f"载入自定义日程prompt:{personality_config.get('prompt_schedule', config.PROMPT_SCHEDULE_GEN)}")
|
logger.info(f"载入自定义日程prompt:{personality_config.get('prompt_schedule', config.PROMPT_SCHEDULE_GEN)}")
|
||||||
config.PROMPT_SCHEDULE_GEN = personality_config.get('prompt_schedule', config.PROMPT_SCHEDULE_GEN)
|
config.PROMPT_SCHEDULE_GEN = personality_config.get("prompt_schedule", config.PROMPT_SCHEDULE_GEN)
|
||||||
|
|
||||||
if config.INNER_VERSION in SpecifierSet(">=0.0.2"):
|
if config.INNER_VERSION in SpecifierSet(">=0.0.2"):
|
||||||
config.PERSONALITY_1 = personality_config.get('personality_1_probability', config.PERSONALITY_1)
|
config.PERSONALITY_1 = personality_config.get("personality_1_probability", config.PERSONALITY_1)
|
||||||
config.PERSONALITY_2 = personality_config.get('personality_2_probability', config.PERSONALITY_2)
|
config.PERSONALITY_2 = personality_config.get("personality_2_probability", config.PERSONALITY_2)
|
||||||
config.PERSONALITY_3 = personality_config.get('personality_3_probability', config.PERSONALITY_3)
|
config.PERSONALITY_3 = personality_config.get("personality_3_probability", config.PERSONALITY_3)
|
||||||
|
|
||||||
def emoji(parent: dict):
|
def emoji(parent: dict):
|
||||||
emoji_config = parent["emoji"]
|
emoji_config = parent["emoji"]
|
||||||
config.EMOJI_CHECK_INTERVAL = emoji_config.get("check_interval", config.EMOJI_CHECK_INTERVAL)
|
config.EMOJI_CHECK_INTERVAL = emoji_config.get("check_interval", config.EMOJI_CHECK_INTERVAL)
|
||||||
config.EMOJI_REGISTER_INTERVAL = emoji_config.get("register_interval", config.EMOJI_REGISTER_INTERVAL)
|
config.EMOJI_REGISTER_INTERVAL = emoji_config.get("register_interval", config.EMOJI_REGISTER_INTERVAL)
|
||||||
config.EMOJI_CHECK_PROMPT = emoji_config.get('check_prompt', config.EMOJI_CHECK_PROMPT)
|
config.EMOJI_CHECK_PROMPT = emoji_config.get("check_prompt", config.EMOJI_CHECK_PROMPT)
|
||||||
config.EMOJI_SAVE = emoji_config.get('auto_save', config.EMOJI_SAVE)
|
config.EMOJI_SAVE = emoji_config.get("auto_save", config.EMOJI_SAVE)
|
||||||
config.EMOJI_CHECK = emoji_config.get('enable_check', config.EMOJI_CHECK)
|
config.EMOJI_CHECK = emoji_config.get("enable_check", config.EMOJI_CHECK)
|
||||||
|
|
||||||
def cq_code(parent: dict):
|
def cq_code(parent: dict):
|
||||||
cq_code_config = parent["cq_code"]
|
cq_code_config = parent["cq_code"]
|
||||||
|
|
@ -191,12 +193,16 @@ class BotConfig:
|
||||||
config.BOT_QQ = int(bot_qq)
|
config.BOT_QQ = int(bot_qq)
|
||||||
config.BOT_NICKNAME = bot_config.get("nickname", config.BOT_NICKNAME)
|
config.BOT_NICKNAME = bot_config.get("nickname", config.BOT_NICKNAME)
|
||||||
|
|
||||||
|
if config.INNER_VERSION in SpecifierSet(">=0.0.5"):
|
||||||
|
config.BOT_ALIAS_NAMES = bot_config.get("alias_names", config.BOT_ALIAS_NAMES)
|
||||||
|
|
||||||
def response(parent: dict):
|
def response(parent: dict):
|
||||||
response_config = parent["response"]
|
response_config = parent["response"]
|
||||||
config.MODEL_R1_PROBABILITY = response_config.get("model_r1_probability", config.MODEL_R1_PROBABILITY)
|
config.MODEL_R1_PROBABILITY = response_config.get("model_r1_probability", config.MODEL_R1_PROBABILITY)
|
||||||
config.MODEL_V3_PROBABILITY = response_config.get("model_v3_probability", config.MODEL_V3_PROBABILITY)
|
config.MODEL_V3_PROBABILITY = response_config.get("model_v3_probability", config.MODEL_V3_PROBABILITY)
|
||||||
config.MODEL_R1_DISTILL_PROBABILITY = response_config.get("model_r1_distill_probability",
|
config.MODEL_R1_DISTILL_PROBABILITY = response_config.get(
|
||||||
config.MODEL_R1_DISTILL_PROBABILITY)
|
"model_r1_distill_probability", config.MODEL_R1_DISTILL_PROBABILITY
|
||||||
|
)
|
||||||
config.max_response_length = response_config.get("max_response_length", config.max_response_length)
|
config.max_response_length = response_config.get("max_response_length", config.max_response_length)
|
||||||
|
|
||||||
def model(parent: dict):
|
def model(parent: dict):
|
||||||
|
|
@ -213,7 +219,7 @@ class BotConfig:
|
||||||
"llm_emotion_judge",
|
"llm_emotion_judge",
|
||||||
"vlm",
|
"vlm",
|
||||||
"embedding",
|
"embedding",
|
||||||
"moderation"
|
"moderation",
|
||||||
]
|
]
|
||||||
|
|
||||||
for item in config_list:
|
for item in config_list:
|
||||||
|
|
@ -222,13 +228,7 @@ class BotConfig:
|
||||||
|
|
||||||
# base_url 的例子: SILICONFLOW_BASE_URL
|
# base_url 的例子: SILICONFLOW_BASE_URL
|
||||||
# key 的例子: SILICONFLOW_KEY
|
# key 的例子: SILICONFLOW_KEY
|
||||||
cfg_target = {
|
cfg_target = {"name": "", "base_url": "", "key": "", "pri_in": 0, "pri_out": 0}
|
||||||
"name": "",
|
|
||||||
"base_url": "",
|
|
||||||
"key": "",
|
|
||||||
"pri_in": 0,
|
|
||||||
"pri_out": 0
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.INNER_VERSION in SpecifierSet("<=0.0.0"):
|
if config.INNER_VERSION in SpecifierSet("<=0.0.0"):
|
||||||
cfg_target = cfg_item
|
cfg_target = cfg_item
|
||||||
|
|
@ -247,7 +247,7 @@ class BotConfig:
|
||||||
cfg_target[i] = cfg_item[i]
|
cfg_target[i] = cfg_item[i]
|
||||||
except KeyError as e:
|
except KeyError as e:
|
||||||
logger.error(f"{item} 中的必要字段不存在,请检查")
|
logger.error(f"{item} 中的必要字段不存在,请检查")
|
||||||
raise KeyError(f"{item} 中的必要字段 {e} 不存在,请检查")
|
raise KeyError(f"{item} 中的必要字段 {e} 不存在,请检查") from e
|
||||||
|
|
||||||
provider = cfg_item.get("provider")
|
provider = cfg_item.get("provider")
|
||||||
if provider is None:
|
if provider is None:
|
||||||
|
|
@ -272,17 +272,22 @@ class BotConfig:
|
||||||
|
|
||||||
if config.INNER_VERSION in SpecifierSet(">=0.0.2"):
|
if config.INNER_VERSION in SpecifierSet(">=0.0.2"):
|
||||||
config.thinking_timeout = msg_config.get("thinking_timeout", config.thinking_timeout)
|
config.thinking_timeout = msg_config.get("thinking_timeout", config.thinking_timeout)
|
||||||
config.response_willing_amplifier = msg_config.get("response_willing_amplifier",
|
config.response_willing_amplifier = msg_config.get(
|
||||||
config.response_willing_amplifier)
|
"response_willing_amplifier", config.response_willing_amplifier
|
||||||
config.response_interested_rate_amplifier = msg_config.get("response_interested_rate_amplifier",
|
)
|
||||||
config.response_interested_rate_amplifier)
|
config.response_interested_rate_amplifier = msg_config.get(
|
||||||
|
"response_interested_rate_amplifier", config.response_interested_rate_amplifier
|
||||||
|
)
|
||||||
config.down_frequency_rate = msg_config.get("down_frequency_rate", config.down_frequency_rate)
|
config.down_frequency_rate = msg_config.get("down_frequency_rate", config.down_frequency_rate)
|
||||||
|
|
||||||
|
if config.INNER_VERSION in SpecifierSet(">=0.0.6"):
|
||||||
|
config.ban_msgs_regex = msg_config.get("ban_msgs_regex", config.ban_msgs_regex)
|
||||||
|
|
||||||
def memory(parent: dict):
|
def memory(parent: dict):
|
||||||
memory_config = parent["memory"]
|
memory_config = parent["memory"]
|
||||||
config.build_memory_interval = memory_config.get("build_memory_interval", config.build_memory_interval)
|
config.build_memory_interval = memory_config.get("build_memory_interval", config.build_memory_interval)
|
||||||
config.forget_memory_interval = memory_config.get("forget_memory_interval", config.forget_memory_interval)
|
config.forget_memory_interval = memory_config.get("forget_memory_interval", config.forget_memory_interval)
|
||||||
|
|
||||||
# 在版本 >= 0.0.4 时才处理新增的配置项
|
# 在版本 >= 0.0.4 时才处理新增的配置项
|
||||||
if config.INNER_VERSION in SpecifierSet(">=0.0.4"):
|
if config.INNER_VERSION in SpecifierSet(">=0.0.4"):
|
||||||
config.memory_ban_words = set(memory_config.get("memory_ban_words", []))
|
config.memory_ban_words = set(memory_config.get("memory_ban_words", []))
|
||||||
|
|
@ -303,10 +308,12 @@ class BotConfig:
|
||||||
config.chinese_typo_enable = chinese_typo_config.get("enable", config.chinese_typo_enable)
|
config.chinese_typo_enable = chinese_typo_config.get("enable", config.chinese_typo_enable)
|
||||||
config.chinese_typo_error_rate = chinese_typo_config.get("error_rate", config.chinese_typo_error_rate)
|
config.chinese_typo_error_rate = chinese_typo_config.get("error_rate", config.chinese_typo_error_rate)
|
||||||
config.chinese_typo_min_freq = chinese_typo_config.get("min_freq", config.chinese_typo_min_freq)
|
config.chinese_typo_min_freq = chinese_typo_config.get("min_freq", config.chinese_typo_min_freq)
|
||||||
config.chinese_typo_tone_error_rate = chinese_typo_config.get("tone_error_rate",
|
config.chinese_typo_tone_error_rate = chinese_typo_config.get(
|
||||||
config.chinese_typo_tone_error_rate)
|
"tone_error_rate", config.chinese_typo_tone_error_rate
|
||||||
config.chinese_typo_word_replace_rate = chinese_typo_config.get("word_replace_rate",
|
)
|
||||||
config.chinese_typo_word_replace_rate)
|
config.chinese_typo_word_replace_rate = chinese_typo_config.get(
|
||||||
|
"word_replace_rate", config.chinese_typo_word_replace_rate
|
||||||
|
)
|
||||||
|
|
||||||
def groups(parent: dict):
|
def groups(parent: dict):
|
||||||
groups_config = parent["groups"]
|
groups_config = parent["groups"]
|
||||||
|
|
@ -325,61 +332,19 @@ class BotConfig:
|
||||||
# 例如:"notice": "personality 将在 1.3.2 后被移除",那么在有效版本中的用户就会虽然可以
|
# 例如:"notice": "personality 将在 1.3.2 后被移除",那么在有效版本中的用户就会虽然可以
|
||||||
# 正常执行程序,但是会看到这条自定义提示
|
# 正常执行程序,但是会看到这条自定义提示
|
||||||
include_configs = {
|
include_configs = {
|
||||||
"personality": {
|
"personality": {"func": personality, "support": ">=0.0.0"},
|
||||||
"func": personality,
|
"emoji": {"func": emoji, "support": ">=0.0.0"},
|
||||||
"support": ">=0.0.0"
|
"cq_code": {"func": cq_code, "support": ">=0.0.0"},
|
||||||
},
|
"bot": {"func": bot, "support": ">=0.0.0"},
|
||||||
"emoji": {
|
"response": {"func": response, "support": ">=0.0.0"},
|
||||||
"func": emoji,
|
"model": {"func": model, "support": ">=0.0.0"},
|
||||||
"support": ">=0.0.0"
|
"message": {"func": message, "support": ">=0.0.0"},
|
||||||
},
|
"memory": {"func": memory, "support": ">=0.0.0", "necessary": False},
|
||||||
"cq_code": {
|
"mood": {"func": mood, "support": ">=0.0.0"},
|
||||||
"func": cq_code,
|
"keywords_reaction": {"func": keywords_reaction, "support": ">=0.0.2", "necessary": False},
|
||||||
"support": ">=0.0.0"
|
"chinese_typo": {"func": chinese_typo, "support": ">=0.0.3", "necessary": False},
|
||||||
},
|
"groups": {"func": groups, "support": ">=0.0.0"},
|
||||||
"bot": {
|
"others": {"func": others, "support": ">=0.0.0"},
|
||||||
"func": bot,
|
|
||||||
"support": ">=0.0.0"
|
|
||||||
},
|
|
||||||
"response": {
|
|
||||||
"func": response,
|
|
||||||
"support": ">=0.0.0"
|
|
||||||
},
|
|
||||||
"model": {
|
|
||||||
"func": model,
|
|
||||||
"support": ">=0.0.0"
|
|
||||||
},
|
|
||||||
"message": {
|
|
||||||
"func": message,
|
|
||||||
"support": ">=0.0.0"
|
|
||||||
},
|
|
||||||
"memory": {
|
|
||||||
"func": memory,
|
|
||||||
"support": ">=0.0.0",
|
|
||||||
"necessary": False
|
|
||||||
},
|
|
||||||
"mood": {
|
|
||||||
"func": mood,
|
|
||||||
"support": ">=0.0.0"
|
|
||||||
},
|
|
||||||
"keywords_reaction": {
|
|
||||||
"func": keywords_reaction,
|
|
||||||
"support": ">=0.0.2",
|
|
||||||
"necessary": False
|
|
||||||
},
|
|
||||||
"chinese_typo": {
|
|
||||||
"func": chinese_typo,
|
|
||||||
"support": ">=0.0.3",
|
|
||||||
"necessary": False
|
|
||||||
},
|
|
||||||
"groups": {
|
|
||||||
"func": groups,
|
|
||||||
"support": ">=0.0.0"
|
|
||||||
},
|
|
||||||
"others": {
|
|
||||||
"func": others,
|
|
||||||
"support": ">=0.0.0"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# 原地修改,将 字符串版本表达式 转换成 版本对象
|
# 原地修改,将 字符串版本表达式 转换成 版本对象
|
||||||
|
|
@ -391,7 +356,7 @@ class BotConfig:
|
||||||
with open(config_path, "rb") as f:
|
with open(config_path, "rb") as f:
|
||||||
try:
|
try:
|
||||||
toml_dict = tomli.load(f)
|
toml_dict = tomli.load(f)
|
||||||
except(tomli.TOMLDecodeError) as e:
|
except tomli.TOMLDecodeError as e:
|
||||||
logger.critical(f"配置文件bot_config.toml填写有误,请检查第{e.lineno}行第{e.colno}处:{e.msg}")
|
logger.critical(f"配置文件bot_config.toml填写有误,请检查第{e.lineno}行第{e.colno}处:{e.msg}")
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
|
|
@ -406,7 +371,7 @@ class BotConfig:
|
||||||
# 检查配置文件版本是否在支持范围内
|
# 检查配置文件版本是否在支持范围内
|
||||||
if config.INNER_VERSION in group_specifierset:
|
if config.INNER_VERSION in group_specifierset:
|
||||||
# 如果版本在支持范围内,检查是否存在通知
|
# 如果版本在支持范围内,检查是否存在通知
|
||||||
if 'notice' in include_configs[key]:
|
if "notice" in include_configs[key]:
|
||||||
logger.warning(include_configs[key]["notice"])
|
logger.warning(include_configs[key]["notice"])
|
||||||
|
|
||||||
include_configs[key]["func"](toml_dict)
|
include_configs[key]["func"](toml_dict)
|
||||||
|
|
@ -420,7 +385,7 @@ class BotConfig:
|
||||||
raise InvalidVersion(f"当前程序仅支持以下版本范围: {group_specifierset}")
|
raise InvalidVersion(f"当前程序仅支持以下版本范围: {group_specifierset}")
|
||||||
|
|
||||||
# 如果 necessary 项目存在,而且显式声明是 False,进入特殊处理
|
# 如果 necessary 项目存在,而且显式声明是 False,进入特殊处理
|
||||||
elif "necessary" in include_configs[key] and include_configs[key].get("necessary") == False:
|
elif "necessary" in include_configs[key] and include_configs[key].get("necessary") is False:
|
||||||
# 通过 pass 处理的项虽然直接忽略也是可以的,但是为了不增加理解困难,依然需要在这里显式处理
|
# 通过 pass 处理的项虽然直接忽略也是可以的,但是为了不增加理解困难,依然需要在这里显式处理
|
||||||
if key == "keywords_reaction":
|
if key == "keywords_reaction":
|
||||||
pass
|
pass
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import base64
|
import base64
|
||||||
import html
|
import html
|
||||||
import os
|
|
||||||
import time
|
import time
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Dict, Optional
|
from typing import Dict, Optional
|
||||||
|
|
@ -17,7 +16,7 @@ from urllib3.util import create_urllib3_context
|
||||||
from ..models.utils_model import LLM_request
|
from ..models.utils_model import LLM_request
|
||||||
from .config import global_config
|
from .config import global_config
|
||||||
from .mapper import emojimapper
|
from .mapper import emojimapper
|
||||||
from .utils_image import storage_emoji, storage_image
|
from .utils_image import image_path_to_base64, storage_emoji, storage_image
|
||||||
from .utils_user import get_user_nickname
|
from .utils_user import get_user_nickname
|
||||||
|
|
||||||
driver = get_driver()
|
driver = get_driver()
|
||||||
|
|
@ -155,8 +154,8 @@ class CQCode:
|
||||||
logger.error(f"最终请求失败: {str(e)}")
|
logger.error(f"最终请求失败: {str(e)}")
|
||||||
time.sleep(1.5 ** retry) # 指数退避
|
time.sleep(1.5 ** retry) # 指数退避
|
||||||
|
|
||||||
except Exception as e:
|
except Exception:
|
||||||
logger.exception(f"[未知错误]")
|
logger.exception("[未知错误]")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
@ -281,7 +280,7 @@ class CQCode:
|
||||||
logger.debug(f"合并后的转发消息: {combined_messages}")
|
logger.debug(f"合并后的转发消息: {combined_messages}")
|
||||||
return f"[转发消息:\n{combined_messages}]"
|
return f"[转发消息:\n{combined_messages}]"
|
||||||
|
|
||||||
except Exception as e:
|
except Exception:
|
||||||
logger.exception("处理转发消息失败")
|
logger.exception("处理转发消息失败")
|
||||||
return '[转发消息]'
|
return '[转发消息]'
|
||||||
|
|
||||||
|
|
@ -328,15 +327,10 @@ class CQCode:
|
||||||
Returns:
|
Returns:
|
||||||
表情包CQ码字符串
|
表情包CQ码字符串
|
||||||
"""
|
"""
|
||||||
# 确保使用绝对路径
|
base64_content = image_path_to_base64(file_path)
|
||||||
abs_path = os.path.abspath(file_path)
|
|
||||||
# 转义特殊字符
|
|
||||||
escaped_path = abs_path.replace('&', '&') \
|
|
||||||
.replace('[', '[') \
|
|
||||||
.replace(']', ']') \
|
|
||||||
.replace(',', ',')
|
|
||||||
# 生成CQ码,设置sub_type=1表示这是表情包
|
# 生成CQ码,设置sub_type=1表示这是表情包
|
||||||
return f"[CQ:image,file=file:///{escaped_path},sub_type=1]"
|
return f"[CQ:image,file=base64://{base64_content},sub_type=1]"
|
||||||
|
|
||||||
|
|
||||||
class CQCode_tool:
|
class CQCode_tool:
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ class EmojiManager:
|
||||||
self.db = Database.get_instance()
|
self.db = Database.get_instance()
|
||||||
self._scan_task = None
|
self._scan_task = None
|
||||||
self.vlm = LLM_request(model=global_config.vlm, temperature=0.3, max_tokens=1000)
|
self.vlm = LLM_request(model=global_config.vlm, temperature=0.3, max_tokens=1000)
|
||||||
self.llm_emotion_judge = LLM_request(model=global_config.llm_normal_minor, max_tokens=60,
|
self.llm_emotion_judge = LLM_request(model=global_config.llm_emotion_judge, max_tokens=60,
|
||||||
temperature=0.8) # 更高的温度,更少的token(后续可以根据情绪来调整温度)
|
temperature=0.8) # 更高的温度,更少的token(后续可以根据情绪来调整温度)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -51,8 +51,8 @@ class EmojiManager:
|
||||||
self._initialized = True
|
self._initialized = True
|
||||||
# 启动时执行一次完整性检查
|
# 启动时执行一次完整性检查
|
||||||
self.check_emoji_file_integrity()
|
self.check_emoji_file_integrity()
|
||||||
except Exception as e:
|
except Exception:
|
||||||
logger.exception(f"初始化表情管理器失败")
|
logger.exception("初始化表情管理器失败")
|
||||||
|
|
||||||
def _ensure_db(self):
|
def _ensure_db(self):
|
||||||
"""确保数据库已初始化"""
|
"""确保数据库已初始化"""
|
||||||
|
|
@ -87,8 +87,8 @@ class EmojiManager:
|
||||||
{'_id': emoji_id},
|
{'_id': emoji_id},
|
||||||
{'$inc': {'usage_count': 1}}
|
{'$inc': {'usage_count': 1}}
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception:
|
||||||
logger.exception(f"记录表情使用失败")
|
logger.exception("记录表情使用失败")
|
||||||
|
|
||||||
async def get_emoji_for_text(self, text: str) -> Optional[str]:
|
async def get_emoji_for_text(self, text: str) -> Optional[str]:
|
||||||
"""根据文本内容获取相关表情包
|
"""根据文本内容获取相关表情包
|
||||||
|
|
@ -117,7 +117,7 @@ class EmojiManager:
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# 获取所有表情包
|
# 获取所有表情包
|
||||||
all_emojis = list(self.db.db.emoji.find({}, {'_id': 1, 'path': 1, 'embedding': 1, 'discription': 1}))
|
all_emojis = list(self.db.db.emoji.find({}, {'_id': 1, 'path': 1, 'embedding': 1, 'description': 1}))
|
||||||
|
|
||||||
if not all_emojis:
|
if not all_emojis:
|
||||||
logger.warning("数据库中没有任何表情包")
|
logger.warning("数据库中没有任何表情包")
|
||||||
|
|
@ -160,9 +160,9 @@ class EmojiManager:
|
||||||
{'$inc': {'usage_count': 1}}
|
{'$inc': {'usage_count': 1}}
|
||||||
)
|
)
|
||||||
logger.success(
|
logger.success(
|
||||||
f"找到匹配的表情包: {selected_emoji.get('discription', '无描述')} (相似度: {similarity:.4f})")
|
f"找到匹配的表情包: {selected_emoji.get('description', '无描述')} (相似度: {similarity:.4f})")
|
||||||
# 稍微改一下文本描述,不然容易产生幻觉,描述已经包含 表情包 了
|
# 稍微改一下文本描述,不然容易产生幻觉,描述已经包含 表情包 了
|
||||||
return selected_emoji['path'], "[ %s ]" % selected_emoji.get('discription', '无描述')
|
return selected_emoji['path'], "[ %s ]" % selected_emoji.get('description', '无描述')
|
||||||
|
|
||||||
except Exception as search_error:
|
except Exception as search_error:
|
||||||
logger.error(f"搜索表情包失败: {str(search_error)}")
|
logger.error(f"搜索表情包失败: {str(search_error)}")
|
||||||
|
|
@ -174,7 +174,7 @@ class EmojiManager:
|
||||||
logger.error(f"获取表情包失败: {str(e)}")
|
logger.error(f"获取表情包失败: {str(e)}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
async def _get_emoji_discription(self, image_base64: str) -> str:
|
async def _get_emoji_description(self, image_base64: str) -> str:
|
||||||
"""获取表情包的标签"""
|
"""获取表情包的标签"""
|
||||||
try:
|
try:
|
||||||
prompt = '这是一个表情包,使用中文简洁的描述一下表情包的内容和表情包所表达的情感'
|
prompt = '这是一个表情包,使用中文简洁的描述一下表情包的内容和表情包所表达的情感'
|
||||||
|
|
@ -236,36 +236,36 @@ class EmojiManager:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# 获取表情包的描述
|
# 获取表情包的描述
|
||||||
discription = await self._get_emoji_discription(image_base64)
|
description = await self._get_emoji_description(image_base64)
|
||||||
if global_config.EMOJI_CHECK:
|
if global_config.EMOJI_CHECK:
|
||||||
check = await self._check_emoji(image_base64)
|
check = await self._check_emoji(image_base64)
|
||||||
if '是' not in check:
|
if '是' not in check:
|
||||||
os.remove(image_path)
|
os.remove(image_path)
|
||||||
logger.info(f"描述: {discription}")
|
logger.info(f"描述: {description}")
|
||||||
logger.info(f"其不满足过滤规则,被剔除 {check}")
|
logger.info(f"其不满足过滤规则,被剔除 {check}")
|
||||||
continue
|
continue
|
||||||
logger.info(f"check通过 {check}")
|
logger.info(f"check通过 {check}")
|
||||||
|
|
||||||
if discription is not None:
|
if description is not None:
|
||||||
embedding = await get_embedding(discription)
|
embedding = await get_embedding(description)
|
||||||
# 准备数据库记录
|
# 准备数据库记录
|
||||||
emoji_record = {
|
emoji_record = {
|
||||||
'filename': filename,
|
'filename': filename,
|
||||||
'path': image_path,
|
'path': image_path,
|
||||||
'embedding': embedding,
|
'embedding': embedding,
|
||||||
'discription': discription,
|
'description': description,
|
||||||
'timestamp': int(time.time())
|
'timestamp': int(time.time())
|
||||||
}
|
}
|
||||||
|
|
||||||
# 保存到数据库
|
# 保存到数据库
|
||||||
self.db.db['emoji'].insert_one(emoji_record)
|
self.db.db['emoji'].insert_one(emoji_record)
|
||||||
logger.success(f"注册新表情包: {filename}")
|
logger.success(f"注册新表情包: {filename}")
|
||||||
logger.info(f"描述: {discription}")
|
logger.info(f"描述: {description}")
|
||||||
else:
|
else:
|
||||||
logger.warning(f"跳过表情包: {filename}")
|
logger.warning(f"跳过表情包: {filename}")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception:
|
||||||
logger.exception(f"扫描表情包失败")
|
logger.exception("扫描表情包失败")
|
||||||
|
|
||||||
async def _periodic_scan(self, interval_MINS: int = 10):
|
async def _periodic_scan(self, interval_MINS: int = 10):
|
||||||
"""定期扫描新表情包"""
|
"""定期扫描新表情包"""
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,7 @@ class ResponseGenerator:
|
||||||
try:
|
try:
|
||||||
content, reasoning_content = await model.generate_response(prompt)
|
content, reasoning_content = await model.generate_response(prompt)
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.exception(f"生成回复时出错")
|
logger.exception("生成回复时出错")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# 保存到数据库
|
# 保存到数据库
|
||||||
|
|
@ -146,7 +146,7 @@ class ResponseGenerator:
|
||||||
return ["neutral"]
|
return ["neutral"]
|
||||||
|
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.exception(f"获取情感标签时出错")
|
logger.exception("获取情感标签时出错")
|
||||||
return ["neutral"]
|
return ["neutral"]
|
||||||
|
|
||||||
async def _process_response(self, content: str) -> Tuple[List[str], List[str]]:
|
async def _process_response(self, content: str) -> Tuple[List[str], List[str]]:
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ class Message_Sender:
|
||||||
auto_escape=auto_escape
|
auto_escape=auto_escape
|
||||||
)
|
)
|
||||||
logger.debug(f"发送消息{message}成功")
|
logger.debug(f"发送消息{message}成功")
|
||||||
except Exception as e:
|
except Exception:
|
||||||
logger.exception(f"发送消息{message}失败")
|
logger.exception(f"发送消息{message}失败")
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -120,7 +120,7 @@ class MessageContainer:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.exception(f"移除消息时发生错误")
|
logger.exception("移除消息时发生错误")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def has_messages(self) -> bool:
|
def has_messages(self) -> bool:
|
||||||
|
|
@ -214,7 +214,7 @@ class MessageManager:
|
||||||
if not container.remove_message(msg):
|
if not container.remove_message(msg):
|
||||||
logger.warning("尝试删除不存在的消息")
|
logger.warning("尝试删除不存在的消息")
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.exception(f"处理超时消息时发生错误")
|
logger.exception("处理超时消息时发生错误")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
async def start_processor(self):
|
async def start_processor(self):
|
||||||
|
|
|
||||||
|
|
@ -131,18 +131,19 @@ class PromptBuilder:
|
||||||
probability_1 = global_config.PERSONALITY_1
|
probability_1 = global_config.PERSONALITY_1
|
||||||
probability_2 = global_config.PERSONALITY_2
|
probability_2 = global_config.PERSONALITY_2
|
||||||
probability_3 = global_config.PERSONALITY_3
|
probability_3 = global_config.PERSONALITY_3
|
||||||
prompt_personality = ''
|
|
||||||
|
prompt_personality = f'{activate_prompt}你的网名叫{global_config.BOT_NICKNAME},你还有很多别名:{"/".join(global_config.BOT_ALIAS_NAMES)},'
|
||||||
personality_choice = random.random()
|
personality_choice = random.random()
|
||||||
if personality_choice < probability_1: # 第一种人格
|
if personality_choice < probability_1: # 第一种人格
|
||||||
prompt_personality = f'''{activate_prompt}你的网名叫{global_config.BOT_NICKNAME},{personality[0]}, 你正在浏览qq群,{promt_info_prompt},
|
prompt_personality += f'''{personality[0]}, 你正在浏览qq群,{promt_info_prompt},
|
||||||
现在请你给出日常且口语化的回复,平淡一些,尽量简短一些。{keywords_reaction_prompt}
|
现在请你给出日常且口语化的回复,平淡一些,尽量简短一些。{keywords_reaction_prompt}
|
||||||
请注意把握群里的聊天内容,不要刻意突出自身学科背景,不要回复的太有条理,可以有个性。'''
|
请注意把握群里的聊天内容,不要刻意突出自身学科背景,不要回复的太有条理,可以有个性。'''
|
||||||
elif personality_choice < probability_1 + probability_2: # 第二种人格
|
elif personality_choice < probability_1 + probability_2: # 第二种人格
|
||||||
prompt_personality = f'''{activate_prompt}你的网名叫{global_config.BOT_NICKNAME},{personality[1]}, 你正在浏览qq群,{promt_info_prompt},
|
prompt_personality += f'''{personality[1]}, 你正在浏览qq群,{promt_info_prompt},
|
||||||
现在请你给出日常且口语化的回复,请表现你自己的见解,不要一昧迎合,尽量简短一些。{keywords_reaction_prompt}
|
现在请你给出日常且口语化的回复,请表现你自己的见解,不要一昧迎合,尽量简短一些。{keywords_reaction_prompt}
|
||||||
请你表达自己的见解和观点。可以有个性。'''
|
请你表达自己的见解和观点。可以有个性。'''
|
||||||
else: # 第三种人格
|
else: # 第三种人格
|
||||||
prompt_personality = f'''{activate_prompt}你的网名叫{global_config.BOT_NICKNAME},{personality[2]}, 你正在浏览qq群,{promt_info_prompt},
|
prompt_personality += f'''{personality[2]}, 你正在浏览qq群,{promt_info_prompt},
|
||||||
现在请你给出日常且口语化的回复,请表现你自己的见解,不要一昧迎合,尽量简短一些。{keywords_reaction_prompt}
|
现在请你给出日常且口语化的回复,请表现你自己的见解,不要一昧迎合,尽量简短一些。{keywords_reaction_prompt}
|
||||||
请你表达自己的见解和观点。可以有个性。'''
|
请你表达自己的见解和观点。可以有个性。'''
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,6 @@ class MessageStorage:
|
||||||
|
|
||||||
self.db.db.messages.insert_one(message_data)
|
self.db.db.messages.insert_one(message_data)
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.exception(f"存储消息失败")
|
logger.exception("存储消息失败")
|
||||||
|
|
||||||
# 如果需要其他存储相关的函数,可以在这里添加
|
# 如果需要其他存储相关的函数,可以在这里添加
|
||||||
|
|
|
||||||
|
|
@ -53,19 +53,13 @@ def db_message_to_str(message_dict: Dict) -> str:
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def is_mentioned_bot_in_message(message: Message) -> bool:
|
|
||||||
"""检查消息是否提到了机器人"""
|
|
||||||
keywords = [global_config.BOT_NICKNAME]
|
|
||||||
for keyword in keywords:
|
|
||||||
if keyword in message.processed_plain_text:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def is_mentioned_bot_in_txt(message: str) -> bool:
|
def is_mentioned_bot_in_txt(message: str) -> bool:
|
||||||
"""检查消息是否提到了机器人"""
|
"""检查消息是否提到了机器人"""
|
||||||
keywords = [global_config.BOT_NICKNAME]
|
if global_config.BOT_NICKNAME is None:
|
||||||
for keyword in keywords:
|
return True
|
||||||
|
if global_config.BOT_NICKNAME in message:
|
||||||
|
return True
|
||||||
|
for keyword in global_config.BOT_ALIAS_NAMES:
|
||||||
if keyword in message:
|
if keyword in message:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
|
||||||
|
|
@ -318,6 +318,8 @@ class Hippocampus:
|
||||||
compressed_memory, similar_topics_dict = await self.memory_compress(messages, compress_rate)
|
compressed_memory, similar_topics_dict = await self.memory_compress(messages, compress_rate)
|
||||||
logger.info(f"压缩后记忆数量: {len(compressed_memory)},似曾相识的话题: {len(similar_topics_dict)}")
|
logger.info(f"压缩后记忆数量: {len(compressed_memory)},似曾相识的话题: {len(similar_topics_dict)}")
|
||||||
|
|
||||||
|
current_time = datetime.datetime.now().timestamp()
|
||||||
|
|
||||||
for topic, memory in compressed_memory:
|
for topic, memory in compressed_memory:
|
||||||
logger.info(f"添加节点: {topic}")
|
logger.info(f"添加节点: {topic}")
|
||||||
self.memory_graph.add_dot(topic, memory)
|
self.memory_graph.add_dot(topic, memory)
|
||||||
|
|
@ -330,7 +332,10 @@ class Hippocampus:
|
||||||
if topic != similar_topic:
|
if topic != similar_topic:
|
||||||
strength = int(similarity * 10)
|
strength = int(similarity * 10)
|
||||||
logger.info(f"连接相似节点: {topic} 和 {similar_topic} (强度: {strength})")
|
logger.info(f"连接相似节点: {topic} 和 {similar_topic} (强度: {strength})")
|
||||||
self.memory_graph.G.add_edge(topic, similar_topic, strength=strength)
|
self.memory_graph.G.add_edge(topic, similar_topic,
|
||||||
|
strength=strength,
|
||||||
|
created_time=current_time,
|
||||||
|
last_modified=current_time)
|
||||||
|
|
||||||
# 连接同批次的相关话题
|
# 连接同批次的相关话题
|
||||||
for i in range(len(all_topics)):
|
for i in range(len(all_topics)):
|
||||||
|
|
@ -438,21 +443,39 @@ class Hippocampus:
|
||||||
|
|
||||||
def sync_memory_from_db(self):
|
def sync_memory_from_db(self):
|
||||||
"""从数据库同步数据到内存中的图结构"""
|
"""从数据库同步数据到内存中的图结构"""
|
||||||
|
current_time = datetime.datetime.now().timestamp()
|
||||||
|
need_update = False
|
||||||
|
|
||||||
# 清空当前图
|
# 清空当前图
|
||||||
self.memory_graph.G.clear()
|
self.memory_graph.G.clear()
|
||||||
|
|
||||||
# 从数据库加载所有节点
|
# 从数据库加载所有节点
|
||||||
nodes = self.memory_graph.db.db.graph_data.nodes.find()
|
nodes = list(self.memory_graph.db.db.graph_data.nodes.find())
|
||||||
for node in nodes:
|
for node in nodes:
|
||||||
concept = node['concept']
|
concept = node['concept']
|
||||||
memory_items = node.get('memory_items', [])
|
memory_items = node.get('memory_items', [])
|
||||||
# 确保memory_items是列表
|
|
||||||
if not isinstance(memory_items, list):
|
if not isinstance(memory_items, list):
|
||||||
memory_items = [memory_items] if memory_items else []
|
memory_items = [memory_items] if memory_items else []
|
||||||
|
|
||||||
# 获取时间信息
|
# 检查时间字段是否存在
|
||||||
created_time = node.get('created_time', datetime.datetime.now().timestamp())
|
if 'created_time' not in node or 'last_modified' not in node:
|
||||||
last_modified = node.get('last_modified', datetime.datetime.now().timestamp())
|
need_update = True
|
||||||
|
# 更新数据库中的节点
|
||||||
|
update_data = {}
|
||||||
|
if 'created_time' not in node:
|
||||||
|
update_data['created_time'] = current_time
|
||||||
|
if 'last_modified' not in node:
|
||||||
|
update_data['last_modified'] = current_time
|
||||||
|
|
||||||
|
self.memory_graph.db.db.graph_data.nodes.update_one(
|
||||||
|
{'concept': concept},
|
||||||
|
{'$set': update_data}
|
||||||
|
)
|
||||||
|
logger.info(f"为节点 {concept} 添加缺失的时间字段")
|
||||||
|
|
||||||
|
# 获取时间信息(如果不存在则使用当前时间)
|
||||||
|
created_time = node.get('created_time', current_time)
|
||||||
|
last_modified = node.get('last_modified', current_time)
|
||||||
|
|
||||||
# 添加节点到图中
|
# 添加节点到图中
|
||||||
self.memory_graph.G.add_node(concept,
|
self.memory_graph.G.add_node(concept,
|
||||||
|
|
@ -461,15 +484,31 @@ class Hippocampus:
|
||||||
last_modified=last_modified)
|
last_modified=last_modified)
|
||||||
|
|
||||||
# 从数据库加载所有边
|
# 从数据库加载所有边
|
||||||
edges = self.memory_graph.db.db.graph_data.edges.find()
|
edges = list(self.memory_graph.db.db.graph_data.edges.find())
|
||||||
for edge in edges:
|
for edge in edges:
|
||||||
source = edge['source']
|
source = edge['source']
|
||||||
target = edge['target']
|
target = edge['target']
|
||||||
strength = edge.get('strength', 1) # 获取 strength,默认为 1
|
strength = edge.get('strength', 1)
|
||||||
|
|
||||||
# 获取时间信息
|
# 检查时间字段是否存在
|
||||||
created_time = edge.get('created_time', datetime.datetime.now().timestamp())
|
if 'created_time' not in edge or 'last_modified' not in edge:
|
||||||
last_modified = edge.get('last_modified', datetime.datetime.now().timestamp())
|
need_update = True
|
||||||
|
# 更新数据库中的边
|
||||||
|
update_data = {}
|
||||||
|
if 'created_time' not in edge:
|
||||||
|
update_data['created_time'] = current_time
|
||||||
|
if 'last_modified' not in edge:
|
||||||
|
update_data['last_modified'] = current_time
|
||||||
|
|
||||||
|
self.memory_graph.db.db.graph_data.edges.update_one(
|
||||||
|
{'source': source, 'target': target},
|
||||||
|
{'$set': update_data}
|
||||||
|
)
|
||||||
|
logger.info(f"为边 {source} - {target} 添加缺失的时间字段")
|
||||||
|
|
||||||
|
# 获取时间信息(如果不存在则使用当前时间)
|
||||||
|
created_time = edge.get('created_time', current_time)
|
||||||
|
last_modified = edge.get('last_modified', current_time)
|
||||||
|
|
||||||
# 只有当源节点和目标节点都存在时才添加边
|
# 只有当源节点和目标节点都存在时才添加边
|
||||||
if source in self.memory_graph.G and target in self.memory_graph.G:
|
if source in self.memory_graph.G and target in self.memory_graph.G:
|
||||||
|
|
@ -477,6 +516,9 @@ class Hippocampus:
|
||||||
strength=strength,
|
strength=strength,
|
||||||
created_time=created_time,
|
created_time=created_time,
|
||||||
last_modified=last_modified)
|
last_modified=last_modified)
|
||||||
|
|
||||||
|
if need_update:
|
||||||
|
logger.success("已为缺失的时间字段进行补充")
|
||||||
|
|
||||||
async def operation_forget_topic(self, percentage=0.1):
|
async def operation_forget_topic(self, percentage=0.1):
|
||||||
"""随机选择图中一定比例的节点和边进行检查,根据时间条件决定是否遗忘"""
|
"""随机选择图中一定比例的节点和边进行检查,根据时间条件决定是否遗忘"""
|
||||||
|
|
|
||||||
|
|
@ -44,8 +44,8 @@ class LLM_request:
|
||||||
self.db.db.llm_usage.create_index([("model_name", 1)])
|
self.db.db.llm_usage.create_index([("model_name", 1)])
|
||||||
self.db.db.llm_usage.create_index([("user_id", 1)])
|
self.db.db.llm_usage.create_index([("user_id", 1)])
|
||||||
self.db.db.llm_usage.create_index([("request_type", 1)])
|
self.db.db.llm_usage.create_index([("request_type", 1)])
|
||||||
except Exception as e:
|
except Exception:
|
||||||
logger.error(f"创建数据库索引失败")
|
logger.error("创建数据库索引失败")
|
||||||
|
|
||||||
def _record_usage(self, prompt_tokens: int, completion_tokens: int, total_tokens: int,
|
def _record_usage(self, prompt_tokens: int, completion_tokens: int, total_tokens: int,
|
||||||
user_id: str = "system", request_type: str = "chat",
|
user_id: str = "system", request_type: str = "chat",
|
||||||
|
|
@ -80,7 +80,7 @@ class LLM_request:
|
||||||
f"总计: {total_tokens}"
|
f"总计: {total_tokens}"
|
||||||
)
|
)
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.error(f"记录token使用情况失败")
|
logger.error("记录token使用情况失败")
|
||||||
|
|
||||||
def _calculate_cost(self, prompt_tokens: int, completion_tokens: int) -> float:
|
def _calculate_cost(self, prompt_tokens: int, completion_tokens: int) -> float:
|
||||||
"""计算API调用成本
|
"""计算API调用成本
|
||||||
|
|
@ -194,7 +194,7 @@ class LLM_request:
|
||||||
if hasattr(global_config, 'llm_normal') and global_config.llm_normal.get(
|
if hasattr(global_config, 'llm_normal') and global_config.llm_normal.get(
|
||||||
'name') == old_model_name:
|
'name') == old_model_name:
|
||||||
global_config.llm_normal['name'] = self.model_name
|
global_config.llm_normal['name'] = self.model_name
|
||||||
logger.warning(f"已将全局配置中的 llm_normal 模型降级")
|
logger.warning("已将全局配置中的 llm_normal 模型降级")
|
||||||
|
|
||||||
# 更新payload中的模型名
|
# 更新payload中的模型名
|
||||||
if payload and 'model' in payload:
|
if payload and 'model' in payload:
|
||||||
|
|
@ -227,7 +227,7 @@ class LLM_request:
|
||||||
delta_content = ""
|
delta_content = ""
|
||||||
accumulated_content += delta_content
|
accumulated_content += delta_content
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.exception(f"解析流式输出错")
|
logger.exception("解析流式输出错")
|
||||||
content = accumulated_content
|
content = accumulated_content
|
||||||
reasoning_content = ""
|
reasoning_content = ""
|
||||||
think_match = re.search(r'<think>(.*?)</think>', content, re.DOTALL)
|
think_match = re.search(r'<think>(.*?)</think>', content, re.DOTALL)
|
||||||
|
|
@ -355,7 +355,7 @@ class LLM_request:
|
||||||
"""构建请求头"""
|
"""构建请求头"""
|
||||||
if no_key:
|
if no_key:
|
||||||
return {
|
return {
|
||||||
"Authorization": f"Bearer **********",
|
"Authorization": "Bearer **********",
|
||||||
"Content-Type": "application/json"
|
"Content-Type": "application/json"
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ class ScheduleGenerator:
|
||||||
1. 早上的学习和工作安排
|
1. 早上的学习和工作安排
|
||||||
2. 下午的活动和任务
|
2. 下午的活动和任务
|
||||||
3. 晚上的计划和休息时间
|
3. 晚上的计划和休息时间
|
||||||
请按照时间顺序列出具体时间点和对应的活动,用一个时间点而不是时间段来表示时间,用JSON格式返回日程表,仅返回内容,不要返回注释,时间采用24小时制,格式为{"时间": "活动","时间": "活动",...}。"""
|
请按照时间顺序列出具体时间点和对应的活动,用一个时间点而不是时间段来表示时间,用JSON格式返回日程表,仅返回内容,不要返回注释,不要添加任何markdown或代码块样式,时间采用24小时制,格式为{"时间": "活动","时间": "活动",...}。"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
schedule_text, _ = await self.llm_scheduler.generate_response(prompt)
|
schedule_text, _ = await self.llm_scheduler.generate_response(prompt)
|
||||||
|
|
@ -91,7 +91,7 @@ class ScheduleGenerator:
|
||||||
try:
|
try:
|
||||||
schedule_dict = json.loads(schedule_text)
|
schedule_dict = json.loads(schedule_text)
|
||||||
return schedule_dict
|
return schedule_dict
|
||||||
except json.JSONDecodeError as e:
|
except json.JSONDecodeError:
|
||||||
logger.exception("解析日程失败: {}".format(schedule_text))
|
logger.exception("解析日程失败: {}".format(schedule_text))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -155,7 +155,7 @@ class LLMStatistics:
|
||||||
all_stats = self._collect_all_statistics()
|
all_stats = self._collect_all_statistics()
|
||||||
self._save_statistics(all_stats)
|
self._save_statistics(all_stats)
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.exception(f"统计数据处理失败")
|
logger.exception("统计数据处理失败")
|
||||||
|
|
||||||
# 等待1分钟
|
# 等待1分钟
|
||||||
for _ in range(60):
|
for _ in range(60):
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
[inner]
|
[inner]
|
||||||
version = "0.0.4"
|
version = "0.0.6"
|
||||||
|
|
||||||
#如果你想要修改配置文件,请在修改后将version的值进行变更
|
#如果你想要修改配置文件,请在修改后将version的值进行变更
|
||||||
#如果新增项目,请在BotConfig类下新增相应的变量
|
#如果新增项目,请在BotConfig类下新增相应的变量
|
||||||
|
|
@ -15,6 +15,7 @@ version = "0.0.4"
|
||||||
[bot]
|
[bot]
|
||||||
qq = 123
|
qq = 123
|
||||||
nickname = "麦麦"
|
nickname = "麦麦"
|
||||||
|
alias_names = ["小麦", "阿麦"]
|
||||||
|
|
||||||
[personality]
|
[personality]
|
||||||
prompt_personality = [
|
prompt_personality = [
|
||||||
|
|
@ -40,6 +41,13 @@ ban_words = [
|
||||||
# "403","张三"
|
# "403","张三"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
ban_msgs_regex = [
|
||||||
|
# 需要过滤的消息(原始消息)匹配的正则表达式,匹配到的消息将被过滤(支持CQ码),若不了解正则表达式请勿修改
|
||||||
|
#"https?://[^\\s]+", # 匹配https链接
|
||||||
|
#"\\d{4}-\\d{2}-\\d{2}", # 匹配日期
|
||||||
|
# "\\[CQ:at,qq=\\d+\\]" # 匹配@
|
||||||
|
]
|
||||||
|
|
||||||
[emoji]
|
[emoji]
|
||||||
check_interval = 120 # 检查表情包的时间间隔
|
check_interval = 120 # 检查表情包的时间间隔
|
||||||
register_interval = 10 # 注册表情包的时间间隔
|
register_interval = 10 # 注册表情包的时间间隔
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue