def submit_order_request(self): data = { 'secretStr': urllib.parse.unquote( self.query_ins.get_info_of_secret_str()), # 解密 'train_date': self.query_ins.left_date, # 出发时间 'back_train_date': self.query_ins.left_date, # 返程时间 'tour_flag': 'dc', # 旅途类型 'purpose_codes': 'ADULT', # 成人 | 学生 'query_from_station_name': self.query_ins.left_station, 'query_to_station_name': self.query_ins.arrive_station, } response = self.session.post(API_SUBMIT_ORDER_REQUEST, data) result = response.json() if result.get('data') == 'N': OrderLog.add_quick_log( OrderLog.MESSAGE_SUBMIT_ORDER_REQUEST_SUCCESS).flush() return True else: if (str(result.get('messages', '')).find('未处理') >= 0): # 未处理订单 stay_second(self.retry_time) OrderLog.add_quick_log( OrderLog.MESSAGE_SUBMIT_ORDER_REQUEST_FAIL.format( result.get('messages', '-'))).flush() return False
def get_queue_count(self): """ 获取队列人数 train_date Mon Jan 01 2019 00:00:00 GMT+0800 (China Standard Time) train_no 630000Z12208 stationTrainCode Z122 seatType 4 fromStationTelecode GZQ toStationTelecode RXW leftTicket CmDJZYrwUoJ1jFNonIgPzPFdMBvSSE8xfdUwvb2lq8CCWn%2Bzk1vM3roJaHk%3D purpose_codes 00 train_location QY _json_att REPEAT_SUBMIT_TOKEN 0977caf26f25d1da43e3213eb35ff87c :return: """ data = { # 'train_date': '{} 00:00:00 GMT+0800 (China Standard Time)'.format( datetime.datetime.today().strftime("%a %h %d %Y")), 'train_no': self.user_ins.ticket_info_for_passenger_form['queryLeftTicketRequestDTO']['train_no'], 'stationTrainCode': self.user_ins.ticket_info_for_passenger_form['queryLeftTicketRequestDTO'][ 'station_train_code'], 'seatType': self.query_ins.current_order_seat, 'fromStationTelecode': self.user_ins.ticket_info_for_passenger_form['queryLeftTicketRequestDTO'][ 'from_station'], 'toStationTelecode': self.user_ins.ticket_info_for_passenger_form['queryLeftTicketRequestDTO'][ 'to_station'], 'leftTicket': self.user_ins.ticket_info_for_passenger_form['leftTicketStr'], 'purpose_codes': self.user_ins.ticket_info_for_passenger_form['purpose_codes'], 'train_location': self.user_ins.ticket_info_for_passenger_form['train_location'], '_json_att': '', 'REPEAT_SUBMIT_TOKEN': self.user_ins.global_repeat_submit_token, } response = self.session.post(API_GET_QUEUE_COUNT, data) result = response.json() if 'data' in result and ('countT' in result['data'] or 'ticket' in result['data']): # 成功 """ "data": { "count": "66", "ticket": "0,73", "op_2": "false", "countT": "0", "op_1": "true" } """ ticket = result['data']['ticket'].split(',') # 暂不清楚具体作用 ticket_number = sum(map(int, ticket)) current_position = int(data.get('countT', 0)) OrderLog.add_quick_log( OrderLog.MESSAGE_GET_QUEUE_COUNT_SUCCESS.format( current_position, ticket_number)).flush() return True else: # 加入小黑屋 OrderLog.add_quick_log( OrderLog.MESSAGE_GET_QUEUE_COUNT_FAIL.format( result.get('messages', result.get('validateMessages', '-')))).flush() return False
def request_init_dc_page(self): """ 请求下单页面 拿到 token :return: """ data = {'_json_att': ''} response = self.session.post(API_INITDC_URL, data) html = response.text token = re.search(r'var globalRepeatSubmitToken = \'(.+?)\'', html) form = re.search(r'var ticketInfoForPassengerForm *= *(\{.+\})', html) order = re.search(r'var orderRequestDTO *= *(\{.+\})', html) # 系统忙,请稍后重试 if html.find('系统忙,请稍后重试') != -1: OrderLog.add_quick_log(OrderLog.MESSAGE_REQUEST_INIT_DC_PAGE_FAIL ).flush() # 重试无用,直接跳过 return False try: self.global_repeat_submit_token = token.groups()[0] self.ticket_info_for_passenger_form = json.loads( form.groups()[0].replace("'", '"')) self.order_request_dto = json.loads(order.groups()[0].replace( "'", '"')) except: pass # TODO Error return True
def submit_order_request(self): data = { 'secretStr': urllib.parse.unquote(self.query_ins.get_info_of_secret_str()), # 解密 'train_date': self.query_ins.left_date, # 出发时间 'back_train_date': self.query_ins.left_date, # 返程时间 'tour_flag': 'dc', # 旅途类型 'purpose_codes': 'ADULT', # 成人 | 学生 'query_from_station_name': self.query_ins.left_station, 'query_to_station_name': self.query_ins.arrive_station, } response = self.session.post(API_SUBMIT_ORDER_REQUEST, data) result = response.json() if result.get('data') == 'N': OrderLog.add_quick_log(OrderLog.MESSAGE_SUBMIT_ORDER_REQUEST_SUCCESS).flush() return True else: if (str(result.get('messages', '')).find('未处理') >= 0): # 未处理订单 # 0125 增加排队时长到 5 分钟之后,更多的是 排队失败,得通过拿到订单列表才能确认,再打个 TODO # self.order_id = 0 # 需要拿到订单号 TODO # return -1 pass OrderLog.add_quick_log( OrderLog.MESSAGE_SUBMIT_ORDER_REQUEST_FAIL.format( result.get('messages', CommonLog.MESSAGE_RESPONSE_EMPTY_ERROR))).flush() return False
def normal_order(self): order_request_res = self.submit_order_request() if order_request_res == -1: return self.order_did_success() elif not order_request_res: return init_res, self.is_slide, init_html = self.user_ins.request_init_dc_page() if not init_res: return slide_info = {} if self.is_slide: try: slide_info = Browser().request_init_slide(self.session, init_html) if not slide_info.get('session_id') or not slide_info.get('sig'): raise Exception() except Exception: OrderLog.add_quick_log('滑动验证码识别失败').flush() return OrderLog.add_quick_log('滑动验证码识别成功').flush() if not self.check_order_info(slide_info): return if not self.get_queue_count(): return if not self.confirm_single_for_queue(): return order_id = self.query_order_wait_time() if order_id: # 发送通知 self.order_id = order_id self.order_did_success() return True return False
def send_notification(self): num = 0 # 通知次数 sustain_time = self.notification_sustain_time if Config().EMAIL_ENABLED: # 邮件通知 Notification.send_email(Config().EMAIL_RECEIVER, OrderLog.MESSAGE_ORDER_SUCCESS_NOTIFICATION_TITLE, OrderLog.MESSAGE_ORDER_SUCCESS_NOTIFICATION_OF_EMAIL_CONTENT.format(self.order_id)) if Config().DINGTALK_ENABLED: # 钉钉通知 Notification.dingtalk_webhook( OrderLog.MESSAGE_ORDER_SUCCESS_NOTIFICATION_OF_EMAIL_CONTENT.format(self.order_id)) if Config().TELEGRAM_ENABLED: # Telegram推送 Notification.send_to_telegram( OrderLog.MESSAGE_ORDER_SUCCESS_NOTIFICATION_OF_EMAIL_CONTENT.format(self.order_id)) if Config().SERVERCHAN_ENABLED: # ServerChan通知 Notification.server_chan(Config().SERVERCHAN_KEY, OrderLog.MESSAGE_ORDER_SUCCESS_NOTIFICATION_TITLE, OrderLog.MESSAGE_ORDER_SUCCESS_NOTIFICATION_OF_EMAIL_CONTENT.format(self.order_id)) if Config().PUSHBEAR_ENABLED: # PushBear通知 Notification.push_bear(Config().PUSHBEAR_KEY, OrderLog.MESSAGE_ORDER_SUCCESS_NOTIFICATION_TITLE, OrderLog.MESSAGE_ORDER_SUCCESS_NOTIFICATION_OF_EMAIL_CONTENT.format(self.order_id)) while sustain_time: # TODO 后面直接查询有没有待支付的订单就可以 num += 1 if Config().NOTIFICATION_BY_VOICE_CODE: # 语音通知 OrderLog.add_quick_log(OrderLog.MESSAGE_ORDER_SUCCESS_NOTIFICATION_OF_VOICE_CODE_START_SEND.format(num)) Notification.voice_code(Config().NOTIFICATION_VOICE_CODE_PHONE, self.user_ins.get_name(), OrderLog.MESSAGE_ORDER_SUCCESS_NOTIFICATION_OF_VOICE_CODE_CONTENT.format( self.query_ins.left_station, self.query_ins.arrive_station)) else: break sustain_time -= self.notification_interval sleep(self.notification_interval) OrderLog.add_quick_log(OrderLog.MESSAGE_JOB_CLOSED).flush()
def send_notification(self): # num = 0 # 通知次数 # sustain_time = self.notification_sustain_time info_message = OrderLog.get_order_success_notification_info( self.query_ins) normal_message = OrderLog.MESSAGE_ORDER_SUCCESS_NOTIFICATION_OF_EMAIL_CONTENT.format( self.order_id, self.user_ins.user_name) if Config().EMAIL_ENABLED: # 邮件通知 Notification.send_email( Config().EMAIL_RECEIVER, OrderLog.MESSAGE_ORDER_SUCCESS_NOTIFICATION_TITLE, normal_message + info_message) if Config().DINGTALK_ENABLED: # 钉钉通知 Notification.dingtalk_webhook(normal_message + info_message) if Config().TELEGRAM_ENABLED: # Telegram推送 Notification.send_to_telegram(normal_message + info_message) if Config().SERVERCHAN_ENABLED: # ServerChan通知 Notification.server_chan( Config().SERVERCHAN_KEY, OrderLog.MESSAGE_ORDER_SUCCESS_NOTIFICATION_TITLE, normal_message + info_message) if Config().PUSHBEAR_ENABLED: # PushBear通知 Notification.push_bear( Config().PUSHBEAR_KEY, OrderLog.MESSAGE_ORDER_SUCCESS_NOTIFICATION_TITLE, normal_message + info_message) if Config().BARK_ENABLED: Notification.push_bark(normal_message + info_message) if Config().NOTIFICATION_BY_VOICE_CODE: # 语音通知 if Config().NOTIFICATION_VOICE_CODE_TYPE == 'dingxin': voice_info = { 'left_station': self.query_ins.left_station, 'arrive_station': self.query_ins.arrive_station, 'set_type': self.query_ins.current_seat_name, 'orderno': self.order_id } else: voice_info = OrderLog.MESSAGE_ORDER_SUCCESS_NOTIFICATION_OF_VOICE_CODE_CONTENT.format( self.query_ins.left_station, self.query_ins.arrive_station) OrderLog.add_quick_log( OrderLog. MESSAGE_ORDER_SUCCESS_NOTIFICATION_OF_VOICE_CODE_START_SEND) Notification.voice_code(Config().NOTIFICATION_VOICE_CODE_PHONE, self.user_ins.get_name(), voice_info) # 取消循环发送通知 # while sustain_time: # TODO 后面直接查询有没有待支付的订单就可以 # num += 1 # else: # break # sustain_time -= self.notification_interval # sleep(self.notification_interval) OrderLog.add_quick_log(OrderLog.MESSAGE_JOB_CLOSED).flush() return True
def check_order_info(self, slide_info=None): """ cancel_flag=2 bed_level_order_num=000000000000000000000000000000 passengerTicketStr= tour_flag=dc randCode= whatsSelect=1 _json_att= REPEAT_SUBMIT_TOKEN=458bf1b0a69431f34f9d2e9d3a11cfe9 :return: """ data = { # 'cancel_flag': 2, 'bed_level_order_num': '000000000000000000000000000000', 'passengerTicketStr': self.passenger_ticket_str, 'oldPassengerStr': self.old_passenger_str, 'tour_flag': 'dc', 'randCode': '', 'whatsSelect': '1', '_json_att': '', 'REPEAT_SUBMIT_TOKEN': self.user_ins.global_repeat_submit_token } if self.is_slide: data.update({ 'sessionId': slide_info['session_id'], 'sig': slide_info['sig'], 'scene': 'nc_login', }) response = self.session.post(API_CHECK_ORDER_INFO, data) result = response.json() if result.get('data.submitStatus'): # 成功 # ifShowPassCode 需要验证码 OrderLog.add_quick_log(OrderLog.MESSAGE_CHECK_ORDER_INFO_SUCCESS).flush() if result.get('data.ifShowPassCode') != 'N': self.is_need_auth_code = True # if ( ticketInfoForPassengerForm.isAsync == ticket_submit_order.request_flag.isAsync & & ticketInfoForPassengerForm.queryLeftTicketRequestDTO.ypInfoDetail != "") { 不需要排队检测 js TODO return True else: error = CommonLog.MESSAGE_API_RESPONSE_CAN_NOT_BE_HANDLE if not result.get('data.isNoActive'): error = result.get('data.errMsg', CommonLog.MESSAGE_RESPONSE_EMPTY_ERROR) else: if result.get('data.checkSeatNum'): error = '无法提交您的订单! ' + result.get('data.errMsg') else: error = '出票失败! ' + result.get('data.errMsg') OrderLog.add_quick_log(OrderLog.MESSAGE_CHECK_ORDER_INFO_FAIL.format(error)).flush() return False
def send_notification(self): num = 0 # 通知次数 sustain_time = self.notification_sustain_time while sustain_time: # TODO 后面直接查询有没有待支付的订单就可以 num += 1 if Config().NOTIFICATION_BY_VOICE_CODE: # 语音通知 OrderLog.add_quick_log(OrderLog.MESSAGE_ORDER_SUCCESS_NOTIFICATION_OF_VOICE_CODE_START_SEND.format(num)) Notification.voice_code(Config().NOTIFICATION_VOICE_CODE_PHONE, self.user_ins.get_name(), OrderLog.MESSAGE_ORDER_SUCCESS_NOTIFICATION_OF_VOICE_CODE_CONTENT.format( self.query_ins.left_station, self.query_ins.arrive_station)) sustain_time -= self.notification_interval sleep(self.notification_interval) OrderLog.add_quick_log(OrderLog.MESSAGE_JOB_CLOSED)
def query_order_wait_time(self): """ 排队查询 random 1546849953542 tourFlag dc _json_att REPEAT_SUBMIT_TOKEN 0977caf26f25d1da43e3213eb35ff87c :return: """ self.current_queue_wait = self.max_queue_wait self.queue_num = 0 while self.current_queue_wait: self.current_queue_wait -= self.wait_queue_interval self.queue_num += 1 # TODO 取消超时订单,待优化 data = { # 'random': str(random.random())[2:], 'tourFlag': 'dc', '_json_att': '', 'REPEAT_SUBMIT_TOKEN': self.user_ins.global_repeat_submit_token, } response = self.session.get(API_QUERY_ORDER_WAIT_TIME.format(urllib.parse.urlencode(data))) result = response.json() if result.get('status') and 'data' in result: """ "data": { "queryOrderWaitTimeStatus": true, "count": 0, "waitTime": -1, "requestId": 6487958947291482523, "waitCount": 0, "tourFlag": "dc", "orderId": "E222646122" } """ result_data = result['data'] order_id = result_data.get('orderId') if order_id: # 成功 return order_id elif result_data.get('waitTime') != -100: OrderLog.add_quick_log( OrderLog.MESSAGE_QUERY_ORDER_WAIT_TIME_WAITING.format(result_data.get('waitCount', 0), result_data.get('waitTime'))).flush() elif result_data.get('msg'): # 失败 对不起,由于您取消次数过多,今日将不能继续受理您的订票请求。1月8日您可继续使用订票功能。 # TODO 需要增加判断 直接结束 OrderLog.add_quick_log( OrderLog.MESSAGE_QUERY_ORDER_WAIT_TIME_FAIL.format(result_data.get('msg', '-'))).flush() stay_second(self.retry_time) return False elif result.get('messages') or result.get('validateMessages'): OrderLog.add_quick_log(OrderLog.MESSAGE_QUERY_ORDER_WAIT_TIME_FAIL.format( result.get('messages', result.get('validateMessages')))).flush() else: pass OrderLog.add_quick_log(OrderLog.MESSAGE_QUERY_ORDER_WAIT_TIME_INFO.format(self.queue_num)).flush() stay_second(self.wait_queue_interval) return False
def send_notification(self): num = 0 # 通知次数 sustain_time = self.notification_sustain_time if Config().EMAIL_ENABLED: # 邮件通知 Notification.send_email(Config().EMAIL_RECEIVER, OrderLog.MESSAGE_ORDER_SUCCESS_NOTIFICATION_TITLE, OrderLog.MESSAGE_ORDER_SUCCESS_NOTIFICATION_OF_EMAIL_CONTENT.format(self.order_id)) while sustain_time: # TODO 后面直接查询有没有待支付的订单就可以 num += 1 if Config().NOTIFICATION_BY_VOICE_CODE: # 语音通知 OrderLog.add_quick_log(OrderLog.MESSAGE_ORDER_SUCCESS_NOTIFICATION_OF_VOICE_CODE_START_SEND.format(num)) Notification.voice_code(Config().NOTIFICATION_VOICE_CODE_PHONE, self.user_ins.get_name(), OrderLog.MESSAGE_ORDER_SUCCESS_NOTIFICATION_OF_VOICE_CODE_CONTENT.format( self.query_ins.left_station, self.query_ins.arrive_station)) else: break sustain_time -= self.notification_interval sleep(self.notification_interval) OrderLog.add_quick_log(OrderLog.MESSAGE_JOB_CLOSED).flush()
def confirm_single_for_queue(self): """ 确认排队 passengerTicketStr oldPassengerStr randCode purpose_codes 00 key_check_isChange FEE6C6634A3EAA93E1E6CFC39A99E555A92E438436F18AFF78837CDB leftTicketStr CmDJZYrwUoJ1jFNonIgPzPFdMBvSSE8xfdUwvb2lq8CCWn%2Bzk1vM3roJaHk%3D train_location QY choose_seats seatDetailType 000 whatsSelect 1 roomType 00 dwAll N _json_att REPEAT_SUBMIT_TOKEN 0977caf26f25d1da43e3213eb35ff87c :return: """ data = { # 'passengerTicketStr': self.passenger_ticket_str, 'oldPassengerStr': self.old_passenger_str, 'randCode': '', 'purpose_codes': self.user_ins.ticket_info_for_passenger_form['purpose_codes'], 'key_check_isChange': self.user_ins.ticket_info_for_passenger_form['key_check_isChange'], 'leftTicketStr': self.user_ins.ticket_info_for_passenger_form['leftTicketStr'], 'train_location': self.user_ins.ticket_info_for_passenger_form['train_location'], 'choose_seats': '', 'seatDetailType': '000', 'whatsSelect': '1', 'roomType': '00', 'dwAll': 'N', '_json_att': '', 'REPEAT_SUBMIT_TOKEN': self.user_ins.global_repeat_submit_token, } if self.is_need_auth_code: # 目前好像是都不需要了,有问题再处理 pass response = self.session.post(API_CONFIRM_SINGLE_FOR_QUEUE, data) result = response.json() if 'data' in result: """ "data": { "submitStatus": true } """ if result.get('data.submitStatus'): # 成功 OrderLog.add_quick_log(OrderLog.MESSAGE_CONFIRM_SINGLE_FOR_QUEUE_SUCCESS).flush() return True else: # 加入小黑屋 TODO OrderLog.add_quick_log( OrderLog.MESSAGE_CONFIRM_SINGLE_FOR_QUEUE_ERROR.format( result.get('data.errMsg', CommonLog.MESSAGE_RESPONSE_EMPTY_ERROR))).flush() else: OrderLog.add_quick_log(OrderLog.MESSAGE_CONFIRM_SINGLE_FOR_QUEUE_FAIL.format( result.get('messages', CommonLog.MESSAGE_RESPONSE_EMPTY_ERROR))).flush() return False
def check_order_info(self): """ cancel_flag=2 bed_level_order_num=000000000000000000000000000000 passengerTicketStr= tour_flag=dc randCode= whatsSelect=1 _json_att= REPEAT_SUBMIT_TOKEN=458bf1b0a69431f34f9d2e9d3a11cfe9 :return: """ data = { # 'cancel_flag': 2, 'bed_level_order_num': '000000000000000000000000000000', 'passengerTicketStr': self.passenger_ticket_str, 'oldPassengerStr': self.old_passenger_str, 'tour_flag': 'dc', 'randCode': '', 'whatsSelect': '1', '_json_att': '', 'REPEAT_SUBMIT_TOKEN': self.user_ins.global_repeat_submit_token } response = self.session.post(API_CHECK_ORDER_INFO, data) result = response.json() if 'data' in result and result['data'].get('submitStatus'): # 成功 OrderLog.add_quick_log( OrderLog.MESSAGE_CHECK_ORDER_INFO_SUCCESS).flush() if result['data'].get("ifShowPassCode") != 'N': self.is_need_auth_code = True return True else: result_data = result.get('data', {}) OrderLog.add_quick_log( OrderLog.MESSAGE_CHECK_ORDER_INFO_FAIL.format( result_data.get('errMsg', result.get('messages', '-')))).flush() return False
def get_queue_count(self): """ 获取队列人数 train_date Mon Jan 01 2019 00:00:00 GMT+0800 (China Standard Time) train_no 630000Z12208 stationTrainCode Z122 seatType 4 fromStationTelecode GZQ toStationTelecode RXW leftTicket CmDJZYrwUoJ1jFNonIgPzPFdMBvSSE8xfdUwvb2lq8CCWn%2Bzk1vM3roJaHk%3D purpose_codes 00 train_location QY _json_att REPEAT_SUBMIT_TOKEN 0977caf26f25d1da43e3213eb35ff87c :return: """ data = { # 'train_date': '{} 00:00:00 GMT+0800 (China Standard Time)'.format( datetime.datetime.today().strftime("%a %h %d %Y")), 'train_no': self.user_ins.ticket_info_for_passenger_form['queryLeftTicketRequestDTO']['train_no'], 'stationTrainCode': self.user_ins.ticket_info_for_passenger_form['queryLeftTicketRequestDTO'][ 'station_train_code'], 'seatType': self.query_ins.current_order_seat, 'fromStationTelecode': self.user_ins.ticket_info_for_passenger_form['queryLeftTicketRequestDTO'][ 'from_station'], 'toStationTelecode': self.user_ins.ticket_info_for_passenger_form['queryLeftTicketRequestDTO'][ 'to_station'], 'leftTicket': self.user_ins.ticket_info_for_passenger_form['leftTicketStr'], 'purpose_codes': self.user_ins.ticket_info_for_passenger_form['purpose_codes'], 'train_location': self.user_ins.ticket_info_for_passenger_form['train_location'], '_json_att': '', 'REPEAT_SUBMIT_TOKEN': self.user_ins.global_repeat_submit_token, } response = self.session.post(API_GET_QUEUE_COUNT, data) result = response.json() if result.get('status', False): # 成功 """ "data": { "count": "66", "ticket": "0,73", "op_2": "false", "countT": "0", "op_1": "true" } """ # if result.get('isRelogin') == 'Y': # 重新登录 TODO ticket = result.get('data.ticket').split(',') # 余票列表 # 这里可以判断 是真实是 硬座还是无座,避免自动分配到无座 ticket_number = ticket[0] # 余票 if ticket_number != '充足' or int(ticket_number) <= 0: if self.query_ins.current_seat == SeatType.NO_SEAT: # 允许无座 ticket_number = ticket[1] if result.get('data.op_2') == 'true': OrderLog.add_quick_log( OrderLog.MESSAGE_GET_QUEUE_LESS_TICKET).flush() return False current_position = int(result.get('data.countT', 0)) OrderLog.add_quick_log( OrderLog.MESSAGE_GET_QUEUE_COUNT_SUCCESS.format( current_position, ticket_number)).flush() return True else: # 加入小黑屋 OrderLog.add_quick_log( OrderLog.MESSAGE_GET_QUEUE_COUNT_FAIL.format( result.get('messages', result.get('validateMessages', '-')))).flush() return False
def request_init_slide(self, session, html): """ 处理滑块,拿到 session_id, sig """ OrderLog.add_quick_log('正在识别滑动验证码...').flush() return asyncio.get_event_loop_policy().new_event_loop().run_until_complete( self.__request_init_slide(session, html))