前言

在这里插入图片描述
在这里插入图片描述

前面已经有不止一篇博客教大家如何搭建nonebot2环境了大家可以去专栏查看,这篇博客并不会再次带大家来搭建nonebot2环境,而是着手与插件的编写,也就是开始使用机器人处理QQ用户发来的消息。在说插件编写之前先带大家回顾一下Nonebot2机器人处理用户信息的整体过程。机器人后台接收消息时会经过如下流程:首先gocqhttp会从QQ服务器获取消息,然后通过接口将消息传输给Nonebot2,Nonebot2将消息处理的结果再发给gocqhttp端,最后gocqhttp根据发还的数据向QQ服务器发出消息,这就是消息收发与处理的整个流程。简单来说gocqhttp就是实现Nonebot2和QQ服务器之间通信的桥梁,也就是眼睛耳朵鼻子或者说身体的各个部位,而Nonebot2就是相当于"大脑",负责处理接收到的信息并且发送指令给手、脚这种身体部位让他们行动,今天要编写的插件就是大脑中的神经元,不同的神经元负责处理不同类型的消息。

接下来将分为三部分进行介绍,捕获消息就是用户的消息触发到Nonebot2的神经元,处理消息就是Nonebot这个大脑如何处理捕获到的消息,回复消息是指做出了什么行为。先结合一个实例观察一下:

from nonebot import on_keyword
# nonebot2中的适配器这么引入
from nonebot.adapters.onebot.v11 import Message

helloword=on_keyword({"hello"})

@helloword.handle()
async def _():
    await helloword.finish(Message("你好!"))

这段代码实现的功能是:如果有人发送(无论是与机器人的私聊还是群聊里)任一包含"hello"这个关键词的消息的时候,机器人会回复消息:“你好!”。

接下来的三部分将会由这段简短的代码进行展开叙述。在正式展开之前先对Nonebot2引入基本库进行一下说明。

from nonebot import on_keyword
from nonebot.adapters.onebot.v11 import Message

这段import代码的是从nonebot2库中引入on_keyword方法,再从Onebot V11库中引入Message方法,后面会介绍这两个有啥用。

Nonebot2中的很多功能都需要通过import导入后才能使用,这里列举了一些常见库里包含的常见函数:(大家只需先记住引入库从哪里引入即可)

常用函数/方法说明
noneboton_message,on_notice,on_request,on_keyword,on_command,on_regex基础库
nonebot.configConfig配置文件库
nonebot.matcherMatcher事件响应器
nonebot.paramsArg,State,CommandArgs,RegexMatched参数库
nonebot.permissionSUPERUSER,Permission权限库
nonebot.loglogger,default_format日志库
nonebot.adapters.onebot.v11Bot,Message,MessageSegment,Event,PRIVATE,GROUP等Onebot库

注:OneBot V11的库可以从nonebot.adapters.onebot.v11直接导入,也可以用诸如nonebot.adapters.onebot.v11.message的方式导入

捕获消息

helloword=on_keyword({"hello"})

这行代码的含义是“创建一个名为helloword的变量,其为一个类型为on_keyword的事件响应器”,这段代码的作用就是当用户发送的信息包含hello时就触发其绑定的函数,而helloword的作用仅仅是方便找到这个响应器。在此大家可以了解一下有关on_keyword的参数。

on_keyword(keywords, rule=..., *, permission=..., handlers=..., temp=..., priority=..., block=..., state=...) 注册一个消息事件响应器,并且当消息纯文本部分包含关键词时响应

  • 第一个参数是keywords,类型为集合(Set),传入的是用来触发该响应器工作的关键词集合(用{}包起来的)。

  • 第二个参数是rule,传入的是bool类型规则函数或者Rule类。

  • 第三个参数是permission,传入的是bool类型的权限函数或者Permission类。

  • 第四个参数是handlers,传入的参数是被事件响应器触发的函数列表。

  • 第五个参数是temp,传入的是bool类型参数,若为True则此响应器只会使用一次。

  • 第六个参数是priority,传入的是int型参数,用于设定响应器的优先级。注意!此参数越大优先级越低

  • 第七个参数是block,传入的是bool类型的参数,若为True则不会注册优先级小于此响应器的响应器,相当于消息被阻断在这里了。

  • 最后一个参数是state,传入的是字典类型参数,用于存储响应器状态信息。

