🚀 快速开始:创建你的第一个全功能插件
欢迎来到 MoFox_Bot 插件开发的世界!本指南将带你从零开始,创建一个包含 Action、Command、Tool 和 Event Handler 四大核心组件的 hello_world
插件。
通过这个过程,你将掌握插件系统的基本结构和开发流程。
📂 步骤一:创建插件基础结构
首先,我们需要为插件创建一个家。
1. 创建插件目录
在项目根目录的 plugins/
文件夹下,创建一个新的目录,命名为 hello_world_plugin
。
2. 创建清单文件 _manifest.json
每个插件都需要一个清单文件来描述它的基本信息。在 hello_world_plugin
目录下创建 _manifest.json
文件,并填入以下内容:
{
"manifest_version": 1,
"name": "Hello World 插件",
"version": "1.0.1",
"description": "一个包含四大核心组件的入门示例插件。",
"author": {
"name": "你的名字"
}
}
这个文件告诉 MoFox_Bot 你的插件叫什么、版本号以及它的功能。
3. 创建主文件 plugin.py
这是插件的灵魂所在。在 hello_world_plugin
目录下创建 plugin.py
文件。我们先写一个最基础的框架:
from typing import List, Tuple, Type
from src.plugin_system import (
BasePlugin,
register_plugin,
ComponentInfo
)
@register_plugin
class HelloWorldPlugin(BasePlugin):
"""一个包含四大核心组件的入门示例插件。"""
# --- 插件基础信息 ---
plugin_name = "hello_world_plugin"
enable_plugin = True
dependencies = []
python_dependencies = []
config_file_name = "config.toml"
config_schema = {}
def get_plugin_components(self) -> List[Tuple[ComponentInfo, Type]]:
"""注册插件的所有功能组件。"""
return []
到这里,你的插件已经可以被系统加载了,虽然它现在还什么都做不了。
🛠️ 步骤二:逐一添加四大组件
现在,让我们开始为插件添加真正的功能。我们将在 plugin.py
文件中添加代码。
1. 添加 Event Handler (事件处理器)
功能:我们想在机器人启动时,在控制台打印一条消息,证明插件已成功加载。
将以下代码添加到 plugin.py
的顶部:
# (放在 import 语句下方)
from src.plugin_system import BaseEventHandler, EventType
from src.plugin_system.base.base_event import HandlerResult
import logging
# ... (其他 import)
class StartupMessageHandler(BaseEventHandler):
"""启动时打印消息的事件处理器。"""
handler_name = "hello_world_startup_handler"
handler_description = "在机器人启动时打印一条日志。"
init_subscribe = [EventType.ON_START] # 订阅启动事件
async def execute(self, params: dict) -> HandlerResult:
logging.info("🎉 Hello World 插件已启动,准备就绪!")
return HandlerResult(success=True, continue_process=True)
BaseEventHandler
: 所有事件处理器的父类。init_subscribe
: 告诉系统我们关心哪个事件,这里是EventType.ON_START
(启动事件)。execute
: 事件发生时,这里的代码会被执行。
2. 添加 Tool (工具)
功能:创建一个简单的工具,可以提供一些固定的系统信息。这个工具本身不被调用,仅用于展示结构。
将以下代码添加到 StartupMessageHandler
类的下方:
from src.plugin_system import BaseTool, ToolParamType
from typing import Dict, Any
# ... (其他类定义)
class GetSystemInfoTool(BaseTool):
"""一个提供系统信息的示例工具。"""
name = "get_system_info"
description = "获取当前系统的模拟版本和状态信息。"
available_for_llm = True # 允许被 LLM 发现和使用
parameters = [] # 这个工具不需要参数
async def execute(self, function_args: Dict[str, Any]) -> Dict[str, Any]:
return {
"name": self.name,
"content": "系统版本: 1.0.1, 状态: 运行正常"
}
BaseTool
: 所有工具的父类。name
,description
,parameters
: 这三者定义了工具的“签名”,LLM 会根据这些信息来决定是否以及如何使用它。
3. 添加 Command (命令)
功能:让用户可以通过输入 /hello
来获得一句问候。
这里我们使用更现代的 PlusCommand
。将以下代码添加到 GetSystemInfoTool
类的下方:
from src.plugin_system import PlusCommand, CommandArgs, ChatType
from typing import Tuple, Optional
# ... (其他类定义)
class HelloCommand(PlusCommand):
"""一个简单的 /hello 命令。"""
command_name = "hello"
command_description = "向机器人发送一个简单的问候。"
command_aliases = ["hi", "你好"] # 命令的别名
chat_type_allow = ChatType.ALL # 在群聊和私聊中都可用
# --- 可用范围控制 ---
# chat_type_allow 控制命令在哪些类型的聊天中可用。
# - ChatType.ALL: 在群聊和私聊中都可用。
# - ChatType.GROUP: 仅在群聊中可用。
# - ChatType.PRIVATE: 仅在私聊中可用。
chat_type_allow = ChatType.ALL
async def execute(self, args: CommandArgs) -> Tuple[bool, Optional[str], bool]:
await self.send_text("Hello, World! 我是一个由 MoFox_Bot 驱动的插件。")
return True, "成功发送问候", True
PlusCommand
: 推荐使用的命令基类,无需编写正则表达式。command_name
,command_aliases
: 定义了用户如何触发这个命令。execute
: 当命令被触发时,这里的代码会被执行。self.send_text
是一个方便的内置方法,用于发送文本消息。
4. 添加 Action (动作)
功能:让机器人有时会“自发地”发送一个随机表情,增加一点趣味性。
将以下代码添加到 HelloCommand
类的下方:
from src.plugin_system import BaseAction, ActionActivationType
import random
# ... (其他类定义)
class RandomEmojiAction(BaseAction):
"""一个随机发送表情的动作。"""
action_name = "random_emoji"
action_description = "随机发送一个表情符号,增加聊天的趣味性。"
# --- 激活控制 (第一层决策) ---
activation_type = ActionActivationType.RANDOM
random_activation_probability = 0.1 # 10% 的概率被激活
# --- 使用条件 (第二层决策) ---
action_require = ["当对话气氛轻松时", "可以用来回应简单的情感表达"]
associated_types = ["text"]
async def execute(self) -> Tuple[bool, str]:
emojis = ["😊", "😂", "👍", "🎉", "🤔", "🤖"]
await self.send_text(random.choice(emojis))
return True, "成功发送了一个随机表情"
BaseAction
: 所有动作的父类。activation_type
: 定义了动作如何进入“备选池”。这里使用RANDOM
,意味着它有一定概率被考虑。action_require
: 告诉 LLM 在什么情境下选择使用这个动作。execute
: 当 LLM 最终决定使用这个动作时,这里的代码会被执行。
✅ 步骤三:注册所有组件
现在我们已经定义好了四个组件,最后一步是告诉插件主类它们的存在。
回到 HelloWorldPlugin
类,修改 get_plugin_components
方法,将所有组件注册进去。
# (修改 HelloWorldPlugin 类)
# ... (所有组件的类定义) ...
@register_plugin
class HelloWorldPlugin(BasePlugin):
"""一个包含四大核心组件的入门示例插件。"""
# --- 插件基础信息 (保持不变) ---
plugin_name = "hello_world_plugin"
enable_plugin = True
dependencies = []
python_dependencies = []
config_file_name = "config.toml"
config_schema = {}
def get_plugin_components(self) -> List[Tuple[ComponentInfo, Type]]:
"""注册插件的所有功能组件。"""
return [
(StartupMessageHandler.get_handler_info(), StartupMessageHandler),
(GetSystemInfoTool.get_tool_info(), GetSystemInfoTool),
(HelloCommand.get_command_info(), HelloCommand),
(RandomEmojiAction.get_action_info(), RandomEmojiAction),
]
- 每个组件都有一个
get_..._info()
的类方法,用于获取其元信息。 - 我们将每个组件的元信息和类本身作为一个元组,添加到返回的列表中。
🎉 恭喜!
你已经成功创建了一个功能完整的 hello_world
插件!重启你的 MoFox_Bot,你将会:
- 在控制台看到 "🎉 Hello World 插件已启动,准备就绪!" 的消息。
- 可以向机器人发送
/hello
或!你好
,并收到回复。 - 在与机器人聊天时,偶尔会收到一个随机的表情符号。
- 虽然
get_system_info
工具不会被直接触发,但它已经作为一项能力被注册到了系统中。
现在,你已经掌握了插件开发的基础。可以尝试修改这个插件,或者创建属于你自己的全新插件了!
🚀 进阶:让插件更灵活
硬编码的文本不是一个好习惯。让我们学习如何使用配置文件,让你的插件可以由用户自由配置。
1. 定义配置 Schema
我们通过在插件主类中定义 config_schema
属性,来声明插件所需的配置项。系统会基于这个 schema 自动处理配置的生成和加载。
配置工作流
插件配置遵循一个清晰的生命周期:
定义 Schema: 开发者在插件代码中通过
config_schema
来定义配置的结构、类型和默认值。这是配置的“蓝图”。自动生成配置文件: 当 MoFox_Bot 首次加载插件时,它会读取
config_schema
并在config/plugins/<plugin_name>/config.toml
路径下生成一个默认的配置文件。用户自定义配置: 用户应仅修改
config/plugins/
目录下的config.toml
文件来覆盖默认值。插件代码永远不应修改此文件。这确保了用户配置的持久性和唯一性。运行时加载配置: 在插件内部,通过调用
self.get_config()
方法来安全地读取和验证用户配置。此方法返回一个 Pydantic 模型实例,确保了数据的类型安全和完整性。如果用户配置不符合config_schema
定义的格式,系统会记录错误并加载默认值,保证插件的稳定运行。
🚨 重要:你不需要手动创建 config.toml
文件!系统会根据你的定义自动生成它。
修改 plugin.py
中的 HelloWorldPlugin
类:
# (在 plugin.py 顶部)
from src.plugin_system import ConfigField # 别忘了导入 ConfigField
# ... (其他代码)
@register_plugin
class HelloWorldPlugin(BasePlugin):
# ... (其他基础信息)
# --- 配置文件定义 ---
config_schema = {
"greeting": {
"message": ConfigField(
type=str,
default="这是来自配置文件的问候!👋",
description="HelloCommand 使用的问候语。"
),
},
}
def get_plugin_components(self) -> List[Tuple[ComponentInfo, Type]]:
# ... (保持不变)
config_schema
是一个字典,定义了配置的结构。ConfigField
用于详细定义每个配置项的类型、默认值和描述。
2. 在代码中使用配置
定义好 Schema 后,我们就可以在组件中通过 self.get_config()
方法来读取配置值了。
修改 HelloCommand
的 execute
方法:
class HelloCommand(PlusCommand):
# ... (基础信息不变)
async def execute(self, args: CommandArgs) -> Tuple[bool, Optional[str], bool]:
# 从配置文件读取问候语,如果找不到则使用默认值
greeting = str(self.get_config("greeting.message", "Hello, World! 我是一个由 MoFox_Bot 驱动的插件。"))
await self.send_text(greeting)
return True, "成功发送问候", True
self.get_config("greeting.message", ...)
: 第一个参数是配置的路径([greeting]
下的message
),第二个是找不到配置时的默认值。- 我们用
str()
确保最终得到的是一个字符串,以保证类型安全。
3. 它是如何工作的?
- 首次启动: 当 MoFox_Bot 第一次加载你的插件时,它会检查
config_schema
。 - 生成文件: 它会在
config/plugins/hello_world_plugin/
目录下自动生成一个config.toml
文件,内容如下:toml# hello_world_plugin - 自动生成的配置文件 # 一个包含四大核心组件和配置功能的入门示例插件。 # greeting [greeting] # HelloCommand 使用的问候语。 message = "这是来自配置文件的问候!👋"
- 用户修改: 用户可以随时修改这个
config.toml
文件中的message
值。 - 读取配置: 下次
/hello
命令被触发时,get_config
就会读取用户修改后的新值。
现在,你的插件不仅功能完整,而且变得更加灵活和强大了!
4. 终极技巧:组件开关与配置版本
我们还可以做得更专业。通过配置文件,我们不仅可以改变文本,甚至可以控制插件的哪些部分需要加载!
a. 添加组件开关和版本号
让我们再次升级 config_schema
,加入组件开关和版本号:
@register_plugin
class HelloWorldPlugin(BasePlugin):
# ... (基础信息)
config_schema = {
"meta": {
"config_version": ConfigField(
type=int,
default=1,
description="配置文件版本,请勿手动修改。"
),
},
"greeting": {
"message": ConfigField(
type=str,
default="这是来自配置文件的问候!👋",
description="HelloCommand 使用的问候语。"
),
},
"components": {
"hello_command_enabled": ConfigField(
type=bool,
default=True,
description="是否启用 /hello 命令。"
),
"random_emoji_action_enabled": ConfigField(
type=bool,
default=True,
description="是否启用随机表情动作。"
),
}
}
# ...
[meta]
: 我们添加了一个meta
表,用于存放元信息,比如config_version
。这对于未来管理插件配置的更新非常有帮助。[components]
: 在这里,我们为HelloCommand
和RandomEmojiAction
分别添加了一个布尔类型的开关。
b. 动态加载组件
最后,也是最关键的一步,修改 get_plugin_components
方法,让它在加载组件前先读取配置:
def get_plugin_components(self) -> List[Tuple[ComponentInfo, Type]]:
"""根据配置文件动态注册插件的功能组件。"""
components: List[Tuple[ComponentInfo, Type]] = []
# 总是注册这两个基础组件
components.append((StartupMessageHandler.get_handler_info(), StartupMessageHandler))
components.append((GetSystemInfoTool.get_tool_info(), GetSystemInfoTool))
# 根据配置决定是否注册 HelloCommand
if self.get_config("components.hello_command_enabled", True):
components.append((HelloCommand.get_command_info(), HelloCommand))
# 根据配置决定是否注册 RandomEmojiAction
if self.get_config("components.random_emoji_action_enabled", True):
components.append((RandomEmojiAction.get_action_info(), RandomEmojiAction))
return components
现在,用户可以直接在 config.toml
文件中将 hello_command_enabled
设置为 false
,重启后 /hello
命令就会失效,而插件的其他部分(如随机表情)仍然可以正常工作。
这为用户提供了极大的灵活性,也让你的插件变得更加健壮和专业。