几个月前学院组织了一次「晨起打卡」活动,要在每天的一个很早的时间向指定公众号发送指定消息,我就顺手做了SYSU-HitCard. 里面用到的itchat是一个针对微信的Python库,感觉挺强大的,就想用Python+itchat库实现微信聊天机器人。现有的轮子实现思路基本一致:关注一个真·人工智能机器人公众号(如微软小冰、图灵机器人),使用itchat库的接口编写转发策略,通过聊天好友和公众号之间的消息转发造成自动机器人回复的假象。
但这里有两个问题:

  1. 当我把脚本挂在服务器上的时候,我没有办法在不建立ssh连接的情况下得知脚本运行状态。考虑到微信严格的反机器人机制,被强制下线是很常见的事。这里需要设定一个定时确认运行状态的机制。
  2. 当我使用我的不常用微信号来运行机器人时,我希望我能用常用账号来监视机器人的活动状态,即每次聊天的内容能被转发到指定账号,最好还能注明机器人在和哪个好友对话。

由此,就做了一个能每日确认存活,且自动转发消息的聊天机器人。

聊天机器人部分

这部分只需要对已有的轮子按自己的需要修改一下。

#------------chat bot------------# 
# answer by userId
userId = ''

# get friends' message
@itchat.msg_register([itchat.content.TEXT,itchat.content.PICTURE])
def text_reply(msg):
    # let ice answer
    global userId
    userId = msg['FromUserName']
    xbAnswer(msg)
    # print(getUserNickName(msg) + "send:\n" + getText(msg))

# group message
@itchat.msg_register([itchat.content.TEXT,itchat.content.PICTURE], isGroupChat = True)
def group_reply(msg):
    fromUserName = msg['FromUserName']
    group = itchat.search_chatrooms(userName=fromUserName)
    # print(group['NickName'] + "group " + msg['ActualNickName'] + " message:\n" + getText(msg) )

    if msg['isAt'] == True :
        global userId
        userId = msg['FromUserName']
        xbAnswer(msg)

# group message
@itchat.msg_register(itchat.content.PICTURE, isGroupChat = True)
def group_pic(msg):
    msg['Text'](msg['FileName'])
    itchat.send_image(msg['FileName'])

# mp message
@itchat.msg_register([itchat.content.TEXT,itchat.content.PICTURE], isMpChat = True)
def map_reply(msg):
    text = getText(msg)
    global userId
    if msg['Type'] == 'Picture':
        msg['Text'](msg['FileName'])
        itchat.send_image(msg['FileName'],userId)
        # itchat.send_msg(' ', userId)
    else:
        itchat.send_msg(text + " ", userId)
        receiver = itchat.search_friends(name=u"Yzstr")[0]["UserName"]
        itchat.send_msg(text+"\n----------\nauto reply", receiver)

# get name
def getUserNickName(msg):
    fromUserName = msg['FromUserName']
    fromUser = itchat.search_friends(userName=fromUserName)
    nickName = fromUser['NickName']
    return nickName

# get text
def getText(msg):
    if msg['Type'] == 'Text':
        return msg['Text']
    else:
        return " type failed "

# ask ice
def xbAnswer(msg):
    xb = itchat.search_mps(name=u'小冰')[0]
    quest = getText(msg)
    if msg['Type'] == 'Picture':
        msg['Text'](msg['FileName'])
        itchat.send_image(msg['FileName'],xb['UserName'])
    else:
        receiver = itchat.search_friends(name=u"Yzstr")[0]["UserName"]
        # target=itchat.search_friends(userName='xb['UserName']')
        itchat.send_msg(quest+"\n----------\nsend by "+getUserNickName(msg), receiver)
        itchat.send_msg(quest, xb['UserName'])
#--------------------------------#

注意在ask ice部分加入了

    receiver = itchat.search_friends(name=u"Yzstr")[0]["UserName"]     
    itchat.send_msg(quest+"\n----------\nsend by "+getUserNickName(msg), receiver)

用以将 message 转发给微软小冰的同时也转发给自己的微信常用账号。同理,在mp message部分的:

    receiver = itchat.search_friends(name=u"Yzstr")[0]["UserName"]
    itchat.send_msg(text+"\n----------\nauto reply", receiver)

用于将自动回复转发给常用账号。

运行状态确认部分

这一部分比较简单,和「微信打卡签到」类似。

#----------status check----------#
task_list = [("00:01", "I'm stay'in alive")]

def  task_remind():
    for task in task_list:
        task_time = task[0]
        task_content = task[1]
        if datetime.datetime.now().strftime("%H:%M") == task_time:
           
            receiver = itchat.search_friends(name="Yzstr")[0]["UserName"]
            itchat.send_msg(task_content, receiver)

def remind_run():
    sched = BlockingScheduler()
    sched.add_job(task_remind, 'cron', second=0)
    sched.start()
#--------------------------------#

这里实现了在每天的00:01向指定账户发送I'm stay'in alive,带来比基斯兄弟的问候。

同时运行

聊天机器人和运行状态确认是两个独立的部分,分别由itchat.run()remind_run()两个函数进入,而且这两个函数必须同时运行,这让习惯了面向过程编程的我有点头疼。后来发现可以使用Python的threads来完成并行处理,只需要两行代码将这两个函数写入thread,再main函数里启动这两个线程即可。
创建线程:

#----------making threads--------#
    t1=threading.Thread(target=itchat.run,name='itchat')
    t2=threading.Thread(target=remind_run,name='remind')
#--------------------------------#

启动线程:

    if __name__=='__main__':
        itchat.auto_login(hotReload=True,enableCmdQR=0)    
        t1.start()  
        t2.start()

运行

桌面环境

完成配置以后,可以直接运行

    python WeChatter.py

此时微信登陆二维码会被自动加载和打开,扫描二维码即可运行。

服务器环境

考虑到服务器没有桌面也没有图片查看器,需要使用命令行来加载二维码。
将代码第106行改为:

    itchat.auto_login(hotReload=True,enableCmdQR=2)  

其中enableCmdQR参数可以用来设置缩放比例,0表示不使用命令行加载二维码,1表示按原大小显示。
经过测试,在enableCmdQR=2时,二维码在PuTY或者Xshell上都会有较好效果。
如果要在服务器后台运行而不是一直开着终端,可以使用nohup来保持:

    nohup python -u WeChatter.py

注意,使用nohup时,建议将参数enableCmdQR设为1.

完整源码

支付宝扫码打赏 微信打赏

若你觉得我的文章对你有帮助,欢迎点击上方按钮对我打赏

扫描二维码,分享此文章

Yzstr Andy's Picture
Yzstr Andy

School of Data and Computer Science, SUN YAT-SEN UNIVERSITY