refc:重构插件api,补全文档,合并expressor和replyer,分离reply和sender,新log浏览器
This commit is contained in:
158
docs/plugins/README.md
Normal file
158
docs/plugins/README.md
Normal file
@@ -0,0 +1,158 @@
|
||||
# MaiBot插件开发文档
|
||||
|
||||
> 欢迎来到MaiBot插件系统开发文档!这里是你开始插件开发旅程的最佳起点。
|
||||
|
||||
## 🎯 快速导航
|
||||
|
||||
### 🌟 新手入门
|
||||
|
||||
- [📖 快速开始指南](quick-start.md) - 5分钟创建你的第一个插件
|
||||
- [🧱 Action组件详解](action-components.md) - 掌握最核心的Action组件
|
||||
- [💻 Command组件详解](command-components.md) - 学习直接响应命令的组件
|
||||
- [⚙️ 配置管理指南](configuration-guide.md) - 学会使用配置驱动开发
|
||||
|
||||
### 📖 API参考
|
||||
|
||||
- [📡 消息API](api/message-api.md) - 消息发送接口
|
||||
|
||||
### 🔧 高级主题
|
||||
|
||||
- [📦 依赖管理系统](dependency-management.md) - Python包依赖管理
|
||||
- [🔧 工具系统详解](tool-system.md) - 工具系统的使用和开发
|
||||
|
||||
## 🔥 最新更新 (v2.0 新API格式)
|
||||
|
||||
### 🎉 重大变更
|
||||
|
||||
1. **新API格式**:
|
||||
- 不再使用 `self.api`,改为直接方法调用
|
||||
- `await self.send_text()` 替代旧的发送方式
|
||||
- `await self.send_emoji()` 专门的表情发送方法
|
||||
- `self.get_config()` 简化的配置访问
|
||||
|
||||
2. **replyer_1集成**:
|
||||
- 新增专用的 `generator_api` 模块
|
||||
- 在Action中直接使用 `replyer_1` 生成个性化内容
|
||||
- 支持多种生成风格和情感色彩
|
||||
|
||||
3. **更好的类型安全**:
|
||||
- 完整的类型注解支持
|
||||
- 更清晰的返回值类型
|
||||
- 更好的IDE支持
|
||||
|
||||
## 🚀 最佳学习路径
|
||||
|
||||
### 📚 初学者路径(推荐)
|
||||
|
||||
1. **基础入门**:
|
||||
```
|
||||
快速开始指南 → Action组件详解 → Command组件详解
|
||||
```
|
||||
|
||||
2. **API掌握**:
|
||||
```
|
||||
消息API指南 → 配置管理指南
|
||||
```
|
||||
|
||||
3. **高级功能**:
|
||||
```
|
||||
依赖管理系统 → 工具系统详解
|
||||
```
|
||||
|
||||
## 💡 核心概念速览
|
||||
|
||||
### 🧱 Action组件
|
||||
|
||||
- **用途**:增强麦麦的主动行为,让对话更自然
|
||||
- **激活**:关键词、LLM判断、随机等多种方式
|
||||
- **新特性**:支持replyer_1智能生成、更简洁的API
|
||||
|
||||
### 💻 Command组件
|
||||
|
||||
- **用途**:响应用户的明确指令,提供确定性功能
|
||||
- **触发**:正则表达式匹配用户输入
|
||||
- **特点**:即时响应、参数解析、拦截控制
|
||||
|
||||
### ⚙️ 配置系统
|
||||
|
||||
- **Schema驱动**:使用ConfigField定义配置结构
|
||||
- **类型安全**:强类型配置验证
|
||||
- **嵌套访问**:支持 `section.key` 形式访问
|
||||
|
||||
### 🧠 replyer_1集成
|
||||
|
||||
- **智能生成**:AI驱动的个性化内容生成
|
||||
- **简单易用**:通过 `generator_api` 轻松调用
|
||||
- **灵活配置**:支持多种生成风格和参数
|
||||
|
||||
## 📋 开发清单
|
||||
|
||||
在开始开发之前,确保你已经:
|
||||
|
||||
- [ ] 阅读了[快速开始指南](quick-start.md)
|
||||
- [ ] 了解了Action组件或Command组件
|
||||
- [ ] 熟悉了[Action组件](action-components.md)或[Command组件](command-components.md)
|
||||
- [ ] 查看了[配置管理](configuration-guide.md)
|
||||
|
||||
开发完成后,请检查:
|
||||
|
||||
- [ ] 使用了新的API格式(`self.send_text()`等)
|
||||
- [ ] 正确配置了Schema和ConfigField
|
||||
- [ ] 添加了适当的错误处理
|
||||
- [ ] 测试了所有功能路径
|
||||
|
||||
## 🤝 获取帮助
|
||||
|
||||
### 📖 文档问题
|
||||
|
||||
如果你在文档中发现错误或需要补充,请:
|
||||
|
||||
1. 检查最新的文档版本
|
||||
2. 查看相关示例代码
|
||||
3. 参考其他类似插件
|
||||
|
||||
### 💻 开发问题
|
||||
|
||||
遇到开发问题时:
|
||||
|
||||
1. 查看现有插件示例
|
||||
2. 检查配置是否正确
|
||||
3. 参考API文档
|
||||
|
||||
### 🎯 最佳实践建议
|
||||
|
||||
为了创建高质量的插件:
|
||||
|
||||
1. 始终使用新的API格式
|
||||
2. 充分利用replyer_1的智能生成能力
|
||||
3. 设计配置驱动的功能
|
||||
4. 实现完善的错误处理
|
||||
5. 编写清晰的文档注释
|
||||
|
||||
---
|
||||
|
||||
## 🌟 推荐插件示例
|
||||
|
||||
### 🎯 新手友好
|
||||
|
||||
- **Hello World插件**:展示基础API使用
|
||||
- **简单计算器**:Command组件入门
|
||||
- **智能问候**:Action组件和replyer_1集成
|
||||
|
||||
### 🔧 实用工具
|
||||
|
||||
- **智能聊天助手**:完整的replyer_1集成示例
|
||||
- **用户管理系统**:配置驱动的复杂功能
|
||||
- **定时提醒插件**:状态管理和持久化
|
||||
|
||||
### 🚀 高级应用
|
||||
|
||||
- **多功能聊天助手**:综合功能展示
|
||||
- **游戏管理插件**:复杂状态管理
|
||||
- **数据分析插件**:外部服务集成
|
||||
|
||||
---
|
||||
|
||||
**🎉 准备好开始了吗?从[快速开始指南](quick-start.md)开始你的插件开发之旅!**
|
||||
|
||||
使用新的API格式,你可以创建更强大、更智能、更易维护的插件。让我们一起构建更好的MaiBot生态系统!
|
||||
@@ -23,27 +23,28 @@ Action采用**两层决策机制**来优化性能和决策质量:
|
||||
|
||||
#### 激活类型说明
|
||||
|
||||
| 激活类型 | 说明 | 使用场景 |
|
||||
|---------|-----|---------|
|
||||
| `NEVER` | 从不激活,Action对麦麦不可见 | 临时禁用某个Action |
|
||||
| `ALWAYS` | 永远激活,Action总是在麦麦的候选池中 | 核心功能,如回复、表情 |
|
||||
| `LLM_JUDGE` | 通过LLM智能判断当前情境是否需要激活此Action | 需要智能判断的复杂场景 |
|
||||
| `RANDOM` | 基于随机概率决定是否激活 | 增加行为随机性的功能 |
|
||||
| `KEYWORD` | 当检测到特定关键词时激活 | 明确触发条件的功能 |
|
||||
| 激活类型 | 说明 | 使用场景 |
|
||||
| ------------- | ------------------------------------------- | ------------------------ |
|
||||
| `NEVER` | 从不激活,Action对麦麦不可见 | 临时禁用某个Action |
|
||||
| `ALWAYS` | 永远激活,Action总是在麦麦的候选池中 | 核心功能,如回复、不回复 |
|
||||
| `LLM_JUDGE` | 通过LLM智能判断当前情境是否需要激活此Action | 需要智能判断的复杂场景 |
|
||||
| `RANDOM` | 基于随机概率决定是否激活 | 增加行为随机性的功能 |
|
||||
| `KEYWORD` | 当检测到特定关键词时激活 | 明确触发条件的功能 |
|
||||
|
||||
#### 聊天模式控制
|
||||
|
||||
| 模式 | 说明 |
|
||||
|-----|-----|
|
||||
| `ChatMode.FOCUS` | 仅在专注聊天模式下可激活 |
|
||||
| 模式 | 说明 |
|
||||
| ------------------- | ------------------------ |
|
||||
| `ChatMode.FOCUS` | 仅在专注聊天模式下可激活 |
|
||||
| `ChatMode.NORMAL` | 仅在普通聊天模式下可激活 |
|
||||
| `ChatMode.ALL` | 所有模式下都可激活 |
|
||||
| `ChatMode.ALL` | 所有模式下都可激活 |
|
||||
|
||||
### 第二层:使用决策(Usage Decision)
|
||||
|
||||
**在Action被激活后,使用条件决定麦麦什么时候会"选择"使用这个Action**。
|
||||
|
||||
这一层由以下因素综合决定:
|
||||
|
||||
- `action_require`:使用场景描述,帮助LLM判断何时选择
|
||||
- `action_parameters`:所需参数,影响Action的可执行性
|
||||
- 当前聊天上下文和麦麦的决策逻辑
|
||||
@@ -58,7 +59,7 @@ class EmojiAction(BaseAction):
|
||||
focus_activation_type = ActionActivationType.RANDOM # 专注模式下随机激活
|
||||
normal_activation_type = ActionActivationType.KEYWORD # 普通模式下关键词激活
|
||||
activation_keywords = ["表情", "emoji", "😊"]
|
||||
|
||||
|
||||
# 第二层:使用决策
|
||||
action_require = [
|
||||
"表达情绪时可以选择使用",
|
||||
@@ -68,12 +69,14 @@ class EmojiAction(BaseAction):
|
||||
```
|
||||
|
||||
**决策流程**:
|
||||
|
||||
1. **第一层激活判断**:
|
||||
|
||||
- 普通模式:只有当用户消息包含"表情"、"emoji"或"😊"时,麦麦才"知道"可以使用这个Action
|
||||
- 专注模式:随机激活,有概率让麦麦"看到"这个Action
|
||||
|
||||
2. **第二层使用决策**:
|
||||
- 即使Action被激活,麦麦还会根据`action_require`中的条件判断是否真正选择使用
|
||||
|
||||
- 即使Action被激活,麦麦还会根据 `action_require`中的条件判断是否真正选择使用
|
||||
- 例如:如果刚刚已经发过表情,根据"不要连续发送多个表情"的要求,麦麦可能不会选择这个Action
|
||||
|
||||
## 📋 Action必须项清单
|
||||
@@ -125,38 +128,147 @@ action_require = [
|
||||
associated_types = ["text", "emoji", "image"]
|
||||
```
|
||||
|
||||
### 4. 动作记录必须项
|
||||
### 4. 新API导入必须项
|
||||
|
||||
每个 Action 在执行完成后,**必须**使用 `store_action_info` 记录动作信息。这是非常重要的,因为:
|
||||
|
||||
1. **记忆连续性**:让麦麦记住自己执行过的动作,避免重复执行
|
||||
2. **上下文理解**:帮助麦麦理解自己的行为历史,做出更合理的决策
|
||||
3. **行为追踪**:便于后续查询和分析麦麦的行为模式
|
||||
使用新插件系统时,必须导入所需的API模块:
|
||||
|
||||
```python
|
||||
async def execute(self) -> Tuple[bool, Optional[str]]:
|
||||
# 导入新API模块
|
||||
from src.plugin_system.apis import generator_api, send_api, emoji_api
|
||||
|
||||
# 如果需要使用其他API
|
||||
from src.plugin_system.apis import llm_api, database_api, message_api
|
||||
```
|
||||
|
||||
### 5. 动作记录必须项
|
||||
|
||||
每个 Action 在执行完成后,**必须**使用 `store_action_info` 记录动作信息:
|
||||
|
||||
```python
|
||||
async def execute(self) -> Tuple[bool, str]:
|
||||
# ... 执行动作的代码 ...
|
||||
|
||||
|
||||
if success:
|
||||
# 存储动作信息
|
||||
await self.api.store_action_info(
|
||||
# 存储动作信息 - 使用新API格式
|
||||
await self.store_action_info(
|
||||
action_build_into_prompt=True, # 让麦麦知道这个动作
|
||||
action_prompt_display=f"执行了xxx动作,参数:{param}", # 动作描述
|
||||
action_done=True, # 动作是否完成
|
||||
thinking_id=self.thinking_id, # 关联的思考ID
|
||||
action_data={ # 动作的详细数据
|
||||
"param1": value1,
|
||||
"param2": value2
|
||||
}
|
||||
)
|
||||
return True, "动作执行成功"
|
||||
```
|
||||
|
||||
> ⚠️ **重要提示**:如果不记录动作信息,可能会导致以下问题:
|
||||
> - 麦麦不知道自己执行过什么动作,可能会重复执行
|
||||
> - 无法追踪动作历史,影响后续决策
|
||||
> - 在长对话中失去上下文连续性
|
||||
> - 无法进行行为分析和优化
|
||||
> ⚠️ **重要提示**:新API格式中不再需要手动传递 `thinking_id` 等参数,BaseAction会自动处理。
|
||||
|
||||
## 🚀 新API使用指南
|
||||
|
||||
### 📨 消息发送API
|
||||
|
||||
新的消息发送API更加简洁,自动处理群聊/私聊逻辑:
|
||||
|
||||
```python
|
||||
class MessageAction(BaseAction):
|
||||
async def execute(self) -> Tuple[bool, str]:
|
||||
# 发送文本消息 - 自动判断群聊/私聊
|
||||
await self.send_text("Hello World!")
|
||||
|
||||
# 发送表情包
|
||||
emoji_base64 = await emoji_api.get_by_description("开心")
|
||||
if emoji_base64:
|
||||
await self.send_emoji(emoji_base64)
|
||||
|
||||
# 发送图片
|
||||
await self.send_image(image_base64)
|
||||
|
||||
# 发送自定义类型消息
|
||||
await self.send_custom("video", video_data, typing=True)
|
||||
|
||||
return True, "消息发送完成"
|
||||
```
|
||||
|
||||
### 🤖 智能生成API (replyer_1)
|
||||
|
||||
使用replyer_1生成个性化内容:
|
||||
|
||||
```python
|
||||
class SmartReplyAction(BaseAction):
|
||||
async def execute(self) -> Tuple[bool, str]:
|
||||
# 构建生成参数
|
||||
reply_data = {
|
||||
"text": "请生成一个友好的回复",
|
||||
"style": "casual",
|
||||
"topic": "日常聊天",
|
||||
"replyer_name": "replyer_1" # 指定使用replyer_1
|
||||
}
|
||||
|
||||
# 使用generator_api生成回复
|
||||
success, reply_set = await generator_api.generate_reply(
|
||||
chat_stream=self.chat_stream,
|
||||
action_data=reply_data,
|
||||
platform=self.platform,
|
||||
chat_id=self.chat_id,
|
||||
is_group=self.is_group
|
||||
)
|
||||
|
||||
if success and reply_set:
|
||||
# 提取并发送文本回复
|
||||
for reply_type, reply_content in reply_set:
|
||||
if reply_type == "text":
|
||||
await self.send_text(reply_content)
|
||||
elif reply_type == "emoji":
|
||||
await self.send_emoji(reply_content)
|
||||
|
||||
# 记录动作
|
||||
await self.store_action_info(
|
||||
action_build_into_prompt=True,
|
||||
action_prompt_display=f"使用replyer_1生成了智能回复",
|
||||
action_done=True
|
||||
)
|
||||
|
||||
return True, "智能回复生成成功"
|
||||
else:
|
||||
return False, "回复生成失败"
|
||||
```
|
||||
|
||||
### ⚙️ 配置访问API
|
||||
|
||||
使用便捷的配置访问方法:
|
||||
|
||||
```python
|
||||
class ConfigurableAction(BaseAction):
|
||||
async def execute(self) -> Tuple[bool, str]:
|
||||
# 获取插件配置 - 支持嵌套键访问
|
||||
enable_feature = self.get_config("features.enable_smart_mode", False)
|
||||
max_length = self.get_config("limits.max_text_length", 200)
|
||||
style = self.get_config("behavior.response_style", "friendly")
|
||||
|
||||
if enable_feature:
|
||||
# 启用高级功能
|
||||
pass
|
||||
|
||||
return True, "配置获取成功"
|
||||
```
|
||||
|
||||
### 📊 数据库API
|
||||
|
||||
使用新的数据库API存储和查询数据:
|
||||
|
||||
```python
|
||||
class DataAction(BaseAction):
|
||||
async def execute(self) -> Tuple[bool, str]:
|
||||
# 使用database_api
|
||||
from src.plugin_system.apis import database_api
|
||||
|
||||
# 存储数据
|
||||
await database_api.store_action_info(
|
||||
chat_stream=self.chat_stream,
|
||||
action_name=self.action_name,
|
||||
action_data=self.action_data,
|
||||
# ... 其他参数
|
||||
)
|
||||
|
||||
return True, "数据存储完成"
|
||||
```
|
||||
|
||||
## 🔧 激活类型详解
|
||||
|
||||
@@ -168,10 +280,34 @@ async def execute(self) -> Tuple[bool, Optional[str]]:
|
||||
class GreetingAction(BaseAction):
|
||||
focus_activation_type = ActionActivationType.KEYWORD
|
||||
normal_activation_type = ActionActivationType.KEYWORD
|
||||
|
||||
|
||||
# 关键词配置
|
||||
activation_keywords = ["你好", "hello", "hi", "嗨"]
|
||||
keyword_case_sensitive = False # 不区分大小写
|
||||
|
||||
async def execute(self) -> Tuple[bool, str]:
|
||||
# 可选:使用replyer_1生成个性化问候
|
||||
if self.get_config("greeting.use_smart_reply", False):
|
||||
greeting_data = {
|
||||
"text": "生成一个友好的问候语",
|
||||
"replyer_name": "replyer_1"
|
||||
}
|
||||
|
||||
success, reply_set = await generator_api.generate_reply(
|
||||
chat_stream=self.chat_stream,
|
||||
action_data=greeting_data
|
||||
)
|
||||
|
||||
if success:
|
||||
for reply_type, content in reply_set:
|
||||
if reply_type == "text":
|
||||
await self.send_text(content)
|
||||
break
|
||||
return True, "发送智能问候"
|
||||
|
||||
# 传统问候方式
|
||||
await self.send_text("你好!很高兴见到你!")
|
||||
return True, "发送问候"
|
||||
```
|
||||
|
||||
### LLM_JUDGE激活
|
||||
@@ -182,16 +318,38 @@ class GreetingAction(BaseAction):
|
||||
class HelpAction(BaseAction):
|
||||
focus_activation_type = ActionActivationType.LLM_JUDGE
|
||||
normal_activation_type = ActionActivationType.LLM_JUDGE
|
||||
|
||||
|
||||
# LLM判断提示词
|
||||
llm_judge_prompt = """
|
||||
判定是否需要使用帮助动作的条件:
|
||||
1. 用户表达了困惑或需要帮助
|
||||
2. 用户提出了问题但没有得到满意答案
|
||||
3. 对话中出现了技术术语或复杂概念
|
||||
|
||||
|
||||
请回答"是"或"否"。
|
||||
"""
|
||||
|
||||
async def execute(self) -> Tuple[bool, str]:
|
||||
# 使用replyer_1生成帮助内容
|
||||
help_data = {
|
||||
"text": "用户需要帮助,请提供适当的帮助信息",
|
||||
"help_type": self.action_data.get("help_type", "general"),
|
||||
"replyer_name": "replyer_1"
|
||||
}
|
||||
|
||||
success, reply_set = await generator_api.generate_reply(
|
||||
chat_stream=self.chat_stream,
|
||||
action_data=help_data
|
||||
)
|
||||
|
||||
if success:
|
||||
for reply_type, content in reply_set:
|
||||
if reply_type == "text":
|
||||
await self.send_text(content)
|
||||
return True, "提供了帮助"
|
||||
else:
|
||||
await self.send_text("我来帮助你!有什么问题吗?")
|
||||
return True, "提供了默认帮助"
|
||||
```
|
||||
|
||||
### RANDOM激活
|
||||
@@ -202,466 +360,210 @@ class HelpAction(BaseAction):
|
||||
class SurpriseAction(BaseAction):
|
||||
focus_activation_type = ActionActivationType.RANDOM
|
||||
normal_activation_type = ActionActivationType.RANDOM
|
||||
|
||||
|
||||
# 随机激活概率
|
||||
random_activation_probability = 0.1 # 10%概率激活
|
||||
```
|
||||
|
||||
### ALWAYS/NEVER激活
|
||||
|
||||
```python
|
||||
class CoreAction(BaseAction):
|
||||
focus_activation_type = ActionActivationType.ALWAYS # 总是激活
|
||||
normal_activation_type = ActionActivationType.NEVER # 在普通模式下禁用
|
||||
```
|
||||
|
||||
## 🎨 完整Action示例
|
||||
|
||||
### 智能问候Action
|
||||
|
||||
```python
|
||||
from src.plugin_system import BaseAction, ActionActivationType, ChatMode
|
||||
|
||||
class SmartGreetingAction(BaseAction):
|
||||
"""智能问候Action - 展示关键词激活的完整示例"""
|
||||
|
||||
# ===== 激活控制必须项 =====
|
||||
focus_activation_type = ActionActivationType.KEYWORD
|
||||
normal_activation_type = ActionActivationType.KEYWORD
|
||||
mode_enable = ChatMode.ALL
|
||||
parallel_action = False
|
||||
|
||||
# ===== 基本信息必须项 =====
|
||||
action_name = "smart_greeting"
|
||||
action_description = "智能问候系统,基于关键词触发,支持个性化问候消息"
|
||||
|
||||
# 关键词配置
|
||||
activation_keywords = ["你好", "hello", "hi", "嗨", "问候", "早上好", "晚上好"]
|
||||
keyword_case_sensitive = False
|
||||
|
||||
# ===== 功能定义必须项 =====
|
||||
action_parameters = {
|
||||
"username": "要问候的用户名(可选)",
|
||||
"greeting_style": "问候风格:casual(随意)、formal(正式)、friendly(友好)"
|
||||
}
|
||||
|
||||
action_require = [
|
||||
"用户发送包含问候词汇的消息时使用",
|
||||
"检测到新用户加入时使用",
|
||||
"响应友好交流需求时使用",
|
||||
"避免在短时间内重复问候同一用户"
|
||||
]
|
||||
|
||||
associated_types = ["text", "emoji"]
|
||||
|
||||
|
||||
async def execute(self) -> Tuple[bool, str]:
|
||||
"""执行智能问候"""
|
||||
# 获取参数
|
||||
username = self.action_data.get("username", "")
|
||||
greeting_style = self.action_data.get("greeting_style", "casual")
|
||||
|
||||
# 根据风格生成问候消息
|
||||
if greeting_style == "formal":
|
||||
message = f"您好{username}!很荣幸为您服务!"
|
||||
emoji = "🙏"
|
||||
elif greeting_style == "friendly":
|
||||
message = f"你好{username}!欢迎来到这里,希望我们能成为好朋友!"
|
||||
emoji = "😊"
|
||||
else: # casual
|
||||
message = f"嗨{username}!很开心见到你~"
|
||||
emoji = "👋"
|
||||
|
||||
# 发送消息
|
||||
await self.send_text(message)
|
||||
await self.send_type("emoji", emoji)
|
||||
|
||||
return True, f"向{username or '用户'}发送了{greeting_style}风格的问候"
|
||||
import random
|
||||
|
||||
surprises = ["🎉", "✨", "🌟", "💝", "🎈"]
|
||||
selected = random.choice(surprises)
|
||||
|
||||
await self.send_emoji(selected)
|
||||
return True, f"发送了惊喜表情: {selected}"
|
||||
```
|
||||
|
||||
### 智能禁言Action
|
||||
## 💡 完整示例
|
||||
|
||||
以下是一个真实的群管理禁言Action示例,展示了LLM判断、参数验证、配置管理等高级功能:
|
||||
### 智能聊天Action
|
||||
|
||||
```python
|
||||
from typing import Optional
|
||||
import random
|
||||
from src.plugin_system.base.base_action import BaseAction
|
||||
from src.plugin_system.base.component_types import ActionActivationType, ChatMode
|
||||
from src.plugin_system.apis import generator_api, emoji_api
|
||||
|
||||
class MuteAction(BaseAction):
|
||||
"""智能禁言Action - 基于LLM智能判断是否需要禁言"""
|
||||
|
||||
# ===== 激活控制必须项 =====
|
||||
focus_activation_type = ActionActivationType.LLM_JUDGE # Focus模式使用LLM判定
|
||||
normal_activation_type = ActionActivationType.KEYWORD # Normal模式使用关键词
|
||||
class IntelligentChatAction(BaseAction):
|
||||
"""智能聊天Action - 展示新API的完整用法"""
|
||||
|
||||
# 激活设置
|
||||
focus_activation_type = ActionActivationType.ALWAYS
|
||||
normal_activation_type = ActionActivationType.LLM_JUDGE
|
||||
mode_enable = ChatMode.ALL
|
||||
parallel_action = False
|
||||
|
||||
# ===== 基本信息必须项 =====
|
||||
action_name = "mute"
|
||||
action_description = "智能禁言系统,基于LLM判断是否需要禁言"
|
||||
|
||||
# ===== 激活配置 =====
|
||||
# 关键词设置(用于Normal模式)
|
||||
activation_keywords = ["禁言", "mute", "ban", "silence"]
|
||||
keyword_case_sensitive = False
|
||||
|
||||
# LLM判定提示词(用于Focus模式)
|
||||
llm_judge_prompt = """
|
||||
判定是否需要使用禁言动作的严格条件:
|
||||
|
||||
使用禁言的情况:
|
||||
1. 用户发送明显违规内容(色情、暴力、政治敏感等)
|
||||
2. 恶意刷屏或垃圾信息轰炸
|
||||
3. 用户主动明确要求被禁言("禁言我"等)
|
||||
4. 严重违反群规的行为
|
||||
5. 恶意攻击他人或群组管理
|
||||
|
||||
绝对不要使用的情况:
|
||||
1. 正常聊天和交流
|
||||
2. 情绪化表达但无恶意
|
||||
3. 开玩笑或调侃,除非过分
|
||||
4. 单纯的意见分歧或争论
|
||||
"""
|
||||
|
||||
# ===== 功能定义必须项 =====
|
||||
action_parameters = {
|
||||
"target": "禁言对象,必填,输入你要禁言的对象的名字,请仔细思考不要弄错禁言对象",
|
||||
"duration": "禁言时长,必填,输入你要禁言的时长(秒),单位为秒,必须为数字",
|
||||
"reason": "禁言理由,可选",
|
||||
}
|
||||
|
||||
action_require = [
|
||||
"当有人违反了公序良俗的内容",
|
||||
"当有人刷屏时使用",
|
||||
"当有人发了擦边,或者色情内容时使用",
|
||||
"当有人要求禁言自己时使用",
|
||||
"如果某人已经被禁言了,就不要再次禁言了,除非你想追加时间!!",
|
||||
]
|
||||
|
||||
associated_types = ["text", "command"]
|
||||
|
||||
async def execute(self) -> Tuple[bool, Optional[str]]:
|
||||
"""执行智能禁言判定"""
|
||||
# 获取参数
|
||||
target = self.action_data.get("target")
|
||||
duration = self.action_data.get("duration")
|
||||
reason = self.action_data.get("reason", "违反群规")
|
||||
|
||||
# 参数验证
|
||||
if not target:
|
||||
await self.send_text("没有指定禁言对象呢~")
|
||||
return False, "禁言目标不能为空"
|
||||
|
||||
if not duration:
|
||||
await self.send_text("没有指定禁言时长呢~")
|
||||
return False, "禁言时长不能为空"
|
||||
|
||||
# 获取时长限制配置
|
||||
min_duration = self.api.get_config("mute.min_duration", 60)
|
||||
max_duration = self.api.get_config("mute.max_duration", 2592000)
|
||||
|
||||
# 验证时长格式并转换
|
||||
try:
|
||||
duration_int = int(duration)
|
||||
if duration_int <= 0:
|
||||
await self.send_text("禁言时长必须是正数哦~")
|
||||
return False, "禁言时长必须大于0"
|
||||
|
||||
# 限制禁言时长范围
|
||||
if duration_int < min_duration:
|
||||
duration_int = min_duration
|
||||
elif duration_int > max_duration:
|
||||
duration_int = max_duration
|
||||
|
||||
except (ValueError, TypeError):
|
||||
await self.send_text("禁言时长必须是数字哦~")
|
||||
return False, f"禁言时长格式无效: {duration}"
|
||||
|
||||
# 获取用户ID
|
||||
try:
|
||||
platform, user_id = await self.api.get_user_id_by_person_name(target)
|
||||
except Exception as e:
|
||||
await self.send_text("查找用户信息时出现问题~")
|
||||
return False, f"查找用户ID时出错: {e}"
|
||||
|
||||
if not user_id:
|
||||
await self.send_text(f"找不到 {target} 这个人呢~")
|
||||
return False, f"未找到用户 {target} 的ID"
|
||||
|
||||
# 格式化时长显示
|
||||
time_str = self._format_duration(duration_int)
|
||||
|
||||
# 获取模板化消息
|
||||
message = self._get_template_message(target, time_str, reason)
|
||||
await self.send_message_by_expressor(message)
|
||||
|
||||
# 发送群聊禁言命令
|
||||
success = await self.send_command(
|
||||
command_name="GROUP_BAN",
|
||||
args={"qq_id": str(user_id), "duration": str(duration_int)},
|
||||
display_message=f"禁言了 {target} {time_str}",
|
||||
)
|
||||
|
||||
if success:
|
||||
return True, f"成功禁言 {target},时长 {time_str}"
|
||||
else:
|
||||
await self.send_text("执行禁言动作失败")
|
||||
return False, "发送禁言命令失败"
|
||||
|
||||
def _get_template_message(self, target: str, duration_str: str, reason: str) -> str:
|
||||
"""获取模板化的禁言消息"""
|
||||
templates = self.api.get_config(
|
||||
"mute.templates",
|
||||
[
|
||||
"好的,禁言 {target} {duration},理由:{reason}",
|
||||
"收到,对 {target} 执行禁言 {duration},因为{reason}",
|
||||
"明白了,禁言 {target} {duration},原因是{reason}",
|
||||
"哇哈哈哈哈哈,已禁言 {target} {duration},理由:{reason}",
|
||||
],
|
||||
)
|
||||
template = random.choice(templates)
|
||||
return template.format(target=target, duration=duration_str, reason=reason)
|
||||
|
||||
def _format_duration(self, seconds: int) -> str:
|
||||
"""将秒数格式化为可读的时间字符串"""
|
||||
if seconds < 60:
|
||||
return f"{seconds}秒"
|
||||
elif seconds < 3600:
|
||||
minutes = seconds // 60
|
||||
remaining_seconds = seconds % 60
|
||||
if remaining_seconds > 0:
|
||||
return f"{minutes}分{remaining_seconds}秒"
|
||||
else:
|
||||
return f"{minutes}分钟"
|
||||
else:
|
||||
hours = seconds // 3600
|
||||
remaining_minutes = (seconds % 3600) // 60
|
||||
if remaining_minutes > 0:
|
||||
return f"{hours}小时{remaining_minutes}分钟"
|
||||
else:
|
||||
return f"{hours}小时"
|
||||
```
|
||||
|
||||
**关键特性说明**:
|
||||
|
||||
1. **🎯 双模式激活**:Focus模式使用LLM_JUDGE更谨慎,Normal模式使用KEYWORD快速响应
|
||||
2. **🧠 严格的LLM判定**:详细提示词指导LLM何时应该/不应该使用禁言,避免误判
|
||||
3. **✅ 完善的参数验证**:验证必需参数、数值转换、用户ID查找等多重验证
|
||||
4. **⚙️ 配置驱动**:时长限制、消息模板等都可通过配置文件自定义
|
||||
5. **😊 友好的用户反馈**:错误提示清晰、随机化消息模板、时长格式化显示
|
||||
6. **🛡️ 安全措施**:严格权限控制、防误操作验证、完整错误处理
|
||||
|
||||
### 智能助手Action
|
||||
|
||||
```python
|
||||
class IntelligentHelpAction(BaseAction):
|
||||
"""智能助手Action - 展示LLM判断激活的完整示例"""
|
||||
|
||||
# ===== 激活控制必须项 =====
|
||||
focus_activation_type = ActionActivationType.LLM_JUDGE
|
||||
normal_activation_type = ActionActivationType.RANDOM
|
||||
mode_enable = ChatMode.ALL
|
||||
parallel_action = True
|
||||
|
||||
# ===== 基本信息必须项 =====
|
||||
action_name = "intelligent_help"
|
||||
action_description = "智能助手,主动提供帮助和建议"
|
||||
|
||||
|
||||
# 基本信息
|
||||
action_name = "intelligent_chat"
|
||||
action_description = "使用replyer_1进行智能聊天回复,支持表情包和个性化回复"
|
||||
|
||||
# LLM判断提示词
|
||||
llm_judge_prompt = """
|
||||
判定是否需要提供智能帮助的条件:
|
||||
1. 用户表达了困惑或需要帮助
|
||||
2. 对话中出现了技术问题
|
||||
3. 用户寻求解决方案或建议
|
||||
4. 适合提供额外信息的场合
|
||||
|
||||
不要使用的情况:
|
||||
1. 用户明确表示不需要帮助
|
||||
2. 对话进行得很顺利
|
||||
3. 刚刚已经提供过帮助
|
||||
|
||||
判断是否需要进行智能聊天回复:
|
||||
1. 用户提出了有趣的话题
|
||||
2. 需要更加个性化的回复
|
||||
3. 适合发送表情包的情况
|
||||
|
||||
请回答"是"或"否"。
|
||||
"""
|
||||
|
||||
# 随机激活概率
|
||||
random_activation_probability = 0.15
|
||||
|
||||
# ===== 功能定义必须项 =====
|
||||
|
||||
# 功能定义
|
||||
action_parameters = {
|
||||
"help_type": "帮助类型:explanation(解释)、suggestion(建议)、guidance(指导)",
|
||||
"topic": "帮助主题或用户关心的问题",
|
||||
"urgency": "紧急程度:low(低)、medium(中)、high(高)"
|
||||
"topic": "聊天话题",
|
||||
"mood": "当前氛围(happy/sad/excited/calm)",
|
||||
"include_emoji": "是否包含表情包(true/false)"
|
||||
}
|
||||
|
||||
|
||||
action_require = [
|
||||
"用户表达困惑或寻求帮助时使用",
|
||||
"检测到用户遇到技术问题时使用",
|
||||
"对话中出现知识盲点时主动提供帮助",
|
||||
"避免过度频繁地提供帮助,要恰到好处"
|
||||
"需要更个性化回复时使用",
|
||||
"聊天氛围适合发送表情时使用",
|
||||
"避免在正式场合使用"
|
||||
]
|
||||
|
||||
|
||||
associated_types = ["text", "emoji"]
|
||||
|
||||
|
||||
async def execute(self) -> Tuple[bool, str]:
|
||||
"""执行智能帮助"""
|
||||
# 获取参数
|
||||
help_type = self.action_data.get("help_type", "suggestion")
|
||||
topic = self.action_data.get("topic", "")
|
||||
urgency = self.action_data.get("urgency", "medium")
|
||||
|
||||
# 根据帮助类型和紧急程度生成消息
|
||||
if help_type == "explanation":
|
||||
message = f"关于{topic},让我来为你解释一下..."
|
||||
elif help_type == "guidance":
|
||||
message = f"在{topic}方面,我可以为你提供一些指导..."
|
||||
else: # suggestion
|
||||
message = f"针对{topic},我建议你可以尝试以下方法..."
|
||||
|
||||
# 根据紧急程度调整表情
|
||||
if urgency == "high":
|
||||
emoji = "🚨"
|
||||
elif urgency == "low":
|
||||
emoji = "💡"
|
||||
topic = self.action_data.get("topic", "日常聊天")
|
||||
mood = self.action_data.get("mood", "happy")
|
||||
include_emoji = self.action_data.get("include_emoji", "true") == "true"
|
||||
|
||||
# 构建智能回复数据
|
||||
chat_data = {
|
||||
"text": f"请针对{topic}话题进行回复,当前氛围是{mood}",
|
||||
"topic": topic,
|
||||
"mood": mood,
|
||||
"style": "conversational",
|
||||
"replyer_name": "replyer_1" # 使用replyer_1
|
||||
}
|
||||
|
||||
# 生成智能回复
|
||||
success, reply_set = await generator_api.generate_reply(
|
||||
chat_stream=self.chat_stream,
|
||||
action_data=chat_data,
|
||||
platform=self.platform,
|
||||
chat_id=self.chat_id,
|
||||
is_group=self.is_group
|
||||
)
|
||||
|
||||
reply_sent = False
|
||||
|
||||
if success and reply_set:
|
||||
# 发送生成的回复
|
||||
for reply_type, content in reply_set:
|
||||
if reply_type == "text":
|
||||
await self.send_text(content)
|
||||
reply_sent = True
|
||||
elif reply_type == "emoji":
|
||||
await self.send_emoji(content)
|
||||
|
||||
# 如果配置允许且生成失败,发送表情包
|
||||
if include_emoji and not reply_sent:
|
||||
emoji_result = await emoji_api.get_by_description(mood)
|
||||
if emoji_result:
|
||||
emoji_base64, emoji_desc, matched_emotion = emoji_result
|
||||
await self.send_emoji(emoji_base64)
|
||||
reply_sent = True
|
||||
|
||||
# 记录动作执行
|
||||
if reply_sent:
|
||||
await self.store_action_info(
|
||||
action_build_into_prompt=True,
|
||||
action_prompt_display=f"进行了智能聊天回复,话题:{topic},氛围:{mood}",
|
||||
action_done=True
|
||||
)
|
||||
return True, f"完成智能聊天回复:{topic}"
|
||||
else:
|
||||
emoji = "🤔"
|
||||
|
||||
# 发送帮助消息
|
||||
await self.send_text(message)
|
||||
await self.send_type("emoji", emoji)
|
||||
|
||||
return True, f"提供了{help_type}类型的帮助,主题:{topic}"
|
||||
return False, "智能回复生成失败"
|
||||
```
|
||||
|
||||
## 📊 性能优化建议
|
||||
## 🛠️ 调试技巧
|
||||
|
||||
### 1. 合理使用激活类型
|
||||
|
||||
- **ALWAYS**: 仅用于核心功能
|
||||
- **LLM_JUDGE**: 适度使用,避免过多LLM调用
|
||||
- **KEYWORD**: 优选,性能最好
|
||||
- **RANDOM**: 控制概率,避免过于频繁
|
||||
|
||||
### 2. 优化execute方法
|
||||
### 开发调试Action
|
||||
|
||||
```python
|
||||
async def execute(self) -> Tuple[bool, str]:
|
||||
try:
|
||||
# 快速参数验证
|
||||
if not self._validate_parameters():
|
||||
return False, "参数验证失败"
|
||||
|
||||
# 核心逻辑
|
||||
result = await self._core_logic()
|
||||
|
||||
# 成功返回
|
||||
return True, "执行成功"
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"{self.log_prefix} 执行失败: {e}")
|
||||
return False, f"执行失败: {str(e)}"
|
||||
class DebugAction(BaseAction):
|
||||
"""调试Action - 展示如何调试新API"""
|
||||
|
||||
focus_activation_type = ActionActivationType.KEYWORD
|
||||
normal_activation_type = ActionActivationType.KEYWORD
|
||||
activation_keywords = ["debug", "调试"]
|
||||
mode_enable = ChatMode.ALL
|
||||
parallel_action = True
|
||||
|
||||
action_name = "debug_helper"
|
||||
action_description = "调试助手,显示当前状态信息"
|
||||
|
||||
action_parameters = {}
|
||||
action_require = ["需要调试信息时使用"]
|
||||
associated_types = ["text"]
|
||||
|
||||
async def execute(self) -> Tuple[bool, str]:
|
||||
# 收集调试信息
|
||||
debug_info = {
|
||||
"聊天类型": "群聊" if self.is_group else "私聊",
|
||||
"平台": self.platform,
|
||||
"目标ID": self.target_id,
|
||||
"用户ID": self.user_id,
|
||||
"用户昵称": self.user_nickname,
|
||||
"动作数据": self.action_data,
|
||||
}
|
||||
|
||||
if self.is_group:
|
||||
debug_info.update({
|
||||
"群ID": self.group_id,
|
||||
"群名": self.group_name,
|
||||
})
|
||||
|
||||
# 格式化调试信息
|
||||
info_lines = ["🔍 调试信息:"]
|
||||
for key, value in debug_info.items():
|
||||
info_lines.append(f" • {key}: {value}")
|
||||
|
||||
debug_text = "\n".join(info_lines)
|
||||
|
||||
# 发送调试信息
|
||||
await self.send_text(debug_text)
|
||||
|
||||
# 测试配置获取
|
||||
test_config = self.get_config("debug.verbose", True)
|
||||
if test_config:
|
||||
await self.send_text(f"配置测试: debug.verbose = {test_config}")
|
||||
|
||||
return True, "调试信息已发送"
|
||||
```
|
||||
|
||||
### 3. 合理设置并行执行
|
||||
## 📚 最佳实践
|
||||
|
||||
```python
|
||||
# 轻量级Action可以并行
|
||||
parallel_action = True # 如:发送表情、记录日志
|
||||
1. **总是导入所需的API模块**:
|
||||
|
||||
# 重要Action应该独占
|
||||
parallel_action = False # 如:回复消息、状态切换
|
||||
```
|
||||
```python
|
||||
from src.plugin_system.apis import generator_api, send_api, emoji_api
|
||||
```
|
||||
2. **在生成内容时指定replyer_1**:
|
||||
|
||||
## 🐛 调试技巧
|
||||
```python
|
||||
action_data = {
|
||||
"text": "生成内容的请求",
|
||||
"replyer_name": "replyer_1"
|
||||
}
|
||||
```
|
||||
3. **使用便捷发送方法**:
|
||||
|
||||
### 1. 日志记录
|
||||
```python
|
||||
await self.send_text("文本") # 自动处理群聊/私聊
|
||||
await self.send_emoji(emoji_base64)
|
||||
```
|
||||
4. **合理使用配置**:
|
||||
|
||||
```python
|
||||
from src.common.logger import get_logger
|
||||
```python
|
||||
enable_feature = self.get_config("section.key", default_value)
|
||||
```
|
||||
5. **总是记录动作信息**:
|
||||
|
||||
logger = get_logger("my_action")
|
||||
```python
|
||||
await self.store_action_info(
|
||||
action_build_into_prompt=True,
|
||||
action_prompt_display="动作描述",
|
||||
action_done=True
|
||||
)
|
||||
```
|
||||
|
||||
async def execute(self) -> Tuple[bool, str]:
|
||||
logger.info(f"{self.log_prefix} 开始执行: {self.reasoning}")
|
||||
logger.debug(f"{self.log_prefix} 参数: {self.action_data}")
|
||||
|
||||
# 执行逻辑...
|
||||
|
||||
logger.info(f"{self.log_prefix} 执行完成")
|
||||
```
|
||||
|
||||
### 2. 激活状态检查
|
||||
|
||||
```python
|
||||
# 在execute方法中检查激活原因
|
||||
def _debug_activation(self):
|
||||
logger.debug(f"激活类型: Focus={self.focus_activation_type}, Normal={self.normal_activation_type}")
|
||||
logger.debug(f"当前模式: {self.api.get_chat_mode()}")
|
||||
logger.debug(f"激活原因: {self.reasoning}")
|
||||
```
|
||||
|
||||
### 3. 参数验证
|
||||
|
||||
```python
|
||||
def _validate_parameters(self) -> bool:
|
||||
required_params = ["param1", "param2"]
|
||||
for param in required_params:
|
||||
if param not in self.action_data:
|
||||
logger.warning(f"{self.log_prefix} 缺少必需参数: {param}")
|
||||
return False
|
||||
return True
|
||||
```
|
||||
|
||||
## 🎯 最佳实践
|
||||
|
||||
### 1. 清晰的Action命名
|
||||
|
||||
- 使用描述性的类名:`SmartGreetingAction` 而不是 `Action1`
|
||||
- action_name要简洁明确:`"smart_greeting"` 而不是 `"action_1"`
|
||||
|
||||
### 2. 完整的文档字符串
|
||||
|
||||
```python
|
||||
class MyAction(BaseAction):
|
||||
"""
|
||||
我的Action - 一句话描述功能
|
||||
|
||||
详细描述Action的用途、激活条件、执行逻辑等。
|
||||
|
||||
激活条件:
|
||||
- Focus模式:关键词激活
|
||||
- Normal模式:LLM判断激活
|
||||
|
||||
执行逻辑:
|
||||
1. 验证参数
|
||||
2. 生成响应
|
||||
3. 发送消息
|
||||
"""
|
||||
```
|
||||
|
||||
### 3. 错误处理
|
||||
|
||||
```python
|
||||
async def execute(self) -> Tuple[bool, str]:
|
||||
try:
|
||||
# 主要逻辑
|
||||
pass
|
||||
except ValueError as e:
|
||||
await self.send_text("参数错误,请检查输入")
|
||||
return False, f"参数错误: {e}"
|
||||
except Exception as e:
|
||||
await self.send_text("操作失败,请稍后重试")
|
||||
return False, f"执行失败: {e}"
|
||||
```
|
||||
|
||||
### 4. 配置驱动
|
||||
|
||||
```python
|
||||
# 从配置文件读取设置
|
||||
enable_feature = self.api.get_config("my_action.enable_feature", True)
|
||||
max_retries = self.api.get_config("my_action.max_retries", 3)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
🎉 **现在你已经掌握了Action组件开发的完整知识!继续学习 [Command组件详解](command-components.md) 来了解命令开发。**
|
||||
通过使用新的API格式,Action的开发变得更加简洁和强大!
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
|
||||
## 📖 概述
|
||||
|
||||
消息API提供了发送各种类型消息的接口,支持文本、表情、图片等多种消息类型,以及向不同目标发送消息的功能。
|
||||
消息API提供了发送各种类型消息的接口,支持文本、表情、图片等多种消息类型。新版API格式更加简洁直观,自动处理群聊/私聊判断。
|
||||
|
||||
## 🔄 基础消息发送
|
||||
|
||||
### 发送文本消息
|
||||
|
||||
```python
|
||||
# 发送普通文本消息
|
||||
# 新API格式 - 自动判断群聊/私聊
|
||||
await self.send_text("这是一条文本消息")
|
||||
|
||||
# 发送多行文本
|
||||
@@ -21,60 +21,73 @@ message = """
|
||||
await self.send_text(message.strip())
|
||||
```
|
||||
|
||||
### 发送表情消息
|
||||
|
||||
```python
|
||||
# 新API格式 - 发送表情
|
||||
await self.send_emoji("😊")
|
||||
await self.send_emoji("🎉")
|
||||
await self.send_emoji("👋")
|
||||
```
|
||||
|
||||
### 发送特定类型消息
|
||||
|
||||
```python
|
||||
# 发送表情
|
||||
await self.send_type("emoji", "😊")
|
||||
|
||||
# 发送图片
|
||||
await self.send_type("image", "https://example.com/image.jpg")
|
||||
|
||||
# 发送音频
|
||||
await self.send_type("audio", "audio_file_path")
|
||||
|
||||
# 发送视频
|
||||
await self.send_type("video", "video_file_path")
|
||||
|
||||
# 发送文件
|
||||
await self.send_type("file", "file_path")
|
||||
```
|
||||
|
||||
### 发送命令消息
|
||||
## 🎯 跨目标消息发送
|
||||
|
||||
### 使用send_api模块发送消息
|
||||
|
||||
```python
|
||||
# 发送命令类型的消息
|
||||
await self.send_command("system_command", {"param": "value"})
|
||||
```
|
||||
# 导入send_api
|
||||
from src.plugin_system.apis import send_api
|
||||
|
||||
## 🎯 目标消息发送
|
||||
|
||||
### 向指定群聊发送消息
|
||||
|
||||
```python
|
||||
# 向指定群聊发送文本消息
|
||||
success = await self.api.send_text_to_group(
|
||||
success = await send_api.text_to_group(
|
||||
text="这是发送到群聊的消息",
|
||||
group_id="123456789",
|
||||
platform="qq"
|
||||
)
|
||||
|
||||
if success:
|
||||
print("消息发送成功")
|
||||
else:
|
||||
print("消息发送失败")
|
||||
```
|
||||
|
||||
### 向指定用户发送私聊消息
|
||||
|
||||
```python
|
||||
# 向指定用户发送私聊消息
|
||||
success = await self.api.send_text_to_user(
|
||||
success = await send_api.text_to_user(
|
||||
text="这是私聊消息",
|
||||
user_id="987654321",
|
||||
platform="qq"
|
||||
)
|
||||
|
||||
# 向指定群聊发送表情
|
||||
success = await send_api.emoji_to_group(
|
||||
emoji="😊",
|
||||
group_id="123456789",
|
||||
platform="qq"
|
||||
)
|
||||
|
||||
# 向指定用户发送表情
|
||||
success = await send_api.emoji_to_user(
|
||||
emoji="🎉",
|
||||
user_id="987654321",
|
||||
platform="qq"
|
||||
)
|
||||
```
|
||||
|
||||
### 通用目标消息发送
|
||||
|
||||
```python
|
||||
# 向任意目标发送任意类型消息
|
||||
success = await self.api.send_message_to_target(
|
||||
success = await send_api.message_to_target(
|
||||
message_type="text", # 消息类型
|
||||
content="消息内容", # 消息内容
|
||||
platform="qq", # 平台
|
||||
@@ -88,76 +101,83 @@ success = await self.api.send_message_to_target(
|
||||
|
||||
### 支持的消息类型
|
||||
|
||||
| 类型 | 说明 | 示例 |
|
||||
|-----|------|------|
|
||||
| `text` | 普通文本消息 | "Hello World" |
|
||||
| `emoji` | 表情消息 | "😊" |
|
||||
| `image` | 图片消息 | 图片URL或路径 |
|
||||
| `audio` | 音频消息 | 音频文件路径 |
|
||||
| `video` | 视频消息 | 视频文件路径 |
|
||||
| `file` | 文件消息 | 文件路径 |
|
||||
| 类型 | 说明 | 新API方法 | send_api方法 |
|
||||
|-----|------|----------|-------------|
|
||||
| `text` | 普通文本消息 | `await self.send_text()` | `await send_api.text_to_group()` |
|
||||
| `emoji` | 表情消息 | `await self.send_emoji()` | `await send_api.emoji_to_group()` |
|
||||
| `image` | 图片消息 | `await self.send_type("image", url)` | `await send_api.message_to_target()` |
|
||||
| `audio` | 音频消息 | `await self.send_type("audio", path)` | `await send_api.message_to_target()` |
|
||||
| `video` | 视频消息 | `await self.send_type("video", path)` | `await send_api.message_to_target()` |
|
||||
| `file` | 文件消息 | `await self.send_type("file", path)` | `await send_api.message_to_target()` |
|
||||
|
||||
### 消息类型示例
|
||||
### 新API格式示例
|
||||
|
||||
```python
|
||||
# 文本消息
|
||||
await self.send_type("text", "普通文本")
|
||||
|
||||
# 表情消息
|
||||
await self.send_type("emoji", "🎉")
|
||||
|
||||
# 图片消息
|
||||
await self.send_type("image", "/path/to/image.jpg")
|
||||
|
||||
# 音频消息
|
||||
await self.send_type("audio", "/path/to/audio.mp3")
|
||||
|
||||
# 文件消息
|
||||
await self.send_type("file", "/path/to/document.pdf")
|
||||
class ExampleAction(BaseAction):
|
||||
async def execute(self) -> Tuple[bool, str]:
|
||||
# 文本消息 - 最常用
|
||||
await self.send_text("普通文本消息")
|
||||
|
||||
# 表情消息 - 直接方法
|
||||
await self.send_emoji("🎉")
|
||||
|
||||
# 图片消息
|
||||
await self.send_type("image", "/path/to/image.jpg")
|
||||
|
||||
# 音频消息
|
||||
await self.send_type("audio", "/path/to/audio.mp3")
|
||||
|
||||
# 文件消息
|
||||
await self.send_type("file", "/path/to/document.pdf")
|
||||
|
||||
return True, "发送了多种类型的消息"
|
||||
```
|
||||
|
||||
## 🔍 消息查询
|
||||
|
||||
### 获取聊天类型
|
||||
|
||||
```python
|
||||
# 获取当前聊天类型
|
||||
chat_type = self.api.get_chat_type()
|
||||
|
||||
if chat_type == "group":
|
||||
print("当前是群聊")
|
||||
elif chat_type == "private":
|
||||
print("当前是私聊")
|
||||
```
|
||||
|
||||
### 获取最近消息
|
||||
|
||||
```python
|
||||
# 获取最近的5条消息
|
||||
recent_messages = self.api.get_recent_messages(count=5)
|
||||
|
||||
for message in recent_messages:
|
||||
print(f"用户: {message.user_nickname}")
|
||||
print(f"内容: {message.processed_plain_text}")
|
||||
print(f"时间: {message.timestamp}")
|
||||
```
|
||||
## 🔍 消息信息获取
|
||||
|
||||
### 获取当前消息信息
|
||||
|
||||
```python
|
||||
# 在Action或Command中获取当前处理的消息
|
||||
current_message = self.message
|
||||
# 新API格式 - 直接属性访问
|
||||
class ExampleCommand(BaseCommand):
|
||||
async def execute(self) -> Tuple[bool, str]:
|
||||
# 用户信息
|
||||
user_id = self.user_id
|
||||
user_nickname = self.user_nickname
|
||||
|
||||
# 聊天信息
|
||||
is_group_chat = self.is_group
|
||||
chat_id = self.chat_id
|
||||
platform = self.platform
|
||||
|
||||
# 消息内容
|
||||
message_text = self.message.processed_plain_text
|
||||
|
||||
# 构建信息显示
|
||||
info = f"""
|
||||
👤 用户: {user_nickname}({user_id})
|
||||
💬 类型: {'群聊' if is_group_chat else '私聊'}
|
||||
📱 平台: {platform}
|
||||
📝 内容: {message_text}
|
||||
""".strip()
|
||||
|
||||
await self.send_text(info)
|
||||
return True, "显示了消息信息"
|
||||
```
|
||||
|
||||
# 消息基本信息
|
||||
user_id = current_message.message_info.user_info.user_id
|
||||
user_nickname = current_message.message_info.user_info.user_nickname
|
||||
message_content = current_message.processed_plain_text
|
||||
timestamp = current_message.timestamp
|
||||
### 获取群聊信息(如果适用)
|
||||
|
||||
# 群聊信息(如果是群聊)
|
||||
if current_message.message_info.group_info:
|
||||
group_id = current_message.message_info.group_info.group_id
|
||||
group_name = current_message.message_info.group_info.group_name
|
||||
```python
|
||||
# 在Action或Command中检查群聊信息
|
||||
if self.is_group:
|
||||
group_info = self.message.message_info.group_info
|
||||
if group_info:
|
||||
group_id = group_info.group_id
|
||||
group_name = getattr(group_info, 'group_name', '未知群聊')
|
||||
|
||||
await self.send_text(f"当前群聊: {group_name}({group_id})")
|
||||
else:
|
||||
await self.send_text("当前是私聊对话")
|
||||
```
|
||||
|
||||
## 🌐 平台支持
|
||||
@@ -170,242 +190,209 @@ if current_message.message_info.group_info:
|
||||
| 微信 | `wechat` | 微信聊天平台 |
|
||||
| Discord | `discord` | Discord聊天平台 |
|
||||
|
||||
### 平台特定功能
|
||||
### 平台适配示例
|
||||
|
||||
```python
|
||||
# 获取当前平台
|
||||
current_platform = self.api.get_current_platform()
|
||||
|
||||
# 根据平台调整消息格式
|
||||
if current_platform == "qq":
|
||||
# QQ平台特定处理
|
||||
await self.send_text("[QQ] 消息内容")
|
||||
elif current_platform == "wechat":
|
||||
# 微信平台特定处理
|
||||
await self.send_text("【微信】消息内容")
|
||||
class PlatformAdaptiveAction(BaseAction):
|
||||
async def execute(self) -> Tuple[bool, str]:
|
||||
# 获取当前平台
|
||||
current_platform = self.platform
|
||||
|
||||
# 根据平台调整消息格式
|
||||
if current_platform == "qq":
|
||||
await self.send_text("[QQ] 这是QQ平台的消息")
|
||||
await self.send_emoji("🐧") # QQ企鹅表情
|
||||
elif current_platform == "wechat":
|
||||
await self.send_text("【微信】这是微信平台的消息")
|
||||
await self.send_emoji("💬") # 微信气泡表情
|
||||
elif current_platform == "discord":
|
||||
await self.send_text("**Discord** 这是Discord平台的消息")
|
||||
await self.send_emoji("🎮") # Discord游戏表情
|
||||
else:
|
||||
await self.send_text(f"未知平台: {current_platform}")
|
||||
|
||||
return True, f"发送了{current_platform}平台适配消息"
|
||||
```
|
||||
|
||||
## 🎨 消息格式化
|
||||
## 🎨 消息格式化和高级功能
|
||||
|
||||
### Markdown支持
|
||||
### 长消息分割
|
||||
|
||||
```python
|
||||
# 发送Markdown格式的消息(如果平台支持)
|
||||
markdown_message = """
|
||||
**粗体文本**
|
||||
*斜体文本*
|
||||
`代码块`
|
||||
[链接](https://example.com)
|
||||
"""
|
||||
# 自动处理长消息分割
|
||||
long_message = "这是一条很长的消息..." * 100
|
||||
|
||||
await self.send_text(markdown_message)
|
||||
# 新API会自动处理长消息分割
|
||||
await self.send_text(long_message)
|
||||
```
|
||||
|
||||
### 消息模板
|
||||
### 消息模板和格式化
|
||||
|
||||
```python
|
||||
# 使用模板生成消息
|
||||
def format_user_info(username: str, level: int, points: int) -> str:
|
||||
return f"""
|
||||
👤 用户信息
|
||||
━━━━━━━━━━━━━━━━━━
|
||||
📛 用户名: {username}
|
||||
⭐ 等级: Lv.{level}
|
||||
💰 积分: {points:,}
|
||||
━━━━━━━━━━━━━━━━━━
|
||||
""".strip()
|
||||
|
||||
# 使用模板
|
||||
user_info = format_user_info("张三", 15, 12580)
|
||||
await self.send_text(user_info)
|
||||
```
|
||||
|
||||
### 表情和Unicode
|
||||
|
||||
```python
|
||||
# 发送Unicode表情
|
||||
await self.send_text("消息发送成功 ✅")
|
||||
|
||||
# 发送表情包
|
||||
await self.send_type("emoji", "🎉")
|
||||
|
||||
# 组合文本和表情
|
||||
await self.send_text("恭喜你完成任务!🎊🎉")
|
||||
```
|
||||
|
||||
## 🔄 流式消息
|
||||
|
||||
### 获取聊天流信息
|
||||
|
||||
```python
|
||||
# 获取当前聊天流
|
||||
chat_stream = self.api.get_service("chat_stream")
|
||||
|
||||
if chat_stream:
|
||||
# 流基本信息
|
||||
stream_id = chat_stream.stream_id
|
||||
platform = chat_stream.platform
|
||||
|
||||
# 群聊信息
|
||||
if chat_stream.group_info:
|
||||
group_id = chat_stream.group_info.group_id
|
||||
group_name = chat_stream.group_info.group_name
|
||||
print(f"当前群聊: {group_name} ({group_id})")
|
||||
|
||||
# 用户信息
|
||||
user_id = chat_stream.user_info.user_id
|
||||
user_name = chat_stream.user_info.user_nickname
|
||||
print(f"当前用户: {user_name} ({user_id})")
|
||||
```
|
||||
|
||||
## 🚨 错误处理
|
||||
|
||||
### 消息发送错误处理
|
||||
|
||||
```python
|
||||
async def safe_send_message(self, content: str) -> bool:
|
||||
"""安全发送消息,包含错误处理"""
|
||||
try:
|
||||
await self.send_text(content)
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"消息发送失败: {e}")
|
||||
# 发送错误提示
|
||||
try:
|
||||
await self.send_text("❌ 消息发送失败,请稍后重试")
|
||||
except:
|
||||
pass # 避免循环错误
|
||||
return False
|
||||
```
|
||||
|
||||
### 目标消息发送错误处理
|
||||
|
||||
```python
|
||||
async def send_to_group_safely(self, text: str, group_id: str) -> bool:
|
||||
"""安全向群聊发送消息"""
|
||||
try:
|
||||
success = await self.api.send_text_to_group(
|
||||
text=text,
|
||||
group_id=group_id,
|
||||
platform="qq"
|
||||
class TemplateMessageAction(BaseAction):
|
||||
async def execute(self) -> Tuple[bool, str]:
|
||||
# 使用配置中的消息模板
|
||||
template = self.get_config("messages.greeting_template", "你好 {username}!")
|
||||
|
||||
# 格式化消息
|
||||
formatted_message = template.format(
|
||||
username=self.user_nickname,
|
||||
time=datetime.now().strftime("%H:%M"),
|
||||
platform=self.platform
|
||||
)
|
||||
|
||||
if not success:
|
||||
logger.warning(f"向群聊 {group_id} 发送消息失败")
|
||||
|
||||
return success
|
||||
await self.send_text(formatted_message)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"向群聊发送消息异常: {e}")
|
||||
return False
|
||||
# 根据配置决定是否发送表情
|
||||
if self.get_config("messages.include_emoji", True):
|
||||
await self.send_emoji("😊")
|
||||
|
||||
return True, "发送了模板化消息"
|
||||
```
|
||||
|
||||
## 📊 最佳实践
|
||||
|
||||
### 1. 消息长度控制
|
||||
### 条件消息发送
|
||||
|
||||
```python
|
||||
async def send_long_message(self, content: str, max_length: int = 500):
|
||||
"""发送长消息,自动分段"""
|
||||
if len(content) <= max_length:
|
||||
await self.send_text(content)
|
||||
else:
|
||||
# 分段发送
|
||||
parts = [content[i:i+max_length] for i in range(0, len(content), max_length)]
|
||||
for i, part in enumerate(parts):
|
||||
prefix = f"[{i+1}/{len(parts)}] " if len(parts) > 1 else ""
|
||||
await self.send_text(f"{prefix}{part}")
|
||||
class ConditionalMessageAction(BaseAction):
|
||||
async def execute(self) -> Tuple[bool, str]:
|
||||
# 根据用户类型发送不同消息
|
||||
if self.is_group:
|
||||
await self.send_text(f"群聊消息 - 当前群成员: @{self.user_nickname}")
|
||||
else:
|
||||
await self.send_text(f"私聊消息 - 你好 {self.user_nickname}!")
|
||||
|
||||
# 根据时间发送不同表情
|
||||
from datetime import datetime
|
||||
hour = datetime.now().hour
|
||||
|
||||
if 6 <= hour < 12:
|
||||
await self.send_emoji("🌅") # 早上
|
||||
elif 12 <= hour < 18:
|
||||
await self.send_emoji("☀️") # 下午
|
||||
else:
|
||||
await self.send_emoji("🌙") # 晚上
|
||||
|
||||
return True, "发送了条件化消息"
|
||||
```
|
||||
|
||||
## 🛠️ 高级消息发送功能
|
||||
|
||||
### 批量消息发送
|
||||
|
||||
```python
|
||||
class BatchMessageAction(BaseAction):
|
||||
async def execute(self) -> Tuple[bool, str]:
|
||||
messages = [
|
||||
("text", "第一条消息"),
|
||||
("emoji", "🎉"),
|
||||
("text", "第二条消息"),
|
||||
("emoji", "✨")
|
||||
]
|
||||
|
||||
for msg_type, content in messages:
|
||||
if msg_type == "text":
|
||||
await self.send_text(content)
|
||||
elif msg_type == "emoji":
|
||||
await self.send_emoji(content)
|
||||
|
||||
# 避免发送过快
|
||||
if i < len(parts) - 1:
|
||||
await asyncio.sleep(0.5)
|
||||
# 可选:添加延迟避免消息发送过快
|
||||
import asyncio
|
||||
await asyncio.sleep(0.5)
|
||||
|
||||
return True, "发送了批量消息"
|
||||
```
|
||||
|
||||
### 2. 消息格式规范
|
||||
### 错误处理和重试
|
||||
|
||||
```python
|
||||
class MessageFormatter:
|
||||
"""消息格式化工具类"""
|
||||
|
||||
@staticmethod
|
||||
def success(message: str) -> str:
|
||||
return f"✅ {message}"
|
||||
|
||||
@staticmethod
|
||||
def error(message: str) -> str:
|
||||
return f"❌ {message}"
|
||||
|
||||
@staticmethod
|
||||
def warning(message: str) -> str:
|
||||
return f"⚠️ {message}"
|
||||
|
||||
@staticmethod
|
||||
def info(message: str) -> str:
|
||||
return f"ℹ️ {message}"
|
||||
|
||||
# 使用示例
|
||||
await self.send_text(MessageFormatter.success("操作成功完成"))
|
||||
await self.send_text(MessageFormatter.error("操作失败,请重试"))
|
||||
class ReliableMessageAction(BaseAction):
|
||||
async def execute(self) -> Tuple[bool, str]:
|
||||
max_retries = 3
|
||||
retry_count = 0
|
||||
|
||||
while retry_count < max_retries:
|
||||
try:
|
||||
await self.send_text("重要消息")
|
||||
return True, "消息发送成功"
|
||||
except Exception as e:
|
||||
retry_count += 1
|
||||
logger.warning(f"消息发送失败 (尝试 {retry_count}/{max_retries}): {e}")
|
||||
|
||||
if retry_count < max_retries:
|
||||
import asyncio
|
||||
await asyncio.sleep(1) # 等待1秒后重试
|
||||
|
||||
return False, "消息发送失败,已达到最大重试次数"
|
||||
```
|
||||
|
||||
### 3. 异步消息处理
|
||||
## 📝 最佳实践
|
||||
|
||||
### 1. 消息发送最佳实践
|
||||
|
||||
```python
|
||||
async def batch_send_messages(self, messages: List[str]):
|
||||
"""批量发送消息"""
|
||||
tasks = []
|
||||
|
||||
for message in messages:
|
||||
task = self.send_text(message)
|
||||
tasks.append(task)
|
||||
|
||||
# 并发发送,但控制并发数
|
||||
semaphore = asyncio.Semaphore(3) # 最多3个并发
|
||||
|
||||
async def send_with_limit(message):
|
||||
async with semaphore:
|
||||
# ✅ 好的做法
|
||||
class GoodMessageAction(BaseAction):
|
||||
async def execute(self) -> Tuple[bool, str]:
|
||||
# 1. 检查配置
|
||||
if not self.get_config("features.enable_messages", True):
|
||||
return True, "消息功能已禁用"
|
||||
|
||||
# 2. 简洁的消息发送
|
||||
await self.send_text("简洁明了的消息")
|
||||
|
||||
# 3. 适当的表情使用
|
||||
if self.get_config("features.enable_emoji", True):
|
||||
await self.send_emoji("😊")
|
||||
|
||||
return True, "消息发送完成"
|
||||
|
||||
# ❌ 避免的做法
|
||||
class BadMessageAction(BaseAction):
|
||||
async def execute(self) -> Tuple[bool, str]:
|
||||
# 避免:过长的消息
|
||||
await self.send_text("这是一条非常非常长的消息" * 50)
|
||||
|
||||
# 避免:过多的表情
|
||||
for emoji in ["😊", "🎉", "✨", "🌟", "💫"]:
|
||||
await self.send_emoji(emoji)
|
||||
|
||||
return True, "发送了糟糕的消息"
|
||||
```
|
||||
|
||||
### 2. 错误处理
|
||||
|
||||
```python
|
||||
# ✅ 推荐的错误处理
|
||||
class SafeMessageAction(BaseAction):
|
||||
async def execute(self) -> Tuple[bool, str]:
|
||||
try:
|
||||
message = self.get_config("messages.default", "默认消息")
|
||||
await self.send_text(message)
|
||||
|
||||
await asyncio.gather(*[send_with_limit(msg) for msg in messages])
|
||||
return True, "消息发送成功"
|
||||
except Exception as e:
|
||||
logger.error(f"消息发送失败: {e}")
|
||||
# 可选:发送备用消息
|
||||
await self.send_text("消息发送遇到问题,请稍后再试")
|
||||
return False, f"发送失败: {str(e)}"
|
||||
```
|
||||
|
||||
### 4. 消息缓存
|
||||
### 3. 性能优化
|
||||
|
||||
```python
|
||||
class MessageCache:
|
||||
"""消息缓存管理"""
|
||||
|
||||
def __init__(self):
|
||||
self._cache = {}
|
||||
self._max_size = 100
|
||||
|
||||
def get_cached_message(self, key: str) -> Optional[str]:
|
||||
return self._cache.get(key)
|
||||
|
||||
def cache_message(self, key: str, message: str):
|
||||
if len(self._cache) >= self._max_size:
|
||||
# 删除最旧的缓存
|
||||
oldest_key = next(iter(self._cache))
|
||||
del self._cache[oldest_key]
|
||||
# ✅ 性能友好的消息发送
|
||||
class OptimizedMessageAction(BaseAction):
|
||||
async def execute(self) -> Tuple[bool, str]:
|
||||
# 合并多个短消息为一条长消息
|
||||
parts = [
|
||||
"第一部分信息",
|
||||
"第二部分信息",
|
||||
"第三部分信息"
|
||||
]
|
||||
|
||||
self._cache[key] = message
|
||||
|
||||
# 使用缓存避免重复生成消息
|
||||
cache = MessageCache()
|
||||
|
||||
async def send_user_info(self, user_id: str):
|
||||
cache_key = f"user_info_{user_id}"
|
||||
cached_message = cache.get_cached_message(cache_key)
|
||||
|
||||
if cached_message:
|
||||
await self.send_text(cached_message)
|
||||
else:
|
||||
# 生成新消息
|
||||
message = await self._generate_user_info(user_id)
|
||||
cache.cache_message(cache_key, message)
|
||||
await self.send_text(message)
|
||||
combined_message = "\n".join(parts)
|
||||
await self.send_text(combined_message)
|
||||
|
||||
return True, "发送了优化的消息"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
🎉 **现在你已经掌握了消息API的完整用法!继续学习其他API接口。**
|
||||
通过新的API格式,消息发送变得更加简洁高效!
|
||||
@@ -73,7 +73,7 @@ class SimpleCommand(BaseCommand):
|
||||
|
||||
### 参数捕获
|
||||
|
||||
使用命名组 `(?P<name>pattern)` 捕获参数:
|
||||
使用命名组 `(?P<n>pattern)` 捕获参数:
|
||||
|
||||
```python
|
||||
class UserCommand(BaseCommand):
|
||||
@@ -336,7 +336,7 @@ class SystemInfoCommand(BaseCommand):
|
||||
|
||||
async def _show_plugin_info(self):
|
||||
"""显示插件信息"""
|
||||
# 通过API获取插件信息
|
||||
# 通过配置获取插件信息
|
||||
plugins = await self._get_loaded_plugins()
|
||||
|
||||
plugin_info = f"""
|
||||
@@ -349,7 +349,7 @@ class SystemInfoCommand(BaseCommand):
|
||||
|
||||
async def _get_loaded_plugins(self) -> list:
|
||||
"""获取已加载的插件列表"""
|
||||
# 这里可以通过self.api获取实际的插件信息
|
||||
# 这里可以通过配置或API获取实际的插件信息
|
||||
return [
|
||||
{"name": "core_actions", "active": True},
|
||||
{"name": "example_plugin", "active": True},
|
||||
@@ -386,6 +386,55 @@ class CustomPrefixCommand(BaseCommand):
|
||||
return True, f"投掷了{count}面骰子,结果{result}"
|
||||
```
|
||||
|
||||
## 🔧 新API格式使用指南
|
||||
|
||||
### 消息发送
|
||||
|
||||
```python
|
||||
# 新API格式 ✅
|
||||
await self.send_text("消息内容")
|
||||
await self.send_emoji("😊")
|
||||
|
||||
# 旧API格式 ❌
|
||||
await self.api.send_text_to_group("消息内容", group_id, "qq")
|
||||
```
|
||||
|
||||
### 配置访问
|
||||
|
||||
```python
|
||||
# 新API格式 ✅
|
||||
config_value = self.get_config("section.key", "default_value")
|
||||
|
||||
# 旧API格式 ❌
|
||||
config_value = self.api.get_config("section.key", "default_value")
|
||||
```
|
||||
|
||||
### 用户信息获取
|
||||
|
||||
```python
|
||||
# 新API格式 ✅
|
||||
user_id = self.user_id
|
||||
user_nickname = self.user_nickname
|
||||
is_group_chat = self.is_group
|
||||
|
||||
# 旧API格式 ❌
|
||||
user_id = self.message.message_info.user_info.user_id
|
||||
```
|
||||
|
||||
### 动作记录
|
||||
|
||||
```python
|
||||
# 新API格式 ✅ (在Action中)
|
||||
await self.store_action_info(
|
||||
action_build_into_prompt=True,
|
||||
action_prompt_display="执行了某操作",
|
||||
action_done=True
|
||||
)
|
||||
|
||||
# 旧API格式 ❌
|
||||
await self.api.store_action_info(...)
|
||||
```
|
||||
|
||||
## 📊 性能优化建议
|
||||
|
||||
### 1. 正则表达式优化
|
||||
@@ -398,83 +447,39 @@ command_pattern = r"^/ping$"
|
||||
command_pattern = r"^/(?:ping|pong|test|check|status|info|help|...)"
|
||||
|
||||
# ✅ 好的做法 - 分离复杂逻辑
|
||||
class PingCommand(BaseCommand):
|
||||
command_pattern = r"^/ping$"
|
||||
|
||||
class StatusCommand(BaseCommand):
|
||||
command_pattern = r"^/status$"
|
||||
```
|
||||
|
||||
### 2. 参数验证
|
||||
|
||||
```python
|
||||
# ✅ 好的做法 - 早期验证
|
||||
async def execute(self) -> Tuple[bool, Optional[str]]:
|
||||
# 快速参数验证
|
||||
username = self.matched_groups.get("username")
|
||||
if not username or len(username) < 2:
|
||||
await self.send_text("❌ 用户名不合法")
|
||||
return False, "参数验证失败"
|
||||
if not username:
|
||||
await self.send_text("❌ 请提供用户名")
|
||||
return False, "缺少参数"
|
||||
|
||||
# 主要逻辑
|
||||
...
|
||||
# 继续处理...
|
||||
```
|
||||
|
||||
### 3. 异常处理
|
||||
### 3. 错误处理
|
||||
|
||||
```python
|
||||
# ✅ 好的做法 - 完整错误处理
|
||||
async def execute(self) -> Tuple[bool, Optional[str]]:
|
||||
try:
|
||||
# 命令逻辑
|
||||
result = await self._do_command()
|
||||
# 主要逻辑
|
||||
result = await self._process_command()
|
||||
return True, "执行成功"
|
||||
except ValueError as e:
|
||||
await self.send_text(f"❌ 参数错误: {e}")
|
||||
return False, f"参数错误: {e}"
|
||||
except Exception as e:
|
||||
logger.error(f"{self.log_prefix} 命令执行失败: {e}")
|
||||
await self.send_text("❌ 命令执行失败")
|
||||
await self.send_text(f"❌ 执行失败: {e}")
|
||||
return False, f"执行失败: {e}"
|
||||
```
|
||||
|
||||
## 🐛 调试技巧
|
||||
|
||||
### 1. 正则测试
|
||||
|
||||
```python
|
||||
import re
|
||||
|
||||
pattern = r"^/user\s+(?P<action>add|del)\s+(?P<username>\w+)$"
|
||||
test_inputs = [
|
||||
"/user add 张三",
|
||||
"/user del 李四",
|
||||
"/user info 王五", # 不匹配
|
||||
]
|
||||
|
||||
for input_text in test_inputs:
|
||||
match = re.match(pattern, input_text)
|
||||
print(f"'{input_text}' -> {match.groupdict() if match else 'No match'}")
|
||||
```
|
||||
|
||||
### 2. 参数调试
|
||||
|
||||
```python
|
||||
async def execute(self) -> Tuple[bool, Optional[str]]:
|
||||
# 调试输出
|
||||
logger.debug(f"匹配组: {self.matched_groups}")
|
||||
logger.debug(f"原始消息: {self.message.processed_plain_text}")
|
||||
|
||||
# 命令逻辑...
|
||||
```
|
||||
|
||||
### 3. 拦截测试
|
||||
|
||||
```python
|
||||
# 测试不同的拦截设置
|
||||
intercept_message = True # 测试拦截
|
||||
intercept_message = False # 测试不拦截
|
||||
|
||||
# 观察后续Action是否被触发
|
||||
```
|
||||
通过新的API格式,Command开发变得更加简洁和直观!
|
||||
|
||||
## 🎯 最佳实践
|
||||
|
||||
|
||||
@@ -1,255 +0,0 @@
|
||||
# 🔧 插件配置访问指南
|
||||
|
||||
> **💡 阅读须知**
|
||||
>
|
||||
> 本文主要介绍如何在插件的 **Action** 或 **Command** 组件中 **访问(读取)** 配置值。
|
||||
>
|
||||
> 如果你还不了解如何为插件 **定义** 配置并让系统 **自动生成** 带注释的 `config.toml` 文件,请务必先阅读 ➡️ **[⚙️ 插件配置定义指南](configuration-guide.md)**。
|
||||
|
||||
## 问题描述
|
||||
|
||||
在插件开发中,你可能遇到这样的问题:
|
||||
- `get_config`方法只在`BasePlugin`类中
|
||||
- `BaseAction`和`BaseCommand`无法直接继承这个方法
|
||||
- 想要在Action或Command中访问插件配置
|
||||
|
||||
## ✅ 解决方案
|
||||
|
||||
**直接使用 `self.api.get_config()` 方法!**
|
||||
|
||||
系统已经自动为你处理了配置传递,你只需要通过`PluginAPI`访问配置即可。
|
||||
|
||||
## 📖 快速示例
|
||||
|
||||
### 在Action中访问配置
|
||||
|
||||
```python
|
||||
from src.plugin_system import BaseAction
|
||||
|
||||
class MyAction(BaseAction):
|
||||
async def execute(self):
|
||||
# 方法1: 获取配置值(带默认值)
|
||||
api_key = self.api.get_config("api.key", "default_key")
|
||||
timeout = self.api.get_config("api.timeout", 30)
|
||||
|
||||
# 方法2: 检查配置是否存在
|
||||
if self.api.has_config("features.premium"):
|
||||
premium_enabled = self.api.get_config("features.premium")
|
||||
# 使用高级功能
|
||||
|
||||
# 方法3: 支持嵌套键访问
|
||||
log_level = self.api.get_config("advanced.logging.level", "INFO")
|
||||
|
||||
# 方法4: 获取所有配置
|
||||
all_config = self.api.get_all_config()
|
||||
|
||||
await self.send_text(f"API密钥: {api_key}")
|
||||
return True, "配置访问成功"
|
||||
```
|
||||
|
||||
### 在Command中访问配置
|
||||
|
||||
```python
|
||||
from src.plugin_system import BaseCommand
|
||||
|
||||
class MyCommand(BaseCommand):
|
||||
async def execute(self):
|
||||
# 使用方式与Action完全相同
|
||||
welcome_msg = self.api.get_config("messages.welcome", "欢迎!")
|
||||
max_results = self.api.get_config("search.max_results", 10)
|
||||
|
||||
# 根据配置执行不同逻辑
|
||||
if self.api.get_config("features.debug_mode", False):
|
||||
await self.send_text(f"调试模式已启用,最大结果数: {max_results}")
|
||||
|
||||
await self.send_text(welcome_msg)
|
||||
return True, "命令执行完成"
|
||||
```
|
||||
|
||||
## 🔧 API方法详解
|
||||
|
||||
### 1. `get_config(key, default=None)`
|
||||
|
||||
获取配置值,支持嵌套键访问:
|
||||
|
||||
```python
|
||||
# 简单键
|
||||
value = self.api.get_config("timeout", 30)
|
||||
|
||||
# 嵌套键(用点号分隔)
|
||||
value = self.api.get_config("database.connection.host", "localhost")
|
||||
value = self.api.get_config("features.ai.model", "gpt-3.5-turbo")
|
||||
```
|
||||
|
||||
### 2. `has_config(key)`
|
||||
|
||||
检查配置项是否存在:
|
||||
|
||||
```python
|
||||
if self.api.has_config("api.secret_key"):
|
||||
# 配置存在,可以安全使用
|
||||
secret = self.api.get_config("api.secret_key")
|
||||
else:
|
||||
# 配置不存在,使用默认行为
|
||||
pass
|
||||
```
|
||||
|
||||
### 3. `get_all_config()`
|
||||
|
||||
获取所有配置的副本:
|
||||
|
||||
```python
|
||||
all_config = self.api.get_all_config()
|
||||
for section, config in all_config.items():
|
||||
print(f"配置节: {section}, 包含 {len(config)} 项配置")
|
||||
```
|
||||
|
||||
## 📁 配置文件示例
|
||||
|
||||
假设你的插件有这样的配置文件 `config.toml`:
|
||||
|
||||
```toml
|
||||
[api]
|
||||
key = "your_api_key"
|
||||
timeout = 30
|
||||
base_url = "https://api.example.com"
|
||||
|
||||
[features]
|
||||
enable_cache = true
|
||||
debug_mode = false
|
||||
max_retries = 3
|
||||
|
||||
[messages]
|
||||
welcome = "欢迎使用我的插件!"
|
||||
error = "出现了错误,请稍后重试"
|
||||
|
||||
[advanced]
|
||||
[advanced.logging]
|
||||
level = "INFO"
|
||||
file_path = "logs/plugin.log"
|
||||
|
||||
[advanced.cache]
|
||||
ttl_seconds = 3600
|
||||
max_size = 100
|
||||
```
|
||||
|
||||
## 🎯 实际使用案例
|
||||
|
||||
### 案例1:API调用配置
|
||||
|
||||
```python
|
||||
class ApiAction(BaseAction):
|
||||
async def execute(self):
|
||||
# 获取API配置
|
||||
api_key = self.api.get_config("api.key")
|
||||
if not api_key:
|
||||
await self.send_text("❌ API密钥未配置")
|
||||
return False, "缺少API密钥"
|
||||
|
||||
timeout = self.api.get_config("api.timeout", 30)
|
||||
base_url = self.api.get_config("api.base_url", "https://api.example.com")
|
||||
|
||||
# 使用配置进行API调用
|
||||
# ... API调用逻辑
|
||||
|
||||
return True, "API调用完成"
|
||||
```
|
||||
|
||||
### 案例2:功能开关配置
|
||||
|
||||
```python
|
||||
class FeatureCommand(BaseCommand):
|
||||
async def execute(self):
|
||||
# 检查功能开关
|
||||
if not self.api.get_config("features.enable_cache", True):
|
||||
await self.send_text("缓存功能已禁用")
|
||||
return True, "功能被禁用"
|
||||
|
||||
# 检查调试模式
|
||||
debug_mode = self.api.get_config("features.debug_mode", False)
|
||||
if debug_mode:
|
||||
await self.send_text("🐛 调试模式已启用")
|
||||
|
||||
max_retries = self.api.get_config("features.max_retries", 3)
|
||||
# 使用重试配置
|
||||
|
||||
return True, "功能执行完成"
|
||||
```
|
||||
|
||||
### 案例3:个性化消息配置
|
||||
|
||||
```python
|
||||
class WelcomeAction(BaseAction):
|
||||
async def execute(self):
|
||||
# 获取个性化消息
|
||||
welcome_msg = self.api.get_config("messages.welcome", "欢迎!")
|
||||
|
||||
# 检查是否有自定义问候语列表
|
||||
if self.api.has_config("messages.custom_greetings"):
|
||||
greetings = self.api.get_config("messages.custom_greetings", [])
|
||||
if greetings:
|
||||
import random
|
||||
welcome_msg = random.choice(greetings)
|
||||
|
||||
await self.send_text(welcome_msg)
|
||||
return True, "发送了个性化问候"
|
||||
```
|
||||
|
||||
## 🔄 配置传递机制
|
||||
|
||||
系统自动处理配置传递,无需手动操作:
|
||||
|
||||
1. **插件初始化** → `BasePlugin`加载`config.toml`到`self.config`
|
||||
2. **组件注册** → 系统记录插件配置
|
||||
3. **组件实例化** → 自动传递`plugin_config`参数给Action/Command
|
||||
4. **API初始化** → 配置保存到`PluginAPI`实例中
|
||||
5. **组件使用** → 通过`self.api.get_config()`访问
|
||||
|
||||
## ⚠️ 注意事项
|
||||
|
||||
### 1. 总是提供默认值
|
||||
|
||||
```python
|
||||
# ✅ 好的做法
|
||||
timeout = self.api.get_config("api.timeout", 30)
|
||||
|
||||
# ❌ 避免这样做
|
||||
timeout = self.api.get_config("api.timeout") # 可能返回None
|
||||
```
|
||||
|
||||
### 2. 验证配置类型
|
||||
|
||||
```python
|
||||
# 获取配置后验证类型
|
||||
max_items = self.api.get_config("list.max_items", 10)
|
||||
if not isinstance(max_items, int) or max_items <= 0:
|
||||
max_items = 10 # 使用安全的默认值
|
||||
```
|
||||
|
||||
### 3. 缓存复杂配置解析
|
||||
|
||||
```python
|
||||
class MyAction(BaseAction):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
# 在初始化时解析复杂配置,避免重复解析
|
||||
self._api_config = self._parse_api_config()
|
||||
|
||||
def _parse_api_config(self):
|
||||
return {
|
||||
'key': self.api.get_config("api.key", ""),
|
||||
'timeout': self.api.get_config("api.timeout", 30),
|
||||
'retries': self.api.get_config("api.max_retries", 3)
|
||||
}
|
||||
```
|
||||
|
||||
## 🎉 总结
|
||||
|
||||
现在你知道了!在Action和Command中访问配置很简单:
|
||||
|
||||
```python
|
||||
# 这就是你需要的全部代码!
|
||||
config_value = self.api.get_config("your.config.key", "default_value")
|
||||
```
|
||||
|
||||
不需要继承`BasePlugin`,不需要复杂的配置传递,`PluginAPI`已经为你准备好了一切!
|
||||
@@ -1,10 +1,30 @@
|
||||
# ⚙️ 插件配置定义指南
|
||||
# ⚙️ 插件配置完整指南
|
||||
|
||||
本文档将指导你如何为你的插件定义一个健壮、规范且自带文档的配置文件。
|
||||
本文档将全面指导你如何为你的插件**定义配置**和在组件中**访问配置**,帮助你构建一个健壮、规范且自带文档的配置系统。
|
||||
|
||||
## 核心理念:Schema驱动的配置
|
||||
> **🚨 重要原则:任何时候都不要手动创建 config.toml 文件!**
|
||||
>
|
||||
> 系统会根据你在代码中定义的 `config_schema` 自动生成配置文件。手动创建配置文件会破坏自动化流程,导致配置不一致、缺失注释和文档等问题。
|
||||
|
||||
在新版插件系统中,我们引入了一套 **配置Schema(模式)驱动** 的机制。你不再需要手动创建和维护 `config.toml` 文件,而是通过在插件代码中 **声明配置的结构**,系统将为你完成剩下的工作。
|
||||
## 📖 目录
|
||||
|
||||
1. [配置定义:Schema驱动的配置系统](#配置定义schema驱动的配置系统)
|
||||
2. [配置访问:在Action和Command中使用配置](#配置访问在action和command中使用配置)
|
||||
3. [完整示例:从定义到使用](#完整示例从定义到使用)
|
||||
4. [最佳实践与注意事项](#最佳实践与注意事项)
|
||||
|
||||
---
|
||||
|
||||
## 配置定义:Schema驱动的配置系统
|
||||
|
||||
### 核心理念:Schema驱动的配置
|
||||
|
||||
在新版插件系统中,我们引入了一套 **配置Schema(模式)驱动** 的机制。**你不需要也不应该手动创建和维护 `config.toml` 文件**,而是通过在插件代码中 **声明配置的结构**,系统将为你完成剩下的工作。
|
||||
|
||||
> **⚠️ 绝对不要手动创建 config.toml 文件!**
|
||||
>
|
||||
> - ❌ **错误做法**:手动在插件目录下创建 `config.toml` 文件
|
||||
> - ✅ **正确做法**:在插件代码中定义 `config_schema`,让系统自动生成配置文件
|
||||
|
||||
**核心优势:**
|
||||
|
||||
@@ -14,9 +34,24 @@
|
||||
- **健壮性 (Robustness)**: 在代码中直接定义配置的类型和默认值,减少了因配置错误导致的运行时问题。
|
||||
- **易于管理 (Easy Management)**: 生成的配置文件可以方便地加入 `.gitignore`,避免将个人配置(如API Key)提交到版本库。
|
||||
|
||||
---
|
||||
### 配置生成工作流程
|
||||
|
||||
## 如何定义配置
|
||||
```mermaid
|
||||
graph TD
|
||||
A[编写插件代码] --> B[定义 config_schema]
|
||||
B --> C[首次加载插件]
|
||||
C --> D{config.toml 是否存在?}
|
||||
D -->|不存在| E[系统自动生成 config.toml]
|
||||
D -->|存在| F[加载现有配置文件]
|
||||
E --> G[配置完成,插件可用]
|
||||
F --> G
|
||||
|
||||
style E fill:#90EE90
|
||||
style B fill:#87CEEB
|
||||
style G fill:#DDA0DD
|
||||
```
|
||||
|
||||
### 如何定义配置
|
||||
|
||||
配置的定义在你的插件主类(继承自 `BasePlugin`)中完成,主要通过两个类属性:
|
||||
|
||||
@@ -41,18 +76,13 @@ class ConfigField:
|
||||
choices: Optional[List[Any]] = None # 可选值列表 (可选)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 完整示例
|
||||
### 配置定义示例
|
||||
|
||||
让我们以一个功能丰富的 `MutePlugin` 为例,看看如何定义它的配置。
|
||||
|
||||
### 1. 插件代码 (`plugin.py`)
|
||||
|
||||
```python
|
||||
# src/plugins/built_in/mute_plugin/plugin.py
|
||||
|
||||
# ... 其他导入 ...
|
||||
from src.plugin_system import BasePlugin, register_plugin
|
||||
from src.plugin_system.base.config_types import ConfigField
|
||||
from typing import List, Tuple, Type
|
||||
@@ -119,13 +149,20 @@ class MutePlugin(BasePlugin):
|
||||
}
|
||||
|
||||
def get_plugin_components(self) -> List[Tuple[ComponentInfo, Type]]:
|
||||
# ... 组件注册逻辑 ...
|
||||
# 在这里可以通过 self.get_config() 来获取配置值
|
||||
pass
|
||||
|
||||
enable_smart_mute = self.get_config("components.enable_smart_mute", True)
|
||||
enable_mute_command = self.get_config("components.enable_mute_command", False)
|
||||
|
||||
components = []
|
||||
if enable_smart_mute:
|
||||
components.append((SmartMuteAction.get_action_info(), SmartMuteAction))
|
||||
if enable_mute_command:
|
||||
components.append((MuteCommand.get_command_info(), MuteCommand))
|
||||
|
||||
return components
|
||||
```
|
||||
|
||||
### 2. 自动生成的配置文件 (`config.toml`)
|
||||
### 自动生成的配置文件
|
||||
|
||||
当 `mute_plugin` 首次加载且其目录中不存在 `config.toml` 时,系统会自动创建以下文件:
|
||||
|
||||
@@ -190,13 +227,405 @@ level = "INFO"
|
||||
# 日志记录前缀
|
||||
# 示例: [MyMutePlugin]
|
||||
prefix = "[MutePlugin]"
|
||||
|
||||
```
|
||||
|
||||
## 最佳实践
|
||||
---
|
||||
|
||||
1. **定义优于创建**: 始终优先在 `plugin.py` 中定义 `config_schema`,而不是手动创建 `config.toml`。
|
||||
2. **描述清晰**: 为每个 `ConfigField` 和 `config_section_descriptions` 编写清晰、准确的描述。这会直接成为你的插件文档的一部分。
|
||||
3. **提供合理默认值**: 确保你的插件在默认配置下就能正常运行(或处于一个安全禁用的状态)。
|
||||
4. **gitignore**: 将 `plugins/*/config.toml` 或 `src/plugins/built_in/*/config.toml` 加入 `.gitignore`,以避免提交个人敏感信息。
|
||||
5. **访问配置**: 在插件的任何地方,统一使用 `self.api.get_config("section.key", "default_value")` 来安全地获取配置值。详细请参考 [**插件配置访问指南**](config-access-guide.md)。
|
||||
## 配置访问:在Action和Command中使用配置
|
||||
|
||||
### 问题描述
|
||||
|
||||
在插件开发中,你可能遇到这样的问题:
|
||||
- 想要在Action或Command中访问插件配置
|
||||
|
||||
### ✅ 解决方案
|
||||
|
||||
**直接使用 `self.get_config()` 方法!**
|
||||
|
||||
系统已经自动为你处理了配置传递,你只需要通过组件内置的 `get_config` 方法访问配置即可。
|
||||
|
||||
### 📖 快速示例
|
||||
|
||||
#### 在Action中访问配置
|
||||
|
||||
```python
|
||||
from src.plugin_system import BaseAction
|
||||
|
||||
class MyAction(BaseAction):
|
||||
async def execute(self):
|
||||
# 方法1: 获取配置值(带默认值)
|
||||
api_key = self.get_config("api.key", "default_key")
|
||||
timeout = self.get_config("api.timeout", 30)
|
||||
|
||||
# 方法2: 支持嵌套键访问
|
||||
log_level = self.get_config("advanced.logging.level", "INFO")
|
||||
|
||||
# 方法3: 直接访问顶层配置
|
||||
enable_feature = self.get_config("features.enable_smart", False)
|
||||
|
||||
# 使用配置值
|
||||
if enable_feature:
|
||||
await self.send_text(f"API密钥: {api_key}")
|
||||
|
||||
return True, "配置访问成功"
|
||||
```
|
||||
|
||||
#### 在Command中访问配置
|
||||
|
||||
```python
|
||||
from src.plugin_system import BaseCommand
|
||||
|
||||
class MyCommand(BaseCommand):
|
||||
async def execute(self):
|
||||
# 使用方式与Action完全相同
|
||||
welcome_msg = self.get_config("messages.welcome", "欢迎!")
|
||||
max_results = self.get_config("search.max_results", 10)
|
||||
|
||||
# 根据配置执行不同逻辑
|
||||
if self.get_config("features.debug_mode", False):
|
||||
await self.send_text(f"调试模式已启用,最大结果数: {max_results}")
|
||||
|
||||
await self.send_text(welcome_msg)
|
||||
return True, "命令执行完成"
|
||||
```
|
||||
|
||||
### 🔧 API方法详解
|
||||
|
||||
#### 1. `get_config(key, default=None)`
|
||||
|
||||
获取配置值,支持嵌套键访问:
|
||||
|
||||
```python
|
||||
# 简单键
|
||||
value = self.get_config("timeout", 30)
|
||||
|
||||
# 嵌套键(用点号分隔)
|
||||
value = self.get_config("database.connection.host", "localhost")
|
||||
value = self.get_config("features.ai.model", "gpt-3.5-turbo")
|
||||
```
|
||||
|
||||
#### 2. 类型安全的配置访问
|
||||
|
||||
```python
|
||||
# 确保正确的类型
|
||||
max_retries = self.get_config("api.max_retries", 3)
|
||||
if not isinstance(max_retries, int):
|
||||
max_retries = 3 # 使用安全的默认值
|
||||
|
||||
# 布尔值配置
|
||||
debug_mode = self.get_config("features.debug_mode", False)
|
||||
if debug_mode:
|
||||
# 调试功能逻辑
|
||||
pass
|
||||
```
|
||||
|
||||
#### 3. 配置驱动的组件行为
|
||||
|
||||
```python
|
||||
class ConfigDrivenAction(BaseAction):
|
||||
async def execute(self):
|
||||
# 根据配置决定激活行为
|
||||
activation_config = {
|
||||
"use_keywords": self.get_config("activation.use_keywords", True),
|
||||
"use_llm": self.get_config("activation.use_llm", False),
|
||||
"keywords": self.get_config("activation.keywords", []),
|
||||
}
|
||||
|
||||
# 根据配置调整功能
|
||||
features = {
|
||||
"enable_emoji": self.get_config("features.enable_emoji", True),
|
||||
"enable_llm_reply": self.get_config("features.enable_llm_reply", False),
|
||||
"max_length": self.get_config("output.max_length", 200),
|
||||
}
|
||||
|
||||
# 使用配置执行逻辑
|
||||
if features["enable_llm_reply"]:
|
||||
# 使用LLM生成回复
|
||||
pass
|
||||
else:
|
||||
# 使用模板回复
|
||||
pass
|
||||
|
||||
return True, "配置驱动执行完成"
|
||||
```
|
||||
|
||||
### 🔄 配置传递机制
|
||||
|
||||
系统自动处理配置传递,无需手动操作:
|
||||
|
||||
1. **插件初始化** → `BasePlugin`加载`config.toml`到`self.config`
|
||||
2. **组件注册** → 系统记录插件配置
|
||||
3. **组件实例化** → 自动传递`plugin_config`参数给Action/Command
|
||||
4. **配置访问** → 组件通过`self.get_config()`直接访问配置
|
||||
|
||||
---
|
||||
|
||||
## 完整示例:从定义到使用
|
||||
|
||||
### 插件定义
|
||||
|
||||
```python
|
||||
from src.plugin_system.base.config_types import ConfigField
|
||||
|
||||
@register_plugin
|
||||
class GreetingPlugin(BasePlugin):
|
||||
"""问候插件完整示例"""
|
||||
|
||||
plugin_name = "greeting_plugin"
|
||||
plugin_description = "智能问候插件,展示配置定义和访问的完整流程"
|
||||
plugin_version = "1.0.0"
|
||||
config_file_name = "config.toml"
|
||||
|
||||
# 配置节描述
|
||||
config_section_descriptions = {
|
||||
"plugin": "插件基本信息",
|
||||
"greeting": "问候功能配置",
|
||||
"features": "功能开关配置",
|
||||
"messages": "消息模板配置"
|
||||
}
|
||||
|
||||
# 配置Schema定义
|
||||
config_schema = {
|
||||
"plugin": {
|
||||
"enabled": ConfigField(type=bool, default=True, description="是否启用插件"),
|
||||
"version": ConfigField(type=str, default="1.0.0", description="插件版本")
|
||||
},
|
||||
"greeting": {
|
||||
"template": ConfigField(
|
||||
type=str,
|
||||
default="你好,{username}!欢迎使用问候插件!",
|
||||
description="问候消息模板"
|
||||
),
|
||||
"enable_emoji": ConfigField(type=bool, default=True, description="是否启用表情符号"),
|
||||
"enable_llm": ConfigField(type=bool, default=False, description="是否使用LLM生成个性化问候")
|
||||
},
|
||||
"features": {
|
||||
"smart_detection": ConfigField(type=bool, default=True, description="是否启用智能检测"),
|
||||
"random_greeting": ConfigField(type=bool, default=False, description="是否使用随机问候语"),
|
||||
"max_greetings_per_hour": ConfigField(type=int, default=5, description="每小时最大问候次数")
|
||||
},
|
||||
"messages": {
|
||||
"custom_greetings": ConfigField(
|
||||
type=list,
|
||||
default=["你好!", "嗨!", "欢迎!"],
|
||||
description="自定义问候语列表"
|
||||
),
|
||||
"error_message": ConfigField(
|
||||
type=str,
|
||||
default="问候功能暂时不可用",
|
||||
description="错误时显示的消息"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
def get_plugin_components(self) -> List[Tuple[ComponentInfo, Type]]:
|
||||
"""根据配置动态注册组件"""
|
||||
components = []
|
||||
|
||||
# 根据配置决定是否注册组件
|
||||
if self.get_config("plugin.enabled", True):
|
||||
components.append((SmartGreetingAction.get_action_info(), SmartGreetingAction))
|
||||
components.append((GreetingCommand.get_command_info(), GreetingCommand))
|
||||
|
||||
return components
|
||||
```
|
||||
|
||||
### Action组件使用配置
|
||||
|
||||
```python
|
||||
class SmartGreetingAction(BaseAction):
|
||||
"""智能问候Action - 展示配置访问"""
|
||||
|
||||
focus_activation_type = ActionActivationType.KEYWORD
|
||||
normal_activation_type = ActionActivationType.KEYWORD
|
||||
activation_keywords = ["你好", "hello", "hi"]
|
||||
|
||||
async def execute(self) -> Tuple[bool, str]:
|
||||
"""执行智能问候,大量使用配置"""
|
||||
try:
|
||||
# 检查插件是否启用
|
||||
if not self.get_config("plugin.enabled", True):
|
||||
return False, "插件已禁用"
|
||||
|
||||
# 获取问候配置
|
||||
template = self.get_config("greeting.template", "你好,{username}!")
|
||||
enable_emoji = self.get_config("greeting.enable_emoji", True)
|
||||
enable_llm = self.get_config("greeting.enable_llm", False)
|
||||
|
||||
# 获取功能配置
|
||||
smart_detection = self.get_config("features.smart_detection", True)
|
||||
random_greeting = self.get_config("features.random_greeting", False)
|
||||
max_per_hour = self.get_config("features.max_greetings_per_hour", 5)
|
||||
|
||||
# 获取消息配置
|
||||
custom_greetings = self.get_config("messages.custom_greetings", [])
|
||||
error_message = self.get_config("messages.error_message", "问候功能不可用")
|
||||
|
||||
# 根据配置执行不同逻辑
|
||||
username = self.action_data.get("username", "用户")
|
||||
|
||||
if random_greeting and custom_greetings:
|
||||
# 使用随机自定义问候语
|
||||
import random
|
||||
greeting_msg = random.choice(custom_greetings)
|
||||
elif enable_llm:
|
||||
# 使用LLM生成个性化问候
|
||||
greeting_msg = await self._generate_llm_greeting(username)
|
||||
else:
|
||||
# 使用模板问候
|
||||
greeting_msg = template.format(username=username)
|
||||
|
||||
# 发送问候消息
|
||||
await self.send_text(greeting_msg)
|
||||
|
||||
# 根据配置发送表情
|
||||
if enable_emoji:
|
||||
await self.send_emoji("😊")
|
||||
|
||||
return True, f"向{username}发送了问候"
|
||||
|
||||
except Exception as e:
|
||||
# 使用配置的错误消息
|
||||
await self.send_text(self.get_config("messages.error_message", "出错了"))
|
||||
return False, f"问候失败: {str(e)}"
|
||||
|
||||
async def _generate_llm_greeting(self, username: str) -> str:
|
||||
"""根据配置使用LLM生成问候语"""
|
||||
# 这里可以进一步使用配置来定制LLM行为
|
||||
llm_style = self.get_config("greeting.llm_style", "friendly")
|
||||
# ... LLM调用逻辑
|
||||
return f"你好 {username}!很高兴见到你!"
|
||||
```
|
||||
|
||||
### Command组件使用配置
|
||||
|
||||
```python
|
||||
class GreetingCommand(BaseCommand):
|
||||
"""问候命令 - 展示配置访问"""
|
||||
|
||||
command_pattern = r"^/greet(?:\s+(?P<username>\w+))?$"
|
||||
command_help = "发送问候消息"
|
||||
command_examples = ["/greet", "/greet Alice"]
|
||||
|
||||
async def execute(self) -> Tuple[bool, Optional[str]]:
|
||||
"""执行问候命令"""
|
||||
# 检查功能是否启用
|
||||
if not self.get_config("plugin.enabled", True):
|
||||
await self.send_text("问候功能已禁用")
|
||||
return False, "功能禁用"
|
||||
|
||||
# 获取用户名
|
||||
username = self.matched_groups.get("username", "用户")
|
||||
|
||||
# 根据配置选择问候方式
|
||||
if self.get_config("features.random_greeting", False):
|
||||
custom_greetings = self.get_config("messages.custom_greetings", ["你好!"])
|
||||
import random
|
||||
greeting = random.choice(custom_greetings)
|
||||
else:
|
||||
template = self.get_config("greeting.template", "你好,{username}!")
|
||||
greeting = template.format(username=username)
|
||||
|
||||
# 发送问候
|
||||
await self.send_text(greeting)
|
||||
|
||||
# 根据配置发送表情
|
||||
if self.get_config("greeting.enable_emoji", True):
|
||||
await self.send_text("😊")
|
||||
|
||||
return True, "问候发送成功"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 最佳实践与注意事项
|
||||
|
||||
### 配置定义最佳实践
|
||||
|
||||
> **🚨 核心原则:永远不要手动创建 config.toml 文件!**
|
||||
|
||||
1. **🔥 绝不手动创建配置文件**: **任何时候都不要手动创建 `config.toml` 文件**!必须通过在 `plugin.py` 中定义 `config_schema` 让系统自动生成。
|
||||
- ❌ **禁止**:`touch config.toml`、手动编写配置文件
|
||||
- ✅ **正确**:定义 `config_schema`,启动插件,让系统自动生成
|
||||
|
||||
2. **Schema优先**: 所有配置项都必须在 `config_schema` 中声明,包括类型、默认值和描述。
|
||||
|
||||
3. **描述清晰**: 为每个 `ConfigField` 和 `config_section_descriptions` 编写清晰、准确的描述。这会直接成为你的插件文档的一部分。
|
||||
|
||||
4. **提供合理默认值**: 确保你的插件在默认配置下就能正常运行(或处于一个安全禁用的状态)。
|
||||
|
||||
5. **gitignore**: 将 `plugins/*/config.toml` 或 `src/plugins/built_in/*/config.toml` 加入 `.gitignore`,以避免提交个人敏感信息。
|
||||
|
||||
6. **配置文件只供修改**: 自动生成的 `config.toml` 文件只应该被用户**修改**,而不是从零创建。
|
||||
|
||||
### 配置访问最佳实践
|
||||
|
||||
#### 1. 总是提供默认值
|
||||
|
||||
```python
|
||||
# ✅ 好的做法
|
||||
timeout = self.get_config("api.timeout", 30)
|
||||
|
||||
# ❌ 避免这样做
|
||||
timeout = self.get_config("api.timeout") # 可能返回None
|
||||
```
|
||||
|
||||
#### 2. 验证配置类型
|
||||
|
||||
```python
|
||||
# 获取配置后验证类型
|
||||
max_items = self.get_config("list.max_items", 10)
|
||||
if not isinstance(max_items, int) or max_items <= 0:
|
||||
max_items = 10 # 使用安全的默认值
|
||||
```
|
||||
|
||||
#### 3. 缓存复杂配置解析
|
||||
|
||||
```python
|
||||
class MyAction(BaseAction):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
# 在初始化时解析复杂配置,避免重复解析
|
||||
self._api_config = self._parse_api_config()
|
||||
|
||||
def _parse_api_config(self):
|
||||
return {
|
||||
'key': self.get_config("api.key", ""),
|
||||
'timeout': self.get_config("api.timeout", 30),
|
||||
'retries': self.get_config("api.max_retries", 3)
|
||||
}
|
||||
```
|
||||
|
||||
#### 4. 配置驱动的组件注册
|
||||
|
||||
```python
|
||||
def get_plugin_components(self) -> List[Tuple[ComponentInfo, Type]]:
|
||||
"""根据配置动态注册组件"""
|
||||
components = []
|
||||
|
||||
# 从配置获取组件启用状态
|
||||
enable_action = self.get_config("components.enable_action", True)
|
||||
enable_command = self.get_config("components.enable_command", True)
|
||||
|
||||
if enable_action:
|
||||
components.append((MyAction.get_action_info(), MyAction))
|
||||
if enable_command:
|
||||
components.append((MyCommand.get_command_info(), MyCommand))
|
||||
|
||||
return components
|
||||
```
|
||||
|
||||
### 🎉 总结
|
||||
|
||||
现在你掌握了插件配置的完整流程:
|
||||
|
||||
1. **定义配置**: 在插件中使用 `config_schema` 定义配置结构
|
||||
2. **访问配置**: 在组件中使用 `self.get_config("key", default_value)` 访问配置
|
||||
3. **自动生成**: 系统自动生成带注释的配置文件
|
||||
4. **动态行为**: 根据配置动态调整插件行为
|
||||
|
||||
> **🚨 最后强调:任何时候都不要手动创建 config.toml 文件!**
|
||||
>
|
||||
> 让系统根据你的 `config_schema` 自动生成配置文件,这是插件系统的核心设计原则。
|
||||
|
||||
不需要继承`BasePlugin`,不需要复杂的配置传递,不需要手动创建配置文件,组件内置的`get_config`方法和自动化的配置生成机制已经为你准备好了一切!
|
||||
@@ -1,412 +0,0 @@
|
||||
# 📋 开发标准规范
|
||||
|
||||
## 🎯 概述
|
||||
|
||||
本文档定义了MaiBot插件开发的标准规范,包括Action组件、Command组件和Tool组件的开发规范,确保代码质量、可维护性和性能。
|
||||
|
||||
## 🧩 组件开发规范
|
||||
|
||||
### Tool组件开发
|
||||
|
||||
**工具基本要求**:
|
||||
- 继承 `BaseTool` 基类
|
||||
- 定义唯一的工具名称
|
||||
- 提供清晰的功能描述
|
||||
- 使用JSONSchema定义参数
|
||||
- 实现 `execute` 异步方法
|
||||
- 使用 `register_tool()` 注册
|
||||
|
||||
**工具开发模板**:
|
||||
```python
|
||||
from src.tools.tool_can_use.base_tool import BaseTool, register_tool
|
||||
|
||||
class MyTool(BaseTool):
|
||||
"""工具类文档字符串"""
|
||||
|
||||
name = "my_tool"
|
||||
description = "详细的工具功能描述,告诉LLM这个工具的用途"
|
||||
|
||||
parameters = {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"param": {
|
||||
"type": "string",
|
||||
"description": "参数详细描述"
|
||||
}
|
||||
},
|
||||
"required": ["param"]
|
||||
}
|
||||
|
||||
async def execute(self, function_args, message_txt=""):
|
||||
"""执行工具逻辑
|
||||
|
||||
Args:
|
||||
function_args: 工具调用参数
|
||||
message_txt: 原始消息文本
|
||||
|
||||
Returns:
|
||||
dict: 包含name和content字段的结果
|
||||
"""
|
||||
# 实现工具功能逻辑
|
||||
result = "处理结果"
|
||||
|
||||
return {
|
||||
"name": self.name,
|
||||
"content": result
|
||||
}
|
||||
|
||||
# 注册工具
|
||||
register_tool(MyTool)
|
||||
```
|
||||
|
||||
**工具命名规范**:
|
||||
- 使用描述性的英文名称
|
||||
- 采用下划线命名法(snake_case)
|
||||
- 体现工具的核心功能
|
||||
- 避免过于简短或复杂的名称
|
||||
|
||||
**示例**:
|
||||
```python
|
||||
# ✅ 好的命名
|
||||
name = "weather_query" # 天气查询
|
||||
name = "knowledge_search" # 知识搜索
|
||||
name = "stock_price_check" # 股价检查
|
||||
|
||||
# ❌ 避免的命名
|
||||
name = "tool1" # 无意义
|
||||
name = "wq" # 过于简短
|
||||
name = "weather_and_news" # 功能复杂
|
||||
```
|
||||
|
||||
### Action组件开发
|
||||
|
||||
**Action必需字段检查表**:
|
||||
|
||||
**激活控制字段**:
|
||||
- ✅ `activation_type`:激活类型(KEYWORD/LLM_JUDGE/RANDOM/ALWAYS/NEVER)
|
||||
- ✅ `activation_config`:激活配置参数
|
||||
|
||||
**基本信息字段**:
|
||||
- ✅ `name`:Action唯一标识名称
|
||||
- ✅ `description`:功能描述
|
||||
- ✅ `usage_tip`:使用提示
|
||||
|
||||
**功能定义字段**:
|
||||
- ✅ `func`:执行函数
|
||||
- ✅ `llm_function_tips`:LLM调用提示
|
||||
|
||||
**Action开发模板**:
|
||||
```python
|
||||
from src.plugin_system.base_actions import BaseAction
|
||||
|
||||
class MyAction(BaseAction):
|
||||
"""Action类文档字符串"""
|
||||
|
||||
# 激活控制
|
||||
activation_type = "KEYWORD" # 或 LLM_JUDGE/RANDOM/ALWAYS/NEVER
|
||||
activation_config = {
|
||||
"keywords": ["关键词1", "关键词2"],
|
||||
"priority": 1
|
||||
}
|
||||
|
||||
# 基本信息
|
||||
name = "my_action"
|
||||
description = "Action功能描述"
|
||||
usage_tip = "使用场景和方法提示"
|
||||
|
||||
# 功能定义
|
||||
func = "执行函数名"
|
||||
llm_function_tips = "告诉LLM何时以及如何使用这个Action"
|
||||
|
||||
async def 执行函数名(self, message_txt, sender_name, chat_stream):
|
||||
"""Action执行逻辑"""
|
||||
# 实现Action功能
|
||||
await chat_stream.send_message("执行结果")
|
||||
```
|
||||
|
||||
**激活类型使用规范**:
|
||||
- `KEYWORD`:适用于有明确关键词的功能,性能最优
|
||||
- `LLM_JUDGE`:适用于需要智能判断的复杂场景
|
||||
- `RANDOM`:适用于随机触发的功能
|
||||
- `ALWAYS`:适用于总是可用的基础功能
|
||||
- `NEVER`:适用于临时禁用的功能
|
||||
|
||||
### Command组件开发
|
||||
|
||||
**Command开发模板**:
|
||||
```python
|
||||
from src.plugin_system.base_commands import BaseCommand
|
||||
|
||||
class MyCommand(BaseCommand):
|
||||
"""Command类文档字符串"""
|
||||
|
||||
# 命令基本信息
|
||||
command_name = "my_command"
|
||||
description = "命令功能描述"
|
||||
usage = "/my_command <参数> - 命令使用说明"
|
||||
|
||||
# 匹配模式
|
||||
pattern = r"^/my_command\s+(.*)"
|
||||
|
||||
async def execute(self, match, message_txt, sender_name, chat_stream):
|
||||
"""Command执行逻辑"""
|
||||
params = match.group(1) if match.group(1) else ""
|
||||
|
||||
# 实现命令功能
|
||||
await chat_stream.send_message(f"命令执行结果: {params}")
|
||||
```
|
||||
|
||||
## 📝 代码结构标准
|
||||
|
||||
### 文件组织结构
|
||||
|
||||
```
|
||||
plugins/my_plugin/
|
||||
├── __init__.py # 插件入口
|
||||
├── plugin.py # 插件主文件
|
||||
├── config.toml # 插件配置
|
||||
├── actions/ # Action组件目录
|
||||
│ ├── __init__.py
|
||||
│ └── my_action.py
|
||||
├── commands/ # Command组件目录
|
||||
│ ├── __init__.py
|
||||
│ └── my_command.py
|
||||
├── utils/ # 工具函数目录
|
||||
│ ├── __init__.py
|
||||
│ └── helpers.py
|
||||
└── README.md # 插件说明文档
|
||||
```
|
||||
|
||||
### 插件主文件模板
|
||||
|
||||
```python
|
||||
"""
|
||||
插件名称:My Plugin
|
||||
插件描述:插件功能描述
|
||||
作者:作者名称
|
||||
版本:1.0.0
|
||||
"""
|
||||
|
||||
from src.plugin_system.plugin_interface import PluginInterface
|
||||
from .actions.my_action import MyAction
|
||||
from .commands.my_command import MyCommand
|
||||
|
||||
class MyPlugin(PluginInterface):
|
||||
"""插件主类"""
|
||||
|
||||
def get_action_info(self):
|
||||
"""获取Action信息"""
|
||||
return [MyAction()]
|
||||
|
||||
def get_command_info(self):
|
||||
"""获取Command信息"""
|
||||
return [MyCommand()]
|
||||
|
||||
# 插件实例
|
||||
plugin_instance = MyPlugin()
|
||||
```
|
||||
|
||||
## 🔧 命名规范
|
||||
|
||||
### 类命名
|
||||
- **Action类**:使用 `Action` 后缀,如 `GreetingAction`
|
||||
- **Command类**:使用 `Command` 后缀,如 `HelpCommand`
|
||||
- **Tool类**:使用 `Tool` 后缀,如 `WeatherTool`
|
||||
- **插件类**:使用 `Plugin` 后缀,如 `ExamplePlugin`
|
||||
|
||||
### 变量命名
|
||||
- 使用小写字母和下划线(snake_case)
|
||||
- 布尔变量使用 `is_`、`has_`、`can_` 前缀
|
||||
- 常量使用全大写字母
|
||||
|
||||
### 函数命名
|
||||
- 使用小写字母和下划线(snake_case)
|
||||
- 异步函数不需要特殊前缀
|
||||
- 私有方法使用单下划线前缀
|
||||
|
||||
## 📊 性能优化规范
|
||||
|
||||
### Action激活类型选择
|
||||
1. **首选KEYWORD**:明确知道触发关键词时
|
||||
2. **谨慎使用LLM_JUDGE**:仅在必须智能判断时使用
|
||||
3. **合理设置优先级**:避免过多高优先级Action
|
||||
|
||||
### 异步编程规范
|
||||
- 所有I/O操作必须使用异步
|
||||
- 避免在异步函数中使用阻塞操作
|
||||
- 合理使用 `asyncio.gather()` 并发执行
|
||||
|
||||
### 资源管理
|
||||
- 及时关闭文件、网络连接等资源
|
||||
- 使用上下文管理器(`async with`)
|
||||
- 避免内存泄漏
|
||||
|
||||
## 🚨 错误处理规范
|
||||
|
||||
### 异常处理模板
|
||||
|
||||
```python
|
||||
async def my_function(self, message_txt, sender_name, chat_stream):
|
||||
"""函数文档字符串"""
|
||||
try:
|
||||
# 核心逻辑
|
||||
result = await some_operation()
|
||||
|
||||
# 成功处理
|
||||
await chat_stream.send_message(f"操作成功: {result}")
|
||||
|
||||
except ValueError as e:
|
||||
# 具体异常处理
|
||||
await chat_stream.send_message(f"参数错误: {str(e)}")
|
||||
|
||||
except Exception as e:
|
||||
# 通用异常处理
|
||||
await chat_stream.send_message(f"操作失败: {str(e)}")
|
||||
# 记录错误日志
|
||||
logger.error(f"Function my_function failed: {str(e)}")
|
||||
```
|
||||
|
||||
### 错误信息规范
|
||||
- 使用用户友好的错误提示
|
||||
- 避免暴露系统内部信息
|
||||
- 提供解决建议或替代方案
|
||||
- 记录详细的错误日志
|
||||
|
||||
## 🧪 测试标准
|
||||
|
||||
### 单元测试模板
|
||||
|
||||
```python
|
||||
import unittest
|
||||
import asyncio
|
||||
from unittest.mock import Mock, AsyncMock
|
||||
from plugins.my_plugin.actions.my_action import MyAction
|
||||
|
||||
class TestMyAction(unittest.TestCase):
|
||||
"""MyAction测试类"""
|
||||
|
||||
def setUp(self):
|
||||
"""测试前准备"""
|
||||
self.action = MyAction()
|
||||
self.mock_chat_stream = AsyncMock()
|
||||
|
||||
def test_action_properties(self):
|
||||
"""测试Action属性"""
|
||||
self.assertEqual(self.action.name, "my_action")
|
||||
self.assertIsNotNone(self.action.description)
|
||||
self.assertIsNotNone(self.action.activation_type)
|
||||
|
||||
async def test_action_execution(self):
|
||||
"""测试Action执行"""
|
||||
await self.action.执行函数名("测试消息", "测试用户", self.mock_chat_stream)
|
||||
|
||||
# 验证消息发送
|
||||
self.mock_chat_stream.send_message.assert_called()
|
||||
|
||||
def test_action_execution_sync(self):
|
||||
"""同步测试包装器"""
|
||||
asyncio.run(self.test_action_execution())
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
```
|
||||
|
||||
### 测试覆盖率要求
|
||||
- 核心功能必须有测试覆盖
|
||||
- 异常处理路径需要测试
|
||||
- 边界条件需要验证
|
||||
|
||||
## 📚 文档规范
|
||||
|
||||
### 代码文档
|
||||
- 所有类和函数必须有文档字符串
|
||||
- 使用Google风格的docstring
|
||||
- 包含参数说明和返回值说明
|
||||
|
||||
### README文档模板
|
||||
|
||||
```markdown
|
||||
# 插件名称
|
||||
|
||||
## 📖 插件描述
|
||||
简要描述插件的功能和用途
|
||||
|
||||
## ✨ 功能特性
|
||||
- 功能1:功能描述
|
||||
- 功能2:功能描述
|
||||
|
||||
## 🚀 快速开始
|
||||
### 安装配置
|
||||
1. 步骤1
|
||||
2. 步骤2
|
||||
|
||||
### 使用方法
|
||||
具体的使用说明和示例
|
||||
|
||||
## 📝 配置说明
|
||||
配置文件的详细说明
|
||||
|
||||
## 🔧 开发信息
|
||||
- 作者:作者名称
|
||||
- 版本:版本号
|
||||
- 许可证:许可证类型
|
||||
```
|
||||
|
||||
## 🔍 代码审查清单
|
||||
|
||||
### 基础检查
|
||||
- [ ] 代码符合命名规范
|
||||
- [ ] 类和函数有完整文档字符串
|
||||
- [ ] 异常处理覆盖完整
|
||||
- [ ] 没有硬编码的配置信息
|
||||
|
||||
### Action组件检查
|
||||
- [ ] 包含所有必需字段
|
||||
- [ ] 激活类型选择合理
|
||||
- [ ] LLM函数提示清晰
|
||||
- [ ] 执行函数实现正确
|
||||
|
||||
### Command组件检查
|
||||
- [ ] 正则表达式模式正确
|
||||
- [ ] 参数提取和验证完整
|
||||
- [ ] 使用说明准确
|
||||
|
||||
### Tool组件检查
|
||||
- [ ] 继承BaseTool基类
|
||||
- [ ] 参数定义遵循JSONSchema
|
||||
- [ ] 返回值格式正确
|
||||
- [ ] 工具已正确注册
|
||||
|
||||
### 性能检查
|
||||
- [ ] 避免不必要的LLM_JUDGE激活
|
||||
- [ ] 异步操作使用正确
|
||||
- [ ] 资源管理合理
|
||||
|
||||
### 安全检查
|
||||
- [ ] 输入参数验证
|
||||
- [ ] SQL注入防护
|
||||
- [ ] 敏感信息保护
|
||||
|
||||
## 🎯 最佳实践总结
|
||||
|
||||
### 设计原则
|
||||
1. **单一职责**:每个组件专注单一功能
|
||||
2. **松耦合**:减少组件间依赖
|
||||
3. **高内聚**:相关功能聚合在一起
|
||||
4. **可扩展**:易于添加新功能
|
||||
|
||||
### 性能优化
|
||||
1. **合理选择激活类型**:优先使用KEYWORD
|
||||
2. **避免阻塞操作**:使用异步编程
|
||||
3. **缓存重复计算**:提高响应速度
|
||||
4. **资源池化**:复用连接和对象
|
||||
|
||||
### 用户体验
|
||||
1. **友好的错误提示**:帮助用户理解问题
|
||||
2. **清晰的使用说明**:降低学习成本
|
||||
3. **一致的交互方式**:统一的命令格式
|
||||
4. **及时的反馈**:让用户知道操作状态
|
||||
|
||||
---
|
||||
|
||||
🎉 **遵循这些标准可以确保插件的质量、性能和用户体验!**
|
||||
@@ -1,414 +0,0 @@
|
||||
# 📚 完整示例
|
||||
|
||||
## 📖 概述
|
||||
|
||||
这里收集了各种类型的完整插件示例,展示了MaiBot插件系统的最佳实践和高级用法。每个示例都包含完整的代码、配置和说明。
|
||||
|
||||
## 🎯 示例列表
|
||||
|
||||
### 🌟 基础示例
|
||||
- [Hello World插件](#hello-world插件) - 快速入门示例
|
||||
- [简单计算器](#简单计算器) - Command基础用法
|
||||
- [智能问答](#智能问答) - Action基础用法
|
||||
|
||||
### 🔧 实用示例
|
||||
- [用户管理系统](#用户管理系统) - 数据库操作示例
|
||||
- [定时提醒插件](#定时提醒插件) - 定时任务示例
|
||||
- [天气查询插件](#天气查询插件) - 外部API调用示例
|
||||
|
||||
### 🛠️ 工具系统示例
|
||||
- [天气查询工具](#天气查询工具) - Focus模式信息获取工具
|
||||
- [知识搜索工具](#知识搜索工具) - 百科知识查询工具
|
||||
|
||||
### 🚀 高级示例
|
||||
- [多功能聊天助手](#多功能聊天助手) - 综合功能插件
|
||||
- [游戏管理插件](#游戏管理插件) - 复杂状态管理
|
||||
- [数据分析插件](#数据分析插件) - 数据处理和可视化
|
||||
|
||||
---
|
||||
|
||||
## Hello World插件
|
||||
|
||||
最基础的入门插件,展示Action和Command的基本用法。
|
||||
|
||||
### 功能说明
|
||||
- **HelloAction**: 响应问候语,展示关键词激活
|
||||
- **TimeCommand**: 查询当前时间,展示命令处理
|
||||
|
||||
### 完整代码
|
||||
|
||||
`plugins/hello_world_plugin/plugin.py`:
|
||||
|
||||
```python
|
||||
from typing import List, Tuple, Type
|
||||
from src.plugin_system import (
|
||||
BasePlugin, register_plugin, BaseAction, BaseCommand,
|
||||
ComponentInfo, ActionActivationType, ChatMode
|
||||
)
|
||||
|
||||
class HelloAction(BaseAction):
|
||||
"""问候Action"""
|
||||
|
||||
# ===== 激活控制必须项 =====
|
||||
focus_activation_type = ActionActivationType.KEYWORD
|
||||
normal_activation_type = ActionActivationType.KEYWORD
|
||||
mode_enable = ChatMode.ALL
|
||||
parallel_action = False
|
||||
|
||||
# ===== 基本信息必须项 =====
|
||||
action_name = "hello_greeting"
|
||||
action_description = "向用户发送友好的问候消息"
|
||||
|
||||
# 关键词配置
|
||||
activation_keywords = ["你好", "hello", "hi"]
|
||||
keyword_case_sensitive = False
|
||||
|
||||
# ===== 功能定义必须项 =====
|
||||
action_parameters = {
|
||||
"greeting_style": "问候风格:casual(随意) 或 formal(正式)"
|
||||
}
|
||||
|
||||
action_require = [
|
||||
"用户发送问候语时使用",
|
||||
"营造友好的聊天氛围"
|
||||
]
|
||||
|
||||
associated_types = ["text", "emoji"]
|
||||
|
||||
async def execute(self) -> Tuple[bool, str]:
|
||||
style = self.action_data.get("greeting_style", "casual")
|
||||
|
||||
if style == "formal":
|
||||
message = "您好!很高兴为您服务!"
|
||||
emoji = "🙏"
|
||||
else:
|
||||
message = "嗨!很开心见到你!"
|
||||
emoji = "😊"
|
||||
|
||||
await self.send_text(message)
|
||||
await self.send_type("emoji", emoji)
|
||||
|
||||
return True, f"发送了{style}风格的问候"
|
||||
|
||||
class TimeCommand(BaseCommand):
|
||||
"""时间查询Command"""
|
||||
|
||||
command_pattern = r"^/time$"
|
||||
command_help = "查询当前时间"
|
||||
command_examples = ["/time"]
|
||||
intercept_message = True
|
||||
|
||||
async def execute(self) -> Tuple[bool, str]:
|
||||
import datetime
|
||||
|
||||
now = datetime.datetime.now()
|
||||
time_str = now.strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
await self.send_text(f"⏰ 当前时间:{time_str}")
|
||||
|
||||
return True, f"显示了当前时间: {time_str}"
|
||||
|
||||
@register_plugin
|
||||
class HelloWorldPlugin(BasePlugin):
|
||||
"""Hello World插件"""
|
||||
|
||||
plugin_name = "hello_world_plugin"
|
||||
plugin_description = "Hello World演示插件"
|
||||
plugin_version = "1.0.0"
|
||||
plugin_author = "MaiBot Team"
|
||||
enable_plugin = True
|
||||
|
||||
def get_plugin_components(self) -> List[Tuple[ComponentInfo, Type]]:
|
||||
return [
|
||||
(HelloAction.get_action_info(), HelloAction),
|
||||
(TimeCommand.get_command_info(
|
||||
name="time_query",
|
||||
description="查询当前系统时间"
|
||||
), TimeCommand),
|
||||
]
|
||||
```
|
||||
|
||||
### 配置文件
|
||||
|
||||
`plugins/hello_world_plugin/config.toml`:
|
||||
|
||||
```toml
|
||||
[plugin]
|
||||
name = "hello_world_plugin"
|
||||
version = "1.0.0"
|
||||
enabled = true
|
||||
|
||||
[greeting]
|
||||
default_style = "casual"
|
||||
enable_emoji = true
|
||||
|
||||
[time]
|
||||
timezone = "Asia/Shanghai"
|
||||
format = "%Y-%m-%d %H:%M:%S"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 天气查询工具
|
||||
|
||||
展示如何创建Focus模式下的信息获取工具,专门用于扩展麦麦的信息获取能力。
|
||||
|
||||
### 功能说明
|
||||
- **Focus模式专用**:仅在专注聊天模式下工作
|
||||
- **自动调用**:LLM根据用户查询自动判断是否使用
|
||||
- **信息增强**:为麦麦提供实时天气数据
|
||||
- **必须启用工具处理器**
|
||||
|
||||
### 完整代码
|
||||
|
||||
`src/tools/tool_can_use/weather_tool.py`:
|
||||
|
||||
```python
|
||||
from src.tools.tool_can_use.base_tool import BaseTool, register_tool
|
||||
import aiohttp
|
||||
import json
|
||||
|
||||
class WeatherTool(BaseTool):
|
||||
"""天气查询工具 - 获取指定城市的实时天气信息"""
|
||||
|
||||
# 工具名称,必须唯一
|
||||
name = "weather_query"
|
||||
|
||||
# 工具描述,告诉LLM这个工具的用途
|
||||
description = "查询指定城市的实时天气信息,包括温度、湿度、天气状况等"
|
||||
|
||||
# 参数定义,遵循JSONSchema格式
|
||||
parameters = {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"city": {
|
||||
"type": "string",
|
||||
"description": "要查询天气的城市名称,如:北京、上海、纽约"
|
||||
},
|
||||
"country": {
|
||||
"type": "string",
|
||||
"description": "国家代码,如:CN、US,可选参数"
|
||||
}
|
||||
},
|
||||
"required": ["city"]
|
||||
}
|
||||
|
||||
async def execute(self, function_args, message_txt=""):
|
||||
"""执行天气查询"""
|
||||
try:
|
||||
city = function_args.get("city")
|
||||
country = function_args.get("country", "")
|
||||
|
||||
# 构建查询参数
|
||||
location = f"{city},{country}" if country else city
|
||||
|
||||
# 调用天气API
|
||||
weather_data = await self._fetch_weather(location)
|
||||
|
||||
# 格式化结果
|
||||
result = self._format_weather_data(weather_data)
|
||||
|
||||
return {
|
||||
"name": self.name,
|
||||
"content": result
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
return {
|
||||
"name": self.name,
|
||||
"content": f"天气查询失败: {str(e)}"
|
||||
}
|
||||
|
||||
async def _fetch_weather(self, location: str) -> dict:
|
||||
"""获取天气数据"""
|
||||
# 这里是示例,实际需要接入真实的天气API
|
||||
# 例如:OpenWeatherMap、和风天气等
|
||||
api_url = f"http://api.weather.com/v1/current?q={location}"
|
||||
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(api_url) as response:
|
||||
return await response.json()
|
||||
|
||||
def _format_weather_data(self, data: dict) -> str:
|
||||
"""格式化天气数据"""
|
||||
if not data:
|
||||
return "暂无天气数据"
|
||||
|
||||
# 提取关键信息
|
||||
city = data.get("location", {}).get("name", "未知城市")
|
||||
temp = data.get("current", {}).get("temp_c", "未知")
|
||||
condition = data.get("current", {}).get("condition", {}).get("text", "未知")
|
||||
humidity = data.get("current", {}).get("humidity", "未知")
|
||||
|
||||
# 格式化输出
|
||||
return f"""
|
||||
🌤️ {city} 实时天气
|
||||
━━━━━━━━━━━━━━━━━━
|
||||
🌡️ 温度: {temp}°C
|
||||
☁️ 天气: {condition}
|
||||
💧 湿度: {humidity}%
|
||||
━━━━━━━━━━━━━━━━━━
|
||||
""".strip()
|
||||
|
||||
# 注册工具(重要!必须调用)
|
||||
register_tool(WeatherTool)
|
||||
```
|
||||
|
||||
### 使用说明
|
||||
|
||||
1. **部署位置**:将文件放在 `src/tools/tool_can_use/` 目录下
|
||||
2. **模式要求**:仅在Focus模式下可用
|
||||
3. **配置要求**:必须开启工具处理器 `enable_tool_processor = True`
|
||||
4. **自动调用**:用户发送"今天北京天气怎么样?"时,麦麦会自动调用此工具
|
||||
|
||||
---
|
||||
|
||||
## 知识搜索工具
|
||||
|
||||
展示如何创建知识查询工具,为麦麦提供百科知识和专业信息。
|
||||
|
||||
### 功能说明
|
||||
- **知识增强**:扩展麦麦的知识获取能力
|
||||
- **分类搜索**:支持科学、历史、技术等分类
|
||||
- **多语言支持**:支持中英文结果
|
||||
- **智能调用**:LLM自动判断何时需要知识查询
|
||||
|
||||
### 完整代码
|
||||
|
||||
`src/tools/tool_can_use/knowledge_search_tool.py`:
|
||||
|
||||
```python
|
||||
from src.tools.tool_can_use.base_tool import BaseTool, register_tool
|
||||
import aiohttp
|
||||
import json
|
||||
|
||||
class KnowledgeSearchTool(BaseTool):
|
||||
"""知识搜索工具 - 查询百科知识和专业信息"""
|
||||
|
||||
name = "knowledge_search"
|
||||
description = "搜索百科知识、专业术语解释、历史事件等信息"
|
||||
|
||||
parameters = {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"query": {
|
||||
"type": "string",
|
||||
"description": "要搜索的知识关键词或问题"
|
||||
},
|
||||
"category": {
|
||||
"type": "string",
|
||||
"description": "知识分类:science(科学)、history(历史)、technology(技术)、general(通用)等",
|
||||
"enum": ["science", "history", "technology", "general"]
|
||||
},
|
||||
"language": {
|
||||
"type": "string",
|
||||
"description": "结果语言:zh(中文)、en(英文)",
|
||||
"enum": ["zh", "en"]
|
||||
}
|
||||
},
|
||||
"required": ["query"]
|
||||
}
|
||||
|
||||
async def execute(self, function_args, message_txt=""):
|
||||
"""执行知识搜索"""
|
||||
try:
|
||||
query = function_args.get("query")
|
||||
category = function_args.get("category", "general")
|
||||
language = function_args.get("language", "zh")
|
||||
|
||||
# 执行搜索逻辑
|
||||
search_results = await self._search_knowledge(query, category, language)
|
||||
|
||||
# 格式化结果
|
||||
result = self._format_search_results(query, search_results)
|
||||
|
||||
return {
|
||||
"name": self.name,
|
||||
"content": result
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
return {
|
||||
"name": self.name,
|
||||
"content": f"知识搜索失败: {str(e)}"
|
||||
}
|
||||
|
||||
async def _search_knowledge(self, query: str, category: str, language: str) -> list:
|
||||
"""执行知识搜索"""
|
||||
# 这里实现实际的搜索逻辑
|
||||
# 可以对接维基百科API、百度百科API等
|
||||
|
||||
# 示例API调用
|
||||
if language == "zh":
|
||||
api_url = f"https://zh.wikipedia.org/api/rest_v1/page/summary/{query}"
|
||||
else:
|
||||
api_url = f"https://en.wikipedia.org/api/rest_v1/page/summary/{query}"
|
||||
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(api_url) as response:
|
||||
if response.status == 200:
|
||||
data = await response.json()
|
||||
return [
|
||||
{
|
||||
"title": data.get("title", "无标题"),
|
||||
"summary": data.get("extract", "无摘要"),
|
||||
"source": "Wikipedia"
|
||||
}
|
||||
]
|
||||
else:
|
||||
return []
|
||||
|
||||
def _format_search_results(self, query: str, results: list) -> str:
|
||||
"""格式化搜索结果"""
|
||||
if not results:
|
||||
return f"未找到关于 '{query}' 的相关信息"
|
||||
|
||||
formatted_text = f"📚 关于 '{query}' 的搜索结果:\n\n"
|
||||
|
||||
for i, result in enumerate(results[:3], 1): # 限制显示前3条
|
||||
title = result.get("title", "无标题")
|
||||
summary = result.get("summary", "无摘要")
|
||||
source = result.get("source", "未知来源")
|
||||
|
||||
formatted_text += f"{i}. **{title}**\n"
|
||||
formatted_text += f" {summary}\n"
|
||||
formatted_text += f" 📖 来源: {source}\n\n"
|
||||
|
||||
return formatted_text.strip()
|
||||
|
||||
# 注册工具
|
||||
register_tool(KnowledgeSearchTool)
|
||||
```
|
||||
|
||||
### 配置示例
|
||||
|
||||
Focus模式配置文件示例:
|
||||
|
||||
```python
|
||||
# 在Focus模式配置中
|
||||
focus_config = {
|
||||
"enable_tool_processor": True, # 必须启用工具处理器
|
||||
"tool_timeout": 30, # 工具执行超时时间(秒)
|
||||
"max_tools_per_message": 3 # 单次消息最大工具调用数
|
||||
}
|
||||
```
|
||||
|
||||
### 使用流程
|
||||
|
||||
1. **用户查询**:用户在Focus模式下发送"什么是量子计算?"
|
||||
2. **LLM判断**:麦麦识别这是知识查询需求
|
||||
3. **工具调用**:自动调用 `knowledge_search` 工具
|
||||
4. **信息获取**:工具查询相关知识信息
|
||||
5. **整合回复**:麦麦将获取的信息整合到回复中
|
||||
|
||||
### 工具系统特点
|
||||
|
||||
- **🎯 专用性**:仅在Focus模式下工作,专注信息获取
|
||||
- **🔍 智能性**:LLM自动判断何时需要使用工具
|
||||
- **📊 丰富性**:为麦麦提供外部数据和实时信息
|
||||
- **⚡ 高效性**:系统自动发现和注册工具
|
||||
- **🔧 独立性**:目前需要单独编写,未来将更好融入插件系统
|
||||
|
||||
---
|
||||
|
||||
🎉 **这些示例展示了MaiBot插件系统的强大功能!根据你的需求选择合适的示例作为起点。**
|
||||
@@ -1,520 +0,0 @@
|
||||
# 📖 插件配置访问完整示例
|
||||
|
||||
> 这个示例展示了如何在Action和Command组件中正确访问插件的配置文件。
|
||||
|
||||
## 🎯 问题背景
|
||||
|
||||
在插件开发过程中,你可能遇到这样的问题:
|
||||
- `get_config`方法只在`BasePlugin`类中
|
||||
- `BaseAction`和`BaseCommand`无法直接继承这个方法
|
||||
- 想要在Action或Command中访问插件配置
|
||||
|
||||
## ✅ 解决方案
|
||||
|
||||
通过`self.api.get_config()`方法访问配置,系统会自动将插件配置传递给组件。
|
||||
|
||||
## 📁 完整示例
|
||||
|
||||
### 1. 插件配置文件
|
||||
|
||||
创建 `config.toml`:
|
||||
|
||||
```toml
|
||||
[greeting]
|
||||
default_style = "casual"
|
||||
enable_emoji = true
|
||||
custom_messages = [
|
||||
"你好呀!",
|
||||
"嗨!很高兴见到你!",
|
||||
"哈喽!"
|
||||
]
|
||||
|
||||
[database]
|
||||
enabled = true
|
||||
table_prefix = "hello_"
|
||||
max_records = 1000
|
||||
|
||||
[features]
|
||||
enable_weather = false
|
||||
enable_jokes = true
|
||||
api_timeout = 30
|
||||
|
||||
[advanced.logging]
|
||||
level = "INFO"
|
||||
file_path = "logs/hello_plugin.log"
|
||||
|
||||
[advanced.cache]
|
||||
enabled = true
|
||||
ttl_seconds = 3600
|
||||
max_size = 100
|
||||
```
|
||||
|
||||
### 2. 插件主文件
|
||||
|
||||
创建 `plugin.py`:
|
||||
|
||||
```python
|
||||
"""
|
||||
配置访问示例插件
|
||||
展示如何在Action和Command中访问配置
|
||||
"""
|
||||
|
||||
from src.plugin_system import (
|
||||
BasePlugin,
|
||||
BaseAction,
|
||||
BaseCommand,
|
||||
register_plugin,
|
||||
ActionInfo,
|
||||
CommandInfo,
|
||||
PythonDependency,
|
||||
ActionActivationType
|
||||
)
|
||||
from src.common.logger import get_logger
|
||||
|
||||
logger = get_logger("config_example_plugin")
|
||||
|
||||
|
||||
@register_plugin
|
||||
class ConfigExamplePlugin(BasePlugin):
|
||||
"""配置访问示例插件"""
|
||||
|
||||
plugin_name = "config_example_plugin"
|
||||
plugin_description = "展示如何在组件中访问配置的示例插件"
|
||||
plugin_version = "1.0.0"
|
||||
plugin_author = "MaiBot Team"
|
||||
config_file_name = "config.toml"
|
||||
|
||||
def get_plugin_components(self):
|
||||
"""返回插件组件"""
|
||||
return [
|
||||
(ActionInfo(
|
||||
name="config_greeting_action",
|
||||
description="使用配置的问候Action",
|
||||
focus_activation_type=ActionActivationType.KEYWORD,
|
||||
normal_activation_type=ActionActivationType.KEYWORD,
|
||||
activation_keywords=["配置问候", "config hello"],
|
||||
), ConfigGreetingAction),
|
||||
|
||||
(CommandInfo(
|
||||
name="config_status",
|
||||
description="显示配置状态",
|
||||
command_pattern=r"^/config\s*(status|show)?$",
|
||||
command_help="显示插件配置状态",
|
||||
command_examples=["/config", "/config status"],
|
||||
), ConfigStatusCommand),
|
||||
|
||||
(CommandInfo(
|
||||
name="config_test",
|
||||
description="测试配置访问",
|
||||
command_pattern=r"^/config\s+test\s+(?P<key>\S+)$",
|
||||
command_help="测试访问指定配置项",
|
||||
command_examples=["/config test greeting.default_style"],
|
||||
), ConfigTestCommand),
|
||||
]
|
||||
|
||||
|
||||
class ConfigGreetingAction(BaseAction):
|
||||
"""使用配置的问候Action"""
|
||||
|
||||
async def execute(self):
|
||||
"""执行配置化的问候"""
|
||||
try:
|
||||
# 方法1: 直接访问配置项
|
||||
style = self.api.get_config("greeting.default_style", "casual")
|
||||
enable_emoji = self.api.get_config("greeting.enable_emoji", True)
|
||||
|
||||
# 方法2: 检查配置是否存在
|
||||
if self.api.has_config("greeting.custom_messages"):
|
||||
messages = self.api.get_config("greeting.custom_messages", [])
|
||||
if messages:
|
||||
# 随机选择一个问候语
|
||||
import random
|
||||
message = random.choice(messages)
|
||||
else:
|
||||
message = "你好!"
|
||||
else:
|
||||
# 使用默认问候语
|
||||
if style == "formal":
|
||||
message = "您好!很高兴为您服务!"
|
||||
else:
|
||||
message = "嗨!很开心见到你!"
|
||||
|
||||
# 添加表情符号
|
||||
if enable_emoji:
|
||||
emoji = "😊" if style == "casual" else "🙏"
|
||||
message += emoji
|
||||
|
||||
# 发送问候消息
|
||||
await self.send_text(message)
|
||||
|
||||
# 记录到数据库(如果启用)
|
||||
await self._save_greeting_record(style, message)
|
||||
|
||||
return True, f"发送了{style}风格的配置化问候"
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"配置问候执行失败: {e}")
|
||||
await self.send_text("抱歉,问候功能遇到了问题")
|
||||
return False, f"执行失败: {str(e)}"
|
||||
|
||||
async def _save_greeting_record(self, style: str, message: str):
|
||||
"""保存问候记录到数据库"""
|
||||
try:
|
||||
# 检查数据库功能是否启用
|
||||
if not self.api.get_config("database.enabled", False):
|
||||
return
|
||||
|
||||
# 获取数据库配置
|
||||
table_prefix = self.api.get_config("database.table_prefix", "hello_")
|
||||
max_records = self.api.get_config("database.max_records", 1000)
|
||||
|
||||
# 构造记录数据
|
||||
record_data = {
|
||||
"style": style,
|
||||
"message": message,
|
||||
"timestamp": "now", # 实际应用中使用datetime
|
||||
"user_id": "demo_user" # 从context获取真实用户ID
|
||||
}
|
||||
|
||||
# 这里应该调用数据库API保存记录
|
||||
logger.info(f"保存问候记录到 {table_prefix}greetings: {record_data}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"保存问候记录失败: {e}")
|
||||
|
||||
|
||||
class ConfigStatusCommand(BaseCommand):
|
||||
"""显示配置状态Command"""
|
||||
|
||||
async def execute(self):
|
||||
"""显示插件配置状态"""
|
||||
try:
|
||||
# 获取所有配置
|
||||
all_config = self.api.get_all_config()
|
||||
|
||||
if not all_config:
|
||||
await self.send_text("❌ 没有找到配置文件")
|
||||
return True, "没有配置文件"
|
||||
|
||||
# 构建状态报告
|
||||
status_lines = ["📋 插件配置状态:", ""]
|
||||
|
||||
# 问候配置
|
||||
greeting_config = all_config.get("greeting", {})
|
||||
if greeting_config:
|
||||
status_lines.append("🎯 问候配置:")
|
||||
status_lines.append(f" - 默认风格: {greeting_config.get('default_style', 'N/A')}")
|
||||
status_lines.append(f" - 启用表情: {'✅' if greeting_config.get('enable_emoji') else '❌'}")
|
||||
custom_msgs = greeting_config.get('custom_messages', [])
|
||||
status_lines.append(f" - 自定义消息: {len(custom_msgs)}条")
|
||||
status_lines.append("")
|
||||
|
||||
# 数据库配置
|
||||
db_config = all_config.get("database", {})
|
||||
if db_config:
|
||||
status_lines.append("🗄️ 数据库配置:")
|
||||
status_lines.append(f" - 状态: {'✅ 启用' if db_config.get('enabled') else '❌ 禁用'}")
|
||||
status_lines.append(f" - 表前缀: {db_config.get('table_prefix', 'N/A')}")
|
||||
status_lines.append(f" - 最大记录: {db_config.get('max_records', 'N/A')}")
|
||||
status_lines.append("")
|
||||
|
||||
# 功能配置
|
||||
features_config = all_config.get("features", {})
|
||||
if features_config:
|
||||
status_lines.append("🔧 功能配置:")
|
||||
for feature, enabled in features_config.items():
|
||||
if isinstance(enabled, bool):
|
||||
status_lines.append(f" - {feature}: {'✅' if enabled else '❌'}")
|
||||
else:
|
||||
status_lines.append(f" - {feature}: {enabled}")
|
||||
status_lines.append("")
|
||||
|
||||
# 高级配置
|
||||
advanced_config = all_config.get("advanced", {})
|
||||
if advanced_config:
|
||||
status_lines.append("⚙️ 高级配置:")
|
||||
for section, config in advanced_config.items():
|
||||
status_lines.append(f" - {section}: {len(config) if isinstance(config, dict) else 1}项")
|
||||
|
||||
# 发送状态报告
|
||||
status_text = "\n".join(status_lines)
|
||||
await self.send_text(status_text)
|
||||
|
||||
return True, "显示了配置状态"
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"获取配置状态失败: {e}")
|
||||
await self.send_text(f"❌ 获取配置状态失败: {str(e)}")
|
||||
return False, f"获取失败: {str(e)}"
|
||||
|
||||
|
||||
class ConfigTestCommand(BaseCommand):
|
||||
"""测试配置访问Command"""
|
||||
|
||||
async def execute(self):
|
||||
"""测试访问指定的配置项"""
|
||||
try:
|
||||
# 获取要测试的配置键
|
||||
config_key = self.matched_groups.get("key", "")
|
||||
|
||||
if not config_key:
|
||||
await self.send_text("❌ 请指定要测试的配置项\n用法: /config test <key>")
|
||||
return True, "缺少配置键参数"
|
||||
|
||||
# 测试配置访问的不同方法
|
||||
result_lines = [f"🔍 测试配置项: `{config_key}`", ""]
|
||||
|
||||
# 方法1: 检查是否存在
|
||||
exists = self.api.has_config(config_key)
|
||||
result_lines.append(f"📋 配置存在: {'✅ 是' if exists else '❌ 否'}")
|
||||
|
||||
if exists:
|
||||
# 方法2: 获取配置值
|
||||
config_value = self.api.get_config(config_key)
|
||||
value_type = type(config_value).__name__
|
||||
|
||||
result_lines.append(f"📊 数据类型: {value_type}")
|
||||
|
||||
# 根据类型显示值
|
||||
if isinstance(config_value, (str, int, float, bool)):
|
||||
result_lines.append(f"💾 配置值: {config_value}")
|
||||
elif isinstance(config_value, list):
|
||||
result_lines.append(f"📝 列表长度: {len(config_value)}")
|
||||
if config_value:
|
||||
result_lines.append(f"📋 首项: {config_value[0]}")
|
||||
elif isinstance(config_value, dict):
|
||||
result_lines.append(f"🗂️ 字典大小: {len(config_value)}项")
|
||||
if config_value:
|
||||
keys = list(config_value.keys())[:3]
|
||||
result_lines.append(f"🔑 键示例: {', '.join(keys)}")
|
||||
else:
|
||||
result_lines.append(f"💾 配置值: {str(config_value)[:100]}...")
|
||||
|
||||
# 方法3: 测试默认值
|
||||
test_default = self.api.get_config(config_key, "DEFAULT_VALUE")
|
||||
if test_default != "DEFAULT_VALUE":
|
||||
result_lines.append("✅ 默认值机制正常")
|
||||
else:
|
||||
result_lines.append("⚠️ 配置值为空或等于测试默认值")
|
||||
else:
|
||||
# 测试默认值返回
|
||||
default_value = self.api.get_config(config_key, "NOT_FOUND")
|
||||
result_lines.append(f"🔄 默认值返回: {default_value}")
|
||||
|
||||
# 显示相关配置项
|
||||
if "." in config_key:
|
||||
section = config_key.split(".")[0]
|
||||
all_config = self.api.get_all_config()
|
||||
section_config = all_config.get(section, {})
|
||||
if section_config and isinstance(section_config, dict):
|
||||
related_keys = list(section_config.keys())[:5]
|
||||
result_lines.append("")
|
||||
result_lines.append(f"🔗 相关配置项 ({section}):")
|
||||
for key in related_keys:
|
||||
full_key = f"{section}.{key}"
|
||||
status = "✅" if self.api.has_config(full_key) else "❌"
|
||||
result_lines.append(f" {status} {full_key}")
|
||||
|
||||
# 发送测试结果
|
||||
result_text = "\n".join(result_lines)
|
||||
await self.send_text(result_text)
|
||||
|
||||
return True, f"测试了配置项: {config_key}"
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"配置测试失败: {e}")
|
||||
await self.send_text(f"❌ 配置测试失败: {str(e)}")
|
||||
return False, f"测试失败: {str(e)}"
|
||||
|
||||
|
||||
# 演示代码
|
||||
async def demo_config_access():
|
||||
"""演示配置访问功能"""
|
||||
|
||||
print("🔧 插件配置访问演示")
|
||||
print("=" * 50)
|
||||
|
||||
# 模拟插件配置
|
||||
mock_config = {
|
||||
"greeting": {
|
||||
"default_style": "casual",
|
||||
"enable_emoji": True,
|
||||
"custom_messages": ["你好呀!", "嗨!很高兴见到你!"]
|
||||
},
|
||||
"database": {
|
||||
"enabled": True,
|
||||
"table_prefix": "hello_",
|
||||
"max_records": 1000
|
||||
},
|
||||
"advanced": {
|
||||
"logging": {
|
||||
"level": "INFO",
|
||||
"file_path": "logs/hello_plugin.log"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# 创建模拟API
|
||||
from src.plugin_system.apis.plugin_api import PluginAPI
|
||||
api = PluginAPI(plugin_config=mock_config)
|
||||
|
||||
print("\n📋 配置访问测试:")
|
||||
|
||||
# 测试1: 基本配置访问
|
||||
style = api.get_config("greeting.default_style", "unknown")
|
||||
print(f" 问候风格: {style}")
|
||||
|
||||
# 测试2: 布尔值配置
|
||||
enable_emoji = api.get_config("greeting.enable_emoji", False)
|
||||
print(f" 启用表情: {enable_emoji}")
|
||||
|
||||
# 测试3: 列表配置
|
||||
messages = api.get_config("greeting.custom_messages", [])
|
||||
print(f" 自定义消息: {len(messages)}条")
|
||||
|
||||
# 测试4: 深层嵌套配置
|
||||
log_level = api.get_config("advanced.logging.level", "INFO")
|
||||
print(f" 日志级别: {log_level}")
|
||||
|
||||
# 测试5: 不存在的配置
|
||||
unknown = api.get_config("unknown.config", "default")
|
||||
print(f" 未知配置: {unknown}")
|
||||
|
||||
# 测试6: 配置存在检查
|
||||
exists1 = api.has_config("greeting.default_style")
|
||||
exists2 = api.has_config("nonexistent.config")
|
||||
print(f" greeting.default_style 存在: {exists1}")
|
||||
print(f" nonexistent.config 存在: {exists2}")
|
||||
|
||||
# 测试7: 获取所有配置
|
||||
all_config = api.get_all_config()
|
||||
print(f" 总配置节数: {len(all_config)}")
|
||||
|
||||
print("\n✅ 配置访问测试完成!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import asyncio
|
||||
asyncio.run(demo_config_access())
|
||||
```
|
||||
|
||||
## 🎯 核心要点
|
||||
|
||||
### 1. 在Action中访问配置
|
||||
|
||||
```python
|
||||
class MyAction(BaseAction):
|
||||
async def execute(self):
|
||||
# 基本配置访问
|
||||
value = self.api.get_config("section.key", "default")
|
||||
|
||||
# 检查配置是否存在
|
||||
if self.api.has_config("section.key"):
|
||||
# 配置存在,执行相应逻辑
|
||||
pass
|
||||
|
||||
# 获取所有配置
|
||||
all_config = self.api.get_all_config()
|
||||
```
|
||||
|
||||
### 2. 在Command中访问配置
|
||||
|
||||
```python
|
||||
class MyCommand(BaseCommand):
|
||||
async def execute(self):
|
||||
# 访问配置的方法与Action完全相同
|
||||
value = self.api.get_config("section.key", "default")
|
||||
|
||||
# 支持嵌套键访问
|
||||
nested_value = self.api.get_config("section.subsection.key")
|
||||
```
|
||||
|
||||
### 3. 配置传递机制
|
||||
|
||||
系统会自动处理配置传递:
|
||||
1. `BasePlugin`加载配置文件到`self.config`
|
||||
2. 组件注册时,系统通过`component_registry.get_plugin_config()`获取配置
|
||||
3. Action/Command实例化时,配置作为`plugin_config`参数传递
|
||||
4. `PluginAPI`初始化时保存配置到`self._plugin_config`
|
||||
5. 组件通过`self.api.get_config()`访问配置
|
||||
|
||||
## 🔧 使用这个示例
|
||||
|
||||
### 1. 创建插件目录
|
||||
|
||||
```bash
|
||||
mkdir plugins/config_example_plugin
|
||||
cd plugins/config_example_plugin
|
||||
```
|
||||
|
||||
### 2. 复制文件
|
||||
|
||||
- 将配置文件保存为 `config.toml`
|
||||
- 将插件代码保存为 `plugin.py`
|
||||
|
||||
### 3. 测试功能
|
||||
|
||||
```bash
|
||||
# 启动MaiBot后测试以下命令:
|
||||
|
||||
# 测试配置状态显示
|
||||
/config status
|
||||
|
||||
# 测试特定配置项
|
||||
/config test greeting.default_style
|
||||
/config test database.enabled
|
||||
/config test advanced.logging.level
|
||||
|
||||
# 触发配置化问候
|
||||
配置问候
|
||||
```
|
||||
|
||||
## 💡 最佳实践
|
||||
|
||||
### 1. 提供合理的默认值
|
||||
|
||||
```python
|
||||
# 总是提供默认值
|
||||
timeout = self.api.get_config("api.timeout", 30)
|
||||
enabled = self.api.get_config("feature.enabled", False)
|
||||
```
|
||||
|
||||
### 2. 验证配置类型
|
||||
|
||||
```python
|
||||
# 验证配置类型
|
||||
max_items = self.api.get_config("list.max_items", 10)
|
||||
if not isinstance(max_items, int) or max_items <= 0:
|
||||
max_items = 10 # 使用安全的默认值
|
||||
```
|
||||
|
||||
### 3. 缓存复杂配置
|
||||
|
||||
```python
|
||||
class MyAction(BaseAction):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
# 缓存复杂配置避免重复解析
|
||||
self._cached_config = self._parse_complex_config()
|
||||
|
||||
def _parse_complex_config(self):
|
||||
# 解析复杂配置逻辑
|
||||
return processed_config
|
||||
```
|
||||
|
||||
### 4. 配置变更检测
|
||||
|
||||
```python
|
||||
# 对于支持热更新的配置
|
||||
last_config_hash = None
|
||||
|
||||
def check_config_changes(self):
|
||||
current_config = self.api.get_all_config()
|
||||
current_hash = hash(str(current_config))
|
||||
|
||||
if current_hash != self.last_config_hash:
|
||||
self.last_config_hash = current_hash
|
||||
self._reload_config()
|
||||
```
|
||||
|
||||
通过这种方式,你的Action和Command组件可以灵活地访问插件配置,实现更加强大和可定制的功能!
|
||||
@@ -1,477 +0,0 @@
|
||||
# 📦 依赖管理完整示例
|
||||
|
||||
> 这个示例展示了如何在插件中正确使用Python依赖管理功能。
|
||||
|
||||
## 🎯 示例插件:智能数据分析插件
|
||||
|
||||
这个插件展示了如何处理必需依赖、可选依赖,以及优雅降级处理。
|
||||
|
||||
```python
|
||||
"""
|
||||
智能数据分析插件
|
||||
展示依赖管理的完整用法
|
||||
"""
|
||||
|
||||
from src.plugin_system import (
|
||||
BasePlugin,
|
||||
BaseAction,
|
||||
register_plugin,
|
||||
ActionInfo,
|
||||
PythonDependency,
|
||||
ActionActivationType
|
||||
)
|
||||
from src.common.logger import get_logger
|
||||
|
||||
logger = get_logger("data_analysis_plugin")
|
||||
|
||||
|
||||
@register_plugin
|
||||
class DataAnalysisPlugin(BasePlugin):
|
||||
"""智能数据分析插件"""
|
||||
|
||||
plugin_name = "data_analysis_plugin"
|
||||
plugin_description = "提供数据分析和可视化功能的示例插件"
|
||||
plugin_version = "1.0.0"
|
||||
plugin_author = "MaiBot Team"
|
||||
|
||||
# 声明Python包依赖
|
||||
python_dependencies = [
|
||||
# 必需依赖 - 核心功能
|
||||
PythonDependency(
|
||||
package_name="requests",
|
||||
version=">=2.25.0",
|
||||
description="HTTP库,用于获取外部数据"
|
||||
),
|
||||
|
||||
# 可选依赖 - 数据处理
|
||||
PythonDependency(
|
||||
package_name="pandas",
|
||||
version=">=1.3.0",
|
||||
optional=True,
|
||||
description="数据处理库,提供高级数据操作功能"
|
||||
),
|
||||
|
||||
# 可选依赖 - 数值计算
|
||||
PythonDependency(
|
||||
package_name="numpy",
|
||||
version=">=1.20.0",
|
||||
optional=True,
|
||||
description="数值计算库,用于数学运算"
|
||||
),
|
||||
|
||||
# 可选依赖 - 数据可视化
|
||||
PythonDependency(
|
||||
package_name="matplotlib",
|
||||
version=">=3.3.0",
|
||||
optional=True,
|
||||
description="绘图库,用于生成数据图表"
|
||||
),
|
||||
|
||||
# 特殊情况:导入名与安装名不同
|
||||
PythonDependency(
|
||||
package_name="PIL",
|
||||
install_name="Pillow",
|
||||
version=">=8.0.0",
|
||||
optional=True,
|
||||
description="图像处理库,用于图表保存和处理"
|
||||
),
|
||||
]
|
||||
|
||||
def get_plugin_components(self):
|
||||
"""返回插件组件"""
|
||||
return [
|
||||
# 基础数据获取(只依赖requests)
|
||||
(ActionInfo(
|
||||
name="fetch_data_action",
|
||||
description="获取外部数据",
|
||||
focus_activation_type=ActionActivationType.KEYWORD,
|
||||
normal_activation_type=ActionActivationType.KEYWORD,
|
||||
activation_keywords=["获取数据", "下载数据"],
|
||||
), FetchDataAction),
|
||||
|
||||
# 数据分析(依赖pandas和numpy)
|
||||
(ActionInfo(
|
||||
name="analyze_data_action",
|
||||
description="数据分析和统计",
|
||||
focus_activation_type=ActionActivationType.KEYWORD,
|
||||
normal_activation_type=ActionActivationType.KEYWORD,
|
||||
activation_keywords=["分析数据", "数据统计"],
|
||||
), AnalyzeDataAction),
|
||||
|
||||
# 数据可视化(依赖matplotlib)
|
||||
(ActionInfo(
|
||||
name="visualize_data_action",
|
||||
description="数据可视化",
|
||||
focus_activation_type=ActionActivationType.KEYWORD,
|
||||
normal_activation_type=ActionActivationType.KEYWORD,
|
||||
activation_keywords=["数据图表", "可视化"],
|
||||
), VisualizeDataAction),
|
||||
]
|
||||
|
||||
|
||||
class FetchDataAction(BaseAction):
|
||||
"""数据获取Action - 仅依赖必需的requests库"""
|
||||
|
||||
async def execute(self, action_input, context=None):
|
||||
"""获取外部数据"""
|
||||
try:
|
||||
import requests
|
||||
|
||||
# 模拟数据获取
|
||||
url = action_input.get("url", "https://api.github.com/users/octocat")
|
||||
|
||||
response = requests.get(url, timeout=10)
|
||||
response.raise_for_status()
|
||||
|
||||
data = response.json()
|
||||
|
||||
return {
|
||||
"status": "success",
|
||||
"message": f"成功获取数据,响应大小: {len(str(data))} 字符",
|
||||
"data": data,
|
||||
"capabilities": ["basic_fetch"]
|
||||
}
|
||||
|
||||
except ImportError:
|
||||
return {
|
||||
"status": "error",
|
||||
"message": "缺少必需依赖:requests库",
|
||||
"hint": "请运行: pip install requests>=2.25.0",
|
||||
"error_code": "MISSING_DEPENDENCY"
|
||||
}
|
||||
except Exception as e:
|
||||
return {
|
||||
"status": "error",
|
||||
"message": f"数据获取失败: {str(e)}",
|
||||
"error_code": "FETCH_ERROR"
|
||||
}
|
||||
|
||||
|
||||
class AnalyzeDataAction(BaseAction):
|
||||
"""数据分析Action - 支持多级功能降级"""
|
||||
|
||||
async def execute(self, action_input, context=None):
|
||||
"""分析数据,支持功能降级"""
|
||||
|
||||
# 检查可用的依赖
|
||||
has_pandas = self._check_dependency("pandas")
|
||||
has_numpy = self._check_dependency("numpy")
|
||||
|
||||
# 获取输入数据
|
||||
data = action_input.get("data", [1, 2, 3, 4, 5])
|
||||
|
||||
if has_pandas and has_numpy:
|
||||
return await self._advanced_analysis(data)
|
||||
elif has_numpy:
|
||||
return await self._numpy_analysis(data)
|
||||
else:
|
||||
return await self._basic_analysis(data)
|
||||
|
||||
def _check_dependency(self, package_name):
|
||||
"""检查依赖是否可用"""
|
||||
try:
|
||||
__import__(package_name)
|
||||
return True
|
||||
except ImportError:
|
||||
return False
|
||||
|
||||
async def _advanced_analysis(self, data):
|
||||
"""高级分析(使用pandas + numpy)"""
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
|
||||
# 转换为DataFrame
|
||||
df = pd.DataFrame({"values": data})
|
||||
|
||||
# 高级统计分析
|
||||
stats = {
|
||||
"count": len(df),
|
||||
"mean": df["values"].mean(),
|
||||
"median": df["values"].median(),
|
||||
"std": df["values"].std(),
|
||||
"min": df["values"].min(),
|
||||
"max": df["values"].max(),
|
||||
"quartiles": df["values"].quantile([0.25, 0.5, 0.75]).to_dict(),
|
||||
"skewness": df["values"].skew(),
|
||||
"kurtosis": df["values"].kurtosis()
|
||||
}
|
||||
|
||||
return {
|
||||
"status": "success",
|
||||
"message": "高级数据分析完成",
|
||||
"data": stats,
|
||||
"method": "advanced",
|
||||
"capabilities": ["pandas", "numpy", "advanced_stats"]
|
||||
}
|
||||
|
||||
async def _numpy_analysis(self, data):
|
||||
"""中级分析(仅使用numpy)"""
|
||||
import numpy as np
|
||||
|
||||
arr = np.array(data)
|
||||
|
||||
stats = {
|
||||
"count": len(arr),
|
||||
"mean": np.mean(arr),
|
||||
"median": np.median(arr),
|
||||
"std": np.std(arr),
|
||||
"min": np.min(arr),
|
||||
"max": np.max(arr),
|
||||
"sum": np.sum(arr)
|
||||
}
|
||||
|
||||
return {
|
||||
"status": "success",
|
||||
"message": "数值计算分析完成",
|
||||
"data": stats,
|
||||
"method": "numpy",
|
||||
"capabilities": ["numpy", "basic_stats"]
|
||||
}
|
||||
|
||||
async def _basic_analysis(self, data):
|
||||
"""基础分析(纯Python)"""
|
||||
|
||||
stats = {
|
||||
"count": len(data),
|
||||
"mean": sum(data) / len(data) if data else 0,
|
||||
"min": min(data) if data else None,
|
||||
"max": max(data) if data else None,
|
||||
"sum": sum(data)
|
||||
}
|
||||
|
||||
return {
|
||||
"status": "success",
|
||||
"message": "基础数据分析完成",
|
||||
"data": stats,
|
||||
"method": "basic",
|
||||
"capabilities": ["pure_python"],
|
||||
"note": "安装numpy和pandas可获得更多分析功能"
|
||||
}
|
||||
|
||||
|
||||
class VisualizeDataAction(BaseAction):
|
||||
"""数据可视化Action - 展示条件功能启用"""
|
||||
|
||||
async def execute(self, action_input, context=None):
|
||||
"""数据可视化"""
|
||||
|
||||
# 检查可视化依赖
|
||||
visualization_available = self._check_visualization_deps()
|
||||
|
||||
if not visualization_available:
|
||||
return {
|
||||
"status": "unavailable",
|
||||
"message": "数据可视化功能不可用",
|
||||
"reason": "缺少matplotlib和PIL依赖",
|
||||
"install_hint": "pip install matplotlib>=3.3.0 Pillow>=8.0.0",
|
||||
"alternative": "可以使用基础数据分析功能"
|
||||
}
|
||||
|
||||
return await self._create_visualization(action_input)
|
||||
|
||||
def _check_visualization_deps(self):
|
||||
"""检查可视化所需的依赖"""
|
||||
try:
|
||||
import matplotlib
|
||||
import PIL
|
||||
return True
|
||||
except ImportError:
|
||||
return False
|
||||
|
||||
async def _create_visualization(self, action_input):
|
||||
"""创建数据可视化"""
|
||||
import matplotlib.pyplot as plt
|
||||
import io
|
||||
import base64
|
||||
from PIL import Image
|
||||
|
||||
# 获取数据
|
||||
data = action_input.get("data", [1, 2, 3, 4, 5])
|
||||
chart_type = action_input.get("type", "line")
|
||||
|
||||
# 创建图表
|
||||
plt.figure(figsize=(10, 6))
|
||||
|
||||
if chart_type == "line":
|
||||
plt.plot(data)
|
||||
plt.title("线性图")
|
||||
elif chart_type == "bar":
|
||||
plt.bar(range(len(data)), data)
|
||||
plt.title("柱状图")
|
||||
elif chart_type == "hist":
|
||||
plt.hist(data, bins=10)
|
||||
plt.title("直方图")
|
||||
else:
|
||||
plt.plot(data)
|
||||
plt.title("默认线性图")
|
||||
|
||||
plt.xlabel("索引")
|
||||
plt.ylabel("数值")
|
||||
plt.grid(True)
|
||||
|
||||
# 保存为字节流
|
||||
buffer = io.BytesIO()
|
||||
plt.savefig(buffer, format='png', dpi=150, bbox_inches='tight')
|
||||
buffer.seek(0)
|
||||
|
||||
# 转换为base64
|
||||
image_base64 = base64.b64encode(buffer.getvalue()).decode()
|
||||
|
||||
plt.close() # 释放内存
|
||||
|
||||
return {
|
||||
"status": "success",
|
||||
"message": f"生成{chart_type}图表成功",
|
||||
"data": {
|
||||
"chart_type": chart_type,
|
||||
"data_points": len(data),
|
||||
"image_base64": image_base64
|
||||
},
|
||||
"capabilities": ["matplotlib", "pillow", "visualization"]
|
||||
}
|
||||
|
||||
|
||||
# 测试和演示代码
|
||||
async def demo_dependency_management():
|
||||
"""演示依赖管理功能"""
|
||||
|
||||
print("🔍 插件依赖管理演示")
|
||||
print("=" * 50)
|
||||
|
||||
# 创建插件实例
|
||||
plugin = DataAnalysisPlugin()
|
||||
|
||||
print("\n📦 插件依赖信息:")
|
||||
for dep in plugin.python_dependencies:
|
||||
status = "✅" if plugin._check_dependency_available(dep.package_name) else "❌"
|
||||
optional_str = " (可选)" if dep.optional else " (必需)"
|
||||
print(f" {status} {dep.package_name} {dep.version}{optional_str}")
|
||||
print(f" {dep.description}")
|
||||
|
||||
print("\n🧪 功能测试:")
|
||||
|
||||
# 测试数据获取
|
||||
fetch_action = FetchDataAction()
|
||||
result = await fetch_action.execute({"url": "https://httpbin.org/json"})
|
||||
print(f" 数据获取: {result['status']}")
|
||||
|
||||
# 测试数据分析
|
||||
analyze_action = AnalyzeDataAction()
|
||||
test_data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
||||
result = await analyze_action.execute({"data": test_data})
|
||||
print(f" 数据分析: {result['status']} (方法: {result.get('method', 'unknown')})")
|
||||
print(f" 可用功能: {result.get('capabilities', [])}")
|
||||
|
||||
# 测试数据可视化
|
||||
viz_action = VisualizeDataAction()
|
||||
result = await viz_action.execute({"data": test_data, "type": "line"})
|
||||
print(f" 数据可视化: {result['status']}")
|
||||
|
||||
print("\n💡 依赖管理建议:")
|
||||
missing_deps = plugin.plugin_info.get_missing_packages()
|
||||
if missing_deps:
|
||||
print(" 缺失的必需依赖:")
|
||||
for dep in missing_deps:
|
||||
print(f" - {dep.get_pip_requirement()}")
|
||||
print(f"\n 安装命令:")
|
||||
print(f" pip install {' '.join([dep.get_pip_requirement() for dep in missing_deps])}")
|
||||
else:
|
||||
print(" ✅ 所有必需依赖都已安装")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import asyncio
|
||||
|
||||
# 为演示添加依赖检查方法
|
||||
def _check_dependency_available(package_name):
|
||||
try:
|
||||
__import__(package_name)
|
||||
return True
|
||||
except ImportError:
|
||||
return False
|
||||
|
||||
DataAnalysisPlugin._check_dependency_available = _check_dependency_available
|
||||
|
||||
# 运行演示
|
||||
asyncio.run(demo_dependency_management())
|
||||
```
|
||||
|
||||
## 🎯 示例说明
|
||||
|
||||
### 1. 依赖分层设计
|
||||
|
||||
这个示例展示了三层依赖设计:
|
||||
|
||||
- **必需依赖**: `requests` - 核心功能必需
|
||||
- **增强依赖**: `pandas`, `numpy` - 提供更强大的分析能力
|
||||
- **可选依赖**: `matplotlib`, `PIL` - 提供可视化功能
|
||||
|
||||
### 2. 优雅降级策略
|
||||
|
||||
```python
|
||||
# 三级功能降级
|
||||
if has_pandas and has_numpy:
|
||||
return await self._advanced_analysis(data) # 最佳体验
|
||||
elif has_numpy:
|
||||
return await self._numpy_analysis(data) # 中等体验
|
||||
else:
|
||||
return await self._basic_analysis(data) # 基础体验
|
||||
```
|
||||
|
||||
### 3. 条件功能启用
|
||||
|
||||
```python
|
||||
# 只有依赖可用时才提供功能
|
||||
visualization_available = self._check_visualization_deps()
|
||||
if not visualization_available:
|
||||
return {"status": "unavailable", "install_hint": "..."}
|
||||
```
|
||||
|
||||
## 🚀 使用这个示例
|
||||
|
||||
### 1. 复制代码
|
||||
|
||||
将示例代码保存为 `plugins/data_analysis_plugin/plugin.py`
|
||||
|
||||
### 2. 测试依赖检查
|
||||
|
||||
```python
|
||||
from src.plugin_system import plugin_manager
|
||||
|
||||
# 检查这个插件的依赖
|
||||
result = plugin_manager.check_all_dependencies()
|
||||
print(result['plugin_status']['data_analysis_plugin'])
|
||||
```
|
||||
|
||||
### 3. 安装缺失依赖
|
||||
|
||||
```python
|
||||
# 生成requirements文件
|
||||
plugin_manager.generate_plugin_requirements("data_plugin_deps.txt")
|
||||
|
||||
# 手动安装
|
||||
# pip install -r data_plugin_deps.txt
|
||||
```
|
||||
|
||||
### 4. 测试功能降级
|
||||
|
||||
```bash
|
||||
# 测试基础功能(只安装requests)
|
||||
pip install requests>=2.25.0
|
||||
|
||||
# 测试增强功能(添加数据处理)
|
||||
pip install numpy>=1.20.0 pandas>=1.3.0
|
||||
|
||||
# 测试完整功能(添加可视化)
|
||||
pip install matplotlib>=3.3.0 Pillow>=8.0.0
|
||||
```
|
||||
|
||||
## 💡 最佳实践总结
|
||||
|
||||
1. **分层依赖设计**: 区分核心、增强、可选依赖
|
||||
2. **优雅降级处理**: 提供多级功能体验
|
||||
3. **明确错误信息**: 告诉用户如何解决依赖问题
|
||||
4. **条件功能启用**: 根据依赖可用性动态调整功能
|
||||
5. **详细依赖描述**: 说明每个依赖的用途
|
||||
|
||||
这个示例展示了如何构建一个既强大又灵活的插件,即使在依赖不完整的情况下也能提供有用的功能。
|
||||
BIN
docs/plugins/image/quick-start/1750326700269.png
Normal file
BIN
docs/plugins/image/quick-start/1750326700269.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.1 KiB |
BIN
docs/plugins/image/quick-start/1750332444690.png
Normal file
BIN
docs/plugins/image/quick-start/1750332444690.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
BIN
docs/plugins/image/quick-start/1750332508760.png
Normal file
BIN
docs/plugins/image/quick-start/1750332508760.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
@@ -1,28 +1,24 @@
|
||||
# 🚀 快速开始指南
|
||||
|
||||
本指南将带你用5分钟时间,从零开始创建一个功能完整的MaiBot插件。
|
||||
|
||||
> **💡 配置先行**
|
||||
>
|
||||
> 在开始之前,强烈建议你先阅读 ➡️ **[⚙️ 插件配置定义指南](configuration-guide.md)**。
|
||||
>
|
||||
> 了解如何通过 `config_schema` 定义插件配置,可以让系统为你自动生成带详细注释的 `config.toml` 文件,这是现代插件开发的最佳实践。
|
||||
本指南将带你用5分钟时间,从零开始创建一个功能完整的MaiCore插件。
|
||||
|
||||
## 📖 概述
|
||||
|
||||
这个指南将带你在5分钟内创建你的第一个MaiBot插件。我们将创建一个简单的问候插件,展示插件系统的基本概念。
|
||||
这个指南将带你快速创建你的第一个MaiCore插件。我们将创建一个简单的问候插件,展示插件系统的基本概念。无需阅读其他文档,跟着本指南就能完成!
|
||||
|
||||
## 🎯 学习目标
|
||||
|
||||
- 理解插件的基本结构
|
||||
- 创建你的第一个Action组件
|
||||
- 创建你的第一个Command组件
|
||||
- 学会配置插件
|
||||
- 从最简单的插件开始,循序渐进
|
||||
- 学会创建Action组件(智能动作)
|
||||
- 学会创建Command组件(命令响应)
|
||||
- 掌握配置Schema定义和配置文件自动生成(可选)
|
||||
|
||||
## 📂 准备工作
|
||||
|
||||
确保你已经:
|
||||
1. 克隆了MaiBot项目
|
||||
|
||||
1. 克隆了MaiCore项目
|
||||
2. 安装了Python依赖
|
||||
3. 了解基本的Python语法
|
||||
|
||||
@@ -30,360 +26,502 @@
|
||||
|
||||
### 1. 创建插件目录
|
||||
|
||||
在项目根目录的 `plugins/` 文件夹下创建你的插件目录:
|
||||
在项目根目录的 `plugins/` 文件夹下创建你的插件目录,目录名与插件名保持一致:
|
||||
|
||||
可以用以下命令快速创建:
|
||||
|
||||
```bash
|
||||
mkdir plugins/hello_world_plugin
|
||||
cd plugins/hello_world_plugin
|
||||
```
|
||||
|
||||
### 2. 创建插件主文件
|
||||
### 2. 创建最简单的插件
|
||||
|
||||
创建 `plugin.py` 文件:
|
||||
让我们从最基础的开始!创建 `plugin.py` 文件:
|
||||
|
||||
```python
|
||||
from typing import List, Tuple, Type
|
||||
from src.plugin_system import BasePlugin, register_plugin, ComponentInfo
|
||||
|
||||
# ===== 插件注册 =====
|
||||
|
||||
@register_plugin
|
||||
class HelloWorldPlugin(BasePlugin):
|
||||
"""Hello World插件 - 你的第一个MaiCore插件"""
|
||||
|
||||
# 插件基本信息(必须填写)
|
||||
plugin_name = "hello_world_plugin"
|
||||
plugin_description = "我的第一个MaiCore插件"
|
||||
plugin_version = "1.0.0"
|
||||
plugin_author = "你的名字"
|
||||
enable_plugin = True # 启用插件
|
||||
|
||||
def get_plugin_components(self) -> List[Tuple[ComponentInfo, Type]]:
|
||||
"""返回插件包含的组件列表(目前是空的)"""
|
||||
return []
|
||||
```
|
||||
|
||||
🎉 **恭喜!你刚刚创建了一个最简单但完整的MaiCore插件!**
|
||||
|
||||
**解释一下这些代码:**
|
||||
|
||||
- 首先,我们在plugin.py中定义了一个HelloWorldPulgin插件类,继承自 `BasePlugin` ,提供基本功能。
|
||||
- 通过给类加上,`@register_plugin` 装饰器,我们告诉系统"这是一个插件"
|
||||
- `plugin_name` 等是插件的基本信息,必须填写
|
||||
- `get_plugin_components()` 返回插件的功能组件,现在我们没有定义任何action(动作)或者command(指令),是空的
|
||||
|
||||
### 3. 测试基础插件
|
||||
|
||||
现在就可以测试这个插件了!启动MaiCore:
|
||||
|
||||
直接通过启动器运行MaiCore或者 `python bot.py`
|
||||
|
||||
在日志中你应该能看到插件被加载的信息。虽然插件还没有任何功能,但它已经成功运行了!
|
||||
|
||||

