Merge branch 'main-fix' of https://github.com/SengokuCola/MaiMBot into main-fix

This commit is contained in:
SengokuCola
2025-03-18 10:00:03 +08:00
13 changed files with 1241 additions and 431 deletions

17
.github/pull_request_template.md vendored Normal file
View File

@@ -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**
- **附加信息**:

29
.github/workflows/precheck.yml vendored Normal file
View File

@@ -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: ['🚫冲突需处理']
})

View File

@@ -6,3 +6,4 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: astral-sh/ruff-action@v3

3
.gitignore vendored
View File

@@ -213,3 +213,6 @@ jieba.cache
.python-version
OtherRes.txt
/eula.confirmed
/privacy.confirmed

122
EULA.md
View File

@@ -1,69 +1,97 @@
# **MaiMBot最终用户许可协议**
**版本V1.0**
**更新日期2025年3月18日**
**生效日期2025年3月18日**
**适用的MaiMBot版本号<=v0.5.15**
---
# **MaimBot用户协议**
**生效日期:** 2025.3.14
**2025© MaiMBot项目团队**
---
### **特别声明**
1. **MaimBot为遵循GPLv3协议的开源项目**
- 代码托管于GitHub**开发者不持有任何法律实体**,项目由社区共同维护;
- 用户可自由使用、修改、分发代码,但**必须遵守GPLv3许可证要求**(详见项目仓库)。
## 一、一般条款
2. **无责任声明**
- 本项目**不提供任何形式的担保**,开发者及贡献者均不对使用后果负责;
- 所有功能依赖第三方API**生成内容不受我方控制**。
**1.1** MaiMBot项目包括MaiMBot的源代码、可执行文件、文档以及其它在本协议中所列出的文件以下简称“本项目”是由开发者及贡献者以下简称“项目团队”共同维护为用户提供自动回复功能的机器人代码项目。以下最终用户许可协议EULA以下简称“本协议”是用户以下简称“您”与项目团队之间关于使用本项目所订立的合同条件。
---
**1.2** 在运行或使用本项目之前,您**必须阅读并同意本协议的所有条款**。未成年人或其它无/不完全民事行为能力责任人请**在监护人的陪同下**阅读并同意本协议。如果您不同意,则不得运行或使用本项目。在这种情况下,您应立即从您的设备上卸载或删除本项目及其所有副本。
### **一、基础说明**
1. **MaimBot是什么**
- MaimBot是基于第三方AI技术如ChatGPT等的自动回复机器人**所有输出内容均由AI自动生成不代表我方观点**。
- 用户可提交自定义指令Prompt经我方内容过滤后调用第三方API生成结果**输出可能存在错误、偏见或不适宜内容**。
---
## 二、许可授权
### **二、用户责任**
1. **禁止内容**
您承诺**不提交或生成以下内容**,否则我方有权永久封禁账号:
- 违法、暴力、色情、歧视性内容;
- 诈骗、谣言、恶意代码等危害他人或社会的内容;
- 侵犯他人隐私、肖像权、知识产权的内容。
### 源代码许可
**2.1** 您**了解**本项目的源代码是基于GPLv3GNU通用公共许可证第三版开源协议发布的。您**可以自由使用、修改、分发**本项目的源代码,但**必须遵守**GPLv3许可证的要求。详细内容请参阅项目仓库中的LICENSE文件。
2. **后果自负**
- 您需对**输入的指令Prompt和生成内容的使用负全责**
- **禁止将结果用于医疗、法律、投资等专业领域**,否则风险自行承担。
**2.2** **了解**本项目的源代码中可能包含第三方开源代码这些代码的许可证可能与GPLv3许可证不同。您**同意**在使用这些代码时**遵守**相应的许可证要求。
---
### **三、我们不负责什么**
1. **技术问题**
- 因第三方API故障、网络延迟、内容过滤误判导致的服务异常
- AI生成内容的不准确、冒犯性、时效性错误。
### 输入输出内容授权
2. **用户行为**
- 因您违反本协议或滥用MaimBot导致的任何纠纷、损失
- 他人通过您的账号生成的违规内容。
**2.3** **了解**本项目是使用您的配置信息、提交的指令以下简称“输入内容”和生成的内容以下简称“输出内容”构建请求发送到第三方API生成回复的机器人项目。
---
**2.4** 您**授权**本项目使用您的输入和输出内容按照项目的隐私政策用于以下行为:
- 调用第三方API生成回复
- 调用第三方API用于构建本项目专用的存储于您部署或使用的数据库中的知识库和记忆库
- 收集并记录本项目专用的存储于您部署或使用的设备中的日志;
### **四、其他重要条款**
1. **隐私与数据**
- 您提交的指令和生成内容可能被匿名化后用于优化服务,**敏感信息请勿输入**
- **我方会收集部分统计信息(如使用频率、基础指令类型)以改进服务,您可在[bot_config.toml]随时关闭此功能**。
**2.4** 您**了解**本项目的源代码中包含第三方API的调用代码这些API的使用可能受到第三方的服务条款和隐私政策的约束。在使用这些API时您**必须遵守**相应的服务条款。
2. **精神健康风险**
⚠️ **MaimBot仅为工具型机器人不具备情感交互能力。建议用户**
**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. **封禁权利**
- 我方有权不经通知**删除违规内容、暂停或终止您的访问权限**。
4. **争议解决**
### 三、其他
**3.1 争议解决**
- 本协议适用中国法律,争议提交相关地区法院管辖;
- 若因GPLv3许可产生纠纷以许可证官方解释为准。
---

View File

