前言※
起因是想做一个能够每天定时在群里发送一些工作通知和预警。钉钉上有基于 webhook 的机器人,可是钉钉的使用频率远不及微信。所以想着在微信上实现一个自动通知的机器人。
效果※
定时在群里发送今日值班通知,并且@相关人员:
记录备忘并且按时提醒:
选用方案※
查找资料后发现,由于腾讯对 web 登录的限制,大部分基于 web 的方案都失效了,目前可以实现微信机器人的方式有 hook 和模拟 gui 操作。
出于安全性和稳定性角度我选择了使用 gui 方式,github 上有人分享了思路 cluic/wxauto,我们可以直接站在前人的肩膀上针对自己的业务流程进行开发。
准备工作※
下载微信的最新版本客户端,copy 一份 v3 版本的代码到本地。
本方案的原理是模拟 GUI 点击,所以还需要一台 24 小时运行的服务器以及一个始终保持登录状态的微信小号。
实现值班通知※
首先从数据库中获取今日值班表
from MysqlDb import MysqlDb
def sendTodaySche():
mysqldb = MysqlDb("schedule")
thisMonth = datetime.today().__format__("%Y-%m")
today = datetime.today().__format__("%m-%d").split('-')
result = mysqldb.sql_select("SELECT * FROM `" + thisMonth + "` WHERE date = '" + today + "'")["data"]
msgs = ["今日值班人员如下:"]
for i in range(0,len(tempMsgs)):
if '、' in result[0][i+2]:
splitStrs = result[0][i+2].split("、")
temp = []
for k in range(0,len(splitStrs)):
if i == 0 and k == 1:
temp.append("、"+splitStrs[k])
else:
temp.append("@"+splitStrs[k])
temp[0] = tempMsgs[i]+temp[0]
msgs.append(temp)
else:
msgs.append(tempMsgs[i] + '@' + result[0][i+2])
从数据库中获取并整理的是一串数组。其中,如果该班次为单人,则格式为「@人名」 ,如果某个班次有两名以上的值班人员,则格式为数组「['@人名','@人名']」。
加上「@」的目的是在群聊中能够@到相对应的人员,是一个强提醒。
获取完数据之后需要向群聊中发送消息,但 wxAuto 库中的默认方法并不适合我的需求。
观察微信 pc 版发送消息的逻辑可知,发送换行消息需要按下「shift+enter」键,如果要@某人,则需要在输入人名后再次按下「enter」键才会成功,所以需要增加一个方法,如下:
def SendWrapAndATMsg(self,msgs, clear=True):
'''向当前窗口发送换行消息和@消息
msgs : 要发送的消息列表
clear : 是否清除当前已编辑内容
'''
self.UiaAPI.SwitchToThisWindow()
if clear:
self.EditMsg.SendKeys('{Ctrl}a', waitTime=0)
for i in range(0,len(msgs)):
if type(msgs[i]) ==type([]):
for k in range(0,len(msgs[i])):
self.EditMsg.SendKeys(msgs[i][k], waitTime=0)
if i == 1 and k == 1:
pass
else:
self.EditMsg.SendKeys('{Enter}', waitTime=0)
self.EditMsg.SendKeys('{Shift}{Enter}', waitTime=0)
else:
self.EditMsg.SendKeys(msgs[i], waitTime=0)
if i > 1:
self.EditMsg.SendKeys('{Enter}', waitTime=0)
self.EditMs 备 g.SendKeys('{Shift}{Enter}', waitTime=0)
self.EditMsg.SendKeys('{Enter}', waitTime=0)
上述新增的方法可实现我的需求,所以在`sendTodaySche()`
方法中继续添加如下代码,调用我们新增的方法:
wechat = WeChat()
wechat.ChatWith("群聊名称")
wechat.SendWrapAndATMsg(msgs=msgs)
接着使用schedule
库实现每日定时通知:
schedule.every().day.at("07:29").do(sendTodaySche)
schedule.run_pending()
实现备忘通知※
记录备忘※
首先需要定义一个方法持续监听与自己对话框。
def listening(who = '东东'):
wechat = WeChat()
wechat.ChatWith(who)
lastMsg = []
While True:
newMsg = wechat.GetLastMessage
if newMsg[0]=='东东' and lastMsg != newMsg:
pass
time.sleep(1)
在数据库中建立一个表格用于记录提醒事项
- Id 索引
- reminder 事项名称
- todoDate 提醒日期
- todoTime 提醒时间
- isDone 是否已经提醒
- earlyReminder 提前通知
我定义的提醒的消息格式为:
<日期><时间>【提醒我】<干什么事情>
比如:
- 明天上午 9 点提醒我买菜
- 1 月 23 日 17 点 20 分提醒我收拾行李
首先要按照我们的需求对接受的消息进行格式化:
# 格式化消息
def formatMsg(msgs):
'''格式化消息
return:事项、日期、时间
'''
datestr1=["今","明","后天","大后天"]
splitMsgs = msgs.split("提醒我")
nowDate = datetime.today().date()
todoDate = str(nowDate + timedelta(days=0))
todoTime = "08:00"
todoThing = splitMsgs[-1]
for i in range(0,len(datestr1)):
if datestr1[i] in splitMsgs[0]:
todoDate = str(nowDate + timedelta(days=i))
break
reDate = re.search(r'\d{1,2}月\d{1,2}',splitMsgs[0])
if reDate != None:
nowYear = str(datetime.today().date().year)
temp = str(reDate.group(0)).split("月")
if len(temp[0]) == 1:
temp[0] = '0'+temp[0]
if len(temp[1]) == 1:
temp[1] = '0'+temp[1]
todoDate = nowYear+"-"+temp[0]+'-'+temp[1]
reTime = re.search(r'\d{1,2}点\d{1,2}',splitMsgs[0])
if reTime != None:
temp = str(reTime.group(0)).split('点')
if len(temp[0]) == 1:
temp[0] = "0"+temp[0]
if temp[1] == '':
temp[1] = "00"
if ("下午" in splitMsgs[0]) or ("晚" in splitMsgs[0]) or (todoDate == str(nowDate + timedelta(days=0)) and int(temp[0])<int(datetime.today().time().hour)):
temp[0] = str(int(temp[0])+12)
todoTime = temp[0]+":"+temp[1]
else:
reTime2 = re.search(r'\d{1,2}点',splitMsgs[0])
if reTime2 != None:
temp = str(reTime2.group(0)).split('点')
if len(temp[0]) == 1:
temp[0] = "0"+temp[0]
if ("下午" in splitMsgs[0]) or ("晚" in splitMsgs[0]) or (todoDate == str(nowDate + timedelta(days=0)) and int(temp[0])<int(datetime.today().time().hour)):
temp[0] = str(int(temp[0])+12)
todoTime = temp[0]+":00"
return [str(todoThing),str(todoDate),str(todoTime)]
将格式化后的字符串存进数据库:
def writeReminder(msgs):
msglist = formatMsg(msgs)
mysqldb = MysqlDb()
mysqldb.sql_execute("INSERT INTO reminder (reminder,todoDate,todoTime,isDone) VALUES(%s,%s,%s,%s)",msglist+['0'])
return msglist
这样就实现记录备忘的功能。
按时提醒※
查找数据库中的提醒事项:
todoList = mysqldb.sql_select("SELECT Id,reminder,todoDate,todoTime,earlyReminder FROM reminder WHERE isDone = 0")["data"]
对返回的数据进行判断:
for i in range(0,len(todoList)):
if todoList[i][4] == "0" and nowDate == todoList[i][2] and (todaytime+timedelta(minutes=3)).__format__("%H:%M")==todoList[i][3]:
wechat.SendMsg("离 "+todoList[i][1]+" 还有三分钟,请做好准备!")
mysqldb.sql_execute("UPDATE reminder SET earlyReminder = 1 WHERE Id = '"+str(todoList[i][0])+"'")
if nowDate == todoList[i][2] and nowTime == todoList[i][3]:
wechat.SendMsg("记得"+todoList[i][1]+"哦!")
wechat.SendMsg("记得"+todoList[i][1]+"哦!")
wechat.SendMsg("记得"+todoList[i][1]+"哦!")
mysqldb.sql_execute("UPDATE reminder SET isDone = 1 WHERE Id = '"+str(todoList[i][0])+"'")
查看备忘和删除备忘※
def deleteTask(taskId):
mysqldb = MysqlDb()
result = mysqldb.sql_execute("DELETE FROM reminder WHERE Id = "+str(taskId))
return result
def findAllUndo():
mysqldb = MysqlDb()
todoList = mysqldb.sql_select("SELECT Id,reminder,todoDate,todoTime FROM reminder WHERE isDone = 0")["data"]
msgs=[]
for i in range(0,len(todoList)):
msgs.append("备忘"+str(todoList[i][0])+":"+str(todoList[i][1])+" "+str(todoList[i][2])[5:]+" "+str(todoList[i][3]))
return msgs
设置关键词※
只有在监听到相应的关键词时,机器人才会做出反应。
if "提醒我" in newMsg[1]:
tempMsg = re.search(r'\d{1,2}点',newMsg[1])
if tempMsg == None:
wechat.SendMsg("提醒时间未说明,请重试!")
continue
if int(str(tempMsg.group(0)).split("点")[0]) >=24 or int(str(tempMsg.group(0)).split("点")[0]) < 0:
wechat.SendMsg("时间设置有误!")
continue
result = writeReminder(newMsg[1])
newDates = result[1].split("-")
newTimes = result[2].split(":")
reminderDate = newDates[1]+"月"+newDates[2]+"日"
reminderTime = newTimes[0]+"点"+newTimes[1]+"分"
wechat.SendMsg("好的!我会在"+reminderDate+reminderTime+"的时候提醒你")
elif newMsg[1] == "所有备忘":
msgs = findAllUndo()
if msgs == []:
wechat.SendMsg("目前没有需要提醒的事情了。")
wechat.SendWrapMsg(msgs=msgs)
elif "删除备忘" in newMsg[1]:
taskId = newMsg[1][4:]
res = deleteTask(taskId)
if int(res) == 1:
wechat.SendMsg("删除成功!")
wechat.SendWrapMsg(findAllUndo())
else:
wechat.SendMsg("没有这个备忘!")
接入青云客机器人※
这是一个聊胜于无的功能,青云客是一个免费无需登录的机器人,向接口发送信息即可获取机器人的回答。
def chatWithQingYunKe(msg):
url = 'http://api.qingyunke.com/api.php?key=free&appid=0&msg={}'.format(urllib.parse.quote(msg))
html = requests.get(url)
rt = html.json()["content"]
rt = rt.replace("菲菲","东东 bot")
if "{br}" in rt:
rt = rt.split("{br}")
return rt
结语※
本文介绍了如何使用微信制作机器人,并且实现定时发送值班通知和备忘提醒功能,功能比较基础,这里仅作抛砖引玉。下一篇文章将会实现微信机器人与一款超好用的笔记软件-trilium 的联动,没有使用过 trilium 的,可以前往这篇文章了解一下,我理想中的笔记软件 - trilium | 东东的小黑盒。