|
||||
|
||||
### 4. 添加第一个功能:问候Action
|
||||
|
||||
现在我们要给插件加入一个有用的功能,我们从最好玩的Action做起
|
||||
|
||||
Action是一类可以让MaiCore根据自身意愿选择使用的“动作”,在MaiCore中,不论是“回复”还是“不回复”,或者“发送表情”以及“禁言”等等,都是通过Action实现的。
|
||||
|
||||
你可以通过编写动作,来拓展MaiCore的能力,包括发送语音,截图,甚至操作文件,编写代码......
|
||||
|
||||
现在让我们给插件添加第一个简单的功能。这个Action可以对用户发送一句问候语。
|
||||
|
||||
在 `plugin.py` 文件中添加Action组件,完整代码如下:
|
||||
|
||||
```python
|
||||
from typing import List, Tuple, Type
|
||||
from src.plugin_system import (
|
||||
BasePlugin, register_plugin, BaseAction, BaseCommand,
|
||||
BasePlugin, register_plugin, BaseAction,
|
||||
ComponentInfo, ActionActivationType, ChatMode
|
||||
)
|
||||
|
||||
# ===== Action组件 =====
|
||||
|
||||
class HelloAction(BaseAction):
|
||||
"""问候Action - 展示智能动作的基本用法"""
|
||||
"""问候Action - 简单的问候动作"""
|
||||
|
||||
# ===== 激活控制必须项 =====
|
||||
focus_activation_type = ActionActivationType.KEYWORD
|
||||
normal_activation_type = ActionActivationType.KEYWORD
|
||||
mode_enable = ChatMode.ALL
|
||||
parallel_action = False
|
||||
|
||||
# ===== 基本信息必须项 =====
|
||||
# === 基本信息(必须填写)===
|
||||
action_name = "hello_greeting"
|
||||
action_description = "向用户发送友好的问候消息"
|
||||
action_description = "向用户发送问候消息"
|
||||
|
||||
# 关键词配置
|
||||
activation_keywords = ["你好", "hello", "hi"]
|
||||
keyword_case_sensitive = False
|
||||
|
||||
# ===== 功能定义必须项 =====
|
||||
# === 功能描述(必须填写)===
|
||||
action_parameters = {
|
||||
"greeting_style": "问候风格:casual(随意) 或 formal(正式)"
|
||||
"greeting_message": "要发送的问候消息"
|
||||
}
|
||||
|
||||
action_require = [
|
||||
"用户发送问候语时使用",
|
||||
"营造友好的聊天氛围"
|
||||
]
|
||||
|
||||
associated_types = ["text", "emoji"]
|
||||
"需要发送友好问候时使用",
|
||||
"当有人向你问好时使用",
|
||||
"当你遇见没有见过的人时使用"
|
||||
]
|
||||
associated_types = ["text"]
|
||||
|
||||
async def execute(self) -> Tuple[bool, str]:
|
||||
"""执行问候动作"""
|
||||
# 获取参数
|
||||
style = self.action_data.get("greeting_style", "casual")
|
||||
|
||||
# 根据风格生成问候语
|
||||
if style == "formal":
|
||||
message = "您好!很高兴为您服务!"
|
||||
emoji = "🙏"
|
||||
else:
|
||||
message = "嗨!很开心见到你!"
|
||||
emoji = "😊"
|
||||
|
||||
# 发送消息
|
||||
"""执行问候动作 - 这是核心功能"""
|
||||
# 发送问候消息
|
||||
greeting_message = self.action_data.get("greeting_message","")
|
||||
|
||||
message = "嗨!很开心见到你!😊" + greeting_message
|
||||
await self.send_text(message)
|
||||
await self.send_type("emoji", emoji)
|
||||
|
||||
return True, f"发送了{style}风格的问候"
|
||||
|
||||
return True, "发送了问候消息"
|
||||
|
||||
# ===== 插件注册 =====
|
||||
|
||||
@register_plugin
|
||||
class HelloWorldPlugin(BasePlugin):
|
||||
"""Hello World插件 - 你的第一个MaiCore插件"""
|
||||
|
||||
# 插件基本信息
|
||||
plugin_name = "hello_world_plugin"
|
||||
plugin_description = "我的第一个MaiCore插件,包含问候功能"
|
||||
plugin_version = "1.0.0"
|
||||
plugin_author = "你的名字"
|
||||
enable_plugin = True
|
||||
|
||||
def get_plugin_components(self) -> List[Tuple[ComponentInfo, Type]]:
|
||||
"""返回插件包含的组件列表"""
|
||||
return [
|
||||
# 添加我们的问候Action
|
||||
(HelloAction.get_action_info(), HelloAction),
|
||||
]
|
||||
```
|
||||
|
||||
**新增内容解释:**
|
||||
|
||||
- `HelloAction` 是一个Action组件,MaiCore可能会选择使用它
|
||||
- `execute()` 函数是Action的核心,定义了当Action被MaiCore选择后,具体要做什么
|
||||
- `self.send_text()` 是发送文本消息的便捷方法
|
||||
|
||||
### 5. 测试问候功能
|
||||
|
||||
重启MaiCore,然后在聊天中发送任意消息,比如:
|
||||
|
||||
```
|
||||
你好
|
||||
```
|
||||
|
||||
MaiCore可能会选择使用你的问候Action,发送回复:
|
||||
|
||||
```
|
||||
嗨!很开心见到你!😊
|
||||
```
|
||||
|
||||

