封面 PID 为 55325876

大家好啊,我是说的938,今天来教大家怎么写最简单的 NB2 插件

首先看这篇文章,即默认你已配置好 Python 环境和 NoneBot 2,并且有一双健全的手和智力正常的脑子。请注意我也是新手小白,很多地方我也不懂。这篇文章只是分享一下自己的经验,如有错误、描述不准确的地方还请指正,勿喷

我默认所有看这篇文章的人绝大多数都只有对接 QQ 的需求,所以本文只使用了 OneBot V11 适配器

这篇文章只会教一些简单的东西,不会教如何发布你的插件

由于我的文笔不好,加上我也是小白,所以可能会有很多表达不准确的地方

配置插件目录

因为我们不是用 pip 装的插件,而是自己写的,所以肯定需要让 NB 加载一个指定目录下的插件

我们只需要编辑pyproject.toml,在[tool.nonebot]下加入这一行配置即可:

1
plugin_dirs = ["plugins"]

这么做之后,我们以后只需要把自己编写的插件丢进 bot 目录下的plugins文件夹就能让 NB 识别到并自动加载了

Hello World

先从最简单的开始——让你的 bot 注册一个指令并返回 Hello World

我们在插件目录plugins下创建一个.py文件,命名随意 (最好不要包含中文)

然后第一步要干什么?当然是先import个库进去

1
from nonebot import on_command

在这个代码中,我们从nonebot导入了on_command这个函数,它是一个命令触发器

然后就是新建一个事件响应器:

1
cmd = on_command("test",priority=10,block=True)

其中,test可以看作是指令的名字,在你向 bot 发送/test之后,这个事件响应器便会触发。对于其他参数的解释,priority表示优先级为 10,block表示这个指令会屏蔽其他低优先级指令

接下来,编写一个触发函数:

1
2
3
@cmd.handle()
async def main():
await cmd.finish(message="hello world!",reply_message=True)

这个函数会在发送/test指令触发,并且会回复用户的原消息,发送 “hello world!”,如果不想回复而是直接发送,那把reply_message改为False或者直接删掉

为什么要加个@cmd.handle()呢?因为这玩意是个装饰器,可以把上面的on_command...比作是门铃,这个handle比作是门铃的处理装置,而下面的async def...是你家 (不知道我这样比喻贴不贴切)

最后,你的这个.py文件看起来应该是这样的:

1
2
3
4
5
6
7
from nonebot import on_command

cmd = on_command("test",priority=10,block=True)

@cmd.handle()
async def main():
await cmd.finish(message="hello world!",reply_message=True)

如果没问题的话,启动 NB,向你的 bot 发送/test,它应该会回复你 “hello world!”

发送多条消息

如果我们想让 bot 一次性发送多条消息呢?很简单,只需要再加一条cmd.send即可:

1
2
3
4
5
6
7
8
from nonebot import on_command

cmd = on_command("test",priority=10,block=True)

@cmd.handle()
async def main():
await cmd.send(message="hi",reply_message=True)
await cmd.finish(message="hello world!",reply_message=True)

这样,你的 bot 会在触发指令之后发送 “hi” 和 “hello world!” 这两条消息

其实,在这段代码中,把最后一行的finish改成send也是可以的,因为后边没有代码。但是如果你后边有代码的话,还不想因为这个指令被执行最好还是改成finish,不然可能会因为事件响应器没有结束而导致后续代码继续执行

发送图片

有时候仅靠发文字是远远无法满足我们的需求的 (比如发涩图),这时候就得发图片了

这个时候就该请MessageSegment上场了,它可以生成一个消息段。运用这个类,我们可以发送图片、语音、QQ 自带的表情、卡片、视频、聊天记录等一大堆东西

发送图片是MessageSegment.image(),其参数可以是本地图片文件的路径,也可以是图片链接

为了调用它,我们需要从nonebot.adapters.onebot.v11导入MessageSegment

1
2
3
4
5
6
7
8
from nonebot import on_command
from nonebot.adapters.onebot.v11 import MessageSegment

cmd = on_command("test",priority=10,block=True)

@cmd.handle()
async def main():
await cmd.send(message=MessageSegment.image("https://s2.loli.net/2025/04/05/XK5bL6xmYW98kRA.webp"))

效果如下:

如果有在一条消息内发送多张图片的需求呢? 很简单,直接+拼接起来即可:

1
2
3
@cmd.handle()
async def main():
await cmd.send(message=MessageSegment.image("https://s2.loli.net/2025/04/05/XK5bL6xmYW98kRA.webp")+MessageSegment.image("https://s2.loli.net/2025/04/05/XK5bL6xmYW98kRA.webp"))

如果我不想使用这种方法插入图片,而是用变量插值 ({}) 的方法插入图片或者说想发送变量的内容呢?
很简单,只需把 message 用 Message 包裹起来即可:

1
2
3
4
5
6
7
8
9
10
11
12
@cmd.handle()
async def main():
await cmd.send(message=Message(f"{MessageSegment.image("https://s2.loli.net/2025/04/05/XK5bL6xmYW98kRA.webp")}\nhoshino"))

# or

msg=f"{MessageSegment.image("https://s2.loli.net/2025/04/05/XK5bL6xmYW98kRA.webp")}\nhoshino"

@cmd.handle()
async def main():
await cmd.send(message=Message(msg))

在 NoneBot 中,消息序列 Message 的主要作用是用于表达“一串消息”。由于消息序列继承自 List[MessageSegment],所以 Message 的本质是由若干消息段所组成的序列。因此,消息序列的使用方法与 List 有很多相似之处,例如切片、索引、拼接等。
https://nonebot.dev/docs/tutorial/message

让 bot 获取消息内容

有时候,我们需要通过用户输入的指令的参数来给用户提供他们所需的内容,比如查天气,/天气 北京

首先需要获取用户发送的消息原文,我们可以使用event.get_plaintext()。在使用这个函数之前,需要先为main传入Event

1
2
3
4
5
6
7
8
9
from nonebot import on_command
from nonebot.adapters.onebot.v11 import Event # 注意这里 import 了个 Event

cmd = on_command("test",priority=10,block=True)

@cmd.handle()
async def main(event:Event):
user_input=event.get_plaintext()
await cmd.send(message=f"你发送了: {user_input}")

这么写之后,当用户触发了指令,bot 会回复 “你发送了: /test”,如果用户发送的是 “/test 114514” 这种带指令参数的,bot 也能正常回复 “你发送了: /test 114514”

那如果只想获取用户输入的指令参数也是很简单的,只需要调用 Python 原生的split()方法对消息内容进行切割即可:

1
2
3
4
5
@cmd.handle()
async def main(event:Event):
user_input = event.get_plaintext()
args = user_input.split()
await cmd.send(message=f"你输入的指令参数: {args[1]}")

想在用户直接发送指令的情况下发送这个指令的帮助?那很简单,用if判断不就行了:

1
2
3
4
5
6
7
8
@cmd.handle()
async def main(event:Event):
user_input = event.get_plaintext()
args = user_input.split()
if len(args) > 1 and args[1]:
await cmd.finish(message=f"你输入的指令参数: {args[1]}")
else:
await cmd.finish(message="命令语法不正确")

如果要获取第二个指令参数的话,那就用args[2],以此类推。但是如果你的指令只需要一个参数并且这个参数内容还可能会有空格的话,那就得这么写了:

1
2
3
4
5
6
@cmd.handle()
async def main(event:Event):
user_input = event.get_plaintext()
args = user_input.split()
arg = ' '.join(args[1:])
await cmd.finish(message=f"你输入的指令参数: {arg}")

这样,当用户发送/chat 你 好这种指令时,bot 会回复 “你输入的指令参数: 你 好” 而不是 “你输入的指令参数: 你”

让 bot 获取图片内容

上面这一个章节的代码只能够获取纯文本消息,但如果我们要获取图片呢?

nonebot.adapters.onebot.v11.helpers中提供了一个extract_image_urls函数,它能够帮我们提取消息中的图片链接

extract_image_urls会返回一个列表,包含了这条消息中所有的图片链接

1
2
3
4
5
6
7
8
9
10
from nonebot import on_command
from nonebot.adapters.onebot.v11 import Event
from nonebot.adapters.onebot.v11.helpers import extract_image_urls

cmd = on_command("test",priority=10,block=True)

@cmd.handle()
async def main(event:Event):
pic_urls=extract_image_urls(event.get_message())
await cmd.send(message=f"你的消息中包含了以下图片: {pic_urls}")

效果如图:

有了图片链接一切就好办了,接下来怎么写完全看你了

禁言、撤回、设精操作

与 禁言、撤回、精华消息、踢人 等操作的实现在nonebot.adapters.onebot.v11Bot这个类,除了上述操作,利用这个类提供的函数我们还可以实现 bot 退群、点赞个人资料、改群名、设置群管理等

为了实现这些操作,需要再为main函数传入一个Bot

1
2
3
4
5
6
7
8
9
10
11
from nonebot import on_command
from nonebot.adapters.onebot.v11 import Event, Bot

cmd = on_command("test",priority=10,block=True)

@cmd.handle()
async def main(event:Event, bot:Bot):
# 禁言
await bot.set_group_ban(group_id="群组 ID (即群号)", user_id="用户 ID (即 QQ 号)", duration="禁言时长 (单位: 秒)")
# 踢人
await bot.set_group_kick(group_id="群组 ID", user_id="用户 ID", reject_add_request="以后拒绝此人的加群请求 (true/false)")

以上是如何禁言和踢人的例子,如果你想整一个发送/test就把这个发指令的人禁言的话,那么需要用到eventget_user_id方法来获取发送者的用户 ID (QQ 号),用group_id来获取群号

1
2
3
4
5
6
7
8
9
10
11
from nonebot import on_command
from nonebot.adapters.onebot.v11 import Event, Bot

cmd = on_command("test",priority=10,block=True)

@cmd.handle()
async def main(event:Event, bot:Bot):
# 禁言
await bot.set_group_ban(group_id=event.group_id, user_id=event.get_user_id(), duration=60)
# 踢人
await bot.set_group_kick(group_id=event.group_id user_id=event.get_user_id())

上面的代码会把发送指令/test的用户禁言 1 分钟移出群聊

未完待续…

实战

通过以上部分的学习,相信你已经对 NB2 插件的编写有了一定的初步了解,接下来让我们写几个插件试试

接入 OpenWeatherMap 查询天气

未完待续…

利用 RCON 实现 MC 服务器自助添加白名单

未完待续…

用 GPT+OpenAI 库来实现图中人物颜值打分

未完待续…


本站由 Creeper938 使用 Stellar 1.33.0 主题创建。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。

本站已运行 ...
本站总访问量 | 总访客数