├── AutoReply2.2.py ├── README.md ├── gentle.png ├── heat_map.png ├── info_1.png ├── info_2.png ├── utils.py └── yooongchun_cabin.jpg /AutoReply2.2.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | import utils 3 | import itchat 4 | from time import sleep 5 | import threading 6 | from logging import basicConfig, INFO, info, ERROR 7 | import os.path 8 | 9 | basicConfig(level=ERROR) 10 | 11 | ''' 12 | # 该程序实现接入个人微信号并实现自动回复的功能,同时可选择好友有消息时短信通知或者邮件通知你, 13 | 接入微信号使用了作者:LittleCoder的itchat微信接口开源库, 14 | github地址:https://itchat.readthedocs.io/zh/latest/#itchat 15 | 在此感谢该作者! 16 | 17 | # 基本使用说明: 18 | # 接收的消息类型包括: 19 | 文字: 20 | 图片: 21 | 视频: 22 | 语音: 23 | 共享: 24 | 联系人名片: 25 | 地图: 26 | 声音文件: 27 | 28 | # 处理规则: 29 | # 对于文字类消息,分为两类好友进行判别 30 | # 非星标好友:收到该好友第一条消息后会发送以下全局变量中的:FirstMsg,然后根据好友的回复来处理 31 | 如果好友回复内容是ReceiveYes中的内容之一,则会发邮件通知你,并给好友回复ReplyYes中的内容 32 | 如果好友回复内容是ReceiveNo中的内容之一,则不会通知你,并且给好友回复ReplyNo中的内容 33 | 否则调用图灵机器人,同时计时器开始计时会话周期,当计时器过了CountTime时间后没有收到该好友消息或者你也没有发送消息给给好友 34 | 那么当前会话结束 35 | # 星标好友:与普通好友的区别在于好友第一条消息后会发送以下全局变量中的:FirstMsg,并且加送一条提示VIPMsg,然后自动邮件通知你 36 | 其余一致 37 | # 对于非文字类消息 38 | 所有好友统一回复,说明接收到的文件类型,并说明小助手暂不支持处理该类消息 39 | 40 | # 会话类型:包括三类,一类是自己发给自己,用来测试,一类是自己发给别人,另一类是别人发给自己 41 | 对于自己发给自己的消息,使用host_info函数处理 42 | 对另外两类消息统一由auto_reply函数代理 43 | 44 | ''' 45 | # 版本更新信息 46 | log = "更新信息:\n" \ 47 | "2017-10-01-v1.0:实现对微信的接入,实现图灵机器人接入。\n" \ 48 | "2017-10-02-v1.1:实现对消息的自动回复。\n" \ 49 | "2017-10-03-v1.2:完善消息回复逻辑,增加好友信息短信通知功能。\n" \ 50 | "2017-10-04-v2.0:重构程序,将好友列表机制改为会话列表机制。\n" \ 51 | "2017-10-05-v2.1:修复主人进入会话后不能自动停止机器人交互的bug.\n" \ 52 | "2017-10-06-v2.2:添加主人通过指令获取好友信息功能。\n" \ 53 | "2017-10-07-v2.2:修改对好友回复启用机器人的逻辑规则。\n" 54 | 55 | # 支持的命令信息 56 | command = "支持的命令:\n" \ 57 | "START:启动全局回复代理。\n" \ 58 | "STOP:停止全局回复代理。\n" \ 59 | "STARFRIEND:返回星标好友列表。\n" \ 60 | "NEVERLIST:返回不使用代理的好友名单。\n" \ 61 | "ADD NEVER‘用户名’:添加好友到不使用代理名单。\n" \ 62 | "DELETE NEVER ‘用户名’:将好友从不使用代理名单中删除。\n" \ 63 | "N:刷新NEVERLIST。\n" \ 64 | "REFRESH:刷新好友列表。\n" \ 65 | "LOG:获取软件版本信息。\n" \ 66 | "COMMAND:获取当前支持的命令信息。\n" 67 | # 需要的全局变量 68 | SessionList = {} # 会话列表 69 | Auto_Reply_Status = True # 是否开启自动回复 70 | CountTime = 60 * 10 # 按照10分钟计算一个新会话的开始 71 | # 消息类型 72 | HOST_TO_HOST = 1 # 自己发给自己 73 | HOST_TO_OTHER = 2 # 自己发给好友 74 | OTHER_TO_HOST = 3 # 别人发给自己 75 | NONE = 4 # 错误类型 76 | # 调用图灵机器人需要用到的key 77 | key = 'bfa6182deac04323b72c1705e4897ae4' 78 | # 发邮件需要用到的信息 79 | HostUserName = "yooongchun@foxmail.com" 80 | KEY = "ynfrkvjmyhwwcfij" 81 | ToUserName = "zyc121561@sjtu.edu.cn" 82 | # 发短信需要用到的,查看utils.py里的详细说明 83 | account_sid = 'ACaf4b6a367d3dc718ba8c1ccaaaff91a9' 84 | auth_token = '1d561065ea4b8857ec0537c25f9add1a' 85 | twilio_number = '+12067456298' 86 | send_number = '+8618217235290' 87 | # 不回复的好友名单 88 | NeverList = [] 89 | # 主人 90 | Host = '' 91 | # 好友名单 92 | UserName = [] 93 | NameList = [] 94 | # 星标好友 95 | StarFriend = [] 96 | ############################################ 97 | # 通用第一条消息模板 98 | FirstMsg = u'嘿嘿嘿,你的消息我已经收到了,但是主人现在木有在呢,要不要小助手为你短信通知主人呢?[这是什么(W),聊天(C),这很烦人(N)]' 99 | # 星标好友额外消息模板 100 | VIPMsgHead = u'小助手检测到你是:' 101 | VIPMsgRear = u',主人说你是对他特别重要的人,正在为你自动短信通知他喔,你稍等啦,或者也可以让我陪你聊天哈~' 102 | # 对是否短信通知的回答及回复模板 103 | ReceiveYes = ['要的', '有', '要', '好的', '好吧', '好啊', '好呀', '那你通知吧', '嗯', '可以', '有急事', '急事', '快点叫查永春来', '快叫他来', '快' 104 | '叫你主人来', '好'] 105 | ReplyYes = '好哒,小助手已经为你短信通知主人啦,你稍等喔~' 106 | ReceiveWhat = ['W', '这是什么', '这是什么(W)', '啥?', '蛤?', '什么鬼?'] 107 | ReplyWhat = '亲爱的朋友,这是个人编写的微信代理小程序,如果您觉得这样的代理回复打扰到了您,可在任意时候回复 N ,即可永久停止我' \ 108 | '与您之间的小助手回复代理。如果你也想试一试这样的功能,可联系我,我会为你免费提供此程序,包括源代码,不足之处,还望海' \ 109 | '涵。谢谢您的谅解,祝您生活愉快!\n该助手主要功能包括:1.在好友有急事联系时短信/邮件通知我。2.使用手机微信控制电脑,如' \ 110 | '给电脑传送文件,或者获取电脑文件,等。3.统计自己微信好友信息,比如性别比例、好友城市分布、个性签名、星标好友等。4.接' \ 111 | '入图灵机器人实现智能聊天,等。' 112 | ReceiveNoise = ['N', '这很烦人', '这份烦人(N)'] 113 | ReplyNoise = '抱歉给您带来的不便,小助手还在成长,望您谅解,今后我将停止代理所有您与主人之间的会话,祝您生活愉快,再见!' 114 | ReceiveNo = ['不要', '不', '算了', '没事,不用了', '没急事', '不用', 'No'] 115 | ReplyNo = '好吧,那你可能要等他晚些回复你喔~' 116 | ReceiveChat = ['聊天', 'C', '聊天(C)', 'c'] 117 | ReplyChat = '接下来小助手为你接入聊天机器人进行互动喔,回复 stop 即可退出,have fun!' 118 | 119 | 120 | # 加载不自动回复的好友名单 121 | def load_never_list(): 122 | info('进入函数:load_never_list') 123 | global NeverList 124 | # 加载不回复好友列表 125 | if os.path.exists('NeverList.never'): 126 | with open('NeverList.never', 'r') as f: 127 | never = f.readlines() 128 | NeverList = [name.rstrip('\n') for name in never] 129 | else: 130 | with open('NeverList.never', 'w') as f: 131 | f.write(' \n') 132 | 133 | 134 | # 更新好友列表 135 | def refresh_friend_list(): 136 | info('进入函数:refresh_friend_list') 137 | global Host 138 | global NameList 139 | global StarFriend 140 | global UserName 141 | UserName = itchat.get_friends(update=True) 142 | Host = UserName[0] # 主人 143 | # 获得好友信息列表 144 | NameList = init(UserName) 145 | NameList['filehelper'] = '文件助手' # 添加文件助手 146 | StarFriend = [NameList[star['UserName']] for star in UserName if star['StarFriend'] == 1] # 星标好友 147 | 148 | 149 | # 初始化:返回好友微信id及备注名 150 | def init(UserName): 151 | info('进入函数:init') 152 | NameList = {} 153 | for user in UserName: 154 | if user['RemarkName']: 155 | NameList[user['UserName']] = user['RemarkName'] 156 | else: 157 | NameList[user['UserName']] = user['NickName'] 158 | return NameList 159 | 160 | 161 | # 计时:维持会话时间,当会话时间结束,则好友从当前会话列表中移除 162 | def count_time(msg): 163 | info('进入函数:count_time') 164 | global SessionList 165 | # 获得线程锁 166 | lock = SessionList[msg['FromUserName'] + msg['ToUserName']]['Lock'] 167 | # 维持计时器 168 | while SessionList[msg['FromUserName'] + msg['ToUserName']]['LastTime']: 169 | lock.acquire() 170 | try: 171 | SessionList[msg['FromUserName'] + msg['ToUserName']]['LastTime'] -= 1 172 | finally: 173 | lock.release() 174 | sleep(1) # 睡眠一秒 175 | # 计时周期到期,销毁该对话 176 | SessionList.pop(msg['FromUserName'] + msg['ToUserName']) 177 | 178 | 179 | # 对消息的处理判别 180 | def msg_status(msg): 181 | info('进入函数:msg_status') 182 | # 判断好友是否为星标好友 183 | if NameList[msg['FromUserName']] in StarFriend: 184 | vip_flag = True 185 | else: 186 | vip_flag = False 187 | # 如果消息是自己发给别人 188 | if msg['FromUserName'] == Host['UserName'] and msg['ToUserName'] != Host['UserName']: 189 | Type = HOST_TO_OTHER 190 | host_in = True 191 | host_count = 1 192 | friend_count = 0 193 | session_holder = 'Host' 194 | # 如果消息是自己发给自己 195 | elif msg['FromUserName'] == Host['UserName'] and msg['ToUserName'] == Host['UserName']: 196 | Type = HOST_TO_HOST 197 | host_in = True 198 | host_count = 1 199 | friend_count = 0 200 | session_holder = 'Host' 201 | # 如果消息是别人发给自己 202 | elif msg['FromUserName'] != Host['UserName'] and msg['ToUserName'] == Host['UserName']: 203 | Type = OTHER_TO_HOST 204 | host_in = False 205 | host_count = 0 206 | friend_count = 1 207 | session_holder = 'Friend' 208 | else: 209 | Type = NONE 210 | host_in = None 211 | host_count = None 212 | friend_count = None 213 | session_holder = None 214 | return vip_flag, Type, host_in, host_count, friend_count, session_holder 215 | 216 | 217 | # 创建会话函数 218 | def create_session(msg): 219 | info('为用户:%s 创建会话' % NameList[msg['FromUserName']]) 220 | global SessionList # 添加 221 | # 参数说明: 222 | # FromUserName:消息发送者 223 | # ToUserName:消息接收者 224 | # 获得备注名或昵称:NameList[msg['FromUserName']], 225 | # FirstMsg:该会话周期内产生的第一条消息状态 226 | # FriendFirstReply:该对象的第一次回复状态 227 | # VIP:该对象是否属于星标好友状态 228 | # Lock:该对象的线程锁 229 | # HostIn:主人加入标志 230 | # LastTime:对话生命周期剩余状态 231 | # MsgType:消息类别:文字,声音等 232 | # FromUserCount:发送者发送的消息数 233 | # ToUserCount:接收者发送的消息数 234 | # Type:会话类别 235 | # SessionHolder:会话的发起者 236 | # host-->other:主人主动发消息给好友,Type = HOST_TO_OTHER 237 | # host-->host:主人发消息给自己,可作为测试,Type = HOST_TO_HOST 238 | # other-->host:好友发来的消息,Type = OTHER_TO_HOST 239 | # 其它:Type = NONE 240 | # 消息类别:文字类型:Text 241 | vip_flag, Type, host_in, host_count, friend_count, session_holder = msg_status(msg) # 取得消息状态 242 | SessionList[msg['FromUserName'] + msg['ToUserName']] = {'FromUserName': NameList[msg['FromUserName']], 243 | 'ToUserName': NameList[msg['ToUserName']], 244 | 'SessionHolder': session_holder, 'Lock': threading.Lock(), 245 | 'HostIn': host_in, 'RobotIn': False, 'Type': Type, 246 | 'VIP': vip_flag, 'LastTime': CountTime, 247 | 'HostCount': host_count, 'FriendCount': friend_count} 248 | # 启动会话计时线程 249 | th_time = threading.Thread(target=count_time, args=(msg,)) 250 | th_time.start() 251 | return msg['FromUserName'] + msg['ToUserName'] 252 | 253 | 254 | # 自己发给自己,用于启动全局回复代理,特殊处理 255 | def host_info(msg): 256 | info('进入函数:host_info') 257 | global Auto_Reply_Status 258 | if msg['Text'] == 'START': 259 | Auto_Reply_Status = True 260 | itchat.send_msg('主人,我已开启全局回复代理!', msg['FromUserName']) 261 | 262 | 263 | # 处理会话 264 | def operate_session(session_id, msg, msg_type): 265 | info('进入函数:operate_session') 266 | global SessionList 267 | global Auto_Reply_Status 268 | global NeverList 269 | # 更新会话计时器 270 | lock = SessionList[session_id]['Lock'] 271 | lock.acquire() 272 | try: 273 | # 更新会话生命周期 274 | SessionList[session_id]['LastTime'] = CountTime 275 | finally: 276 | lock.release() 277 | # 自己发给自己 278 | if SessionList[session_id]['Type'] == HOST_TO_HOST: 279 | host_to_host(msg, msg_type) 280 | # 自己发给别人 281 | elif SessionList[session_id]['Type'] == HOST_TO_OTHER: 282 | SessionList[session_id]['HostIn'] = True 283 | pass 284 | # itchat.send_msg('主人,你给%s发了一条消息' % msg['ToUserName']) 285 | # 别人发给自己 286 | elif SessionList[session_id]['Type'] == OTHER_TO_HOST: 287 | other_to_host(session_id, msg, msg_type) 288 | 289 | 290 | # 自己发给自己 291 | # 发文件 ithcat.send("@fil@%s" % '/tmp/test.text') 292 | # 发图片 ithcat.send("@img@%s" % '/tmp/test.png') 293 | # 发视频 ithcat.send("@vid@%s" % '/tmp/test.mkv') 294 | # send_file(fileDir, toUserName=None) 295 | # send_image(fileDir, toUserName=None) 296 | # send_video(fileDir, toUserName=None) 297 | def host_to_host(msg, msg_type): 298 | info('进入函数:host_to_host') 299 | global SessionList 300 | global Auto_Reply_Status 301 | global NeverList 302 | if msg_type == 'Text': 303 | # 停止全局自动回复代理 304 | if msg['Text'].lower() == 'stop': 305 | Auto_Reply_Status = False 306 | itchat.send_msg('主人,我已停止全局自动回复代理!', msg['FromUserName']) 307 | # 命令信息 308 | elif msg['Text'].lower() == 'command': 309 | itchat.send_msg('主人,这是目前支持的命令信息:\n%s' % command, msg['FromUserName']) 310 | # 获取log信息 311 | elif msg['Text'].lower() == 'log': 312 | itchat.send_msg('主人,这是版本更新信息:\n%s' % log, msg['FromUserName']) 313 | 314 | # 更新好友名单 315 | elif msg['Text'].lower() == 'refresh': 316 | refresh_friend_list() 317 | itchat.send_msg('主人,我已经更新了你的好友列表', msg['FromUserName']) 318 | # START用于特殊情况开启全局代理,N用于刷新NeverList,都不使用机器人回复 319 | elif msg['Text'].lower() == 'start': 320 | pass 321 | elif msg['Text'].lower() == 'n': 322 | itchat.send_msg('主人,我已经刷新了不回复好友名单', msg['FromUserName']) 323 | # 添加不回复好友 324 | elif 'add never' in msg['Text'].lower(): 325 | text = msg['Text'].lower().replace('add never ', '') 326 | exflag = False 327 | for name in NameList: 328 | if text == NameList[name]: 329 | exflag = True 330 | break 331 | if exflag: # 存在该好友 332 | with open('NeverList.never', 'a') as fff: 333 | fff.write(text + '\n') 334 | itchat.send_msg('主人,已经将好友:%s 放入NeverList名单中' % text, msg['FromUserName']) 335 | else: 336 | itchat.send_msg('主人,你要添加的好友:%s 不在你的好友名单中' % text, msg['FromUserName']) 337 | # 删除不回复好友 338 | elif 'delete never' in msg['Text'].lower(): 339 | text = msg['Text'].lower().replace('delete never ', '') 340 | with open('NeverList.never', 'r') as ffff: 341 | name = ffff.readlines() 342 | NeverList = [na.rstrip('\n') for na in name] 343 | if text in NeverList: 344 | with open('NeverList.never', 'w') as fffff: 345 | for name in NeverList: 346 | if name != text: 347 | fffff.write(name + '\n') 348 | itchat.send_msg('主人,已经将好友:%s 从NeverList名单中删除' % text, msg['FromUserName']) 349 | else: 350 | itchat.send_msg('主人,你要从NeverList中删除的好友:%s 不在你的NeverList名单中' % text, msg['FromUserName']) 351 | # 获取不回复的好友名单 352 | elif msg['Text'].lower() == 'neverlist': 353 | str_name = '' 354 | with open('NeverList.never', 'r') as f: 355 | never = f.readlines() 356 | NeverList = [] 357 | for ne in never: 358 | NeverList.append(ne.rstrip('\n')) 359 | for name in NeverList: 360 | str_name += '【' + name + '】' 361 | itchat.send_msg('主人,这是目前不进行代理回复的好友名单:%s' % str_name, msg['FromUserName']) 362 | # 获取星标好友名单 363 | elif msg['Text'].lower() == 'starfriend': 364 | str_name = '' 365 | for name in StarFriend: 366 | str_name += '【' + name + '】' 367 | itchat.send_msg('主人,这是目前你好友中的星标好友名单:%s' % str_name, msg['FromUserName']) 368 | # 获取好友列表 369 | elif msg['Text'].lower() == 'friendlist': 370 | str_name = '' 371 | for id, name in NameList.items(): 372 | str_name += '【' + name + '】' 373 | itchat.send_msg('主人,这是目前你的好友名单:\n%s' % str_name, msg['FromUserName']) 374 | # 获取好友性别,星标好友分布图 375 | elif msg['Text'].lower() == 'viewinfo': 376 | itchat.send_msg('主人,我正在为你生成好友性别比例图和城市分布热力图,请稍等!', msg['FromUserName']) 377 | try: 378 | male, female, other, pro_city, signature, star_friend = utils.frinds_info(UserName) 379 | utils.view_info(male, female, other, pro_city, star_friend) 380 | if os.path.exists('sex.png'): 381 | itchat.send_image('sex.png', msg['FromUserName']) 382 | else: 383 | itchat.send_msg('主人,性别统计数据生成成功啦,只是好像没保存成功呢,再试一次吧!', msg['FromUserName']) 384 | if os.path.exists('heatmap.html'): 385 | itchat.send_file('heatmap.html', msg['FromUserName']) 386 | else: 387 | itchat.send_msg('主人,好友城市分布热力图数据生成成功啦,只是好像没保存成功呢,再试一次吧!', msg['FromUserName']) 388 | except ConnectionRefusedError: 389 | itchat.send_msg('主人,生成失败了呢,检查一下网络连接吧!', msg['FromUserName']) 390 | 391 | # 别的文字消息则启动机器人回复 392 | else: 393 | robot_reply = utils.get_response(msg['Text'], key) 394 | itchat.send_msg(robot_reply, msg['FromUserName']) 395 | elif msg_type == 'Attachment': 396 | itchat.send_msg('主人,我收到了你发来的附件!', msg['FromUserName']) 397 | elif msg_type == 'Voice': 398 | itchat.send_msg('主人,我收到了你发来的语音!', msg['FromUserName']) 399 | elif msg_type == 'Recording': 400 | itchat.send_msg('主人,我收到了你发来的音频文件爱你!', msg['FromUserName']) 401 | elif msg_type == 'Video': 402 | itchat.send_msg('主人,我收到了你发来的视频文件!', msg['FromUserName']) 403 | elif msg_type == 'Picture': 404 | itchat.send_msg('主人,我收到了你发来的图片!', msg['FromUserName']) 405 | elif msg_type == 'Map': 406 | itchat.send_msg('主人,我收到了你发来的地图!', msg['FromUserName']) 407 | elif msg_type == 'Card': 408 | itchat.send_msg('主人,我收到了你发来的联系人卡片!', msg['FromUserName']) 409 | elif msg_type == 'Sharing': 410 | itchat.send_msg('主人,我收到了你发来的共享文件!', msg['FromUserName']) 411 | elif msg_type == 'Note': 412 | itchat.send_msg('主人,我收到了系统提示!', msg['FromUserName']) 413 | elif msg_type == 'System': 414 | itchat.send_msg('主人,我收到了系统消息!', msg['FromUserName']) 415 | 416 | 417 | # 别人发给自己消息处理 418 | def other_to_host(session_id, msg, msg_type): 419 | info('进入好友发给自己的处理函数') 420 | # 判断主人是否在会话中 421 | if msg['FromUserName'] == Host['UserName']: 422 | SessionList[session_id]['HostIn'] = True 423 | 424 | # 主人不在会话中,则使用回复代理 425 | if not SessionList[session_id]['HostIn']: 426 | # 发送给自己的消息类型 427 | if msg_type == 'Text': 428 | send_text = msg['Text'] 429 | else: 430 | send_text = msg_type 431 | # 好友回复了永久停止小助手 432 | if msg['Text'] in ReceiveNoise: 433 | with open('NeverList.never', 'a') as f: 434 | f.write(NameList[msg['FromUserName']] + '\n') 435 | itchat.send_msg(ReplyNoise, msg['FromUserName']) 436 | return 437 | # 好友回复了对小助手的询问 438 | if msg['Text'] in ReceiveWhat: 439 | itchat.send_msg(ReplyWhat, msg['FromUserName']) 440 | return 441 | # 好友回复聊天 442 | if msg['Text'] in ReceiveChat: 443 | SessionList[session_id]['RobotIn'] = True 444 | itchat.send_msg(ReplyChat, msg['FromUserName']) 445 | return 446 | # 好友回复退出聊天 447 | if msg['Text'] == 'stop': 448 | SessionList[session_id]['RobotIn'] = False 449 | itchat.send_msg('已退出机器人聊天!', msg['FromUserName']) 450 | return 451 | 452 | # 判断这是该好友发来的第几条消息 453 | if SessionList[session_id]['FriendCount'] == 1: 454 | SessionList[session_id]['FriendCount'] += 1 455 | # 发送第一条消息 456 | itchat.send_msg(FirstMsg, msg['FromUserName']) 457 | # VIP好友加送一条 458 | if SessionList[session_id]['VIP']: 459 | itchat.send_msg(VIPMsgHead + NameList[msg['FromUserName']] + VIPMsgRear, msg['FromUserName']) 460 | # 发短信通知 461 | try: 462 | utils.send_sms( 463 | '主人,你的微信VIP好友%s在微信上给你发消息:%s。快去看看吧' % (NameList[msg['FromUserName']], send_text), 464 | send_number) 465 | except Exception: 466 | itchat.send_msg('阿欧,短信发不了呢,为你尝试发邮件联系喔~', msg['FromUserName']) 467 | # 发邮件 468 | try: 469 | content = {'header': '微信好友%s发来消息' % NameList[msg['FromUserName']], 470 | 'text': '主人,你的微信好友%s 在微信上给你发了消息:%s。发你短信出故障了呢!' % (NameList[msg['FromUserName']], 471 | send_text)} 472 | utils.send_mail(content, HostUserName, KEY, ToUserName) 473 | except Exception: 474 | itchat.send_msg('啊,今天真是倒霉,邮件也发不出去啦,要是有急事的话,试试打电话吧:%s' % send_number, msg['FromUserName']) 475 | # 好友发来的第二条消息,可能是对第一条的回复 476 | elif SessionList[session_id]['FriendCount'] == 2: 477 | SessionList[session_id]['FriendCount'] += 1 478 | # 非VIP好友 479 | if not SessionList[session_id]['VIP']: 480 | if msg['Text'] in ReceiveYes: 481 | # 发短信通知 482 | try: 483 | utils.send_sms( 484 | '主人,你的微信好友 %s 在微信上给你发消息:%s。快去看看吧' % (NameList[msg['FromUserName']], send_text), 485 | send_number) 486 | itchat.send_msg(ReplyYes, msg['FromUserName']) 487 | except Exception: 488 | itchat.send_msg('阿欧,短信发不了了呢,为你尝试发邮件联系喔~', msg['FromUserName']) 489 | # 发邮件 490 | try: 491 | content = {'header': '微信好友 %s 发来消息' % NameList[msg['FromUserName']], 492 | 'text': '主人,你的微信好友 %s 在微信上给你发了消息:%s。发你短信出故障了呢!' % ( 493 | NameList[msg['FromUserName']], 494 | send_text)} 495 | utils.send_mail(content, HostUserName, KEY, ToUserName) 496 | except Exception: 497 | itchat.send_msg('啊,今天真是倒霉呢,邮件也发不出去啦,要是有急事的话,试试打电话吧:%s' % send_number, 498 | msg['FromUserName']) 499 | elif msg['Text'] in ReceiveNo: 500 | itchat.send_msg(ReplyNo, msg['FromUserName']) 501 | 502 | else: 503 | # 好友回复聊天状态为True 504 | if SessionList[session_id]['RobotIn']: 505 | robot_reply = utils.get_response(msg['Text'], key) 506 | itchat.send_msg(robot_reply, msg['FromUserName']) 507 | else: 508 | # 好友回复聊天状态为True 509 | if SessionList[session_id]['RobotIn']: 510 | robot_reply = utils.get_response(msg['Text'], key) 511 | itchat.send_msg(robot_reply, msg['FromUserName']) 512 | else: # 启动机器人回复 513 | # 好友回复聊天状态为True 514 | if SessionList[session_id]['RobotIn']: 515 | robot_reply = utils.get_response(msg['Text'], key) 516 | itchat.send_msg(robot_reply, msg['FromUserName']) 517 | 518 | 519 | ############################################# 520 | # 消息接收 521 | @itchat.msg_register(itchat.content.TEXT) 522 | def info_text(msg): 523 | info('进入函数:info_text') 524 | print('收到来自 %s 的文字消息:%s' % (NameList[msg['FromUserName']], msg['Text'])) 525 | global SessionList 526 | global NeverList 527 | # 创建线程处理会话 528 | # 特殊情况,用于在全局回复代理关闭时进行启动 529 | if msg['FromUserName'] == Host['UserName'] and (not Auto_Reply_Status): 530 | host_sess = threading.Thread(target=host_info, args=(msg,)) 531 | host_sess.start() 532 | # 仅当全局回复状态及该好友不在NeverList中时传递该消息 533 | if msg['FromUserName'] == Host['UserName'] and msg['Text'] == 'N': # 收到主人发的N更新NeverList 534 | with open('NeverList.never', 'r') as ff: 535 | never = ff.readlines() 536 | NeverList = [name.rstrip('\n') for name in never] 537 | if Auto_Reply_Status and NameList[msg['FromUserName']] not in NeverList: 538 | # 判断会话是否已经存在 539 | if (msg['FromUserName'] + msg['ToUserName']) in SessionList: 540 | session_id = msg['FromUserName'] + msg['ToUserName'] 541 | elif (msg['ToUserName'] + msg['FromUserName']) in SessionList: 542 | session_id = msg['ToUserName'] + msg['FromUserName'] 543 | # 会话已经存在 544 | else: 545 | session_id = create_session(msg) # 创建会话 546 | 547 | # 创建线程处理会话 548 | new_sess = threading.Thread(target=operate_session, args=(session_id, msg, 'Text')) 549 | new_sess.start() 550 | 551 | 552 | # 语音消息 553 | @itchat.msg_register(itchat.content.RECORDING) 554 | def info_recording(msg): 555 | info('进入函数:info_recording') 556 | global SessionList 557 | # 仅当全局回复状态及该好友不在NeverList中时传递该消息 558 | if Auto_Reply_Status and NameList[msg['FromUserName']] not in NeverList: 559 | # 判断会话是否已经存在 560 | if ((msg['FromUserName'] + msg['ToUserName']) not in SessionList) and ( 561 | (msg['ToUserName'] + msg['FromUserName']) not in SessionList): 562 | session_id = create_session(msg) # 创建会话 563 | else: 564 | if (msg['FromUserName'] + msg['ToUserName']) in SessionList: 565 | session_id = msg['FromUserName'] + msg['ToUserName'] 566 | else: 567 | session_id = msg['ToUserName'] + msg['FromUserName'] 568 | # 处理会话 569 | # 创建线程处理会话 570 | new_sess = threading.Thread(target=operate_session, args=(session_id, msg, 'Recording')) 571 | new_sess.start() 572 | 573 | 574 | # 附件 575 | @itchat.msg_register(itchat.content.ATTACHMENT) 576 | def info_attachment(msg): 577 | info('进入函数:info_attachment') 578 | global SessionList 579 | # 仅当全局回复状态及该好友不在NeverList中时传递该消息 580 | if Auto_Reply_Status and NameList[msg['FromUserName']] not in NeverList: 581 | # 判断会话是否已经存在 582 | if ((msg['FromUserName'] + msg['ToUserName']) not in SessionList) and ( 583 | (msg['ToUserName'] + msg['FromUserName']) not in SessionList): 584 | session_id = create_session(msg) # 创建会话 585 | else: 586 | if (msg['FromUserName'] + msg['ToUserName']) in SessionList: 587 | session_id = msg['FromUserName'] + msg['ToUserName'] 588 | else: 589 | session_id = msg['ToUserName'] + msg['FromUserName'] 590 | # 处理会话 591 | # 创建线程处理会话 592 | new_sess = threading.Thread(target=operate_session, args=(session_id, msg, 'Attachment')) 593 | new_sess.start() 594 | 595 | 596 | # 通知消息 597 | @itchat.msg_register(itchat.content.NOTE) 598 | def info_note(msg): 599 | info('进入函数:info_note') 600 | pass 601 | 602 | 603 | # 图片 604 | @itchat.msg_register(itchat.content.PICTURE) 605 | def info_picture(msg): 606 | info('进入函数:info_picture') 607 | global SessionList 608 | # 仅当全局回复状态及该好友不在NeverList中时传递该消息 609 | if Auto_Reply_Status and NameList[msg['FromUserName']] not in NeverList: 610 | # 判断会话是否已经存在 611 | if ((msg['FromUserName'] + msg['ToUserName']) not in SessionList) and ( 612 | (msg['ToUserName'] + msg['FromUserName']) not in SessionList): 613 | session_id = create_session(msg) # 创建会话 614 | else: 615 | if (msg['FromUserName'] + msg['ToUserName']) in SessionList: 616 | session_id = msg['FromUserName'] + msg['ToUserName'] 617 | else: 618 | session_id = msg['ToUserName'] + msg['FromUserName'] 619 | # 处理会话 620 | # 创建线程处理会话 621 | new_sess = threading.Thread(target=operate_session, args=(session_id, msg, 'Picture')) 622 | new_sess.start() 623 | 624 | 625 | # 地图 626 | @itchat.msg_register(itchat.content.MAP) 627 | def info_map(msg): 628 | info('进入函数:info_map') 629 | global SessionList 630 | # 仅当全局回复状态及该好友不在NeverList中时传递该消息 631 | if Auto_Reply_Status and NameList[msg['FromUserName']] not in NeverList: 632 | # 判断会话是否已经存在 633 | if ((msg['FromUserName'] + msg['ToUserName']) not in SessionList) and ( 634 | (msg['ToUserName'] + msg['FromUserName']) not in SessionList): 635 | session_id = create_session(msg) # 创建会话 636 | else: 637 | if (msg['FromUserName'] + msg['ToUserName']) in SessionList: 638 | session_id = msg['FromUserName'] + msg['ToUserName'] 639 | else: 640 | session_id = msg['ToUserName'] + msg['FromUserName'] 641 | # 处理会话 642 | # 创建线程处理会话 643 | new_sess = threading.Thread(target=operate_session, args=(session_id, msg, 'Map')) 644 | new_sess.start() 645 | 646 | 647 | # 卡片 648 | @itchat.msg_register(itchat.content.CARD) 649 | def info_card(msg): 650 | info('进入函数:info_card') 651 | global SessionList 652 | # 仅当全局回复状态及该好友不在NeverList中时传递该消息 653 | if Auto_Reply_Status and NameList[msg['FromUserName']] not in NeverList: 654 | # 判断会话是否已经存在 655 | if ((msg['FromUserName'] + msg['ToUserName']) not in SessionList) and ( 656 | (msg['ToUserName'] + msg['FromUserName']) not in SessionList): 657 | session_id = create_session(msg) # 创建会话 658 | else: 659 | if (msg['FromUserName'] + msg['ToUserName']) in SessionList: 660 | session_id = msg['FromUserName'] + msg['ToUserName'] 661 | else: 662 | session_id = msg['ToUserName'] + msg['FromUserName'] 663 | # 处理会话 664 | # 创建线程处理会话 665 | new_sess = threading.Thread(target=operate_session, args=(session_id, msg, 'Card')) 666 | new_sess.start() 667 | 668 | 669 | # 共享 670 | @itchat.msg_register(itchat.content.SHARING) 671 | def info_sharing(msg): 672 | info('进入函数:info_sharing') 673 | global SessionList 674 | # 仅当全局回复状态及该好友不在NeverList中时传递该消息 675 | if Auto_Reply_Status and NameList[msg['FromUserName']] not in NeverList: 676 | # 判断会话是否已经存在 677 | if ((msg['FromUserName'] + msg['ToUserName']) not in SessionList) and ( 678 | (msg['ToUserName'] + msg['FromUserName']) not in SessionList): 679 | session_id = create_session(msg) # 创建会话 680 | else: 681 | if (msg['FromUserName'] + msg['ToUserName']) in SessionList: 682 | session_id = msg['FromUserName'] + msg['ToUserName'] 683 | else: 684 | session_id = msg['ToUserName'] + msg['FromUserName'] 685 | # 处理会话 686 | # 创建线程处理会话 687 | new_sess = threading.Thread(target=operate_session, args=(session_id, msg, 'Sharing')) 688 | new_sess.start() 689 | 690 | 691 | # 视频 692 | @itchat.msg_register(itchat.content.VIDEO) 693 | def info_video(msg): 694 | info('进入函数:info_video') 695 | global SessionList 696 | # 仅当全局回复状态及该好友不在NeverList中时传递该消息 697 | if Auto_Reply_Status and NameList[msg['FromUserName']] not in NeverList: 698 | # 判断会话是否已经存在 699 | if ((msg['FromUserName'] + msg['ToUserName']) not in SessionList) and ( 700 | (msg['ToUserName'] + msg['FromUserName']) not in SessionList): 701 | session_id = create_session(msg) # 创建会话 702 | else: 703 | if (msg['FromUserName'] + msg['ToUserName']) in SessionList: 704 | session_id = msg['FromUserName'] + msg['ToUserName'] 705 | else: 706 | session_id = msg['ToUserName'] + msg['FromUserName'] 707 | # 处理会话 708 | # 创建线程处理会话 709 | new_sess = threading.Thread(target=operate_session, args=(session_id, msg, 'Video')) 710 | new_sess.start() 711 | 712 | 713 | # 朋友邀请 714 | @itchat.msg_register(itchat.content.FRIENDS) 715 | def info_friends(msg): 716 | info('进入函数:info_friends') 717 | pass 718 | 719 | 720 | # 系统消息 721 | @itchat.msg_register(itchat.content.SYSTEM) 722 | def info_system(msg): 723 | info('进入函数:info_system') 724 | pass 725 | 726 | 727 | # 声音 728 | @itchat.msg_register(itchat.content.VOICE) 729 | def info_voice(msg): 730 | info('进入函数:info_voice') 731 | global SessionList 732 | # 仅当全局回复状态及该好友不在NeverList中时传递该消息 733 | if Auto_Reply_Status and NameList[msg['FromUserName']] not in NeverList: 734 | # 判断会话是否已经存在 735 | if ((msg['FromUserName'] + msg['ToUserName']) not in SessionList) and ( 736 | (msg['ToUserName'] + msg['FromUserName']) not in SessionList): 737 | session_id = create_session(msg) # 创建会话 738 | else: 739 | if (msg['FromUserName'] + msg['ToUserName']) in SessionList: 740 | session_id = msg['FromUserName'] + msg['ToUserName'] 741 | else: 742 | session_id = msg['ToUserName'] + msg['FromUserName'] 743 | # 处理会话 744 | # 创建线程处理会话 745 | new_sess = threading.Thread(target=operate_session, args=(session_id, msg, 'Voice')) 746 | new_sess.start() 747 | 748 | 749 | if __name__ == '__main__': 750 | print('正在登录微信,若第一次登录,请扫描弹出的二维码,没加载出来则请重新启动程序') # 登录微信 751 | itchat.auto_login(hotReload=True) 752 | print('登录成功!') 753 | load_never_list() # 加载不代理的好友名单 754 | refresh_friend_list() # 更新好友名单 755 | print('开启自动回复!') 756 | itchat.run() # 持续监听自动回复 757 | print('程序运行结束!') 758 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Python实现微信小助手 2 | 3 | --- 4 | 5 | 摘要:该程序实现接入个人微信号并实现自动回复的功能,同时可选择好友有消息时短信通知或者邮件通知你。接入微信号使用了作者:$LittleCoder$ 的$itchat$ 微信接口开源库,在此感谢该作者! 6 | 7 | 作者:yooongchun 8 | 9 | 微信公众号:yooongchun小屋 10 | 11 | ![](yooongchun_cabin.jpg) 12 | 13 | --- 14 | 15 | ### 0.首先我们来看几张图: 16 | 17 | - 好友性别信息统计结果: 18 | 19 | ![](gentle.png) 20 | 21 | --- 22 | 23 | - 好友城市分布信息统计:使用百度热力图实现 24 | 25 | ![](heat_map.png) 26 | 27 | --- 28 | 29 | - 自己给文件助手发送命令及与机器人聊天 30 | 31 | ![](info_2.png) 32 | 33 | --- 34 | 35 | - 与好友的聊天接入机器人: 36 | 37 | ![](info_1.png) 38 | 39 | --- 40 | 41 | 42 | 43 | ### 1.基本功能介绍: 44 | 45 | - 收到好友消息时自动回复 46 | - 收到特定好友消息时短信/邮件通知你 47 | - 接入图灵机器人自动聊天(可启用对指定好友的机器人代理回复) 48 | - 统计好友信息生成热力图,包括好友的性别比例、城市分布 49 | 50 | ### 2.基本使用说明: 51 | 52 | - 接收的消息类型包括:文字、图片、视频、语音、共享、联系人名片、地图、声音文件 53 | 54 | ### 3.处理规则: 55 | 56 | - 对于文字类消息,分为两类好友进行判别: 57 | 58 | - **非星标好友** :收到该类好友第一条消息后会发送以下全局变量中的:`FirstMsg`,然后根据好友的回复来选择回复逻辑: 59 | - 如果好友回复内容是`ReceiveYes`中的内容之一,则会发邮件通知你,并给好友回复`ReplyYes`中的内容 60 | - 如果好友回复内容是`ReceiveNo`中的内容之一,则不会通知你,并且给好友回复`ReplyNo`中的内容 61 | - 否则调用图灵机器人,同时计时器开始计时会话周期,当计时器过了`CountTime`时间后没有收到该好友消息或者你也没有发送消息给给好友那么当前会话结束 62 | - 星标好友:与普通好友的区别在于好友第一条消息后会发送以下全局变量中的:`FirstMsg`,并且加送一条提示`VIPMsg` ,然后自动邮件通知你 63 | 64 | 其余一致 65 | 66 | - 对于非文字类消息: 67 | 68 | 所有好友统一回复,说明接收到的文件类型,并说明小助手暂不支持处理该类消息 69 | ### 4.会话类型 70 | 71 | 会话类型包括三类,一类是自己发给自己,用来测试,一类是自己发给别人,另一类是别人发给自己 72 | 73 | - 对于自己发给自己的消息,使用`host_info`函数处理 74 | - 对另外两类消息统一由`auto_reply` 函数代理 75 | 76 | ### 5.支持的命令信息 77 | 78 | 该命令需要在微信文件助手中发送,发送`command` 则会返回这些支持的命令,使用时发送对应的指令即可 79 | 80 | command = "支持的命令:\n" \ 81 | "START:启动全局回复代理。\n" \ 82 | "STOP:停止全局回复代理。\n" \ 83 | "STARFRIEND:返回星标好友列表。\n" \ 84 | "NEVERLIST:返回不使用代理的好友名单。\n" \ 85 | "ADD NEVER‘用户名’:添加好友到不使用代理名单。\n" \ 86 | "DELETE NEVER ‘用户名’:将好友从不使用代理名单中删除。\n" \ 87 | "N:刷新NEVERLIST。\n" \ 88 | "REFRESH:刷新好友列表。\n" \ 89 | "LOG:获取软件版本信息。\n" \ 90 | "COMMAND:获取当前支持的命令信息。\n" 91 | ### 6.代码结构说明 92 | 93 | - 代码包含两部分,`AutoReply2.2.py` 是主文件,运行该文件即可运行主程序,`util.py` 是实现功能的模块,提供主程序需要的功能,如发送短信,发送邮件,生成热力图等等 94 | 95 | ### 7.项目代码下载地址 96 | 97 | Github :https://github.com/yooongchun/Python_wechat 98 | 99 | -------------------------------------------------------------------------------- /gentle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yooongchun/Python_wechat/f72a897f9486c01acd4c0f8b8793e26826896464/gentle.png -------------------------------------------------------------------------------- /heat_map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yooongchun/Python_wechat/f72a897f9486c01acd4c0f8b8793e26826896464/heat_map.png -------------------------------------------------------------------------------- /info_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yooongchun/Python_wechat/f72a897f9486c01acd4c0f8b8793e26826896464/info_1.png -------------------------------------------------------------------------------- /info_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yooongchun/Python_wechat/f72a897f9486c01acd4c0f8b8793e26826896464/info_2.png -------------------------------------------------------------------------------- /utils.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yooongchun/Python_wechat/f72a897f9486c01acd4c0f8b8793e26826896464/utils.py -------------------------------------------------------------------------------- /yooongchun_cabin.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yooongchun/Python_wechat/f72a897f9486c01acd4c0f8b8793e26826896464/yooongchun_cabin.jpg --------------------------------------------------------------------------------