396 lines
9.8 KiB
Markdown
396 lines
9.8 KiB
Markdown
# 🔧 插件开发故障排除指南
|
||
|
||
本指南帮助你快速解决 MoFox-Bot 插件开发中的常见问题。
|
||
|
||
---
|
||
|
||
## 📋 快速诊断清单
|
||
|
||
遇到问题时,首先按照以下步骤检查:
|
||
|
||
1. ✅ 检查日志文件 `logs/app_*.jsonl`
|
||
2. ✅ 确认插件已在 `_manifest.json` 中正确配置
|
||
3. ✅ 验证你使用的是 `PlusCommand` 而不是 `BaseCommand`
|
||
4. ✅ 检查 `execute()` 方法签名是否正确
|
||
5. ✅ 确认返回值格式正确
|
||
|
||
---
|
||
|
||
## 🔴 严重问题:插件无法加载
|
||
|
||
### 错误 #1: "未检测到插件"
|
||
|
||
**症状**:
|
||
- 插件目录存在,但日志中没有加载信息
|
||
- `get_plugin_components()` 返回空列表
|
||
|
||
**可能原因与解决方案**:
|
||
|
||
#### ❌ 缺少 `@register_plugin` 装饰器
|
||
|
||
```python
|
||
# 错误 - 缺少装饰器
|
||
class MyPlugin(BasePlugin): # 不会被检测到
|
||
pass
|
||
|
||
# 正确 - 添加装饰器
|
||
@register_plugin # 必须添加!
|
||
class MyPlugin(BasePlugin):
|
||
pass
|
||
```
|
||
|
||
#### ❌ `plugin.py` 文件不存在或位置错误
|
||
|
||
```
|
||
plugins/
|
||
└── my_plugin/
|
||
├── _manifest.json ✅
|
||
└── plugin.py ✅ 必须在这里
|
||
```
|
||
|
||
#### ❌ `_manifest.json` 格式错误
|
||
|
||
```json
|
||
{
|
||
"manifest_version": 1,
|
||
"name": "My Plugin",
|
||
"version": "1.0.0",
|
||
"description": "插件描述",
|
||
"author": {
|
||
"name": "Your Name"
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 错误 #2: "ActionInfo.__init__() missing required argument: 'component_type'"
|
||
|
||
**症状**:
|
||
```
|
||
TypeError: ActionInfo.__init__() missing 1 required positional argument: 'component_type'
|
||
```
|
||
|
||
**原因**:手动创建 `ActionInfo` 时未指定 `component_type` 参数
|
||
|
||
**解决方案**:
|
||
|
||
```python
|
||
from src.plugin_system import ActionInfo, ComponentType
|
||
|
||
# ❌ 错误 - 缺少 component_type
|
||
action_info = ActionInfo(
|
||
name="my_action",
|
||
description="我的动作"
|
||
)
|
||
|
||
# ✅ 正确方法 1 - 使用自动生成(推荐)
|
||
class MyAction(BaseAction):
|
||
action_name = "my_action"
|
||
action_description = "我的动作"
|
||
|
||
def get_plugin_components(self):
|
||
return [
|
||
(MyAction.get_action_info(), MyAction) # 自动生成,推荐!
|
||
]
|
||
|
||
# ✅ 正确方法 2 - 手动指定 component_type
|
||
action_info = ActionInfo(
|
||
name="my_action",
|
||
description="我的动作",
|
||
component_type=ComponentType.ACTION # 必须指定!
|
||
)
|
||
```
|
||
|
||
---
|
||
|
||
## 🟡 命令问题:命令无响应
|
||
|
||
### 错误 #3: 命令被识别但不执行
|
||
|
||
**症状**:
|
||
- 输入 `/mycommand` 后没有任何反应
|
||
- 日志显示命令已匹配但未执行
|
||
|
||
**可能原因与解决方案**:
|
||
|
||
#### ❌ 使用了 `BaseCommand` 而不是 `PlusCommand`
|
||
|
||
```python
|
||
# ❌ 错误 - 使用 BaseCommand
|
||
from src.plugin_system import BaseCommand
|
||
|
||
class MyCommand(BaseCommand): # 不推荐!
|
||
command_name = "mycommand"
|
||
command_pattern = r"^/mycommand$" # 需要手动写正则
|
||
|
||
async def execute(self): # 签名错误!
|
||
pass
|
||
|
||
# ✅ 正确 - 使用 PlusCommand
|
||
from src.plugin_system import PlusCommand, CommandArgs
|
||
|
||
class MyCommand(PlusCommand): # 推荐!
|
||
command_name = "mycommand"
|
||
# 不需要 command_pattern,会自动生成!
|
||
|
||
async def execute(self, args: CommandArgs): # 正确签名
|
||
await self.send_text("命令执行成功")
|
||
return True, "执行了mycommand", True
|
||
```
|
||
|
||
#### ❌ `execute()` 方法签名错误
|
||
|
||
```python
|
||
# ❌ 错误的签名(缺少 args 参数)
|
||
async def execute(self) -> Tuple[bool, Optional[str], bool]:
|
||
pass
|
||
|
||
# ❌ 错误的签名(参数类型错误)
|
||
async def execute(self, args: list[str]) -> Tuple[bool, Optional[str], bool]:
|
||
pass
|
||
|
||
# ✅ 正确的签名
|
||
async def execute(self, args: CommandArgs) -> Tuple[bool, Optional[str], bool]:
|
||
await self.send_text("响应用户")
|
||
return True, "日志描述", True
|
||
```
|
||
|
||
---
|
||
|
||
### 错误 #4: 命令发送了消息但用户没收到
|
||
|
||
**症状**:
|
||
- 日志显示命令执行成功
|
||
- 但用户没有收到任何消息
|
||
|
||
**原因**:在返回值中返回消息,而不是使用 `self.send_text()`
|
||
|
||
**解决方案**:
|
||
|
||
```python
|
||
# ❌ 错误 - 在返回值中返回消息
|
||
async def execute(self, args: CommandArgs):
|
||
message = "这是给用户的消息"
|
||
return True, message, True # 这不会发送给用户!
|
||
|
||
# ✅ 正确 - 使用 self.send_text()
|
||
async def execute(self, args: CommandArgs):
|
||
# 发送消息给用户
|
||
await self.send_text("这是给用户的消息")
|
||
|
||
# 返回日志描述(不是用户消息)
|
||
return True, "执行了某个操作", True
|
||
```
|
||
|
||
---
|
||
|
||
### 错误 #5: "notice处理失败" 或重复消息
|
||
|
||
**症状**:
|
||
- 日志中出现 "notice处理失败"
|
||
- 用户收到重复的消息
|
||
|
||
**原因**:同时使用了 `send_api.send_text()` 和返回消息
|
||
|
||
**解决方案**:
|
||
|
||
```python
|
||
# ❌ 错误 - 混用不同的发送方式
|
||
from src.plugin_system.apis.chat_api import send_api
|
||
|
||
async def execute(self, args: CommandArgs):
|
||
await send_api.send_text(self.stream_id, "消息1") # 不要这样做
|
||
return True, "消息2", True # 也不要返回消息
|
||
|
||
# ✅ 正确 - 只使用 self.send_text()
|
||
async def execute(self, args: CommandArgs):
|
||
await self.send_text("这是唯一的消息") # 推荐方式
|
||
return True, "日志:执行成功", True # 仅用于日志
|
||
```
|
||
|
||
---
|
||
|
||
## 🟢 配置问题
|
||
|
||
### 错误 #6: 配置警告 "配置中不存在字空间或键"
|
||
|
||
**症状**:
|
||
```
|
||
获取全局配置 plugins.my_plugin 失败: "配置中不存在字空间或键 'plugins'"
|
||
```
|
||
|
||
**这是正常的吗?**
|
||
|
||
✅ **是的,这是正常行为!** 不需要修复。
|
||
|
||
**说明**:
|
||
- 系统首先尝试从全局配置加载:`config/plugins/my_plugin/config.toml`
|
||
- 如果不存在,会自动回退到插件本地配置:`plugins/my_plugin/config.toml`
|
||
- 这个警告可以安全忽略
|
||
|
||
**如果你想消除警告**:
|
||
1. 在 `config/plugins/` 目录创建你的插件配置目录
|
||
2. 或者直接忽略 - 使用本地配置完全正常
|
||
|
||
---
|
||
|
||
## 🔧 返回值问题
|
||
|
||
### 错误 #7: 返回值格式错误
|
||
|
||
**Action 返回值** (2个元素):
|
||
```python
|
||
async def execute(self) -> Tuple[bool, str]:
|
||
await self.send_text("消息")
|
||
return True, "日志描述" # 2个元素
|
||
```
|
||
|
||
**Command 返回值** (3个元素):
|
||
```python
|
||
async def execute(self, args: CommandArgs) -> Tuple[bool, Optional[str], bool]:
|
||
await self.send_text("消息")
|
||
return True, "日志描述", True # 3个元素(增加了拦截标志)
|
||
```
|
||
|
||
**对比表格**:
|
||
|
||
| 组件类型 | 返回值 | 元素说明 |
|
||
|----------|--------|----------|
|
||
| **Action** | `(bool, str)` | (成功标志, 日志描述) |
|
||
| **Command** | `(bool, str, bool)` | (成功标志, 日志描述, 拦截标志) |
|
||
|
||
---
|
||
|
||
## 🎯 参数解析问题
|
||
|
||
### 错误 #8: 无法获取命令参数
|
||
|
||
**症状**:
|
||
- `args` 为空或不包含预期的参数
|
||
|
||
**解决方案**:
|
||
|
||
```python
|
||
async def execute(self, args: CommandArgs):
|
||
# 检查是否有参数
|
||
if args.is_empty():
|
||
await self.send_text("❌ 缺少参数\n用法: /command <参数>")
|
||
return True, "缺少参数", True
|
||
|
||
# 获取原始参数字符串
|
||
raw_input = args.get_raw()
|
||
|
||
# 获取解析后的参数列表
|
||
arg_list = args.get_args()
|
||
|
||
# 获取第一个参数
|
||
first_arg = args.get_first("默认值")
|
||
|
||
# 获取指定索引的参数
|
||
second_arg = args.get_arg(1, "默认值")
|
||
|
||
# 检查标志
|
||
if args.has_flag("--verbose"):
|
||
# 处理 --verbose 模式
|
||
pass
|
||
|
||
# 获取标志的值
|
||
output = args.get_flag_value("--output", "default.txt")
|
||
```
|
||
|
||
---
|
||
|
||
## 📝 类型注解问题
|
||
|
||
### 错误 #9: IDE 报类型错误
|
||
|
||
**解决方案**:确保使用正确的类型导入
|
||
|
||
```python
|
||
from typing import Tuple, Optional, List, Type
|
||
from src.plugin_system import (
|
||
BasePlugin,
|
||
PlusCommand,
|
||
BaseAction,
|
||
CommandArgs,
|
||
ComponentInfo,
|
||
CommandInfo,
|
||
ActionInfo,
|
||
ComponentType
|
||
)
|
||
|
||
# 正确的类型注解
|
||
def get_plugin_components(self) -> List[Tuple[ComponentInfo, Type]]:
|
||
return [
|
||
(MyCommand.get_command_info(), MyCommand),
|
||
(MyAction.get_action_info(), MyAction)
|
||
]
|
||
```
|
||
|
||
---
|
||
|
||
## 🚀 性能问题
|
||
|
||
### 错误 #10: 插件响应缓慢
|
||
|
||
**可能原因**:
|
||
|
||
1. **阻塞操作**:在 `execute()` 中使用了同步 I/O
|
||
2. **大量数据处理**:在主线程处理大文件或复杂计算
|
||
3. **频繁的数据库查询**:每次都查询数据库
|
||
|
||
**解决方案**:
|
||
|
||
```python
|
||
import asyncio
|
||
|
||
async def execute(self, args: CommandArgs):
|
||
# ✅ 使用异步操作
|
||
result = await some_async_function()
|
||
|
||
# ✅ 对于同步操作,使用 asyncio.to_thread
|
||
result = await asyncio.to_thread(blocking_function)
|
||
|
||
# ✅ 批量数据库操作
|
||
from src.common.database.optimization.batch_scheduler import get_batch_scheduler
|
||
scheduler = get_batch_scheduler()
|
||
await scheduler.schedule_batch_insert(Model, data_list)
|
||
|
||
return True, "执行成功", True
|
||
```
|
||
|
||
---
|
||
|
||
## 📞 获取帮助
|
||
|
||
如果以上方案都无法解决你的问题:
|
||
|
||
1. **查看日志**:检查 `logs/app_*.jsonl` 获取详细错误信息
|
||
2. **查阅文档**:
|
||
- [快速开始指南](./quick-start.md)
|
||
- [增强命令指南](./PLUS_COMMAND_GUIDE.md)
|
||
- [Action组件指南](./action-components.md)
|
||
3. **在线文档**:https://mofox-studio.github.io/MoFox-Bot-Docs/
|
||
4. **提交 Issue**:在 GitHub 仓库提交详细的问题报告
|
||
|
||
---
|
||
|
||
## 🎓 最佳实践速查
|
||
|
||
| 场景 | 推荐做法 | 避免 |
|
||
|------|----------|------|
|
||
| 创建命令 | 使用 `PlusCommand` | ❌ 使用 `BaseCommand` |
|
||
| 发送消息 | `await self.send_text()` | ❌ 在返回值中返回消息 |
|
||
| 注册组件 | 使用 `get_action_info()` | ❌ 手动创建不带 `component_type` 的 Info |
|
||
| 参数处理 | 使用 `CommandArgs` 方法 | ❌ 手动解析字符串 |
|
||
| 异步操作 | 使用 `async/await` | ❌ 使用同步阻塞操作 |
|
||
| 配置读取 | `self.get_config()` | ❌ 硬编码配置值 |
|
||
|
||
---
|
||
|
||
**最后更新**:2024-12-17
|
||
**版本**:v1.0.0
|
||
|
||
有问题欢迎反馈,帮助我们改进这份指南!
|