@@ -170,7 +170,10 @@ if exist "%_root%\tools\git\bin" (
cls
sc query | findstr /i "MongoDB" >nul
if !errorlevel! neq 0 (
echo MongoDB服务未运行正在尝试启动...
echo MongoDB服务未运行是否尝试运行服务?
set /p confirm="是否启动?(Y/N): "
if /i "!confirm!"=="Y" (
echo 正在尝试启动MongoDB服务...
powershell -Command "Start-Process -Verb RunAs cmd -ArgumentList '/c net start MongoDB'"
echo 正在等待MongoDB服务启动...
echo 按下任意键跳过等待...
@@ -178,23 +181,27 @@ if !errorlevel! neq 0 (
sc query | findstr /i "MongoDB" >nul
if !errorlevel! neq 0 (
echo MongoDB服务启动失败可能是没有安装要安装吗
set /p confirm="继续?(Y/N): "
if /i "!confirm!"=="Y" (
set /p install_confirm="继续安装(Y/N): "
if /i "!install_confirm!"=="Y" (
echo 正在安装MongoDB...
winget install --id MongoDB.Server -e --accept-package-agreements --accept-source-agreements
echo 安装完成正在启动MongoDB服务...
net start MongoDB
if %errorlevel% neq 0 (
if !errorlevel! neq 0 (
echo 启动MongoDB服务失败请手动启动
exit /b
) else (
echo MongoDB服务已成功启动
)
echo MongoDB服务已启动
) else (
echo 取消安装MongoDB按任意键退出...
pause >nul
exit /b
)
)
) else (
echo "警告MongoDB服务未运行将导致MaiMBot无法访问数据库"
)
) else (
echo MongoDB服务已运行
)
@@ -259,43 +266,135 @@ if "!BRANCH!"=="main" (
@REM endlocal & set "BRANCH_COLOR=%BRANCH_COLOR%"
:check_is_venv
echo 正在检查是否在虚拟环境...
echo 正在检查虚拟环境状态...
if exist "%_root%\config\no_venv" (
echo 检测到no_venv,跳过虚拟环境检查
goto menu
)
if not defined VIRTUAL_ENV (
echo 当前使用的Python环境为
echo !PYTHON_HOME!
echo 似乎没有使用虚拟环境,是否要创建一个新的虚拟环境?
set /p confirm="继续?(Y/N): "
if /i "!confirm!"=="Y" (
echo 正在创建虚拟环境...
python -m virtualenv venv
call venv\Scripts\activate.bat
echo 要安装依赖吗?
set /p install_confirm="继续?(Y/N): "
if /i "%install_confirm%"=="Y" (
echo 正在安装依赖...
python -m pip config set global.index-url https://mirrors.aliyun.com/pypi/simple
python -m pip install -r requirements.txt
)
echo 虚拟环境创建完成,按任意键返回...
) else (
:: 环境检测
if defined VIRTUAL_ENV (
goto menu
)
echo =====================================
echo 虚拟环境检测警告:
echo 当前使用系统Python路径!PYTHON_HOME!
echo 未检测到激活的虚拟环境!
:env_interaction
echo =====================================
echo 请选择操作:
echo 1 - 创建并激活Venv虚拟环境
echo 2 - 创建/激活Conda虚拟环境
echo 3 - 临时跳过本次检查
echo 4 - 永久跳过虚拟环境检查
set /p choice="请输入选项(1-4): "
if "!choice!"=="4" (
echo 要永久跳过虚拟环境检查吗?
set /p no_venv_confirm="继续?(Y/N): "
set /p no_venv_confirm="继续?(Y/N): ....."
if /i "!no_venv_confirm!"=="Y" (
echo 正在创建no_venv文件...
echo 1 > "%_root%\config\no_venv"
echo 已创建no_venv文件,按任意键返回...
echo 已创建no_venv文件
pause >nul
goto menu
) else (
echo 取消跳过虚拟环境检查,按任意键返回...
)
)
pause >nul
goto env_interaction
)
)
if "!choice!"=="3" (
echo 警告:使用系统环境可能导致依赖冲突!
timeout /t 2 >nul
goto menu
)
if "!choice!"=="2" goto handle_conda
if "!choice!"=="1" goto handle_venv
echo 无效的输入请输入1-4之间的数字
timeout /t 2 >nul
goto env_interaction
:handle_venv
python -m pip config set global.index-url https://mirrors.aliyun.com/pypi/simple
echo 正在初始化Venv环境...
python -m pip install virtualenv || (
echo 安装环境失败,错误码:!errorlevel!
pause
goto env_interaction
)
echo 创建虚拟环境到venv
python -m virtualenv venv || (
echo 环境创建失败,错误码:!errorlevel!
pause
goto env_interaction
)
call venv\Scripts\activate.bat
echo 已激活Venv环境
echo 要安装依赖吗?
set /p install_confirm="继续?(Y/N): "
if /i "!install_confirm!"=="Y" (
goto update_dependencies
)
goto menu
:handle_conda
where conda >nul 2>&1 || (
echo 未检测到conda可能原因
echo 1. 未安装Miniconda
echo 2. conda配置异常
timeout /t 10 >nul
goto env_interaction
)
:conda_menu
echo 请选择Conda操作
echo 1 - 创建新环境
echo 2 - 激活已有环境
echo 3 - 返回上级菜单
set /p choice="请输入选项(1-3): "
if "!choice!"=="3" goto env_interaction
if "!choice!"=="2" goto activate_conda
if "!choice!"=="1" goto create_conda
echo 无效的输入请输入1-3之间的数字
timeout /t 2 >nul
goto conda_menu
:create_conda
set /p "CONDA_ENV=请输入新环境名称:"
if "!CONDA_ENV!"=="" (
echo 环境名称不能为空!
goto create_conda
)
conda create -n !CONDA_ENV! python=3.13 -y || (
echo 环境创建失败,错误码:!errorlevel!
timeout /t 10 >nul
goto conda_menu
)
goto activate_conda
:activate_conda
set /p "CONDA_ENV=请输入要激活的环境名称:"
call conda activate !CONDA_ENV! || (
echo 激活失败,可能原因:
echo 1. 环境不存在
echo 2. conda配置异常
pause
goto conda_menu
)
echo 成功激活conda环境!CONDA_ENV!
echo 要安装依赖吗?
set /p install_confirm="继续?(Y/N): "
if /i "!install_confirm!"=="Y" (
goto update_dependencies
)
:menu
@chcp 936
cls

21
PRIVACY.md Normal file
View File

@@ -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** 项目团队保留在未来更新隐私条款的权利,但没有义务通知您。若您不同意更新后的隐私条款,您应立即停止使用本项目。

115
README.md
View File

@@ -1,3 +1,81 @@
# 关于项目分支调整与贡献指南的重要通知
<details>
<summary>
- 📂 致所有为麦麦提交过贡献,以及想要为麦麦提交贡献的朋友们!</summary>
---
**📢 关于项目分支调整与贡献指南的重要通知**
**致所有关注MaiMBot的开发者与贡献者**
首先我们由衷感谢大家近期的热情参与感谢大家对MaiMBot的喜欢项目突然受到广泛关注让我们倍感惊喜也深深感受到开源社区的温暖力量。为了保障项目长期健康发展我们不得不对开发流程做出重要调整恳请理解与支持。
---
### **📌 本次调整的核心原因**
1. **维护团队精力有限**
核心成员(包括我本人)均为在校学生/在职开发者近期涌入的大量PR和意见已远超我们的处理能力。为确保本职工作与项目质量我们必须优化协作流程。
2. **重构核心架构的紧迫性**
当前我们正与核心团队全力重构项目底层逻辑,这是为未来扩展性、性能提升打下的必要基础,需要高度专注。
3. **保障现有用户的稳定性**
我们深知许多用户已依赖当前版本,因此必须划分清晰的维护边界,确保生产环境可用性。
---
### **🌿 全新分支策略与贡献指南**
为平衡上述目标,即日起启用以下分支结构:
| 分支 | 定位 | 接受PR类型 | 提交对象 |
| ---------- | ---------------------------- | --------------------------------------------- | ---------------- |
| `main` | **稳定版**(供下载使用) | 仅接受来自`main-fix`的合并 | 维护团队直接管理 |
| `main-fix` | 生产环境紧急修复 | 明确的功能缺陷修复(需附带复现步骤/测试用例) | 所有开发者 |
| `refactor` | 重构版(**不兼容当前main** | 仅重构与相关Bug修复 | 重构小组维护 |
---
### **⚠️ 对现有PR的处理说明**
由于分支结构调整,**GitHub已自动关闭所有未合并的PR**这并非否定您的贡献价值如果您认为自己的PR符合以下条件
- 属于`main-fix`明确的**功能性缺陷修复**(非功能增强) 包括非预期行为和严重报错需要发布issue讨论确定。
- 属于`refactor`分支的**重构适配性修复**
**欢迎您重新提交到对应分支**并在PR描述中标注`[Re-submit from closed PR]`我们将优先审查。其他类型PR暂缓受理但您的创意我们已记录在案未来重构完成后将重新评估。
---
### **🙏 致谢与协作倡议**
- 感谢每一位提交Issue、PR、参与讨论的开发者您的每一行代码都是maim吃的
- 特别致敬在交流群中积极答疑的社区成员,你们自发维护的氛围令人感动❤️ maim哭了
- **重构期间的非代码贡献同样珍贵**文档改进、测试用例补充、用户反馈整理等欢迎通过Issue认领任务
---
### **📬 高效协作小贴士**
1. **提交前请先讨论**创建Issue描述问题确认是否符合`main-fix`修复范围
2. **对重构提出您的想法**如果您对重构版有自己的想法欢迎提交讨论issue亟需测试伙伴欢迎邮件联系`team@xxx.org`报名
3. **部分main-fix的功能在issue讨论后经过严格讨论一致决定可以添加功能改动或修复的可以提交pr**
---
**谢谢大家谢谢大家谢谢大家谢谢大家谢谢大家谢谢大家!**
虽然此刻不得不放缓脚步,但这一切都是为了跳得更高。期待在重构完成后与各位共建更强大的版本!
千石可乐 敬上
2025年3月14日
</details>
# 麦麦MaiMBot (编辑中)
<div align="center">
@@ -37,12 +115,6 @@
> - 由于持续迭代可能存在一些已知或未知的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开发和建议相关讨论不一定有空回复会优先写文档和代码
**📚 有热心网友创作的wiki:** https://maimbot.pages.dev/
**📚 由SLAPQ制作的B站教程:** https://www.bilibili.com/opus/1041609335464001545
@@ -51,9 +123,17 @@
- (由 [CabLate](https://github.com/cablate) 贡献) [Telegram 与其他平台(未来可能会有)的版本](https://github.com/cablate/MaiMBot/tree/telegram) - [集中讨论串](https://github.com/SengokuCola/MaiMBot/discussions/149)
## 📝 注意注意注意注意注意注意注意注意注意注意注意注意注意注意注意注意注意
**如果你有想法想要提交pr**
- 由于本项目在快速迭代和功能调整并且有重构计划目前不接受任何未经过核心开发组讨论的pr合并谢谢如您仍旧希望提交pr可以详情请看置顶issue
## 如何给本项目报告BUG/提交建议/做贡献
MaiMBot是一个开源项目我们非常欢迎你的参与。你的贡献无论是提交bug报告、功能需求还是代码pr都对项目非常宝贵。我们非常感谢你的支持🎉 但无序的讨论会降低沟通效率,进而影响问题的解决速度,因此在提交任何贡献前,请务必先阅读本项目的[贡献指南](CONTRIBUTE.md)
### 💬交流群
- [一群](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开发和建议相关讨论不一定有空回复会优先写文档和代码
<div align="left">
<h2>📚 文档 ⬇️ 快速开始使用麦麦 ⬇️</h2>
@@ -148,23 +228,24 @@
## 设计理念
- **千石可乐说:**
- 这个项目最初只是为了给牛牛bot添加一点额外的功能但是功能越写越多最后决定重写。其目的是为了创造一个活跃在QQ群聊的"生命体"。可以目的并不是为了写一个功能齐全的机器人,而是一个尽可能让人感知到真实的类人存在.
- 程序的功能设计理念基于一个核心的原则:"最像而不是好"
- 主打一个陪伴
- 如果人类真的需要一个AI来陪伴自己并不是所有人都需要一个完美的能解决所有问题的helpful assistant而是一个会犯错的拥有自己感知和想法的"生命形式"。
- 代码会保持开源和开放但个人希望MaiMbot的运行时数据保持封闭尽量避免以显式命令来对其进行控制和调试.我认为一个你无法完全掌控的个体才更能让你感觉到它的自主性,而视其成为一个对话机器.
> **千石可乐说:**
> - 这个项目最初只是为了给牛牛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 协议端实现
### 贡献者

54
bot.py
View File

@@ -166,25 +166,57 @@ async def uvicorn_main():
await server.serve()
def check_eula():
eula_file = Path("elua.confirmed")
eula_confirm_file = Path("elua.confirmed")
privacy_confirm_file = Path("privacy.confirmed")
eula_file = Path("EULA.md")
privacy_file = Path("PRIVACY.md")
# 如果已经确认过EULA直接返回
if eula_file.exists():
return
eula_updated = True
privacy_updated = True
print("使用MaiMBot前请先阅读ELUA协议继续运行视为同意协议")
print("协议内容https://github.com/SengokuCola/MaiMBot/blob/main/EULA.md")
eula_confirmed = False
privacy_confirmed = False
# 检查EULA确认文件是否存在
if eula_confirm_file.exists():
# 检查EULA文件版本是否更新与elua.confirmed文件对比
with open(eula_file, "r") as f:
eula_content = f.read()
with open(eula_confirm_file, "r") as f:
confirmed_content = f.read()
if eula_content == confirmed_content:
eula_confirmed = True
eula_updated = False
# 检查隐私条款确认文件是否存在
if privacy_confirm_file.exists():
# 检查隐私条款文件版本是否更新与privacy.confirmed文件对比
with open(privacy_file, "r") as f:
privacy_content = f.read()
with open(privacy_confirm_file, "r") as f:
confirmed_content = f.read()
if privacy_content == confirmed_content:
privacy_confirmed = True
privacy_updated = False
# 如果EULA或隐私条款有更新提示用户重新确认
if eula_updated or privacy_updated:
print("EULA或隐私条款内容已更新请在阅读后重新确认继续运行视为同意更新后的以上两款协议")
print('输入"同意""confirmed"继续运行')
while True:
user_input = input().strip().lower() # 转换为小写以忽略大小写
user_input = input().strip().lower()
if user_input in ['同意', 'confirmed']:
# 创建确认文件
eula_file.touch()
if eula_updated:
eula_confirm_file.write_text(eula_file.read_text(encoding="utf-8"),encoding="utf-8")
if privacy_updated:
privacy_confirm_file.write_text(privacy_file.read_text(encoding="utf-8"),encoding="utf-8")
break
else:
print('请输入"同意""confirmed"以继续运行')
return
elif eula_confirmed and privacy_confirmed:
return
def raw_main():
# 利用 TZ 环境变量设定程序工作的时区

280
run.sh
View File

@@ -1,280 +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" "稳定版本(推荐,供下载使用)" \
"main-fix" "生产环境紧急修复" 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
}
# 运行安装步骤
whiptail --title "⚠️ 警告:安装前详阅" --msgbox "项目处于活跃开发阶段,代码可能随时更改\n文档未完善有问题可以提交 Issue 或者 Discussion\nQQ机器人存在被限制风险请自行了解谨慎使用\n由于持续迭代可能存在一些已知或未知的bug\n由于开发中可能消耗较多token\n\n本脚本可能更新不及时如遇到bug请优先尝试手动部署以确定是否为脚本问题" 14 60
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 --cli y --docker n
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 <<EOF | tee /etc/systemd/system/$SERVICE_NAME.service
[Unit]
Description=MaiMbot 麦麦
After=network.target mongod.service
[Service]
Type=simple
WorkingDirectory=$INSTALL_DIR/repo/
ExecStart=$INSTALL_DIR/venv/bin/python3 bot.py
ExecStop=/bin/kill -2 $MAINPID
Restart=always
RestartSec=10s
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable maimbot
systemctl start maimbot
whiptail --title "🎉 安装完成" --msgbox "麦麦Bot安装完成\n已经启动麦麦Bot服务。\n\n安装路径: $INSTALL_DIR\n分支: $BRANCH" 12 60

422
run_debian12.sh Normal file
View File

@@ -0,0 +1,422 @@
#!/bin/bash
# 麦麦Bot一键安装脚本 by Cookie_987
# 适用于Debian12
# 请小心使用任何一键脚本!
LANG=C.UTF-8
# 如无法访问GitHub请修改此处镜像地址
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-daemon"
SERVICE_NAME_WEB="maimbot-web"
IS_INSTALL_MONGODB=false
IS_INSTALL_NAPCAT=false
IS_INSTALL_DEPENDENCIES=false
INSTALLER_VERSION="0.0.1"
# 检查是否已安装
check_installed() {
[[ -f /etc/systemd/system/${SERVICE_NAME}.service ]]
}
# 加载安装信息
load_install_info() {
if [[ -f /etc/maimbot_install.conf ]]; then
source /etc/maimbot_install.conf
else
INSTALL_DIR="$DEFAULT_INSTALL_DIR"
BRANCH="main"
fi
}
# 显示管理菜单
show_menu() {
while true; do
choice=$(whiptail --title "麦麦Bot管理菜单" --menu "请选择要执行的操作:" 15 60 7 \
"1" "启动麦麦Bot" \
"2" "停止麦麦Bot" \
"3" "重启麦麦Bot" \
"4" "启动WebUI" \
"5" "停止WebUI" \
"6" "重启WebUI" \
"7" "更新麦麦Bot及其依赖" \
"8" "切换分支" \
"9" "更新配置文件" \
"10" "退出" 3>&1 1>&2 2>&3)
[[ $? -ne 0 ]] && exit 0
case "$choice" in
1)
systemctl start ${SERVICE_NAME}
whiptail --msgbox "✅麦麦Bot已启动" 10 60
;;
2)
systemctl stop ${SERVICE_NAME}
whiptail --msgbox "🛑麦麦Bot已停止" 10 60
;;
3)
systemctl restart ${SERVICE_NAME}
whiptail --msgbox "🔄麦麦Bot已重启" 10 60
;;
4)
systemctl start ${SERVICE_NAME_WEB}
whiptail --msgbox "✅WebUI已启动" 10 60
;;
5)
systemctl stop ${SERVICE_NAME_WEB}
whiptail --msgbox "🛑WebUI已停止" 10 60
;;
6)
systemctl restart ${SERVICE_NAME_WEB}
whiptail --msgbox "🔄WebUI已重启" 10 60
;;
7)
update_dependencies
;;
8)
switch_branch
;;
9)
update_config
;;
10)
exit 0
;;
*)
whiptail --msgbox "无效选项!" 10 60
;;
esac
done
}
# 更新依赖
update_dependencies() {
cd "${INSTALL_DIR}/repo" || {
whiptail --msgbox "🚫 无法进入安装目录!" 10 60
return 1
}
if ! git pull origin "${BRANCH}"; then
whiptail --msgbox "🚫 代码更新失败!" 10 60
return 1
fi
source "${INSTALL_DIR}/venv/bin/activate"
if ! pip install -r requirements.txt; then
whiptail --msgbox "🚫 依赖安装失败!" 10 60
deactivate
return 1
fi
deactivate
systemctl restart ${SERVICE_NAME}
whiptail --msgbox "✅ 依赖已更新并重启服务!" 10 60
}
# 切换分支
switch_branch() {
new_branch=$(whiptail --inputbox "请输入要切换的分支名称:" 10 60 "${BRANCH}" 3>&1 1>&2 2>&3)
[[ -z "$new_branch" ]] && {
whiptail --msgbox "🚫 分支名称不能为空!" 10 60
return 1
}
cd "${INSTALL_DIR}/repo" || {
whiptail --msgbox "🚫 无法进入安装目录!" 10 60
return 1
}
if ! git ls-remote --exit-code --heads origin "${new_branch}" >/dev/null 2>&1; then
whiptail --msgbox "🚫 分支 ${new_branch} 不存在!" 10 60
return 1
fi
if ! git checkout "${new_branch}"; then
whiptail --msgbox "🚫 分支切换失败!" 10 60
return 1
fi
if ! git pull origin "${new_branch}"; then
whiptail --msgbox "🚫 代码拉取失败!" 10 60
return 1
fi
source "${INSTALL_DIR}/venv/bin/activate"
pip install -r requirements.txt
deactivate
sed -i "s/^BRANCH=.*/BRANCH=${new_branch}/" /etc/maimbot_install.conf
BRANCH="${new_branch}"
systemctl restart ${SERVICE_NAME}
touch "${INSTALL_DIR}/repo/elua.confirmed"
whiptail --msgbox "✅ 已切换到分支 ${new_branch} 并重启服务!" 10 60
}
# 更新配置文件
update_config() {
cd "${INSTALL_DIR}/repo" || {
whiptail --msgbox "🚫 无法进入安装目录!" 10 60
return 1
}
if [[ -f config/bot_config.toml ]]; then
cp config/bot_config.toml config/bot_config.toml.bak
whiptail --msgbox "📁 原配置文件已备份为 bot_config.toml.bak" 10 60
source "${INSTALL_DIR}/venv/bin/activate"
python3 config/auto_update.py
deactivate
whiptail --msgbox "🆕 已更新配置文件请重启麦麦Bot" 10 60
return 0
else
whiptail --msgbox "🚫 未找到配置文件 bot_config.toml\n 请先运行一次麦麦Bot" 10 60
return 1
fi
}
# ----------- 主安装流程 -----------
run_installation() {
# 1/6: 检测是否安装 whiptail
if ! command -v whiptail &>/dev/null; then
echo -e "${RED}[1/6] whiptail 未安装,正在安装...${RESET}"
apt update && apt install -y whiptail
fi
# 协议确认
if ! (whiptail --title " [1/6] 使用协议" --yes-button "我同意" --no-button "我拒绝" --yesno "使用麦麦Bot及此脚本前请先阅读ELUA协议\nhttps://github.com/SengokuCola/MaiMBot/blob/main/EULA.md\n\n您是否同意此协议" 12 70); then
exit 1
fi
# 欢迎信息
whiptail --title "[2/6] 欢迎使用麦麦Bot一键安装脚本 by Cookie987" --msgbox "检测到您未安装麦麦Bot将自动进入安装流程安装完成后再次运行此脚本即可进入管理菜单。\n\n项目处于活跃开发阶段代码可能随时更改\n文档未完善有问题可以提交 Issue 或者 Discussion\nQQ机器人存在被限制风险请自行了解谨慎使用\n由于持续迭代可能存在一些已知或未知的bug\n由于开发中可能消耗较多token\n\n本脚本可能更新不及时如遇到bug请优先尝试手动部署以确定是否为脚本问题" 17 60
# 系统检查
check_system() {
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
}
check_system
# 检查MongoDB
check_mongodb() {
if command -v mongod &>/dev/null; then
MONGO_INSTALLED=true
else
MONGO_INSTALLED=false
fi
}
check_mongodb
# 检查NapCat
check_napcat() {
if command -v napcat &>/dev/null; then
NAPCAT_INSTALLED=true
else
NAPCAT_INSTALLED=false
fi
}
check_napcat
# 安装必要软件包
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
IS_INSTALL_DEPENDENCIES=true
else
whiptail --title "⚠️ 注意" --yesno "某些必要的依赖项未安装,可能会影响运行!\n是否继续" 10 60 || exit 1
fi
fi
}
install_packages
# 安装MongoDB
install_mongodb() {
[[ $MONGO_INSTALLED == true ]] && return
whiptail --title "📦 [3/6] 软件包检查" --yesno "检测到未安装MongoDB是否安装\n如果您想使用远程数据库请跳过此步。" 10 60 && {
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" | tee /etc/apt/sources.list.d/mongodb-org-8.0.list
apt update
apt install -y mongodb-org
systemctl enable --now mongod
IS_INSTALL_MONGODB=true
}
}
install_mongodb
# 安装NapCat
install_napcat() {
[[ $NAPCAT_INSTALLED == true ]] && return
whiptail --title "📦 [3/6] 软件包检查" --yesno "检测到未安装NapCat是否安装\n如果您想使用远程NapCat请跳过此步。" 10 60 && {
echo -e "${GREEN}安装 NapCat...${RESET}"
curl -o napcat.sh https://nclatest.znin.net/NapNeko/NapCat-Installer/main/script/install.sh && bash napcat.sh --cli y --docker n
IS_INSTALL_NAPCAT=true
}
}
install_napcat
# Python版本检查
check_python() {
PYTHON_VERSION=$(python3 -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")')
if ! python3 -c "import sys; exit(0) if sys.version_info >= (3,9) else exit(1)"; then
whiptail --title "⚠️ [4/6] Python 版本过低" --msgbox "检测到 Python 版本为 $PYTHON_VERSION,需要 3.9 或以上!\n请升级 Python 后重新运行本脚本。" 10 60
exit 1
fi
}
check_python
# 选择分支
choose_branch() {
BRANCH=$(whiptail --title "🔀 [5/6] 选择麦麦Bot分支" --menu "请选择要安装的麦麦Bot分支" 15 60 2 \
"main" "稳定版本(推荐,供下载使用)" \
"main-fix" "生产环境紧急修复" 3>&1 1>&2 2>&3)
[[ -z "$BRANCH" ]] && BRANCH="main"
}
choose_branch
# 选择安装路径
choose_install_dir() {
INSTALL_DIR=$(whiptail --title "📂 [6/6] 选择安装路径" --inputbox "请输入麦麦Bot的安装目录" 10 60 "$DEFAULT_INSTALL_DIR" 3>&1 1>&2 2>&3)
[[ -z "$INSTALL_DIR" ]] && {
whiptail --title "⚠️ 取消输入" --yesno "未输入安装路径,是否退出安装?" 10 60 && exit 1
INSTALL_DIR="$DEFAULT_INSTALL_DIR"
}
}
choose_install_dir
# 确认安装
confirm_install() {
local confirm_msg="请确认以下信息:\n\n"
confirm_msg+="📂 安装麦麦Bot到: $INSTALL_DIR\n"
confirm_msg+="🔀 分支: $BRANCH\n"
[[ $IS_INSTALL_DEPENDENCIES == true ]] && confirm_msg+="📦 安装依赖:${missing_packages}\n"
[[ $IS_INSTALL_MONGODB == true || $IS_INSTALL_NAPCAT == true ]] && confirm_msg+="📦 安装额外组件:\n"
[[ $IS_INSTALL_MONGODB == true ]] && confirm_msg+=" - MongoDB\n"
[[ $IS_INSTALL_NAPCAT == true ]] && confirm_msg+=" - NapCat\n"
confirm_msg+="\n注意本脚本默认使用ghfast.top为GitHub进行加速如不想使用请手动修改脚本开头的GITHUB_REPO变量。"
whiptail --title "🔧 安装确认" --yesno "$confirm_msg" 16 60 || exit 1
}
confirm_install
# 开始安装
echo -e "${GREEN}安装依赖...${RESET}"
[[ $IS_INSTALL_DEPENDENCIES == true ]] && apt update && apt install -y "${missing_packages[@]}"
echo -e "${GREEN}创建安装目录...${RESET}"
mkdir -p "$INSTALL_DIR"
cd "$INSTALL_DIR" || exit 1
echo -e "${GREEN}设置Python虚拟环境...${RESET}"
python3 -m venv venv
source venv/bin/activate
echo -e "${GREEN}克隆仓库...${RESET}"
git clone -b "$BRANCH" "$GITHUB_REPO" repo || {
echo -e "${RED}克隆仓库失败!${RESET}"
exit 1
}
echo -e "${GREEN}安装Python依赖...${RESET}"
pip install -r repo/requirements.txt
echo -e "${GREEN}同意协议...${RESET}"
touch repo/elua.confirmed
echo -e "${GREEN}创建系统服务...${RESET}"
cat > /etc/systemd/system/${SERVICE_NAME}.service <<EOF
[Unit]
Description=麦麦Bot 主进程
After=network.target mongod.service
[Service]
Type=simple
WorkingDirectory=${INSTALL_DIR}/repo
ExecStart=$INSTALL_DIR/venv/bin/python3 bot.py
Restart=always
RestartSec=10s
[Install]
WantedBy=multi-user.target
EOF
cat > /etc/systemd/system/${SERVICE_NAME_WEB}.service <<EOF
[Unit]
Description=麦麦Bot WebUI
After=network.target mongod.service ${SERVICE_NAME}.service
[Service]
Type=simple
WorkingDirectory=${INSTALL_DIR}/repo
ExecStart=$INSTALL_DIR/venv/bin/python3 webui.py
Restart=always
RestartSec=10s
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable ${SERVICE_NAME}
# 保存安装信息
echo "INSTALLER_VERSION=${INSTALLER_VERSION}" > /etc/maimbot_install.conf
echo "INSTALL_DIR=${INSTALL_DIR}" >> /etc/maimbot_install.conf
echo "BRANCH=${BRANCH}" >> /etc/maimbot_install.conf
whiptail --title "🎉 安装完成" --msgbox "麦麦Bot安装完成\n已创建系统服务${SERVICE_NAME}${SERVICE_NAME_WEB}\n\n使用以下命令管理服务\n启动服务systemctl start ${SERVICE_NAME}\n查看状态systemctl status ${SERVICE_NAME}" 14 60
}
# ----------- 主执行流程 -----------
# 检查root权限
[[ $(id -u) -ne 0 ]] && {
echo -e "${RED}请使用root用户运行此脚本${RESET}"
exit 1
}
# 如果已安装显示菜单
if check_installed; then
load_install_info
show_menu
else
run_installation
# 安装完成后询问是否启动
if whiptail --title "安装完成" --yesno "是否立即启动麦麦Bot服务" 10 60; then
systemctl start ${SERVICE_NAME}
whiptail --msgbox "✅ 服务已启动!\n使用 systemctl status ${SERVICE_NAME} 查看状态" 10 60
fi
fi

