制作一个微信机器人

-
-
2023-01-12

前言

起因是想做一个能够每天定时在群里发送一些工作通知和预警。钉钉上有基于 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 | 东东的小黑盒

 

 

 

 

 

 

“您的支持是我持续分享的动力”

微信收款码
微信
支付宝收款码
支付宝

目录