|
||||
|
||||
> **💡 小提示**:MaiCore会智能地决定什么时候使用它。如果没有立即看到效果,多试几次不同的消息。
|
||||
|
||||
🎉 **太棒了!你的插件已经有实际功能了!**
|
||||
|
||||
### 5.5. 了解激活系统(重要概念)
|
||||
|
||||
Action固然好用简单,但是现在有个问题,当用户加载了非常多的插件,添加了很多自定义Action,LLM需要选择的Action也会变多
|
||||
|
||||
而不断增多的Action会加大LLM的消耗和负担,降低Action使用的精准度。而且我们并不需要LLM在所有时候都考虑所有Action
|
||||
|
||||
例如,当群友只是在进行正常的聊天,就没有必要每次都考虑是否要选择“禁言”动作,这不仅影响决策速度,还会增加消耗。
|
||||
|
||||
那有什么办法,能够让Action有选择的加入MaiCore的决策池呢?
|
||||
|
||||
**什么是激活系统?**
|
||||
激活系统决定了什么时候你的Action会被MaiCore"考虑"使用:
|
||||
|
||||
- **`ActionActivationType.ALWAYS`** - 总是可用(默认值)
|
||||
- **`ActionActivationType.KEYWORD`** - 只有消息包含特定关键词时才可用
|
||||
- **`ActionActivationType.PROBABILITY`** - 根据概率随机可用
|
||||
- **`ActionActivationType.NEVER`** - 永不可用(用于调试)
|
||||
|
||||
> **💡 使用提示**:
|
||||
>
|
||||
> - 推荐使用枚举类型(如 `ActionActivationType.ALWAYS`),有代码提示和类型检查
|
||||
> - 也可以直接使用字符串(如 `"always"`),系统都支持
|
||||
|
||||
### 5.6. 进阶:尝试关键词激活(可选)
|
||||
|
||||
现在让我们尝试一个更精确的激活方式!添加一个只在用户说特定关键词时才激活的Action:
|
||||
|
||||
```python
|
||||
# 在HelloAction后面添加这个新Action
|
||||
class ByeAction(BaseAction):
|
||||
"""告别Action - 只在用户说再见时激活"""
|
||||
|
||||
action_name = "bye_greeting"
|
||||
action_description = "向用户发送告别消息"
|
||||
|
||||
# 使用关键词激活
|
||||
focus_activation_type = ActionActivationType.KEYWORD
|
||||
normal_activation_type = ActionActivationType.KEYWORD
|
||||
|
||||
# 关键词设置
|
||||
activation_keywords = ["再见", "bye", "88", "拜拜"]
|
||||
keyword_case_sensitive = False
|
||||
|
||||
action_parameters = {"bye_message": "要发送的告别消息"}
|
||||
action_require = [
|
||||
"用户要告别时使用",
|
||||
"当有人要离开时使用",
|
||||
"当有人和你说再见时使用",
|
||||
]
|
||||
associated_types = ["text"]
|
||||
|
||||
async def execute(self) -> Tuple[bool, str]:
|
||||
bye_message = self.action_data.get("bye_message","")
|
||||
|
||||
message = "再见!期待下次聊天!👋" + bye_message
|
||||
await self.send_text(message)
|
||||
return True, "发送了告别消息"
|
||||
```
|
||||
|
||||
然后在插件注册中添加这个Action:
|
||||
|
||||
```python
|
||||
def get_plugin_components(self) -> List[Tuple[ComponentInfo, Type]]:
|
||||
return [
|
||||
(HelloAction.get_action_info(), HelloAction),
|
||||
(ByeAction.get_action_info(), ByeAction), # 添加告别Action
|
||||
]
|
||||
```
|
||||
|
||||
现在测试:发送"再见",应该会触发告别Action!
|
||||
|
||||
**关键词激活的特点:**
|
||||
|
||||
- 更精确:只在包含特定关键词时才会被考虑
|
||||
- 更可预测:用户知道说什么会触发什么功能
|
||||
- 更适合:特定场景或命令式的功能
|
||||
|
||||
### 6. 添加第二个功能:时间查询Command
|
||||
|
||||
现在让我们添加一个Command组件。Command和Action不同,它是直接响应用户命令的:
|
||||
|
||||
Command是最简单,最直接的相应,不由LLM判断选择使用
|
||||
|
||||
```python
|
||||
# 在现有代码基础上,添加Command组件
|
||||
|
||||
# ===== Command组件 =====
|
||||
|
||||
class TimeCommand(BaseCommand):
|
||||
"""时间查询Command - 展示命令的基本用法"""
|
||||
from src.plugin_system import BaseCommand
|
||||
#导入Command基类
|
||||
|
||||
command_pattern = r"^/time$"
|
||||
class TimeCommand(BaseCommand):
|
||||
"""时间查询Command - 响应/time命令"""
|
||||
|
||||
command_name = "time"
|
||||
command_description = "查询当前时间"
|
||||
|
||||
# === 命令设置(必须填写)===
|
||||
command_pattern = r"^/time$" # 精确匹配 "/time" 命令
|
||||
command_help = "查询当前时间"
|
||||
command_examples = ["/time"]
|
||||
intercept_message = True # 拦截消息处理
|
||||
intercept_message = True # 拦截消息,不让其他组件处理
|
||||
|
||||
async def execute(self) -> Tuple[bool, str]:
|
||||
"""执行时间查询"""
|
||||
import datetime
|
||||
|
||||
|
||||
# 获取当前时间
|
||||
time_format = self.get_config("time.format", "%Y-%m-%d %H:%M:%S")
|
||||
now = datetime.datetime.now()
|
||||
time_str = now.strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
await self.send_text(f"⏰ 当前时间:{time_str}")
|
||||
|
||||
time_str = now.strftime(time_format)
|
||||
|
||||
# 发送时间信息
|
||||
message = f"⏰ 当前时间:{time_str}"
|
||||
await self.send_text(message)
|
||||
|
||||
return True, f"显示了当前时间: {time_str}"
|
||||
|
||||
# ===== 插件注册 =====
|
||||
|
||||
@register_plugin
|
||||
class HelloWorldPlugin(BasePlugin):
|
||||
"""Hello World插件 - 你的第一个MaiBot插件"""
|
||||
"""Hello World插件 - 你的第一个MaiCore插件"""
|
||||
|
||||
# 插件基本信息
|
||||
plugin_name = "hello_world_plugin"
|
||||
plugin_description = "Hello World演示插件,展示基本的Action和Command用法"
|
||||
plugin_description = "我的第一个MaiCore插件,包含问候和时间查询功能"
|
||||
plugin_version = "1.0.0"
|
||||
plugin_author = "你的名字"
|
||||
enable_plugin = True # 默认启用插件
|
||||
config_file_name = "config.toml"
|
||||
|
||||
# Python依赖声明(可选)
|
||||
python_dependencies = [
|
||||
# 如果你的插件需要额外的Python包,在这里声明
|
||||
# PythonDependency(
|
||||
# package_name="requests",
|
||||
# version=">=2.25.0",
|
||||
# description="HTTP请求库"
|
||||
# ),
|
||||
]
|
||||
enable_plugin = True
|
||||
|
||||
def get_plugin_components(self) -> List[Tuple[ComponentInfo, Type]]:
|
||||
"""返回插件包含的组件列表"""
|
||||
return [
|
||||
# Action组件 - 使用类中定义的所有属性
|
||||
(HelloAction.get_action_info(), HelloAction),
|
||||
|
||||
# Command组件 - 需要指定name和description
|
||||
(TimeCommand.get_command_info(
|
||||
name="time_query",
|
||||
description="查询当前系统时间"
|
||||
), TimeCommand),
|
||||
(ByeAction.get_action_info(), ByeAction),
|
||||
(TimeCommand.get_command_info(), TimeCommand),
|
||||
]
|
||||
```
|
||||
|
||||
### 3. 创建配置文件
|
||||
**Command组件解释:**
|
||||
|
||||
创建 `config.toml` 文件:
|
||||
- Command是直接响应用户命令的组件
|
||||
- `command_pattern` 使用正则表达式匹配用户输入
|
||||
- `^/time$` 表示精确匹配 "/time"
|
||||
- `intercept_message = True` 表示处理完命令后不再让其他组件处理
|
||||
|
||||
```toml
|
||||
[plugin]
|
||||
name = "hello_world_plugin"
|
||||
version = "1.0.0"
|
||||
enabled = true
|
||||
description = "Hello World演示插件"
|
||||
### 7. 测试时间查询功能
|
||||
|
||||
[greeting]
|
||||
default_style = "casual"
|
||||
enable_emoji = true
|
||||
重启MaiCore,发送命令:
|
||||
|
||||
[time]
|
||||
timezone = "Asia/Shanghai"
|
||||
format = "%Y-%m-%d %H:%M:%S"
|
||||
|
||||
[logging]
|
||||
level = "INFO"
|
||||
```
|
||||
/time
|
||||
```
|
||||
|
||||
### 4. 创建说明文档
|
||||
你应该会收到回复:
|
||||
|
||||
创建 `README.md` 文件:
|
||||
```
|
||||
⏰ 当前时间:2024-01-01 12:30:45
|
||||
```
|
||||
|
||||
🎉 **太棒了!现在你的插件有3个功能了!**
|
||||
|
||||
### 8. 添加配置文件(可选进阶)
|
||||
|
||||
如果你想让插件更加灵活,可以添加配置支持。
|
||||
|
||||
> **🚨 重要:不要手动创建config.toml文件!**
|
||||
>
|
||||
> 我们需要在插件代码中定义配置Schema,让系统自动生成配置文件。
|
||||
|
||||
首先,在插件类中定义配置Schema:
|
||||
|
||||
```python
|
||||
from src.plugin_system.base.config_types import ConfigField
|
||||
|
||||
@register_plugin
|
||||
class HelloWorldPlugin(BasePlugin):
|
||||
"""Hello World插件 - 你的第一个MaiCore插件"""
|
||||
|
||||
plugin_name = "hello_world_plugin"
|
||||
plugin_description = "我的第一个MaiCore插件,包含问候和时间查询功能"
|
||||
plugin_version = "1.0.0"
|
||||
plugin_author = "你的名字"
|
||||
enable_plugin = True
|
||||
config_file_name = "config.toml" # 配置文件名
|
||||
|
||||
# 配置节描述
|
||||
config_section_descriptions = {
|
||||
"plugin": "插件基本信息",
|
||||
"greeting": "问候功能配置",
|
||||
"time": "时间查询配置"
|
||||
}
|
||||
|
||||
# 配置Schema定义
|
||||
config_schema = {
|
||||
"plugin": {
|
||||
"name": ConfigField(type=str, default="hello_world_plugin", description="插件名称"),
|
||||
"version": ConfigField(type=str, default="1.0.0", description="插件版本"),
|
||||
"enabled": ConfigField(type=bool, default=True, description="是否启用插件")
|
||||
},
|
||||
"greeting": {
|
||||
"message": ConfigField(
|
||||
type=str,
|
||||
default="嗨!很开心见到你!😊",
|
||||
description="默认问候消息"
|
||||
),
|
||||
"enable_emoji": ConfigField(type=bool, default=True, description="是否启用表情符号")
|
||||
},
|
||||
"time": {
|
||||
"format": ConfigField(
|
||||
type=str,
|
||||
default="%Y-%m-%d %H:%M:%S",
|
||||
description="时间显示格式"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
def get_plugin_components(self) -> List[Tuple[ComponentInfo, Type]]:
|
||||
return [
|
||||
(HelloAction.get_action_info(), HelloAction),
|
||||
(ByeAction.get_action_info(), ByeAction),
|
||||
(TimeCommand.get_command_info(), TimeCommand),
|
||||
]
|
||||
```
|
||||
|
||||
然后修改Action和Command代码,让它们读取配置:
|
||||
|
||||
```python
|
||||
# 在HelloAction的execute方法中:
|
||||
async def execute(self) -> Tuple[bool, str]:
|
||||
# 从配置文件读取问候消息
|
||||
greeting_message = self.action_data.get("greeting_message", "")
|
||||
base_message = self.get_config("greeting.message", "嗨!很开心见到你!😊")
|
||||
|
||||
message = base_message + greeting_message
|
||||
await self.send_text(message)
|
||||
return True, "发送了问候消息"
|
||||
|
||||
# 在TimeCommand的execute方法中:
|
||||
async def execute(self) -> Tuple[bool, str]:
|
||||
import datetime
|
||||
|
||||
# 从配置文件读取时间格式
|
||||
time_format = self.get_config("time.format", "%Y-%m-%d %H:%M:%S")
|
||||
now = datetime.datetime.now()
|
||||
time_str = now.strftime(time_format)
|
||||
|
||||
message = f"⏰ 当前时间:{time_str}"
|
||||
await self.send_text(message)
|
||||
return True, f"显示了当前时间: {time_str}"
|
||||
```
|
||||
|
||||
**配置系统工作流程:**
|
||||
|
||||
1. **定义Schema**: 在插件代码中定义配置结构
|
||||
2. **自动生成**: 启动插件时,系统会自动生成 `config.toml` 文件
|
||||
3. **用户修改**: 用户可以修改生成的配置文件
|
||||
4. **代码读取**: 使用 `self.get_config()` 读取配置值
|
||||
|
||||
**配置功能解释:**
|
||||
|
||||
- `self.get_config()` 可以读取配置文件中的值
|
||||
- 第一个参数是配置路径(用点分隔),第二个参数是默认值
|
||||
- 配置文件会包含详细的注释和说明,用户可以轻松理解和修改
|
||||
- **绝不要手动创建配置文件**,让系统自动生成
|
||||
|
||||
### 9. 创建说明文档(可选)
|
||||
|
||||
创建 `README.md` 文件来说明你的插件:
|
||||
|
||||
```markdown
|
||||
# Hello World 插件
|
||||
|
||||
## 概述
|
||||
|
||||
这是一个简单的Hello World插件,演示了MaiBot插件系统的基本用法。
|
||||
我的第一个MaiCore插件,包含问候和时间查询功能。
|
||||
|
||||
## 功能
|
||||
|
||||
- **HelloAction**: 智能问候动作,响应用户的问候语
|
||||
- **TimeCommand**: 时间查询命令,显示当前时间
|
||||
- **问候功能**: 当用户说"你好"、"hello"、"hi"时自动回复
|
||||
- **时间查询**: 发送 `/time` 命令查询当前时间
|
||||
|
||||
## 使用方法
|
||||
### 问候功能
|
||||
发送包含以下关键词的消息:
|
||||
- "你好"
|
||||
- "hello"
|
||||
- "hi"
|
||||
|
||||
### Action使用
|
||||
当用户发送包含"你好"、"hello"或"hi"的消息时,插件会自动触发问候动作。
|
||||
### 时间查询
|
||||
发送命令:`/time`
|
||||
|
||||
### Command使用
|
||||
发送 `/time` 查询当前时间。
|
||||
## 配置文件
|
||||
插件会自动生成 `config.toml` 配置文件,用户可以修改:
|
||||
- 问候消息内容
|
||||
- 时间显示格式
|
||||
- 插件启用状态
|
||||
|
||||
## 配置
|
||||
|
||||
可以通过 `config.toml` 调整插件行为。
|
||||
注意:配置文件是自动生成的,不要手动创建!
|
||||
```
|
||||
|
||||
## 🎮 测试插件
|
||||
## 🎯 你学会了什么
|
||||
|
||||
### 1. 启动MaiBot
|
||||
恭喜!你刚刚从零开始创建了一个完整的MaiCore插件!让我们回顾一下:
|
||||
|
||||
将插件放入 `plugins/` 目录后,启动MaiBot:
|
||||
### 核心概念
|
||||
|
||||
- **插件(Plugin)**: 包含多个功能组件的集合
|
||||
- **Action组件**: 智能动作,由麦麦根据情境自动选择使用
|
||||
- **Command组件**: 直接响应用户命令的功能
|
||||
- **配置Schema**: 定义配置结构,系统自动生成配置文件
|
||||
|
||||
### 开发流程
|
||||
|
||||
1. ✅ 创建最简单的插件框架
|
||||
2. ✅ 添加Action
|
||||
3. ✅ 理解激活系统的工作原理
|
||||
4. ✅ 尝试KEYWORD激活的Action(进阶)
|
||||
5. ✅ 添加Command组件
|
||||
6. ✅ 可选定义配置Schema
|
||||
7. ✅ 测试完整功能
|
||||
|
||||
## 📚 进阶学习
|
||||
|
||||
现在你已经掌握了基础,可以继续深入学习:
|
||||
|
||||
1. **掌握更多Action功能** 📖 [Action组件详解](action-components.md)
|
||||
|
||||
- 学习不同的激活方式
|
||||
- 了解Action的生命周期
|
||||
- 掌握参数传递
|
||||
2. **学会配置管理** ⚙️ [插件配置定义指南](configuration-guide.md)
|
||||
|
||||
- 定义配置Schema
|
||||
- 自动生成配置文件
|
||||
- 配置验证和类型检查
|
||||
3. **深入Command系统** 📖 [Command组件详解](command-components.md)
|
||||
|
||||
- 复杂正则表达式
|
||||
- 参数提取和处理
|
||||
- 错误处理
|
||||
4. **掌握API系统** 📖 [新API使用指南](examples/replyer_api_usage.md)
|
||||
|
||||
- replyer_1智能生成
|
||||
- 高级消息处理
|
||||
- 表情和媒体发送
|
||||
|
||||
祝你插件开发愉快!🎉
|
||||
|
||||
```bash
|
||||
python main.py
|
||||
```
|
||||
|
||||
### 2. 测试Action
|
||||
|
||||
发送消息:
|
||||
```
|
||||
你好
|
||||
```
|
||||
|
||||
期望输出:
|
||||
```
|
||||
嗨!很开心见到你!😊
|
||||
```
|
||||
|
||||
### 3. 测试Command
|
||||
|
||||
发送命令:
|
||||
```
|
||||
/time
|
||||
```
|
||||
|
||||
期望输出:
|
||||
```
|
||||
⏰ 当前时间:2024-01-01 12:00:00
|
||||
```
|
||||
|
||||
## 🔍 解析代码
|
||||
|
||||
### Action组件重点
|
||||
|
||||
1. **激活控制**: 使用 `KEYWORD` 激活类型,当检测到指定关键词时触发
|
||||
2. **必须项完整**: 包含所有必须的类属性
|
||||
3. **智能决策**: 麦麦会根据情境决定是否使用这个Action
|
||||
|
||||
### Command组件重点
|
||||
|
||||
1. **正则匹配**: 使用 `^/time$` 精确匹配 `/time` 命令
|
||||
2. **消息拦截**: 设置 `intercept_message = True` 防止命令继续处理
|
||||
3. **即时响应**: 匹配到命令立即执行
|
||||
|
||||
### 插件注册重点
|
||||
|
||||
1. **@register_plugin**: 装饰器自动注册插件
|
||||
2. **组件列表**: `get_plugin_components()` 返回所有组件
|
||||
3. **配置加载**: 自动加载 `config.toml` 文件
|
||||
|
||||
## 📦 添加依赖包(可选)
|
||||
|
||||
如果你的插件需要额外的Python包,可以声明依赖:
|
||||
|
||||
```python
|
||||
from src.plugin_system import PythonDependency
|
||||
|
||||
@register_plugin
|
||||
class HelloWorldPlugin(BasePlugin):
|
||||
# ... 其他配置 ...
|
||||
|
||||
# 声明Python依赖
|
||||
python_dependencies = [
|
||||
PythonDependency(
|
||||
package_name="requests",
|
||||
version=">=2.25.0",
|
||||
description="HTTP请求库,用于网络功能"
|
||||
),
|
||||
PythonDependency(
|
||||
package_name="numpy",
|
||||
version=">=1.20.0",
|
||||
optional=True,
|
||||
description="数值计算库(可选功能)"
|
||||
),
|
||||
]
|
||||
```
|
||||
|
||||
### 依赖检查
|
||||
|
||||
系统会自动检查依赖,你也可以手动检查:
|
||||
|
||||
```python
|
||||
from src.plugin_system import plugin_manager
|
||||
|
||||
# 检查所有插件依赖
|
||||
result = plugin_manager.check_all_dependencies()
|
||||
print(f"缺少依赖的插件: {result['plugins_with_missing_required']}个")
|
||||
|
||||
# 生成requirements文件
|
||||
plugin_manager.generate_plugin_requirements("plugin_deps.txt")
|
||||
```
|
||||
|
||||
📚 **详细了解**: [依赖管理系统](dependency-management.md)
|
||||
|
||||
## 🎯 下一步
|
||||
|
||||
恭喜!你已经创建了第一个MaiBot插件。接下来可以:
|
||||
|
||||
1. 学习 [Action组件详解](action-components.md) 掌握更复杂的Action开发
|
||||
2. 学习 [Command组件详解](command-components.md) 创建更强大的命令
|
||||
3. 了解 [依赖管理系统](dependency-management.md) 管理Python包依赖
|
||||
4. 查看 [API参考](api/) 了解所有可用的接口
|
||||
5. 参考 [完整示例](examples/complete-examples.md) 学习最佳实践
|
||||
|
||||
## 🐛 常见问题
|
||||
|
||||
### Q: 插件没有加载怎么办?
|
||||
A: 检查:
|
||||
1. 插件是否放在 `plugins/` 目录下
|
||||
2. `plugin.py` 文件语法是否正确
|
||||
3. 查看启动日志中的错误信息
|
||||
|
||||
### Q: Action没有触发怎么办?
|
||||
A: 检查:
|
||||
1. 关键词是否正确配置
|
||||
2. 消息是否包含激活关键词
|
||||
3. 聊天模式是否匹配
|
||||
|
||||
### Q: Command无响应怎么办?
|
||||
A: 检查:
|
||||
1. 正则表达式是否正确
|
||||
2. 命令格式是否精确匹配
|
||||
3. 是否有其他插件拦截了消息
|
||||
|
||||
## 🔧 插件启用状态管理
|
||||
|
||||
### 启用状态控制方式
|
||||
|
||||
插件可以通过以下两种方式控制启用状态:
|
||||
|
||||
1. **类属性控制**
|
||||
```python
|
||||
class MyPlugin(BasePlugin):
|
||||
enable_plugin = True # 在类中设置启用状态
|
||||
```
|
||||
|
||||
2. **配置文件控制**
|
||||
```toml
|
||||
[plugin]
|
||||
enabled = true # 在配置文件中设置启用状态
|
||||
```
|
||||
|
||||
### 启用状态优先级
|
||||
|
||||
1. 配置文件中的设置优先级高于类属性
|
||||
2. 如果配置文件中没有 `[plugin] enabled` 设置,则使用类属性中的值
|
||||
3. 如果类属性也没有设置,则使用 `BasePlugin` 的默认值 `False`
|
||||
|
||||
### 最佳实践
|
||||
|
||||
1. 在开发插件时,建议在类中设置 `enable_plugin = True`
|
||||
2. 在部署插件时,通过配置文件控制启用状态
|
||||
3. 在文档中明确说明插件的默认启用状态
|
||||
4. 提供配置示例,说明如何启用/禁用插件
|
||||
|
||||
### 常见问题
|
||||
|
||||
1. **插件未加载**
|
||||
- 检查类属性 `enable_plugin` 是否设置为 `True`
|
||||
- 检查配置文件中的 `[plugin] enabled` 设置
|
||||
- 查看日志中是否有插件加载相关的错误信息
|
||||
|
||||
2. **配置文件不生效**
|
||||
- 确保配置文件名称正确(默认为 `config.toml`)
|
||||
- 确保配置文件格式正确(TOML格式)
|
||||
- 确保配置文件中的 `[plugin]` 部分存在
|
||||
|
||||
3. **动态启用/禁用**
|
||||
- 修改配置文件后需要重启MaiBot才能生效
|
||||
- 目前不支持运行时动态启用/禁用插件
|
||||
|
||||
---
|
||||
|
||||
🎉 **成功!你已经掌握了MaiBot插件开发的基础!**
|
||||
Reference in New Issue
Block a user