def login(): """ 用户登录 :return: """ username = request.json.get('username', None) password = request.json.get('password', None) if username and password and username == Config().WEB_USER.get('username') and password == Config().WEB_USER.get( 'password'): access_token = create_access_token(identity=username) return jsonify(access_token=access_token) return jsonify({"msg": "用户名或密码错误"}), 422
def handler_exit(self, *args, **kwargs): """ 程序退出 :param args: :param kwargs: :return: """ if Config.is_cluster_enabled(): from py12306.cluster.cluster import Cluster Cluster().left_cluster() sys.exit()
def order(self): """ 开始下单 下单模式 暂时不清楚,使用正常步骤下单 :return: """ # Debug if Config().IS_DEBUG: self.order_id = 'test' self.order_did_success() return random.randint(0, 10) > 7 return self.normal_order()
def load_user(self): if Config().is_cluster_enabled(): return cookie_path = self.get_cookie_path() if path.exists(cookie_path): with open(self.get_cookie_path(), 'rb') as f: cookie = pickle.load(f) self.cookie = True self.session.cookies.update(cookie) self.did_loaded_user() return True return None
def send_email_by_smtp(self, to, title, content): import smtplib from email.message import EmailMessage to = to if isinstance(to, list) else [to] message = EmailMessage() message['Subject'] = title message['From'] = Config().EMAIL_SENDER message['To'] = to message.set_content(content) try: server = smtplib.SMTP(Config().EMAIL_SERVER_HOST) server.login(Config().EMAIL_SERVER_USER, Config().EMAIL_SERVER_PASSWORD) server.ehlo() server.starttls() server.send_message(message) server.quit() CommonLog.add_quick_log( CommonLog.MESSAGE_SEND_EMAIL_SUCCESS).flush() except Exception as e: CommonLog.add_quick_log( CommonLog.MESSAGE_SEND_EMAIL_FAIL.format(e)).flush()
def app_available_check(): # return True # Debug if Config().IS_DEBUG: return True now = time_now() if (now.hour >= 23 and now.minute >= 30) or now.hour < 6: CommonLog.add_quick_log( CommonLog.MESSAGE_12306_IS_CLOSED.format(time_now())).flush() open_time = datetime.datetime(now.year, now.month, now.day, 6) if open_time < now: open_time += datetime.timedelta(1) sleep((open_time - now).seconds) return True
def push_to_bark(self, content): bark_url = Config().BARK_PUSH_URL if not bark_url: return False response = self.session.request(url=bark_url + '/' + content, method='get') result = response.json() response_status = result.get('code') if response_status == 200: CommonLog.add_quick_log(CommonLog.MESSAGE_SEND_BARK_SUCCESS).flush() else: response_error_message = result.get('message') CommonLog.add_quick_log(CommonLog.MESSAGE_SEND_BARK_FAIL.format(response_error_message)).flush()
def index(): file = Config().WEB_ENTER_HTML_PATH result = '' with open(file, 'r', encoding='utf-8') as f: result = f.read() config = { 'API_BASE_URL': '' # TODO 自定义 Host } result = re.sub( r'<script>[\s\S]*?<\/script>', '<script>window.config={}</script>'.format(json.dumps(config)), result) return result
def load_user_from_remote(self): cookie = self.cluster.get_user_cookie(self.key) info = self.cluster.get_user_info(self.key) if Config().is_slave() and (not cookie or not info): while True: # 子节点只能取 UserLog.add_quick_log( UserLog.MESSAGE_USER_COOKIE_NOT_FOUND_FROM_REMOTE.format( self.user_name)).flush() stay_second(self.retry_time) return self.load_user_from_remote() if info: self.info = info if cookie: self.session.cookies.update(cookie) if not self.cookie: # 第一次加载 self.cookie = True if not Config().is_slave(): self.did_loaded_user() else: self.is_ready = True # 设置子节点用户 已准备好 UserLog.print_welcome_user(self) return True return False
def handle_seats(self, allow_seats, ticket_info): for seat in allow_seats: # 检查座位是否有票 self.set_seat(seat) ticket_of_seat = ticket_info[self.current_seat] if not self.is_has_ticket_by_seat(ticket_of_seat): # 座位是否有效 continue QueryLog.print_ticket_seat_available(left_date=self.get_info_of_left_date(), train_number=self.get_info_of_train_number(), seat_type=seat, rest_num=ticket_of_seat) if not self.is_member_number_valid(ticket_of_seat): # 乘车人数是否有效 if self.allow_less_member: self.member_num_take = int(ticket_of_seat) QueryLog.print_ticket_num_less_than_specified(ticket_of_seat, self) else: QueryLog.add_quick_log( QueryLog.MESSAGE_GIVE_UP_CHANCE_CAUSE_TICKET_NUM_LESS_THAN_SPECIFIED).flush() continue if Const.IS_TEST: return # 检查完成 开始提交订单 QueryLog.print_ticket_available(left_date=self.get_info_of_left_date(), train_number=self.get_info_of_train_number(), rest_num=ticket_of_seat) if User.is_empty(): QueryLog.add_quick_log(QueryLog.MESSAGE_USER_IS_EMPTY_WHEN_DO_ORDER.format(self.retry_time)) return stay_second(self.retry_time) order_result = False user = self.get_user() if not user: QueryLog.add_quick_log(QueryLog.MESSAGE_ORDER_USER_IS_EMPTY.format(self.retry_time)) return stay_second(self.retry_time) lock_id = Cluster.KEY_LOCK_DO_ORDER + '_' + user.key if Config().is_cluster_enabled(): if self.cluster.get_lock(lock_id, Cluster.lock_do_order_time, {'node': self.cluster.node_name}): # 获得下单锁 order_result = self.do_order(user) if not order_result: # 下单失败,解锁 self.cluster.release_lock(lock_id) else: QueryLog.add_quick_log( QueryLog.MESSAGE_SKIP_ORDER.format(self.cluster.get_lock_info(lock_id).get('node'), user.user_name)) stay_second(self.retry_time) # 防止过多重复 else: order_result = self.do_order(user) # 任务已成功 通知集群停止任务 if order_result: Event().job_destroy({'name': self.job_name})
def send_to_telegram_bot(self, content): bot_api_url = Config().TELEGRAM_BOT_API_URL if not bot_api_url: return False data = { 'text': content } response = self.session.request(url=bot_api_url, method='POST', data=data) result = response.json().get('result') response_status = result.get('statusCode') if response_status == 200: CommonLog.add_quick_log(CommonLog.MESSAGE_SEND_TELEGRAM_SUCCESS).flush() else: response_error_message = result.get('description') CommonLog.add_quick_log(CommonLog.MESSAGE_SEND_TELEGRAM_FAIL.format(response_error_message)).flush()
def get_user_passengers(self): if self.passengers: return self.passengers response = self.session.post(API_USER_PASSENGERS) result = response.json() if result.get('data.normal_passengers'): self.passengers = result.get('data.normal_passengers') # 将乘客写入到文件 with open(Config().USER_PASSENGERS_FILE % self.user_name, 'w', encoding='utf-8') as f: f.write( json.dumps(self.passengers, indent=4, ensure_ascii=False)) return self.passengers else: UserLog.add_quick_log( UserLog.MESSAGE_GET_USER_PASSENGERS_FAIL.format( result.get('messages', CommonLog.MESSAGE_RESPONSE_EMPTY_ERROR), self.retry_time)).flush() if Config().is_slave(): self.load_user_from_remote() # 加载最新 cookie stay_second(self.retry_time) return self.get_user_passengers()
def app_available_check(): if Config().IS_DEBUG: return True now = time_now() if now.weekday() == 1 and (now.hour > 23 and now.minute > 30 or now.hour < 5): CommonLog.add_quick_log(CommonLog.MESSAGE_12306_IS_CLOSED.format(time_now())).flush() open_time = datetime.datetime(now.year, now.month, now.day, 5) if open_time < now: open_time += datetime.timedelta(1) sleep((open_time - now).seconds) elif 1 < now.hour < 5: CommonLog.add_quick_log(CommonLog.MESSAGE_12306_IS_CLOSED.format(time_now())).flush() open_time = datetime.datetime(now.year, now.month, now.day, 5) sleep((open_time - now).seconds) return True
def query_by_date(self, date): """ 通过日期进行查询 :return: """ from py12306.helpers.cdn import Cdn QueryLog.add_log(('\n' if not is_main_thread() else '') + QueryLog.MESSAGE_QUERY_START_BY_DATE.format( date, self.left_station, self.arrive_station)) url = LEFT_TICKETS.get('url').format( left_date=date, left_station=self.left_station_code, arrive_station=self.arrive_station_code, type='leftTicket/queryZ') if Config.is_cdn_enabled() and Cdn().is_ready: self.is_cdn = True return self.query.session.cdn_request(url, timeout=self.query_time_out, allow_redirects=False) self.is_cdn = False if Config.is_zf(): headers = ZFProxyUtil.getProxySign() proxy = ZFProxyUtil.getProxy() yp_by_data = self.query.session.get(url, headers=headers, proxies=proxy, verify=False, allow_redirects=False, timeout=self.query_time_out) else: yp_by_data = self.query.session.get(url, verify=False, allow_redirects=False, timeout=self.query_time_out) return yp_by_data
def flush(cls, sep='\n', end='\n', file=None, exit=False, publish=True): from py12306.cluster.cluster import Cluster self = cls() logs = self.get_logs() # 输出到文件 if file == None and Config( ).OUT_PUT_LOG_TO_FILE_ENABLED and not Const.IS_TEST: # TODO 文件无法写入友好提示 file = open(Config().OUT_PUT_LOG_TO_FILE_PATH, 'a', encoding='utf-8') if not file: file = None # 输出日志到各个节点 if publish and self.quick_log and Config().is_cluster_enabled( ) and Cluster().is_ready: # f = io.StringIO() with redirect_stdout(f): print(*logs, sep=sep, end='' if end == '\n' else end) out = f.getvalue() Cluster().publish_log_message(out) else: print(*logs, sep=sep, end=end, file=file) self.empty_logs(logs) if exit: sys.exit()
def query_by_date(self, date): """ 通过日期进行查询 :return: """ from py12306.helpers.cdn import Cdn QueryLog.add_log(('\n' if not is_main_thread() else '') + QueryLog.MESSAGE_QUERY_START_BY_DATE.format(date, self.left_station, self.arrive_station)) url = LEFT_TICKETS.get('url').format(left_date=date, left_station=self.left_station_code, arrive_station=self.arrive_station_code, type=self.query.api_type) if Config.is_cdn_enabled() and Cdn().is_ready: self.is_cdn = True return self.query.session.cdn_request(url, timeout=self.query_time_out, allow_redirects=True) self.is_cdn = False return self.query.session.get(url, timeout=self.query_time_out, allow_redirects=True)
def start(self): # return # DEBUG QueryLog.init_data() stay_second(3) # 多线程 while True: if Config().QUERY_JOB_THREAD_ENABLED: # 多线程 if not self.is_in_thread: self.is_in_thread = True create_thread_and_run(jobs=self.jobs, callback_name='run', wait=Const.IS_TEST) if Const.IS_TEST: return stay_second(self.retry_time) else: if not self.jobs: break self.is_in_thread = False jobs_do(self.jobs, 'run') if Const.IS_TEST: return
def request(self, *args, **kwargs): # 拦截所有错误 try: if not 'timeout' in kwargs: from py12306.config import Config kwargs['timeout'] = Config().TIME_OUT_OF_REQUEST response = super().request(*args, **kwargs) return response except RequestException as e: from py12306.log.common_log import CommonLog if e.response: response = e.response else: response = HTMLResponse(HTMLSession) # response.status_code = 500 expand_class(response, 'json', Request.json) response.reason = response.reason if response.reason else CommonLog.MESSAGE_RESPONSE_EMPTY_ERROR return response
def request_device_id(self): """ :return: """ print("cookie获取中") driver = webdriver.Chrome(executable_path=Config().CHROME_PATH) driver.get("https://www.12306.cn/index/index.html") time.sleep(10) for c in driver.get_cookies(): print() cookie = dict() if c.get("name") == "RAIL_DEVICEID" or c.get( "name") == "RAIL_EXPIRATION": cookie[c.get('name')] = c.get('value') # print(f"获取cookie: {cookie}") self.session.cookies.update(cookie)
def send_voice_code_of_dingxin(self, phone, name='', info={}): """ 发送语音验证码 ( 鼎信 ) 购买地址 https://market.aliyun.com/products/56928004/cmapi026600.html?spm=5176.2020520132.101.2.51547218rkAXxy :return: """ appcode = Config().NOTIFICATION_API_APP_CODE if not appcode: CommonLog.add_quick_log(CommonLog.MESSAGE_EMPTY_APP_CODE).flush() return False data = { 'tpl_id': 'TP1901174', 'phone': phone, 'param': 'name:{name},job_name:{left_station}到{arrive_station}{set_type},orderno:{orderno}' .format(name=name, left_station=info.get('left_station'), arrive_station=info.get('arrive_station'), set_type=info.get('set_type'), orderno=info.get('orderno')) } response = self.session.request( url=API_NOTIFICATION_BY_VOICE_CODE_DINGXIN, method='POST', data=data, headers={'Authorization': 'APPCODE {}'.format(appcode)}) result = response.json() response_message = result.get('return_code') if response.status_code in [400, 401, 403]: return CommonLog.add_quick_log( CommonLog.MESSAGE_VOICE_API_FORBID).flush() if response.status_code == 200 and result.get( 'return_code') == '00000': CommonLog.add_quick_log( CommonLog.MESSAGE_VOICE_API_SEND_SUCCESS.format( response_message)).flush() return True else: return CommonLog.add_quick_log( CommonLog.MESSAGE_VOICE_API_SEND_FAIL.format( response_message)).flush()
def refresh_jobs(self): """ 更新任务 :return: """ allow_jobs = [] for job in self.query_jobs: id = md5(job) job_ins = objects_find_object_by_key_value(self.jobs, 'id', id) # [1 ,2] if not job_ins: job_ins = self.init_job(job) if Config().QUERY_JOB_THREAD_ENABLED: # 多线程重新添加 create_thread_and_run(jobs=job_ins, callback_name='run', wait=Const.IS_TEST) allow_jobs.append(job_ins) for job in self.jobs: # 退出已删除 Job if job not in allow_jobs: job.destroy() QueryLog.print_init_jobs(jobs=self.jobs)
def dashboard(): """ 状态统计 任务数量,用户数量,查询次数 节点信息(TODO) :return: """ from py12306.log.query_log import QueryLog query_job_count = len(Query().jobs) user_job_count = len(User().users) query_count = QueryLog().data.get('query_count') res = { 'query_job_count': query_job_count, 'user_job_count': user_job_count, 'query_count': query_count, } if Config().CDN_ENABLED: from py12306.helpers.cdn import Cdn res['cdn_count'] = len(Cdn().available_items) return jsonify(res)
def check_master(self): """ 检测主节点是否可用 :return: """ master = self.have_master() if master == self.node_name: # 动态提升 self.is_master = True else: self.is_master = False if not master: if Config().NODE_SLAVE_CAN_BE_MASTER: # 提升子节点为主节点 slave = list(self.nodes)[0] self.session.hset(self.KEY_NODES, slave, self.KEY_MASTER) self.publish_log_message(ClusterLog.MESSAGE_ASCENDING_MASTER_NODE.format(slave, ClusterLog.get_print_nodes( self.get_nodes()))) return True else: self.publish_log_message(ClusterLog.MESSAGE_MASTER_DID_LOST.format(self.retry_time)) stay_second(self.retry_time) os._exit(1) # 退出整个程序
def send_voice_code_of_yiyuan(self, phone, name='', content=''): """ 发送语音验证码 购买地址 https://market.aliyun.com/products/57126001/cmapi019902.html?spm=5176.2020520132.101.5.37857218O6iJ3n :return: """ appcode = Config().NOTIFICATION_API_APP_CODE if not appcode: CommonLog.add_quick_log(CommonLog.MESSAGE_EMPTY_APP_CODE).flush() return False body = {'userName': name, 'mailNo': content} params = { 'content': body, 'mobile': phone, 'sex': 2, 'tNum': 'T170701001056' } response = self.session.request( url=API_NOTIFICATION_BY_VOICE_CODE + urllib.parse.urlencode(params), method='GET', headers={'Authorization': 'APPCODE {}'.format(appcode)}) result = response.json() response_message = result.get('showapi_res_body.remark') if response.status_code in [400, 401, 403]: return CommonLog.add_quick_log( CommonLog.MESSAGE_VOICE_API_FORBID).flush() if response.status_code == 200 and result.get('showapi_res_body.flag'): CommonLog.add_quick_log( CommonLog.MESSAGE_VOICE_API_SEND_SUCCESS.format( response_message)).flush() return True else: return CommonLog.add_quick_log( CommonLog.MESSAGE_VOICE_API_SEND_FAIL.format( response_message)).flush()
def test_send_notifications(cls): if Config().NOTIFICATION_BY_VOICE_CODE: # 语音通知 CommonLog.add_quick_log( CommonLog.MESSAGE_TEST_SEND_VOICE_CODE).flush() Notification.voice_code( Config().NOTIFICATION_VOICE_CODE_PHONE, '张三', OrderLog. MESSAGE_ORDER_SUCCESS_NOTIFICATION_OF_VOICE_CODE_CONTENT. format('北京', '深圳')) if Config().EMAIL_ENABLED: # 语音通知 CommonLog.add_quick_log(CommonLog.MESSAGE_TEST_SEND_EMAIL).flush() Notification.send_email(Config().EMAIL_RECEIVER, '测试发送邮件', 'By py12306') if Config().DINGTALK_ENABLED: # 钉钉通知 CommonLog.add_quick_log( CommonLog.MESSAGE_TEST_SEND_DINGTALK).flush() Notification.dingtalk_webhook('测试发送信息') if Config().TELEGRAM_ENABLED: # Telegram通知 CommonLog.add_quick_log( CommonLog.MESSAGE_TEST_SEND_TELEGRAM).flush() Notification.send_to_telegram('测试发送信息')
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)) 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 __init__(self, session): self.data_path = Config().RUNTIME_DIR self.session = session
def update_query_interval(self, auto=False): self.interval = init_interval_by_number(Config().QUERY_INTERVAL) if auto: jobs_do(self.jobs, 'update_interval')
def run_session(self): debug = False if is_main_thread(): debug = Config().IS_DEBUG self.session.run(debug=debug, port=Config().WEB_PORT, host='0.0.0.0')
def start(self): if not Config().WEB_ENABLE or Config().is_slave(): return # if Config().IS_DEBUG: # self.run_session() # else: create_thread_and_run(self, 'run_session', wait=False)