目前需要了解的只有keywordsrule,permissionpriorityblock这四个参数,其他参数用法较为麻烦,我们之后才会讲到

  • 首先是介绍的是keywords参数,这个参数作用比较简单,就是传入一个元素是字符串的集合,然后如果接收到的消息包含这个集合内的任一关键字,则触发该事件响应器。

  • 第二个是rule参数,通过这个参数我们可以过滤掉想过滤掉的语句,有两种方式,一个是自定义规则,一个是使用官方的rule库。rule参数的本质其实就是一个bool类型的变量或者返回值类型为bool的函数,当rule值为True时即可以触发事件响应器,反之为False时则不会触发。或许有人会问在响应器处理函数当中判断是否符合条件和rule的功能是不是一样的?虽然作用上两者一样,但是ruler相当于从源头上"掐断"该事件响应器,而处理函数是从中途阻止。

    # 自定义规则函数(当@机器人时触发)
    from nonebot import on_keyword
    from nonebot.adapters.onebot.v11 import GroupMessageEvent
    def _checker(event: GroupMessageEvent) -> bool :
        return event.to_me
    helloword=on_keyword({"hello"},rule=_checker)
    # 使用官方的规则库(当@机器人时触发)
    from nonebot import on_keyword
    from nonebot.rule import to_me
    
    helloword=on_keyword({"hello"},rule=to_me())
    
  • 第三个是permission参数,这个参数负责传入能触发此事件响应器的消息发送者类型,也就是哪些人能触发这个响应器。一般来说重要的指令都会加上一些权限限制,诸如操作机器人后台的一些指令。常见的permission有SUPERUSER(写在.env文件中的超级用户),GROUP_ADMIN(群管理员)和GROUP_OWNER(群主),这些是框架本身提供的。除此以外我们也可以自定义权限组,当然这个内容之后再谈。

  • 第四个参数是priority,负责调控响应器触发的优先级,优先级越高,就越早被执行。举个例子,如有以下代码

    helloword=on_keyword({"hello"},priority=60)
    helloword_2=on_keyword({"hello"},priority=50)
    

    两个响应器都以"hello"作为触发词,这时候如果接收到了包含"你是谁"的消息后,二者执行顺序就由priority值来决定了。注意!priority值越小,优先级越高!如上面那个例子中helloword_2就会比helloword要更早触发。

    最后一个介绍的是block参数,这个参数一般情况下是和前面那个priority参数搭配起来用,用以阻断消息传递。还是以上面那段代码为例

    helloword=on_keyword({"hello"},priority=60,block=True)
    helloword_2=on_keyword({"hello"},priority=50,block=True)
    

    用户输入"hello"后并不会触发两个响应器,而是只会触发优先级更高的那个也就是helloword_2,触发完之后因为block值为True,所以消息传到这里就被阻断了,也就没办法触发优先级更低的响应器了。

处理消息

处理消息这部分需要先知道用户发送的什么然后才能处理,所以我们需要先了解Nonebot2的特性—依赖注入。

事件响应器函数常用的传入参数一般有以下三种:bot(机器人)event(事件)以及args(参数),不过除此之外还有state,match等。这里我们先只讲前两个,第三个args参数我们暂时还用不到。

Bot机器人参数

首先是第一种参数:bot,bot类型的传入参数类就只有一个:Bot,下面是使用样例

from nonebot import on_keyword
from nonebot.adapters.onebot.v11 import Bot
helloword=on_keyword({"hello"})
@helloword.handle()
async def _(bot: Bot):
    await helloword.finish(str(bot.self_id))

在上述代码中可以看到,Bot作为一个参数传入事件响应器函数当中使用。Bot是Nonebot库提供的一个类,当使用机器人的时候会自动创建一个Bot类,然后我们可以通过这个类来调用与机器人交互的各种属性与方法。比如上面例子当中的self_id就是Bot类的一个属性,返回的数据是当前bot的QQ号,类型为int(所以才要转换为str类型,不然会报参数类型不匹配的错误,因为finish方法不支持传入int型参数)。

Bot类有如下的属性

属性名返回值类型说明
self_idint(整数型)机器人自己的QQ号
adapteradapter(协议适配器)返回机器人所用的协议适配器

还有一些个人认为的Bot中比较常用的方法,篇幅有限其他方法以及具体的参数传递就不讲了,可以自行翻阅文档

方法名作用
send_msg发送(群聊/私聊)消息
delete_msg撤回消息
set_group_ban设置群组禁言
set_friend_add_request设置好友验证是否通过
set_group_add_request设置加群验证是否通过
get_login_info获取登录信息
get_group_member_info获取指定群成员信息
get_group_honor_info获取群荣誉(龙王等)信息

Event事件参数

接下来是第二种参数:event事件参数,event类型其实包含很多类:如Event,GroupMessageEvent,PrivateMessageEvent等等。Event类一些方法/属性和Bot类重复,比如self_id等,不过由于Event类下的方法/属性相较于Bot类更多,且通用性更好,所以要获取的信息相同时一般常用Event类来代替Bot类。下面列举了一些常见的Event类

