该系列为用于QQ群聊天机器人的nonebot2相关插件,不保证完全符合标准规范写法,如有差错和改进余地,欢迎大佬指点修正。
前端:nonebot2
后端:go-cqhttp
插件所用语言:python3
前置环境安装过程建议参考零基础2分钟教你搭建QQ机器人——基于nonebot2,但是请注意该教程中的后端版本过旧导致私聊发图异常,需要手动更新go-cqhttp版本。
用户能够通过"计算"命令让bot完成对相应算式的计算,从而实现简单的对话式计算器。
缺点:python无法在异步执行时强行终止超时的计算工作,导致在用户使用例如6**6**6**6**6这样的连续幂计算,或者过于夸张的阶乘计算时,程序不会报错中断,而是会花很长的时间进行计算导致bot的响应卡死。
暂时没有很好的超时中止解决方法,使用asyncio库的超时报错在实装部署到nonebo2插件中时也并没有生效(连超时异常都没有捕获到),原因不明,目前只能禁止用户使用数量过大的阶乘和幂运算。
在plugins文件夹中新建一个文件夹calculator
,文件夹内目录结构如下:
|-calculator
|-img
|-think.gif
|-__init__.py
|-calculator.py
|-config.py
其中img
为用于存储发送的图片文件的文件夹,calculator.py
为程序主要代码的位置,config.py
用于存储配置项,__init__.py
为程序启动位置。
使用python自带的eval()方法运行字符串,必须事先import可能用到的函数,直接用变量获取运行后的返回值。
# 将字符串作为代码运行计算,并保留5位小数防止浮点数的尾数溢出
result = round(eval(msg), 5)
将信息字符串作为代码运行需要小心可能存在的注入攻击,因此使用正则匹配的形式只允许输入规范中的算式内容,对不在规范中的符号与函数拒绝执行。
# 将中文的括号与逗号都替换为英文的
msg = str(event.get_message()).strip().replace('\r\n','').replace('\n','').replace('(','(').replace(')',')').replace(',',',')
require = "(\d| |\+|\-|\*|/|\(|\)|abs\(|sqrt\(|factorial\(|%|E|e|\.|log\(|\,|j)*"
# 未通过正则匹配
if msg != re.match(require, msg, flags=0).group() or "**" in msg:
last_response = time.time()
await calculator.finish("错误:输入算式中含有非法格式,请使用[计算帮助]查询规范。"+MessageSegment.image(think_img))
Config.py
class Config:
# 记录在哪些群组中使用
used_in_group = ["131551175"]
# 插件执行优先级
priority = 10
# 计算最小间隔
cd = 10
# 管理员QQ号,管理员无视冷却cd
super_uid = ["673321342"]
# 允许的最大阶乘
factorial_limit = 100
__init__.py
from .calculator import *
calculator.py
from nonebot import on_command
from nonebot.typing import T_State
from nonebot.adapters import Bot, Event
from math import sqrt, factorial, log
import re
import os
from nonebot.adapters.cqhttp import MessageSegment
import time
from .config import Config
__plugin_name__ = 'calculator'
__plugin_usage__ = '用法:计算器。'
dir_path = 'file:///' + os.path.split(os.path.realpath(__file__))[0] + '/img/'
# 这是一张图片,会在击杀复读者时发送
think_img = dir_path + 'think.gif'
# 记录上一次响应时间, 初始化为开机时间-cd时间
last_response = time.time() - Config.cd
calculator_help = on_command("计算帮助", priority=Config.priority)
@calculator_help.handle()
async def handle_first_receive(bot: Bot, event: Event, state: T_State):
await calculator_help.finish(f'''关于计算模块的使用说明:
使用命令为"计算 [算式]"
命令响应冷却时间为:{
Config.cd}秒
警告:由于遭到大数据滥用,现已不支持幂运算!
警告:阶乘计算输入值不得超过{
Config.factorial_limit}!
目前支持的输入内容包括(由张3作为示例):
空格与换行符:不会影响计算
3:整数
3.3:小数
3+3j:虚数
3e3或3e-3或3E3:科学计数法表示的数字
():小括号
+-*/:四则运算或负号
abs(-3):绝对值
sqrt(3):平方根
//:整除
%:求余数
factorial(3):阶乘
log(3,3):对数''')
calculator = on_command("计算", priority=Config.priority)
@calculator.handle()
async def handle_first_receive(bot: Bot, event: Event, state: T_State):
global last_response
use_calculator = False
ids = event.get_session_id()
# 如果这是一条群聊信息
if ids.startswith("group"):
_, group_id, user_id = event.get_session_id().split("_")
if group_id in Config.used_in_group:
use_calculator = True
else:
user_id = ids
use_calculator = True
if use_calculator:
# 检测响应cd, 如果允许则继续,管理员无视冷却cd
if time.time() - last_response >= Config.cd or user_id in Config.super_uid:
# 将中文的括号与逗号都替换为英文的
msg = str(event.get_message()).strip().replace('\r\n','').replace('\n','').replace('(','(').replace(')',')').replace(',',',')
require = "(\d| |\+|\-|\*|/|\(|\)|abs\(|sqrt\(|factorial\(|%|E|e|\.|log\(|\,|j)*"
# 判断是否有过大阶乘输入
facts = re.findall("factorial\(\d*\)", msg)
facts = list(map(lambda x: int(x[10:-1]), facts))
large_fact = False
for fact in facts:
if fact > Config.factorial_limit:
large_fact = True
break
# 未通过正则匹配
if msg != re.match(require, msg, flags=0).group() or "**" in msg:
last_response = time.time()
await calculator.finish("错误:输入算式中含有非法格式,请使用[计算帮助]查询规范。"+MessageSegment.image(think_img))
# 如果阶乘输入过大
elif large_fact:
await calculator.finish("错误:输入算式中阶乘输入值过大,请使用[计算帮助]查询规范。" + MessageSegment.image(think_img))
# 通过匹配
else:
try:
# 将字符串作为代码运行计算,并保留5位小数防止浮点数的尾数溢出
result = round(eval(msg), 5)
except ZeroDivisionError:
last_response = time.time()
await calculator.finish("错误:除数为零。" + MessageSegment.image(think_img))
except:
last_response = time.time()
await calculator.finish("错误:输入算式并不是正确的算式。"+MessageSegment.image(think_img))
else:
last_response = time.time()
await calculator.finish("计算结果:" + str(result) + MessageSegment.image(think_img))
think.gif
nonebot2聊天机器人插件4:群聊与戳一戳响应chat