Skip to content

Commit 6e6da93

Browse files
committed
优化代码,增加测试模式
1 parent 27a5a13 commit 6e6da93

17 files changed

+314
-58
lines changed

main.py

+28-7
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,41 @@
11
# encoding=utf8
2-
import os
3-
from threading import Thread
2+
import sys
3+
from time import sleep
44

55
from py12306.helpers.func import *
6+
from py12306.helpers.app import *
7+
from py12306.log.common_log import CommonLog
68
from py12306.query.query import Query
79
from py12306.user.user import User
810

911

1012
def main():
11-
# Thread(target=Query.run).start() # 余票查询
12-
# create_thread_and_run(User, 'run', wait=False)
13+
if '--test' in sys.argv or '-t' in sys.argv: test()
14+
CommonLog.print_welcome().print_configs()
15+
16+
App.run_check()
1317
User.run()
1418
Query.run()
15-
# Query.run()
16-
while True:
17-
sleep(1)
19+
if not Const.IS_TEST:
20+
while True:
21+
sleep(1)
22+
23+
CommonLog.test_complete()
24+
25+
26+
def test():
27+
"""
28+
功能检查
29+
包含:
30+
账号密码验证 (打码)
31+
座位验证
32+
乘客验证
33+
语音验证码验证
34+
:return:
35+
"""
36+
Const.IS_TEST = True
37+
if '--test-notification' in sys.argv or '-n' in sys.argv:
38+
Const.IS_TEST_NOTIFICATION = True
1839
pass
1940

2041

py12306/config.py

+7-6
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,13 @@
5252
QUERY_DATA_DIR = RUNTIME_DIR + 'query/'
5353
USER_DATA_DIR = RUNTIME_DIR + 'user/'
5454

55-
STATION_FILE = 'data/stations.txt'
56-
CONFIG_FILE = 'env.py'
55+
STATION_FILE = PROJECT_DIR + 'data/stations.txt'
56+
CONFIG_FILE = PROJECT_DIR + 'env.py'
57+
58+
# 语音验证码
59+
NOTIFICATION_BY_VOICE_CODE = 0
60+
NOTIFICATION_VOICE_CODE_PHONE = ''
61+
NOTIFICATION_API_APP_CODE = ''
5762

5863
if path.exists(CONFIG_FILE):
5964
exec(open(CONFIG_FILE, encoding='utf8').read())
@@ -71,7 +76,3 @@ class UserType:
7176
'学生': STUDENT,
7277
'残疾军人、伤残人民警察': SOLDIER,
7378
}
74-
75-
76-
def get(key, default=None):
77-
return eval(key)

py12306/exceptions/MemberInvalidException.py

-4
This file was deleted.

py12306/helpers/api.py

+2
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@
5050
API_CONFIRM_SINGLE_FOR_QUEUE = BASE_URL_OF_12306 + '/otn/confirmPassenger/confirmSingleForQueue'
5151
API_QUERY_ORDER_WAIT_TIME = BASE_URL_OF_12306 + '/otn/confirmPassenger/queryOrderWaitTime?{}' # 排队查询
5252

