完成自动下单
This commit is contained in:
@@ -24,16 +24,27 @@ AUTO_CODE_ACCOUNT = {
|
||||
}
|
||||
|
||||
SEAT_TYPES = {
|
||||
'特等座': 25,
|
||||
'商务座': 32,
|
||||
'一等座': 31,
|
||||
'二等座': 30,
|
||||
'特等座': 25,
|
||||
'软卧': 23,
|
||||
'硬卧': 28,
|
||||
'硬座': 29,
|
||||
'无座': 26,
|
||||
}
|
||||
|
||||
ORDER_SEAT_TYPES = {
|
||||
'特等座': 'P',
|
||||
'商务座': 9,
|
||||
'一等座': 'M',
|
||||
'二等座': 'O',
|
||||
'软卧': 4,
|
||||
'硬卧': 3,
|
||||
'硬座': 1,
|
||||
'无座': 1,
|
||||
}
|
||||
|
||||
PROJECT_DIR = path.dirname(path.dirname(path.abspath(__file__))) + '/'
|
||||
|
||||
# Query
|
||||
|
||||
@@ -43,6 +43,12 @@ API_USER_INFO = {
|
||||
'url': BASE_URL_OF_12306 + '/otn/modifyUser/initQueryUserInfoApi'
|
||||
}
|
||||
API_USER_PASSENGERS = BASE_URL_OF_12306 + '/otn/confirmPassenger/getPassengerDTOs'
|
||||
API_SUBMIT_ORDER_REQUEST = BASE_URL_OF_12306 + '/otn/leftTicket/submitOrderRequest'
|
||||
API_CHECK_ORDER_INFO = BASE_URL_OF_12306 + '/otn/confirmPassenger/checkOrderInfo'
|
||||
API_INITDC_URL = BASE_URL_OF_12306 + '/otn/confirmPassenger/initDc' # 生成订单时需要先请求这个页面
|
||||
API_GET_QUEUE_COUNT = BASE_URL_OF_12306 + '/otn/confirmPassenger/getQueueCount'
|
||||
API_CONFIRM_SINGLE_FOR_QUEUE = BASE_URL_OF_12306 + '/otn/confirmPassenger/confirmSingleForQueue'
|
||||
API_QUERY_ORDER_WAIT_TIME = BASE_URL_OF_12306 + '/otn/confirmPassenger/queryOrderWaitTime?{}' # 排队查询
|
||||
|
||||
urls = {
|
||||
"auth": { # 登录接口
|
||||
|
||||
@@ -59,8 +59,10 @@ def get_interval_num(interval, decimal=2):
|
||||
return round(random.uniform(interval.get('min'), interval.get('max')), decimal)
|
||||
|
||||
|
||||
def stay_second(second):
|
||||
def stay_second(second, call_back=None):
|
||||
sleep(second)
|
||||
if call_back:
|
||||
return call_back()
|
||||
|
||||
|
||||
def is_main_thread():
|
||||
|
||||
42
py12306/log/order_log.py
Normal file
42
py12306/log/order_log.py
Normal file
@@ -0,0 +1,42 @@
|
||||
from py12306.log.base import BaseLog
|
||||
from py12306.helpers.func import *
|
||||
|
||||
|
||||
@singleton
|
||||
class OrderLog(BaseLog):
|
||||
MESSAGE_REQUEST_INIT_DC_PAGE_FAIL = '请求初始化订单页面失败'
|
||||
|
||||
MESSAGE_SUBMIT_ORDER_REQUEST_FAIL = '提交订单失败,错误原因 {}'
|
||||
MESSAGE_SUBMIT_ORDER_REQUEST_SUCCESS = '提交订单成功'
|
||||
MESSAGE_CHECK_ORDER_INFO_FAIL = '检查订单失败,错误原因 {}'
|
||||
MESSAGE_CHECK_ORDER_INFO_SUCCESS = '检查订单成功'
|
||||
|
||||
MESSAGE_GET_QUEUE_COUNT_SUCCESS = '排队成功,你当前排在第 {} 位, 余票还剩余 {} 张'
|
||||
MESSAGE_GET_QUEUE_COUNT_FAIL = '排队失败,错误原因 {}'
|
||||
|
||||
MESSAGE_CONFIRM_SINGLE_FOR_QUEUE_SUCCESS = '# 提交订单成功!#'
|
||||
MESSAGE_CONFIRM_SINGLE_FOR_QUEUE_ERROR = '提交订单出错,错误原因 {}'
|
||||
MESSAGE_CONFIRM_SINGLE_FOR_QUEUE_FAIL = '提交订单失败,错误原因 {}'
|
||||
|
||||
MESSAGE_QUERY_ORDER_WAIT_TIME_WAITING = '排队等待中,预计还需要 {} 秒'
|
||||
MESSAGE_QUERY_ORDER_WAIT_TIME_FAIL = '排队失败,错误原因 {}'
|
||||
MESSAGE_QUERY_ORDER_WAIT_TIME_INFO = '第 {} 次排队,请耐心等待'
|
||||
|
||||
MESSAGE_ORDER_SUCCESS_NOTIFICATION_TITLE = '车票购买成功!'
|
||||
MESSAGE_ORDER_SUCCESS_NOTIFICATION_CONTENT = '请及时器登录12306,打开 \'未完成订单\',在30分钟内完成支付!'
|
||||
|
||||
|
||||
@classmethod
|
||||
def print_passenger_did_deleted(cls, passengers):
|
||||
self = cls()
|
||||
result = [passenger.get('name') + '(' + passenger.get('type_text') + ')' for passenger in passengers]
|
||||
self.add_quick_log('# 删减后的乘客列表 {} #'.format(', '.join(result)))
|
||||
self.flush()
|
||||
return self
|
||||
|
||||
@classmethod
|
||||
def print_ticket_did_ordered(cls, order_id):
|
||||
self = cls()
|
||||
self.add_quick_log('# 车票购买成功,订单号{} #'.format(order_id))
|
||||
self.flush()
|
||||
return self
|
||||
@@ -63,7 +63,7 @@ class QueryLog(BaseLog):
|
||||
self.add_quick_log(
|
||||
'余票数小于乘车人数,当前余票数: {rest_num}, 实际人数 {actual_num}, 删减人车人数到: {take_num}'.format(rest_num=rest_num,
|
||||
actual_num=job.member_num,
|
||||
take=job.member_num_take))
|
||||
take_num=job.member_num_take))
|
||||
self.flush()
|
||||
return self
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@ class UserLog(BaseLog):
|
||||
MESSAGE_GET_USER_PASSENGERS_FAIL = '获取用户乘客列表失败,错误原因: {} {} 秒后重试'
|
||||
MESSAGE_USER_PASSENGERS_IS_INVALID = '乘客信息校验失败,在账号 {} 中未找到该乘客: {}'
|
||||
|
||||
MESSAGE_WAIT_USER_INIT_COMPLETE = '未找到可用账号或用户正在初始化,{} 秒重试'
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.init_data()
|
||||
|
||||
@@ -1,34 +1,329 @@
|
||||
import urllib
|
||||
import random
|
||||
|
||||
from py12306.config import UserType
|
||||
from py12306.helpers.api import *
|
||||
from py12306.helpers.app import *
|
||||
from py12306.helpers.func import *
|
||||
from py12306.log.order_log import OrderLog
|
||||
from py12306.log.user_log import UserLog
|
||||
from py12306.user.job import UserJob
|
||||
|
||||
|
||||
# from py12306.query.job import Job
|
||||
# from py12306.user.job import UserJob
|
||||
|
||||
|
||||
class Order:
|
||||
"""
|
||||
处理下单
|
||||
"""
|
||||
heartbeat = 60 * 2
|
||||
users = []
|
||||
session = None
|
||||
query_ins = None
|
||||
user_ins = None
|
||||
|
||||
def __init__(self):
|
||||
passenger_ticket_str = ''
|
||||
old_passenger_str = ''
|
||||
|
||||
is_need_auth_code = False
|
||||
|
||||
max_queue_wait = 120
|
||||
current_queue_wait = 0
|
||||
retry_time = 3
|
||||
wait_queue_interval = 3
|
||||
|
||||
def __init__(self, query, user):
|
||||
self.session = user.session
|
||||
# assert isinstance(query, Job) # 循环引用
|
||||
# assert isinstance(user, UserJob)
|
||||
self.query_ins = query
|
||||
self.user_ins = user
|
||||
|
||||
self.make_passenger_ticket_str()
|
||||
|
||||
def order(self):
|
||||
"""
|
||||
开始下单
|
||||
下单模式 暂时不清楚,使用正常步骤下单
|
||||
:return:
|
||||
"""
|
||||
self.normal_order()
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def run(cls):
|
||||
self = cls()
|
||||
app_available_check()
|
||||
self.start()
|
||||
pass
|
||||
def normal_order(self):
|
||||
if not self.submit_order_request(): return
|
||||
if not self.user_ins.request_init_dc_page(): return
|
||||
if not self.check_order_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: # 发送通知
|
||||
OrderLog.print_ticket_did_ordered(order_id)
|
||||
OrderLog.notification(OrderLog.MESSAGE_ORDER_SUCCESS_NOTIFICATION_TITLE,
|
||||
OrderLog.MESSAGE_ORDER_SUCCESS_NOTIFICATION_CONTENT)
|
||||
|
||||
def start(self):
|
||||
self.init_users()
|
||||
UserLog.print_init_users(users=self.users)
|
||||
# 多线程维护用户
|
||||
create_thread_and_run(jobs=self.users, callback_name='run', wait=False)
|
||||
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:
|
||||
OrderLog.add_quick_log(
|
||||
OrderLog.MESSAGE_SUBMIT_ORDER_REQUEST_FAIL.format(result.get('messages', '-'))).flush()
|
||||
return False
|
||||
|
||||
def init_users(self):
|
||||
accounts = config.USER_ACCOUNTS
|
||||
for account in accounts:
|
||||
user = UserJob(info=account, user=self)
|
||||
self.users.append(user)
|
||||
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 '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 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['data'].get('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['data'].get('errMsg', '-'))).flush()
|
||||
else:
|
||||
OrderLog.add_quick_log(OrderLog.MESSAGE_CONFIRM_SINGLE_FOR_QUEUE_FAIL.format(
|
||||
result.get('messages', '-'))).flush()
|
||||
return False
|
||||
|
||||
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
|
||||
while self.current_queue_wait:
|
||||
self.current_queue_wait -= 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') and result_data.get('waitTime') >= 0:
|
||||
OrderLog.add_quick_log(
|
||||
OrderLog.MESSAGE_QUERY_ORDER_WAIT_TIME_WAITING.format(result_data.get('waitTime'))).flush()
|
||||
elif result_data.get('msg'): # 失败
|
||||
OrderLog.add_quick_log(
|
||||
OrderLog.MESSAGE_QUERY_ORDER_WAIT_TIME_FAIL.format(result_data.get('msg', '-'))).flush()
|
||||
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.current_queue_wait)).flush()
|
||||
stay_second(self.wait_queue_interval)
|
||||
|
||||
return False
|
||||
|
||||
def make_passenger_ticket_str(self):
|
||||
"""
|
||||
生成提交车次的内容
|
||||
格式:
|
||||
1(seatType),0,1(车票类型:ticket_type_codes),张三(passenger_name),1(证件类型:passenger_id_type_code),xxxxxx(passenger_id_no),xxxx(mobile_no),N
|
||||
passengerTicketStr:
|
||||
张三(passenger_name),1(证件类型:passenger_id_type_code),xxxxxx(passenger_id_no),1_
|
||||
oldPassengerStr
|
||||
:return:
|
||||
"""
|
||||
passenger_tickets = []
|
||||
old_passengers = []
|
||||
available_passengers = self.query_ins.passengers
|
||||
if len(available_passengers) > self.query_ins.member_num_take: # 删除人数
|
||||
available_passengers = available_passengers[0:self.query_ins.member_num_take]
|
||||
OrderLog.print_passenger_did_deleted(available_passengers)
|
||||
|
||||
for passenger in available_passengers:
|
||||
tmp_str = '{seat_type},0,{passenger_type},{passenger_name},{passenger_id_card_type},{passenger_id_card},{passenger_mobile},N_'.format(
|
||||
seat_type=self.query_ins.current_order_seat, passenger_type=passenger['type'],
|
||||
passenger_name=passenger['name'],
|
||||
passenger_id_card_type=passenger['id_card_type'], passenger_id_card=passenger['id_card'],
|
||||
passenger_mobile=passenger['mobile']
|
||||
)
|
||||
passenger_tickets.append(tmp_str)
|
||||
|
||||
if int(passenger['type']) != UserType.CHILD:
|
||||
tmp_old_str = '{passenger_name},{passenger_id_card_type},{passenger_id_card},{passenger_type}_'.format(
|
||||
passenger_name=passenger['name'],
|
||||
passenger_id_card_type=passenger['id_card_type'], passenger_id_card=passenger['id_card'],
|
||||
passenger_type=passenger['type'],
|
||||
)
|
||||
old_passengers.append(tmp_old_str)
|
||||
|
||||
self.passenger_ticket_str = ''.join(passenger_tickets).rstrip('_')
|
||||
self.old_passenger_str = ''.join(old_passengers).rstrip('_') + '__ _ _' # 不加后面请求会出错
|
||||
|
||||
@@ -3,6 +3,7 @@ from py12306.helpers.station import Station
|
||||
from py12306.log.query_log import QueryLog
|
||||
from py12306.helpers.func import *
|
||||
from py12306.log.user_log import UserLog
|
||||
from py12306.order.order import Order
|
||||
from py12306.user.user import User
|
||||
|
||||
|
||||
@@ -12,6 +13,7 @@ class Job:
|
||||
"""
|
||||
|
||||
left_dates = []
|
||||
left_date = None
|
||||
left_station = ''
|
||||
arrive_station = ''
|
||||
left_station_code = ''
|
||||
@@ -19,6 +21,8 @@ class Job:
|
||||
|
||||
account_key = 0
|
||||
allow_seats = []
|
||||
current_seat = None
|
||||
current_order_seat = None
|
||||
allow_train_numbers = []
|
||||
members = []
|
||||
member_num = 0
|
||||
@@ -32,10 +36,12 @@ class Job:
|
||||
ticket_info = {}
|
||||
INDEX_TICKET_NUM = 11
|
||||
INDEX_TRAIN_NUMBER = 3
|
||||
INDEX_TRAIN_NO = 2
|
||||
INDEX_LEFT_DATE = 13
|
||||
INDEX_LEFT_STATION = 6 # 4 5 始发 终点
|
||||
INDEX_ARRIVE_STATION = 7
|
||||
INDEX_ORDER_TEXT = 1 # 下单文字
|
||||
INDEX_SECRET_STR = 0
|
||||
|
||||
def __init__(self, info, query):
|
||||
self.left_dates = info.get('left_dates')
|
||||
@@ -67,22 +73,20 @@ class Job:
|
||||
:param job:
|
||||
:return:
|
||||
"""
|
||||
if not self.passengers:
|
||||
User.check_members(self.members, self.account_key, call_back=self.set_passengers)
|
||||
|
||||
QueryLog.print_job_start()
|
||||
for date in self.left_dates:
|
||||
self.left_date = date
|
||||
response = self.query_by_date(date)
|
||||
self.handle_response(response)
|
||||
self.safe_stay()
|
||||
if is_main_thread():
|
||||
QueryLog.flush(sep='\t\t')
|
||||
QueryLog.flush(sep='\t')
|
||||
else:
|
||||
QueryLog.add_log('\n')
|
||||
if is_main_thread():
|
||||
QueryLog.add_quick_log('').flush()
|
||||
else:
|
||||
QueryLog.flush(sep='\t\t')
|
||||
QueryLog.flush(sep='\t')
|
||||
|
||||
def query_by_date(self, date):
|
||||
"""
|
||||
@@ -116,12 +120,14 @@ class Job:
|
||||
self.get_info_of_ticket_num()))
|
||||
if not self.is_has_ticket(ticket_info):
|
||||
continue
|
||||
allow_seats = self.allow_seats if self.allow_seats else list(config.SEAT_TYPES.values()) # 未设置 则所有可用
|
||||
allow_seats = self.allow_seats if self.allow_seats else list(
|
||||
config.SEAT_TYPES.values()) # 未设置 则所有可用 TODO 合法检测
|
||||
self.handle_seats(allow_seats, ticket_info)
|
||||
|
||||
def handle_seats(self, allow_seats, ticket_info):
|
||||
for seat in allow_seats: # 检查座位是否有票
|
||||
ticket_of_seat = ticket_info[get_seat_number_by_name(seat)]
|
||||
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(),
|
||||
@@ -139,7 +145,9 @@ class Job:
|
||||
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)
|
||||
print('检查完成 开始提交订单')
|
||||
self.check_passengers()
|
||||
order = Order(user=self.get_user(), query=self)
|
||||
order.order()
|
||||
|
||||
def get_results(self, response):
|
||||
"""
|
||||
@@ -179,6 +187,22 @@ class Job:
|
||||
UserLog.print_user_passenger_init_success(passengers)
|
||||
self.passengers = passengers
|
||||
|
||||
def set_seat(self, seat):
|
||||
self.current_seat = get_seat_number_by_name(seat)
|
||||
self.current_order_seat = config.ORDER_SEAT_TYPES[seat]
|
||||
|
||||
def get_user(self):
|
||||
user = User.get_user(self.account_key)
|
||||
if not user.check_is_ready():
|
||||
# TODO user is not ready
|
||||
pass
|
||||
return user
|
||||
|
||||
def check_passengers(self):
|
||||
if not self.passengers:
|
||||
User.check_members(self.members, self.account_key, call_back=self.set_passengers)
|
||||
return True
|
||||
|
||||
# 提供一些便利方法
|
||||
def get_info_of_left_date(self):
|
||||
return self.ticket_info[self.INDEX_LEFT_DATE]
|
||||
@@ -189,6 +213,9 @@ class Job:
|
||||
def get_info_of_train_number(self):
|
||||
return self.ticket_info[self.INDEX_TRAIN_NUMBER]
|
||||
|
||||
def get_info_of_train_no(self):
|
||||
return self.ticket_info[self.INDEX_TRAIN_NO]
|
||||
|
||||
def get_info_of_left_station(self):
|
||||
return Station.get_station_name_by_key(self.ticket_info[self.INDEX_LEFT_STATION])
|
||||
|
||||
@@ -197,3 +224,6 @@ class Job:
|
||||
|
||||
def get_info_of_order_text(self):
|
||||
return self.ticket_info[self.INDEX_ORDER_TEXT]
|
||||
|
||||
def get_info_of_secret_str(self):
|
||||
return self.ticket_info[self.INDEX_SECRET_STR]
|
||||
|
||||
@@ -31,7 +31,7 @@ class Query:
|
||||
pass
|
||||
|
||||
def start(self):
|
||||
return # DEBUG
|
||||
# return # DEBUG
|
||||
self.init_jobs()
|
||||
QueryLog.print_init_jobs(jobs=self.jobs)
|
||||
while True:
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import json
|
||||
import pickle
|
||||
import re
|
||||
from os import path
|
||||
|
||||
from py12306.config import *
|
||||
@@ -7,6 +9,7 @@ from py12306.helpers.app import *
|
||||
from py12306.helpers.auth_code import AuthCode
|
||||
from py12306.helpers.func import *
|
||||
from py12306.helpers.request import Request
|
||||
from py12306.log.order_log import OrderLog
|
||||
from py12306.log.user_log import UserLog
|
||||
|
||||
|
||||
@@ -23,6 +26,11 @@ class UserJob:
|
||||
passengers = []
|
||||
retry_time = 5
|
||||
|
||||
# Init page
|
||||
global_repeat_submit_token = None
|
||||
ticket_info_for_passenger_form = None
|
||||
order_request_dto = None
|
||||
|
||||
def __init__(self, info, user):
|
||||
self.session = Request()
|
||||
self.heartbeat = user.heartbeat
|
||||
@@ -229,9 +237,35 @@ class UserJob:
|
||||
new_member = {
|
||||
'name': passenger.get('passenger_name'),
|
||||
'id_card': passenger.get('passenger_id_no'),
|
||||
'id_card_type': passenger.get('passenger_id_type_code'),
|
||||
'mobile': passenger.get('mobile_no'),
|
||||
'type': passenger.get('passenger_type'),
|
||||
'type_text': dict_find_key_by_value(UserType.dicts, int(passenger.get('passenger_type')))
|
||||
}
|
||||
results.append(new_member)
|
||||
|
||||
return results
|
||||
|
||||
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
|
||||
|
||||
@@ -9,6 +9,8 @@ class User:
|
||||
heartbeat = 60 * 2
|
||||
users = []
|
||||
|
||||
retry_time = 3
|
||||
|
||||
def __init__(self):
|
||||
self.interval = config.USER_HEARTBEAT_INTERVAL
|
||||
|
||||
@@ -32,7 +34,15 @@ class User:
|
||||
self.users.append(user)
|
||||
|
||||
@classmethod
|
||||
def check_members(cls, members, user_key, call_back):
|
||||
def get_user(cls, key):
|
||||
self = cls()
|
||||
for user in self.users:
|
||||
if user.key == key:
|
||||
return user
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def check_members(cls, members, key, call_back):
|
||||
"""
|
||||
检测乘客信息
|
||||
:param passengers:
|
||||
@@ -42,7 +52,10 @@ class User:
|
||||
|
||||
for user in self.users:
|
||||
assert isinstance(user, UserJob)
|
||||
if user.key == user_key and user.check_is_ready():
|
||||
if user.key == key and user.check_is_ready():
|
||||
passengers = user.get_passengers_by_members(members)
|
||||
call_back(passengers)
|
||||
pass
|
||||
return call_back(passengers)
|
||||
|
||||
UserLog.add_quick_log(UserLog.MESSAGE_WAIT_USER_INIT_COMPLETE.format(self.retry_time)).flush()
|
||||
stay_second(self.retry_time)
|
||||
return self.check_members(members, key, call_back)
|
||||
|
||||
Reference in New Issue
Block a user