def run(self): status = self.pre_check() if not status: return Log.v("正在查询车次余票信息") count = 1 while True: self.maintain_mode() self.heart_beat_mode() dates = DispatcherTool.query_travel_dates if Config.basic_config.use_station_group: station_groups = [(v.from_station, v.to_station) for v in Config.basic_config.station_groups] else: station_groups = list( product(Config.basic_config.from_stations, Config.basic_config.to_stations)) query_params = list(product(dates, station_groups)) for query_params in query_params: Log.v("查询第 {0} 次".format(count)) n = datetime.datetime.now() data = DispatcherTool.run(query_params) count += 1 self.submit_order(data) DispatcherTool.output_delta_time(n) if self.order_id or self.unfinished_order: break if self.order_id or self.unfinished_order: break self.notice_user()
def _get_queue_count(self): form_data = { 'train_date': datetime.datetime.strptime( self.ticket_passenger_info['queryLeftTicketRequestDTO']['train_date'], '%Y%m%d').strftime( '%b %a %d %Y 00:00:00 GMT+0800') + ' (中国标准时间)', 'train_no': self.ticket_passenger_info['queryLeftTicketRequestDTO']['train_no'], 'stationTrainCode': self.train.stationTrainCode.value, 'seatType': self.seat_type.sys_code, 'fromStationTelecode': self.train.from_station_code.value, 'toStationTelecode': self.train.to_station_code.value, 'leftTicket': self.ticket_passenger_info['leftTicketStr'], 'purpose_codes': self.ticket_passenger_info['purpose_codes'], 'train_location': self.ticket_passenger_info['train_location'], '_json_att': '', 'REPEAT_SUBMIT_TOKEN': self.token } Log.v("正在获取排队信息以及余票信息中...") json_response = send_requests(LOGIN_SESSION, self.URLS['getQueueCount'], data=form_data) status, msg = submit_response_checker(json_response, ["status"], True, "获取余票信息成功") if status: self.left_tickets = json_response['data']['ticket'] Log.v("票数剩余{0}张, 排队人数为{1}人".format( self.left_tickets, json_response['data']['count'] )) else: BlackTrains.add_train(self.train) return status, msg
def login(self): if not LOGIN_SESSION.cookies.get("RAIL_EXPIRATION") or \ not LOGIN_SESSION.cookies.get("RAIL_DEVICEID"): status, msg = self._init() if not status: return status, msg # status, msg = self._get_device_fingerprint() # if not status: # Log.v("设备ID获取失败") # return status, msg # self._init2() captcha = Captcha("normal") status, msg = captcha.verify() if not status: Log.v("验证码校验失败") return status, msg payload = { 'username': Config.train_account.user, 'password': Config.train_account.pwd, 'appid': 'otn', 'answer': captcha.results } json_response = send_requests(LOGIN_SESSION, self.URLS['login'], data=payload) result, msg = json_status(json_response, [], '0') if not result: return (False, json_response.get("result_message", None)) \ if isinstance(json_response, dict) else (False, '登录接口提交返回数据出现问题') self._passportredirect() result, msg, apptk = self._uamtk() if not result: Log.v(msg) return False, msg status, msg = self._uamauthclient(apptk) return status, msg
def verifyhandle_freeapi(self): Log.v("使用免费api进行验证码识别") img_base64 = base64.b64encode(self.generator_image()).decode() r = requests.post(FREE_CAPTCHA_URL, json={"base64": img_base64}) try: check = r.json()["data"]["check"] except (KeyError, JSONDecodeError): return False, "免费打码接口返回出现问题" v = requests.post(FREE_CAPTCHA_CHECK_URL, json={ "=": "", "check": check, "img_buf": img_base64, "logon": 1, "type": "D" }, headers=FREE_CAPTCHA_HEADERS) try: data = ast.literal_eval(v.json()["res"]) except KeyError: return False, "免费打码接口返回出现问题" except SyntaxError: return False, "免费打码接口返回出现问题" if type(data[0]) == int: self.results = ','.join(map(str, list(data))) else: self.results = ','.join(map(str, list(chain(*data)))) return self.check(self.results)
def heart_beat_mode(self): if Config.auto_code_enable: status, msg = self.online_checker() Log.v(msg) if not status: Log.e("心跳登录失败,继续重试中,建议手动检查原因再尝试重启") self.online_checker_now()
def _uamtk(self): json_data = send_requests(LOGIN_SESSION, self.URLS["uamtk"], data={'appid': 'otn'}) Log.d(json_data) result, msg = json_status(json_data, ["result_message", "newapptk"]) if not result: return result, msg, None else: return result, msg, json_data["newapptk"]
def maintain_mode(self): if self.check_maintain(): Log.v("12306系统每天 23:00 - 6:00 之间 维护中, 程序暂时停止运行") maintain_time = self.delta_maintain_time() Log.v("{0}小时 {1}分钟 {2}秒之后重新启动".format( maintain_time.seconds // 3600, (maintain_time.seconds // 60) % 60, maintain_time.seconds % 3600 % 60)) time.sleep(self.delta_maintain_time().total_seconds())
def filter_black_trains(self): result = [] for v in self.result: flag = BlackTrains.check(v[1]) if flag: Log.v("{0} 车次已经在小黑屋".format(v[1].stationTrainCode.value)) else: result.append(copy.copy(v)) return result
def _uamauthclient(self, apptk): json_response = send_requests(LOGIN_SESSION, self.URLS['uamauthclient'], data={'tk': apptk}) status, msg = json_status(json_response, ["username", "result_message"]) if status: Log.v("欢迎 {0} 登录".format(json_response["username"])) return status, msg
def _check_order_status_queue(self): params = { 'orderSequence_no': self.order_id, '_json_att': '', 'REPEAT_SUBMIT_TOKEN': self.token, } Log.v("检查已提交的订单的状态中...") json_response = send_requests(LOGIN_SESSION, self.URLS['resultOrderForQueue'], params=params) status, msg = submit_response_checker(json_response, ["status", "data.submitStatus"], True, "订单已经成功提交") return status, msg
def pre_check(self): if Config.cdn_enable: CdnStorage.load_exists() if not self.login(): return False p_status = self.query_passengers() if not p_status: return False if not Config.auto_code_enable: Log.v("未开启自动打码功能, 不检测用户登录状态") return True
def run(self, travel_date): Log.v("当前查询日期为 {}".format(travel_date)) q = Query(travel_date) data = q.filter() if not data: Log.v("日期 {0} 满足条件的车次暂无余票, 正在重新查询".format(travel_date)) for v in data: print("\t\t\t当前座位席别 {}".format(v[0].name)) q.pretty_output(v[1]) return data
def getcaptcha(): while True: try: data = get_captcha_image( LOGIN_SESSION, LOGIN_URL_MAPPING["normal"]["captcha"]) break except ResponseCodeError: Log.v("获取验证码信息失败,重试获取验证码中...") continue img_binary = base64.b64decode(data["image"]) return img_binary
def generator_image(self): while True: data = self.getcaptcha() try: img = Image.open(BytesIO(data)) img.close() break except OSError: Log.e("获取验证码图片失败, 重试获取") continue return data
def run(self): Log.v("您已开启cdn加速") Log.v("正在检查cdn列表可用状态....(大概将会花费10分钟左右)") self.pool.map(self.verify, self.raw_cdn_list) self.status = True Log.v("共获取{0}个可用的cdn".format(len(self.result))) Log.v("各个cdn的等级情况如下(level等级越低证明, cdn的连接更快):") level_result = [v.level for v in self.result] level_types = set(level_result) for v in level_types: Log.v("level {0} 共有 {1} 个".format(v, level_result.count(v)))
def run(self): while self.retry_time: for v in FAST_PIPELINE: status, msg = getattr(self, v)() if not status: self.retry_time -= 1 break else: Log.v("提交订单成功") return True Log.v("提交订单失败") return False
def run(self, query_data): Log.v("当前查询日期为 **{0}** 出发地 **{1}** 目的地 **{2}**".format( query_data[0], query_data[1][0], query_data[1][1])) q = Query(query_data[0], query_data[1][0], query_data[1][1]) data = q.filter() if not data: Log.v("日期 **{0}** 出发地 **{1}** 目的地 **{2}** 满足条件的车次暂无余票, 正在重新查询". format(query_data[0], query_data[1][0], query_data[1][1])) for v in data: print("\t\t\t当前座位席别 {}".format(v[0].name)) q.pretty_output(v[1]) return data
def filter(self): # 先过滤席位 self.filter_by_seat() # 过滤小黑屋的车次 self.result = self.filter_black_trains() if Config.basic_config.manual_trainnum_enable: self.result = self.filter_train_num() else: self.result = self.filter_train_time() self.result = self.filter_train_type() if self.result: Log.v("查找到符合配置的车次信息: {0}".format(','.join( [v[1].stationTrainCode.value for v in self.result]))) return self.result
def verifyhandle_hand(self): img = Image.open(BytesIO(self.generator_image())) img.show() Log.v(""" ----------------- | 0 | 1 | 2 | 3 | ----------------- | 4 | 5 | 6 | 7 | ----------------- """) results = input("输入验证码索引(见上图,以','分割): ") img.close() trans = self.trans_captcha_results(results) self.results = trans return self.check(trans)
def verifyhandle_ruokuai(self): c = RClient() data = c.rk_create(self.generator_image()) if "Result" in data: trans = self.trans_captcha_results(','.join( [str(int(v) - 1) for v in data["Result"]])) self.results = trans return self.check(trans) else: if "Error" in data and data["Error"]: Log.e( "打码平台错误: {0}, 请登录打码平台查看-http://www.ruokuai.com/client/index?6726" .format(data["Error"])) return False, "若快打码平台错误"
def online_checker(self): # 两分钟检测一次 flag = OnlineCheckerTool.should_check_online(datetime.datetime.now()) if flag: status, msg = OnlineCheckerTool.checker() OnlineCheckerTool.update_check_time() if not status: Log.v("用户登录失效, 正在为您重试登录") login_status = self.login() if not login_status: return False, "重试登录失败" else: return status, msg else: status, msg = True, "用户状态检测:未到检测时间" return status, msg
def _check_order_info(self): form_data = { 'cancel_flag': self.ticket_passenger_info['orderRequestDTO']['cancel_flag'] or '2', 'bed_level_order_num': self.ticket_passenger_info['orderRequestDTO']['bed_level_order_num'] \ or '000000000000000000000000000000', 'passengerTicketStr': build_passenger_ticket_string(self.seat_type, self.passenger_data), 'oldPassengerStr': build_oldpassenger_ticket_string(self.passenger_data), 'tour_flag': self.ticket_passenger_info['tour_flag'] or 'dc', 'randCode': '', 'whatsSelect': '1', '_json_att': '', 'REPEAT_SUBMIT_TOKEN': self.token, } Log.v("正在提交检查订单状态请求") json_response = send_requests(LOGIN_SESSION, self.URLS['checkOrderInfo'], data=form_data) status, msg = submit_response_checker(json_response, ["status", "data.submitStatus"], True, "校验订单成功") return status, msg
def notice_user(self): if self.order_id: Log.v("车票信息:") for order_ticket in self.order_tickets: print(order_ticket) # 抢票成功发邮件信息 NoticeTool.notice( msg_type=2, **{ "order_no": self.order_id, "ticket_info": "</br>".join([v.to_html() for v in self.order_tickets]) }) else: Log.v("您有未完成订单, 请及时处理后再运行程序") NoticeTool.notice(msg_type=3)
def check(self, results): form_data = { 'randCode': results, 'rand': 'sjrand', } json_response = send_requests( LOGIN_SESSION, LOGIN_URL_MAPPING["other"]["captchaCheck"], data=form_data) Log.v('other login captcha verify: %s' % json_response) def verify(response): return response['status'] and self.success_code == response[ 'data']['result'] v = verify(json_response) return v, "Error" if not v else v
def login(self): count = 0 while self.retry_login_time >= count: login_instance = NormalLogin() Log.v("正在为您登录") status, msg = login_instance.login() if not status: count += 1 Log.e(msg) Log.v("登录失败, 重试{0}次".format(count)) if self.retry_login_time >= count else "" continue else: Log.v("登录成功") break if self.retry_login_time < count: Log.v("重试次数已经超过设置") return False return True
def check_current_mode(self): if (not Config.presale_enable) or self.pre_sale_end: return False else: now = datetime.datetime.now() open_times = list( map(format_time, Config.presale_config.start_times)) f = lambda x: x - now <= self.delta_stop_time and now - x <= self.delta_continue_time result = any(filter(f, open_times)) if result: Log.v("当前处于预售模式,不再处理正常模式下的日期查询") return True check_result = list( filter(lambda x: now > x + self.delta_continue_time, open_times)) if check_result and all(check_result): self.pre_sale_end = True return False return False
def _confirm_single_or_go_for_queue(self): form_data = { 'passengerTicketStr': build_passenger_ticket_string(self.seat_type, self.passenger_data), 'oldPassengerStr': build_oldpassenger_ticket_string(self.passenger_data), 'randCode': '', 'purpose_codes': self.ticket_passenger_info['purpose_codes'], 'key_check_isChange': self.ticket_passenger_info['key_check_isChange'], 'leftTicketStr': self.ticket_passenger_info['leftTicketStr'], 'train_location': self.ticket_passenger_info['train_location'], 'choose_seats': '', # 暂时未加选座 'seatDetailType': '000', 'whatsSelect': '1', 'roomType': '00', 'dwAll': 'N', '_json_att': '', 'REPEAT_SUBMIT_TOKEN': self.token, } Log.v("正在为你请求排队提交订单") json_response = send_requests(LOGIN_SESSION, self.URLS['confirmForQueue'], data=form_data) status, msg = submit_response_checker(json_response, ["status", "data.submitStatus"], True, "请求排队成功") if not status: BlackTrains.add_train(self.train) return status, msg
def _wait_for_order_id(self): # 排队逻辑 t = datetime.datetime.now() # 排队10分钟 delta = datetime.timedelta(minutes=10) while not self.order_id: loop_time = datetime.datetime.now() status, msg = self._query_order_wait_time() Log.v(msg) s, data = find_by_phrase(msg) if s: self.break_submit, self.break_msg = False, data["msg"] return self.break_submit, self.break_msg # 5s 获取排队信息 time.sleep(5) if self.order_id: return True, "OK" if loop_time > t + delta: BlackTrains.add_train(self.train) return False, "提交超时" return False, "排队失败"
def _query_order_wait_time(self): params = { 'random': '%10d' % (time.time() * 1000), 'tourFlag': self.ticket_passenger_info['tour_flag'] or 'dc', '_json_att': '', 'REPEAT_SUBMIT_TOKEN': self.token } Log.v("获取订单排队信息...") json_response = send_requests(LOGIN_SESSION, self.URLS['queryOrderWaitTime'], params=params) status, msg = submit_response_checker(json_response, ["status"], True, "排队请求成功") if status: self.wait_time = json_response['data']['waitTime'] self.order_id = json_response['data']['orderId'] people_count = json_response["data"]["waitCount"] msg += " 排队等待时间预计还剩 {0} ms, 排队人数还剩 {1} 人".format( self.wait_time, people_count) if not self.order_id: msg += "\t 订单暂未生成" if "msg" in json_response["data"]: msg += "\t {0}".format(json_response["data"]["msg"]) return status, msg
def rk_create(self, im_string, timeout=60): """ im: 图片字节 im_type: 题目类型 """ params = { 'typeid': 6113, 'timeout': timeout, } params.update(self.base_params) files = {'image': ('a.jpg', im_string)} while True: try: r = requests.post('http://api.ruokuai.com/create.json', data=params, files=files, headers=self.headers) # Log.v("使用若快进行验证码识别") data = r.json() break except requests.RequestException: Log.w("提交若快打码请求出现问题, 正在重试中...") continue Log.d(data) return data