refc:重构插件api,补全文档,合并expressor和replyer,分离reply和sender,新log浏览器

This commit is contained in:
SengokuCola
2025-06-19 20:20:34 +08:00
parent 7e05ede846
commit ab28b94e33
63 changed files with 5285 additions and 8316 deletions

158
docs/plugins/README.md Normal file
View 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生态系统

View File

@@ -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的开发变得更加简洁和强大

View File

@@ -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格式消息发送变得更加简洁高效

View File

@@ -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开发变得更加简洁和直观
## 🎯 最佳实践

View File

@@ -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
```
## 🎯 实际使用案例
### 案例1API调用配置
```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`已经为你准备好了一切!

View File

@@ -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`方法和自动化的配置生成机制已经为你准备好了一切!

View File

@@ -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. **及时的反馈**:让用户知道操作状态
---
🎉 **遵循这些标准可以确保插件的质量、性能和用户体验!**

View File

@@ -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插件系统的强大功能根据你的需求选择合适的示例作为起点。**

View File

@@ -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组件可以灵活地访问插件配置实现更加强大和可定制的功能

View File

@@ -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. **详细依赖描述**: 说明每个依赖的用途
这个示例展示了如何构建一个既强大又灵活的插件,即使在依赖不完整的情况下也能提供有用的功能。

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -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`
在日志中你应该能看到插件被加载的信息。虽然插件还没有任何功能,但它已经成功运行了!
![1750326700269](image/quick-start/1750326700269.png)
### 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发送回复
```
嗨!很开心见到你!😊
```
![1750332508760](image/quick-start/1750332508760.png)
> **💡 小提示**MaiCore会智能地决定什么时候使用它。如果没有立即看到效果多试几次不同的消息。
🎉 **太棒了!你的插件已经有实际功能了!**
### 5.5. 了解激活系统(重要概念)
Action固然好用简单但是现在有个问题当用户加载了非常多的插件添加了很多自定义ActionLLM需要选择的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插件开发的基础**