几个月前学院组织了一次「晨起打卡」活动,要在每天的一个很早的时间向指定公众号发送指定消息,我就顺手做了SYSU-HitCard. 里面用到的itchat是一个针对微信的Python库,感觉挺强大的,就想用Python+itchat库实现微信聊天机器人。现有的轮子实现思路基本一致:关注一个真·人工智能机器人公众号(如微软小冰、图灵机器人),使用itchat库的接口编写转发策略,通过聊天好友和公众号之间的消息转发造成自动机器人回复的假象。
但这里有两个问题:
当我把脚本挂在服务器上的时候,我没有办法在不建立ssh连接的情况下得知脚本运行状态。考虑到微信严格的反机器人机制,被强制下线是很常见的事。这里需要设定一个定时确认运行状态的机制。
当我使用我的不常用微信号来运行机器人时,我希望我能用常用账号来监视机器人的活动状态,即每次聊天的内容能被转发到指定账号,最好还能注明机器人在和哪个好友对话。
由此,就做了一个能每日确认存活,且自动转发消息的聊天机器人。
聊天机器人部分
这部分只需要对已有的轮子按自己的需要修改一下。
#------------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.
完整源码