def check_and_confirm_and_load(self, qrcode_rsp, device_id): qr_code = self.check_qrcode_login(qrcode_rsp, device_id) if qr_code is not False: if self.confirm_qrcode_login(qr_code, keep_heart_beat=False): v_user_pickle = red.get('v_user_' + str(qr_code['Username'])) v_user = pickle.loads(v_user_pickle) if self.new_init(v_user): v_user = pickle.loads( red.get('v_user_' + str(qr_code['Username']))) self.async_check(v_user) from taobaoke.heartbeat_manager import HeartBeatManager HeartBeatManager.begin_heartbeat(str(qr_code['Username']))
def process_notify_package(self, data): # 接收到notify包时的回调处理 cmd = common_utils.read_int(data, 8) if cmd == 318: print "cmd 318" # 这里decode出来的都是微信的官网的html,所以没必要print出来了 # v_user = pickle.loads(red.get('v_user_' + self.wx_username)) # if v_user is not None: # threading.Thread(target=self.decode_cmd_318, args=(v_user, data,)).start() if data is not None and len(data) >= 4: selector = common_utils.read_int(data, 16) if selector > 0: print "selector:{0} start sync thread".format(selector) # TODO:在 set_user_context 中定义的wx_username, 这么写不好, 待修改 if self.wx_username is not None and self.wx_username != "": if self.__lock.acquire(): # 确保只有一个线程在执行async_check,否则会接受多次相同的消息 if not self.__is_async_check: self.__is_async_check = True self.__lock.release() else: print "*********skip async check*********" self.__lock.release() return # 拉取消息之前先查询是否是登陆状态 # 因为用户ipad登陆的同时登陆其他平台,socket仍然会收到notify包 with app.app_context(): user_db = model.User.query.filter_by( userame=self.wx_username).first() is_login = user_db.login == 1 if is_login: bot = WXBot() v_user = pickle.loads( red.get('v_user_' + self.wx_username)) bot.async_check(v_user) self.__is_async_check = False
def heartbeat(cls, wx_username, md_username): is_first = True wx_bot = weixin_bot.WXBot() wx_bot.set_user_context(wx_username) # 微信有新消息就会往socket推20字节的notify包 # 防止该socket断开,每30秒发一次同步消息包 heart_beat_count = 0 user = WxUser.objects.filter(username=wx_username).first() if user is not None: while True: try: # 用户退出登陆,退出线程 if user.login == 0 and wx_bot._auto_retry == 30: logger.info("{}: 用户重启心跳失败,结束心跳".format(user.nickname)) oss_utils.beary_chat("{0}: 用户重启心跳失败,机器人已下线".format(user.nickname)) # 登出时需要把socket断开,否则会一直收到退出登陆的消息 wx_bot.wechat_client.close_when_done() return # 防止心跳无法关闭,采用外部控制 heart_status = red.get('v_user_heart_' + wx_username) if heart_status: if int(heart_status) == 2: v_user = pickle.loads(red.get('v_user_' + wx_username)) wx_bot.logout_bot(v_user) del HeartBeatManager.heartbeat_thread_dict[wx_username] logger.info("{}: 心跳终止成功".format(user.nickname)) oss_utils.beary_chat("{}: 心跳终止成功".format(user.nickname)) wx_bot.wechat_client.close_when_done() red.set('v_user_heart_' + wx_username, 0) return # 如果有多个心跳的时候,只能逐一终止,不能使用logout,否则会使用户变成僵尸用户 if int(heart_status) == 3: wx_bot.wechat_client.close_when_done() del HeartBeatManager.heartbeat_thread_dict[wx_username] logger.info("{}: 心跳终止成功".format(user.nickname)) oss_utils.beary_chat("{}: 心跳终止成功".format(user.nickname)) red.set('v_user_heart_' + wx_username, 1) return if not wx_bot.wechat_client.connected: # 测试过后发现好像没有哪个包能阻止socket断开,断开只是时间问题 # 检测一下socket有没断开,如果断开,重新起一个socket即可 time.sleep(5) # 再一次初始化 is_first = True temp = wx_bot._auto_retry wx_bot = weixin_bot.WXBot() wx_bot._auto_retry = temp wx_bot.set_user_context(wx_username) v_user = pickle.loads(red.get('v_user_' + wx_username)) if is_first: UUid = user.uuid DeviceType = user.device_type logger.info("%s: 进行心跳二次登录中" % user.nickname) res_auto = wx_bot.auto_auth(v_user, UUid, DeviceType, False) is_first = False if res_auto is True: if wx_bot.set_user_login(wx_username): wx_bot.open_notify_callback() v_user = pickle.loads(red.get('v_user_' + wx_username)) t = threading.Thread(target=wx_bot.contact_init, args=(v_user, 0, 0)) t.start() red.set('v_user_heart_' + wx_username, 1) logger.info("{}: 心跳二次登录成功".format(user.nickname)) user = WxUser.objects.filter(username=wx_username).first() oss_utils.beary_chat("{0}: 机器人已上线, 心跳开启成功--{1}, {2}, login为{3}".format(user.nickname, time.asctime(time.localtime(time.time())), user.username, user.login)) else: logger.info("{}: 用户设置login失败" % user.nickname) return elif res_auto is 'Logout': red.set('v_user_heart_' + wx_username, 0) del HeartBeatManager.heartbeat_thread_dict[wx_username] auth_user = User.objects.filter(username=md_username).first() user.user.remove(auth_user) logger.info("{}: 用户主动退出登录,退出心跳,机器人下线".format(user.nickname)) oss_utils.beary_chat("{0}: 用户主动退出登录,退出机器人".format(user.nickname)) # wx_bot.wechat_client.close_when_done() wx_bot.logout_bot(v_user) return else: red.set('v_user_heart_' + wx_username, 0) del HeartBeatManager.heartbeat_thread_dict[wx_username] # auth_user = User.objects.filter(username=md_username).first() # user.user.remove(auth_user) logger.info("{}: 心跳二次登录失败,退出心跳,登录失败".format(user.nickname)) oss_utils.beary_chat("{}: 啊哦,机器人心跳失败,上线失败".format(user.nickname)) wx_bot.wechat_client.close_when_done() wx_bot.logout_bot(v_user) return # c# demo 中的heart_beat包,能延长socket的持续时间 # 但始终会断开 if wx_bot._lock.acquire(): logger.info("%s: 开始发送心跳包" % user.nickname) start_time = datetime.datetime.now() if wx_bot.heart_beat(v_user): logger.info("%s: 心跳包发送成功" % user.nickname) heart_beat_count += 1 if heart_beat_count % 10 == 0: user.last_heart_beat = timezone.now() user.save() else: logger.info("%s: 心跳包发送失败" % user.nickname) # 如果心跳超过10分钟才发送完毕,认定socket阻塞了,重启心跳 if (datetime.datetime.now() - start_time).seconds > 10 * 60: wx_bot.wechat_client.close_when_done() logger.info("%s: 心跳完成发送超时,尝试重启心跳" % user.nickname) wx_bot._lock.release() connection.close() time.sleep(30) except Exception as e: connection.close() logger.error(e) logger.info("{0}heartbeat exception:{1}".format(wx_username, e.message)) oss_utils.beary_chat("{0}heartbeat exception:{1}".format(wx_username, e.message)) return else: logger.info("%s: user不存在", wx_username)
def async_check(self, v_user, new_socket=True): with app.app_context(): bot_param = model.BotParam.query.filter_by( Username=v_user.userame).first() if bot_param: self.long_host = bot_param.LongHost if new_socket: self.wechat_client = WechatClient.WechatClient( self.long_host, 80, True) sync_req = WechatMsg(token=CONST_PROTOCOL_DICT['machine_code'], version=CONST_PROTOCOL_DICT['version'], timeStamp=get_time_stamp(), iP=get_public_ip(), baseMsg=BaseMsg(cmd=138, user=v_user)) sync_rsp = grpc_client.send(sync_req) (buffers, seq) = grpc_utils.get_seq_buffer(sync_rsp) buffers = self.wechat_client.sync_send_and_return( buffers, close_socket=new_socket) if not check_buffer_16_is_191(buffers): try: # TODO:uuid 和 devicetype 存起来? UUid = u"667D18B1-BCE3-4AA2-8ED1-1FDC19446567" DeviceType = u"<k21>TP_lINKS_5G</k21><k22>中国移动</k22><k24>c1:cd:2d:1c:5b:11</k24>" if self.auto_auth(v_user, UUid, DeviceType, new_socket=new_socket): v_user = pickle.loads(red.get('v_user_' + v_user.userame)) self.async_check(v_user, new_socket=new_socket) return True self.logout_bot(v_user) print(read_int(buffers, 18)) if read_int(buffers, 18) == -13: print("Session Time out 离线或用户取消登陆 需执行二次登录") except Exception as e: print(e) return False else: sync_rsp.baseMsg.cmd = -138 sync_rsp.baseMsg.payloads = char_to_str(buffers) sync_rsp = grpc_client.send(sync_rsp) # 刷新用户信息 v_user = sync_rsp.baseMsg.user v_user_pickle = pickle.dumps(v_user) red.set('v_user_' + v_user.userame, v_user_pickle) msg_list = json.loads(sync_rsp.baseMsg.payloads) if msg_list is not None: for msg_dict in msg_list: try: if msg_dict['MsgType'] == 2: with app.app_context(): model.save_contact(msg_dict) elif msg_dict['Status'] is not None: try: action_rule.filter_keyword_rule( v_user.userame, msg_dict) except Exception as e: print(e) with app.app_context(): model.save_message(msg_dict) else: print(msg_dict) except Exception as e: print(e) print(msg_dict) self.async_check(v_user, new_socket=new_socket) else: print "sync 完成"
def try_room_detail(self, username, roomid): v_user_pickle = red.get('v_user_' + username) v_user = pickle.loads(v_user_pickle) self.get_chatroom_detail(v_user, roomid)
def try_search_contact(self, username): v_user_pickle = red.get('v_user_' + username) v_user = pickle.loads(v_user_pickle) self.search_contact("zhengyaohong0724", v_user)
def try_re_login(self, username): v_user_pickle = red.get('v_user_' + username) v_user = pickle.loads(v_user_pickle) self.auto_auth(v_user, 'Q-z_hUogcAFKCP8rWgdF', '')
def try_heart_beat(self, username): v_user_pickle = red.get('v_user_' + username) v_user = pickle.loads(v_user_pickle) return self.heart_beat(v_user)
def try_get_new_message(self, username): v_user_pickle = red.get('v_user_' + username) v_user = pickle.loads(v_user_pickle) self.async_check(v_user) return True
def try_new_init(self, username): v_user_pickle = red.get('v_user_' + username) v_user = pickle.loads(v_user_pickle) # v_user = self.auto_auth(v_user, 'Q-z_hUogcAFKCP8rWgdF', '') self.new_init(v_user)
def try_send_message(self, username): v_user_pickle = red.get('v_user_' + username) v_user = pickle.loads(v_user_pickle) # self.send_text_msg(u"7784635084@chatroom", u"咚咚咚 现在是12点咯 我是你们的老公彭于晏 大家吃了吗", v_user) self.send_text_msg(u"zhengyaohong0724", u"11111", v_user)
# wx_user = "******" wx_user = "******" # 小小 # wx_user = "******" # wx_user = "******" # 点金 # wxid_sygscg13nr0g21 # wx_user = "******" # wxid_mynvgzqgnb5x22 # wx_user = "******" print "**************************" print "enter cmd :{}".format(wx_user) print "**************************" cmd = input() if cmd == 0: wx_bot.set_user_context(wx_user) v_user_pickle = red.get('v_user_' + wx_user) v_user = pickle.loads(v_user_pickle) UUid = u"667D18B1-BCE3-4AA2-8ED1-1FDC19446567" DeviceType = u"<k21>TP_lINKS_5G</k21><k22>中国移动</k22><k24>c1:cd:2d:1c:5b:11</k24>" wx_bot.auto_auth(v_user, UUid, DeviceType, False) elif cmd == 1: # wxid_ceapoyxs555k22 v_user_pickle = red.get('v_user_' + wx_user) # v_user_pickle = red.get('v_user_' + 'wxid_3cimlsancyfg22') v_user = pickle.loads(v_user_pickle) # wx_bot.send_text_msg('fat-phone', '112233', v_user) wx_bot.send_text_msg('8043482794@chatroom', '112233', v_user) elif cmd == 2: v_user_pickle = red.get('v_user_' + wx_user)