写在前面
从很早的时候看到别人的群里有能够自动回复的机器人开始,我就在思考这样的功能是如何实现的,那时还没有接触过编程,所以后来也就不了了之。
前一段时间我在网上看到了对qqbot这个库的介绍,兴趣起来以后我就尝试了两下,发现控制我的qq发消息竟然很有趣,所以我就进一步进行探索,想要做出来一个相对来说还算得上是比较有模有样的机器人。
功能介绍
到目前为止,大概做出了几个我比较感兴趣的功能,虽然比较丑陋,但总算是实现了。功能大概可以分为以下几个:
- 整句识别并回复;
- 字词识别并回复;
- 学习命令与回复;
- 天气查询;
- 签到系统;
- 积分系统。
整句识别并回复指的是,假如有个人发了消息A,那么如果A在我们的指令库中,机器人将会对其进行相应的回复。
字词识别并回复指的是,假如有个人发了消息A,消息A中包含了词语B,那么机器人将会对其回复B的内容。如果A中包含了多个相应词语,那么回复第一个。
学习命令与回复就是所有人可以通过一个特定的格式往指令库中添加命令。当然,因为防止别人乱搞,我这里设置的是只能够添加“整句识别并回复”的命令。
天气查询其实就很简单了,通过特定的格式获取想要查询的城市,然后利用爬虫爬取中国天气网上的相应数据,再回复就行。
签到系统和积分系统联系在一起,每个人每天只能签到一次,并会得到相应的积分,如果多次签到则会扣除定量的积分。
积分系统目前可以打劫(受一些比较成熟的机器人系统启发),其他的功能还没有开发出来,也不准备继续做了,所以相当于制作了一个雏形。
相关知识范围
本次内容最主要的便是qqbot库的用法,这里是qqbot的GitHub,具体的用法我就不再在我这里细讲了。
相比之我之前做的那个图片识别,用的csv文件来当作数据储存,这一次我新学习到了json存储必要的文件,比之前算是方便了许多。
其余的应该没有什么了,都是一些比较稀松平常的内容。
正文开始
假设诸位已经简单的了解过了qqbot的用法,也就是我们只需要自己定义一个消息回应函数onQQMessage(bot, contact, member, content),然后再其中编写我们想要的功能即可。
四个参数的意思我直接从GitHub中搬来
bot : QQBot 对象,提供 List/SendTo/Stop/Restart 等接口,详见本文档第五节
contact : QContact 对象,消息的发送者,具有 ctype/qq/uin/nick/mark/card/name 等属性
member : QContact 对象,仅当本消息为 群消息或讨论组消息 时有效,代表实际发消息的成员
content : str 对象,消息内容
优先级一,检查是否是自己
对于一个机器人回复,我们首先需要给这个机器人设定读取到信息后的处理的优先级,也就是相应动作的先后顺序。毫无疑问的,我们第一步必须要检测收到的这一条语句content的来源member是谁,如果member是自己的话,那么就不能回应,所以我们可以写以下代码
def onQQMessage(bot, contact, member, content):
#优先级一
'''!-------识别是否是自己的话-------!'''
if bot.isMe(contact, member):
print('自己的命令')
else:
#其他优先级
优先级二,检查是否是“主人”的消息
之所以设定一个主人,是为了能够随时“热关闭”机器人,不需要在命令行关闭。当然,也可以添加其他功能,只需要在这一个优先级内添加即可。所以这一个优先级主要是看“主人是否让机器人继续回复消息”。
#优先级二
masterFlag = MasterCommands(bot, contact, member, content)
if masterFlag == '1':
#其他优先级
这一个优先级的内容全部被放在函数内进行,函数返回值为0或者1。如果为0就代表主人将机器人关闭(或者处于关闭状态),机器人不需要回复;如果未1就代表主人将机器人开启(或者处于开启状态),机器人可以进行回复。
def MasterCommands(bot, contact, member, content):
'''!-------不反应或者反应-------!'''
masterFlagFile_name = 'C:\\Users\\vento\\Documents\\QQBotSentence\\MasterFlag.txt'
if member != None:
if member.qq == '798***079':
if content == '闭嘴吧!伊莉雅':
bot.SendTo(contact, '好的好的,我闭嘴:(')
with open(masterFlagFile_name, 'w') as file_obj:
json.dump('0', file_obj)
print('已修改为0')
elif content == '说话吧,伊莉雅':
bot.SendTo(contact, '我又能说话啦')
with open(masterFlagFile_name, 'w') as file_obj:
json.dump('1', file_obj)
print('已修改为1')
with open(masterFlagFile_name) as file_obj:
flag = json.load(file_obj)
print('已读取', flag)
return flag
因为机器人目前所处与的状态需要在每一次接收到消息的时候进行判定,所以我们将这个状态保存在本地。
masterFlagFile_name为状态值保存的文件地址(忽略我这混乱的命名规范),如果你的地点不同请务必要要创建一个txt文件,并在其中添加初始状态,并将masterFlagFile_name替换为你的地址。
初始状态为"1",注意一定要加上双引号,因为这个状态值是以字符存储的。
在上面这一段代码中,我们首先判断member是否为空,也就是说是否是在群里说的话,如果是的话进行下一步。其次判断member的qq号是否为主人的qq号,如果是的话再判断消息是否是关闭(开启)指令,如果是的话就对文件进行相应的修改。
修改则用json来对文件进行修改,对文件的读操作用json.load,写操作用json.dump。这两个用法从上面的代码就可以看得出来。
添加与删除指令
在确定masterFlag=="1"后,我在这里插入了一些关于指令的东西(我当初怎么想的恐怕只有天知道,为什么要在这里加,又懒的换位置怕出bug,所以就干脆这样子吧),主要是三个内容:添加指令,删除指令,字词识别指令库。
下面的代码中sentence1代表“不可被修改的整句识别指令语料库”,sentence2代表“可被修改的整句识别指令语料库”,otherSenList代表字词识别指令语料库。这三个语料库皆用字典存储,key为指令,value为回复。
'''!-------添加口令-------!'''
if 'Command Add' in content:
CommandAdd(bot, contact, member, content, sentence1)
'''!-------删除口令-------!'''
if 'Command Delete' in content:
CommandDelete(bot, contact, member, content, sentence1)
'''!-------字词识别指令字典存储-------!'''
otherSenList = {'苟':['唱诗班!预备,唱!', '富贵!勿相忘!', '全姓名于乱世!'],
'伊莉雅':['叫我干嘛?']}
添加删除指令有特定的形式,所以我就直接通过寻找这个特殊形式来调用相应的函数。这里我的添加指令的形式为
Command Add!
口令&你的口令&
回复&你的回复内容&
【如】
Command Add!
口令&吃了么&
回复&吃啦吃啦,可香呢&
删除的形式与之类似,为
Command Delete!
口令&要删除的口令&
【如】
Command Delete!
口令&吃了么&
之所以搞这么复杂就是防止聊天中稀奇古怪的形式出现,防止出现一些奇怪的bug。不过CommandAdd与CommandDelete这两个函数我做的不是很美观,个人感觉如果用正则表达式去直接匹配字符串,提取出两个&之间的内容可能会更简单一点,但是当初写的时候嫌麻烦,还需要重新学习一下正则,所以就干脆用if做了。
def CommandAdd(bot, contact, member, content, sentence1):
'''!-------添加口令-------!'''
flag = 0
key = ''
val = ''
for item in content:
if item == '&' and flag == 0:
flag = 1
continue
else: flag
key = key + item if flag == 1 and item != '&' else key
TouchFlag = CannotTouch(bot, contact, member, content, key, sentence1)
if TouchFlag == 1: break
if item == '&' and flag == 1:
flag = 2
continue
else: flag
if item == '&' and flag == 2:
flag = 3
continue
else: flag
val = val + item if flag == 3 and item != '&' else val
flag = 4 if item == '&' and flag == 3 else flag
if flag == 4:
sentence2[key] = val
bot.SendTo(contact, '添加成功~现在就可以试试啦!', resendOn1202=False)
break
else:
bot.SendTo(contact, '我好像没有收到正确格式噢', resendOn1202=False)
def CommandDelete(bot, contact, member, content, sentence1):
'''!-------删除口令-------!'''
flag = 0
key = ''
for item in content:
if item == '&' and flag == 0:
flag = 1
continue
else: flag
key = key + item if flag == 1 and item != '&' else key
TouchFlag = CannotTouch(bot, contact, member, content, key, sentence1)
if TouchFlag == 1: break
flag = 2 if item == '&' and flag == 1 else flag
if flag == 2:
sentence2.pop(key)
bot.SendTo(contact, '删除成功~', resendOn1202=False)
break
else:
bot.SendTo(contact, '我好像没有收到正确格式噢', resendOn1202=False)
我这里的做法其实很简单,就是遍历content字符串中所有字符,运用flag来确定当前遇到的&是第几个&,然后分别对key和val进行叠加,并将其保存到整句识别语料库之中。
我中间还运用了一个CannotTouch函数,指的是不能被修改的指令。毕竟如果所有指令都被别人修改的乱西八糟,那就很没意思了,这就相当于权限的问题。这个函数的实现代码如下
def CannotTouch(bot, contact, member, content, key, sentence):
'''!-------不可修改口令禁令-------!'''
flag = 0
if sentence.__contains__(key):
bot.SendTo(contact, '抱歉噢,这个指令不能够被修改的呢~')
flag = 1
return flag
小结
这一篇就先到这里,下一篇内容就就是开始了积分系统,签到系统,天气检测等(按照我这里设定的优先级划分的),语句识别在我这里是在最后面两个优先级,所以可能会晚一点才会写出来。
所有代码我都放在github中了,包括还没有写出来博客的内容,有需要可以自己看。
github地址:https://github.com/HanpuLiang/A-Example-of-qqbot
如果喜欢的话麻烦关注一下再点个赞或者喜欢噢!