欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

如何使用QQ机器人框架NoneBot2来创建插件教程

最编程 2024-07-22 18:43:03
...

引入

NoneBot2 是一个现代、跨平台、可扩展的 Python 聊天机器人框架,它基于 Python 的类型注解和异步特性,能够为你的需求实现提供便捷灵活的支持。

在此记录简单的插件编写规则,避免踩坑。

插件编写

1. 创建插件

1.1 创建

建议使用nb脚手架(nb-cli)创建插件,终端中输入:

nb plugin

按照指引新建插件即可,创建成功后得到插件包文件

plugin_name

├── __init__.py

├── config.py

1.2 初始化调整

此时,直接运行机器人会报错。需要调整__init__.py文件中的内容。

global_config = get_driver().config
config = Config.parse_obj(global_config) # 需要改动的地方
  • 若需要插件配置
global_config = nonebot.get_driver().config
plugin_config = Config(**global_config.dict()) # 注意改成这样,具体作用在配置插件中说明
  • 若不需要

    删除上述需要注意的那一行,global_config视情况删除

1.3 推荐的插件结构:

plugin_name

├── __init__.py

├── config.py (按需使用)

├── data_source.py (按需创建,一般在该文件中编写数据获取函数)

└── model.py (按需创建,一般在该文件中编写数据库模型)

2. 插件配置

config.py

在该文件中使用 pydantic 定义插件所需要的配置项以及类型。

from pydantic import BaseSettings

class Config(BaseSettings):
	
    # 写入插件需要的配置,类型,默认值
    name: str
    level: int = 5
    foo: str = "bar"

    class Config:
        # 忽略传入的其他配置
        extra = "ignore"

此时,__init__.py 中的

plugin_config = Config(**global_config.dict())

会将.evn文件中的填写配置传入插件配置。

3. 插件触发

3.1 创建事件响应器 Matcher

用于定义该插件在什么情况下被触发。

官方例子:

weather = on_command("weather", rule=to_me(), aliases={"天气", "天气预报"}, priority=5)

常用两种消息事件响应器:

  1. on_message():消息事件响应器,参考on
  2. on_command():消息事件的命令响应器,参考 on_command

更多官方事件响应器,参考插件matcher

3.1.1 匹配规则

当事件传递时,在 Matcher 运行前进行规则检查

3.1.1.1 Rule

官方的Rule大部分的可以在封装的事件响应器直接使用,仅对to_me()进行介绍

to_me()判断成功,只需要满足以下任意一条条件:

  1. 直接在消息开头@机器人

  2. 直接在消息开头叫机器人的昵称(.env中定义的nickname)

  3. 私聊

3.1.1.1.1 自定义Rule

简单的例子:

def checker():
    async def _checker(bot: Bot, event: Event, state: T_State) -> bool:
        if True:          #在这个位置写入你的判断代码
            return True   #记住,返回值一定要是个bool类型的值!
    return Rule(_checker)

更多参考自定义Rule

3.1.1.2 Permission

常用两种

  1. SUPERUSER

    匹配任意超级用户消息类型事件

  2. USER(*user, perm=None)

    eventsession_id 在user白名单内且满足 perm

    参数:

    • *user: str: 白名单

    • perm: Optional[Permission]: 需要同时满足的权限

更多参考Permission

3.1.1.3 注意

在这里需要强调的是,PermissionRule 的表现并不相同, Rule 只会在初次响应时生效,在余下的对话中并没有限制事件;

但是 Permission 会持续生效,在连续对话中会一直对事件主体加以限制。

因此,在群聊消息中需要注意使用 Permission

3.2 事件处理

创建事件响应器后,需要对响应的事件进行处理。

weather = on_command("weather", rule=to_me(), aliases={"天气", "天气预报"}, priority=5)

weather 这个变量作为一个容器对事件响应器进行了包裹,而想要对事件进行处理需要使用装饰器

一个事件响应器可以有多个事件处理器对其进行处理,且为顺序执行

NoneBot提供三种事件处理器:

  1. xxx.handle()
  2. xxx.reveive()
  3. xxx.got(key, prompt, args_parser)

参考官方文档

注意V2.0.0b1新语法,参考事件处理流程

xxx.handle()

最基础的处理器装饰器,收到信息后立即处理此消息,不会等待进一步的输入,也不会响应后续输入。 例如:

test = on_command("test")
@test.handle()
async def test_handle(bot: Bot, event: Event, state: T_State):
    print('我收到了命令“/test”')

xxx.receive()