View File

@@ -0,0 +1,319 @@
# -*- coding: utf-8 -*-
import os
import sys
import time
from pathlib import Path
import datetime
from rich.console import Console
from dotenv import load_dotenv
'''
我想 总有那么一个瞬间
你会想和某天才变态少女助手一样
往Bot的海马体里插上几个电极 不是吗
Let's do some dirty job.
'''
# 获取当前文件的目录
current_dir = Path(__file__).resolve().parent
# 获取项目根目录(上三层目录)
project_root = current_dir.parent.parent.parent
# env.dev文件路径
env_path = project_root / ".env.dev"
# from chat.config import global_config
root_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../.."))
sys.path.append(root_path)
from src.common.logger import get_module_logger
from src.common.database import db
from src.plugins.memory_system.offline_llm import LLMModel
logger = get_module_logger('mem_alter')
console = Console()
# 加载环境变量
if env_path.exists():
logger.info(f"{env_path} 加载环境变量")
load_dotenv(env_path)
else:
logger.warning(f"未找到环境变量文件: {env_path}")
logger.info("将使用默认配置")
from memory_manual_build import Memory_graph, Hippocampus #海马体和记忆图
# 查询节点信息
def query_mem_info(memory_graph: Memory_graph):
while True:
query = input("\n请输入新的查询概念(输入'退出'以结束):")
if query.lower() == '退出':
break
items_list = memory_graph.get_related_item(query)
if items_list:
have_memory = False
first_layer, second_layer = items_list
if first_layer:
have_memory = True
print("\n直接相关的记忆:")
for item in first_layer:
print(f"- {item}")
if second_layer:
have_memory = True
print("\n间接相关的记忆:")
for item in second_layer:
print(f"- {item}")
if not have_memory:
print("\n未找到相关记忆。")
else:
print("未找到相关记忆。")
# 增加概念节点
def add_mem_node(hippocampus: Hippocampus):
while True:
concept = input("请输入节点概念名:\n")
result = db.graph_data.nodes.count_documents({'concept': concept})
if result != 0:
console.print("[yellow]已存在名为“{concept}”的节点,行为已取消[/yellow]")
continue
memory_items = list()
while True:
context = input("请输入节点描述信息(输入'终止'以结束)")
if context.lower() == "终止": break
memory_items.append(context)
current_time = datetime.datetime.now().timestamp()
hippocampus.memory_graph.G.add_node(concept,
memory_items=memory_items,
created_time=current_time,
last_modified=current_time)
# 删除概念节点(及连接到它的边)
def remove_mem_node(hippocampus: Hippocampus):
concept = input("请输入节点概念名:\n")
result = db.graph_data.nodes.count_documents({'concept': concept})
if result == 0:
console.print(f"[red]不存在名为“{concept}”的节点[/red]")
edges = db.graph_data.edges.find({
'$or': [
{'source': concept},
{'target': concept}
]
})
for edge in edges:
console.print(f"[yellow]存在边“{edge['source']} -> {edge['target']}”, 请慎重考虑[/yellow]")
console.print(f"[yellow]确定要移除名为“{concept}”的节点以及其相关边吗[/yellow]")
destory = console.input(f"[red]请输入“{concept}”以删除节点 其他输入将被视为取消操作[/red]\n")
if destory == concept:
hippocampus.memory_graph.G.remove_node(concept)
else:
logger.info("[green]删除操作已取消[/green]")
# 增加节点间边
def add_mem_edge(hippocampus: Hippocampus):
while True:
source = input("请输入 **第一个节点** 名称(输入'退出'以结束):\n")
if source.lower() == "退出": break
if db.graph_data.nodes.count_documents({'concept': source}) == 0:
console.print(f"[yellow]“{source}”节点不存在,操作已取消。[/yellow]")
continue
target = input("请输入 **第二个节点** 名称:\n")
if db.graph_data.nodes.count_documents({'concept': target}) == 0:
console.print(f"[yellow]“{target}”节点不存在,操作已取消。[/yellow]")
continue
if source == target:
console.print(f"[yellow]试图创建“{source} <-> {target}”自环,操作已取消。[/yellow]")
continue
hippocampus.memory_graph.connect_dot(source, target)
edge = hippocampus.memory_graph.G.get_edge_data(source, target)
if edge['strength'] == 1:
console.print(f"[green]成功创建边“{source} <-> {target}默认权重1[/green]")
else:
console.print(f"[yellow]边“{source} <-> {target}”已存在,更新权重: {edge['strength']-1} <-> {edge['strength']}[/yellow]")
# 删除节点间边
def remove_mem_edge(hippocampus: Hippocampus):
while True:
source = input("请输入 **第一个节点** 名称(输入'退出'以结束):\n")
if source.lower() == "退出": break
if db.graph_data.nodes.count_documents({'concept': source}) == 0:
console.print("[yellow]“{source}”节点不存在,操作已取消。[/yellow]")
continue
target = input("请输入 **第二个节点** 名称:\n")
if db.graph_data.nodes.count_documents({'concept': target}) == 0:
console.print("[yellow]“{target}”节点不存在,操作已取消。[/yellow]")
continue
if source == target:
console.print("[yellow]试图创建“{source} <-> {target}”自环,操作已取消。[/yellow]")
continue
edge = hippocampus.memory_graph.G.get_edge_data(source, target)
if edge is None:
console.print("[yellow]边“{source} <-> {target}”不存在,操作已取消。[/yellow]")
continue
else:
accept = console.input("[orange]请输入“确认”以确认删除操作(其他输入视为取消)[/orange]\n")
if accept.lower() == "确认":
hippocampus.memory_graph.G.remove_edge(source, target)
console.print(f"[green]边“{source} <-> {target}”已删除。[green]")
# 修改节点信息
def alter_mem_node(hippocampus: Hippocampus):
batchEnviroment = dict()
while True:
concept = input("请输入节点概念名(输入'终止'以结束):\n")
if concept.lower() == "终止": break
_, node = hippocampus.memory_graph.get_dot(concept)
if node is None:
console.print(f"[yellow]“{concept}”节点不存在,操作已取消。[/yellow]")
continue
console.print("[yellow]注意,请确保你知道自己在做什么[/yellow]")
console.print("[yellow]你将获得一个执行任意代码的环境[/yellow]")
console.print("[red]你已经被警告过了。[/red]\n")
nodeEnviroment = {"concept": '<节点名>', 'memory_items': '<记忆文本数组>'}
console.print("[green]环境变量中会有env与batchEnv两个dict, env在切换节点时会清空, batchEnv在操作终止时才会清空[/green]")
console.print(f"[green] env 会被初始化为[/green]\n{nodeEnviroment}\n[green]且会在用户代码执行完毕后被提交 [/green]")
console.print("[yellow]为便于书写临时脚本请手动在输入代码通过Ctrl+C等方式触发KeyboardInterrupt来结束代码执行[/yellow]")
# 拷贝数据以防操作炸了
nodeEnviroment = dict(node)
nodeEnviroment['concept'] = concept
while True:
userexec = lambda script, env, batchEnv: eval(script)
try:
command = console.input()
except KeyboardInterrupt:
# 稍微防一下小天才
try:
if isinstance(nodeEnviroment['memory_items'], list):
node['memory_items'] = nodeEnviroment['memory_items']
else:
raise Exception
except:
console.print("[red]我不知道你做了什么但显然nodeEnviroment['memory_items']已经不是个数组了,操作已取消[/red]")
break
try:
userexec(command, nodeEnviroment, batchEnviroment)
except Exception as e:
console.print(e)
console.print("[red]自定义代码执行时发生异常,已捕获,请重试(可通过 console.print(locals()) 检查环境状态)[/red]")
# 修改边信息
def alter_mem_edge(hippocampus: Hippocampus):
batchEnviroment = dict()
while True:
source = input("请输入 **第一个节点** 名称(输入'终止'以结束):\n")
if source.lower() == "终止": break
if hippocampus.memory_graph.get_dot(source) is None:
console.print(f"[yellow]“{source}”节点不存在,操作已取消。[/yellow]")
continue
target = input("请输入 **第二个节点** 名称:\n")
if hippocampus.memory_graph.get_dot(target) is None:
console.print(f"[yellow]“{target}”节点不存在,操作已取消。[/yellow]")
continue
edge = hippocampus.memory_graph.G.get_edge_data(source, target)
if edge is None:
console.print(f"[yellow]边“{source} <-> {target}”不存在,操作已取消。[/yellow]")
continue
console.print("[yellow]注意,请确保你知道自己在做什么[/yellow]")
console.print("[yellow]你将获得一个执行任意代码的环境[/yellow]")
console.print("[red]你已经被警告过了。[/red]\n")
edgeEnviroment = {"source": '<节点名>', "target": '<节点名>', 'strength': '<强度值,装在一个list里>'}
console.print("[green]环境变量中会有env与batchEnv两个dict, env在切换节点时会清空, batchEnv在操作终止时才会清空[/green]")
console.print(f"[green] env 会被初始化为[/green]\n{edgeEnviroment}\n[green]且会在用户代码执行完毕后被提交 [/green]")
console.print("[yellow]为便于书写临时脚本请手动在输入代码通过Ctrl+C等方式触发KeyboardInterrupt来结束代码执行[/yellow]")
# 拷贝数据以防操作炸了
edgeEnviroment['strength'] = [edge["strength"]]
edgeEnviroment['source'] = source
edgeEnviroment['target'] = target
while True:
userexec = lambda script, env, batchEnv: eval(script)
try:
command = console.input()
except KeyboardInterrupt:
# 稍微防一下小天才
try:
if isinstance(edgeEnviroment['strength'][0], int):
edge['strength'] = edgeEnviroment['strength'][0]
else:
raise Exception
except:
console.print("[red]我不知道你做了什么但显然edgeEnviroment['strength']已经不是个int了操作已取消[/red]")
break
try:
userexec(command, edgeEnviroment, batchEnviroment)
except Exception as e:
console.print(e)
console.print("[red]自定义代码执行时发生异常,已捕获,请重试(可通过 console.print(locals()) 检查环境状态)[/red]")
async def main():
start_time = time.time()
# 创建记忆图
memory_graph = Memory_graph()
# 创建海马体
hippocampus = Hippocampus(memory_graph)
# 从数据库同步数据
hippocampus.sync_memory_from_db()
end_time = time.time()
logger.info(f"\033[32m[加载海马体耗时: {end_time - start_time:.2f} 秒]\033[0m")
while True:
try:
query = int(input("请输入操作类型\n0 -> 查询节点; 1 -> 增加节点; 2 -> 移除节点; 3 -> 增加边; 4 -> 移除边;\n5 -> 修改节点; 6 -> 修改边; 其他任意输入 -> 退出\n"))
except:
query = -1
if query == 0:
query_mem_info(memory_graph)
elif query == 1:
add_mem_node(hippocampus)
elif query == 2:
remove_mem_node(hippocampus)
elif query == 3:
add_mem_edge(hippocampus)
elif query == 4:
remove_mem_edge(hippocampus)
elif query == 5:
alter_mem_node(hippocampus)
elif query == 6:
alter_mem_edge(hippocampus)
else:
print("已结束操作")
break
hippocampus.sync_memory_to_db()
if __name__ == "__main__":
import asyncio
asyncio.run(main())

