插件开发指南

目录

返回文档列表

插件开发指南

🔌 QQ机器人插件开发文档

欢迎使用QQ机器人插件系统!本文档将指导您开发功能强大的机器人插件。

🆕 新功能亮点

🎉 多消息回复支持

现在插件可以在一次回复中发送多条不同类型的消息!

# 🆕 新功能:一次发送多条消息
return {
    'response': [
        MessageBuilder.text('📊 查询结果'),
        MessageBuilder.text_card('详细数据...'),
        MessageBuilder.markdown('## 分析报告\n...'),
        MessageBuilder.text('✅ 查询完成')
    ],
    'handled': True
}

主要优势: - ✅ 丰富表达 - 组合不同消息类型,提供更好的用户体验 - ✅ 逻辑清晰 - 将复杂回复分解为多个有序的消息部分 - ✅ 完全兼容 - 现有插件无需修改,新旧语法并存 - ✅ 灵活组合 - 支持文本、卡片、图片、Markdown等任意组合

🔄 插件系统工作流程

graph TD A[系统启动] --> B[扫描Plugins目录] B --> C[发现插件目录] C --> D[加载插件类] D --> E[注册Hook事件] E --> F[机器人启动] F --> G[触发before_bot_start Hook] G --> H[机器人初始化完成] H --> I[触发after_bot_start Hook] I --> J[等待消息] J --> K[收到消息] K --> L[触发message_received Hook] L --> M{插件处理消息?} M -->|是| N[返回回复内容] M -->|否| O[继续下一个插件] O --> P{还有插件?} P -->|是| L P -->|否| Q[消息处理完成] N --> R[发送回复] R --> J Q --> J S[机器人停止] --> T[触发before_bot_stop Hook] T --> U[清理资源] U --> V[触发after_bot_stop Hook] V --> W[系统关闭] style A fill:#e1f5fe style F fill:#f3e5f5 style K fill:#fff3e0 style N fill:#e8f5e8 style S fill:#ffebee

🏗️ 插件架构图

graph LR subgraph "插件系统" PM[插件管理器] HS[Hook系统] PL[插件加载器] end subgraph "插件实例" P1[Echo插件] P2[Help插件] P3[自定义插件] end subgraph "机器人核心" BM[机器人管理器] MH[消息处理器] QQ[QQ API] end PM --> PL PL --> P1 PL --> P2 PL --> P3 P1 --> HS P2 --> HS P3 --> HS BM --> PM MH --> HS QQ --> MH style PM fill:#e3f2fd style HS fill:#f1f8e9 style BM fill:#fce4ec

📋 目录

🏗️ 插件架构

核心概念

我们的插件系统采用Hook驱动 + 命令发现的混合架构:

  • Hook驱动 - 所有插件功能通过Hook事件触发
  • 命令发现 - 插件可以注册命令信息,用户可以查看命令列表
  • 自主解析 - 插件内部处理命令解析和消息处理
  • 完全隔离 - 插件之间互不干扰
  • 灵活响应 - 可处理任何类型的消息和事件

插件目录结构

Plugins/
├── your_plugin/           # 插件目录
│   ├── __init__.py       # 插件主文件(必需)
│   ├── config.py         # 配置文件(可选)
│   ├── utils.py          # 工具函数(可选)
│   └── README.md         # 插件说明(推荐)
├── echo/                 # 示例插件
└── help/                 # 帮助插件

🚀 快速开始

1. 创建插件目录

mkdir Plugins/my_plugin
cd Plugins/my_plugin

2. 创建插件主文件

创建 __init__.py 文件:

"""
我的第一个插件
"""
from Core.plugin.base import BasePlugin
from Core.logging.file_logger import log_info
from Core.message.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.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} 清理完成'}

3. 重启机器人

重启机器人服务,插件将自动加载。

插件加载机制

自动发现

系统会自动扫描 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系统

可用的Hook事件

Hook事件 触发时机 参数 返回值
message_received 收到消息时 message_data, bot_id {'response': MessageBuilder对象或数组, 'handled': 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} (可选)

message_data 结构

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}

Hook返回值规范

message_received Hook

# 🆕 单条消息回复
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('📊 数据查询结果:'),
        MessageBuilder.text_card(
            text='详细数据内容...',
            description='数据卡片',
            prompt='点击查看详情'
        ),
        MessageBuilder.text('✅ 查询完成!')
    ],
    'handled': True
}

# 没有处理消息,让其他插件处理
return {'handled': False}

# 处理了消息,但不回复
return {'handled': True}

其他Hook

# 返回状态消息
return {'message': '操作完成'}

# 或者返回空
return {}

Hook系统高级特性

频率限制

