Skip to content

Little-UNIkeEN-Bot 通过插件机制实现功能的开发,通过维护插件列表实现功能的增减。

1. 聊天插件 - StandardPlugin

插件逻辑

所有接收私聊消息和群消息的插件都必须继承于 StandardPlugin 类,并实现以下四个接口函数(前三个必须实现,最后一个可选):

接口名称 参数 返回值类型 作用
judgeTrigger @msg: 消息文本,str类型
@data: 消息所包括的所有信息,dict类型
bool 判断此插件是否会触发
executeEvent @msg: 消息文本,str类型
@data: 消息所包括的所有信息,dict类型
str 或者 None 执行插件逻辑
getPluginInfo None dict 返回插件信息
onStateChange @nextState: 下一个状态,bool类型
@data: 消息所包括的所有信息,dict类型
None 在开关插件的时候出发插件特定逻辑

开发者在 main.pyimport 功能插件,并维护 main.py 中的插件列表以增减插件,关于插件的具体示例请见下一节。

Bot 运行收到群聊/私聊消息时,将遍历群聊/私聊插件列表,逐一调用各插件的 judgeTrigger() 函数,若返回值为真则触发,调用该插件的 executeEvent() 函数进行响应。插件的 getPluginInfo() 函数则在用户获取帮助信息时被逐一调用。

特别强调,关于 getPluginInfo() 的返回值格式,要求如下:

{
    'name': 'Faq',                      # require
    'description': '问答库',             # require
    'commandDescription': '问 [title]', # require
    'usePlace': ['group', 'private', ], # require
    'showInHelp': True,                 # suggest, default True
    'pluginConfigTableNames': ['Faq',], # suggest, must be unique among plugins
    'version': '1.0.0',                 # suggest
    'author': 'Unicorn',                # suggest
    ...                                 # any other information you want
}
名称 类型 作用 是否必须
'name' str 提示这个插件的class名称
'description' str 简要描述这个插件
'commandDescription' str 概括触发这个插件的关键词
'usePlace' list 描述这个插件的作用域
'private': 这个插件可以在私聊开启
'group': 这个插件可以在群聊开启
'showInHelp' bool 是否在help时展示 否,默认True
'pluginConfigTableNames' list 这个插件所需要占用的数据库表名。注意,插件开发者创建的sql表尽量挂在BOT_DATA这个库下面,因此需要避免表重名。
'version' str 插件版本
'author' str 插件作者

插件示范

以下的代码可以实现,在群聊或私聊中有人发送关键词“放个烟花”或“烟花”时,Bot 回复超级表情“烟花”。

plugins/superEmoji.py 中实现插件功能代码:

from typing import Union, Any
from utils.basicEvent import *
from utils.standardPlugin import StandardPlugin # 引入StandardPlugin

class FireworksFace(StandardPlugin):
    def judgeTrigger(self, msg:str, data:Any) -> bool:
        return msg in ['放个烟花','烟花']
    def executeEvent(self, msg:str, data:Any) -> Union[None, str]:
        target = data['group_id'] if data['message_type']=='group' else data['user_id']
        send(target, "[CQ:face,id=333,type=sticker]", data['message_type']) # 发送消息
        return "OK"
    def getPluginInfo(self, ) -> dict:
        return {
            'name': 'FireworksFace',
            'description': '烟花',
            'commandDescription': '放个烟花/烟花',
            'usePlace': ['group', 'private'],
            'showInHelp': True,
            'pluginConfigTableNames': [],
            'version': '1.0.0',
            'author': 'Unicorn',
        }

发送消息函数 send() 的具体参数与功能请见 API - 驱动器对接

main.py 的插件列表中启用插件:

from plugins.superEmoji import *
...
GroupPluginList=[ # 群插件列表
    ..., FireworksFace(), # 在此处添加插件类以在群聊时启用
]
PrivatePluginList=[ # 私聊启用插件
    ..., FireworksFace(), # 在此处添加插件类以在私聊时启用
]

提示:代码结构

为便于管理,我们推荐将功能插件的代码文件放在 plugins/ 目录下,并在 main.pyimport 。如果有需要,你可以随意更改此代码结构。

注意:触发条件冲突

