diff --git a/.gitattributes b/.gitattributes index cf5cffa22..1c4521779 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,3 @@ *.bat text eol=crlf -*.cmd text eol=crlf \ No newline at end of file +*.cmd text eol=crlf +MaiLauncher.bat text eol=crlf working-tree-encoding=GBK \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index a4245d0a0..ce2623260 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -12,6 +12,23 @@ body: - label: "我确认在 Issues 列表中并无其他人已经提出过与此问题相同或相似的问题" required: true - label: "我使用了 Docker" +- type: dropdown + attributes: + label: "使用的分支" + description: "请选择您正在使用的版本分支" + options: + - main + - main-fix + - refactor + validations: + required: true +- type: input + attributes: + label: "具体版本号" + description: "请输入您使用的具体版本号" + placeholder: "例如:0.5.11、0.5.8" + validations: + required: true - type: textarea attributes: label: 遇到的问题 diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 000000000..19a587960 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,17 @@ + +- 🔴**当前项目处于重构阶段(2025.3.14-)** +- ✅ 接受:与main直接相关的Bug修复:提交到main-fix分支 +- ⚠️ 冻结:所有新功能开发和非紧急重构 + +# 请填写以下内容 +(删除掉中括号内的空格,并替换为**小写的x**) +1. - [ ] `main` 分支 **禁止修改**,请确认本次提交的分支 **不是 `main` 分支** +2. - [ ] 本次更新 **包含破坏性变更**(如数据库结构变更、配置文件修改等) +3. - [ ] 本次更新是否经过测试 +4. - [ ] 请**不要**在数据库中添加group_id字段,这会影响本项目对其他平台的兼容 +5. 请填写破坏性更新的具体内容(如有): +6. 请简要说明本次更新的内容和目的: +# 其他信息 +- **关联 Issue**:Close # +- **截图/GIF**: +- **附加信息**: diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 5b09b8cda..c06d967ca 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -4,11 +4,10 @@ on: push: branches: - main - - debug # 新增 debug 分支触发 - - stable-dev + - main-fix tags: - - 'v*' - workflow_dispatch: + - 'v*' + workflow_dispatch: jobs: build-and-push: @@ -16,7 +15,7 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v4 - + - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 @@ -33,10 +32,8 @@ jobs: echo "tags=${{ secrets.DOCKERHUB_USERNAME }}/maimbot:${{ github.ref_name }},${{ secrets.DOCKERHUB_USERNAME }}/maimbot:latest" >> $GITHUB_OUTPUT elif [ "${{ github.ref }}" == "refs/heads/main" ]; then echo "tags=${{ secrets.DOCKERHUB_USERNAME }}/maimbot:main,${{ secrets.DOCKERHUB_USERNAME }}/maimbot:latest" >> $GITHUB_OUTPUT - elif [ "${{ github.ref }}" == "refs/heads/debug" ]; then - echo "tags=${{ secrets.DOCKERHUB_USERNAME }}/maimbot:debug" >> $GITHUB_OUTPUT - elif [ "${{ github.ref }}" == "refs/heads/stable-dev" ]; then - echo "tags=${{ secrets.DOCKERHUB_USERNAME }}/maimbot:stable-dev" >> $GITHUB_OUTPUT + elif [ "${{ github.ref }}" == "refs/heads/main-fix" ]; then + echo "tags=${{ secrets.DOCKERHUB_USERNAME }}/maimbot:main-fix" >> $GITHUB_OUTPUT fi - name: Build and Push Docker Image @@ -48,4 +45,4 @@ jobs: tags: ${{ steps.tags.outputs.tags }} push: true cache-from: type=registry,ref=${{ secrets.DOCKERHUB_USERNAME }}/maimbot:buildcache - cache-to: type=registry,ref=${{ secrets.DOCKERHUB_USERNAME }}/maimbot:buildcache,mode=max \ No newline at end of file + cache-to: type=registry,ref=${{ secrets.DOCKERHUB_USERNAME }}/maimbot:buildcache,mode=max diff --git a/.github/workflows/precheck.yml b/.github/workflows/precheck.yml new file mode 100644 index 000000000..a7524ccb3 --- /dev/null +++ b/.github/workflows/precheck.yml @@ -0,0 +1,29 @@ +# .github/workflows/precheck.yml +name: PR Precheck +on: [pull_request] + +jobs: + conflict-check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Check Conflicts + run: | + git fetch origin main + if git diff --name-only --diff-filter=U origin/main...HEAD | grep .; then + echo "CONFLICT=true" >> $GITHUB_ENV + fi + labeler: + runs-on: ubuntu-latest + needs: conflict-check + steps: + - uses: actions/github-script@v6 + if: env.CONFLICT == 'true' + with: + script: | + github.rest.issues.addLabels({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + labels: ['🚫冲突需处理'] + }) diff --git a/.github/workflows/ruff.yml b/.github/workflows/ruff.yml index 0d1e50c5a..697c47759 100644 --- a/.github/workflows/ruff.yml +++ b/.github/workflows/ruff.yml @@ -5,4 +5,5 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: astral-sh/ruff-action@v3 \ No newline at end of file + - uses: astral-sh/ruff-action@v3 + diff --git a/.gitignore b/.gitignore index b4c7154de..d257c3689 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,9 @@ memory_graph.gml .env.* config/bot_config_dev.toml config/bot_config.toml +config/bot_config.toml.bak +src/plugins/remote/client_uuid.json +run_none.bat # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] @@ -24,9 +27,10 @@ llm_statistics.txt mongodb napcat run_dev.bat - +elua.confirmed # C extensions *.so +/results # Distribution / packaging .Python @@ -204,3 +208,21 @@ jieba.cache .idea *.iml *.ipr + +# PyEnv +# If using PyEnv and configured to use a specific Python version locally +# a .local-version file will be created in the root of the project to specify the version. +.python-version + +OtherRes.txt + +/eula.confirmed +/privacy.confirmed + +logs + +.ruff_cache + +.vscode + +/config/* \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 47f3479a0..000000000 --- a/CLAUDE.md +++ /dev/null @@ -1,191 +0,0 @@ -# MaiMBot 项目架构与索引指南 - -## 🛠️ 常用命令 - -- **运行机器人**: `python run.py` 或 `python bot.py` -- **安装依赖**: `pip install --upgrade -r requirements.txt` -- **Docker 部署**: `docker-compose up` -- **代码检查**: `ruff check .` -- **代码格式化**: `ruff format .` -- **内存可视化**: `run_memory_vis.bat` 或 `python -m src.plugins.memory_system.draw_memory` -- **推理过程可视化**: `script/run_thingking.bat` - -## 🔧 脚本工具 - -- **运行MongoDB**: `script/run_db.bat` - 在端口27017启动MongoDB -- **Windows完整启动**: `script/run_windows.bat` - 检查Python版本、设置虚拟环境、安装依赖并运行机器人 -- **快速启动**: `script/run_maimai.bat` - 设置UTF-8编码并执行"nb run"命令 - -## 📝 代码风格 - -- **Python版本**: 3.9+ -- **行长度限制**: 88字符 -- **命名规范**: - - `snake_case` 用于函数和变量 - - `PascalCase` 用于类 - - `_prefix` 用于私有成员 -- **导入顺序**: 标准库 → 第三方库 → 本地模块 -- **异步编程**: 对I/O操作使用async/await -- **日志记录**: 使用loguru进行一致的日志记录 -- **错误处理**: 使用带有具体异常的try/except -- **文档**: 为类和公共函数编写docstrings - -## 🔍 项目结构概览 - -```mermaid -graph TD - A[入口文件] --> A1[run.py:初始安装与启动] - A --> A2[bot.py:主程序入口] - A2 --> B[核心框架] - B --> B1[NoneBot2框架] - B --> B2[MongoDB数据库] - - A2 --> C[插件系统] - C --> C1[聊天系统] - C --> C2[记忆系统] - C --> C3[情绪系统] - C --> C4[日程系统] - C --> C5[配置系统] - - C1 --> D[LLM集成] - D --> D1[ChatAnywhere] - D --> D2[SiliconFlow] - D --> D3[DeepSeek] -``` - -## 📁 关键文件索引 - -| 文件路径 | 功能描述 | -|---------|---------| -| `/bot.py` | 主程序入口,初始化框架和插件加载 | -| `/run.py` | 初始安装脚本,配置MongoDB和启动机器人 | -| `/src/plugins/chat/bot.py` | 聊天核心处理,消息接收与分发 | -| `/src/plugins/chat/llm_generator.py` | LLM交互封装,生成回复内容 | -| `/src/plugins/chat/prompt_builder.py` | 构建提示词,整合上下文和记忆 | -| `/src/plugins/memory_system/memory.py` | 图形记忆系统核心实现 | -| `/src/plugins/moods/moods.py` | 情绪管理系统 | -| `/src/common/database.py` | 数据库连接管理 | -| `/src/plugins/models/utils_model.py` | LLM API请求封装 | -| `/template.env` | 环境变量配置模板 | -| `/template/bot_config_template.toml` | 机器人配置模板 | - -## 🔄 核心流程图 - -### 消息处理流程 - -```mermaid -flowchart LR - A[用户消息] --> B[NoneBot2接收] - B --> C[ChatBot.handle_message] - C --> D{检查回复意愿} - D -->|回复| E[思考状态] - D -->|不回复| Z[结束] - E --> F[构建提示词] - F --> G[选择LLM模型] - G --> H[生成回复] - H --> I[处理回复] - I --> J[消息管理器] - J --> K[发送回复] -``` - -### 记忆系统流程 - -```mermaid -flowchart TD - A[聊天记录] --> B[记忆样本获取] - B --> C[记忆压缩/主题提取] - C --> D[记忆图存储] - D --> E[记忆检索] - D --> F[记忆遗忘] - D --> G[记忆合并] - E --> H[提示词构建] - H --> I[LLM生成] -``` - -## ⚙️ 配置系统概览 - -```mermaid -graph LR - A[配置系统] --> B[环境变量配置] - A --> C[TOML配置文件] - - B --> B1[数据库连接] - B --> B2[LLM API密钥] - B --> B3[服务器设置] - - C --> C1[机器人人格] - C --> C2[消息处理参数] - C --> C3[记忆系统参数] - C --> C4[情绪系统参数] - C --> C5[模型配置] -``` - -## 📊 模块依赖关系 - -```mermaid -graph TD - A[bot.py] --> B[src/plugins] - B --> C[chat] - B --> D[memory_system] - B --> E[moods] - B --> F[models] - - C --> D - C --> E - C --> F - D --> F - C --> G[common/database.py] - D --> G -``` - -## 🧠 记忆系统内部结构 - -- **Memory_graph**: 底层图结构实现 - - 节点 = 主题概念 - - 边 = 主题间关联 - - 属性 = 记忆内容、时间戳 - -- **Hippocampus**: 高级记忆管理 - - 记忆构建: `memory_compress()` - - 记忆检索: `get_relevant_memories()` - - 记忆遗忘: `operation_forget_topic()` - - 记忆合并: `operation_merge_memory()` - -- **LLM集成点**: - - 主题提取 - - 记忆摘要生成 - - 相似度计算 - - 记忆压缩 - -## 💬 聊天系统内部结构 - -- **ChatBot**: 核心控制器 - - 消息处理: `handle_message()` - - 响应生成: `generate_response()` - -- **消息处理链**: - - `MessageRecv` → 消息预处理 - - `willing_manager` → 回复决策 - - `prompt_builder` → 提示词构建 - - `LLM_request` → LLM调用 - - `MessageSending` → 消息发送 - -- **关键组件**: - - 消息管理器: 控制消息流 - - 聊天流管理: 维护会话上下文 - - 关系管理器: 用户关系状态 - - 表情管理器: 表情包处理 - -## 🔧 配置项关键参数 - -### 环境变量 (.env) -- MongoDB连接: `MONGODB_HOST`, `MONGODB_PORT`, `DATABASE_NAME` -- LLM API: `CHAT_ANY_WHERE_KEY`, `SILICONFLOW_KEY`, `DEEP_SEEK_KEY` -- 服务设置: `HOST`, `PORT` - -### 机器人配置 (TOML) -- 版本控制: `[inner].version` -- 人格设置: `[personality]` -- 记忆参数: `[memory]` (构建间隔、压缩率、遗忘周期) -- 情绪参数: `[mood]` (更新间隔、衰减率) -- 模型选择: `[model]` (各功能专用模型配置) \ No newline at end of file diff --git a/EULA.md b/EULA.md new file mode 100644 index 000000000..c7a734a27 --- /dev/null +++ b/EULA.md @@ -0,0 +1,97 @@ +# **MaiMBot最终用户许可协议** +**版本:V1.0** +**更新日期:2025年3月18日** +**生效日期:2025年3月18日** +**适用的MaiMBot版本号:<=v0.5.15** + +**2025© MaiMBot项目团队** + +--- + +## 一、一般条款 + +**1.1** MaiMBot项目(包括MaiMBot的源代码、可执行文件、文档,以及其它在本协议中所列出的文件)(以下简称“本项目”)是由开发者及贡献者(以下简称“项目团队”)共同维护,为用户提供自动回复功能的机器人代码项目。以下最终用户许可协议(EULA,以下简称“本协议”)是用户(以下简称“您”)与项目团队之间关于使用本项目所订立的合同条件。 + +**1.2** 在运行或使用本项目之前,您**必须阅读并同意本协议的所有条款**。未成年人或其它无/不完全民事行为能力责任人请**在监护人的陪同下**阅读并同意本协议。如果您不同意,则不得运行或使用本项目。在这种情况下,您应立即从您的设备上卸载或删除本项目及其所有副本。 + + +## 二、许可授权 + +### 源代码许可 +**2.1** 您**了解**本项目的源代码是基于GPLv3(GNU通用公共许可证第三版)开源协议发布的。您**可以自由使用、修改、分发**本项目的源代码,但**必须遵守**GPLv3许可证的要求。详细内容请参阅项目仓库中的LICENSE文件。 + +**2.2** 您**了解**本项目的源代码中可能包含第三方开源代码,这些代码的许可证可能与GPLv3许可证不同。您**同意**在使用这些代码时**遵守**相应的许可证要求。 + + +### 输入输出内容授权 + +**2.3** 您**了解**本项目是使用您的配置信息、提交的指令(以下简称“输入内容”)和生成的内容(以下简称“输出内容”)构建请求发送到第三方API生成回复的机器人项目。 + +**2.4** 您**授权**本项目使用您的输入和输出内容按照项目的隐私政策用于以下行为: + - 调用第三方API生成回复; + - 调用第三方API用于构建本项目专用的存储于您部署或使用的数据库中的知识库和记忆库; + - 收集并记录本项目专用的存储于您部署或使用的设备中的日志; + +**2.4** 您**了解**本项目的源代码中包含第三方API的调用代码,这些API的使用可能受到第三方的服务条款和隐私政策的约束。在使用这些API时,您**必须遵守**相应的服务条款。 + +**2.5** 项目团队**不对**第三方API的服务质量、稳定性、准确性、安全性负责,亦**不对**第三方API的服务变更、终止、限制等行为负责。 + + +## 三、用户行为 + +**3.1** 您**了解**本项目会将您的配置信息、输入指令和生成内容发送到第三方API,您**不应**在输入指令和生成内容中包含以下内容: + - 涉及任何国家或地区秘密、商业秘密或其他可能会对国家或地区安全或者公共利益造成不利影响的数据; + - 涉及个人隐私、个人信息或其他敏感信息的数据; + - 任何侵犯他人合法权益的内容; + - 任何违反国家或地区法律法规、政策规定的内容; + +**3.2** 您**不应**将本项目用于以下用途: + - 违反任何国家或地区法律法规、政策规定的行为; + +**3.3** 您**应当**自行确保您被存储在本项目的知识库、记忆库和日志中的输入和输出内容的合法性与合规性以及存储行为的合法性与合规性。您需**自行承担**由此产生的任何法律责任。 + + + +## 四、免责条款 + +**4.1** 本项目的输出内容依赖第三方API,**不受**项目团队控制,亦**不代表**项目团队的观点。 + +**4.2** 除本协议条目2.4提到的隐私政策之外,项目团队**不会**对您提供任何形式的担保,亦**不对**使用本项目的造成的任何后果负责。 + +## 五、其他条款 + +**5.1** 项目团队有权**随时修改本协议的条款**,但**没有**义务通知您。修改后的协议将在本项目的新版本中生效,您应定期检查本协议的最新版本。 + +**5.2** 项目团队**保留**本协议的最终解释权。 + + +## 附录:其他重要须知 + +### 一、过往版本使用条件追溯 + +**1.1** 对于本项目此前未配备 EULA 协议的版本,自本协议发布之日起,若用户希望继续使用本项目,应在本协议生效后的合理时间内,通过升级到最新版本并同意本协议全部条款。若在本版协议生效日(2025年3月18日)之后,用户仍使用此前无 EULA 协议的项目版本且未同意本协议,则用户无权继续使用,项目方有权采取措施阻止其使用行为,并保留追究相关法律责任的权利。 + + +### 二、风险提示 + +**2.1 隐私安全风险** + + - 本项目会将您的配置信息、输入指令和生成内容发送到第三方API,而这些API的服务质量、稳定性、准确性、安全性不受项目团队控制。 + - 本项目会收集您的输入和输出内容,用于构建本项目专用的知识库和记忆库,以提高回复的准确性和连贯性。 + + **因此,为了保障您的隐私信息安全,请注意以下事项:** + + - 避免在涉及个人隐私、个人信息或其他敏感信息的环境中使用本项目; + - 避免在不可信的环境中使用本项目; + +**2.2 精神健康风险** + +本项目仅为工具型机器人,不具备情感交互能力。建议用户: + - 避免过度依赖AI回复处理现实问题或情绪困扰; + - 如感到心理不适,请及时寻求专业心理咨询服务。 + - 如遇心理困扰,请寻求专业帮助(全国心理援助热线:12355)。 + +### 三、其他 +**3.1 争议解决** + - 本协议适用中国法律,争议提交相关地区法院管辖; + - 若因GPLv3许可产生纠纷,以许可证官方解释为准。 diff --git a/PRIVACY.md b/PRIVACY.md new file mode 100644 index 000000000..ba85f617f --- /dev/null +++ b/PRIVACY.md @@ -0,0 +1,21 @@ +### MaiMBot用户隐私条款 +**版本:V1.0** +**更新日期:2025年3月18日** +**生效日期:2025年3月18日** +**适用的MaiMBot版本号:<=v0.5.15** + +**2025© MaiMBot项目团队** + +MaiMBot项目团队(以下简称项目团队)**尊重并保护**用户(以下简称您)的隐私。若您选择使用MaiMBot项目(以下简称本项目),则您需同意本项目按照以下隐私条款处理您的输入和输出内容: + +**1.1** 本项目**会**收集您的输入和输出内容并发送到第三方API,用于生成新的输出内容。因此您的输入和输出内容**会**同时受到本项目和第三方API的隐私政策约束。 + +**1.2** 本项目**会**收集您的输入和输出内容,用于构建本项目专用的仅存储在您使用的数据库中的知识库和记忆库,以提高回复的准确性和连贯性。 + +**1.3** 本项目**会**收集您的输入和输出内容,用于生成仅存储于您部署或使用的设备中的不会上传至互联网的日志。但当您向项目团队反馈问题时,项目团队可能需要您提供日志文件以帮助解决问题。 + +**1.4** 本项目可能**会**收集部分统计信息(如使用频率、基础指令类型)以改进服务,您可在[bot_config.toml]中随时关闭此功能**。 + +**1.5** 由于您的自身行为或不可抗力等情形,导致上述可能涉及您隐私或您认为是私人信息的内容发生被泄露、批漏,或被第三方获取、使用、转让等情形的,均由您**自行承担**不利后果,我们对此**不承担**任何责任。 + +**1.6** 项目团队保留在未来更新隐私条款的权利,但没有义务通知您。若您不同意更新后的隐私条款,您应立即停止使用本项目。 \ No newline at end of file diff --git a/README.md b/README.md index 7d06ad789..572c76ad8 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# 麦麦!MaiMBot (编辑中) +# 麦麦!MaiMBot-MaiCore (编辑中)
@@ -10,22 +10,21 @@ ## 📝 项目简介 -**🍔麦麦是一个基于大语言模型的智能QQ群聊机器人** +**🍔MaiCore是一个基于大语言模型的可交互智能体** -- 基于 nonebot2 框架开发 - LLM 提供对话能力 - MongoDB 提供数据持久化支持 -- NapCat 作为QQ协议端支持 +- 可扩展,可支持多种平台和多种功能 -**最新版本: v0.5.13** +**最新版本: v0.6.0** ([查看更新日志](changelog.md)) > [!WARNING] -> 注意,3月12日的v0.5.13, 该版本更新较大,建议单独开文件夹部署,然后转移/data文件 和数据库,数据库可能需要删除messages下的内容(不需要删除记忆) - +> 次版本MaiBot将基于MaiCore运行,不再依赖于nonebot相关组件运行。 +> MaiBot将通过nonebot的插件与nonebot建立联系,然后nonebot与QQ建立联系,实现MaiBot与QQ的交互
- 麦麦演示视频 + 麦麦演示视频
👆 点击观看麦麦演示视频 👆 @@ -39,61 +38,26 @@ > - 由于持续迭代,可能存在一些已知或未知的bug > - 由于开发中,可能消耗较多token -## 💬交流群 -- [一群](https://qm.qq.com/q/VQ3XZrWgMs) 766798517 ,建议加下面的(开发和建议相关讨论)不一定有空回复,会优先写文档和代码 -- [二群](https://qm.qq.com/q/RzmCiRtHEW) 571780722 (开发和建议相关讨论)不一定有空回复,会优先写文档和代码 -- [三群](https://qm.qq.com/q/wlH5eT8OmQ) 1035228475(开发和建议相关讨论)不一定有空回复,会优先写文档和代码 -- [四群](https://qm.qq.com/q/wlH5eT8OmQ) 729957033(开发和建议相关讨论)不一定有空回复,会优先写文档和代码 +### 💬交流群(开发和建议相关讨论)不一定有空回复,会优先写文档和代码 +- [五群](https://qm.qq.com/q/JxvHZnxyec) 1022489779 +- [一群](https://qm.qq.com/q/VQ3XZrWgMs) 766798517 【已满】 +- [二群](https://qm.qq.com/q/RzmCiRtHEW) 571780722【已满】 +- [三群](https://qm.qq.com/q/wlH5eT8OmQ) 1035228475【已满】 +- [四群](https://qm.qq.com/q/wlH5eT8OmQ) 729957033【已满】 - -**📚 有热心网友创作的wiki:** https://maimbot.pages.dev/ - - -**😊 其他平台版本** - -- (由 [CabLate](https://github.com/cablate) 贡献) [Telegram 与其他平台(未来可能会有)的版本](https://github.com/cablate/MaiMBot/tree/telegram) - [集中讨论串](https://github.com/SengokuCola/MaiMBot/discussions/149) - - - -## 📝 注意注意注意注意注意注意注意注意注意注意注意注意注意注意注意注意注意 -**如果你有想法想要提交pr** -- 由于本项目在快速迭代和功能调整,并且有重构计划,目前不接受任何未经过核心开发组讨论的pr合并,谢谢!如您仍旧希望提交pr,可以详情请看置顶issue -
-