Hook系统内置了频率限制机制,防止插件滥用:

  • 限制规则:每个插件每分钟最多触发10次Hook
  • 自动跳过:超过限制的Hook调用会被自动跳过
  • 日志记录:频率限制触发时会记录警告日志
# 插件开发者无需特殊处理,系统自动管理频率限制
def handle_message_hook(self, message_data, bot_id=None):
    # 正常编写处理逻辑即可
    return {'response': '处理结果', 'handled': True}

错误处理

Hook系统具有完善的错误处理机制:

  • 异常捕获:插件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 概述

MessageBuilder是QQ机器人系统的核心消息构建工具,提供了统一的消息格式化接口。它支持多种消息类型,确保插件能够发送各种格式的消息。

导入MessageBuilder

from Core.message.message_builder import MessageBuilder

支持的消息类型

1. 文本消息 (msg_type: 0)

最基础的消息类型,用于发送纯文本内容:

# 基础文本消息
def handle_message_hook(self, message_data, bot_id=None):
    if message_data.get('content') == '/hello':
        return {
            'response': MessageBuilder.text('你好!欢迎使用QQ机器人!'),
            'handled': True
        }

2. Markdown消息 (msg_type: 2)

支持Markdown格式的富文本消息:

def handle_help_command(self):
    markdown_content = """
# 🤖 机器人帮助

## 📋 可用命令

### 基础命令
- `/help` - 显示此帮助信息
- `/status` - 查看机器人状态
- `/ping` - 测试连接

### 功能命令
- `/weather [城市]` - 查询天气
- `/joke` - 随机笑话
- `/time` - 当前时间

## 💡 使用提示
发送命令时请确保格式正确,如有问题请联系管理员。
"""

    return {
        'response': MessageBuilder.markdown(markdown_content),
        'handled': True
    }

3. 富媒体消息 (msg_type: 7)

QQ官方支持多种富媒体消息类型,包括图片、视频、语音和文件。

3.1 图片消息

支持格式: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
    }
3.2 视频消息

支持格式: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
    }
3.3 语音消息

支持格式: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
    }
3.4 文件消息

支持各种文件格式(注意:根据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
    }

# 注意:文件消息目前暂不开放使用

4. Embed消息 (msg_type: 4)

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
    }

5. Ark消息 (msg_type: 3)

QQ官方提供的模板化消息,支持多种卡片样式:

5.1 文卡消息 (模板23)

用于展示文本内容的卡片形式:

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
    }
5.2 链接卡片 (模板24)

用于分享链接和网页内容:

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
    }
5.3 小卡片 (模板34)

用于展示简单信息的小型卡片:

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
    }
5.4 大图消息 (模板37)

用于展示大尺寸图片内容:

def handle_large_image(self):
    return {
        'response': MessageBuilder.large_image(
            title="图片标题",
            subtitle="图片描述",
            image_url="https://example.com/large-image.jpg"
        ),
        'handled': True
    }

6. 按钮消息 (msg_type: 2 + keyboard)

支持交互按钮的消息:

def handle_button_message(self):
    buttons = [
        {'text': '选项1', 'data': 'option_1'},
        {'text': '选项2', 'data': 'option_2'},
        {'text': '帮助', 'data': 'help'}
    ]

    return {
        'response': MessageBuilder.button_card(
            title="请选择操作",
            content="点击下方按钮进行操作",
            buttons=buttons
        ),
        'handled': True
    }

MessageBuilder API详解

text(content: str) -> Dict

构建文本消息:

# 简单文本
message = MessageBuilder.text("Hello World!")

# 多行文本
message = MessageBuilder.text("""
第一行
第二行
第三行
""")

# 带emoji的文本
message = MessageBuilder.text("🎉 恭喜你!操作成功完成!")

markdown(content: str) -> Dict

构建Markdown消息:

# 基础Markdown
message = MessageBuilder.markdown("**粗体文本** 和 *斜体文本*")

# 复杂Markdown结构
markdown_text = """
# 📊 数据报告

## 📈 统计信息
- 用户数量: **1,234**
- 消息总数: **56,789**
- 活跃度: **98.5%**

## 🔗 相关链接
[官方文档](https://example.com)

> 💡 **提示**: 这是一个引用块
"""

message = MessageBuilder.markdown(markdown_text)

image(file_info: str, content: str = "") -> Dict

构建图片消息:

# 网络图片
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服务器
)

video(video_url_or_file_info: str, caption: str = "", auto_upload: bool = True) -> Dict

构建视频消息:

# 网络视频
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")

voice(voice_url_or_file_info: str, caption: str = "", auto_upload: bool = True) -> Dict

构建语音消息:

# 网络语音
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")

file(file_url_or_file_info: str, caption: str = "", auto_upload: bool = True) -> Dict