类名说明
Event事件
GroupMessageEvent群消息事件
PrivateMessageEvent私聊消息事件
NoticeEvent通知事件
GroupUploadNoticeEvent群文件上传事件
GroupAdminNoticeEvent群管理员变动事件
GroupDecreaseNoticeEvent群人数减少事件
GroupIncreaseNoticeEvent群人数增加事件
GroupBanNoticeEvent群管理员禁言事件
FriendAddNoticeEvent好友增加事件
GroupRecallNoticeEvent群消息撤回事件
FriendRecallNoticeEvent私聊消息撤回事件
NotifyEvent提醒事件(这个文档没翻到)
PokeNotifyEvent群戳一戳事件
HonorNotifyEvent群荣誉变更事件
LuckyKingNotifyEvent群红包运气王事件
RequestEvent请求事件
FriendRequestEvent好友申请事件
GroupRequestEvent入群申请事件

虽然没全部列举出来,但是这些类目前来说已经完全够用了,如何知道它们下面有哪些属性和方法呢?有两种方法:一是查阅官方文档,二是在支持代码补全的编辑器中输入对应的类名后再加上.就可以跳出对应的提示,然后可以根据语法提示选择需要的方法/属性就行了。

最后再附上一个具体例子

from nonebot import on_keyword
from nonebot.adapters.onebot.v11 import Message,GroupMessageEvent

helloword=on_keyword({"hello"})

@helloword.handle()
async def _(event:GroupMessageEvent):
    if(event.user_id==123456 and event.group_id==11111):
        await word.finish(Message("OK!"))

此段代码作用:当群聊11111中里有一个QQ号为123456的人发送包含“hello”这个词的句子时,bot会发送“OK”。

回复消息

最后我们要解决的问题是:如何将处理好的消息发送出去,在此之前我们需要了解一下Onebot的消息类型。上面我们曾提到helloword.finish()方法可以传入四种参数:

  • 字符串(Str)

  • 消息(Message)

  • 格式化消息模板(MessageTemplate)

  • 消息段(MessageSegment)

    其中格式化消息模板用法比较特殊,暂且避开不谈,只讲剩下三种。

字符串与Message

字符串(Str)与消息(Message)大部分情况下可以等价,可以替代Message使用,但是有一种特殊情况:消息内包含CQ码的时候只能使用Message,不过个人建议还是统一使用Message来进行消息发送。

CQ码是go-cqhttp协议中的一种特殊的消息字段,用以发送QQ中的特殊消息(如图片、语音、@等等),常见的CQ码格式为[CQ:类型,参数=值,参数=值],如下面的例子就是CQ码的一种使用

举出一个CQ码回复消息的例子:

@word.handle()
async def _():
    await word.finish(Message("[CQ:share,url=https://www.baidu.com,title=百度]"))

上述代码发送了一个百度的链接(不是以文字链接的方式),而是一个可以点击的类似“小程序”的执行框,点击后可以直接跳转。

(因为QQ接口的问题,此CQ码有时候会无法使用,会报错"消息可能被风控",请尝试其他的CQ码。)

这里我列举了一些常见的CQ码,全部的CQ码请查阅go-cqhttp文档或者OneBot文档

类型参数作用
faceid=表情ID发送QQ内的表情
recordfile=文件路径/URL发送语音(一般为mp3格式)
atqq=QQ号,(name=昵称)@某人(不在群里时可以使用name参数)
shareurl=网站链接,name=网站名发送网站分享
musictype=歌曲平台,id=歌曲ID发送音乐
image参数较多,参见文档发送图片
replyid=回复的消息ID,text=消息内容发送回复消息

调用MessageSegment接口

除了CQ码之外还有一种叫消息段(MessageSegment)的东西,可以实现与CQ码相同的功能,并且还可以做一些CQ码无法做到的事情。此外官方推荐使用消息段来替代CQ码(原因:CQ码不方便+容易被注入),后续案例也都会使用消息段。用法如下

@word.handle()
async def _():
    await word.finish(MessageSegment.share(url="https://www.baidu.com",title="百度"))

此段代码效果与CQ码回复无异。使用MessageSegment方式发送特殊消息更加方便明了

提示:MessageSegment和Message可以通过"+"直接拼在一起,形成"文字+图片"类型的消息,另外Message之间也可以相互拼接。Message相当于一个数组,可以容纳一定数量的同类(Message)以及MessageSegment

最后再回顾一下使用CQ码回复消息与使用消息段回复消息的区别:

CQ码

from nonebot import on_keyword
from nonebot.adapters.onebot.v11 import Message,GroupMessageEvent
from nonebot.rule import to_me

helloword=on_keyword({"hello"},rule=to_me())

@helloword.handle()
async def _(event: GroupMessageEvent):
    await helloword.finish(Message(f"[CQ:at,qq={event.user_id}],你@我了!"))

消息段(推荐)

from nonebot import on_keyword
from nonebot.adapters.onebot.v11 import Message,GroupMessageEvent,MessageSegment
from nonebot.rule import to_me

helloword=on_keyword({"hello"},rule=to_me())

@helloword.handle()
async def _(event: GroupMessageEvent):
    await helloword.finish(MessageSegment.at(event.user_id))
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