Nonebot创建聊天教学功能

Nonebot创建聊天教学功能

三月 08, 2023

起因

在阅读前辈代码的时候我发现前辈们会专门的分出一个类专门进行数据处理相关的操作,于是我便顺着这个想法改进了我的代码。

这使我项目代码的结构更为规整和简化,也提高了代码的可读性,在节省代码量的同时并没有影响工作效率。

一、实现简易的教学功能

构建思路:制造一个词库,无非就是,将获取的消息存入指定的json文件中,在需要响应消息的时候查询是否有相关的消息,如果有则回复。

实现:在BOT收到 @LOVE酱 教学-XX=XX 格式的消息时,会自动的分割消息,将教学内容和需要回复的词分开传参。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import os
import random
from nonebot.adapters.onebot.v11 import Event
from nonebot.adapters.onebot.v11.bot import Bot
from nonebot.plugin.on import on_command
from nonebot.rule import to_me
from love.db import JsonDB #这一段尤为重要,正是在这一段中引入了我们处理数据库的类

study = on_command(cmd='教学-', rule=to_me(), priority=20)


@study.handle()
async def handle_func(bot: Bot, event: Event):
QQ = event.get_session_id().split('_')[2] # 获取QQ号
message = str(event.get_message()).split('-')[1].split('=') # 事件消息内容
send = message[0]
Reply = message[1]

Save = JsonDB(QQ=QQ, send=send, Reply=Reply)
Save.Save()

await study.send('教学成功!(请等待主人审核哦~)')

实现操作json数据的类,起到心脏的作用。

将消息名作为文件名进行保存,再将消息存入json中,并记录时间和教学者的信息,

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class JsonDB():
def __init__(self, QQ, send, Reply):
self.QQ = QQ
self.send = send
self.Reply = Reply
#初始化

#保存消息的方法
def Save(self):
now = datetime.now()
data = {
"教学人": self.QQ,
"时间": now.strftime("%Y-%m-%d %H:%M:%S"),
self.send: self.Reply
}
#构建json消息段

with open(f"{curPathTwo + self.send}.json", "w") as json_file:
json.dump(data, json_file)
#将消息存入

解决了保存,那么如何响应呢。

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Reply = on_command(cmd='', rule=to_me(), priority=60)


@Reply.handle()
async def handle_func(bot: Bot, event: Event):
if len(str(event.get_message())) >= 1:

Load = JsonDB(QQ='', Reply='', send=event.get_message())

await Reply.send(Load.Load())

else:
message = ["你好,这里是LOVE酱,发送 帮助 可以查看我的功能哦!",
"你好呀,我是LOVE酱,一个专为铁锈战争服务的虚拟少女!",
"你好~有什么需要帮助的吗?"]
rnd = random.Random()
Num = rnd.randint(0, 2)

await Reply.send(message[Num])

上面这段代码的作用是:在被艾特的时候随机响应一段回复词,而检测到字符串的时候便将字符串传参,实例化类并调用我们的写的查询方法,以此得到我们的回复词。

查询方法的代码如下:

1
2
3
4
5
6
7
8
9
10
11
def Load(self):

try:
# 从JSON文件中读取数据并解码为Python对象
with open(f"{curPath + self.send}.json", "r") as json_file:
data = json.load(json_file)

return data[f"{self.send}"]

except:
return "LOVE酱现在还听不懂哦~可以教我吗?"

这里用到了一段异常处理:

  • 如果用户访问的文件存在,则会返回json中对应的键的值,也就是之前写入的字符串。

  • 如果用户访问的文件并不存在,则说明这个词汇并没有被教学过,同时会引发异常,方法就会返回 “LOVE酱现在还听不懂哦~可以教我吗?” 这段字符串。

二、实现简易的审核功能

写到这里可能就会有人会问了,教学实现了,要是被教学了奇怪的话怎么办呀?能不能写一个审核机制?

当然是可以的。