构建文件消息(注意:文件类型暂不开放):

# 网络文件
message = MessageBuilder.file(
    "https://example.com/document.pdf",
    "文件描述文字"
)

# 本地文件
message = MessageBuilder.file(
    "/local/path/document.pdf",
    "本地文件说明",
    auto_upload=True
)

# 注意:根据QQ官方文档,文件类型暂不开放使用

embed(title: str, description: str, fields: List[Dict[str, str]] = None, color: int = 0x00ff00, thumbnail: str = None) -> Dict

构建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"
)

text_card_link(text: str, button_text: str, button_url: str, description: str = "", prompt: str = "") -> Dict

构建带链接按钮的文卡消息:

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"
)

small_card(title: str, subtitle: str, preview_image: str, icon_image: str, url: str) -> Dict

构建小卡片消息(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"
)

large_image(title: str, subtitle: str, image_url: str, prompt: str = "") -> Dict

构建大图消息(Ark模板37):

message = MessageBuilder.large_image(
    title="精美图片",
    subtitle="高清壁纸推荐",
    image_url="https://example.com/wallpaper.jpg",
    prompt="点击查看大图"
)

button_card(title: str, content: str, buttons: List[Dict[str, str]]) -> Dict

构建带按钮的卡片消息:

buttons = [
    {'text': '确认', 'data': 'confirm'},
    {'text': '取消', 'data': 'cancel'},
    {'text': '帮助', 'data': 'help'}
]

message = MessageBuilder.button_card(
    title="操作确认",
    content="请选择您要执行的操作",
    buttons=buttons
)

消息构建最佳实践

1. 错误处理

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
        }

2. 动态消息类型选择

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
        }

🔧 插件API

命令注册API

插件可以注册命令信息,用于展示和帮助系统:

# 注册命令信息
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 [命令名]'
    }
}

Hook处理流程

  1. 消息接收 → 系统触发message_received Hook
  2. 插件处理 → 插件的handle_message_hook方法被调用
  3. 命令解析 → 插件内部解析命令(如果是命令消息)
  4. 返回结果 → 插件返回处理结果

返回值规范

# 处理了消息,返回响应
return {
    'response': '回复内容',
    'handled': True
}

# 没有处理消息,让其他插件处理
return {'handled': False}

# 处理了消息,但不回复
return {'handled': True}

📚 插件示例

🆕 多消息回复插件

"""
多消息回复示例插件 - 展示新的多消息Hook功能
"""
from Core.plugin.base import BasePlugin
from Core.logging.file_logger import log_info
from Core.message.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
}

支持的消息类型: - 📝 文本消息 (msg_type: 0) - 🎨 Markdown消息 (msg_type: 2) - 🖼️ 图片消息 (msg_type: 7) - 📹 视频消息 (msg_type: 7) - 🎵 语音消息 (msg_type: 7) - 📁 文件消息 (msg_type: 7, 暂不开放) - 📊 Embed消息 (msg_type: 4) - 📋 文卡消息 (Ark模板23) - 🔗 链接卡片 (Ark模板24) - 🎯 小卡片 (Ark模板34) - 🖼️ 大图消息 (Ark模板37) - 🔘 按钮消息 (需要申请) '''),

            # 结束消息
            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'''

📊 详细状态报告

🖥️ 资源使用情况

  • CPU使用率: {cpu_usage}% {'🟢' if cpu_usage < 70 else '🟡' if cpu_usage < 90 else '🔴'}
  • 内存使用率: {memory_usage}% {'🟢' if memory_usage < 80 else '🟡' if memory_usage < 95 else '🔴'}
  • 磁盘使用率: {disk_usage}% {'🟢' if disk_usage < 80 else '🟡' if disk_usage < 95 else '🔴'}

🔧 服务状态

  • 数据库: ✅ 正常
  • 缓存服务: ✅ 正常
  • API服务: ✅ 正常
  • 消息队列: ✅ 正常