View File

@@ -50,7 +50,11 @@ class LLMStatistics:
"total_cost": 0.0,
"costs_by_user": defaultdict(float),
"costs_by_type": defaultdict(float),
"costs_by_model": defaultdict(float)
"costs_by_model": defaultdict(float),
#新增token统计字段
"tokens_by_type": defaultdict(int),
"tokens_by_user": defaultdict(int),
"tokens_by_model": defaultdict(int),
}
cursor = db.llm_usage.find({
@@ -71,7 +75,11 @@ class LLMStatistics:
prompt_tokens = doc.get("prompt_tokens", 0)
completion_tokens = doc.get("completion_tokens", 0)
stats["total_tokens"] += prompt_tokens + completion_tokens
total_tokens = prompt_tokens + completion_tokens # 根据数据库字段调整
stats["tokens_by_type"][request_type] += total_tokens
stats["tokens_by_user"][user_id] += total_tokens
stats["tokens_by_model"][model_name] += total_tokens
stats["total_tokens"] += total_tokens
cost = doc.get("cost", 0.0)
stats["total_cost"] += cost
@@ -98,30 +106,60 @@ class LLMStatistics:
}
def _format_stats_section(self, stats: Dict[str, Any], title: str) -> str:
"""格式化统计部分的输出
Args:
stats: 统计数据
title: 部分标题
"""
"""格式化统计部分的输出"""
output = []
output.append(f"\n{title}")
output.append("=" * len(title))
output.append("\n"+"-" * 84)
output.append(f"{title}")
output.append("-" * 84)
output.append(f"总请求数: {stats['total_requests']}")
if stats['total_requests'] > 0:
output.append(f"总Token数: {stats['total_tokens']}")
output.append(f"总花费: ¥{stats['total_cost']:.4f}")
output.append(f"总花费: {stats['total_cost']:.4f}¥\n")
output.append("\n按模型统计:")
data_fmt = "{:<32} {:>10} {:>14} {:>13.4f} ¥"
# 按模型统计
output.append("按模型统计:")
output.append(("模型名称 调用次数 Token总量 累计花费"))
for model_name, count in sorted(stats["requests_by_model"].items()):
tokens = stats["tokens_by_model"][model_name]
cost = stats["costs_by_model"][model_name]
output.append(f"- {model_name}: {count}次 (花费: ¥{cost:.4f})")
output.append(data_fmt.format(
model_name[:32] + ".." if len(model_name) > 32 else model_name,
count,
tokens,
cost
))
output.append("")
output.append("\n按请求类型统计:")
# 按请求类型统计
output.append("按请求类型统计:")
output.append(("模型名称 调用次数 Token总量 累计花费"))
for req_type, count in sorted(stats["requests_by_type"].items()):
tokens = stats["tokens_by_type"][req_type]
cost = stats["costs_by_type"][req_type]
output.append(f"- {req_type}: {count}次 (花费: ¥{cost:.4f})")
output.append(data_fmt.format(
req_type[:22] + ".." if len(req_type) > 24 else req_type,
count,
tokens,
cost
))
output.append("")
# 修正用户统计列宽
output.append("按用户统计:")
output.append(("模型名称 调用次数 Token总量 累计花费"))
for user_id, count in sorted(stats["requests_by_user"].items()):
tokens = stats["tokens_by_user"][user_id]
cost = stats["costs_by_user"][user_id]
output.append(data_fmt.format(
user_id[:22], # 不再添加省略号保持原始ID
count,
tokens,
cost
))
return "\n".join(output)
@@ -131,7 +169,7 @@ class LLMStatistics:
output = []
output.append(f"LLM请求统计报告 (生成时间: {current_time})")
output.append("=" * 50)
# 添加各个时间段的统计
sections = [