nonebot2聊天机器人插件2:调色盘palette
该插件涉及知识点:获取发送者信息,使用PIL与numpy创建图片,bot发送图片,获取程序所在路径,设置应答冷却时间
插件合集:nonebot2聊天机器人插件
该系列为用于QQ群聊天机器人的nonebot2相关插件,不保证完全符合标准规范写法,如有差错和改进余地,欢迎大佬指点修正。
前端:nonebot2
后端:go-cqhttp
插件所用语言:python3
前置环境安装过程建议参考零基础2分钟教你搭建QQ机器人——基于nonebot2,但是请注意该教程中的后端版本过旧导致私聊发图异常,需要手动更新go-cqhttp版本。
1. 插件用途
用户能够使用“调色盘”命令并且提供RGB颜色参数,让bot自动生成对应色彩的图片作为回应。
命令支持两种形式,一种为3个以空格为分隔的十进制整数,另一种为以#开头的6位十六进制颜色码。
程序需要具备在输入参数错误时的报错能力,只在指定配置的群组中工作,并且拥有一个最短响应cd,在每一次应答后开始计算cd,cd期内不对命令进行回应,以防止群组内同时发送命令的人员过多造成混乱。
配置文件中能够配置该插件的超级用户组【注:不同于bot的管理员用户】,超级用户的命令可以无视冷却cd直接生效。
2. 目录结构
在plugins文件夹中新建一个文件夹palette
,文件夹内目录结构如下:
|-palette
|-temp
|-__init__.py
|-palette.py
|-config.py
其中temp
为用于存储临时文件的空文件夹,palette.py
为程序主要代码的位置,config.py
用于存储配置项,__init__.py
为程序启动位置。
3. 实现难点与解决方案
3.1 获取发送者信息
通过event.get_session_id()
能够获得一个id信息,如果该信息为私聊信息,那么返回值为该用户QQ号。如果该信息为群聊信息,那么返回值为group_群号_该用户QQ号。
为了方便起见,将私聊信息的QQ号前手动添加private_
作为group_id,用于检验对应的响应cd。
分割并获得id的代码如下:
# 是否允许使用这个插件
allow_use = True
# 如果这是一条群聊信息
if ids.startswith("group"):
_, group_id, user_id = event.get_session_id().split("_")
# 对于不在配置文件允许列表里面的群组,拒绝访问
if group_id not in Config.used_in_group:
allow_use = False
# 如果这是一条私聊信息
else:
user_id = ids
group_id = 'private_' + ids
3.2 使用PIL与numpy创建图片
当获取到RGB三色的参数后,使用numpy库创建一个三维矩阵作为图像数据,然后使用PIL库将创建的图片保存到本地。
# 根据输入的三原色数据转换为500x500x3的numpy矩阵
color = np.array([R, G, B], dtype=np.uint8)
img = np.tile(color, (500, 500, 1))
img = Image.fromarray(img)
# 保存图片到临时文件夹
img.save(img_path)
3.3 bot发送图片
使用nonebot2与go-cqhttp从本地磁盘上发送图片时,必须在图片的路径前面加上file:///
,具体写法如下:
from nonebot.adapters.cqhttp import MessageSegment
await palette.send(MessageSegment.image('file:///'+img_path))
3.4 获取程序所在路径
不论是保存图片还是发送图片,为了方便都应该使用相对路径比较友好,而在任何情况下都能获取当前python代码文件所在路径,并且将图片路径设定到temp文件夹内的写法如下:
img_path = os.path.split(os.path.realpath(__file__))[0] + '/temp/temp_img.png'
3.5 设置应答冷却时间
设置一个字典用于储存所有群的上一次应答时间戳,字典的key为以group或者private开头的字符串,在每次响应时计算已经过去的时间是否大于最短cd。
# 记录上一次响应时间
last_response = {}
# 判断是否过了响应cd的函数,默认使用配置文件中的cd
# 如果已经超过了最短响应间隔,返回True
def cool_down(group_id, cd = Config.cd):
global last_response
if group_id not in last_response:
return True
else:
return time() - last_response[group_id] > cd
4. 代码实现
Config.py
class Config:
# 记录在哪些群组中使用
used_in_group = ["131551175"]
# 插件执行优先级
priority = 10
# 接话冷却时间(秒),在这段时间内不会连续两次接话
cd = 15
# 管理员QQ号,管理员无视冷却cd
super_uid = ["673321342"]
__init__.py
from .palette import *
palette.py
from nonebot import on_command
from nonebot.typing import T_State
from nonebot.adapters import Bot, Event
from nonebot.adapters.cqhttp import MessageSegment
from PIL import Image
from .config import Config
import os
import numpy as np
from time import time
__plugin_name__ = 'palette'
__plugin_usage__ = '用法: 调色板,根据输入的BGR值返回彩色图片。'
# 记录上一次响应时间
last_response = {}
# 判断是否过了响应cd的函数,默认使用配置文件中的cd
# 如果已经超过了最短响应间隔,返回True
def cool_down(group_id, cd = Config.cd):
global last_response
if group_id not in last_response:
return True
else:
return time() - last_response[group_id] > cd
img_path = os.path.split(os.path.realpath(__file__))[0] + '/temp/temp_img.png'
palette = on_command("调色盘", priority=Config.priority)
@palette.handle()
async def handle_first_receive(bot: Bot, event: Event, state: T_State):
ids = event.get_session_id()
# 是否允许使用这个插件
allow_use = True
# 如果这是一条群聊信息
if ids.startswith("group"):
_, group_id, user_id = event.get_session_id().split("_")
# 对于不在配置文件允许列表里面的群组,拒绝访问
if group_id not in Config.used_in_group:
allow_use = False
# 如果这是一条私聊信息
else:
user_id = ids
group_id = 'private_' + ids
# 如果允许使用
if allow_use:
# 如果已经过了冷却时间,或者用户是管理员
if cool_down(group_id) or user_id in Config.super_uid:
# 更新cd时间
last_response[group_id] = time()
msg = str(event.get_message()).strip()
# 如果输入为16进制且位数不合规
if msg.startswith("#") and len(msg) != 7:
await palette.finish("输入的RGB数值不符合规范,请使用[调色盘帮助]查询")
try:
# 如果输入为16进制
if msg.startswith("#"):
# 拆解16进制输入
R, G, B = int(msg[1:3], 16), int(msg[3:5], 16), int(msg[5:], 16)
# 如果输入为10进制
else:
R, G, B = msg.split(' ')
R, G, B = int(R), int(G), int(B)
except:
await palette.finish("输入的RGB数值不符合规范,请使用[调色盘帮助]查询")
if min(R, G, B) >= 0 and max(R, G, B) <= 255:
# 根据输入的三原色数据转换为500x500x3的numpy矩阵
color = np.array([R, G, B], dtype=np.uint8)
img = np.tile(color, (500, 500, 1))
img = Image.fromarray(img)
# 保存图片到临时文件夹
img.save(img_path)
# 发送图片
await palette.send(MessageSegment.image('file:///'+img_path))
# 删除临时文件夹中的图片
os.remove(img_path)
else:
await palette.finish("输入的RGB数值不符合规范,请使用[调色盘帮助]查询")
palette_help = on_command("调色盘帮助", priority=Config.priority)
@palette_help.handle()
async def handle_first_receive(bot: Bot, event: Event, state: T_State):
await palette_help.finish(f"""调色盘指令说明:
指令使用cd为{Config.cd}秒,有两种不同的使用方式。
1. 调色盘 R G B——RGB为0-255的整数对应红绿蓝三原色,以空格分隔
2. 调色盘 十六进制颜色码——十六进制颜色以#开头,如'#ff0000'""")
5. 插件配图
该插件无配图