我实现审核机制的原理是,将新教学的词先暂放到一个文件夹里,这个文件夹称为待审核文件夹,而这个文件夹里面的词汇是待审核词汇。在Master同意某个词汇后,就将词汇转移到过审文件夹中,而我们的响应方法只会去响应过审后文件夹里的词汇。这样,便成功的实现了简易的审核机制。

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Examine = on_command(cmd='查看待审核词汇', priority=20)


@Examine.handle()
async def handle_func(bot: Bot, event: Event):
QQ = event.get_session_id().split('_')[2]

if int(QQ) != 3345483363:

await Examine.send("你不是LOVE的主人哦~")

else:

curPath = os.path.abspath(os.path.pardir) + "/LOVE/love/data/Examine/"
listFiles = os.listdir(curPath) # 获取文件目录
message = ''
QQ = ''

for i in range(0, len(listFiles)):
Load = JsonDB(QQ=QQ, Reply='', send=listFiles[i])
Text = listFiles[i].split('.')[0] + ':' + Load.ExamineLoad()
message += Text + '|'

await Examine.send(f"待审核的词汇如下:{message}")

以上代码中,我们通过查看待审核文件夹中的文件,将文件名和文件中键对应的值提取出来组成字符串。就可以看到待审核的教学词汇。

实现移动文件和读取审核文件夹内名称的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
curPathTwo = str(os.path.abspath(os.path.pardir) + "/LOVE/love/data/Examine/").replace('db/', '')


def ExamineLoad(self):
"""读取待审核文件夹内的文件名"""
with open(f"{curPathTwo + self.send}", "r") as json_file:
data = json.load(json_file)

return data[f"{str(self.send).split('.')[0]}"] # 将文件名返回

def MOVE(self):
"""将文件转移实现通过审核"""
try:
shutil.move(curPathTwo + self.send, curPath)
except:
os.unlink(curPath + self.send)
shutil.move(curPathTwo + self.send, curPath) # 发现文件重复则删除文件

实现让用户自行撤回提交错误的词汇的功能

为了减轻Master的工作量,也为了提高用户体验。

我们考虑到用户有时候可能打错字,便增加了一个撤回的功能。

如果没用这个功能,而用户又想要删去这个提交错误的词汇,差不多就只能联系Master了,这样子很不方便。

而在实现功能中,我们需要考虑一点,那就是,需要实现只有发送这个词汇的人才能撤回这个词汇。

还记得我们存储数据使用的格式吗?忘了没关系,我们一起看看:

1
2
3
4
5
{
"教学人": self.QQ,
"时间": now.strftime("%Y-%m-%d %H:%M:%S"),
self.send: self.Reply
}

在我们存储的每一个词汇的json文件里,我们有三个键,教学人、时间、以及接受词。分别对应QQ、时间、回复词。

而解决用户直接身份甄别的关键就在此处,那就是json中的教学人,只要我们将其于发送消息者的QQ进行对比,就可以看出这个词是不是教学人创建的。

ps: 有一个BUG可以卡,就是下一个人创建的词会覆盖上一个人的内容。)

话不多说,上代码:

1
2
3
4
5
6
7
8
9
10
11
12
recall = on_command(cmd='撤回-', rule=to_me(), priority=20)


@recall.handle()
async def handle_func(bot: Bot, event: Event):
QQ = event.get_session_id().split('_')[2] # 获取QQ号
message = str(event.get_message()).split('-')[1] # 事件消息内容
send = message

REC = JsonDB(QQ=QQ, send=send, Reply='')

await recall.send(REC.recall())

然后是在类中实现的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def recall(self):

try:
# 从JSON文件中读取数据并解码为Python对象
with open(f"{curPathTwo + self.send}.json", "r") as json_file:
data = json.load(json_file)

if self.QQ == data['教学人']:
os.remove(curPathTwo + self.send+'.json')
return '已成功撤回词汇'
else:
return '您并不是这个词汇的教学人哦~'
except:

return '您并没有提交过这个词汇哦~'