Bot 在设计时默认一句指令只能进行一次响应,开发者请尽量确保触发条件不冲突,或考虑插件列表中各插件的先后顺序。 如某一插件触发但不提供响应,可以返回 None 以继续执行列表中的后续插件。

2. 接收撤回消息插件 - RecallMessageStandardPlugin

所有接收'私聊撤回消息'和'群聊撤回消息'的插件都必须继承于 RecallMessageStandardPlugin 类,并实现以下接口函数:

接口名称 参数 返回值类型 作用
recallMessage @data: 消息所包括的所有信息,dict类型 None 对'撤回消息'消息进行处理

3. 接收上传文件插件 - GroupUploadStandardPlugin

所有接收'群聊上传文件'的插件都必须继承于 GroupUploadStandardPlugin 类,并实现以下接口函数:

接口名称 参数 返回值类型 作用
uploadFile @data: 消息所包括的所有信息,dict类型 None 对'上传文件'消息进行处理

4. 接收拍一拍插件 - PokeStandardPlugin

所有接收'群聊上传文件'的插件都必须继承于 PokeStandardPlugin 类,并实现以下接口函数:

接口名称 参数 返回值类型 作用
judgeTrigger @data: 消息所包括的所有信息 bool 判断此插件是否会触发
pokeMessage @data: 消息所包括的所有信息 str 或者 None 执行插件逻辑

代码分析

# parts of utils/standardPlugin.py
from abc import ABC, abstractmethod
from typing import Union, Tuple, Any, List

class StandardPlugin(ABC):
    """接收‘正常私聊消息或群消息’的接口"""
    @abstractmethod
    def judgeTrigger(self, msg:str, data:Any) -> bool:
        """
        @msg: message text
        @data: all the message data, including group_id or user_id
        @return: whether trigger this class
        """
        raise NotImplementedError

    @abstractmethod
    def executeEvent(self, msg:str, data:Any) -> Union[None, str]:
        """
        @msg: message text
        @data: all the message data, including group_id or user_id
        @return:
            if None, then continue tranversing following plugins
            if "OK", then stop tranversing
        """
        raise NotImplementedError

    @abstractmethod
    def getPluginInfo(self)->dict:
        """
        @return:
            a dict object like:
            {
                'name': 'Faq',                      # require
                'description': '问答库',             # require
                'commandDescription': '问 [title]', # require
                'usePlace': ['group', 'private', ], # require
                'showInHelp': True,                 # suggest, default True
                'pluginConfigTableNames': ['Faq',], # suggest, must be unique among plugins
                'version': '1.0.0',                 # suggest
                'author': 'Unicorn',                # suggest
                ...                                 # any other information you want
            }
        """
        raise NotImplementedError

    def onStateChange(self, nextState:bool, data:Any)->None:
        """插件的状态(开启或关闭)改变时会调用此接口
        @nextState: 
            if True, plugin will open next
            if False, plugin will close next
        @data: all the message data, including group_id or user_id
        """

class EmptyPlugin(StandardPlugin):
    """空插件"""
    def __init__(self, *args, **kwargs) -> None:
        return
    def judgeTrigger(self, msg: str, data: Any) -> bool:
        return False
    def executeEvent(self, msg: str, data: Any) -> Union[None, str]:
        return None
    def getPluginInfo(self) -> dict:
        return {
            'name': 'EmptyPlugin',
            'description': '空插件',
            'commandDescription': '',
            'usePlace': ['group', 'private', ],
            'showInHelp': False,
            'pluginConfigTableNames': [],
            'version': '1.0.0',
            'author': 'Unicorn',
        }

class PokeStandardPlugin(ABC):
    """接收‘拍一拍’消息"""
    @abstractmethod
    def judgeTrigger(self, data:Any)->bool:
        """判断插件是否触发"""
        raise NotImplementedError

    @abstractmethod
    def pokeMessage(self, data:Any)->Union[str, None]:
        """接收‘戳一戳’消息的接口"""
        raise NotImplementedError

class RecallMessageStandardPlugin(ABC):
    """接收‘撤回类型’消息的接口"""
    @abstractmethod
    def recallMessage(self, data:Any)->Union[str, None]:
        raise NotImplementedError

class GroupUploadStandardPlugin(ABC):
    """接收‘上传文件’消息的接口"""
    @abstractmethod
    def uploadFile(self, data)->Union[str, None]:
        raise NotImplementedError