📈 性能指标

  • 平均响应时间: 120ms
  • 请求成功率: 99.8%
  • 错误率: 0.2% '''),
            # 建议和警告
            MessageBuilder.text_card(
                text='💡 系统建议:\n\n• 定期清理临时文件\n• 监控内存使用情况\n• 保持系统更新\n• 定期备份重要数据',
                description='维护建议',
                prompt='系统优化建议'
            ),
    
            # 完成消息
            MessageBuilder.text('✅ 系统状态检查完成!所有服务运行正常。')
        ],
        'handled': True
    }
    
### 命令处理插件

```python
from Core.message.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.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.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'

💡 最佳实践

1. 插件设计原则

  • 单一职责 - 每个插件专注于一个功能领域
  • 错误处理 - 始终包含异常处理,避免插件崩溃
  • 性能考虑 - 避免阻塞操作,考虑异步处理
  • 用户友好 - 提供清晰的错误信息和帮助

🆕 2. 多消息使用最佳实践

何时使用多消息

# ✅ 适合使用多消息的场景
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
    }

2. 代码规范

# ✅ 好的做法
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()  # 错误的返回格式

3. 日志记录

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")

4. 配置管理

# 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

🐛 调试指南

1. 查看日志

插件日志记录在 logs/system/ 目录下:

# 查看今天的系统日志
tail -f logs/system/$(date +%Y-%m-%d).log

# 搜索特定插件的日志
grep "MyPlugin" logs/system/$(date +%Y-%m-%d).log

2. 常见调试技巧

# 添加调试日志
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")
    # ... 处理逻辑

3. 插件状态检查

通过管理界面查看插件状态:

  • 访问 /admin/plugins 查看插件列表
  • 检查插件是否正确加载
  • 查看Hook注册情况

🌟 新架构优势

Hook驱动 + 命令发现的优势

我们的混合架构结合了Hook系统的灵活性和传统命令系统的可发现性:

1. 保持Hook的灵活性

  • 任意消息处理 - 可以处理命令、自然语言、表情等任何消息
  • 自定义解析 - 插件可以实现自己的消息解析逻辑
  • 完全隔离 - 插件之间不会相互干扰

2. 提供命令发现功能

  • 用户友好 - 用户可以通过/commands查看所有可用命令
  • 管理便利 - 后台管理界面可以显示插件支持的命令
  • 帮助系统 - 自动生成命令帮助信息

3. 开发体验优化

  • 清晰的API - register_command_info()用于注册命令信息
  • 统一的处理 - 所有消息都通过Hook处理
  • 向后兼容 - 现有插件可以轻松升级

与传统命令系统的对比

特性 传统命令系统 Hook驱动 Hook + 命令发现
命令处理 ✅ 统一处理 ❌ 需要自己解析 ✅ 自己解析 + 信息注册
自然语言 ❌ 不支持 ✅ 完全支持 ✅ 完全支持
命令发现 ✅ 自动收集 ❌ 无法发现 ✅ 主动注册
灵活性 ❌ 受限 ✅ 完全灵活 ✅ 完全灵活
管理界面 ✅ 显示命令 ❌ 无法显示 ✅ 显示命令

❓ 常见问题

Q: 插件没有被加载?

A: 检查以下几点:

  1. 插件目录名是否正确
  2. __init__.py 文件是否存在
  3. Plugin 类是否正确定义
  4. 查看系统日志中的错误信息

Q: Hook没有被触发?

A: 确认:

  1. Hook事件名称是否正确(message_received
  2. Hook方法是否正确注册到 self.hooks
  3. 方法签名是否正确

Q: 如何处理异步操作?

A: 示例代码:

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']}"

Q: 如何让我的命令出现在命令列表中?

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}

Q: 如何在插件间通信?

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}'}

Q: 命令信息和实际处理有什么区别?

A:

  • 命令信息注册 - 仅用于展示和帮助,让用户知道有哪些命令可用
  • 实际处理 - 仍然通过Hook系统,插件自己解析和处理命令
  • 两者独立 - 注册的命令信息不会自动处理命令,需要在Hook中实现处理逻辑

🆕 Q: 如何使用新的多消息功能?

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
        }

🆕 Q: 多消息有数量限制吗?

A: 技术上没有硬性限制,但建议:

  • 推荐数量: 单次回复不超过5条消息
  • 用户体验: 过多消息可能影响阅读体验
  • 性能考虑: 消息过多可能影响发送速度

🆕 Q: 可以混合不同类型的消息吗?

A: 完全可以!这正是多消息功能的优势:

return {
    'response': [
        MessageBuilder.text('📊 数据查询结果'),           # 文本
        MessageBuilder.text_card('详细数据...'),          # 卡片
        MessageBuilder.markdown('## 分析\n...'),         # Markdown
        MessageBuilder.image('chart.jpg', '图表'),       # 图片
        MessageBuilder.text('✅ 查询完成')                # 文本
    ],
    'handled': True
}

🆕 Q: 多消息功能向后兼容吗?

A: 完全兼容!现有插件无需修改:

# 旧方式(仍然支持)
return {
    'response': MessageBuilder.text('单条消息'),
    'handled': True
}

# 新方式(多消息)
return {
    'response': [
        MessageBuilder.text('第一条'),
        MessageBuilder.text('第二条')
    ],
    'handled': True
}

📖 参考资源


🎉 开始创建你的第一个插件吧! 如有问题,请查看现有插件代码或联系开发团队。