欢迎使用QQ机器人插件系统!本文档将指导您开发功能强大的机器人插件。
现在插件可以在一次回复中发送多条不同类型的消息!
# 🆕 新功能:一次发送多条消息
return {
'response': [
MessageBuilder.text('📊 查询结果'),
MessageBuilder.text_card('详细数据...'),
MessageBuilder.markdown('## 分析报告\n...'),
MessageBuilder.text('✅ 查询完成')
],
'handled': True
}
插件现在支持优先级设置,控制执行顺序!
class Plugin(BasePlugin):
def __init__(self):
super().__init__()
self.priority = 5 # 数字越小优先级越高,默认为10
插件可以处理消息后允许其他插件继续处理!
return {
'response': MessageBuilder.text('我处理了,但其他插件也可以处理'),
'handled': True,
'continue': True # 允许其他插件继续处理这条消息
}
主要优势:
我们的插件系统采用Hook驱动 + 命令发现的混合架构:
Plugins/
├── your_plugin/ # 插件目录
│ ├── __init__.py # 插件主文件(必需)
│ ├── config.py # 配置文件(可选)
│ ├── utils.py # 工具函数(可选)
│ └── README.md # 插件说明(推荐)
├── echo/ # 示例插件
└── help/ # 帮助插件
mkdir Plugins/my_plugin
cd Plugins/my_plugin
创建 __init__.py 文件:
"""
我的第一个插件
"""
from Core.plugin.base import BasePlugin
from Core.logging.file_logger import log_info
from Core.message.builder import MessageBuilder
class Plugin(BasePlugin):
"""我的插件类"""
def __init__(self):
super().__init__()
# 插件信息
self.name = "MyPlugin"
self.version = "1.0.0"
self.description = "我的第一个QQ机器人插件"
self.author = "Your Name"
self.priority = 10 # 插件优先级,数字越小优先级越高
# 注册命令信息(用于展示和帮助)
self.register_command_info('hello', '打招呼', '/hello')
self.register_command_info('info', '显示插件信息', '/info')
# 注册Hook事件处理器
self.hooks = {
'message_received': [self.handle_message_hook],
'before_bot_start': [self.on_before_bot_start_hook],
'after_bot_start': [self.on_after_bot_start_hook],
'before_bot_stop': [self.on_before_bot_stop_hook],
'after_bot_stop': [self.on_after_bot_stop_hook]
}
log_info(0, f"{self.name}插件初始化完成", "PLUGIN_INIT", plugin=self.name)
def handle_message_hook(self, message_data, bot_id=None):
"""处理消息Hook"""
try:
content = message_data.get('content', '').strip()
# 处理命令
if content.startswith('/'):
return self._handle_command(content, bot_id)
# 处理自然语言
if content.lower() in ['hello', '你好']:
return {
'response': MessageBuilder.text(f'你好!我是{self.name}插件 👋'),
'handled': True
}
# 不处理其他消息
return {'handled': False}
except Exception as e:
log_info(bot_id or 0, f"{self.name}插件处理消息异常: {e}", "PLUGIN_ERROR")
return {'handled': False}
def _handle_command(self, content, bot_id=None):
"""处理命令"""
# 解析命令
parts = content[1:].split()
if not parts:
return {'handled': False}
command = parts[0].lower()
args = parts[1:] if len(parts) > 1 else []
# 处理支持的命令
if command == 'hello':
return {
'response': MessageBuilder.text(f'你好!我是{self.name}插件 👋'),
'handled': True
}
elif command == 'info':
# 🆕 多消息回复示例
return {
'response': [
MessageBuilder.text(f'📋 {self.name} 插件信息'),
MessageBuilder.text_card(
text=f'插件名称:{self.name}\n版本:{self.version}\n作者:{self.author}',
description=self.description,
prompt='插件详细信息'
),
MessageBuilder.text('✨ 感谢使用!')
],
'handled': True
}
return {'handled': False}
def on_before_bot_start_hook(self, bot_id, bot_config):
"""机器人启动前Hook"""
return {'message': f'{self.name}插件准备为机器人 {bot_id} 初始化'}
def on_after_bot_start_hook(self, bot_id, bot_config):
"""机器人启动后Hook"""
return {'message': f'{self.name}插件已为机器人 {bot_id} 准备就绪'}
def on_before_bot_stop_hook(self, bot_id):
"""机器人停止前Hook"""
return {'message': f'{self.name}插件准备为机器人 {bot_id} 清理资源'}
def on_after_bot_stop_hook(self, bot_id):
"""机器人停止后Hook"""
return {'message': f'{self.name}插件已为机器人 {bot_id} 清理完成'}
重启机器人服务,插件将自动加载。
系统会自动扫描 Plugins/ 目录下的所有子目录:
__ 开头(如 __pycache__)__init__.py 文件__init__.py 中必须有 Plugin 类系统会自动加载以下默认插件:
echo - 回声插件help - 帮助插件# 1. 插件发现
plugins = plugin_manager.discover_plugins()
# 2. 插件加载
for plugin_name in plugins:
plugin_manager.load_plugin(plugin_name)
# 3. Hook注册
# 系统自动注册插件的hooks到Hook系统
# 4. 插件卸载(可选)
plugin_manager.unload_plugin(plugin_name)
# 5. 插件重载(可选)
plugin_manager.reload_plugin(plugin_name)
# 获取插件信息
plugin_info = plugin_manager.get_plugin_info('my_plugin')
# 获取所有插件
all_plugins = plugin_manager.get_all_plugins()
# 检查插件是否加载
is_loaded = 'my_plugin' in plugin_manager.plugins
| Hook事件 | 触发时机 | 参数 | 返回值 |
|---|---|---|---|
message_received |
收到消息时 | message_data, user_id, bot_id |
{'response': MessageBuilder对象或数组, 'handled': bool, 'continue': bool} |
message_not_handled |
没有插件处理消息时 | message_data, user_id, bot_id |
{'response': MessageBuilder对象或数组, 'handled': bool, 'continue': bool} |
before_bot_start |
机器人启动前 | bot_id, bot_config |
{'message': str} (可选) |
after_bot_start |
机器人启动后 | bot_id, bot_config |
{'message': str} (可选) |
before_bot_stop |
机器人停止前 | bot_id |
{'message': str} (可选) |
after_bot_stop |
机器人停止后 | bot_id |
{'message': str} (可选) |
🆕 Hook现在按插件优先级执行!
# 执行顺序示例
SecurityPlugin (priority=1) # 最先执行
AdminPlugin (priority=5) # 然后执行
EchoPlugin (priority=10) # 接着执行
StatsPlugin (priority=50) # 再执行
HelpPlugin (priority=100) # 最后执行
# 完整的Hook返回值格式
return {
'response': MessageBuilder.text('回复内容'), # 可选:回复消息
'handled': True, # 必需:是否处理了消息
'continue': False # 🆕 可选:是否允许其他插件继续处理
}
# 简化格式(向后兼容)
return {'handled': False} # 没有处理消息,让其他插件处理
message_data = {
'content': '消息内容', # 消息文本
'type': 'c2c', # 消息类型: c2c(私聊), channel(频道), group_at(群聊@)
'id': 'message_id', # 消息ID
'msg_id': 'message_id', # 消息ID(用于回复)
'timestamp': '2025-01-01...', # 时间戳
'author': { # 发送者信息
'user_openid': 'user_123',
'id': 'author_id',
'username': 'user_name' # 用户名(如果有)
},
# QQ频道消息额外字段
'channel_id': 'channel_123', # 频道ID(仅频道消息)
'guild_id': 'guild_456', # 服务器ID(仅频道消息)
# 群聊消息额外字段
'group_openid': 'group_123', # 群聊ID(仅群聊消息)
# 原始事件数据(用于高级处理)
'raw_event': {...} # 完整的QQ事件数据
}
c2c - 单聊消息(私聊)channel - 频道消息group_at - 群聊@消息direct_message - 私信消息at_message - 公域频道@消息def handle_message_hook(self, message_data, bot_id=None):
"""安全地获取消息信息"""
# 安全获取消息内容
content = message_data.get('content', '').strip()
# 安全获取消息类型
msg_type = message_data.get('type', 'unknown')
# 安全获取发送者信息
author = message_data.get('author', {})
user_id = author.get('user_openid') or author.get('id', 'unknown')
username = author.get('username', '未知用户')
# 根据消息类型获取特定信息
if msg_type == 'group_at':
group_id = message_data.get('group_openid')
elif msg_type == 'channel':
channel_id = message_data.get('channel_id')
guild_id = message_data.get('guild_id')
# 处理消息...
return {'handled': False}
# 🆕 单条消息回复
return {
'response': MessageBuilder.text('单条回复内容'),
'handled': True
}
# 🆕 多条消息回复
return {
'response': [
MessageBuilder.text('第一条消息'),
MessageBuilder.markdown('## 第二条消息\n这是Markdown格式'),
MessageBuilder.image('https://example.com/image.jpg', '第三条图片消息')
],
'handled': True
}
# 🆕 继续执行机制
return {
'response': MessageBuilder.text('我处理了这条消息'),
'handled': True,
'continue': True # 允许其他插件继续处理这条消息
}
# 🆕 混合类型多消息回复
return {
'response': [
MessageBuilder.text('📊 数据查询结果:'),
MessageBuilder.text_card(
text='详细数据内容...',
description='数据卡片',
prompt='点击查看详情'
),
MessageBuilder.text('✅ 查询完成!')
],
'handled': True,
'continue': False # 默认值,处理后停止其他插件
}
# 没有处理消息,让其他插件处理
return {'handled': False}
# 处理了消息,但不回复
return {'handled': True}
🆕 新增Hook: 当没有插件处理消息时触发,通常用于提供帮助信息或兜底处理。
def handle_message_not_handled(self, message_data, user_id, bot_id):
"""处理未知命令"""
content = message_data.get('content', '').strip()
# 提供帮助信息
if content:
return {
'response': [
MessageBuilder.text(f'❓ 未知命令: {content}'),
MessageBuilder.text('📚 输入 "help" 查看可用命令')
],
'handled': True
}
return {'handled': False}
# 注册Hook
self.hooks = {
'message_received': [self.handle_message],
'message_not_handled': [self.handle_message_not_handled] # 🆕 新Hook
}
# 返回状态消息
return {'message': '操作完成'}
# 或者返回空
return {}
Hook现在按插件优先级顺序执行,实现更精确的控制:
# 执行流程示例
用户发送: "hello"
1. SecurityPlugin (priority=1)
→ 检查安全性 → continue: True
2. AdminPlugin (priority=5)
→ 检查管理员权限 → continue: True
3. EchoPlugin (priority=10)
→ 处理echo命令 → handled: True, continue: False (停止)
4. HelpPlugin (priority=100)
→ 不会执行(因为EchoPlugin已处理并停止)
系统支持两阶段消息处理:
message_received Hook按优先级执行message_not_handled Hook# 第一阶段:常规处理
class EchoPlugin(BasePlugin):
def __init__(self):
self.priority = 10
def handle_message(self, message_data, user_id, bot_id):
if message_data.get('content', '').startswith('echo '):
return {'response': '...', 'handled': True}
return {'handled': False} # 不处理,进入第二阶段
# 第二阶段:兜底处理
class HelpPlugin(BasePlugin):
def __init__(self):
self.priority = 100
def handle_not_handled(self, message_data, user_id, bot_id):
return {
'response': MessageBuilder.text('❓ 未知命令,输入help查看帮助'),
'handled': True
}
Hook系统内置了频率限制机制,防止插件滥用:
# 插件开发者无需特殊处理,系统自动管理频率限制
def handle_message_hook(self, message_data, user_id, bot_id):
# 正常编写处理逻辑即可
return {'response': '处理结果', 'handled': True}
Hook系统具有完善的错误处理机制:
def handle_message_hook(self, message_data, bot_id=None):
try:
# 您的处理逻辑
return self.process_message(message_data)
except Exception as e:
# 异常会被Hook系统自动捕获和记录
# 建议在插件内部也进行适当的错误处理
return {'handled': False}
可以通过自定义Hook事件实现插件间通信:
# 插件A:触发自定义事件
class PluginA(BasePlugin):
def some_method(self):
# 通过插件管理器触发自定义Hook
from Core.bot.manager import get_bot_manager
bot_manager = get_bot_manager()
bot_manager.plugin_manager.trigger_hook('custom_data_update', {'data': 'some_value'})
# 插件B:监听自定义事件
class PluginB(BasePlugin):
def __init__(self):
super().__init__()
self.hooks = {
'custom_data_update': [self.handle_data_update]
}
def handle_data_update(self, data):
# 处理来自其他插件的数据
print(f"收到数据更新: {data}")
MessageBuilder是QQ机器人系统的核心消息构建工具,提供了统一的消息格式化接口。它支持多种消息类型,确保插件能够发送各种格式的消息。
from Core.message.builder import MessageBuilder
最基础的消息类型,用于发送纯文本内容:
# 基础文本消息
def handle_message_hook(self, message_data, bot_id=None):
if message_data.get('content') == '/hello':
return {
'response': MessageBuilder.text('你好!欢迎使用QQ机器人!'),
'handled': True
}
支持Markdown格式的富文本消息:
def handle_help_command(self):
markdown_content = """
# 🤖 机器人帮助
## 📋 可用命令
### 基础命令
- `/help` - 显示此帮助信息
- `/status` - 查看机器人状态
- `/ping` - 测试连接
### 功能命令
- `/weather [城市]` - 查询天气
- `/joke` - 随机笑话
- `/time` - 当前时间
## 💡 使用提示
发送命令时请确保格式正确,如有问题请联系管理员。
"""
return {
'response': MessageBuilder.markdown(markdown_content),
'handled': True
}
QQ官方支持多种富媒体消息类型,包括图片、视频、语音和文件。
支持格式:png/jpg
def handle_image_command(self, args):
if not args:
return {
'response': MessageBuilder.text('请提供图片URL'),
'handled': True
}
image_url = args[0]
caption = "这是一张图片"
return {
'response': MessageBuilder.image(image_url, caption),
'handled': True
}
# 发送本地图片
def send_local_image(self):
local_path = "/path/to/image.jpg"
return {
'response': MessageBuilder.image(local_path, "本地图片"),
'handled': True
}
# 自动上传网络图片
def send_network_image(self):
return {
'response': MessageBuilder.image(
"https://example.com/image.jpg",
"网络图片",
auto_upload=True # 自动上传到QQ服务器
),
'handled': True
}
支持格式:mp4
def handle_video_command(self, args):
if not args:
return {
'response': MessageBuilder.text('请提供视频URL'),
'handled': True
}
video_url = args[0]
caption = "这是一个视频"
return {
'response': MessageBuilder.video(video_url, caption),
'handled': True
}
# 发送本地视频
def send_local_video(self):
return {
'response': MessageBuilder.video(
"/path/to/video.mp4",
"本地视频文件",
auto_upload=True
),
'handled': True
}
支持格式:silk
def handle_voice_command(self, args):
if not args:
return {
'response': MessageBuilder.text('请提供语音URL'),
'handled': True
}
voice_url = args[0]
caption = "这是一段语音"
return {
'response': MessageBuilder.voice(voice_url, caption),
'handled': True
}
# 发送本地语音
def send_local_voice(self):
return {
'response': MessageBuilder.voice(
"/path/to/voice.silk",
"本地语音文件",
auto_upload=True
),
'handled': True
}
支持各种文件格式(注意:根据QQ官方文档,文件类型暂不开放)
def handle_file_command(self, args):
if not args:
return {
'response': MessageBuilder.text('请提供文件URL'),
'handled': True
}
file_url = args[0]
caption = "这是一个文件"
return {
'response': MessageBuilder.file(file_url, caption),
'handled': True
}
# 注意:文件消息目前暂不开放使用
Embed消息提供丰富的结构化内容展示,支持标题、描述、字段和缩略图:
def handle_embed_command(self, args):
# 基础Embed消息
return {
'response': MessageBuilder.embed(
title="📊 数据报告",
description="这是一个详细的数据分析报告",
color=0x00ff00 # 绿色边框
),
'handled': True
}
# 带字段的Embed消息
def handle_detailed_embed(self):
fields = [
{'name': '用户数量', 'value': '1,234'},
{'name': '活跃度', 'value': '98.5%'},
{'name': '增长率', 'value': '+15.2%'}
]
return {
'response': MessageBuilder.embed(
title="🎯 系统状态",
description="当前系统运行状态良好",
fields=fields,
color=0x0099ff, # 蓝色边框
thumbnail="https://example.com/icon.png"
),
'handled': True
}
# 多彩Embed消息
def handle_colorful_embed(self):
return {
'response': MessageBuilder.embed(
title="⚠️ 警告信息",
description="检测到异常活动,请注意检查",
color=0xff6600, # 橙色边框
thumbnail="https://example.com/warning.png"
),
'handled': True
}
QQ官方提供的模板化消息,支持多种卡片样式:
用于展示文本内容的卡片形式:
def handle_text_card(self):
return {
'response': MessageBuilder.text_card(
text="这是文卡的主要内容,可以包含多行文本和详细信息。",
description="文卡描述",
prompt="提示文字"
),
'handled': True
}
# 带链接的文卡消息
def handle_text_card_with_link(self):
return {
'response': MessageBuilder.text_card_link(
text="点击下方链接访问官网",
button_text="🔗 访问官网",
button_url="https://example.com",
description="带链接的文卡",
prompt="点击链接跳转"
),
'handled': True
}
用于分享链接和网页内容:
def handle_link_card(self):
return {
'response': MessageBuilder.link_card(
title="网页标题",
description="网页描述内容",
url="https://example.com",
cover_image="https://example.com/cover.jpg"
),
'handled': True
}
用于展示简单信息的小型卡片:
def handle_small_card(self):
return {
'response': MessageBuilder.small_card(
title="通知标题",
subtitle="通知详细内容",
preview_image="https://example.com/preview.jpg",
icon_image="https://example.com/icon.png",
url="https://example.com"
),
'handled': True
}
用于展示大尺寸图片内容:
def handle_large_image(self):
return {
'response': MessageBuilder.large_image(
title="图片标题",
subtitle="图片描述",
image_url="https://example.com/large-image.jpg"
),
'handled': True
}
支持官方申请的按钮模板消息:
def handle_button_message(self):
# 使用官方申请的按钮模板ID
template_id = "102084649_1751807812" # 你的官方按钮模板ID
return {
'response': MessageBuilder.button_card(template_id),
'handled': True
}
注意事项:
构建文本消息:
# 简单文本
message = MessageBuilder.text("Hello World!")
# 多行文本
message = MessageBuilder.text("""
第一行
第二行
第三行
""")
# 带emoji的文本
message = MessageBuilder.text("🎉 恭喜你!操作成功完成!")
构建Markdown消息:
# 基础Markdown
message = MessageBuilder.markdown("**粗体文本** 和 *斜体文本*")
# 复杂Markdown结构
markdown_text = """
# 📊 数据报告
## 📈 统计信息
- 用户数量: **1,234**
- 消息总数: **56,789**
- 活跃度: **98.5%**
## 🔗 相关链接
[官方文档](https://example.com)
> 💡 **提示**: 这是一个引用块
"""
message = MessageBuilder.markdown(markdown_text)
构建图片消息:
# 网络图片
message = MessageBuilder.image(
"https://example.com/image.jpg",
"图片描述文字"
)
# 本地图片
message = MessageBuilder.image(
"/local/path/image.png",
"本地图片说明"
)
# 只有图片,无文字说明
message = MessageBuilder.image("https://example.com/photo.jpg")
# 自动上传网络图片
message = MessageBuilder.image(
"https://example.com/image.jpg",
"网络图片",
auto_upload=True # 自动上传到QQ服务器
)
构建视频消息:
# 网络视频
message = MessageBuilder.video(
"https://example.com/video.mp4",
"视频描述文字"
)
# 本地视频
message = MessageBuilder.video(
"/local/path/video.mp4",
"本地视频说明",
auto_upload=True
)
# 只有视频,无文字说明
message = MessageBuilder.video("https://example.com/video.mp4")
构建语音消息:
# 网络语音
message = MessageBuilder.voice(
"https://example.com/voice.silk",
"语音描述文字"
)
# 本地语音
message = MessageBuilder.voice(
"/local/path/voice.silk",
"本地语音说明",
auto_upload=True
)
# 只有语音,无文字说明
message = MessageBuilder.voice("https://example.com/voice.silk")
构建文件消息(注意:文件类型暂不开放):
# 网络文件
message = MessageBuilder.file(
"https://example.com/document.pdf",
"文件描述文字"
)
# 本地文件
message = MessageBuilder.file(
"/local/path/document.pdf",
"本地文件说明",
auto_upload=True
)
# 注意:根据QQ官方文档,文件类型暂不开放使用
构建Embed消息:
# 基础Embed
message = MessageBuilder.embed(
title="数据报告",
description="详细的数据分析",
color=0x00ff00 # 绿色
)
# 带字段的Embed
fields = [
{'name': '用户数', 'value': '1,234'},
{'name': '活跃度', 'value': '98.5%'}
]
message = MessageBuilder.embed(
title="系统状态",
description="运行状态良好",
fields=fields,
color=0x0099ff, # 蓝色
thumbnail="https://example.com/icon.png"
)
构建文卡消息(Ark模板23):
# 基础文卡
message = MessageBuilder.text_card(
text="这是文卡的主要内容",
description="文卡描述",
prompt="提示文字"
)
# 带链接的文卡
message = MessageBuilder.text_card(
text="点击查看详情",
description="重要通知",
prompt="点击链接了解更多",
link="https://example.com"
)
构建带链接按钮的文卡消息:
message = MessageBuilder.text_card_link(
text="欢迎使用我们的服务!",
button_text="🔗 访问官网",
button_url="https://example.com",
description="服务介绍",
prompt="点击按钮访问"
)
构建链接卡片消息(Ark模板24):
message = MessageBuilder.link_card(
title="精彩文章推荐",
description="这是一篇关于技术发展的深度文章",
url="https://example.com/article",
cover_image="https://example.com/cover.jpg"
)
构建小卡片消息(Ark模板34):
message = MessageBuilder.small_card(
title="系统通知",
subtitle="您有新的消息待处理",
preview_image="https://example.com/preview.jpg",
icon_image="https://example.com/icon.png",
url="https://example.com/notifications"
)
构建大图消息(Ark模板37):
message = MessageBuilder.large_image(
title="精美图片",
subtitle="高清壁纸推荐",
image_url="https://example.com/wallpaper.jpg",
prompt="点击查看大图"
)
构建官方申请的按钮模板消息:
# 使用官方申请的按钮模板
message = MessageBuilder.button_card("102084649_1751807812")
# 实际生成的消息结构
{
'msg_type': 2,
'keyboard': {
'id': '102084649_1751807812'
}
}
使用说明:
template_id: 向QQ官方申请的按钮模板ID数字_数字(如:102084649_1751807812)def handle_command_with_validation(self, args):
try:
if not args:
return {
'response': MessageBuilder.text('❌ 缺少必要参数'),
'handled': True
}
# 处理逻辑...
result = self.process_command(args)
return {
'response': MessageBuilder.text(f'✅ 处理成功: {result}'),
'handled': True
}
except Exception as e:
return {
'response': MessageBuilder.text(f'❌ 处理失败: {str(e)}'),
'handled': True
}
def handle_dynamic_response(self, message_data):
content = message_data.get('content', '')
if '/help' in content:
# 帮助信息用Markdown格式
help_text = self.generate_help_markdown()
return {
'response': MessageBuilder.markdown(help_text),
'handled': True
}
elif '/image' in content:
# 图片命令
image_url = self.get_random_image()
return {
'response': MessageBuilder.image(image_url, "随机图片"),
'handled': True
}
else:
# 普通回复用文本
return {
'response': MessageBuilder.text("收到你的消息了!"),
'handled': True
}
插件可以注册命令信息,用于展示和帮助系统:
# 注册命令信息
self.register_command_info(command, description, usage)
# 参数说明:
# command: 命令名称(不含/前缀)
# description: 命令描述
# usage: 使用方法(可选)
# 示例
self.register_command_info('echo', '重复发送的内容', '/echo <内容>')
self.register_command_info('help', '显示帮助信息', '/help [命令名]')
# 获取插件的所有命令信息
commands = self.get_commands_info()
# 返回格式:
{
'echo': {
'description': '重复发送的内容',
'usage': '/echo <内容>'
},
'help': {
'description': '显示帮助信息',
'usage': '/help [命令名]'
}
}
message_received Hookhandle_message_hook方法被调用# 处理了消息,返回响应
return {
'response': '回复内容',
'handled': True
}
# 没有处理消息,让其他插件处理
return {'handled': False}
# 处理了消息,但不回复
return {'handled': True}
插件可以使用内置的撤回功能来撤回已发送的消息:
# 撤回指定消息ID的消息
success = self.recall(message_id, bot_id)
# 参数说明:
# message_id: 要撤回的消息ID(字符串)
# bot_id: 机器人ID(整数)
# 返回值: bool - True表示撤回成功,False表示撤回失败
# 示例用法
class Plugin(BasePlugin):
def __init__(self):
super().__init__()
self.sent_messages = {} # 插件自己管理消息ID记录
def handle_message_hook(self, message_data, user_id, bot_id):
content = message_data.get('content', '').strip()
if content == '撤回':
# 撤回最后发送的消息
if user_id in self.sent_messages:
message_id = self.sent_messages[user_id]
success = self.recall(message_id, bot_id)
if success:
return {'handled': True} # 撤回成功,不发送确认消息
else:
return {
'response': MessageBuilder.text("❌ 撤回失败,可能超过时间限制"),
'handled': True
}
return {'handled': True}
elif content.startswith('撤回 '):
# 撤回指定消息ID
message_id = content[3:].strip()
success = self.recall(message_id, bot_id)
return {'handled': True}
# 发送消息时记录消息ID(需要从发送结果中获取)
response = MessageBuilder.text("这是一条消息")
# 注意:需要在消息发送后获取message_id并记录
# self.sent_messages[user_id] = returned_message_id
return {'response': response, 'handled': True}
撤回功能注意事项:
推荐的消息ID管理模式:
class Plugin(BasePlugin):
def __init__(self):
super().__init__()
self.user_messages = {} # {user_id: [message_ids]}
def track_message(self, user_id, message_id):
"""记录发送的消息ID"""
if user_id not in self.user_messages:
self.user_messages[user_id] = []
self.user_messages[user_id].append(message_id)
# 只保留最近5条消息记录
if len(self.user_messages[user_id]) > 5:
self.user_messages[user_id] = self.user_messages[user_id][-5:]
def get_last_message_id(self, user_id):
"""获取用户的最后一条消息ID"""
if user_id in self.user_messages and self.user_messages[user_id]:
return self.user_messages[user_id][-1]
return None
def recall_last_message(self, user_id, bot_id):
"""撤回用户的最后一条消息"""
message_id = self.get_last_message_id(user_id)
if message_id:
success = self.recall(message_id, bot_id)
if success:
# 从记录中移除已撤回的消息
self.user_messages[user_id].remove(message_id)
return success
return False
"""
多消息回复示例插件 - 展示新的多消息Hook功能
"""
from Core.plugin.base import BasePlugin
from Core.logging.file_logger import log_info
from Core.message.builder import MessageBuilder
class Plugin(BasePlugin):
"""多消息回复示例插件"""
def __init__(self):
super().__init__()
self.name = "MultiMessagePlugin"
self.version = "1.0.0"
self.description = "展示多消息回复功能的示例插件"
self.author = "Plugin Developer"
# 注册命令信息
self.register_command_info('report', '生成多格式报告', '/report [类型]')
self.register_command_info('tutorial', '显示教程', '/tutorial')
self.register_command_info('status', '系统状态检查', '/status')
self.hooks = {
'message_received': [self.handle_message_hook]
}
def handle_message_hook(self, message_data, bot_id=None):
"""处理消息Hook - 展示多消息功能"""
try:
content = message_data.get('content', '').strip()
if content.startswith('/'):
return self._handle_command(content, bot_id)
return {'handled': False}
except Exception as e:
log_info(bot_id or 0, f"{self.name}插件处理消息异常: {e}", "PLUGIN_ERROR")
return {'handled': False}
def _handle_command(self, content, bot_id=None):
"""处理命令"""
parts = content[1:].split()
if not parts:
return {'handled': False}
command = parts[0].lower()
args = parts[1:] if len(parts) > 1 else []
if command == 'report':
return self.handle_report_command(args)
elif command == 'tutorial':
return self.handle_tutorial_command()
elif command == 'status':
return self.handle_status_command()
return {'handled': False}
def handle_report_command(self, args):
"""生成多格式报告 - 多消息回复示例"""
report_type = args[0] if args else 'basic'
# 🆕 返回多条不同类型的消息
return {
'response': [
# 1. 文本消息 - 报告标题
MessageBuilder.text(f'📊 正在生成 {report_type} 报告...'),
# 2. 文卡消息 - 报告内容
MessageBuilder.text_card(
text=f'报告类型:{report_type}\n生成时间:2025-07-06 01:00:00\n数据来源:系统数据库\n\n主要指标:\n• 用户活跃度:95.2%\n• 系统性能:优秀\n• 错误率:0.1%',
description=f'{report_type.title()} 数据报告',
prompt='详细报告数据'
),
# 3. Markdown消息 - 详细分析
MessageBuilder.markdown(f'''
# 📈 {report_type.title()} 报告分析
## 🔍 关键发现
- **性能表现**: 系统运行稳定
- **用户反馈**: 满意度较高
- **改进建议**: 继续优化响应速度
## 📋 数据摘要
| 指标 | 数值 | 状态 |
|------|------|------|
| 响应时间 | 120ms | ✅ 优秀 |
| 成功率 | 99.9% | ✅ 优秀 |
| 用户满意度 | 4.8/5 | ✅ 优秀 |
> 💡 **建议**: 继续保持当前优化策略
'''),
# 4. 图片消息 - 图表展示
MessageBuilder.image(
'https://via.placeholder.com/600x400/4CAF50/white?text=Report+Chart',
'📊 报告图表'
),
# 5. 文本消息 - 完成提示
MessageBuilder.text('✅ 报告生成完成!如需更多详细信息,请联系管理员。')
],
'handled': True
}
def handle_tutorial_command(self):
"""显示教程 - 多消息教学示例"""
return {
'response': [
# 欢迎消息
MessageBuilder.text('🎓 欢迎来到多消息插件教程!'),
# 教程步骤1
MessageBuilder.text_card(
text='步骤1:了解多消息功能\n\n多消息功能允许插件在一次回复中发送多条不同类型的消息,包括文本、图片、卡片等。',
description='教程 - 第1步',
prompt='多消息基础概念'
),
# 教程步骤2
MessageBuilder.text_card(
text='步骤2:使用方法\n\n在Hook返回值中,将response设置为MessageBuilder对象的数组即可实现多消息发送。',
description='教程 - 第2步',
prompt='实现方法说明'
),
# 代码示例
MessageBuilder.markdown('''
# 💻 代码示例
```python
return {
'response': [
MessageBuilder.text('第一条消息'),
MessageBuilder.text_card('第二条卡片消息'),
MessageBuilder.image('图片URL', '第三条图片')
],
'handled': True
}
支持的消息类型:
# 结束消息
MessageBuilder.text('🎉 教程完成!现在你可以开始使用多消息功能了。')
],
'handled': True
}
def handle_status_command(self): """系统状态检查 - 实用多消息示例""" import datetime import random
# 模拟系统数据
cpu_usage = random.randint(10, 80)
memory_usage = random.randint(30, 90)
disk_usage = random.randint(20, 70)
return {
'response': [
# 状态标题
MessageBuilder.text('🔍 系统状态检查中...'),
# 系统概览卡片
MessageBuilder.text_card(
text=f'系统运行时间:72小时\n最后检查:{datetime.datetime.now().strftime("%H:%M:%S")}\n状态:运行正常',
description='系统概览',
prompt='基本系统信息'
),
# 详细状态报告
MessageBuilder.markdown(f'''
# 建议和警告
MessageBuilder.text_card(
text='💡 系统建议:\n\n• 定期清理临时文件\n• 监控内存使用情况\n• 保持系统更新\n• 定期备份重要数据',
description='维护建议',
prompt='系统优化建议'
),
# 完成消息
MessageBuilder.text('✅ 系统状态检查完成!所有服务运行正常。')
],
'handled': True
}
### 命令处理插件
```python
from Core.message.builder import MessageBuilder
class Plugin(BasePlugin):
def __init__(self):
super().__init__()
self.name = "CommandPlugin"
self.version = "1.0.0"
self.description = "命令处理示例插件"
# 注册命令信息(用于展示和帮助)
self.register_command_info('weather', '查询天气', '/weather [城市]')
self.register_command_info('time', '获取时间', '/time')
self.register_command_info('joke', '讲个笑话', '/joke')
# 命令处理器
self.command_handlers = {
'weather': self.handle_weather,
'time': self.handle_time,
'joke': self.handle_joke
}
self.hooks = {
'message_received': [self.handle_message_hook]
}
def handle_message_hook(self, message_data, bot_id=None):
"""处理消息Hook"""
content = message_data.get('content', '').strip()
# 检查是否是命令
if content.startswith('/'):
return self._handle_command(content, bot_id)
return {'handled': False}
def _handle_command(self, content, bot_id=None):
"""处理命令"""
# 解析命令
parts = content[1:].split()
if not parts:
return {'handled': False}
command = parts[0].lower()
args = parts[1:] if len(parts) > 1 else []
# 检查是否支持该命令
if command in self.command_handlers:
try:
handler = self.command_handlers[command]
response = handler(args)
return {
'response': response,
'handled': True
}
except Exception as e:
return {
'response': f"命令执行出错: {str(e)}",
'handled': True
}
return {'handled': False}
def handle_weather(self, args):
"""处理天气命令"""
city = args[0] if args else "北京"
return MessageBuilder.text(f"🌤️ {city}今天天气晴朗,温度25°C")
def handle_time(self, args):
"""处理时间命令"""
from datetime import datetime
current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
return MessageBuilder.text(f"🕐 当前时间:{current_time}")
def handle_joke(self, args):
"""处理笑话命令"""
jokes = [
"为什么程序员喜欢黑暗?因为光明会产生bug!",
"程序员的三大美德:懒惰、急躁和傲慢。",
"世界上有10种人:懂二进制的和不懂二进制的。"
]
import random
return MessageBuilder.text(f"😄 {random.choice(jokes)}")
class Plugin(BasePlugin):
def __init__(self):
super().__init__()
self.name = "AdvancedPlugin"
self.version = "1.0.0"
self.description = "完整功能示例插件"
self.author = "Plugin Developer"
# 注册命令信息
self.register_command_info('greet', '智能问候', '/greet [名字]')
self.register_command_info('calc', '简单计算', '/calc <表达式>')
self.register_command_info('status', '插件状态', '/status')
# 命令处理器
self.command_handlers = {
'greet': self.handle_greet,
'calc': self.handle_calc,
'status': self.handle_status
}
self.hooks = {
'message_received': [self.handle_message_hook],
'after_bot_start': [self.on_after_bot_start_hook],
'after_bot_stop': [self.on_after_bot_stop_hook]
}
log_info(0, f"{self.name}插件初始化完成", "PLUGIN_INIT", plugin=self.name)
def handle_message_hook(self, message_data, bot_id=None):
"""处理消息Hook"""
try:
content = message_data.get('content', '').strip()
# 处理命令
if content.startswith('/'):
return self._handle_command(content, bot_id)
# 处理自然语言
return self._handle_natural_language(content, bot_id)
except Exception as e:
log_info(bot_id or 0, f"{self.name}插件处理消息异常: {e}", "PLUGIN_ERROR")
return {'handled': False}
def _handle_command(self, content, bot_id=None):
"""处理命令"""
parts = content[1:].split()
if not parts:
return {'handled': False}
command = parts[0].lower()
args = parts[1:] if len(parts) > 1 else []
if command in self.command_handlers:
try:
handler = self.command_handlers[command]
response = handler(args)
return {
'response': response,
'handled': True
}
except Exception as e:
return {
'response': f"命令执行出错: {str(e)}",
'handled': True
}
return {'handled': False}
def _handle_natural_language(self, content, bot_id=None):
"""处理自然语言"""
content_lower = content.lower()
# 问候语处理
if any(word in content_lower for word in ['你好', 'hello', 'hi']):
return {
'response': MessageBuilder.text(f'你好!我是{self.name}插件 👋\n发送 /help 查看可用命令'),
'handled': True
}
return {'handled': False}
def handle_greet(self, args):
"""处理问候命令"""
name = args[0] if args else "朋友"
return MessageBuilder.text(f"你好,{name}!很高兴见到你!😊")
def handle_calc(self, args):
"""处理计算命令"""
if not args:
return MessageBuilder.text("请输入要计算的表达式,例如:/calc 1+2*3")
try:
expression = " ".join(args)
# 简单的安全计算(仅支持基本运算)
allowed_chars = set('0123456789+-*/().')
if not all(c in allowed_chars or c.isspace() for c in expression):
return MessageBuilder.text("❌ 只支持基本数学运算符")
result = eval(expression)
return MessageBuilder.text(f"📊 {expression} = {result}")
except Exception as e:
return MessageBuilder.text(f"❌ 计算错误: {str(e)}")
def handle_status(self, args):
"""处理状态命令"""
commands = self.get_commands_info()
status_text = f"🔌 {self.name} 状态信息\n\n"
status_text += f"版本: {self.version}\n"
status_text += f"作者: {self.author}\n"
status_text += f"支持命令: {len(commands)}个\n\n"
status_text += "可用命令:\n"
for cmd, info in commands.items():
status_text += f"• /{cmd} - {info['description']}\n"
return MessageBuilder.text(status_text)
def on_after_bot_start_hook(self, bot_id, bot_config):
"""机器人启动后Hook"""
return {'message': f'{self.name}插件已为机器人 {bot_id} 准备就绪'}
def on_after_bot_stop_hook(self, bot_id):
"""机器人停止后Hook"""
return {'message': f'{self.name}插件已为机器人 {bot_id} 清理资源'}
from Core.message.builder import MessageBuilder
class Plugin(BasePlugin):
def __init__(self):
super().__init__()
self.name = "NLPPlugin"
self.version = "1.0.0"
self.description = "自然语言处理插件"
self.hooks = {
'message_received': [self.handle_message_hook]
}
def handle_message_hook(self, message_data, bot_id=None):
"""处理自然语言消息"""
content = message_data.get('content', '').strip().lower()
# 情感分析
if any(word in content for word in ['开心', '高兴', '快乐']):
return {
'response': MessageBuilder.text('😊 看起来你心情不错呢!'),
'handled': True
}
if any(word in content for word in ['难过', '伤心', '沮丧']):
return {
'response': MessageBuilder.text('😔 别难过,一切都会好起来的!'),
'handled': True
}
# 问候语处理
if any(word in content for word in ['早上好', '晚上好', '下午好']):
return {
'response': MessageBuilder.text('你好!祝你今天过得愉快!'),
'handled': True
}
return {'handled': False}
import json
import os
from Core.message.builder import MessageBuilder
class Plugin(BasePlugin):
def __init__(self):
super().__init__()
self.name = "DataPlugin"
self.version = "1.0.0"
self.description = "数据存储示例插件"
# 数据文件路径
self.data_file = os.path.join('Plugins', 'data_plugin', 'data.json')
self.ensure_data_file()
self.hooks = {
'message_received': [self.handle_message_hook]
}
def ensure_data_file(self):
"""确保数据文件存在"""
os.makedirs(os.path.dirname(self.data_file), exist_ok=True)
if not os.path.exists(self.data_file):
with open(self.data_file, 'w', encoding='utf-8') as f:
json.dump({}, f)
def load_data(self):
"""加载数据"""
try:
with open(self.data_file, 'r', encoding='utf-8') as f:
return json.load(f)
except:
return {}
def save_data(self, data):
"""保存数据"""
try:
with open(self.data_file, 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
return True
except:
return False
def handle_message_hook(self, message_data, bot_id=None):
"""处理消息"""
content = message_data.get('content', '').strip()
user_id = self._get_user_id(message_data)
if content.startswith('/save '):
# 保存数据
text = content[6:]
data = self.load_data()
if user_id not in data:
data[user_id] = []
data[user_id].append(text)
if self.save_data(data):
return {
'response': MessageBuilder.text(f'✅ 已保存:{text}'),
'handled': True
}
else:
return {
'response': MessageBuilder.text('❌ 保存失败'),
'handled': True
}
elif content == '/list':
# 列出数据
data = self.load_data()
user_data = data.get(user_id, [])
if user_data:
response = "📋 你的保存记录:\n" + "\n".join(f"{i + 1}. {item}" for i, item in enumerate(user_data))
else:
response = "📋 你还没有保存任何记录"
return {
'response': MessageBuilder.text(response),
'handled': True
}
return {'handled': False}
def _get_user_id(self, message_data):
"""获取用户ID"""
if 'author' in message_data:
author = message_data['author']
if isinstance(author, dict):
return author.get('user_openid') or author.get('id') or 'unknown'
return 'unknown'
插件系统提供了多种实用工具,帮助开发者快速构建功能丰富的插件。
系统内置了强大的HTML渲染工具,可以将HTML模板渲染为图片,让插件能够发送美观的图形化内容。
from Core.tools.browser import browser
class MyPlugin:
def __init__(self):
pass
def handle_message_hook(self, message_data, bot_id=None):
"""使用HTML渲染工具"""
content = message_data.get('content', '').strip()
if content == '用户信息':
# 准备模板数据
user_data = {
'username': '张三',
'level': 25,
'score': 1580,
'avatar': 'https://example.com/avatar.jpg'
}
# 渲染HTML模板为图片
image_base64 = browser.render(
'MyPlugin/templates/user_profile.html', # 模板路径
user_data # 模板数据
)
if image_base64:
return {
'response': MessageBuilder.image(base64_data=image_base64, caption="用户信息"),
'handled': True
}
else:
return {
'response': MessageBuilder.text("❌ 图片生成失败,请稍后重试"),
'handled': True
}
在插件目录下创建模板文件:
Plugins/
└── MyPlugin/
├── __init__.py
└── templates/
├── user_profile.html
├── ranking.html
└── statistics.html
<!-- Plugins/MyPlugin/templates/user_profile.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>用户信息</title>
<style>
body {
font-family: 'Microsoft YaHei', sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
margin: 0;
padding: 20px;
color: white;
}
.card {
background: rgba(255, 255, 255, 0.1);
border-radius: 15px;
padding: 30px;
backdrop-filter: blur(10px);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
}
.avatar {
width: 80px;
height: 80px;
border-radius: 50%;
margin: 0 auto 20px;
display: block;
}
.username {
font-size: 24px;
font-weight: bold;
text-align: center;
margin-bottom: 20px;
}
.stats {
display: flex;
justify-content: space-around;
text-align: center;
}
.stat-item {
flex: 1;
}
.stat-value {
font-size: 20px;
font-weight: bold;
color: #ffd700;
}
.stat-label {
font-size: 14px;
opacity: 0.8;
margin-top: 5px;
}
</style>
</head>
<body>
<div class="card">
<img src="{{ avatar }}" alt="头像" class="avatar">
<div class="username">{{ username }}</div>
<div class="stats">
<div class="stat-item">
<div class="stat-value">{{ level }}</div>
<div class="stat-label">等级</div>
</div>
<div class="stat-item">
<div class="stat-value">{{ score }}</div>
<div class="stat-label">积分</div>
</div>
</div>
</div>
</body>
</html>
1. 自适应尺寸
# 自适应宽度和高度(推荐)
image_base64 = browser.render('MyPlugin/templates/card.html', data)
# 指定宽度,高度自适应
image_base64 = browser.render('MyPlugin/templates/card.html', data, width=800)
# 指定宽度和高度
image_base64 = browser.render('MyPlugin/templates/card.html', data, width=800, height=600)
2. 复杂数据渲染
def generate_ranking_image(self, ranking_data):
"""生成排行榜图片"""
template_data = {
'title': '用户排行榜',
'update_time': datetime.now().strftime('%Y-%m-%d %H:%M'),
'rankings': [
{'rank': 1, 'name': '张三', 'score': 2580},
{'rank': 2, 'name': '李四', 'score': 2340},
{'rank': 3, 'name': '王五', 'score': 2120},
]
}
return browser.render('MyPlugin/templates/ranking.html', template_data)
3. 条件渲染
<!-- 在模板中使用条件渲染 -->
<div class="user-info">
<h2>{{ username }}</h2>
{% if is_vip %}
<span class="vip-badge">VIP</span>
{% endif %}
{% if achievements %}
<div class="achievements">
<h3>成就列表</h3>
{% for achievement in achievements %}
<div class="achievement">{{ achievement.name }}</div>
{% endfor %}
</div>
{% else %}
<p>暂无成就</p>
{% endif %}
</div>
1. 错误处理
def render_with_fallback(self, template_name, data):
"""带回退的渲染方法"""
try:
image_base64 = browser.render(f'MyPlugin/templates/{template_name}', data)
if image_base64:
return MessageBuilder.image(base64_data=image_base64, caption="查询结果")
else:
# 图片生成失败,返回错误消息
return MessageBuilder.text("❌ 图片生成失败,请稍后重试")
except Exception as e:
# 异常处理
return MessageBuilder.text("❌ 图片生成失败,请稍后重试")
2. 模板复用
class MyPlugin:
def render_user_card(self, user_data, template_type='default'):
"""可复用的用户卡片渲染"""
templates = {
'default': 'user_card.html',
'detailed': 'user_detailed.html',
'simple': 'user_simple.html'
}
template_name = templates.get(template_type, 'user_card.html')
return browser.render(f'MyPlugin/templates/{template_name}', user_data)
3. 性能优化
def handle_batch_render(self, users_data):
"""批量渲染优化"""
results = []
for user_data in users_data:
# 避免在循环中进行复杂计算
processed_data = self.preprocess_user_data(user_data)
image_base64 = browser.render('MyPlugin/templates/user_card.html', processed_data)
if image_base64:
results.append(MessageBuilder.image(base64_data=image_base64))
return {
'response': results,
'handled': True
}
# ✅ 适合使用多消息的场景
def handle_complex_query(self):
return {
'response': [
MessageBuilder.text('🔍 查询中...'), # 1. 状态提示
MessageBuilder.text_card('查询结果...'), # 2. 主要内容
MessageBuilder.markdown('## 详细分析...'), # 3. 补充信息
MessageBuilder.text('✅ 查询完成') # 4. 完成提示
],
'handled': True
}
# ❌ 不建议的使用方式
def handle_simple_hello(self):
return {
'response': [
MessageBuilder.text('你好'),
MessageBuilder.text('!'),
MessageBuilder.text('欢迎使用')
], # 过度拆分简单消息
'handled': True
}
# ✅ 好的组织方式:逻辑清晰,层次分明
def handle_report_command(self):
return {
'response': [
# 1. 开始提示
MessageBuilder.text('📊 生成报告中...'),
# 2. 主要内容(使用合适的消息类型)
MessageBuilder.text_card(
text='报告数据...',
description='数据报告',
prompt='详细信息'
),
# 3. 补充信息(如果需要)
MessageBuilder.markdown('## 分析结果\n...'),
# 4. 结束提示
MessageBuilder.text('✅ 报告生成完成')
],
'handled': True
}
# 根据内容选择合适的消息类型
def choose_message_type_example(self):
return {
'response': [
# 简单文本 → text()
MessageBuilder.text('操作开始'),
# 结构化数据 → text_card()
MessageBuilder.text_card(
text='用户信息\n姓名:张三\n等级:VIP',
description='用户资料',
prompt='详细信息'
),
# 复杂格式 → markdown()
MessageBuilder.markdown('''
# 数据分析
## 统计结果
- 项目1: 85%
- 项目2: 92%
'''),
# 图片展示 → image()
MessageBuilder.image('chart.jpg', '统计图表'),
# 状态提示 → text()
MessageBuilder.text('✅ 分析完成')
],
'handled': True
}
# ✅ 控制消息数量
def optimized_multi_message(self):
# 建议:单次回复不超过5条消息
return {
'response': [
MessageBuilder.text('开始处理'),
MessageBuilder.text_card('主要内容'),
MessageBuilder.text('处理完成')
], # 3条消息,合理数量
'handled': True
}
# ❌ 避免过多消息
def too_many_messages(self):
return {
'response': [
MessageBuilder.text(f'消息{i}') for i in range(20)
], # 20条消息,可能影响用户体验
'handled': True
}
# ✅ 好的做法
def handle_message_hook(self, message_data, bot_id=None):
"""处理消息Hook"""
try:
content = message_data.get('content', '').strip()
if not content:
return {'handled': False}
# 具体处理逻辑
if content == '/hello':
return {
'response': MessageBuilder.text('Hello World!'),
'handled': True
}
return {'handled': False}
except Exception as e:
log_info(bot_id or 0, f"插件处理异常: {e}", "PLUGIN_ERROR")
return {'handled': False}
# ❌ 避免的做法
def handle_message_hook(self, message_data, bot_id=None):
content = message_data['content'] # 可能KeyError
# 没有异常处理
return content.upper() # 错误的返回格式
from Core.logging.file_logger import log_info, log_error, log_warn
# 记录重要操作
log_info(bot_id, "插件执行成功", "PLUGIN_SUCCESS", operation="command_execute")
# 记录错误
log_error(bot_id, f"插件执行失败: {e}", "PLUGIN_ERROR", error=str(e))
# 记录警告
log_warn(bot_id, "插件配置缺失", "PLUGIN_CONFIG_MISSING")
# config.py
DEFAULT_CONFIG = {
'api_key': '',
'max_requests': 100,
'timeout': 30
}
# __init__.py
import os
from .config import DEFAULT_CONFIG
class Plugin(BasePlugin):
def __init__(self):
super().__init__()
self.config = self.load_config()
def load_config(self):
"""加载配置"""
config = DEFAULT_CONFIG.copy()
# 从环境变量加载
config['api_key'] = os.getenv('MY_PLUGIN_API_KEY', config['api_key'])
return config
插件日志记录在 logs/system/ 目录下:
# 查看今天的系统日志
tail -f logs/system/$(date +%Y-%m-%d).log
# 搜索特定插件的日志
grep "MyPlugin" logs/system/$(date +%Y-%m-%d).log
# 添加调试日志
log_info(bot_id, f"调试信息: {variable}", "DEBUG", data=variable)
# 检查消息数据结构
def handle_message_hook(self, message_data, bot_id=None):
log_info(bot_id, f"收到消息数据: {message_data}", "DEBUG")
# ... 处理逻辑
通过管理界面查看插件状态:
/admin/plugins 查看插件列表我们的混合架构结合了Hook系统的灵活性和传统命令系统的可发现性:
/commands查看所有可用命令register_command_info()用于注册命令信息| 特性 | 传统命令系统 | Hook驱动 | Hook + 命令发现 |
|---|---|---|---|
| 命令处理 | ✅ 统一处理 | ❌ 需要自己解析 | ✅ 自己解析 + 信息注册 |
| 自然语言 | ❌ 不支持 | ✅ 完全支持 | ✅ 完全支持 |
| 命令发现 | ✅ 自动收集 | ❌ 无法发现 | ✅ 主动注册 |
| 灵活性 | ❌ 受限 | ✅ 完全灵活 | ✅ 完全灵活 |
| 管理界面 | ✅ 显示命令 | ❌ 无法显示 | ✅ 显示命令 |
A: 检查以下几点:
__init__.py 文件是否存在Plugin 类是否正确定义A: 确认:
message_received)self.hooksA: 示例代码:
import asyncio
import aiohttp
def handle_message_hook(self, message_data, bot_id=None):
"""处理消息Hook"""
content = message_data.get('content', '').strip()
if content.startswith('/weather'):
# 创建新的事件循环处理异步操作
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
try:
result = loop.run_until_complete(self.get_weather_async())
return {
'response': MessageBuilder.text(result),
'handled': True
}
finally:
loop.close()
return {'handled': False}
async def get_weather_async(self):
"""异步获取天气"""
async with aiohttp.ClientSession() as session:
async with session.get('http://api.weather.com/...') as resp:
data = await resp.json()
return f"天气:{data['weather']}"
A: 使用register_command_info()注册命令信息:
def __init__(self):
super().__init__()
# 注册命令信息
self.register_command_info('mycommand', '我的命令描述', '/mycommand <参数>')
# 在Hook中处理命令
def handle_message_hook(self, message_data, bot_id=None):
content = message_data.get('content', '')
if content.startswith('/mycommand'):
# 处理命令逻辑
return {'response': MessageBuilder.text('命令执行结果'), 'handled': True}
return {'handled': False}
A: 通过Hook系统的自定义事件:
# 插件A:触发自定义事件
class PluginA(BasePlugin):
def trigger_custom_event(self, data):
from Core.bot.manager import get_bot_manager
bot_manager = get_bot_manager()
bot_manager.plugin_manager.trigger_hook('custom_event', data)
# 插件B:监听自定义事件
class PluginB(BasePlugin):
def __init__(self):
super().__init__()
self.hooks = {
'custom_event': [self.handle_custom_event]
}
def handle_custom_event(self, data):
# 处理来自其他插件的数据
return {'message': f'收到数据: {data}'}
A:
A: 在Hook返回值中使用消息数组:
def handle_message_hook(self, message_data, bot_id=None):
if message_data.get('content') == '/multi':
return {
'response': [
MessageBuilder.text('第一条消息'),
MessageBuilder.text_card('第二条卡片消息'),
MessageBuilder.text('第三条消息')
],
'handled': True
}
A: 技术上没有硬性限制,但建议:
A: 完全可以!这正是多消息功能的优势:
return {
'response': [
MessageBuilder.text('📊 数据查询结果'), # 文本
MessageBuilder.text_card('详细数据...'), # 卡片
MessageBuilder.markdown('## 分析\n...'), # Markdown
MessageBuilder.image('chart.jpg', '图表'), # 图片
MessageBuilder.text('✅ 查询完成') # 文本
],
'handled': True
}
A: 完全兼容!现有插件无需修改:
# 旧方式(仍然支持)
return {
'response': MessageBuilder.text('单条消息'),
'handled': True
}
# 新方式(多消息)
return {
'response': [
MessageBuilder.text('第一条'),
MessageBuilder.text('第二条')
],
'handled': True
}
A: 使用系统内置的HTML渲染工具:
from Core.tools.browser import browser
# 渲染HTML模板为图片
image_base64 = browser.render('MyPlugin/templates/card.html', data)
if image_base64:
return MessageBuilder.image(base64_data=image_base64, caption="渲染结果")
else:
return MessageBuilder.text("❌ 图片生成失败,请稍后重试")
A: 在插件目录下创建 templates 文件夹:
Plugins/
└── MyPlugin/
├── __init__.py
└── templates/
├── user_card.html
├── ranking.html
└── statistics.html
A: 支持完整的现代Web技术:
A: 几种调试方法:
# 1. 检查模板路径
template_path = 'MyPlugin/templates/card.html'
print(f"模板路径: {template_path}")
# 2. 检查数据
print(f"模板数据: {data}")
# 3. 错误处理
try:
image_base64 = browser.render(template_path, data)
if not image_base64:
print("渲染返回空结果")
except Exception as e:
print(f"渲染异常: {e}")
🎉 开始创建你的第一个插件吧! 如有问题,请查看现有插件代码或联系开发团队。