📚 文档 ⬇️ 快速开始使用麦麦 ⬇️

+

📚 文档

-### 部署方式(忙于开发,部分内容可能过时) +### (部分内容可能过时,请注意版本对应) -- 📦 **Windows 一键傻瓜式部署**:请运行项目根目录中的 `run.bat`,部署完成后请参照后续配置指南进行配置 +### 核心文档 +- [📚 核心Wiki文档](https://docs.mai-mai.org) - 项目最全面的文档中心,你可以了解麦麦有关的一切 -- 📦 Linux 自动部署(实验) :请下载并运行项目根目录中的`run.sh`并按照提示安装,部署完成后请参照后续配置指南进行配置 +### 最新版本部署教程(MaiCore版本) +- [🚀 最新版本部署教程](https://docs.mai-mai.org/manual/deployment/refactor_deploy.html) - 基于MaiCore的新版本部署方式(与旧版本不兼容) -- [📦 Windows 手动部署指南 ](docs/manual_deploy_windows.md) - -- [📦 Linux 手动部署指南 ](docs/manual_deploy_linux.md) - -如果你不知道Docker是什么,建议寻找相关教程或使用手动部署 **(现在不建议使用docker,更新慢,可能不适配)** - -- [🐳 Docker部署指南](docs/docker_deploy.md) - - - -### 配置说明 - -- [🎀 新手配置指南](docs/installation_cute.md) - 通俗易懂的配置教程,适合初次使用的猫娘 -- [⚙️ 标准配置指南](docs/installation_standard.md) - 简明专业的配置说明,适合有经验的用户 - -### 常见问题 - -- [❓ 快速 Q & A ](docs/fast_q_a.md) - 针对新手的疑难解答,适合完全没接触过编程的新手 - -
-

了解麦麦