运行到此修饰器时,会先暂停对该事件的处理,当再次收到用户输入后才对新的输入后进行处理(类似在开头运行了input()

常见于对后续数据的接收等,一般不会放在第一个使用,放第一个等同于xxx.handle()

test = on_command("test")
@test.handle()
async def test_handle(bot: Bot, event: Event, state: T_State):
    print('我收到了第一条消息')

@test.recive()
async def test_handle(bot: Bot, event: Event, state: T_State):
    print('我收到了第二条消息')

@test.recive()
async def test_handle(bot: Bot, event: Event, state: T_State):
    print('我收到了第三条消息')

xxx.got(key, prompt=None, args_parser=None)

运行到此修饰器时,会先检查这个事件的state,是否带有key字段

有则等同于xxx.handle(),开始执行该处理器内容

无则向用户发送prompt,接着类似xxx.recive(),等待输入,输入完成后进行args_parser处理

,然后执行该处理器内容,最后执行下一个修饰器(如果有的话)

注意:

  1. prompt可以是Message
  2. 如果在后续需要提取这个消息的信息,可用state[key]进行提取。
  3. args_parser是参数解析函数,不填则使用默认解析,参考文档
# V2a语法
test = on_command("test")
@test.handle()
async def test_handle(bot: Bot, event: Event, state: T_State):
    cmd = str(event.get_message()).strip() #获取把命令头部分去除后的消息内容,也就是命令的具体内容。
    if cmd:   #如果命令的具体内容不为空,就将cmd存入state。
        state['cmd'] = cmd 

@test.got('cmd', '请输入命令的具体参数')
async def test_handle(bot: Bot, event: Event, state: T_State):
    print('我已获取到完整的命令,开始执行')
        
# v2b 注意新语法!
async def handle_first_receive(matcher: Matcher, args: Message = CommandArg()):
    plain_text = args.extract_plain_text()
    if plain_text:
        matcher.set_arg("cmd", args)  # 等同于 state['cmd'] = plain_text (注意args是Message)

事件处理器参数

常用的参数有:bot: Bot, event: Event, state: T_State, matcher: Matcher

在事件处理器函数中视情况使用

注意V2.0.0b1新语法,参考获取上下文信息

bot

最常用的方法为call_api,可以直接调用gocq的API以实现nb2不原生支持的功能。

简单例子

await bot.call_api('send_msg',**{
            'user_id' : user_id,
            'message' : message
        })
event

上报事件对象,可以获取到上报的所有信息,也是我们最常用的获取信息的途径。

session_id = event.get_session_id()
#群聊中:gruop_群号_qq号
#私聊中:qq号
qq_id = event.get_user_id() # 只获取qq号

此方法用于获取事件消息内容,包括但不限于文字、Unicode表情、QQ表情、图片等。 其中QQ表情、图片等将会用CQ码进行表示,可以参考gocq官方文档进行解析。

注意,on_command()时,传入的消息将自动去除“命令头”,仅保留后面的内容

msg = event.get_message()

其中,msg为用户的输入。 同时此方法也有其他的方法对消息进行处理,例如仅保留文字部分:

msg = event.get_message().extract_plain_text
# 或者
msg = event.get_get_plaintext()
state

状态字典,可以存储任意的信息,其中还包含一些特殊的值以获取 NoneBot 内部处理时的一些信息,如:

state["current_key"]: 存储当前 got 获取的参数名

state["_prefix"], state["_suffix"]: 存储当前 TRIE 匹配的前缀/后缀,可以通过该值获取用户命令的原始命令

事件处理方法

当我们使用注册一个名为xxx的事件响应器后,可以使用这些处理方法。

xxx.send()

向用户发送一条Message类型的消息,例如:

await xxx.send('你好')
await xxx.send(Message('[CQ:face,id=123]'))
xxx.pause()

这个函数用于结束当前事件处理函数,强制接收一条新的消息,然后运行下一个处理函数。

放弃本处理函数接下来的操作,强制获取新的消息用于下一个处理函数

await xxx.pause('请输入下一条信息')
xxx.reject()

发送一条消息给当前交互用户并暂停事件响应器,在接收用户新的一条消息后重新运行当前处理函数

此方法常用于与receive和got等配合,在用户输入不正确的时候重新要求输入。

注意避免无限reject(),否则只能等消息等待超时(可在.env中修改超时时间)

await xxx.reject('您输入的xxx信息不符合格式,请重新输入')
xxx.finish()

发送一条消息给当前交互用户,并结束当前事件响应器

此方法用于一个事件的结束,即使此后还有未启用的处理器也将停止。 不会影响事件的继续传播。

await xxx.finish('事件已经处理完毕')

4.插件加载

插件加载尤其要注意加载顺序(但存在相互调用关系时)

# 使用手动加载单个插件的形式
nonebot.load_plugin('nonebot_plugin_apscheduler')
nonebot.load_plugin('src.plugins.xmuer_search')
nonebot.load_plugin('src.plugins.xmuer_search')

# 不要使用 load_plugins
# 或者手动配置 load_from_toml

推荐阅读