11 KiB
QChatGPT 插件开发Wiki
问题、需求请到仓库issue发起
提问前请先靠自己尝试
💬简介
尽管“为一个基于OpenAI API的QQ机器人开发插件支持”这事看起来有点小题大做,但萌生此想法后的几天内好几个人提出了这个需求,最终促使此项目正式支持插件。
🧱实现
基于importlib
库加载模块的方法动态加载额外Python程序文件以便实现插件加载,插件均存放在plugins
文件夹,其中的所有.py
文件都将被加载(除了所有__init__.py
)
📚示例代码
请查看代码目录tests/plugin_examples
中的插件目录
💻快速开始
按照文档部署此项目,并使其正常运行。
在plugins
目录下新建目录hello
,在其中新建空文件__init__.py
以标记此目录为软件包,继续新建文件main.py
。
您也可以使用hello_plugin作为模板直接生成插件代码仓库
编辑main.py
输入以下内容:
from pkg.plugin.models import *
from pkg.plugin.host import EventContext, PluginHost
"""
在收到私聊或群聊消息"hello"时,回复"hello, <发送者id>!"或"hello, everyone!"
"""
# 注册插件
@register(name="Hello", description="hello world", version="0.1", author="RockChinQ")
class HelloPlugin(Plugin):
# 插件加载时触发
# plugin_host (pkg.plugin.host.PluginHost) 提供了与主程序交互的一些方法,详细请查看其源码
def __init__(self, plugin_host: PluginHost):
pass
# 当收到个人消息时触发
@on(PersonNormalMessageReceived)
def person_normal_message_received(self, event: EventContext, **kwargs):
msg = kwargs['text_message']
if msg == "hello": # 如果消息为hello
# 输出调试信息
logging.debug("hello, {}".format(kwargs['sender_id']))
# 回复消息 "hello, <发送者id>!"
event.add_return("reply", ["hello, {}!".format(kwargs['sender_id'])])
# 阻止该事件默认行为(向接口获取回复)
event.prevent_default()
# 当收到群消息时触发
@on(GroupNormalMessageReceived)
def group_normal_message_received(self, event: EventContext, **kwargs):
msg = kwargs['text_message']
if msg == "hello": # 如果消息为hello
# 输出调试信息
logging.debug("hello, {}".format(kwargs['sender_id']))
# 回复消息 "hello, everyone!"
event.add_return("reply", ["hello, everyone!"])
# 阻止该事件默认行为(向接口获取回复)
event.prevent_default()
# 插件卸载时触发
def __del__(self):
pass
此插件将实现:私聊收到hello
消息时回复hello, <发送者QQ号>!
,群聊收到hello
消息时回复hello, everyone!
解读此插件程序
import
从pkg.plugin
引入models
模块的所有字段(此程序使用了其中的register函数
、on函数
、Plugin类
、PersonNormalMessageReceived事件
、GroupNormalMessageReceived事件
)@register()
将类HelloPlugin
标记为一个插件类,声明插件名称为Hello
以及插件简介、版本、作者- 声明类
HelloPlugin
继承于Plugin
,此类可以随意命名,插件名称只与register
调用时的参数有关 - 声明此类的
__init__
方法,此方法是可选的,其中的代码将在主程序启动时加载插件的时候被执行 @on
将方法person_normal_message_received
标记为一个事件处理器,处理PersonNormalMessageReceived
(收到私聊消息并在获取OpenAI回复前触发)事件,此方法可以随意命名,绑定的事件只与on
中的参数有关,更多支持的事件可到pkg.plugin.models.py
文件中查看或查看下方API
节- 输出调试信息,程序中可通过
logging
将日志输出到控制台和qchatgpt.log
文件 - 方法内部从参数中取出
text_message
参数,判断是否为hello
,如果是就将返回值reply
设置为["hello, {}!".format(kwargs['sender_id'])]
,接下来调用event
对象的prevent_default
方法,阻止原程序默认行为- 每个事件
提供的参数
和支持的返回值
请查看pkg.plugin.models
中的每个事件的注释或查看下方API
节 event
对象提供的方法请查看pkg.plugin.host
中的EventContext
类或查看下方API
节
- 每个事件
- 用相似的程序注册
GroupNormalMessageReceived
事件处理群消息
编写完毕保存后,重新启动主程序,查看到输出中包含以下内容,即为加载成功:
[2023-01-16 18:29:47.193] host.py (43) - [INFO] : 加载模块: hello.main
[2023-01-16 18:29:47.194] models.py (209) - [INFO] : 插件注册完成: n='Hello', d='hello world', v=0.1, a='RockChinQ' (<class 'plugins.hello.main.HelloPlugin'>)
建议在
config.py
中设置logging_level = logging.DEBUG
以便开启调试输出
❗规范(重要)
- 请每个插件独立一个目录以便管理,建议在Github上创建一个仓库储存单个插件,以便获取和更新
- 插件名使用
大驼峰命名法
,如Hello
、ExamplePlugin
、ChineseCommands
等 - 一个目录内可以存放多个Python程序文件,以独立出插件的各个功能,便于开发者管理,但不建议在一个目录内注册多个插件
- 插件需要的依赖库请在插件目录下的
requirements.txt
中指定,程序从储存库获取此插件时将自动安装依赖
📄API参考
说明
事件处理函数将会获得一系列参数,可以在kwargs
中取出。
其中host
参数(pkg.plugin.host.PluginHost
类的实例)是插件宿主,提供与主程序各个模块交互的一些方法。
event
参数(pkg.plugin.host.EventContext
类的实例)是事件执行期间的上下文,提供对此次事件执行的一些操作方法。
事件返回值均为可选的,可以通过调用event.add_return(key: str, ret)
来提交返回值
事件
所有事件参数均有host
和event
,以下仅展示其他参数
关于YiriMirai
支持的消息链组件,请查看 YiriMirai的文档
PersonMessageReceived = "person_message_received"
"""收到私聊消息时,在判断是否应该响应前触发
kwargs:
launcher_type: str 发起对象类型(group/person)
launcher_id: int 发起对象ID(群号/QQ号)
sender_id: int 发送者ID(QQ号)
message_chain: mirai.models.message.MessageChain 消息链
"""
GroupMessageReceived = "group_message_received"
"""收到群聊消息时,在判断是否应该响应前触发(所有群消息)
kwargs:
launcher_type: str 发起对象类型(group/person)
launcher_id: int 发起对象ID(群号/QQ号)
sender_id: int 发送者ID(QQ号)
message_chain: mirai.models.message.MessageChain 消息链
"""
PersonNormalMessageReceived = "person_normal_message_received"
"""判断为应该处理的私聊普通消息时触发
kwargs:
launcher_type: str 发起对象类型(group/person)
launcher_id: int 发起对象ID(群号/QQ号)
sender_id: int 发送者ID(QQ号)
text_message: str 消息文本
returns (optional):
alter: str 修改后的消息文本
reply: list 回复消息组件列表,元素为YiriMirai支持的消息组件
"""
PersonCommandSent = "person_command_sent"
"""判断为应该处理的私聊指令时触发
kwargs:
launcher_type: str 发起对象类型(group/person)
launcher_id: int 发起对象ID(群号/QQ号)
sender_id: int 发送者ID(QQ号)
command: str 指令
params: list[str] 参数列表
text_message: str 完整指令文本
is_admin: bool 是否为管理员
returns (optional):
alter: str 修改后的完整指令文本
reply: list 回复消息组件列表,元素为YiriMirai支持的消息组件
"""
GroupNormalMessageReceived = "group_normal_message_received"
"""判断为应该处理的群聊普通消息时触发
kwargs:
launcher_type: str 发起对象类型(group/person)
launcher_id: int 发起对象ID(群号/QQ号)
sender_id: int 发送者ID(QQ号)
text_message: str 消息文本
returns (optional):
alter: str 修改后的消息文本
reply: list 回复消息组件列表,元素为YiriMirai支持的消息组件
"""
GroupCommandSent = "group_command_sent"
"""判断为应该处理的群聊指令时触发
kwargs:
launcher_type: str 发起对象类型(group/person)
launcher_id: int 发起对象ID(群号/QQ号)
sender_id: int 发送者ID(QQ号)
command: str 指令
params: list[str] 参数列表
text_message: str 完整指令文本
is_admin: bool 是否为管理员
returns (optional):
alter: str 修改后的完整指令文本
reply: list 回复消息组件列表,元素为YiriMirai支持的消息组件
"""
NormalMessageResponded = "normal_message_responded"
"""获取到对普通消息的文字响应时触发
kwargs:
launcher_type: str 发起对象类型(group/person)
launcher_id: int 发起对象ID(群号/QQ号)
sender_id: int 发送者ID(QQ号)
session: pkg.openai.session.Session 会话对象
prefix: str 回复文字消息的前缀
response_text: str 响应文本
returns (optional):
prefix: str 修改后的回复文字消息的前缀
reply: list 替换回复消息组件列表,元素为YiriMirai支持的消息组件
"""
SessionFirstMessageReceived = "session_first_message_received"
"""会话被第一次交互时触发
kwargs:
session_name: str 会话名称(<launcher_type>_<launcher_id>)
session: pkg.openai.session.Session 会话对象
default_prompt: str 预设值
"""
SessionExplicitReset = "session_reset"
"""会话被用户手动重置时触发,此事件不支持阻止默认行为
kwargs:
session_name: str 会话名称(<launcher_type>_<launcher_id>)
session: pkg.openai.session.Session 会话对象
"""
SessionExpired = "session_expired"
"""会话过期时触发
kwargs:
session_name: str 会话名称(<launcher_type>_<launcher_id>)
session: pkg.openai.session.Session 会话对象
session_expire_time: int 已设置的会话过期时间(秒)
"""
KeyExceeded = "key_exceeded"
"""api-key超额时触发
kwargs:
key_name: str 超额的api-key名称
usage: dict 超额的api-key使用情况
exceeded_keys: list[str] 超额的api-key列表
"""
KeySwitched = "key_switched"
"""api-key超额切换成功时触发,此事件不支持阻止默认行为
kwargs:
key_name: str 切换成功的api-key名称
key_list: list[str] api-key列表
"""
host: PluginHost 详解
提供与主程序各个模块交互的一些方法,具体查看pkg.plugin.host
中的PluginHost
类
event: EventContext 详解
提供对此次事件执行的一些操作方法,具体查看pkg.plugin.host
中的EventContext
类