53+
API_NOTIFICATION_BY_VOICE_CODE = 'http://ali-voice.showapi.com/sendVoice?'
54+
5355
urls = {
5456
"auth": { # 登录接口
5557
"req_url": "/passport/web/auth/uamtk",

py12306/helpers/app.py

+45
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
from py12306.helpers.func import *
2+
from py12306.config import *
3+
from py12306.helpers.notification import Notification
24
from py12306.log.common_log import CommonLog
5+
from py12306.log.order_log import OrderLog
36

47

58
def app_available_check():
9+
# return True # Debug
610
now = time_now()
711
if now.hour >= 23 or now.hour < 6:
812
CommonLog.add_quick_log(CommonLog.MESSAGE_12306_IS_CLOSED.format(time_now())).flush()
@@ -11,3 +15,44 @@ def app_available_check():
1115
open_time += datetime.timedelta(1)
1216
sleep((open_time - now).seconds)
1317
return True
18+
19+
20+
class App:
21+
"""
22+
程序主类
23+
TODO 需要完善
24+
"""
25+
26+
@classmethod
27+
def check_auto_code(cls):
28+
if not config.AUTO_CODE_ACCOUNT.get('user') or not config.AUTO_CODE_ACCOUNT.get('pwd'):
29+
return False
30+
return True
31+
32+
@classmethod
33+
def check_user_account_is_empty(cls):
34+
if config.USER_ACCOUNTS:
35+
for account in config.USER_ACCOUNTS:
36+
if account:
37+
return True
38+
return False
39+
40+
@classmethod
41+
def test_send_notifications(cls):
42+
if config.NOTIFICATION_BY_VOICE_CODE: # 语音通知
43+
CommonLog.add_quick_log(CommonLog.MESSAGE_TEST_SEND_VOICE_CODE).flush()
44+
Notification.voice_code(config.NOTIFICATION_VOICE_CODE_PHONE, '张三',
45+
OrderLog.MESSAGE_ORDER_SUCCESS_NOTIFICATION_OF_VOICE_CODE_CONTENT.format('北京',
46+
'深圳'))
47+
48+
@classmethod
49+
def run_check(cls):
50+
"""
51+
待优化
52+
:return:
53+
"""
54+
if not cls.check_auto_code():
55+
CommonLog.add_quick_log(CommonLog.MESSAGE_CHECK_AUTO_CODE_FAIL).flush(exit=True)
56+
if not cls.check_user_account_is_empty():
57+
CommonLog.add_quick_log(CommonLog.MESSAGE_CHECK_EMPTY_USER_ACCOUNT).flush(exit=True)
58+
if Const.IS_TEST_NOTIFICATION: cls.test_send_notifications()

py12306/helpers/func.py

+26-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import datetime
22
import random
33
import threading
4+
import functools
5+
46
from time import sleep
57

68
from py12306 import config
7-
import functools
9+
810

911

1012
def singleton(cls):
@@ -65,6 +67,15 @@ def stay_second(second, call_back=None):
6567
return call_back()
6668

6769

70+
def sleep_forever():
71+
"""
72+
当不是主线程时,假象停止
73+
:return:
74+
"""
75+
if not is_main_thread():
76+
while True: sleep(10000000)
77+
78+
6879
def is_main_thread():
6980
return threading.current_thread() == threading.main_thread()
7081

@@ -99,4 +110,17 @@ def array_dict_find_by_key_value(data, key, value, default=None):
99110
result = [v for k, v in enumerate(data) if key in v and v[key] == value]
100111
return result.pop() if len(result) else default
101112

102-
# def test:
113+
114+
def get_true_false_text(value, true='', false=''):
115+
if value: return true
116+
return false
117+
118+
119+
def sleep_forever_when_in_test():
120+
if Const.IS_TEST: sleep_forever()
121+
122+
123+
@singleton
124+
class Const:
125+
IS_TEST = False
126+
IS_TEST_NOTIFICATION = False

py12306/helpers/notification.py

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import urllib
2+
3+
from py12306 import config
4+
from py12306.helpers.api import *
5+
from py12306.helpers.request import Request
6+
from py12306.log.common_log import CommonLog
7+
8+
9+
class Notification():
10+
"""
11+
通知类
12+
"""
13+
session = None
14+
15+
def __init__(self):
16+
self.session = Request()
17+
18+
@classmethod
19+
def voice_code(cls, phone, name='', content=''):
20+
self = cls()
21+
self.send_voice_code_of_yiyuan(phone, name=name, content=content)
22+
23+
def send_voice_code_of_yiyuan(self, phone, name='', content=''):
24+
"""
25+
发送语音验证码
26+
购买地址 https://market.aliyun.com/products/57126001/cmapi019902.html?spm=5176.2020520132.101.5.37857218O6iJ3n
27+
:return:
28+
"""
29+
appcode = config.NOTIFICATION_API_APP_CODE
30+
if not appcode:
31+
CommonLog.add_quick_log(CommonLog.MESSAGE_EMPTY_APP_CODE).flush()
32+
return False
33+
body = {
34+
'userName': name,
35+
'mailNo': content
36+
}
37+
params = {
38+
'content': body,
39+
'mobile': phone,
40+
'sex': 2,
41+
'tNum': 'T170701001056'
42+
}
43+
response = self.session.request(url=API_NOTIFICATION_BY_VOICE_CODE + urllib.parse.urlencode(params),
44+
method='GET', headers={
45+
'Authorization': 'APPCODE {}'.format(appcode)
46+
})
47+
response_message = '-'
48+
result = {}
49+
try:
50+
result = response.json()
51+
response_message = result['showapi_res_body']['remark']
52+
except:
53+
pass
54+
if response.status_code == 401 or response.status_code == 403:
55+
return CommonLog.add_quick_log(CommonLog.MESSAGE_VOICE_API_FORBID).flush()
56+
if response.status_code == 200 and 'showapi_res_body' in result and result['showapi_res_body'].get('flag'):
57+
CommonLog.add_quick_log(CommonLog.MESSAGE_VOICE_API_SEND_SUCCESS.format(response_message)).flush()
58+
return True
59+
else:
60+
return CommonLog.add_quick_log(CommonLog.MESSAGE_VOICE_API_SEND_FAIL.format(response_message)).flush()
61+
62+
63+
if __name__ == '__main__':
64+
Notification.voice_code('13800138000', '张三', '你的车票 广州 到 深圳 购买成功,请登录 12306 进行支付')

py12306/helpers/station.py

-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ class Station:
77
stations = []
88

99
def __init__(self):
10-
print('Station 初始化')
1110
if path.exists(config.STATION_FILE):
1211
result = open(config.STATION_FILE, encoding='utf-8').read()
1312
result = result.lstrip('@').split('@')

py12306/log/base.py

+2-5
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,13 @@
33

44
from py12306.helpers.func import *
55

6-
76
class BaseLog:
87
logs = []
98
thread_logs = {}
109
quick_log = []
1110

1211
@classmethod
13-
def add_log(cls, content):
12+
def add_log(cls, content=''):
1413
self = cls()
1514
# print('添加 Log 主进程{} 进程ID{}'.format(is_main_thread(), current_thread_id()))
1615
if is_main_thread():
@@ -31,7 +30,6 @@ def flush(cls, sep='\n', end='\n', file=None, exit=False):
3130
logs = self.logs
3231
else:
3332
logs = self.thread_logs.get(current_thread_id())
34-
# for i in logs:
3533
print(*logs, sep=sep, end=end, file=file)
3634
if self.quick_log:
3735
self.quick_log = []
@@ -40,12 +38,11 @@ def flush(cls, sep='\n', end='\n', file=None, exit=False):
4038
self.logs = []
4139
else:
4240
if logs: del self.thread_logs[current_thread_id()]
43-
# print(self.logs)
4441
if exit:
4542
sys.exit()
4643

4744
@classmethod
48-
def add_quick_log(cls, content):
45+
def add_quick_log(cls, content = ''):
4946
self = cls()
5047
self.quick_log.append(content)
5148
return self

py12306/log/common_log.py

+50-1
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,67 @@
11
from py12306.log.base import BaseLog
2+
from py12306.config import *
23
from py12306.helpers.func import *
34

45

56
@singleton
67
class CommonLog(BaseLog):
8+
# 这里如果不声明,会出现重复打印,目前不知道什么原因
9+
logs = []
10+
thread_logs = {}
11+
quick_log = []
12+
713
MESSAGE_12306_IS_CLOSED = '当前时间: {} | 12306 休息时间,程序将在明天早上 6 点自动运行'
814
MESSAGE_RETRY_AUTH_CODE = '{} 秒后重新获取验证码'
915

16+
MESSAGE_EMPTY_APP_CODE = '无法发送语音消息,未填写验证码接口 appcode'
17+
MESSAGE_VOICE_API_FORBID = '语音消息发送失败,请检查 appcode 是否填写正确或 套餐余额是否充足'
18+
MESSAGE_VOICE_API_SEND_FAIL = '语音消息发送失败,错误原因 {}'
19+
MESSAGE_VOICE_API_SEND_SUCCESS = '语音消息发送成功! 接口返回信息 {} '
20+
21+
MESSAGE_CHECK_AUTO_CODE_FAIL = '请配置打码账号的账号密码'
22+
MESSAGE_CHECK_EMPTY_USER_ACCOUNT = '请配置 12306 账号密码'
23+
24+
MESSAGE_TEST_SEND_VOICE_CODE = '正在测试发送语音验证码...'
25+
1026
def __init__(self):
1127
super().__init__()
1228
self.init_data()
1329

1430
def init_data(self):
15-
print('Common Log 初始化')
31+
pass
32+
33+
@classmethod
34+
def print_welcome(cls):
35+
self = cls()
36+
self.add_quick_log('######## py12306 购票助手,本程序为开源工具,请勿用于商业用途 ########')
37+
if Const.IS_TEST:
38+
self.add_quick_log()
39+
self.add_quick_log('当前为测试模式,程序运行完成后自动结束')
40+
self.add_quick_log()
41+
self.flush()
42+
return self
43+
44+
@classmethod
45+
def print_configs(cls):
46+
# 打印配置
47+
self = cls()
48+
enable = '已开启'
49+
disable = '未开启'
50+
self.add_quick_log('**** 当前配置 ****')
51+
self.add_quick_log('多线程查询: {}'.format(get_true_false_text(config.QUERY_JOB_THREAD_ENABLED, enable, disable)))
52+
self.add_quick_log('语音验证码: {}'.format(get_true_false_text(config.QUERY_JOB_THREAD_ENABLED, enable, disable)))
53+
self.add_quick_log('查询间隔: {} 秒'.format(config.QUERY_INTERVAL))
54+
self.add_quick_log('用户心跳检测间隔: {} 秒'.format(config.USER_HEARTBEAT_INTERVAL))
55+
self.add_quick_log()
56+
self.flush()
57+
return self
58+
59+
@classmethod
60+
def test_complete(cls):
61+
self = cls()
62+
self.add_quick_log('# 测试完成,请检查输出是否正确 #')
63+
self.flush()
64+
return self
1665

1766
@classmethod
1867
def print_auto_code_fail(cls, reason):

0 commit comments

Comments
 (0)