-
- -- [项目架构说明](docs/doc1.md) - 项目结构和核心功能实现细节 ## 🎯 功能介绍 @@ -104,83 +68,85 @@ - 支持多模型,多厂商自定义配置 - 动态的prompt构建器,更拟人 - 支持图片,转发消息,回复消息的识别 -- 错别字和多条回复功能:麦麦可以随机生成错别字,会多条发送回复以及对消息进行reply +- 支持私聊功能,包括消息处理和回复 + +### 🧠 思维流系统(实验性功能) +- 思维流能够生成实时想法,增加回复的拟人性 +- 思维流与日程系统联动,实现动态日程生成 + +### 🧠 记忆系统 +- 对聊天记录进行概括存储,在需要时调用 ### 😊 表情包功能 - - 支持根据发言内容发送对应情绪的表情包 - 会自动偷群友的表情包 +- 表情包审查功能 +- 表情包文件完整性自动检查 ### 📅 日程功能 - - 麦麦会自动生成一天的日程,实现更拟人的回复 +- 支持动态日程生成 +- 优化日程文本解析功能 -### 🧠 记忆功能 +### 👥 关系系统 +- 针对每个用户创建"关系",可以对不同用户进行个性化回复 -- 对聊天记录进行概括存储,在需要时调用,待完善 +### 📊 统计系统 +- 详细统计系统 +- LLM使用统计 -### 📚 知识库功能 - -- 基于embedding模型的知识库,手动放入txt会自动识别,写完了,暂时禁用 - -### 👥 关系功能 - -- 针对每个用户创建"关系",可以对不同用户进行个性化回复,目前只有极其简单的好感度(WIP) -- 针对每个群创建"群印象",可以对不同群进行个性化回复(WIP) +### 🔧 系统功能 +- 支持优雅的shutdown机制 +- 自动保存功能,定期保存聊天记录和关系数据 ## 开发计划TODO:LIST -规划主线 -0.6.0:记忆系统更新 -0.7.0: 麦麦RunTime - - 人格功能:WIP -- 群氛围功能:WIP +- 对特定对象的侧写功能 - 图片发送,转发功能:WIP -- 幽默和meme功能:WIP的WIP -- 让麦麦玩mc:WIP的WIP的WIP +- 幽默和meme功能:WIP - 兼容gif的解析和保存 - 小程序转发链接解析 -- 对思考链长度限制 - 修复已知bug -- ~~完善文档~~ -- 修复转发 -- ~~config自动生成和检测~~ -- ~~log别用print~~ -- ~~给发送消息写专门的类~~ -- 改进表情包发送逻辑 - 自动生成的回复逻辑,例如自生成的回复方向,回复风格 -- 采用截断生成加快麦麦的反应速度 -- 改进发送消息的触发 -## 设计理念 +## ✍️如何给本项目报告BUG/提交建议/做贡献 + +MaiCore是一个开源项目,我们非常欢迎你的参与。你的贡献,无论是提交bug报告、功能需求还是代码pr,都对项目非常宝贵。我们非常感谢你的支持!🎉 但无序的讨论会降低沟通效率,进而影响问题的解决速度,因此在提交任何贡献前,请务必先阅读本项目的[贡献指南](CONTRIBUTE.md)(待补完) + + + +## 设计理念(原始时代的火花) + +> **千石可乐说:** +> - 这个项目最初只是为了给牛牛bot添加一点额外的功能,但是功能越写越多,最后决定重写。其目的是为了创造一个活跃在QQ群聊的"生命体"。可以目的并不是为了写一个功能齐全的机器人,而是一个尽可能让人感知到真实的类人存在。 +> - 程序的功能设计理念基于一个核心的原则:"最像而不是好" +> - 如果人类真的需要一个AI来陪伴自己,并不是所有人都需要一个完美的,能解决所有问题的"helpful assistant",而是一个会犯错的,拥有自己感知和想法的"生命形式"。 +> - 代码会保持开源和开放,但个人希望MaiMbot的运行时数据保持封闭,尽量避免以显式命令来对其进行控制和调试.我认为一个你无法完全掌控的个体才更能让你感觉到它的自主性,而视其成为一个对话机器. +> - SengokuCola~~纯编程外行,面向cursor编程,很多代码写得不好多多包涵~~已得到大脑升级 -- **千石可乐说:** -- 这个项目最初只是为了给牛牛bot添加一点额外的功能,但是功能越写越多,最后决定重写。其目的是为了创造一个活跃在QQ群聊的"生命体"。可以目的并不是为了写一个功能齐全的机器人,而是一个尽可能让人感知到真实的类人存在. -- 程序的功能设计理念基于一个核心的原则:"最像而不是好" -- 主打一个陪伴 -- 如果人类真的需要一个AI来陪伴自己,并不是所有人都需要一个完美的,能解决所有问题的helpful assistant,而是一个会犯错的,拥有自己感知和想法的"生命形式"。 -- 代码会保持开源和开放,但个人希望MaiMbot的运行时数据保持封闭,尽量避免以显式命令来对其进行控制和调试.我认为一个你无法完全掌控的个体才更能让你感觉到它的自主性,而视其成为一个对话机器. ## 📌 注意事项 -SengokuCola~~纯编程外行,面向cursor编程,很多代码写得不好多多包涵~~已得到大脑升级 > [!WARNING] +> 使用本项目前必须阅读和同意用户协议和隐私协议 > 本应用生成内容来自人工智能模型,由 AI 生成,请仔细甄别,请勿用于违反法律的用途,AI生成内容不代表本人观点和立场。 ## 致谢 -[nonebot2](https://github.com/nonebot/nonebot2): 跨平台 Python 异步聊天机器人框架 -[NapCat](https://github.com/NapNeko/NapCatQQ): 现代化的基于 NTQQ 的 Bot 协议端实现 +- [nonebot2](https://github.com/nonebot/nonebot2): 跨平台 Python 异步聊天机器人框架 +- [NapCat](https://github.com/NapNeko/NapCatQQ): 现代化的基于 NTQQ 的 Bot 协议端实现 ### 贡献者 感谢各位大佬! -
- + + +**也感谢每一位给麦麦发展提出宝贵意见与建议的用户,感谢陪伴麦麦走到现在的你们** + ## Stargazers over time -[![Stargazers over time](https://starchart.cc/SengokuCola/MaiMBot.svg?variant=adaptive)](https://starchart.cc/SengokuCola/MaiMBot) +[![Stargazers over time](https://starchart.cc/MaiM-with-u/MaiBot.svg?variant=adaptive)](https://starchart.cc/MaiM-with-u/MaiBot) diff --git a/bot.py b/bot.py index 7a97f485e..a0bf3a3cb 100644 --- a/bot.py +++ b/bot.py @@ -1,19 +1,16 @@ import asyncio +import hashlib import os import shutil import sys - -import nonebot +from pathlib import Path import time - -import uvicorn -from dotenv import load_dotenv -from nonebot.adapters.onebot.v11 import Adapter import platform -from src.plugins.utils.logger_config import LogModule, LogClassification +from dotenv import load_dotenv +from src.common.logger import get_module_logger +from src.main import MainSystem - -# 配置日志格式 +logger = get_module_logger("main_bot") # 获取没有加载env时的环境变量 env_mask = {key: os.getenv(key) for key in os.environ} @@ -48,62 +45,25 @@ def init_config(): logger.info("创建config目录") shutil.copy("template/bot_config_template.toml", "config/bot_config.toml") - logger.info("复制完成,请修改config/bot_config.toml和.env.prod中的配置后重新启动") + logger.info("复制完成,请修改config/bot_config.toml和.env中的配置后重新启动") def init_env(): - # 初始化.env 默认ENVIRONMENT=prod + # 检测.env文件是否存在 if not os.path.exists(".env"): - with open(".env", "w") as f: - f.write("ENVIRONMENT=prod") - - # 检测.env.prod文件是否存在 - if not os.path.exists(".env.prod"): - logger.error("检测到.env.prod文件不存在") - shutil.copy("template.env", "./.env.prod") - - # 检测.env.dev文件是否存在,不存在的话直接复制生产环境配置 - if not os.path.exists(".env.dev"): - logger.error("检测到.env.dev文件不存在") - shutil.copy(".env.prod", "./.env.dev") - - # 首先加载基础环境变量.env - if os.path.exists(".env"): - load_dotenv(".env", override=True) - logger.success("成功加载基础环境变量配置") + logger.error("检测到.env文件不存在") + shutil.copy("template/template.env", "./.env") + logger.info("已从template/template.env复制创建.env,请修改配置后重新启动") def load_env(): - # 使用闭包实现对加载器的横向扩展,避免大量重复判断 - def prod(): - logger.success("加载生产环境变量配置") - load_dotenv(".env.prod", override=True) # override=True 允许覆盖已存在的环境变量 - - def dev(): - logger.success("加载开发环境变量配置") - load_dotenv(".env.dev", override=True) # override=True 允许覆盖已存在的环境变量 - - fn_map = {"prod": prod, "dev": dev} - - env = os.getenv("ENVIRONMENT") - logger.info(f"[load_env] 当前的 ENVIRONMENT 变量值:{env}") - - if env in fn_map: - fn_map[env]() # 根据映射执行闭包函数 - - elif os.path.exists(f".env.{env}"): - logger.success(f"加载{env}环境变量配置") - load_dotenv(f".env.{env}", override=True) # override=True 允许覆盖已存在的环境变量 - + # 直接加载生产环境变量配置 + if os.path.exists(".env"): + load_dotenv(".env", override=True) + logger.success("成功加载环境变量配置") else: - logger.error(f"ENVIRONMENT 配置错误,请检查 .env 文件中的 ENVIRONMENT 变量及对应 .env.{env} 是否存在") - RuntimeError(f"ENVIRONMENT 配置错误,请检查 .env 文件中的 ENVIRONMENT 变量及对应 .env.{env} 是否存在") - - -def load_logger(): - global logger # 使得bot.py中其他函数也能调用 - log_module = LogModule() - logger = log_module.setup_logger(LogClassification.BASE) + logger.error("未找到.env文件,请确保文件存在") + raise FileNotFoundError("未找到.env文件,请确保文件存在") def scan_provider(env_config: dict): @@ -139,11 +99,7 @@ def scan_provider(env_config: dict): async def graceful_shutdown(): try: - global uvicorn_server - if uvicorn_server: - uvicorn_server.force_exit = True # 强制退出 - await uvicorn_server.shutdown() - + logger.info("正在优雅关闭麦麦...") tasks = [t for t in asyncio.all_tasks() if t is not asyncio.current_task()] for task in tasks: task.cancel() @@ -153,75 +109,124 @@ async def graceful_shutdown(): 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 check_eula(): + eula_confirm_file = Path("eula.confirmed") + privacy_confirm_file = Path("privacy.confirmed") + eula_file = Path("EULA.md") + privacy_file = Path("PRIVACY.md") + + eula_updated = True + eula_new_hash = None + privacy_updated = True + privacy_new_hash = None + + eula_confirmed = False + privacy_confirmed = False + + # 首先计算当前EULA文件的哈希值 + if eula_file.exists(): + with open(eula_file, "r", encoding="utf-8") as f: + eula_content = f.read() + eula_new_hash = hashlib.md5(eula_content.encode("utf-8")).hexdigest() + else: + logger.error("EULA.md 文件不存在") + raise FileNotFoundError("EULA.md 文件不存在") + + # 首先计算当前隐私条款文件的哈希值 + if privacy_file.exists(): + with open(privacy_file, "r", encoding="utf-8") as f: + privacy_content = f.read() + privacy_new_hash = hashlib.md5(privacy_content.encode("utf-8")).hexdigest() + else: + logger.error("PRIVACY.md 文件不存在") + raise FileNotFoundError("PRIVACY.md 文件不存在") + + # 检查EULA确认文件是否存在 + if eula_confirm_file.exists(): + with open(eula_confirm_file, "r", encoding="utf-8") as f: + confirmed_content = f.read() + if eula_new_hash == confirmed_content: + eula_confirmed = True + eula_updated = False + if eula_new_hash == os.getenv("EULA_AGREE"): + eula_confirmed = True + eula_updated = False + + # 检查隐私条款确认文件是否存在 + if privacy_confirm_file.exists(): + with open(privacy_confirm_file, "r", encoding="utf-8") as f: + confirmed_content = f.read() + if privacy_new_hash == confirmed_content: + privacy_confirmed = True + privacy_updated = False + if privacy_new_hash == os.getenv("PRIVACY_AGREE"): + privacy_confirmed = True + privacy_updated = False + + # 如果EULA或隐私条款有更新,提示用户重新确认 + if eula_updated or privacy_updated: + print("EULA或隐私条款内容已更新,请在阅读后重新确认,继续运行视为同意更新后的以上两款协议") + print( + f'输入"同意"或"confirmed"或设置环境变量"EULA_AGREE={eula_new_hash}"和"PRIVACY_AGREE={privacy_new_hash}"继续运行' + ) + while True: + user_input = input().strip().lower() + if user_input in ["同意", "confirmed"]: + # print("确认成功,继续运行") + # print(f"确认成功,继续运行{eula_updated} {privacy_updated}") + if eula_updated: + print(f"更新EULA确认文件{eula_new_hash}") + eula_confirm_file.write_text(eula_new_hash, encoding="utf-8") + if privacy_updated: + print(f"更新隐私条款确认文件{privacy_new_hash}") + privacy_confirm_file.write_text(privacy_new_hash, encoding="utf-8") + break + else: + print('请输入"同意"或"confirmed"以继续运行') + return + elif eula_confirmed and privacy_confirmed: + return def raw_main(): # 利用 TZ 环境变量设定程序工作的时区 - # 仅保证行为一致,不依赖 localtime(),实际对生产环境几乎没有作用 if platform.system().lower() != "windows": time.tzset() + check_eula() + print("检查EULA和隐私条款完成") easter_egg() init_config() init_env() load_env() - - # load_logger() env_config = {key: os.getenv(key) for key in os.environ} scan_provider(env_config) - # 设置基础配置 - base_config = { - "websocket_port": int(env_config.get("PORT", 8080)), - "host": env_config.get("HOST", "127.0.0.1"), - "log_level": "INFO", - } - - # 合并配置 - nonebot.init(**base_config, **env_config) - - # 注册适配器 - global driver - driver = nonebot.get_driver() - driver.register_adapter(Adapter) - - # 加载插件 - nonebot.load_plugins("src/plugins") + # 返回MainSystem实例 + return MainSystem() if __name__ == "__main__": try: - # 配置日志,使得主程序直接退出时候也能访问logger - load_logger() - raw_main() + # 获取MainSystem实例 + main_system = raw_main() - app = nonebot.get_asgi() + # 创建事件循环 loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) - + try: - loop.run_until_complete(uvicorn_main()) + # 执行初始化和任务调度 + loop.run_until_complete(main_system.initialize()) + loop.run_until_complete(main_system.schedule_tasks()) except KeyboardInterrupt: + # loop.run_until_complete(global_api.stop()) logger.warning("收到中断信号,正在优雅关闭...") loop.run_until_complete(graceful_shutdown()) finally: loop.close() - + except Exception as e: logger.error(f"主程序异常: {str(e)}") if loop and not loop.is_closed(): diff --git a/changelog.md b/changelog.md deleted file mode 100644 index b9beed81e..000000000 --- a/changelog.md +++ /dev/null @@ -1,84 +0,0 @@ -# Changelog - -## [0.5.13] - 2025-3-12 -AI总结 -### 🌟 核心功能增强 -#### 记忆系统升级 -- 新增了记忆系统的时间戳功能,包括创建时间和最后修改时间 -- 新增了记忆图节点和边的时间追踪功能 -- 新增了自动补充缺失时间字段的功能 -- 新增了记忆遗忘机制,基于时间条件自动遗忘旧记忆 -- 优化了记忆系统的数据同步机制 -- 优化了记忆系统的数据结构,确保所有数据类型的一致性 - -#### 私聊功能完善 -- 新增了完整的私聊功能支持,包括消息处理和回复 -- 新增了聊天流管理器,支持群聊和私聊的上下文管理 -- 新增了私聊过滤开关功能 -- 优化了关系管理系统,支持跨平台用户关系 - -#### 消息处理升级 -- 新增了消息队列管理系统,支持按时间顺序处理消息 -- 新增了消息发送控制器,实现人性化的发送速度和间隔 -- 新增了JSON格式分享卡片读取支持 -- 新增了Base64格式表情包CQ码支持 -- 改进了消息处理流程,支持多种消息类型 - -### 💻 系统架构优化 -#### 配置系统改进 -- 新增了配置文件自动更新和版本检测功能 -- 新增了配置文件热重载API接口 -- 新增了配置文件版本兼容性检查 -- 新增了根据不同环境(dev/prod)显示不同级别的日志功能 -- 优化了配置文件格式和结构 - -#### 部署支持扩展 -- 新增了Linux系统部署指南 -- 新增了Docker部署支持的详细文档 -- 新增了NixOS环境支持(使用venv方式) -- 新增了优雅的shutdown机制 -- 优化了Docker部署文档 - -### 🛠️ 开发体验提升 -#### 工具链升级 -- 新增了ruff代码格式化和检查工具 -- 新增了知识库一键启动脚本 -- 新增了自动保存脚本,定期保存聊天记录和关系数据 -- 新增了表情包自动获取脚本 -- 优化了日志记录(使用logger.debug替代print) -- 精简了日志输出,禁用了Uvicorn/NoneBot默认日志 - -#### 安全性强化 -- 新增了API密钥安全管理机制 -- 新增了数据库完整性检查功能 -- 新增了表情包文件完整性自动检查 -- 新增了异常处理和自动恢复机制 -- 优化了安全性检查机制 - -### 🐛 关键问题修复 -#### 系统稳定性 -- 修复了systemctl强制停止的问题 -- 修复了ENVIRONMENT变量在同一终端下不能被覆盖的问题 -- 修复了libc++.so依赖问题 -- 修复了数据库索引创建失败的问题 -- 修复了MongoDB连接配置相关问题 -- 修复了消息队列溢出问题 -- 修复了配置文件加载时的版本兼容性问题 - -#### 功能完善性 -- 修复了私聊时产生reply消息的bug -- 修复了回复消息无法识别的问题 -- 修复了CQ码解析错误 -- 修复了情绪管理器导入问题 -- 修复了小名无效的问题 -- 修复了表情包发送时的参数缺失问题 -- 修复了表情包重复注册问题 -- 修复了变量拼写错误问题 - -### 主要改进方向 -1. 提升记忆系统的智能性和可靠性 -2. 完善私聊功能的完整生态 -3. 优化系统架构和部署便利性 -4. 提升开发体验和代码质量 -5. 加强系统安全性和稳定性 - diff --git a/changelog_config.md b/changelog_config.md deleted file mode 100644 index c4c560644..000000000 --- a/changelog_config.md +++ /dev/null @@ -1,12 +0,0 @@ -# Changelog - -## [0.0.5] - 2025-3-11 -### Added -- 新增了 `alias_names` 配置项,用于指定麦麦的别名。 - -## [0.0.4] - 2025-3-9 -### Added -- 新增了 `memory_ban_words` 配置项,用于指定不希望记忆的词汇。 - - - diff --git a/changelogs/changelog.md b/changelogs/changelog.md new file mode 100644 index 000000000..d9759ea11 --- /dev/null +++ b/changelogs/changelog.md @@ -0,0 +1,328 @@ +# Changelog + +## [0.6.0] - 2025-3-30 +### 🌟 核心功能增强 +#### 架构重构 +- 将MaiBot重构为MaiCore独立智能体 +- 移除NoneBot相关代码,改为插件方式与NoneBot对接 +- 精简代码结构,优化文件夹组织 +- 新增详细统计系统 + +#### 思维流系统 +- 新增思维流作为实验功能 +- 思维流大核+小核架构 +- 思维流回复意愿模式 +- 优化思维流自动启停机制,提升资源利用效率 +- 思维流与日程系统联动,实现动态日程生成 +- 优化心流运行逻辑和思考时间计算 +- 添加错误检测机制 +- 修复心流无法观察群消息的问题 + +#### 回复系统 +- 优化回复逻辑,添加回复前思考机制 +- 移除推理模型在回复中的使用 + +#### 记忆系统优化 +- 优化记忆抽取策略 +- 优化记忆prompt结构 +- 改进海马体记忆提取机制,提升自然度 + +#### 关系系统优化 +- 修复relationship_value类型错误 +- 优化关系管理系统 +- 改进关系值计算方式 + +### 💻 系统架构优化 +#### 配置系统改进 +- 优化配置文件整理 +- 新增分割器功能 +- 新增表情惩罚系数自定义 +- 修复配置文件保存问题 +- 优化配置项管理 +- 新增配置项: + - `schedule`: 日程表生成功能配置 + - `response_spliter`: 回复分割控制 + - `experimental`: 实验性功能开关 + - `llm_observation`和`llm_sub_heartflow`: 思维流模型配置 + - `llm_heartflow`: 思维流核心模型配置 + - `prompt_schedule_gen`: 日程生成提示词配置 + - `memory_ban_words`: 记忆过滤词配置 +- 优化配置结构: + - 调整模型配置组织结构 + - 优化配置项默认值 + - 调整配置项顺序 +- 移除冗余配置 + +#### WebUI改进 +- 新增回复意愿模式选择功能 +- 优化WebUI界面 +- 优化WebUI配置保存机制 + +#### 部署支持扩展 +- 优化Docker构建流程 +- 完善Windows脚本支持 +- 优化Linux一键安装脚本 +- 新增macOS教程支持 + +### 🐛 问题修复 +#### 功能稳定性 +- 修复表情包审查器问题 +- 修复心跳发送问题 +- 修复拍一拍消息处理异常 +- 修复日程报错问题 +- 修复文件读写编码问题 +- 修复西文字符分割问题 +- 修复自定义API提供商识别问题 +- 修复人格设置保存问题 +- 修复EULA和隐私政策编码问题 +- 修复cfg变量引用问题 + +#### 性能优化 +- 提高topic提取效率 +- 优化logger输出格式 +- 优化cmd清理功能 +- 改进LLM使用统计 +- 优化记忆处理效率 + +### 📚 文档更新 +- 更新README.md内容 +- 添加macOS部署教程 +- 优化文档结构 +- 更新EULA和隐私政策 +- 完善部署文档 + +### 🔧 其他改进 +- 新增神秘小测验功能 +- 新增人格测评模型 +- 优化表情包审查功能 +- 改进消息转发处理 +- 优化代码风格和格式 +- 完善异常处理机制 +- 优化日志输出格式 +- 版本硬编码,新增配置自动更新功能 +- 更新日程生成器功能 +- 优化了统计信息,会在控制台显示统计信息 + +### 主要改进方向 +1. 完善思维流系统功能 +2. 优化记忆系统效率 +3. 改进关系系统稳定性 +4. 提升配置系统可用性 +5. 加强WebUI功能 +6. 完善部署文档 + + + +## [0.5.15] - 2025-3-17 +### 🌟 核心功能增强 +#### 关系系统升级 +- 新增关系系统构建与启用功能 +- 优化关系管理系统 +- 改进prompt构建器结构 +- 新增手动修改记忆库的脚本功能 +- 增加alter支持功能 + +#### 启动器优化 +- 新增MaiLauncher.bat 1.0版本 +- 优化Python和Git环境检测逻辑 +- 添加虚拟环境检查功能 +- 改进工具箱菜单选项 +- 新增分支重置功能 +- 添加MongoDB支持 +- 优化脚本逻辑 +- 修复虚拟环境选项闪退和conda激活问题 +- 修复环境检测菜单闪退问题 +- 修复.env文件复制路径错误 + +#### 日志系统改进 +- 新增GUI日志查看器 +- 重构日志工厂处理机制 +- 优化日志级别配置 +- 支持环境变量配置日志级别 +- 改进控制台日志输出 +- 优化logger输出格式 + +### 💻 系统架构优化 +#### 配置系统升级 +- 更新配置文件到0.0.10版本 +- 优化配置文件可视化编辑 +- 新增配置文件版本检测功能 +- 改进配置文件保存机制 +- 修复重复保存可能清空list内容的bug +- 修复人格设置和其他项配置保存问题 + +#### WebUI改进 +- 优化WebUI界面和功能 +- 支持安装后管理功能 +- 修复部分文字表述错误 + +#### 部署支持扩展 +- 优化Docker构建流程 +- 改进MongoDB服务启动逻辑 +- 完善Windows脚本支持 +- 优化Linux一键安装脚本 +- 新增Debian 12专用运行脚本 + +### 🐛 问题修复 +#### 功能稳定性 +- 修复bot无法识别at对象和reply对象的问题 +- 修复每次从数据库读取额外加0.5的问题 +- 修复新版本由于版本判断不能启动的问题 +- 修复配置文件更新和学习知识库的确认逻辑 +- 优化token统计功能 +- 修复EULA和隐私政策处理时的编码兼容问题 +- 修复文件读写编码问题,统一使用UTF-8 +- 修复颜文字分割问题 +- 修复willing模块cfg变量引用问题 + +### 📚 文档更新 +- 更新CLAUDE.md为高信息密度项目文档 +- 添加mermaid系统架构图和模块依赖图 +- 添加核心文件索引和类功能表格 +- 添加消息处理流程图 +- 优化文档结构 +- 更新EULA和隐私政策文档 + +### 🔧 其他改进 +- 更新全球在线数量展示功能 +- 优化statistics输出展示 +- 新增手动修改内存脚本(支持添加、删除和查询节点和边) + +### 主要改进方向 +1. 完善关系系统功能 +2. 优化启动器和部署流程 +3. 改进日志系统 +4. 提升配置系统稳定性 +5. 加强文档完整性 + +## [0.5.14] - 2025-3-14 +### 🌟 核心功能增强 +#### 记忆系统优化 +- 修复了构建记忆时重复读取同一段消息导致token消耗暴增的问题 +- 优化了记忆相关的工具模型代码 + +#### 消息处理升级 +- 新增了不回答已撤回消息的功能 +- 新增每小时自动删除存留超过1小时的撤回消息 +- 优化了戳一戳功能的响应机制 +- 修复了回复消息未正常发送的问题 +- 改进了图片发送错误时的处理机制 + +#### 日程系统改进 +- 修复了长时间运行的bot在跨天后无法生成新日程的问题 +- 优化了日程文本解析功能 +- 修复了解析日程时遇到markdown代码块等额外内容的处理问题 + +### 💻 系统架构优化 +#### 日志系统升级 +- 建立了新的日志系统 +- 改进了错误处理机制 +- 优化了代码格式化规范 + +#### 部署支持扩展 +- 改进了NAS部署指南,增加HOST设置说明 +- 优化了部署文档的完整性 + +### 🐛 问题修复 +#### 功能稳定性 +- 修复了utils_model.py中的潜在问题 +- 修复了set_reply相关bug +- 修复了回应所有戳一戳的问题 +- 优化了bot被戳时的判断逻辑 + +### 📚 文档更新 +- 更新了README.md的内容 +- 完善了NAS部署指南 +- 优化了部署相关文档 + +### 主要改进方向 +1. 提升记忆系统的效率和稳定性 +2. 完善消息处理机制 +3. 优化日程系统功能 +4. 改进日志和错误处理 +5. 加强部署文档的完整性 + +## [0.5.13] - 2025-3-12 +### 🌟 核心功能增强 +#### 记忆系统升级 +- 新增了记忆系统的时间戳功能,包括创建时间和最后修改时间 +- 新增了记忆图节点和边的时间追踪功能 +- 新增了自动补充缺失时间字段的功能 +- 新增了记忆遗忘机制,基于时间条件自动遗忘旧记忆 +- 优化了记忆系统的数据同步机制 +- 优化了记忆系统的数据结构,确保所有数据类型的一致性 + +#### 私聊功能完善 +- 新增了完整的私聊功能支持,包括消息处理和回复 +- 新增了聊天流管理器,支持群聊和私聊的上下文管理 +- 新增了私聊过滤开关功能 +- 优化了关系管理系统,支持跨平台用户关系 + +#### 消息处理升级 +- 新增了消息队列管理系统,支持按时间顺序处理消息 +- 新增了消息发送控制器,实现人性化的发送速度和间隔 +- 新增了JSON格式分享卡片读取支持 +- 新增了Base64格式表情包CQ码支持 +- 改进了消息处理流程,支持多种消息类型 + +### 💻 系统架构优化 +#### 配置系统改进 +- 新增了配置文件自动更新和版本检测功能 +- 新增了配置文件热重载API接口 +- 新增了配置文件版本兼容性检查 +- 新增了根据不同环境(dev/prod)显示不同级别的日志功能 +- 优化了配置文件格式和结构 + +#### 部署支持扩展 +- 新增了Linux系统部署指南 +- 新增了Docker部署支持的详细文档 +- 新增了NixOS环境支持(使用venv方式) +- 新增了优雅的shutdown机制 +- 优化了Docker部署文档 + +### 🛠️ 开发体验提升 +#### 工具链升级 +- 新增了ruff代码格式化和检查工具 +- 新增了知识库一键启动脚本 +- 新增了自动保存脚本,定期保存聊天记录和关系数据 +- 新增了表情包自动获取脚本 +- 优化了日志记录(使用logger.debug替代print) +- 精简了日志输出,禁用了Uvicorn/NoneBot默认日志 + +#### 安全性强化 +- 新增了API密钥安全管理机制 +- 新增了数据库完整性检查功能 +- 新增了表情包文件完整性自动检查 +- 新增了异常处理和自动恢复机制 +- 优化了安全性检查机制 + +### 🐛 关键问题修复 +#### 系统稳定性 +- 修复了systemctl强制停止的问题 +- 修复了ENVIRONMENT变量在同一终端下不能被覆盖的问题 +- 修复了libc++.so依赖问题 +- 修复了数据库索引创建失败的问题 +- 修复了MongoDB连接配置相关问题 +- 修复了消息队列溢出问题 +- 修复了配置文件加载时的版本兼容性问题 + +#### 功能完善性 +- 修复了私聊时产生reply消息的bug +- 修复了回复消息无法识别的问题 +- 修复了CQ码解析错误 +- 修复了情绪管理器导入问题 +- 修复了小名无效的问题 +- 修复了表情包发送时的参数缺失问题 +- 修复了表情包重复注册问题 +- 修复了变量拼写错误问题 + +### 主要改进方向 +1. 提升记忆系统的智能性和可靠性 +2. 完善私聊功能的完整生态 +3. 优化系统架构和部署便利性 +4. 提升开发体验和代码质量 +5. 加强系统安全性和稳定性 + + + + diff --git a/changelogs/changelog_config.md b/changelogs/changelog_config.md new file mode 100644 index 000000000..32912f691 --- /dev/null +++ b/changelogs/changelog_config.md @@ -0,0 +1,51 @@ +# Changelog + +## [1.0.3] - 2025-3-31 +### Added +- 新增了心流相关配置项: + - `heartflow` 配置项,用于控制心流功能 + +### Removed +- 移除了 `response` 配置项中的 `model_r1_probability` 和 `model_v3_probability` 选项 +- 移除了次级推理模型相关配置 + +## [1.0.1] - 2025-3-30 +### Added +- 增加了流式输出控制项 `stream` +- 修复 `LLM_Request` 不会自动为 `payload` 增加流式输出标志的问题 + +## [1.0.0] - 2025-3-30 +### Added +- 修复了错误的版本命名 +- 杀掉了所有无关文件 + +## [0.0.11] - 2025-3-12 +### Added +- 新增了 `schedule` 配置项,用于配置日程表生成功能 +- 新增了 `response_spliter` 配置项,用于控制回复分割 +- 新增了 `experimental` 配置项,用于实验性功能开关 +- 新增了 `llm_observation` 和 `llm_sub_heartflow` 模型配置 +- 新增了 `llm_heartflow` 模型配置 +- 在 `personality` 配置项中新增了 `prompt_schedule_gen` 参数 + +### Changed +- 优化了模型配置的组织结构 +- 调整了部分配置项的默认值 +- 调整了配置项的顺序,将 `groups` 配置项移到了更靠前的位置 +- 在 `message` 配置项中: + - 新增了 `max_response_length` 参数 +- 在 `willing` 配置项中新增了 `emoji_response_penalty` 参数 +- 将 `personality` 配置项中的 `prompt_schedule` 重命名为 `prompt_schedule_gen` + +### Removed +- 移除了 `min_text_length` 配置项 +- 移除了 `cq_code` 配置项 +- 移除了 `others` 配置项(其功能已整合到 `experimental` 中) + +## [0.0.5] - 2025-3-11 +### Added +- 新增了 `alias_names` 配置项,用于指定麦麦的别名。 + +## [0.0.4] - 2025-3-9 +### Added +- 新增了 `memory_ban_words` 配置项,用于指定不希望记忆的词汇。 \ No newline at end of file diff --git a/config/auto_update.py b/config/auto_update.py deleted file mode 100644 index 28ab108da..000000000 --- a/config/auto_update.py +++ /dev/null @@ -1,59 +0,0 @@ -import os -import shutil -import tomlkit -from pathlib import Path - -def update_config(): - # 获取根目录路径 - root_dir = Path(__file__).parent.parent - template_dir = root_dir / "template" - config_dir = root_dir / "config" - - # 定义文件路径 - template_path = template_dir / "bot_config_template.toml" - old_config_path = config_dir / "bot_config.toml" - new_config_path = config_dir / "bot_config.toml" - - # 读取旧配置文件 - old_config = {} - if old_config_path.exists(): - with open(old_config_path, "r", encoding="utf-8") as f: - old_config = tomlkit.load(f) - - # 删除旧的配置文件 - if old_config_path.exists(): - os.remove(old_config_path) - - # 复制模板文件到配置目录 - shutil.copy2(template_path, new_config_path) - - # 读取新配置文件 - with open(new_config_path, "r", encoding="utf-8") as f: - new_config = tomlkit.load(f) - - # 递归更新配置 - def update_dict(target, source): - for key, value in source.items(): - # 跳过version字段的更新 - if key == "version": - continue - if key in target: - if isinstance(value, dict) and isinstance(target[key], (dict, tomlkit.items.Table)): - update_dict(target[key], value) - else: - try: - # 直接使用tomlkit的item方法创建新值 - target[key] = tomlkit.item(value) - except (TypeError, ValueError): - # 如果转换失败,直接赋值 - target[key] = value - - # 将旧配置的值更新到新配置中 - update_dict(new_config, old_config) - - # 保存更新后的配置(保留注释和格式) - with open(new_config_path, "w", encoding="utf-8") as f: - f.write(tomlkit.dumps(new_config)) - -if __name__ == "__main__": - update_config() diff --git a/char_frequency.json b/depends-data/char_frequency.json similarity index 100% rename from char_frequency.json rename to depends-data/char_frequency.json diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index 227df606b..000000000 --- a/docker-compose.yml +++ /dev/null @@ -1,56 +0,0 @@ -services: - napcat: - container_name: napcat - environment: - - TZ=Asia/Shanghai - - NAPCAT_UID=${NAPCAT_UID} - - NAPCAT_GID=${NAPCAT_GID} # 让 NapCat 获取当前用户 GID,UID,防止权限问题 - ports: - - 6099:6099 - restart: unless-stopped - volumes: - - napcatQQ:/app/.config/QQ # 持久化 QQ 本体 - - napcatCONFIG:/app/napcat/config # 持久化 NapCat 配置文件 - - maimbotDATA:/MaiMBot/data # NapCat 和 NoneBot 共享此卷,否则发送图片会有问题 - image: mlikiowa/napcat-docker:latest - - mongodb: - container_name: mongodb - environment: - - TZ=Asia/Shanghai - # - MONGO_INITDB_ROOT_USERNAME=your_username - # - MONGO_INITDB_ROOT_PASSWORD=your_password - expose: - - "27017" - restart: unless-stopped - volumes: - - mongodb:/data/db # 持久化 MongoDB 数据库 - - mongodbCONFIG:/data/configdb # 持久化 MongoDB 配置文件 - image: mongo:latest - - maimbot: - container_name: maimbot - environment: - - TZ=Asia/Shanghai - expose: - - "8080" - restart: unless-stopped - depends_on: - - mongodb - - napcat - volumes: - - napcatCONFIG:/MaiMBot/napcat # 自动根据配置中的 QQ 号创建 ws 反向客户端配置 - - ./bot_config.toml:/MaiMBot/config/bot_config.toml # Toml 配置文件映射 - - maimbotDATA:/MaiMBot/data # NapCat 和 NoneBot 共享此卷,否则发送图片会有问题 - - ./.env.prod:/MaiMBot/.env.prod # Toml 配置文件映射 - image: sengokucola/maimbot:latest - -volumes: - maimbotCONFIG: - maimbotDATA: - napcatQQ: - napcatCONFIG: - mongodb: - mongodbCONFIG: - - diff --git a/docs/API_KEY.png b/docs/API_KEY.png deleted file mode 100644 index 901d1d137..000000000 Binary files a/docs/API_KEY.png and /dev/null differ diff --git a/docs/Jonathan R.md b/docs/Jonathan R.md deleted file mode 100644 index 660caaeec..000000000 --- a/docs/Jonathan R.md +++ /dev/null @@ -1,20 +0,0 @@ -Jonathan R. Wolpaw 在 “Memory in neuroscience: rhetoric versus reality.” 一文中提到,从神经科学的感觉运动假设出发,整个神经系统的功能是将经验与适当的行为联系起来,而不是单纯的信息存储。 -Jonathan R,Wolpaw. (2019). Memory in neuroscience: rhetoric versus reality.. Behavioral and cognitive neuroscience reviews(2). - -1. **单一过程理论** - - 单一过程理论认为,识别记忆主要是基于熟悉性这一单一因素的影响。熟悉性是指对刺激的一种自动的、无意识的感知,它可以使我们在没有回忆起具体细节的情况下,判断一个刺激是否曾经出现过。 - - 例如,在一些实验中,研究者发现被试可以在没有回忆起具体学习情境的情况下,对曾经出现过的刺激做出正确的判断,这被认为是熟悉性在起作用1。 -2. **双重过程理论** - - 双重过程理论则认为,识别记忆是基于两个过程:回忆和熟悉性。回忆是指对过去经验的有意识的回忆,它可以使我们回忆起具体的细节和情境;熟悉性则是一种自动的、无意识的感知。 - - 该理论认为,在识别记忆中,回忆和熟悉性共同作用,使我们能够判断一个刺激是否曾经出现过。例如,在 “记得 / 知道” 范式中,被试被要求判断他们对一个刺激的记忆是基于回忆还是熟悉性。研究发现,被试可以区分这两种不同的记忆过程,这为双重过程理论提供了支持1。 - - - -1. **神经元节点与连接**:借鉴神经网络原理,将每个记忆单元视为一个神经元节点。节点之间通过连接相互关联,连接的强度代表记忆之间的关联程度。在形态学联想记忆中,具有相似形态特征的记忆节点连接强度较高。例如,苹果和橘子的记忆节点,由于在形状、都是水果等形态语义特征上相似,它们之间的连接强度大于苹果与汽车记忆节点间的连接强度。 -2. **记忆聚类与层次结构**:依据形态特征的相似性对记忆进行聚类,形成不同的记忆簇。每个记忆簇内部的记忆具有较高的相似性,而不同记忆簇之间的记忆相似性较低。同时,构建记忆的层次结构,高层次的记忆节点代表更抽象、概括的概念,低层次的记忆节点对应具体的实例。比如,“水果” 作为高层次记忆节点,连接着 “苹果”“橘子”“香蕉” 等低层次具体水果的记忆节点。 -3. **网络的动态更新**:随着新记忆的不断加入,记忆网络动态调整。新记忆节点根据其形态特征与现有网络中的节点建立连接,同时影响相关连接的强度。若新记忆与某个记忆簇的特征高度相似,则被纳入该记忆簇;若具有独特特征,则可能引发新的记忆簇的形成。例如,当系统学习到一种新的水果 “番石榴”,它会根据番石榴的形态、语义等特征,在记忆网络中找到与之最相似的区域(如水果记忆簇),并建立相应连接,同时调整周围节点连接强度以适应这一新记忆。 - - - -- **相似性联想**:该理论认为,当两个或多个事物在形态上具有相似性时,它们在记忆中会形成关联。例如,梨和苹果在形状和都是水果这一属性上有相似性,所以当我们看到梨时,很容易通过形态学联想记忆联想到苹果。这种相似性联想有助于我们对新事物进行分类和理解,当遇到一个新的类似水果时,我们可以通过与已有的水果记忆进行相似性匹配,来推测它的一些特征。 -- **时空关联性联想**:除了相似性联想,MAM 还强调时空关联性联想。如果两个事物在时间或空间上经常同时出现,它们也会在记忆中形成关联。比如,每次在公园里看到花的时候,都能听到鸟儿的叫声,那么花和鸟儿叫声的形态特征(花的视觉形态和鸟叫的听觉形态)就会在记忆中形成关联,以后听到鸟叫可能就会联想到公园里的花。 \ No newline at end of file diff --git a/docs/MONGO_DB_0.png b/docs/MONGO_DB_0.png deleted file mode 100644 index 8d91d37d8..000000000 Binary files a/docs/MONGO_DB_0.png and /dev/null differ diff --git a/docs/MONGO_DB_1.png b/docs/MONGO_DB_1.png deleted file mode 100644 index 0ef3b5590..000000000 Binary files a/docs/MONGO_DB_1.png and /dev/null differ diff --git a/docs/MONGO_DB_2.png b/docs/MONGO_DB_2.png deleted file mode 100644 index e59cc8793..000000000 Binary files a/docs/MONGO_DB_2.png and /dev/null differ diff --git a/docs/avatars/SengokuCola.jpg b/docs/avatars/SengokuCola.jpg deleted file mode 100644 index deebf5ed5..000000000 Binary files a/docs/avatars/SengokuCola.jpg and /dev/null differ diff --git a/docs/avatars/default.png b/docs/avatars/default.png deleted file mode 100644 index 5b561dac4..000000000 Binary files a/docs/avatars/default.png and /dev/null differ diff --git a/docs/avatars/run.bat b/docs/avatars/run.bat deleted file mode 100644 index 6b9ca9f2b..000000000 --- a/docs/avatars/run.bat +++ /dev/null @@ -1 +0,0 @@ -gource gource.log --user-image-dir docs/avatars/ --default-user-image docs/avatars/default.png \ No newline at end of file diff --git a/docs/doc1.md b/docs/doc1.md deleted file mode 100644 index e8aa0f0d6..000000000 --- a/docs/doc1.md +++ /dev/null @@ -1,175 +0,0 @@ -# 📂 文件及功能介绍 (2025年更新) - -## 根目录 - -- **README.md**: 项目的概述和使用说明。 -- **requirements.txt**: 项目所需的Python依赖包列表。 -- **bot.py**: 主启动文件,负责环境配置加载和NoneBot初始化。 -- **template.env**: 环境变量模板文件。 -- **pyproject.toml**: Python项目配置文件。 -- **docker-compose.yml** 和 **Dockerfile**: Docker配置文件,用于容器化部署。 -- **run_*.bat**: 各种启动脚本,包括数据库、maimai和thinking功能。 - -## `src/` 目录结构 - -- **`plugins/` 目录**: 存放不同功能模块的插件。 - - **chat/**: 处理聊天相关的功能,如消息发送和接收。 - - **memory_system/**: 处理机器人的记忆功能。 - - **knowledege/**: 知识库相关功能。 - - **models/**: 模型相关工具。 - - **schedule/**: 处理日程管理的功能。 - -- **`gui/` 目录**: 存放图形用户界面相关的代码。 - - **reasoning_gui.py**: 负责推理界面的实现,提供用户交互。 - -- **`common/` 目录**: 存放通用的工具和库。 - - **database.py**: 处理与数据库的交互,负责数据的存储和检索。 - - ****init**.py**: 初始化模块。 - -## `config/` 目录 - -- **bot_config_template.toml**: 机器人配置模板。 -- **auto_format.py**: 自动格式化工具。 - -### `src/plugins/chat/` 目录文件详细介绍 - -1. **`__init__.py`**: - - 初始化 `chat` 模块,使其可以作为一个包被导入。 - -2. **`bot.py`**: - - 主要的聊天机器人逻辑实现,处理消息的接收、思考和回复。 - - 包含 `ChatBot` 类,负责消息处理流程控制。 - - 集成记忆系统和意愿管理。 - -3. **`config.py`**: - - 配置文件,定义了聊天机器人的各种参数和设置。 - - 包含 `BotConfig` 和全局配置对象 `global_config`。 - -4. **`cq_code.py`**: - - 处理 CQ 码(CoolQ 码),用于发送和接收特定格式的消息。 - -5. **`emoji_manager.py`**: - - 管理表情包的发送和接收,根据情感选择合适的表情。 - - 提供根据情绪获取表情的方法。 - -6. **`llm_generator.py`**: - - 生成基于大语言模型的回复,处理用户输入并生成相应的文本。 - - 通过 `ResponseGenerator` 类实现回复生成。 - -7. **`message.py`**: - - 定义消息的结构和处理逻辑,包含多种消息类型: - - `Message`: 基础消息类 - - `MessageSet`: 消息集合 - - `Message_Sending`: 发送中的消息 - - `Message_Thinking`: 思考状态的消息 - -8. **`message_sender.py`**: - - 控制消息的发送逻辑,确保消息按照特定规则发送。 - - 包含 `message_manager` 对象,用于管理消息队列。 - -9. **`prompt_builder.py`**: - - 构建用于生成回复的提示,优化机器人的响应质量。 - -10. **`relationship_manager.py`**: - - 管理用户之间的关系,记录用户的互动和偏好。 - - 提供更新关系和关系值的方法。 - -11. **`Segment_builder.py`**: - - 构建消息片段的工具。 - -12. **`storage.py`**: - - 处理数据存储,负责将聊天记录和用户信息保存到数据库。 - - 实现 `MessageStorage` 类管理消息存储。 - -13. **`thinking_idea.py`**: - - 实现机器人的思考机制。 - -14. **`topic_identifier.py`**: - - 识别消息中的主题,帮助机器人理解用户的意图。 - -15. **`utils.py`** 和 **`utils_*.py`** 系列文件: - - 存放各种工具函数,提供辅助功能以支持其他模块。 - - 包括 `utils_cq.py`、`utils_image.py`、`utils_user.py` 等专门工具。 - -16. **`willing_manager.py`**: - - 管理机器人的回复意愿,动态调整回复概率。 - - 通过多种因素(如被提及、话题兴趣度)影响回复决策。 - -### `src/plugins/memory_system/` 目录文件介绍 - -1. **`memory.py`**: - - 实现记忆管理核心功能,包含 `memory_graph` 对象。 - - 提供相关项目检索,支持多层次记忆关联。 - -2. **`draw_memory.py`**: - - 记忆可视化工具。 - -3. **`memory_manual_build.py`**: - - 手动构建记忆的工具。 - -4. **`offline_llm.py`**: - - 离线大语言模型处理功能。 - -## 消息处理流程 - -### 1. 消息接收与预处理 - -- 通过 `ChatBot.handle_message()` 接收群消息。 -- 进行用户和群组的权限检查。 -- 更新用户关系信息。 -- 创建标准化的 `Message` 对象。 -- 对消息进行过滤和敏感词检测。 - -### 2. 主题识别与决策 - -- 使用 `topic_identifier` 识别消息主题。 -- 通过记忆系统检查对主题的兴趣度。 -- `willing_manager` 动态计算回复概率。 -- 根据概率决定是否回复消息。 - -### 3. 回复生成与发送 - -- 如需回复,首先创建 `Message_Thinking` 对象表示思考状态。 -- 调用 `ResponseGenerator.generate_response()` 生成回复内容和情感状态。 -- 删除思考消息,创建 `MessageSet` 准备发送回复。 -- 计算模拟打字时间,设置消息发送时间点。 -- 可能附加情感相关的表情包。 -- 通过 `message_manager` 将消息加入发送队列。 - -### 消息发送控制系统 - -`message_sender.py` 中实现了消息发送控制系统,采用三层结构: - -1. **消息管理**: - - 支持单条消息和消息集合的发送。 - - 处理思考状态消息,控制思考时间。 - - 模拟人类打字速度,添加自然发送延迟。 - -2. **情感表达**: - - 根据生成回复的情感状态选择匹配的表情包。 - - 通过 `emoji_manager` 管理表情资源。 - -3. **记忆交互**: - - 通过 `memory_graph` 检索相关记忆。 - - 根据记忆内容影响回复意愿和内容。 - -## 系统特色功能 - -1. **智能回复意愿系统**: - - 动态调整回复概率,模拟真实人类交流特性。 - - 考虑多种因素:被提及、话题兴趣度、用户关系等。 - -2. **记忆系统集成**: - - 支持多层次记忆关联和检索。 - - 影响机器人的兴趣和回复内容。 - -3. **自然交流模拟**: - - 模拟思考和打字过程,添加合理延迟。 - - 情感表达与表情包结合。 - -4. **多环境配置支持**: - - 支持开发环境和生产环境的不同配置。 - - 通过环境变量和配置文件灵活管理设置。 - -5. **Docker部署支持**: - - 提供容器化部署方案,简化安装和运行。 diff --git a/docs/docker_deploy.md b/docs/docker_deploy.md deleted file mode 100644 index f78f73dca..000000000 --- a/docs/docker_deploy.md +++ /dev/null @@ -1,93 +0,0 @@ -# 🐳 Docker 部署指南 - -## 部署步骤 (推荐,但不一定是最新) - -**"更新镜像与容器"部分在本文档 [Part 6](#6-更新镜像与容器)** - -### 0. 前提说明 - -**本文假设读者已具备一定的 Docker 基础知识。若您对 Docker 不熟悉,建议先参考相关教程或文档进行学习,或选择使用 [📦Linux手动部署指南](./manual_deploy_linux.md) 或 [📦Windows手动部署指南](./manual_deploy_windows.md) 。** - - -### 1. 获取Docker配置文件 - -- 建议先单独创建好一个文件夹并进入,作为工作目录 - -```bash -wget https://raw.githubusercontent.com/SengokuCola/MaiMBot/main/docker-compose.yml -O docker-compose.yml -``` - -- 若需要启用MongoDB数据库的用户名和密码,可进入docker-compose.yml,取消MongoDB处的注释并修改变量旁 `=` 后方的值为你的用户名和密码\ -修改后请注意在之后配置 `.env.prod` 文件时指定MongoDB数据库的用户名密码 - -### 2. 启动服务 - -- **!!! 请在第一次启动前确保当前工作目录下 `.env.prod` 与 `bot_config.toml` 文件存在 !!!**\ -由于Docker文件映射行为的特殊性,若宿主机的映射路径不存在,可能导致意外的目录创建,而不会创建文件,由于此处需要文件映射到文件,需提前确保文件存在且路径正确,可使用如下命令: - -```bash -touch .env.prod -touch bot_config.toml -``` - -- 启动Docker容器: - -```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 -``` - - -### 3. 修改配置并重启Docker - -- 请前往 [🎀 新手配置指南](docs/installation_cute.md) 或 [⚙️ 标准配置指南](docs/installation_standard.md) 完成`.env.prod`与`bot_config.toml`配置文件的编写\ -**需要注意`.env.prod`中HOST处IP的填写,Docker中部署和系统中直接安装的配置会有所不同** - -- 重启Docker容器: - -```bash -docker restart maimbot # 若修改过容器名称则替换maimbot为你自定的名称 -``` - -- 下方命令可以但不推荐,只是同时重启NapCat、MongoDB、MaiMBot三个服务 - -```bash -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 -``` - -### 4. 登入NapCat管理页添加反向WebSocket - -- 在浏览器地址栏输入 `http://<宿主机IP>:6099/` 进入NapCat的管理Web页,添加一个Websocket客户端 - -> 网络配置 -> 新建 -> Websocket客户端 - -- Websocket客户端的名称自定,URL栏填入 `ws://maimbot:8080/onebot/v11/ws`,启用并保存即可\ -(若修改过容器名称则替换maimbot为你自定的名称) - -### 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密钥请妥善保管,不要泄露 -- 建议先在测试环境中运行,确认无误后再部署到生产环境 diff --git a/docs/fast_q_a.md b/docs/fast_q_a.md deleted file mode 100644 index 3b995e24a..000000000 --- a/docs/fast_q_a.md +++ /dev/null @@ -1,149 +0,0 @@ -## 快速更新Q&A❓ - -
- -- 这个文件用来记录一些常见的新手问题。 - -
- -### 完整安装教程 - -
- -[MaiMbot简易配置教程](https://www.bilibili.com/video/BV1zsQ5YCEE6) - -
- -### Api相关问题 - -
- -
- -- 为什么显示:"缺失必要的API KEY" ❓ - -
- - - - - ---- - -
- ->
-> ->你需要在 [Silicon Flow Api](https://cloud.siliconflow.cn/account/ak) ->网站上注册一个账号,然后点击这个链接打开API KEY获取页面。 -> ->点击 "新建API密钥" 按钮新建一个给MaiMBot使用的API KEY。不要忘了点击复制。 -> ->之后打开MaiMBot在你电脑上的文件根目录,使用记事本或者其他文本编辑器打开 [.env.prod](../.env.prod) ->这个文件。把你刚才复制的API KEY填入到 "SILICONFLOW_KEY=" 这个等号的右边。 -> ->在默认情况下,MaiMBot使用的默认Api都是硅基流动的。 -> ->
- -
- -
- - -- 我想使用硅基流动之外的Api网站,我应该怎么做 ❓ - ---- - -
- ->
-> ->你需要使用记事本或者其他文本编辑器打开config目录下的 [bot_config.toml](../config/bot_config.toml) ->然后修改其中的 "provider = " 字段。同时不要忘记模仿 [.env.prod](../.env.prod) ->文件的写法添加 Api Key 和 Base URL。 -> ->举个例子,如果你写了 " provider = \"ABC\" ",那你需要相应的在 [.env.prod](../.env.prod) ->文件里添加形如 " ABC_BASE_URL = https://api.abc.com/v1 " 和 " ABC_KEY = sk-1145141919810 " 的字段。 -> ->**如果你对AI没有较深的了解,修改识图模型和嵌入模型的provider字段可能会产生bug,因为你从Api网站调用了一个并不存在的模型** -> ->这个时候,你需要把字段的值改回 "provider = \"SILICONFLOW\" " 以此解决bug。 -> ->
- - -
- -### MongoDB相关问题 - -
- -- 我应该怎么清空bot内存储的表情包 ❓ - ---- - -
- ->
-> ->打开你的MongoDB Compass软件,你会在左上角看到这样的一个界面: -> ->
-> -> -> ->
-> ->点击 "CONNECT" 之后,点击展开 MegBot 标签栏 -> ->
-> -> -> ->
-> ->点进 "emoji" 再点击 "DELETE" 删掉所有条目,如图所示 -> ->
-> -> -> ->
-> ->你可以用类似的方式手动清空MaiMBot的所有服务器数据。 -> ->MaiMBot的所有图片均储存在 [data](../data) 文件夹内,按类型分为 [emoji](../data/emoji) 和 [image](../data/image) -> ->在删除服务器数据时不要忘记清空这些图片。 -> ->
- -
- -- 为什么我连接不上MongoDB服务器 ❓ - ---- - - ->
-> ->这个问题比较复杂,但是你可以按照下面的步骤检查,看看具体是什么问题 -> ->
-> -> 1. 检查有没有把 mongod.exe 所在的目录添加到 path。 具体可参照 -> ->
-> ->  [CSDN-windows10设置环境变量Path详细步骤](https://blog.csdn.net/flame_007/article/details/106401215) -> ->
-> ->  **需要往path里填入的是 exe 所在的完整目录!不带 exe 本体** -> ->
-> -> 2. 待完成 -> ->
\ No newline at end of file diff --git a/docs/installation_cute.md b/docs/installation_cute.md deleted file mode 100644 index ca97f18e9..000000000 --- a/docs/installation_cute.md +++ /dev/null @@ -1,228 +0,0 @@ -# 🔧 配置指南 喵~ - -## 👋 你好呀 - -让咱来告诉你我们要做什么喵: - -1. 我们要一起设置一个可爱的AI机器人 -2. 这个机器人可以在QQ上陪你聊天玩耍哦 -3. 需要设置两个文件才能让机器人工作呢 - -## 📝 需要设置的文件喵 - -要设置这两个文件才能让机器人跑起来哦: - -1. `.env.prod` - 这个文件告诉机器人要用哪些AI服务呢 -2. `bot_config.toml` - 这个文件教机器人怎么和你聊天喵 - -## 🔑 密钥和域名的对应关系 - -想象一下,你要进入一个游乐园,需要: - -1. 知道游乐园的地址(这就是域名 base_url) -2. 有入场的门票(这就是密钥 key) - -在 `.env.prod` 文件里,我们定义了三个游乐园的地址和门票喵: - -```ini -# 硅基流动游乐园 -SILICONFLOW_KEY=your_key # 硅基流动的门票 -SILICONFLOW_BASE_URL=https://api.siliconflow.cn/v1/ # 硅基流动的地址 - -# DeepSeek游乐园 -DEEP_SEEK_KEY=your_key # DeepSeek的门票 -DEEP_SEEK_BASE_URL=https://api.deepseek.com/v1 # DeepSeek的地址 - -# ChatAnyWhere游乐园 -CHAT_ANY_WHERE_KEY=your_key # ChatAnyWhere的门票 -CHAT_ANY_WHERE_BASE_URL=https://api.chatanywhere.tech/v1 # ChatAnyWhere的地址 -``` - -然后在 `bot_config.toml` 里,机器人会用这些门票和地址去游乐园玩耍: - -```toml -[model.llm_reasoning] -name = "Pro/deepseek-ai/DeepSeek-R1" -provider = "SILICONFLOW" # 告诉机器人:去硅基流动游乐园玩,机器人会自动用硅基流动的门票进去 - -[model.llm_normal] -name = "Pro/deepseek-ai/DeepSeek-V3" -provider = "SILICONFLOW" # 还是去硅基流动游乐园 -``` - -### 🎪 举个例子喵 - -如果你想用DeepSeek官方的服务,就要这样改: - -```toml -[model.llm_reasoning] -name = "deepseek-reasoner" # 改成对应的模型名称,这里为DeepseekR1 -provider = "DEEP_SEEK" # 改成去DeepSeek游乐园 - -[model.llm_normal] -name = "deepseek-chat" # 改成对应的模型名称,这里为DeepseekV3 -provider = "DEEP_SEEK" # 也去DeepSeek游乐园 -``` - -### 🎯 简单来说 - -- `.env.prod` 文件就像是你的票夹,存放着各个游乐园的门票和地址 -- `bot_config.toml` 就是告诉机器人:用哪张票去哪个游乐园玩 -- 所有模型都可以用同一个游乐园的票,也可以去不同的游乐园玩耍 -- 如果用硅基流动的服务,就保持默认配置不用改呢~ - -记住:门票(key)要保管好,不能给别人看哦,不然别人就可以用你的票去玩了喵! - -## ---让我们开始吧--- - -### 第一个文件:环境配置 (.env.prod) - -这个文件就像是机器人的"身份证"呢,告诉它要用哪些AI服务喵~ - -```ini -# 这些是AI服务的密钥,就像是魔法钥匙一样呢 -# 要把 your_key 换成真正的密钥才行喵 -# 比如说:SILICONFLOW_KEY=sk-123456789abcdef -SILICONFLOW_KEY=your_key -SILICONFLOW_BASE_URL=https://api.siliconflow.cn/v1/ -DEEP_SEEK_KEY=your_key -DEEP_SEEK_BASE_URL=https://api.deepseek.com/v1 -CHAT_ANY_WHERE_KEY=your_key -CHAT_ANY_WHERE_BASE_URL=https://api.chatanywhere.tech/v1 - -# 如果你不知道这是什么,那么下面这些不用改,保持原样就好啦 -# 如果使用Docker部署,需要改成0.0.0.0喵,不然听不见群友讲话了喵 -HOST=127.0.0.1 -PORT=8080 - -# 这些是数据库设置,一般也不用改呢 -# 如果使用Docker部署,需要把MONGODB_HOST改成数据库容器的名字喵,默认是mongodb喵 -MONGODB_HOST=127.0.0.1 -MONGODB_PORT=27017 -DATABASE_NAME=MegBot -# 数据库认证信息,如果需要认证就取消注释并填写下面三行喵 -# MONGODB_USERNAME = "" -# MONGODB_PASSWORD = "" -# MONGODB_AUTH_SOURCE = "" - -# 也可以使用URI连接数据库,取消注释填写在下面这行喵(URI的优先级比上面的高) -# MONGODB_URI=mongodb://127.0.0.1:27017/MegBot - -# 这里是机器人的插件列表呢 -PLUGINS=["src2.plugins.chat"] -``` - -### 第二个文件:机器人配置 (bot_config.toml) - -这个文件就像是教机器人"如何说话"的魔法书呢! - -```toml -[bot] -qq = "把这里改成你的机器人QQ号喵" # 填写你的机器人QQ号 -nickname = "麦麦" # 机器人的名字,你可以改成你喜欢的任何名字哦,建议和机器人QQ名称/群昵称一样哦 -alias_names = ["小麦", "阿麦"] # 也可以用这个招呼机器人,可以不设置呢 - -[personality] -# 这里可以设置机器人的性格呢,让它更有趣一些喵 -prompt_personality = [ - "曾经是一个学习地质的女大学生,现在学习心理学和脑科学,你会刷贴吧", # 贴吧风格的性格 - "是一个女大学生,你有黑色头发,你会刷小红书" # 小红书风格的性格 -] -prompt_schedule = "一个曾经学习地质,现在学习心理学和脑科学的女大学生,喜欢刷qq,贴吧,知乎和小红书" # 用来提示机器人每天干什么的提示词喵 - -[message] -min_text_length = 2 # 机器人每次至少要说几个字呢 -max_context_size = 15 # 机器人能记住多少条消息喵 -emoji_chance = 0.2 # 机器人使用表情的概率哦(0.2就是20%的机会呢) -thinking_timeout = 120 # 机器人思考时间,时间越长能思考的时间越多,但是不要太长喵 - -response_willing_amplifier = 1 # 机器人回复意愿放大系数,增大会让他更愿意聊天喵 -response_interested_rate_amplifier = 1 # 机器人回复兴趣度放大系数,听到记忆里的内容时意愿的放大系数喵 -down_frequency_rate = 3.5 # 降低回复频率的群组回复意愿降低系数 -ban_words = ["脏话", "不文明用语"] # 在这里填写不让机器人说的词,要用英文逗号隔开,每个词都要用英文双引号括起来喵 - -[emoji] -auto_save = true # 是否自动保存看到的表情包呢 -enable_check = false # 是否要检查表情包是不是合适的喵 -check_prompt = "符合公序良俗" # 检查表情包的标准呢 - -[others] -enable_advance_output = true # 是否要显示更多的运行信息呢 -enable_kuuki_read = true # 让机器人能够"察言观色"喵 -enable_debug_output = false # 是否启用调试输出喵 -enable_friend_chat = false # 是否启用好友聊天喵 - -[groups] -talk_allowed = [123456, 789012] # 比如:让机器人在群123456和789012里说话 -talk_frequency_down = [345678] # 比如:在群345678里少说点话 -ban_user_id = [111222] # 比如:不回复QQ号为111222的人的消息 - -# 模型配置部分的详细说明喵~ - - -#下面的模型若使用硅基流动则不需要更改,使用ds官方则改成在.env.prod自己指定的密钥和域名,使用自定义模型则选择定位相似的模型自己填写 - -[model.llm_reasoning] #推理模型R1,用来理解和思考的喵 -name = "Pro/deepseek-ai/DeepSeek-R1" # 模型名字 -# name = "Qwen/QwQ-32B" # 如果想用千问模型,可以把上面那行注释掉,用这个呢 -provider = "SILICONFLOW" # 使用在.env.prod里设置的宏,也就是去掉"_BASE_URL"留下来的字喵 - -[model.llm_reasoning_minor] #R1蒸馏模型,是个轻量版的推理模型喵 -name = "deepseek-ai/DeepSeek-R1-Distill-Qwen-32B" -provider = "SILICONFLOW" - -[model.llm_normal] #V3模型,用来日常聊天的喵 -name = "Pro/deepseek-ai/DeepSeek-V3" -provider = "SILICONFLOW" - -[model.llm_normal_minor] #V2.5模型,是V3的前代版本呢 -name = "deepseek-ai/DeepSeek-V2.5" -provider = "SILICONFLOW" - -[model.vlm] #图像识别模型,让机器人能看懂图片喵 -name = "deepseek-ai/deepseek-vl2" -provider = "SILICONFLOW" - -[model.embedding] #嵌入模型,帮助机器人理解文本的相似度呢 -name = "BAAI/bge-m3" -provider = "SILICONFLOW" - -# 如果选择了llm方式提取主题,就用这个模型配置喵 -[topic.llm_topic] -name = "Pro/deepseek-ai/DeepSeek-V3" -provider = "SILICONFLOW" -``` - -## 💡 模型配置说明喵 - -1. **关于模型服务**: - - 如果你用硅基流动的服务,这些配置都不用改呢 - - 如果用DeepSeek官方API,要把provider改成你在.env.prod里设置的宏喵 - - 如果要用自定义模型,选择一个相似功能的模型配置来改呢 - -2. **主要模型功能**: - - `llm_reasoning`: 负责思考和推理的大脑喵 - - `llm_normal`: 负责日常聊天的嘴巴呢 - - `vlm`: 负责看图片的眼睛哦 - - `embedding`: 负责理解文字含义的理解力喵 - - `topic`: 负责理解对话主题的能力呢 - -## 🌟 小提示 - -- 如果你刚开始使用,建议保持默认配置呢 -- 不同的模型有不同的特长,可以根据需要调整它们的使用比例哦 - -## 🌟 小贴士喵 - -- 记得要好好保管密钥(key)哦,不要告诉别人呢 -- 配置文件要小心修改,改错了机器人可能就不能和你玩了喵 -- 如果想让机器人更聪明,可以调整 personality 里的设置呢 -- 不想让机器人说某些话,就把那些词放在 ban_words 里面喵 -- QQ群号和QQ号都要用数字填写,不要加引号哦(除了机器人自己的QQ号) - -## ⚠️ 注意事项 - -- 这个机器人还在测试中呢,可能会有一些小问题喵 -- 如果不知道怎么改某个设置,就保持原样不要动它哦~ -- 记得要先有AI服务的密钥,不然机器人就不能和你说话了呢 -- 修改完配置后要重启机器人才能生效喵~ diff --git a/docs/installation_standard.md b/docs/installation_standard.md deleted file mode 100644 index dcbbf0c99..000000000 --- a/docs/installation_standard.md +++ /dev/null @@ -1,167 +0,0 @@ -# 🔧 配置指南 - -## 简介 - -本项目需要配置两个主要文件: - -1. `.env.prod` - 配置API服务和系统环境 -2. `bot_config.toml` - 配置机器人行为和模型 - -## API配置说明 - -`.env.prod` 和 `bot_config.toml` 中的API配置关系如下: - -### 在.env.prod中定义API凭证 - -```ini -# API凭证配置 -SILICONFLOW_KEY=your_key # 硅基流动API密钥 -SILICONFLOW_BASE_URL=https://api.siliconflow.cn/v1/ # 硅基流动API地址 - -DEEP_SEEK_KEY=your_key # DeepSeek API密钥 -DEEP_SEEK_BASE_URL=https://api.deepseek.com/v1 # DeepSeek API地址 - -CHAT_ANY_WHERE_KEY=your_key # ChatAnyWhere API密钥 -CHAT_ANY_WHERE_BASE_URL=https://api.chatanywhere.tech/v1 # ChatAnyWhere API地址 -``` - -### 在bot_config.toml中引用API凭证 - -```toml -[model.llm_reasoning] -name = "Pro/deepseek-ai/DeepSeek-R1" -provider = "SILICONFLOW" # 引用.env.prod中定义的宏 -``` - -如需切换到其他API服务,只需修改引用: - -```toml -[model.llm_reasoning] -name = "deepseek-reasoner" # 改成对应的模型名称,这里为DeepseekR1 -provider = "DEEP_SEEK" # 使用DeepSeek密钥 -``` - -## 配置文件详解 - -### 环境配置文件 (.env.prod) - -```ini -# API配置 -SILICONFLOW_KEY=your_key -SILICONFLOW_BASE_URL=https://api.siliconflow.cn/v1/ -DEEP_SEEK_KEY=your_key -DEEP_SEEK_BASE_URL=https://api.deepseek.com/v1 -CHAT_ANY_WHERE_KEY=your_key -CHAT_ANY_WHERE_BASE_URL=https://api.chatanywhere.tech/v1 - -# 服务配置 - -HOST=127.0.0.1 # 如果使用Docker部署,需要改成0.0.0.0,否则QQ消息无法传入 -PORT=8080 # 与反向端口相同 - -# 数据库配置 -MONGODB_HOST=127.0.0.1 # 如果使用Docker部署,需要改成数据库容器的名字,默认是mongodb -MONGODB_PORT=27017 # MongoDB端口 - -DATABASE_NAME=MegBot -# 数据库认证信息,如果需要认证就取消注释并填写下面三行 -# MONGODB_USERNAME = "" -# MONGODB_PASSWORD = "" -# MONGODB_AUTH_SOURCE = "" - -# 也可以使用URI连接数据库,取消注释填写在下面这行(URI的优先级比上面的高) -# MONGODB_URI=mongodb://127.0.0.1:27017/MegBot - -# 插件配置 -PLUGINS=["src2.plugins.chat"] -``` - -### 机器人配置文件 (bot_config.toml) - -```toml -[bot] -qq = "机器人QQ号" # 机器人的QQ号,必填 -nickname = "麦麦" # 机器人昵称 -# alias_names: 配置机器人可使用的别名。当机器人在群聊或对话中被调用时,别名可以作为直接命令或提及机器人的关键字使用。 -# 该配置项为字符串数组。例如: ["小麦", "阿麦"] -alias_names = ["小麦", "阿麦"] # 机器人别名 - -[personality] -prompt_personality = [ - "曾经是一个学习地质的女大学生,现在学习心理学和脑科学,你会刷贴吧", - "是一个女大学生,你有黑色头发,你会刷小红书" -] # 人格提示词 -prompt_schedule = "一个曾经学习地质,现在学习心理学和脑科学的女大学生,喜欢刷qq,贴吧,知乎和小红书" # 日程生成提示词 - -[message] -min_text_length = 2 # 最小回复长度 -max_context_size = 15 # 上下文记忆条数 -emoji_chance = 0.2 # 表情使用概率 -thinking_timeout = 120 # 机器人思考时间,时间越长能思考的时间越多,但是不要太长 - -response_willing_amplifier = 1 # 机器人回复意愿放大系数,增大会更愿意聊天 -response_interested_rate_amplifier = 1 # 机器人回复兴趣度放大系数,听到记忆里的内容时意愿的放大系数 -down_frequency_rate = 3.5 # 降低回复频率的群组回复意愿降低系数 -ban_words = [] # 禁用词列表 - -[emoji] -auto_save = true # 自动保存表情 -enable_check = false # 启用表情审核 -check_prompt = "符合公序良俗" - -[groups] -talk_allowed = [] # 允许对话的群号 -talk_frequency_down = [] # 降低回复频率的群号 -ban_user_id = [] # 禁止回复的用户QQ号 - -[others] -enable_advance_output = true # 是否启用高级输出 -enable_kuuki_read = true # 是否启用读空气功能 -enable_debug_output = false # 是否启用调试输出 -enable_friend_chat = false # 是否启用好友聊天 - -# 模型配置 -[model.llm_reasoning] # 推理模型 -name = "Pro/deepseek-ai/DeepSeek-R1" -provider = "SILICONFLOW" - -[model.llm_reasoning_minor] # 轻量推理模型 -name = "deepseek-ai/DeepSeek-R1-Distill-Qwen-32B" -provider = "SILICONFLOW" - -[model.llm_normal] # 对话模型 -name = "Pro/deepseek-ai/DeepSeek-V3" -provider = "SILICONFLOW" - -[model.llm_normal_minor] # 备用对话模型 -name = "deepseek-ai/DeepSeek-V2.5" -provider = "SILICONFLOW" - -[model.vlm] # 图像识别模型 -name = "deepseek-ai/deepseek-vl2" -provider = "SILICONFLOW" - -[model.embedding] # 文本向量模型 -name = "BAAI/bge-m3" -provider = "SILICONFLOW" - - -[topic.llm_topic] -name = "Pro/deepseek-ai/DeepSeek-V3" -provider = "SILICONFLOW" -``` - -## 注意事项 - -1. API密钥安全: - - 妥善保管API密钥 - - 不要将含有密钥的配置文件上传至公开仓库 - -2. 配置修改: - - 修改配置后需重启服务 - - 使用默认服务(硅基流动)时无需修改模型配置 - - QQ号和群号使用数字格式(机器人QQ号除外) - -3. 其他说明: - - 项目处于测试阶段,可能存在未知问题 - - 建议初次使用保持默认配置 diff --git a/docs/linux_deploy_guide_for_beginners.md b/docs/linux_deploy_guide_for_beginners.md deleted file mode 100644 index 04601923f..000000000 --- a/docs/linux_deploy_guide_for_beginners.md +++ /dev/null @@ -1,444 +0,0 @@ -# 面向纯新手的Linux服务器麦麦部署指南 - -## 你得先有一个服务器 - -为了能使麦麦在你的电脑关机之后还能运行,你需要一台不间断开机的主机,也就是我们常说的服务器。 - -华为云、阿里云、腾讯云等等都是在国内可以选择的选择。 - -你可以去租一台最低配置的就足敷需要了,按月租大概十几块钱就能租到了。 - -我们假设你已经租好了一台Linux架构的云服务器。我用的是阿里云ubuntu24.04,其他的原理相似。 - -## 0.我们就从零开始吧 - -### 网络问题 - -为访问github相关界面,推荐去下一款加速器,新手可以试试watttoolkit。 - -### 安装包下载 - -#### MongoDB - -对于ubuntu24.04 x86来说是这个: - -https://repo.mongodb.org/apt/ubuntu/dists/noble/mongodb-org/8.0/multiverse/binary-amd64/mongodb-org-server_8.0.5_amd64.deb - -如果不是就在这里自行选择对应版本 - -https://www.mongodb.com/try/download/community-kubernetes-operator - -#### Napcat - -在这里选择对应版本。 - -https://github.com/NapNeko/NapCatQQ/releases/tag/v4.6.7 - -对于ubuntu24.04 x86来说是这个: - -https://dldir1.qq.com/qqfile/qq/QQNT/ee4bd910/linuxqq_3.2.16-32793_amd64.deb - -#### 麦麦 - -https://github.com/SengokuCola/MaiMBot/archive/refs/tags/0.5.8-alpha.zip - -下载这个官方压缩包。 - -### 路径 - -我把麦麦相关文件放在了/moi/mai里面,你可以凭喜好更改,记得适当调整下面涉及到的部分即可。 - -文件结构: - -``` -moi -└─ mai - ├─ linuxqq_3.2.16-32793_amd64.deb - ├─ mongodb-org-server_8.0.5_amd64.deb - └─ bot - └─ MaiMBot-0.5.8-alpha.zip -``` - -### 网络 - -你可以在你的服务器控制台网页更改防火墙规则,允许6099,8080,27017这几个端口的出入。 - -## 1.正式开始! - -远程连接你的服务器,你会看到一个黑框框闪着白方格,这就是我们要进行设置的场所——终端了。以下的bash命令都是在这里输入。 - -## 2. Python的安装 - -- 导入 Python 的稳定版 PPA: - -```bash -sudo add-apt-repository ppa:deadsnakes/ppa -``` - -- 导入 PPA 后,更新 APT 缓存: - -```bash -sudo apt update -``` - -- 在「终端」中执行以下命令来安装 Python 3.12: - -```bash -sudo apt install python3.12 -``` - -- 验证安装是否成功: - -```bash -python3.12 --version -``` - -- 在「终端」中,执行以下命令安装 pip: - -```bash -sudo apt install python3-pip -``` - -- 检查Pip是否安装成功: - -```bash -pip --version -``` - -- 安装必要组件 - -``` bash -sudo apt install python-is-python3 -``` - -## 3.MongoDB的安装 - -``` bash -cd /moi/mai -``` - -``` bash -dpkg -i mongodb-org-server_8.0.5_amd64.deb -``` - -``` bash -mkdir -p /root/data/mongodb/{data,log} -``` - -## 4.MongoDB的运行 - -```bash -service mongod start -``` - -```bash -systemctl status mongod #通过这条指令检查运行状态 -``` - -有需要的话可以把这个服务注册成开机自启 - -```bash -sudo systemctl enable mongod -``` - -## 5.napcat的安装 - -``` bash -curl -o napcat.sh https://nclatest.znin.net/NapNeko/NapCat-Installer/main/script/install.sh && sudo bash napcat.sh -``` - -上面的不行试试下面的 - -``` bash -dpkg -i linuxqq_3.2.16-32793_amd64.deb -apt-get install -f -dpkg -i linuxqq_3.2.16-32793_amd64.deb -``` - -成功的标志是输入``` napcat ```出来炫酷的彩虹色界面 - -## 6.napcat的运行 - -此时你就可以根据提示在```napcat```里面登录你的QQ号了。 - -```bash -napcat start <你的QQ号> -napcat status #检查运行状态 -``` - -然后你就可以登录napcat的webui进行设置了: - -```http://<你服务器的公网IP>:6099/webui?token=napcat``` - -第一次是这个,后续改了密码之后token就会对应修改。你也可以使用```napcat log <你的QQ号>```来查看webui地址。把里面的```127.0.0.1```改成<你服务器的公网IP>即可。 - -登录上之后在网络配置界面添加websocket客户端,名称随便输一个,url改成`ws://127.0.0.1:8080/onebot/v11/ws`保存之后点启用,就大功告成了。 - -## 7.麦麦的安装 - -### step 1 安装解压软件 - -``` -sudo apt-get install unzip -``` - -### step 2 解压文件 - -```bash -cd /moi/mai/bot # 注意:要切换到压缩包的目录中去 -unzip MaiMBot-0.5.8-alpha.zip -``` - -### step 3 进入虚拟环境安装库 - -```bash -cd /moi/mai/bot -python -m venv venv -source venv/bin/activate -pip install -r requirements.txt -``` - -### step 4 试运行 - -```bash -cd /moi/mai/bot -python -m venv venv -source venv/bin/activate -python bot.py -``` - -肯定运行不成功,不过你会发现结束之后多了一些文件 - -``` -bot -├─ .env.prod -└─ config - └─ bot_config.toml -``` - -你要会vim直接在终端里修改也行,不过也可以把它们下到本地改好再传上去: - -### step 5 文件配置 - -本项目需要配置两个主要文件: - -1. `.env.prod` - 配置API服务和系统环境 -2. `bot_config.toml` - 配置机器人行为和模型 - -#### API - -你可以注册一个硅基流动的账号,通过邀请码注册有14块钱的免费额度:https://cloud.siliconflow.cn/i/7Yld7cfg。 - -#### 在.env.prod中定义API凭证: - -``` -# API凭证配置 -SILICONFLOW_KEY=your_key # 硅基流动API密钥 -SILICONFLOW_BASE_URL=https://api.siliconflow.cn/v1/ # 硅基流动API地址 - -DEEP_SEEK_KEY=your_key # DeepSeek API密钥 -DEEP_SEEK_BASE_URL=https://api.deepseek.com/v1 # DeepSeek API地址 - -CHAT_ANY_WHERE_KEY=your_key # ChatAnyWhere API密钥 -CHAT_ANY_WHERE_BASE_URL=https://api.chatanywhere.tech/v1 # ChatAnyWhere API地址 -``` - -#### 在bot_config.toml中引用API凭证: - -``` -[model.llm_reasoning] -name = "Pro/deepseek-ai/DeepSeek-R1" -base_url = "SILICONFLOW_BASE_URL" # 引用.env.prod中定义的地址 -key = "SILICONFLOW_KEY" # 引用.env.prod中定义的密钥 -``` - -如需切换到其他API服务,只需修改引用: - -``` -[model.llm_reasoning] -name = "Pro/deepseek-ai/DeepSeek-R1" -base_url = "DEEP_SEEK_BASE_URL" # 切换为DeepSeek服务 -key = "DEEP_SEEK_KEY" # 使用DeepSeek密钥 -``` - -#### 配置文件详解 - -##### 环境配置文件 (.env.prod) - -``` -# API配置 -SILICONFLOW_KEY=your_key -SILICONFLOW_BASE_URL=https://api.siliconflow.cn/v1/ -DEEP_SEEK_KEY=your_key -DEEP_SEEK_BASE_URL=https://api.deepseek.com/v1 -CHAT_ANY_WHERE_KEY=your_key -CHAT_ANY_WHERE_BASE_URL=https://api.chatanywhere.tech/v1 - -# 服务配置 -HOST=127.0.0.1 # 如果使用Docker部署,需要改成0.0.0.0,否则QQ消息无法传入 -PORT=8080 - -# 数据库配置 -MONGODB_HOST=127.0.0.1 # 如果使用Docker部署,需要改成数据库容器的名字,默认是mongodb -MONGODB_PORT=27017 -DATABASE_NAME=MegBot -MONGODB_USERNAME = "" # 数据库用户名 -MONGODB_PASSWORD = "" # 数据库密码 -MONGODB_AUTH_SOURCE = "" # 认证数据库 - -# 插件配置 -PLUGINS=["src2.plugins.chat"] -``` - -##### 机器人配置文件 (bot_config.toml) - -``` -[bot] -qq = "机器人QQ号" # 必填 -nickname = "麦麦" # 机器人昵称(你希望机器人怎么称呼它自己) - -[personality] -prompt_personality = [ - "曾经是一个学习地质的女大学生,现在学习心理学和脑科学,你会刷贴吧", - "是一个女大学生,你有黑色头发,你会刷小红书" -] -prompt_schedule = "一个曾经学习地质,现在学习心理学和脑科学的女大学生,喜欢刷qq,贴吧,知乎和小红书" - -[message] -min_text_length = 2 # 最小回复长度 -max_context_size = 15 # 上下文记忆条数 -emoji_chance = 0.2 # 表情使用概率 -ban_words = [] # 禁用词列表 - -[emoji] -auto_save = true # 自动保存表情 -enable_check = false # 启用表情审核 -check_prompt = "符合公序良俗" - -[groups] -talk_allowed = [] # 允许对话的群号 -talk_frequency_down = [] # 降低回复频率的群号 -ban_user_id = [] # 禁止回复的用户QQ号 - -[others] -enable_advance_output = true # 启用详细日志 -enable_kuuki_read = true # 启用场景理解 - -# 模型配置 -[model.llm_reasoning] # 推理模型 -name = "Pro/deepseek-ai/DeepSeek-R1" -base_url = "SILICONFLOW_BASE_URL" -key = "SILICONFLOW_KEY" - -[model.llm_reasoning_minor] # 轻量推理模型 -name = "deepseek-ai/DeepSeek-R1-Distill-Qwen-32B" -base_url = "SILICONFLOW_BASE_URL" -key = "SILICONFLOW_KEY" - -[model.llm_normal] # 对话模型 -name = "Pro/deepseek-ai/DeepSeek-V3" -base_url = "SILICONFLOW_BASE_URL" -key = "SILICONFLOW_KEY" - -[model.llm_normal_minor] # 备用对话模型 -name = "deepseek-ai/DeepSeek-V2.5" -base_url = "SILICONFLOW_BASE_URL" -key = "SILICONFLOW_KEY" - -[model.vlm] # 图像识别模型 -name = "deepseek-ai/deepseek-vl2" -base_url = "SILICONFLOW_BASE_URL" -key = "SILICONFLOW_KEY" - -[model.embedding] # 文本向量模型 -name = "BAAI/bge-m3" -base_url = "SILICONFLOW_BASE_URL" -key = "SILICONFLOW_KEY" - - -[topic.llm_topic] -name = "Pro/deepseek-ai/DeepSeek-V3" -base_url = "SILICONFLOW_BASE_URL" -key = "SILICONFLOW_KEY" -``` - -**step # 6** 运行 - -现在再运行 - -```bash -cd /moi/mai/bot -python -m venv venv -source venv/bin/activate -python bot.py -``` - -应该就能运行成功了。 - -## 8.事后配置 - -可是现在还有个问题:只要你一关闭终端,bot.py就会停止运行。那该怎么办呢?我们可以把bot.py注册成服务。 - -重启服务器,打开MongoDB和napcat服务。 - -新建一个文件,名为`bot.service`,内容如下 - -``` -[Unit] -Description=maimai bot - -[Service] -WorkingDirectory=/moi/mai/bot -ExecStart=/moi/mai/bot/venv/bin/python /moi/mai/bot/bot.py -Restart=on-failure -User=root - -[Install] -WantedBy=multi-user.target -``` - -里面的路径视自己的情况更改。 - -把它放到`/etc/systemd/system`里面。 - -重新加载 `systemd` 配置: - -```bash -sudo systemctl daemon-reload -``` - -启动服务: - -```bash -sudo systemctl start bot.service # 启动服务 -sudo systemctl restart bot.service # 或者重启服务 -``` - -检查服务状态: - -```bash -sudo systemctl status bot.service -``` - -现在再关闭终端,检查麦麦能不能正常回复QQ信息。如果可以的话就大功告成了! - -## 9.命令速查 - -```bash -service mongod start # 启动mongod服务 -napcat start <你的QQ号> # 登录napcat -cd /moi/mai/bot # 切换路径 -python -m venv venv # 创建虚拟环境 -source venv/bin/activate # 激活虚拟环境 - -sudo systemctl daemon-reload # 重新加载systemd配置 -sudo systemctl start bot.service # 启动bot服务 -sudo systemctl enable bot.service # 启动bot服务 - -sudo systemctl status bot.service # 检查bot服务状态 -``` - -``` -python bot.py -``` - diff --git a/docs/manual_deploy_linux.md b/docs/manual_deploy_linux.md deleted file mode 100644 index a5c91d6e2..000000000 --- a/docs/manual_deploy_linux.md +++ /dev/null @@ -1,180 +0,0 @@ -# 📦 Linux系统如何手动部署MaiMbot麦麦? - -## 准备工作 - -- 一台联网的Linux设备(本教程以Ubuntu/Debian系为例) -- QQ小号(QQ框架的使用可能导致qq被风控,严重(小概率)可能会导致账号封禁,强烈不推荐使用大号) -- 可用的大模型API -- 一个AI助手,网上随便搜一家打开来用都行,可以帮你解决一些不懂的问题 -- 以下内容假设你对Linux系统有一定的了解,如果觉得难以理解,请直接用Windows系统部署[Windows系统部署指南](./manual_deploy_windows.md) - -## 你需要知道什么? - -- 如何正确向AI助手提问,来学习新知识 - -- Python是什么 - -- Python的虚拟环境是什么?如何创建虚拟环境 - -- 命令行是什么 - -- 数据库是什么?如何安装并启动MongoDB - -- 如何运行一个QQ机器人,以及NapCat框架是什么 - ---- - -## 环境配置 - -### 1️⃣ **确认Python版本** - -需确保Python版本为3.9及以上 - -```bash -python --version -# 或 -python3 --version -``` - -如果版本低于3.9,请更新Python版本。 - -```bash -# Ubuntu/Debian -sudo apt update -sudo apt install python3.9 -# 如执行了这一步,建议在执行时将python3指向python3.9 -# 更新替代方案,设置 python3.9 为默认的 python3 版本: -sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.9 1 -sudo update-alternatives --config python3 -``` - -### 2️⃣ **创建虚拟环境** - -```bash -# 方法1:使用venv(推荐) -python3 -m venv maimbot -source maimbot/bin/activate # 激活环境 - -# 方法2:使用conda(需先安装Miniconda) -wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh -bash Miniconda3-latest-Linux-x86_64.sh -conda create -n maimbot python=3.9 -conda activate maimbot - -# 通过以上方法创建并进入虚拟环境后,再执行以下命令 - -# 安装依赖(任选一种环境) -pip install -r requirements.txt -``` - ---- - -## 数据库配置 - -### 3️⃣ **安装并启动MongoDB** - -- 安装与启动:Debian参考[官方文档](https://docs.mongodb.com/manual/tutorial/install-mongodb-on-debian/),Ubuntu参考[官方文档](https://docs.mongodb.com/manual/tutorial/install-mongodb-on-ubuntu/) -- 默认连接本地27017端口 - ---- - -## NapCat配置 - -### 4️⃣ **安装NapCat框架** - -- 参考[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地址: `ws://127.0.0.1:8080/onebot/v11/ws` - ---- - -## 配置文件设置 - -### 5️⃣ **配置文件设置,让麦麦Bot正常工作** - -- 修改环境配置文件:`.env.prod` -- 修改机器人配置文件:`bot_config.toml` - ---- - -## 启动机器人 - -### 6️⃣ **启动麦麦机器人** - -```bash -# 在项目目录下操作 -nb run -# 或 -python3 bot.py -``` - ---- - -### 7️⃣ **使用systemctl管理maimbot** - -使用以下命令添加服务文件: - -```bash -sudo nano /etc/systemd/system/maimbot.service -``` - -输入以下内容: - -``:你的maimbot目录 - -``:你的venv环境(就是上文创建环境后,执行的代码`source maimbot/bin/activate`中source后面的路径的绝对路径) - -```ini -[Unit] -Description=MaiMbot 麦麦 -After=network.target mongod.service - -[Service] -Type=simple -WorkingDirectory= -ExecStart=/python3 bot.py -ExecStop=/bin/kill -2 $MAINPID -Restart=always -RestartSec=10s - -[Install] -WantedBy=multi-user.target -``` - -输入以下命令重新加载systemd: - -```bash -sudo systemctl daemon-reload -``` - -启动并设置开机自启: - -```bash -sudo systemctl start maimbot -sudo systemctl enable maimbot -``` - -输入以下命令查看日志: - -```bash -sudo journalctl -xeu maimbot -``` - ---- - -## **其他组件(可选)** - -- 直接运行 knowledge.py生成知识库 - ---- - -## 常见问题 - -🔧 权限问题:在命令前加`sudo` -🔌 端口占用:使用`sudo lsof -i :8080`查看端口占用 -🛡️ 防火墙:确保8080/27017端口开放 - -```bash -sudo ufw allow 8080/tcp -sudo ufw allow 27017/tcp -``` diff --git a/docs/manual_deploy_windows.md b/docs/manual_deploy_windows.md deleted file mode 100644 index 37f0a5e31..000000000 --- a/docs/manual_deploy_windows.md +++ /dev/null @@ -1,110 +0,0 @@ -# 📦 Windows系统如何手动部署MaiMbot麦麦? - -## 你需要什么? - -- 一台电脑,能够上网的那种 - -- 一个QQ小号(QQ框架的使用可能导致qq被风控,严重(小概率)可能会导致账号封禁,强烈不推荐使用大号) - -- 可用的大模型API - -- 一个AI助手,网上随便搜一家打开来用都行,可以帮你解决一些不懂的问题 - -## 你需要知道什么? - -- 如何正确向AI助手提问,来学习新知识 - -- Python是什么 - -- Python的虚拟环境是什么?如何创建虚拟环境 - -- 命令行是什么 - -- 数据库是什么?如何安装并启动MongoDB - -- 如何运行一个QQ机器人,以及NapCat框架是什么 - -## 如果准备好了,就可以开始部署了 - -### 1️⃣ **首先,我们需要安装正确版本的Python** - -在创建虚拟环境之前,请确保你的电脑上安装了Python 3.9及以上版本。如果没有,可以按以下步骤安装: - -1. 访问Python官网下载页面: -2. 下载Windows安装程序 (64-bit): `python-3.9.13-amd64.exe` -3. 运行安装程序,并确保勾选"Add Python 3.9 to PATH"选项 -4. 点击"Install Now"开始安装 - -或者使用PowerShell自动下载安装(需要管理员权限): - -```powershell -# 下载并安装Python 3.9.13 -$pythonUrl = "https://www.python.org/ftp/python/3.9.13/python-3.9.13-amd64.exe" -$pythonInstaller = "$env:TEMP\python-3.9.13-amd64.exe" -Invoke-WebRequest -Uri $pythonUrl -OutFile $pythonInstaller -Start-Process -Wait -FilePath $pythonInstaller -ArgumentList "/quiet", "InstallAllUsers=0", "PrependPath=1" -Verb RunAs -``` - -### 2️⃣ **创建Python虚拟环境来运行程序** - -> 你可以选择使用以下两种方法之一来创建Python环境: - -```bash -# ---方法1:使用venv(Python自带) -# 在命令行中创建虚拟环境(环境名为maimbot) -# 这会让你在运行命令的目录下创建一个虚拟环境 -# 请确保你已通过cd命令前往到了对应路径,不然之后你可能找不到你的python环境 -python -m venv maimbot - -maimbot\\Scripts\\activate - -# 安装依赖 -pip install -r requirements.txt -``` - -```bash -# ---方法2:使用conda -# 创建一个新的conda环境(环境名为maimbot) -# Python版本为3.9 -conda create -n maimbot python=3.9 - -# 激活环境 -conda activate maimbot - -# 安装依赖 -pip install -r requirements.txt -``` - -### 2️⃣ **然后你需要启动MongoDB数据库,来存储信息** - -- 安装并启动MongoDB服务 -- 默认连接本地27017端口 - -### 3️⃣ **配置NapCat,让麦麦bot与qq取得联系** - -- 安装并登录NapCat(用你的qq小号) -- 添加反向WS: `ws://127.0.0.1:8080/onebot/v11/ws` - -### 4️⃣ **配置文件设置,让麦麦Bot正常工作** - -- 修改环境配置文件:`.env.prod` -- 修改机器人配置文件:`bot_config.toml` - -### 5️⃣ **启动麦麦机器人** - -- 打开命令行,cd到对应路径 - -```bash -nb run -``` - -- 或者cd到对应路径后 - -```bash -python bot.py -``` - -### 6️⃣ **其他组件(可选)** - -- `run_thingking.bat`: 启动可视化推理界面(未完善) -- 直接运行 knowledge.py生成知识库 diff --git a/docs/synology_.env.prod.png b/docs/synology_.env.prod.png deleted file mode 100644 index 0bdcacdf3..000000000 Binary files a/docs/synology_.env.prod.png and /dev/null differ diff --git a/docs/synology_create_project.png b/docs/synology_create_project.png deleted file mode 100644 index f716d4605..000000000 Binary files a/docs/synology_create_project.png and /dev/null differ diff --git a/docs/synology_deploy.md b/docs/synology_deploy.md deleted file mode 100644 index a7b3bebda..000000000 --- a/docs/synology_deploy.md +++ /dev/null @@ -1,68 +0,0 @@ -# 群晖 NAS 部署指南 - -**笔者使用的是 DSM 7.2.2,其他 DSM 版本的操作可能不完全一样** -**需要使用 Container Manager,群晖的部分部分入门级 NAS 可能不支持** - -## 部署步骤 - -### 创建配置文件目录 - -打开 `DSM ➡️ 控制面板 ➡️ 共享文件夹`,点击 `新增` ,创建一个共享文件夹 -只需要设置名称,其他设置均保持默认即可。如果你已经有 docker 专用的共享文件夹了,就跳过这一步 - -打开 `DSM ➡️ FileStation`, 在共享文件夹中创建一个 `MaiMBot` 文件夹 - -### 准备配置文件 - -docker-compose.yml: https://github.com/SengokuCola/MaiMBot/blob/main/docker-compose.yml -下载后打开,将 `services-mongodb-image` 修改为 `mongo:4.4.24`。这是因为最新的 MongoDB 强制要求 AVX 指令集,而群晖似乎不支持这个指令集 -![](https://raw.githubusercontent.com/ProperSAMA/MaiMBot/refs/heads/debug/docs/synology_docker-compose.png) - -bot_config.toml: https://github.com/SengokuCola/MaiMBot/blob/main/template/bot_config_template.toml -下载后,重命名为 `bot_config.toml` -打开它,按自己的需求填写配置文件 - -.env.prod: https://github.com/SengokuCola/MaiMBot/blob/main/template.env -下载后,重命名为 `.env.prod` -将 `HOST` 修改为 `0.0.0.0`,确保 maimbot 能被 napcat 访问 -按下图修改 mongodb 设置,使用 `MONGODB_URI` -![](https://raw.githubusercontent.com/ProperSAMA/MaiMBot/refs/heads/debug/docs/synology_.env.prod.png) - -把 `bot_config.toml` 和 `.env.prod` 放入之前创建的 `MaiMBot`文件夹 - -#### 如何下载? - -点这里!![](https://raw.githubusercontent.com/ProperSAMA/MaiMBot/refs/heads/debug/docs/synology_how_to_download.png) - -### 创建项目 - -打开 `DSM ➡️ ContainerManager ➡️ 项目`,点击 `新增` 创建项目,填写以下内容: - -- 项目名称: `maimbot` -- 路径:之前创建的 `MaiMBot` 文件夹 -- 来源: `上传 docker-compose.yml` -- 文件:之前下载的 `docker-compose.yml` 文件 - -图例: - -![](https://raw.githubusercontent.com/ProperSAMA/MaiMBot/refs/heads/debug/docs/synology_create_project.png) - -一路点下一步,等待项目创建完成 - -### 设置 Napcat - -1. 登陆 napcat - 打开 napcat: `http://<你的nas地址>:6099` ,输入token登陆 - token可以打开 `DSM ➡️ ContainerManager ➡️ 项目 ➡️ MaiMBot ➡️ 容器 ➡️ Napcat ➡️ 日志`,找到类似 `[WebUi] WebUi Local Panel Url: http://127.0.0.1:6099/webui?token=xxxx` 的日志 - 这个 `token=` 后面的就是你的 napcat token - -2. 按提示,登陆你给麦麦准备的QQ小号 - -3. 设置 websocket 客户端 - `网络配置 -> 新建 -> Websocket客户端`,名称自定,URL栏填入 `ws://maimbot:8080/onebot/v11/ws`,启用并保存即可。 - 若修改过容器名称,则替换 `maimbot` 为你自定的名称 - -### 部署完成 - -找个群,发送 `麦麦,你在吗` 之类的 -如果一切正常,应该能正常回复了 \ No newline at end of file diff --git a/docs/synology_docker-compose.png b/docs/synology_docker-compose.png deleted file mode 100644 index f70003e29..000000000 Binary files a/docs/synology_docker-compose.png and /dev/null differ diff --git a/docs/synology_how_to_download.png b/docs/synology_how_to_download.png deleted file mode 100644 index 011f98876..000000000 Binary files a/docs/synology_how_to_download.png and /dev/null differ diff --git a/docs/video.png b/docs/video.png deleted file mode 100644 index 95754a0c0..000000000 Binary files a/docs/video.png and /dev/null differ diff --git a/pyproject.toml b/pyproject.toml index 0a4805744..ccc5c566b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,10 +3,6 @@ name = "MaiMaiBot" version = "0.1.0" description = "MaiMaiBot" -[tool.nonebot] -plugins = ["src.plugins.chat"] -plugin_dirs = ["src/plugins"] - [tool.ruff] include = ["*.py"] @@ -28,7 +24,7 @@ select = [ "B", # flake8-bugbear ] -ignore = ["E711"] +ignore = ["E711","E501"] [tool.ruff.format] docstring-code-format = true diff --git a/requirements.txt b/requirements.txt index 8330c8d06..cea511f10 100644 Binary files a/requirements.txt and b/requirements.txt differ diff --git a/run.bat b/run.bat deleted file mode 100644 index 91904bc34..000000000 --- a/run.bat +++ /dev/null @@ -1,10 +0,0 @@ -@ECHO OFF -chcp 65001 -if not exist "venv" ( - python -m venv venv - call venv\Scripts\activate.bat - pip install -i https://mirrors.aliyun.com/pypi/simple --upgrade -r requirements.txt - ) else ( - call venv\Scripts\activate.bat -) -python run.py \ No newline at end of file diff --git a/run.py b/run.py deleted file mode 100644 index cfd3a5f14..000000000 --- a/run.py +++ /dev/null @@ -1,144 +0,0 @@ -import os -import subprocess -import zipfile -import sys -import requests -from tqdm import tqdm - - -def extract_files(zip_path, target_dir): - """ - 解压 - - Args: - zip_path: 源ZIP压缩包路径(需确保是有效压缩包) - target_dir: 目标文件夹路径(会自动创建不存在的目录) - """ - # 打开ZIP压缩包(上下文管理器自动处理关闭) - with zipfile.ZipFile(zip_path) as zip_ref: - # 通过第一个文件路径推断顶层目录名(格式如:top_dir/) - top_dir = zip_ref.namelist()[0].split("/")[0] + "/" - - # 遍历压缩包内所有文件条目 - for file in zip_ref.namelist(): - # 跳过目录条目,仅处理文件 - if file.startswith(top_dir) and not file.endswith("/"): - # 截取顶层目录后的相对路径(如:sub_dir/file.txt) - rel_path = file[len(top_dir) :] - - # 创建目标目录结构(含多级目录) - os.makedirs( - os.path.dirname(f"{target_dir}/{rel_path}"), - exist_ok=True, # 忽略已存在目录的错误 - ) - - # 读取压缩包内文件内容并写入目标路径 - with open(f"{target_dir}/{rel_path}", "wb") as f: - f.write(zip_ref.read(file)) - - -def run_cmd(command: str, open_new_window: bool = True): - """ - 运行 cmd 命令 - - Args: - command (str): 指定要运行的命令 - open_new_window (bool): 指定是否新建一个 cmd 窗口运行 - """ - if open_new_window: - command = "start " + command - subprocess.Popen(command, shell=True) - - -def run_maimbot(): - run_cmd(r"napcat\NapCatWinBootMain.exe 10001", False) - if not os.path.exists(r"mongodb\db"): - os.makedirs(r"mongodb\db") - run_cmd( - r"mongodb\bin\mongod.exe --dbpath=" + os.getcwd() + r"\mongodb\db --port 27017" - ) - run_cmd("nb run") - - -def install_mongodb(): - """ - 安装 MongoDB - """ - print("下载 MongoDB") - resp = requests.get( - "https://fastdl.mongodb.org/windows/mongodb-windows-x86_64-latest.zip", - stream=True, - ) - total = int(resp.headers.get("content-length", 0)) # 计算文件大小 - with open("mongodb.zip", "w+b") as file, tqdm( # 展示下载进度条,并解压文件 - desc="mongodb.zip", - total=total, - unit="iB", - unit_scale=True, - unit_divisor=1024, - ) as bar: - for data in resp.iter_content(chunk_size=1024): - size = file.write(data) - bar.update(size) - extract_files("mongodb.zip", "mongodb") - print("MongoDB 下载完成") - os.remove("mongodb.zip") - choice = input( - "是否安装 MongoDB Compass?此软件可以以可视化的方式修改数据库,建议安装(Y/n)" - ).upper() - if choice == "Y" or choice == "": - install_mongodb_compass() - - -def install_mongodb_compass(): - run_cmd( - r"powershell Start-Process powershell -Verb runAs 'Set-ExecutionPolicy RemoteSigned'" - ) - input("请在弹出的用户账户控制中点击“是”后按任意键继续安装") - run_cmd(r"powershell mongodb\bin\Install-Compass.ps1") - input("按任意键启动麦麦") - input("如不需要启动此窗口可直接关闭,无需等待 Compass 安装完成") - run_maimbot() - - -def install_napcat(): - run_cmd("start https://github.com/NapNeko/NapCatQQ/releases", False) - print("请检查弹出的浏览器窗口,点击**第一个**蓝色的“Win64无头” 下载 napcat") - napcat_filename = input( - "下载完成后请把文件复制到此文件夹,并将**不包含后缀的文件名**输入至此窗口,如 NapCat.32793.Shell:" - ) - if(napcat_filename[-4:] == ".zip"): - napcat_filename = napcat_filename[:-4] - extract_files(napcat_filename + ".zip", "napcat") - print("NapCat 安装完成") - os.remove(napcat_filename + ".zip") - - -if __name__ == "__main__": - os.system("cls") - if sys.version_info < (3, 9): - print("当前 Python 版本过低,最低版本为 3.9,请更新 Python 版本") - print("按任意键退出") - input() - exit(1) - choice = input( - "请输入要进行的操作:\n" - "1.首次安装\n" - "2.运行麦麦\n" - ) - os.system("cls") - if choice == "1": - confirm = input("首次安装将下载并配置所需组件\n1.确认\n2.取消\n") - if confirm == "1": - install_napcat() - install_mongodb() - else: - print("已取消安装") - elif choice == "2": - run_maimbot() - choice = input("是否启动推理可视化?(未完善)(y/N)").upper() - if choice == "Y": - run_cmd(r"python src\gui\reasoning_gui.py") - choice = input("是否启动记忆可视化?(未完善)(y/N)").upper() - if choice == "Y": - run_cmd(r"python src/plugins/memory_system/memory_manual_build.py") diff --git a/run.sh b/run.sh deleted file mode 100644 index c3f6969b6..000000000 --- a/run.sh +++ /dev/null @@ -1,278 +0,0 @@ -#!/bin/bash - -# Maimbot 一键安装脚本 by Cookie987 -# 适用于Debian系 -# 请小心使用任何一键脚本! - -# 如无法访问GitHub请修改此处镜像地址 - -LANG=C.UTF-8 - -GITHUB_REPO="https://ghfast.top/https://github.com/SengokuCola/MaiMBot.git" - -# 颜色输出 -GREEN="\e[32m" -RED="\e[31m" -RESET="\e[0m" - -# 需要的基本软件包 -REQUIRED_PACKAGES=("git" "sudo" "python3" "python3-venv" "curl" "gnupg" "python3-pip") - -# 默认项目目录 -DEFAULT_INSTALL_DIR="/opt/maimbot" - -# 服务名称 -SERVICE_NAME="maimbot" - -IS_INSTALL_MONGODB=false -IS_INSTALL_NAPCAT=false - -# 1/6: 检测是否安装 whiptail -if ! command -v whiptail &>/dev/null; then - echo -e "${RED}[1/6] whiptail 未安装,正在安装...${RESET}" - apt update && apt install -y whiptail -fi - -get_os_info() { - if command -v lsb_release &>/dev/null; then - OS_INFO=$(lsb_release -d | cut -f2) - elif [[ -f /etc/os-release ]]; then - OS_INFO=$(grep "^PRETTY_NAME=" /etc/os-release | cut -d '"' -f2) - else - OS_INFO="Unknown OS" - fi - echo "$OS_INFO" -} - -# 检查系统 -check_system() { - # 检查是否为 root 用户 - if [[ "$(id -u)" -ne 0 ]]; then - whiptail --title "🚫 权限不足" --msgbox "请使用 root 用户运行此脚本!\n执行方式: sudo bash $0" 10 60 - exit 1 - fi - - if [[ -f /etc/os-release ]]; then - source /etc/os-release - if [[ "$ID" != "debian" || "$VERSION_ID" != "12" ]]; then - whiptail --title "🚫 不支持的系统" --msgbox "此脚本仅支持 Debian 12 (Bookworm)!\n当前系统: $PRETTY_NAME\n安装已终止。" 10 60 - exit 1 - fi - else - whiptail --title "⚠️ 无法检测系统" --msgbox "无法识别系统版本,安装已终止。" 10 60 - exit 1 - fi -} - -# 3/6: 询问用户是否安装缺失的软件包 -install_packages() { - missing_packages=() - for package in "${REQUIRED_PACKAGES[@]}"; do - if ! dpkg -s "$package" &>/dev/null; then - missing_packages+=("$package") - fi - done - - if [[ ${#missing_packages[@]} -gt 0 ]]; then - whiptail --title "📦 [3/6] 软件包检查" --yesno "检测到以下必须的依赖项目缺失:\n${missing_packages[*]}\n\n是否要自动安装?" 12 60 - if [[ $? -eq 0 ]]; then - return 0 - else - whiptail --title "⚠️ 注意" --yesno "某些必要的依赖项未安装,可能会影响运行!\n是否继续?" 10 60 || exit 1 - fi - fi -} - -# 4/6: Python 版本检查 -check_python() { - PYTHON_VERSION=$(python3 -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")') - - python3 -c "import sys; exit(0) if sys.version_info >= (3,9) else exit(1)" - if [[ $? -ne 0 ]]; then - whiptail --title "⚠️ [4/6] Python 版本过低" --msgbox "检测到 Python 版本为 $PYTHON_VERSION,需要 3.9 或以上!\n请升级 Python 后重新运行本脚本。" 10 60 - exit 1 - fi -} - -# 5/6: 选择分支 -choose_branch() { - BRANCH=$(whiptail --title "🔀 [5/6] 选择 Maimbot 分支" --menu "请选择要安装的 Maimbot 分支:" 15 60 2 \ - "main" "稳定版本(推荐)" \ - "debug" "开发版本(可能不稳定)" 3>&1 1>&2 2>&3) - - if [[ -z "$BRANCH" ]]; then - BRANCH="main" - whiptail --title "🔀 默认选择" --msgbox "未选择分支,默认安装稳定版本(main)" 10 60 - fi -} - -# 6/6: 选择安装路径 -choose_install_dir() { - INSTALL_DIR=$(whiptail --title "📂 [6/6] 选择安装路径" --inputbox "请输入 Maimbot 的安装目录:" 10 60 "$DEFAULT_INSTALL_DIR" 3>&1 1>&2 2>&3) - - if [[ -z "$INSTALL_DIR" ]]; then - whiptail --title "⚠️ 取消输入" --yesno "未输入安装路径,是否退出安装?" 10 60 - if [[ $? -ne 0 ]]; then - INSTALL_DIR="$DEFAULT_INSTALL_DIR" - else - exit 1 - fi - fi -} - -# 显示确认界面 -confirm_install() { - local confirm_message="请确认以下更改:\n\n" - - if [[ ${#missing_packages[@]} -gt 0 ]]; then - confirm_message+="📦 安装缺失的依赖项: ${missing_packages[*]}\n" - else - confirm_message+="✅ 所有依赖项已安装\n" - fi - - confirm_message+="📂 安装麦麦Bot到: $INSTALL_DIR\n" - confirm_message+="🔀 分支: $BRANCH\n" - - if [[ "$MONGODB_INSTALLED" == "true" ]]; then - confirm_message+="✅ MongoDB 已安装\n" - else - if [[ "$IS_INSTALL_MONGODB" == "true" ]]; then - confirm_message+="📦 安装 MongoDB\n" - fi - fi - - if [[ "$NAPCAT_INSTALLED" == "true" ]]; then - confirm_message+="✅ NapCat 已安装\n" - else - if [[ "$IS_INSTALL_NAPCAT" == "true" ]]; then - confirm_message+="📦 安装 NapCat\n" - fi - fi - - confirm_message+="🛠️ 添加麦麦Bot作为系统服务 ($SERVICE_NAME.service)\n" - - confitm_message+="\n\n注意:本脚本默认使用ghfast.top为GitHub进行加速,如不想使用请手动修改脚本开头的GITHUB_REPO变量。" - whiptail --title "🔧 安装确认" --yesno "$confirm_message\n\n是否继续安装?" 15 60 - if [[ $? -ne 0 ]]; then - whiptail --title "🚫 取消安装" --msgbox "安装已取消。" 10 60 - exit 1 - fi -} - -check_mongodb() { - if command -v mongod &>/dev/null; then - MONGO_INSTALLED=true - else - MONGO_INSTALLED=false - fi -} - -# 安装 MongoDB -install_mongodb() { - if [[ "$MONGO_INSTALLED" == "true" ]]; then - return 0 - fi - - whiptail --title "📦 [3/6] 软件包检查" --yesno "检测到未安装MongoDB,是否安装?\n如果您想使用远程数据库,请跳过此步。" 10 60 - if [[ $? -ne 0 ]]; then - return 1 - fi - IS_INSTALL_MONGODB=true -} - -check_napcat() { - if command -v napcat &>/dev/null; then - NAPCAT_INSTALLED=true - else - NAPCAT_INSTALLED=false - fi -} - -install_napcat() { - if [[ "$NAPCAT_INSTALLED" == "true" ]]; then - return 0 - fi - - whiptail --title "📦 [3/6] 软件包检查" --yesno "检测到未安装NapCat,是否安装?\n如果您想使用远程NapCat,请跳过此步。" 10 60 - if [[ $? -ne 0 ]]; then - return 1 - fi - IS_INSTALL_NAPCAT=true -} - -# 运行安装步骤 -check_system -check_mongodb -check_napcat -install_packages -install_mongodb -install_napcat -check_python -choose_branch -choose_install_dir -confirm_install - -# 开始安装 -whiptail --title "🚀 开始安装" --msgbox "所有环境检查完毕,即将开始安装麦麦Bot!" 10 60 - -echo -e "${GREEN}安装依赖项...${RESET}" - -apt update && apt install -y "${missing_packages[@]}" - - -if [[ "$IS_INSTALL_MONGODB" == "true" ]]; then - echo -e "${GREEN}安装 MongoDB...${RESET}" - curl -fsSL https://www.mongodb.org/static/pgp/server-8.0.asc | gpg -o /usr/share/keyrings/mongodb-server-8.0.gpg --dearmor - echo "deb [ signed-by=/usr/share/keyrings/mongodb-server-8.0.gpg ] http://repo.mongodb.org/apt/debian bookworm/mongodb-org/8.0 main" | sudo tee /etc/apt/sources.list.d/mongodb-org-8.0.list - apt-get update - apt-get install -y mongodb-org - - systemctl enable mongod - systemctl start mongod -fi - -if [[ "$IS_INSTALL_NAPCAT" == "true" ]]; then - echo -e "${GREEN}安装 NapCat...${RESET}" - curl -o napcat.sh https://nclatest.znin.net/NapNeko/NapCat-Installer/main/script/install.sh && bash napcat.sh -fi - -echo -e "${GREEN}创建 Python 虚拟环境...${RESET}" -mkdir -p "$INSTALL_DIR" -cd "$INSTALL_DIR" || exit -python3 -m venv venv -source venv/bin/activate - -echo -e "${GREEN}克隆仓库...${RESET}" -# 安装 Maimbot -mkdir -p "$INSTALL_DIR/repo" -cd "$INSTALL_DIR/repo" || exit 1 -git clone -b "$BRANCH" $GITHUB_REPO . - -echo -e "${GREEN}安装 Python 依赖...${RESET}" -pip install -r requirements.txt - -echo -e "${GREEN}设置服务...${RESET}" - -# 设置 Maimbot 服务 -cat < nul -set /p CONDA_ENV="请输入要激活的 conda 环境名称: " -call conda activate %CONDA_ENV% -if errorlevel 1 ( - echo 激活 conda 环境失败 - pause - exit /b 1 -) -echo Conda 环境 "%CONDA_ENV%" 激活成功 - -set /p OPTION="请选择运行选项 (1: 运行全部绘制, 2: 运行简单绘制): " -if "%OPTION%"=="1" ( - python src/plugins/memory_system/memory_manual_build.py -) else if "%OPTION%"=="2" ( - python src/plugins/memory_system/draw_memory.py -) else ( - echo 无效的选项 - pause - exit /b 1 -) - -if errorlevel 1 ( - echo 命令执行失败,错误代码 %errorlevel% - pause - exit /b 1 -) -echo 脚本成功完成 -pause \ No newline at end of file diff --git a/script/run_db.bat b/script/run_db.bat deleted file mode 100644 index 1741dfd3f..000000000 --- a/script/run_db.bat +++ /dev/null @@ -1 +0,0 @@ -mongod --dbpath="mongodb" --port 27017 \ No newline at end of file diff --git a/script/run_maimai.bat b/script/run_maimai.bat deleted file mode 100644 index 3a099fd7f..000000000 --- a/script/run_maimai.bat +++ /dev/null @@ -1,7 +0,0 @@ -chcp 65001 -call conda activate maimbot -cd . - -REM 执行nb run命令 -nb run -pause \ No newline at end of file diff --git a/script/run_thingking.bat b/script/run_thingking.bat deleted file mode 100644 index a134da6fe..000000000 --- a/script/run_thingking.bat +++ /dev/null @@ -1,5 +0,0 @@ -call conda activate niuniu -cd src\gui -start /b python reasoning_gui.py -exit - diff --git a/script/run_windows.bat b/script/run_windows.bat deleted file mode 100644 index bea397ddc..000000000 --- a/script/run_windows.bat +++ /dev/null @@ -1,68 +0,0 @@ -@echo off -setlocal enabledelayedexpansion -chcp 65001 - -REM 修正路径获取逻辑 -cd /d "%~dp0" || ( - echo 错误:切换目录失败 - exit /b 1 -) - -if not exist "venv\" ( - echo 正在初始化虚拟环境... - - where python >nul 2>&1 - if %errorlevel% neq 0 ( - echo 未找到Python解释器 - exit /b 1 - ) - - for /f "tokens=2" %%a in ('python --version 2^>^&1') do set version=%%a - for /f "tokens=1,2 delims=." %%b in ("!version!") do ( - set major=%%b - set minor=%%c - ) - - if !major! lss 3 ( - echo 需要Python大于等于3.0,当前版本 !version! - exit /b 1 - ) - - if !major! equ 3 if !minor! lss 9 ( - echo 需要Python大于等于3.9,当前版本 !version! - exit /b 1 - ) - - echo 正在安装virtualenv... - python -m pip install virtualenv || ( - echo virtualenv安装失败 - exit /b 1 - ) - - echo 正在创建虚拟环境... - python -m virtualenv venv || ( - echo 虚拟环境创建失败 - exit /b 1 - ) - - call venv\Scripts\activate.bat - -) else ( - call venv\Scripts\activate.bat -) - -echo 正在更新依赖... -pip install -r requirements.txt - -echo 当前代理设置: -echo HTTP_PROXY=%HTTP_PROXY% -echo HTTPS_PROXY=%HTTPS_PROXY% - -set HTTP_PROXY= -set HTTPS_PROXY= -echo 代理已取消。 - -set no_proxy=0.0.0.0/32 - -call nb run -pause \ No newline at end of file diff --git a/setup.py b/setup.py deleted file mode 100644 index 2598a38a8..000000000 --- a/setup.py +++ /dev/null @@ -1,11 +0,0 @@ -from setuptools import find_packages, setup - -setup( - name="maimai-bot", - version="0.1", - packages=find_packages(), - install_requires=[ - 'python-dotenv', - 'pymongo', - ], -) \ No newline at end of file diff --git a/src/common/__init__.py b/src/common/__init__.py index 9a8a345dc..497b4a41a 100644 --- a/src/common/__init__.py +++ b/src/common/__init__.py @@ -1 +1 @@ -# 这个文件可以为空,但必须存在 \ No newline at end of file +# 这个文件可以为空,但必须存在 diff --git a/src/common/database.py b/src/common/database.py index cd149e526..a3e5b4e3b 100644 --- a/src/common/database.py +++ b/src/common/database.py @@ -1,5 +1,4 @@ import os -from typing import cast from pymongo import MongoClient from pymongo.database import Database @@ -11,7 +10,7 @@ def __create_database_instance(): uri = os.getenv("MONGODB_URI") host = os.getenv("MONGODB_HOST", "127.0.0.1") port = int(os.getenv("MONGODB_PORT", "27017")) - db_name = os.getenv("DATABASE_NAME", "MegBot") + # db_name 变量在创建连接时不需要,在获取数据库实例时才使用 username = os.getenv("MONGODB_USERNAME") password = os.getenv("MONGODB_PASSWORD") auth_source = os.getenv("MONGODB_AUTH_SOURCE") diff --git a/src/common/logger.py b/src/common/logger.py new file mode 100644 index 000000000..9e118622d --- /dev/null +++ b/src/common/logger.py @@ -0,0 +1,448 @@ +from loguru import logger +from typing import Dict, Optional, Union, List +import sys +import os +from types import ModuleType +from pathlib import Path +from dotenv import load_dotenv +# from ..plugins.chat.config import global_config + +# 加载 .env 文件 +env_path = Path(__file__).resolve().parent.parent.parent / ".env" +load_dotenv(dotenv_path=env_path) + +# 保存原生处理器ID +default_handler_id = None +for handler_id in logger._core.handlers: + default_handler_id = handler_id + break + +# 移除默认处理器 +if default_handler_id is not None: + logger.remove(default_handler_id) + +# 类型别名 +LoguruLogger = logger.__class__ + +# 全局注册表:记录模块与处理器ID的映射 +_handler_registry: Dict[str, List[int]] = {} + +# 获取日志存储根地址 +current_file_path = Path(__file__).resolve() +LOG_ROOT = "logs" + +SIMPLE_OUTPUT = os.getenv("SIMPLE_OUTPUT", "false") +print(f"SIMPLE_OUTPUT: {SIMPLE_OUTPUT}") + +if not SIMPLE_OUTPUT: + # 默认全局配置 + DEFAULT_CONFIG = { + # 日志级别配置 + "console_level": "INFO", + "file_level": "DEBUG", + # 格式配置 + "console_format": ( + "{time:YYYY-MM-DD HH:mm:ss} | " + "{level: <8} | " + "{extra[module]: <12} | " + "{message}" + ), + "file_format": ("{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | {message}"), + "log_dir": LOG_ROOT, + "rotation": "00:00", + "retention": "3 days", + "compression": "zip", + } +else: + DEFAULT_CONFIG = { + # 日志级别配置 + "console_level": "INFO", + "file_level": "DEBUG", + # 格式配置 + "console_format": ("{time:MM-DD HH:mm} | {extra[module]} | {message}"), + "file_format": ("{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | {message}"), + "log_dir": LOG_ROOT, + "rotation": "00:00", + "retention": "3 days", + "compression": "zip", + } + + +# 海马体日志样式配置 +MEMORY_STYLE_CONFIG = { + "advanced": { + "console_format": ( + "{time:YYYY-MM-DD HH:mm:ss} | " + "{level: <8} | " + "{extra[module]: <12} | " + "海马体 | " + "{message}" + ), + "file_format": ("{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 海马体 | {message}"), + }, + "simple": { + "console_format": ( + "{time:MM-DD HH:mm} | 海马体 | {message}" + ), + "file_format": ("{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 海马体 | {message}"), + }, +} + + +# MOOD +MOOD_STYLE_CONFIG = { + "advanced": { + "console_format": ( + "{time:YYYY-MM-DD HH:mm:ss} | " + "{level: <8} | " + "{extra[module]: <12} | " + "心情 | " + "{message}" + ), + "file_format": ("{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 心情 | {message}"), + }, + "simple": { + "console_format": ("{time:MM-DD HH:mm} | 心情 | {message}"), + "file_format": ("{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 心情 | {message}"), + }, +} + +# relationship +RELATION_STYLE_CONFIG = { + "advanced": { + "console_format": ( + "{time:YYYY-MM-DD HH:mm:ss} | " + "{level: <8} | " + "{extra[module]: <12} | " + "关系 | " + "{message}" + ), + "file_format": ("{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 关系 | {message}"), + }, + "simple": { + "console_format": ("{time:MM-DD HH:mm} | 关系 | {message}"), + "file_format": ("{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 关系 | {message}"), + }, +} + +# config +CONFIG_STYLE_CONFIG = { + "advanced": { + "console_format": ( + "{time:YYYY-MM-DD HH:mm:ss} | " + "{level: <8} | " + "{extra[module]: <12} | " + "配置 | " + "{message}" + ), + "file_format": ("{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 配置 | {message}"), + }, + "simple": { + "console_format": ("{time:MM-DD HH:mm} | 配置 | {message}"), + "file_format": ("{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 配置 | {message}"), + }, +} + +SENDER_STYLE_CONFIG = { + "advanced": { + "console_format": ( + "{time:YYYY-MM-DD HH:mm:ss} | " + "{level: <8} | " + "{extra[module]: <12} | " + "消息发送 | " + "{message}" + ), + "file_format": ("{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 消息发送 | {message}"), + }, + "simple": { + "console_format": ("{time:MM-DD HH:mm} | 消息发送 | {message}"), + "file_format": ("{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 消息发送 | {message}"), + }, +} + +HEARTFLOW_STYLE_CONFIG = { + "advanced": { + "console_format": ( + "{time:YYYY-MM-DD HH:mm:ss} | " + "{level: <8} | " + "{extra[module]: <12} | " + "麦麦大脑袋 | " + "{message}" + ), + "file_format": ("{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 麦麦大脑袋 | {message}"), + }, + "simple": { + "console_format": ( + "{time:MM-DD HH:mm} | 麦麦大脑袋 | {message}" + ), # noqa: E501 + "file_format": ("{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 麦麦大脑袋 | {message}"), + }, +} + +SCHEDULE_STYLE_CONFIG = { + "advanced": { + "console_format": ( + "{time:YYYY-MM-DD HH:mm:ss} | " + "{level: <8} | " + "{extra[module]: <12} | " + "在干嘛 | " + "{message}" + ), + "file_format": ("{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 在干嘛 | {message}"), + }, + "simple": { + "console_format": ("{time:MM-DD HH:mm} | 在干嘛 | {message}"), + "file_format": ("{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 在干嘛 | {message}"), + }, +} + +LLM_STYLE_CONFIG = { + "advanced": { + "console_format": ( + "{time:YYYY-MM-DD HH:mm:ss} | " + "{level: <8} | " + "{extra[module]: <12} | " + "麦麦组织语言 | " + "{message}" + ), + "file_format": ("{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 麦麦组织语言 | {message}"), + }, + "simple": { + "console_format": ("{time:MM-DD HH:mm} | 麦麦组织语言 | {message}"), + "file_format": ("{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 麦麦组织语言 | {message}"), + }, +} + + +# Topic日志样式配置 +TOPIC_STYLE_CONFIG = { + "advanced": { + "console_format": ( + "{time:YYYY-MM-DD HH:mm:ss} | " + "{level: <8} | " + "{extra[module]: <12} | " + "话题 | " + "{message}" + ), + "file_format": ("{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 话题 | {message}"), + }, + "simple": { + "console_format": ("{time:MM-DD HH:mm} | 主题 | {message}"), + "file_format": ("{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 话题 | {message}"), + }, +} + +# Topic日志样式配置 +CHAT_STYLE_CONFIG = { + "advanced": { + "console_format": ( + "{time:YYYY-MM-DD HH:mm:ss} | " + "{level: <8} | " + "{extra[module]: <12} | " + "见闻 | " + "{message}" + ), + "file_format": ("{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 见闻 | {message}"), + }, + "simple": { + "console_format": ( + "{time:MM-DD HH:mm} | 见闻 | {message}" + ), # noqa: E501 + "file_format": ("{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 见闻 | {message}"), + }, +} + +SUB_HEARTFLOW_STYLE_CONFIG = { + "advanced": { + "console_format": ( + "{time:YYYY-MM-DD HH:mm:ss} | " + "{level: <8} | " + "{extra[module]: <12} | " + "麦麦小脑袋 | " + "{message}" + ), + "file_format": ("{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 麦麦小脑袋 | {message}"), + }, + "simple": { + "console_format": ( + "{time:MM-DD HH:mm} | 麦麦小脑袋 | {message}" + ), # noqa: E501 + "file_format": ("{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 麦麦小脑袋 | {message}"), + }, +} + +WILLING_STYLE_CONFIG = { + "advanced": { + "console_format": ( + "{time:YYYY-MM-DD HH:mm:ss} | " + "{level: <8} | " + "{extra[module]: <12} | " + "意愿 | " + "{message}" + ), + "file_format": ("{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 意愿 | {message}"), + }, + "simple": { + "console_format": ( + "{time:MM-DD HH:mm} | 意愿 | {message}" + ), # noqa: E501 + "file_format": ("{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 意愿 | {message}"), + }, +} + + +# 根据SIMPLE_OUTPUT选择配置 +MEMORY_STYLE_CONFIG = MEMORY_STYLE_CONFIG["simple"] if SIMPLE_OUTPUT else MEMORY_STYLE_CONFIG["advanced"] +TOPIC_STYLE_CONFIG = TOPIC_STYLE_CONFIG["simple"] if SIMPLE_OUTPUT else TOPIC_STYLE_CONFIG["advanced"] +SENDER_STYLE_CONFIG = SENDER_STYLE_CONFIG["simple"] if SIMPLE_OUTPUT else SENDER_STYLE_CONFIG["advanced"] +LLM_STYLE_CONFIG = LLM_STYLE_CONFIG["simple"] if SIMPLE_OUTPUT else LLM_STYLE_CONFIG["advanced"] +CHAT_STYLE_CONFIG = CHAT_STYLE_CONFIG["simple"] if SIMPLE_OUTPUT else CHAT_STYLE_CONFIG["advanced"] +MOOD_STYLE_CONFIG = MOOD_STYLE_CONFIG["simple"] if SIMPLE_OUTPUT else MOOD_STYLE_CONFIG["advanced"] +RELATION_STYLE_CONFIG = RELATION_STYLE_CONFIG["simple"] if SIMPLE_OUTPUT else RELATION_STYLE_CONFIG["advanced"] +SCHEDULE_STYLE_CONFIG = SCHEDULE_STYLE_CONFIG["simple"] if SIMPLE_OUTPUT else SCHEDULE_STYLE_CONFIG["advanced"] +HEARTFLOW_STYLE_CONFIG = HEARTFLOW_STYLE_CONFIG["simple"] if SIMPLE_OUTPUT else HEARTFLOW_STYLE_CONFIG["advanced"] +SUB_HEARTFLOW_STYLE_CONFIG = ( + SUB_HEARTFLOW_STYLE_CONFIG["simple"] if SIMPLE_OUTPUT else SUB_HEARTFLOW_STYLE_CONFIG["advanced"] +) # noqa: E501 +WILLING_STYLE_CONFIG = WILLING_STYLE_CONFIG["simple"] if SIMPLE_OUTPUT else WILLING_STYLE_CONFIG["advanced"] +CONFIG_STYLE_CONFIG = CONFIG_STYLE_CONFIG["simple"] if SIMPLE_OUTPUT else CONFIG_STYLE_CONFIG["advanced"] + + +def is_registered_module(record: dict) -> bool: + """检查是否为已注册的模块""" + return record["extra"].get("module") in _handler_registry + + +def is_unregistered_module(record: dict) -> bool: + """检查是否为未注册的模块""" + return not is_registered_module(record) + + +def log_patcher(record: dict) -> None: + """自动填充未设置模块名的日志记录,保留原生模块名称""" + if "module" not in record["extra"]: + # 尝试从name中提取模块名 + module_name = record.get("name", "") + if module_name == "": + module_name = "root" + record["extra"]["module"] = module_name + + +# 应用全局修补器 +logger.configure(patcher=log_patcher) + + +class LogConfig: + """日志配置类""" + + def __init__(self, **kwargs): + self.config = DEFAULT_CONFIG.copy() + self.config.update(kwargs) + + def to_dict(self) -> dict: + return self.config.copy() + + def update(self, **kwargs): + self.config.update(kwargs) + + +def get_module_logger( + module: Union[str, ModuleType], + *, + console_level: Optional[str] = None, + file_level: Optional[str] = None, + extra_handlers: Optional[List[dict]] = None, + config: Optional[LogConfig] = None, +) -> LoguruLogger: + module_name = module if isinstance(module, str) else module.__name__ + current_config = config.config if config else DEFAULT_CONFIG + + # 清理旧处理器 + if module_name in _handler_registry: + for handler_id in _handler_registry[module_name]: + logger.remove(handler_id) + del _handler_registry[module_name] + + handler_ids = [] + + # 控制台处理器 + console_id = logger.add( + sink=sys.stderr, + level=os.getenv("CONSOLE_LOG_LEVEL", console_level or current_config["console_level"]), + format=current_config["console_format"], + filter=lambda record: record["extra"].get("module") == module_name, + enqueue=True, + ) + handler_ids.append(console_id) + + # 文件处理器 + log_dir = Path(current_config["log_dir"]) + log_dir.mkdir(parents=True, exist_ok=True) + log_file = log_dir / module_name / "{time:YYYY-MM-DD}.log" + log_file.parent.mkdir(parents=True, exist_ok=True) + + file_id = logger.add( + sink=str(log_file), + level=os.getenv("FILE_LOG_LEVEL", file_level or current_config["file_level"]), + format=current_config["file_format"], + rotation=current_config["rotation"], + retention=current_config["retention"], + compression=current_config["compression"], + encoding="utf-8", + filter=lambda record: record["extra"].get("module") == module_name, + enqueue=True, + ) + handler_ids.append(file_id) + + # 额外处理器 + if extra_handlers: + for handler in extra_handlers: + handler_id = logger.add(**handler) + handler_ids.append(handler_id) + + # 更新注册表 + _handler_registry[module_name] = handler_ids + + return logger.bind(module=module_name) + + +def remove_module_logger(module_name: str) -> None: + """清理指定模块的日志处理器""" + if module_name in _handler_registry: + for handler_id in _handler_registry[module_name]: + logger.remove(handler_id) + del _handler_registry[module_name] + + +# 添加全局默认处理器(只处理未注册模块的日志--->控制台) +# print(os.getenv("DEFAULT_CONSOLE_LOG_LEVEL", "SUCCESS")) +DEFAULT_GLOBAL_HANDLER = logger.add( + sink=sys.stderr, + level=os.getenv("DEFAULT_CONSOLE_LOG_LEVEL", "SUCCESS"), + format=( + "{time:YYYY-MM-DD HH:mm:ss} | " + "{level: <8} | " + "{name: <12} | " + "{message}" + ), + filter=lambda record: is_unregistered_module(record), # 只处理未注册模块的日志,并过滤nonebot + enqueue=True, +) + +# 添加全局默认文件处理器(只处理未注册模块的日志--->logs文件夹) +log_dir = Path(DEFAULT_CONFIG["log_dir"]) +log_dir.mkdir(parents=True, exist_ok=True) +other_log_dir = log_dir / "other" +other_log_dir.mkdir(parents=True, exist_ok=True) + +DEFAULT_FILE_HANDLER = logger.add( + sink=str(other_log_dir / "{time:YYYY-MM-DD}.log"), + level=os.getenv("DEFAULT_FILE_LOG_LEVEL", "DEBUG"), + format=("{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {name: <15} | {message}"), + rotation=DEFAULT_CONFIG["rotation"], + retention=DEFAULT_CONFIG["retention"], + compression=DEFAULT_CONFIG["compression"], + encoding="utf-8", + filter=lambda record: is_unregistered_module(record), # 只处理未注册模块的日志,并过滤nonebot + enqueue=True, +) diff --git a/src/gui/logger_gui.py b/src/gui/logger_gui.py new file mode 100644 index 000000000..f2dd698cd --- /dev/null +++ b/src/gui/logger_gui.py @@ -0,0 +1,347 @@ +import customtkinter as ctk +import subprocess +import threading +import queue +import re +import os +import signal +from collections import deque + +# 设置应用的外观模式和默认颜色主题 +ctk.set_appearance_mode("dark") +ctk.set_default_color_theme("blue") + + +class LogViewerApp(ctk.CTk): + """日志查看器应用的主类,继承自customtkinter的CTk类""" + + def __init__(self): + """初始化日志查看器应用的界面和状态""" + super().__init__() + self.title("日志查看器") + self.geometry("1200x800") + + # 初始化进程、日志队列、日志数据等变量 + self.process = None + self.log_queue = queue.Queue() + self.log_data = deque(maxlen=10000) # 使用固定长度队列 + self.available_levels = set() + self.available_modules = set() + self.sorted_modules = [] + self.module_checkboxes = {} # 存储模块复选框的字典 + + # 日志颜色配置 + self.color_config = { + "time": "#888888", + "DEBUG": "#2196F3", + "INFO": "#4CAF50", + "WARNING": "#FF9800", + "ERROR": "#F44336", + "module": "#D4D0AB", + "default": "#FFFFFF", + } + + # 列可见性配置 + self.column_visibility = {"show_time": True, "show_level": True, "show_module": True} + + # 选中的日志等级和模块 + self.selected_levels = set() + self.selected_modules = set() + + # 创建界面组件并启动日志队列处理 + self.create_widgets() + self.after(100, self.process_log_queue) + + def create_widgets(self): + """创建应用界面的各个组件""" + self.grid_columnconfigure(0, weight=1) + self.grid_rowconfigure(1, weight=1) + + # 控制面板 + control_frame = ctk.CTkFrame(self) + control_frame.grid(row=0, column=0, sticky="ew", padx=10, pady=5) + + self.start_btn = ctk.CTkButton(control_frame, text="启动", command=self.start_process) + self.start_btn.pack(side="left", padx=5) + + self.stop_btn = ctk.CTkButton(control_frame, text="停止", command=self.stop_process, state="disabled") + self.stop_btn.pack(side="left", padx=5) + + self.clear_btn = ctk.CTkButton(control_frame, text="清屏", command=self.clear_logs) + self.clear_btn.pack(side="left", padx=5) + + column_filter_frame = ctk.CTkFrame(control_frame) + column_filter_frame.pack(side="left", padx=20) + + self.time_check = ctk.CTkCheckBox(column_filter_frame, text="显示时间", command=self.refresh_logs) + self.time_check.pack(side="left", padx=5) + self.time_check.select() + + self.level_check = ctk.CTkCheckBox(column_filter_frame, text="显示等级", command=self.refresh_logs) + self.level_check.pack(side="left", padx=5) + self.level_check.select() + + self.module_check = ctk.CTkCheckBox(column_filter_frame, text="显示模块", command=self.refresh_logs) + self.module_check.pack(side="left", padx=5) + self.module_check.select() + + # 筛选面板 + filter_frame = ctk.CTkFrame(self) + filter_frame.grid(row=0, column=1, rowspan=2, sticky="ns", padx=5) + + ctk.CTkLabel(filter_frame, text="日志等级筛选").pack(pady=5) + self.level_scroll = ctk.CTkScrollableFrame(filter_frame, width=150, height=200) + self.level_scroll.pack(fill="both", expand=True, padx=5) + + ctk.CTkLabel(filter_frame, text="模块筛选").pack(pady=5) + self.module_filter_entry = ctk.CTkEntry(filter_frame, placeholder_text="输入模块过滤词") + self.module_filter_entry.pack(pady=5) + self.module_filter_entry.bind("", self.update_module_filter) + + self.module_scroll = ctk.CTkScrollableFrame(filter_frame, width=300, height=200) + self.module_scroll.pack(fill="both", expand=True, padx=5) + + self.log_text = ctk.CTkTextbox(self, wrap="word") + self.log_text.grid(row=1, column=0, sticky="nsew", padx=10, pady=5) + + self.init_text_tags() + + def update_module_filter(self, event): + """根据模块过滤词更新模块复选框的显示""" + filter_text = self.module_filter_entry.get().strip().lower() + for module, checkbox in self.module_checkboxes.items(): + if filter_text in module.lower(): + checkbox.pack(anchor="w", padx=5, pady=2) + else: + checkbox.pack_forget() + + def update_filters(self, level, module): + """更新日志等级和模块的筛选器""" + if level not in self.available_levels: + self.available_levels.add(level) + self.add_checkbox(self.level_scroll, level, "level") + + module_key = self.get_module_key(module) + if module_key not in self.available_modules: + self.available_modules.add(module_key) + self.sorted_modules = sorted(self.available_modules, key=lambda x: x.lower()) + self.rebuild_module_checkboxes() + + def rebuild_module_checkboxes(self): + """重新构建模块复选框""" + # 清空现有复选框 + for widget in self.module_scroll.winfo_children(): + widget.destroy() + self.module_checkboxes.clear() + + # 重建排序后的复选框 + for module in self.sorted_modules: + self.add_checkbox(self.module_scroll, module, "module") + + def add_checkbox(self, parent, text, type_): + """在指定父组件中添加复选框""" + + def update_filter(): + current = cb.get() + if type_ == "level": + (self.selected_levels.add if current else self.selected_levels.discard)(text) + else: + (self.selected_modules.add if current else self.selected_modules.discard)(text) + self.refresh_logs() + + cb = ctk.CTkCheckBox(parent, text=text, command=update_filter) + cb.select() # 初始选中 + + # 手动同步初始状态到集合(关键修复) + if type_ == "level": + self.selected_levels.add(text) + else: + self.selected_modules.add(text) + + if type_ == "module": + self.module_checkboxes[text] = cb + cb.pack(anchor="w", padx=5, pady=2) + return cb + + def check_filter(self, entry): + """检查日志条目是否符合当前筛选条件""" + level_ok = not self.selected_levels or entry["level"] in self.selected_levels + module_key = self.get_module_key(entry["module"]) + module_ok = not self.selected_modules or module_key in self.selected_modules + return level_ok and module_ok + + def init_text_tags(self): + """初始化日志文本的颜色标签""" + for tag, color in self.color_config.items(): + self.log_text.tag_config(tag, foreground=color) + self.log_text.tag_config("default", foreground=self.color_config["default"]) + + def start_process(self): + """启动日志进程并开始读取输出""" + self.process = subprocess.Popen( + ["nb", "run"], + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + text=True, + bufsize=1, + encoding="utf-8", + errors="ignore", + ) + self.start_btn.configure(state="disabled") + self.stop_btn.configure(state="normal") + threading.Thread(target=self.read_output, daemon=True).start() + + def stop_process(self): + """停止日志进程并清理相关资源""" + if self.process: + try: + if hasattr(self.process, "pid"): + if os.name == "nt": + subprocess.run( + ["taskkill", "/F", "/T", "/PID", str(self.process.pid)], check=True, capture_output=True + ) + else: + os.killpg(os.getpgid(self.process.pid), signal.SIGTERM) + except (subprocess.CalledProcessError, ProcessLookupError, OSError) as e: + print(f"终止进程失败: {e}") + finally: + self.process = None + self.log_queue.queue.clear() + self.start_btn.configure(state="normal") + self.stop_btn.configure(state="disabled") + self.refresh_logs() + + def read_output(self): + """读取日志进程的输出并放入队列""" + try: + while self.process and self.process.poll() is None: + line = self.process.stdout.readline() + if line: + self.log_queue.put(line) + else: + break # 避免空循环 + self.process.stdout.close() # 确保关闭文件描述符 + except ValueError: # 处理可能的I/O操作异常 + pass + + def process_log_queue(self): + """处理日志队列中的日志条目""" + while not self.log_queue.empty(): + line = self.log_queue.get() + self.process_log_line(line) + self.after(100, self.process_log_queue) + + def process_log_line(self, line): + """解析单行日志并更新日志数据和筛选器""" + match = re.match( + r"""^ + (?:(?P