Compare commits
15 Commits
fix/drag-c
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8e13823d88 | ||
|
|
481a9c0310 | ||
|
|
43caf8fdc8 | ||
|
|
ac4abae019 | ||
|
|
08799d61f0 | ||
|
|
6e5ceca134 | ||
|
|
db34583c7d | ||
|
|
4f3abc9446 | ||
|
|
c98c423c5e | ||
|
|
f7c8ff4daa | ||
|
|
68a508e30a | ||
|
|
49d35aabdc | ||
|
|
9e529c1f9f | ||
|
|
6b6bf41a51 | ||
|
|
badc8b8c31 |
@@ -6,12 +6,14 @@ USER_ACCOUNTS = [
|
|||||||
{
|
{
|
||||||
'key': 0, # 如使用多个账号 key 不能重复
|
'key': 0, # 如使用多个账号 key 不能重复
|
||||||
'user_name': 'your user name',
|
'user_name': 'your user name',
|
||||||
'password': 'your password'
|
'password': '忽略',
|
||||||
|
'type': 'qr' # qr 为扫码登录,填写其他为密码登录
|
||||||
},
|
},
|
||||||
# {
|
# {
|
||||||
# 'key': 'wangwu',
|
# 'key': 'wangwu',
|
||||||
# 'user_name': 'wangwu@qq.com',
|
# 'user_name': 'wangwu@qq.com',
|
||||||
# 'password': 'wangwu'
|
# 'password': 'wangwu',
|
||||||
|
# 'type': ''
|
||||||
# }
|
# }
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -6,12 +6,14 @@ USER_ACCOUNTS = [
|
|||||||
{
|
{
|
||||||
'key': 0, # 如使用多个账号 key 不能重复
|
'key': 0, # 如使用多个账号 key 不能重复
|
||||||
'user_name': 'your user name',
|
'user_name': 'your user name',
|
||||||
'password': 'your password'
|
'password': '忽略',
|
||||||
|
'type': 'qr' # qr 为扫码登录,填写其他为密码登录
|
||||||
},
|
},
|
||||||
# {
|
# {
|
||||||
# 'key': 'wangwu',
|
# 'key': 'wangwu',
|
||||||
# 'user_name': 'wangwu@qq.com',
|
# 'user_name': 'wangwu@qq.com',
|
||||||
# 'password': 'wangwu'
|
# 'password': 'wangwu',
|
||||||
|
# 'type': ''
|
||||||
# }
|
# }
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import os
|
|
||||||
import signal
|
import signal
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
@@ -11,16 +10,19 @@ from py12306.log.order_log import OrderLog
|
|||||||
|
|
||||||
|
|
||||||
def app_available_check():
|
def app_available_check():
|
||||||
# return True # Debug
|
|
||||||
if Config().IS_DEBUG:
|
if Config().IS_DEBUG:
|
||||||
return True
|
return True
|
||||||
now = time_now()
|
now = time_now()
|
||||||
if (now.hour >= 23 and now.minute >= 30) or now.hour < 6:
|
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()
|
CommonLog.add_quick_log(CommonLog.MESSAGE_12306_IS_CLOSED.format(time_now())).flush()
|
||||||
open_time = datetime.datetime(now.year, now.month, now.day, 6)
|
open_time = datetime.datetime(now.year, now.month, now.day, 5)
|
||||||
if open_time < now:
|
if open_time < now:
|
||||||
open_time += datetime.timedelta(1)
|
open_time += datetime.timedelta(1)
|
||||||
sleep((open_time - now).seconds)
|
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
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
# coding=utf-8
|
# coding=utf-8
|
||||||
# 查询余票
|
|
||||||
import time
|
|
||||||
|
|
||||||
HOST_URL_OF_12306 = 'kyfw.12306.cn'
|
HOST_URL_OF_12306 = 'kyfw.12306.cn'
|
||||||
BASE_URL_OF_12306 = 'https://' + HOST_URL_OF_12306
|
BASE_URL_OF_12306 = 'https://' + HOST_URL_OF_12306
|
||||||
@@ -15,6 +13,18 @@ API_BASE_LOGIN = {
|
|||||||
|
|
||||||
API_USER_LOGIN_CHECK = BASE_URL_OF_12306 + '/otn/login/conf'
|
API_USER_LOGIN_CHECK = BASE_URL_OF_12306 + '/otn/login/conf'
|
||||||
|
|
||||||
|
API_AUTH_QRCODE_BASE64_DOWNLOAD = {
|
||||||
|
'url': BASE_URL_OF_12306 + '/passport/web/create-qr64'
|
||||||
|
}
|
||||||
|
|
||||||
|
API_AUTH_QRCODE_CHECK = {
|
||||||
|
'url': BASE_URL_OF_12306 + '/passport/web/checkqr'
|
||||||
|
}
|
||||||
|
|
||||||
|
API_USER_LOGIN = {
|
||||||
|
'url': BASE_URL_OF_12306 + '/otn/login/userLogin'
|
||||||
|
}
|
||||||
|
|
||||||
API_AUTH_CODE_DOWNLOAD = {
|
API_AUTH_CODE_DOWNLOAD = {
|
||||||
'url': BASE_URL_OF_12306 + '/passport/captcha/captcha-image?login_site=E&module=login&rand=sjrand&_={random}'
|
'url': BASE_URL_OF_12306 + '/passport/captcha/captcha-image?login_site=E&module=login&rand=sjrand&_={random}'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,11 @@ class Notification():
|
|||||||
self = cls()
|
self = cls()
|
||||||
self.send_email_by_smtp(to, title, content)
|
self.send_email_by_smtp(to, title, content)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def send_email_with_qrcode(cls, to, title='', qrcode_path=''):
|
||||||
|
self = cls()
|
||||||
|
self.send_email_by_smtp_with_qrcode(to, title, qrcode_path)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def send_to_telegram(cls, content=''):
|
def send_to_telegram(cls, content=''):
|
||||||
self = cls()
|
self = cls()
|
||||||
@@ -134,6 +139,46 @@ class Notification():
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
CommonLog.add_quick_log(CommonLog.MESSAGE_SEND_EMAIL_FAIL.format(e)).flush()
|
CommonLog.add_quick_log(CommonLog.MESSAGE_SEND_EMAIL_FAIL.format(e)).flush()
|
||||||
|
|
||||||
|
def send_email_by_smtp_with_qrcode(self, to, title, qrcode_path):
|
||||||
|
import smtplib
|
||||||
|
from email.mime.text import MIMEText
|
||||||
|
from email.mime.multipart import MIMEMultipart
|
||||||
|
from email.mime.image import MIMEImage
|
||||||
|
to = to if isinstance(to, list) else [to]
|
||||||
|
message = MIMEMultipart()
|
||||||
|
message['Subject'] = title
|
||||||
|
message['From'] = Config().EMAIL_SENDER
|
||||||
|
message['To'] = ", ".join(to)
|
||||||
|
htmlFile = """
|
||||||
|
<html>
|
||||||
|
<head></head>
|
||||||
|
<body>
|
||||||
|
<p>
|
||||||
|
这是你的二维码
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<br /><img src="cid:0", width=200, height=200 ></p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""
|
||||||
|
htmlApart = MIMEText(htmlFile, 'html')
|
||||||
|
imageFile = qrcode_path
|
||||||
|
imageApart = MIMEImage(open(imageFile, 'rb').read(), imageFile.split('.')[-1])
|
||||||
|
imageApart.add_header('Content-ID', '<0>')
|
||||||
|
message.attach(imageApart)
|
||||||
|
message.attach(htmlApart)
|
||||||
|
try:
|
||||||
|
server = smtplib.SMTP(Config().EMAIL_SERVER_HOST)
|
||||||
|
server.ehlo()
|
||||||
|
server.starttls()
|
||||||
|
server.login(Config().EMAIL_SERVER_USER, Config().EMAIL_SERVER_PASSWORD)
|
||||||
|
server.send_message(message)
|
||||||
|
server.quit()
|
||||||
|
CommonLog.add_quick_log(CommonLog.MESSAGE_SEND_EMAIL_WITH_QRCODE_SUCCESS).flush()
|
||||||
|
self.push_bark(CommonLog.MESSAGE_SEND_EMAIL_WITH_QRCODE_SUCCESS)
|
||||||
|
except Exception as e:
|
||||||
|
CommonLog.add_quick_log(CommonLog.MESSAGE_SEND_EMAIL_FAIL.format(e)).flush()
|
||||||
|
|
||||||
def send_dingtalk_by_webbook(self, content):
|
def send_dingtalk_by_webbook(self, content):
|
||||||
from dingtalkchatbot.chatbot import DingtalkChatbot
|
from dingtalkchatbot.chatbot import DingtalkChatbot
|
||||||
webhook = Config().DINGTALK_WEBHOOK
|
webhook = Config().DINGTALK_WEBHOOK
|
||||||
|
|||||||
106
py12306/helpers/qrcode.py
Normal file
106
py12306/helpers/qrcode.py
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import png
|
||||||
|
|
||||||
|
|
||||||
|
def print_qrcode(path):
|
||||||
|
"""
|
||||||
|
将二维码输出到控制台
|
||||||
|
需要终端尺寸足够大才能显示
|
||||||
|
|
||||||
|
:param path: 二维码图片路径 (PNG 格式)
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
|
reader = png.Reader(path)
|
||||||
|
width, height, rows, info = reader.read()
|
||||||
|
lines = list(rows)
|
||||||
|
|
||||||
|
planes = info['planes'] # 通道数
|
||||||
|
threshold = (2 ** info['bitdepth']) / 2 # 色彩阈值
|
||||||
|
|
||||||
|
# 识别二维码尺寸
|
||||||
|
x_flag = -1 # x 边距标志
|
||||||
|
y_flag = -1 # y 边距标志
|
||||||
|
x_white = -1 # 定位图案白块 x 坐标
|
||||||
|
y_white = -1 # 定位图案白块 y 坐标
|
||||||
|
|
||||||
|
i = y_flag
|
||||||
|
while i < height:
|
||||||
|
if y_white > 0 and x_white > 0:
|
||||||
|
break
|
||||||
|
j = x_flag
|
||||||
|
while j < width:
|
||||||
|
total = 0
|
||||||
|
for k in range(planes):
|
||||||
|
px = lines[i][j * planes + k]
|
||||||
|
total += px
|
||||||
|
avg = total / planes
|
||||||
|
black = avg < threshold
|
||||||
|
if y_white > 0 and x_white > 0:
|
||||||
|
break
|
||||||
|
if x_flag > 0 > x_white and not black:
|
||||||
|
x_white = j
|
||||||
|
if x_flag == -1 and black:
|
||||||
|
x_flag = j
|
||||||
|
if y_flag > 0 > y_white and not black:
|
||||||
|
y_white = i
|
||||||
|
if y_flag == -1 and black:
|
||||||
|
y_flag = i
|
||||||
|
if x_flag > 0 and y_flag > 0:
|
||||||
|
i += 1
|
||||||
|
j += 1
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
assert y_white - y_flag == x_white - x_flag
|
||||||
|
scale = y_white - y_flag
|
||||||
|
|
||||||
|
assert width - x_flag == height - y_flag
|
||||||
|
module_count = int((width - x_flag * 2) / scale)
|
||||||
|
|
||||||
|
whole_white = '█'
|
||||||
|
whole_black = ' '
|
||||||
|
down_black = '▀'
|
||||||
|
up_black = '▄'
|
||||||
|
|
||||||
|
dual_flag = False
|
||||||
|
last_line = []
|
||||||
|
output = '\n'
|
||||||
|
for i in range(module_count + 2):
|
||||||
|
output += up_black
|
||||||
|
output += '\n'
|
||||||
|
i = y_flag
|
||||||
|
while i < height - y_flag:
|
||||||
|
if dual_flag:
|
||||||
|
output += whole_white
|
||||||
|
t = 0
|
||||||
|
j = x_flag
|
||||||
|
while j < width - x_flag:
|
||||||
|
total = 0
|
||||||
|
for k in range(planes):
|
||||||
|
px = lines[i][j * planes + k]
|
||||||
|
total += px
|
||||||
|
avg = total / planes
|
||||||
|
black = avg < threshold
|
||||||
|
if dual_flag:
|
||||||
|
last_black = last_line[t]
|
||||||
|
if black and last_black:
|
||||||
|
output += whole_black
|
||||||
|
elif black and not last_black:
|
||||||
|
output += down_black
|
||||||
|
elif not black and last_black:
|
||||||
|
output += up_black
|
||||||
|
elif not black and not last_black:
|
||||||
|
output += whole_white
|
||||||
|
else:
|
||||||
|
last_line[t:t+1] = [black]
|
||||||
|
t = t + 1
|
||||||
|
j += scale
|
||||||
|
if dual_flag:
|
||||||
|
output += whole_white + '\n'
|
||||||
|
dual_flag = not dual_flag
|
||||||
|
i += scale
|
||||||
|
output += whole_white
|
||||||
|
for i in range(module_count):
|
||||||
|
output += up_black if last_line[i] else whole_white
|
||||||
|
output += whole_white + '\n'
|
||||||
|
print(output, flush=True)
|
||||||
@@ -35,6 +35,8 @@ class CommonLog(BaseLog):
|
|||||||
MESSAGE_SEND_EMAIL_SUCCESS = '邮件发送成功,请检查收件箱'
|
MESSAGE_SEND_EMAIL_SUCCESS = '邮件发送成功,请检查收件箱'
|
||||||
MESSAGE_SEND_EMAIL_FAIL = '邮件发送失败,请手动检查配置,错误原因 {}'
|
MESSAGE_SEND_EMAIL_FAIL = '邮件发送失败,请手动检查配置,错误原因 {}'
|
||||||
|
|
||||||
|
MESSAGE_SEND_EMAIL_WITH_QRCODE_SUCCESS = '二维码邮件发送成功,请检查收件箱扫描登陆'
|
||||||
|
|
||||||
MESSAGE_SEND_TELEGRAM_SUCCESS = 'Telegram推送成功'
|
MESSAGE_SEND_TELEGRAM_SUCCESS = 'Telegram推送成功'
|
||||||
MESSAGE_SEND_TELEGRAM_FAIL = 'Telegram推送失败,错误原因 {}'
|
MESSAGE_SEND_TELEGRAM_FAIL = 'Telegram推送失败,错误原因 {}'
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,9 @@ class UserLog(BaseLog):
|
|||||||
MESSAGE_DOWNLAODING_THE_CODE = '正在下载验证码...'
|
MESSAGE_DOWNLAODING_THE_CODE = '正在下载验证码...'
|
||||||
MESSAGE_CODE_AUTH_FAIL = '验证码验证失败 错误原因: {}'
|
MESSAGE_CODE_AUTH_FAIL = '验证码验证失败 错误原因: {}'
|
||||||
MESSAGE_CODE_AUTH_SUCCESS = '验证码验证成功 开始登录...'
|
MESSAGE_CODE_AUTH_SUCCESS = '验证码验证成功 开始登录...'
|
||||||
|
MESSAGE_QRCODE_DOWNLOADING = '正在下载二维码...'
|
||||||
|
MESSAGE_QRCODE_DOWNLOADED = '二维码保存在: {},请使用手机客户端扫描'
|
||||||
|
MESSAGE_QRCODE_FAIL = '二维码获取失败: {}, {} 秒后重试'
|
||||||
MESSAGE_LOGIN_FAIL = '登录失败 错误原因: {}'
|
MESSAGE_LOGIN_FAIL = '登录失败 错误原因: {}'
|
||||||
MESSAGE_LOADED_USER = '正在尝试恢复用户: {}'
|
MESSAGE_LOADED_USER = '正在尝试恢复用户: {}'
|
||||||
MESSAGE_LOADED_USER_SUCCESS = '用户恢复成功: {}'
|
MESSAGE_LOADED_USER_SUCCESS = '用户恢复成功: {}'
|
||||||
|
|||||||
@@ -225,7 +225,7 @@ class Order:
|
|||||||
}
|
}
|
||||||
response = self.session.post(API_SUBMIT_ORDER_REQUEST, data)
|
response = self.session.post(API_SUBMIT_ORDER_REQUEST, data)
|
||||||
result = response.json()
|
result = response.json()
|
||||||
if result.get('data') == 'N':
|
if result.get('data') == '0':
|
||||||
OrderLog.add_quick_log(OrderLog.MESSAGE_SUBMIT_ORDER_REQUEST_SUCCESS).flush()
|
OrderLog.add_quick_log(OrderLog.MESSAGE_SUBMIT_ORDER_REQUEST_SUCCESS).flush()
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import sys
|
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
@@ -154,14 +153,14 @@ class Job:
|
|||||||
QueryLog.add_quick_log(msg).flush(publish=False)
|
QueryLog.add_quick_log(msg).flush(publish=False)
|
||||||
raise RuntimeError(msg)
|
raise RuntimeError(msg)
|
||||||
else:
|
else:
|
||||||
pass
|
return date_query.strftime("%Y-%m-%d")
|
||||||
|
|
||||||
def query_by_date(self, date):
|
def query_by_date(self, date):
|
||||||
"""
|
"""
|
||||||
通过日期进行查询
|
通过日期进行查询
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
self.judge_date_legal(date)
|
date = self.judge_date_legal(date)
|
||||||
from py12306.helpers.cdn import Cdn
|
from py12306.helpers.cdn import Cdn
|
||||||
QueryLog.add_log(('\n' if not is_main_thread() else '') + QueryLog.MESSAGE_QUERY_START_BY_DATE.format(date,
|
QueryLog.add_log(('\n' if not is_main_thread() else '') + QueryLog.MESSAGE_QUERY_START_BY_DATE.format(date,
|
||||||
self.left_station,
|
self.left_station,
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
from base64 import b64decode
|
||||||
from py12306.config import Config
|
from py12306.config import Config
|
||||||
from py12306.cluster.cluster import Cluster
|
from py12306.cluster.cluster import Cluster
|
||||||
from py12306.app import app_available_check
|
from py12306.app import app_available_check
|
||||||
@@ -5,7 +6,7 @@ from py12306.helpers.func import *
|
|||||||
from py12306.helpers.request import Request
|
from py12306.helpers.request import Request
|
||||||
from py12306.log.query_log import QueryLog
|
from py12306.log.query_log import QueryLog
|
||||||
from py12306.query.job import Job
|
from py12306.query.job import Job
|
||||||
from py12306.helpers.api import API_QUERY_INIT_PAGE
|
from py12306.helpers.api import API_QUERY_INIT_PAGE, API_GET_BROWSER_DEVICE_ID
|
||||||
|
|
||||||
|
|
||||||
@singleton
|
@singleton
|
||||||
@@ -29,6 +30,7 @@ class Query:
|
|||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.session = Request()
|
self.session = Request()
|
||||||
|
self.request_device_id()
|
||||||
self.cluster = Cluster()
|
self.cluster = Cluster()
|
||||||
self.update_query_interval()
|
self.update_query_interval()
|
||||||
self.update_query_jobs()
|
self.update_query_jobs()
|
||||||
@@ -117,6 +119,32 @@ class Query:
|
|||||||
self.jobs.append(job)
|
self.jobs.append(job)
|
||||||
return job
|
return job
|
||||||
|
|
||||||
|
def request_device_id(self):
|
||||||
|
"""
|
||||||
|
获取加密后的浏览器特征 ID
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
response = self.session.get(API_GET_BROWSER_DEVICE_ID)
|
||||||
|
if response.status_code == 200:
|
||||||
|
try:
|
||||||
|
result = json.loads(response.text)
|
||||||
|
response = self.session.get(b64decode(result['id']).decode())
|
||||||
|
if response.text.find('callbackFunction') >= 0:
|
||||||
|
result = response.text[18:-2]
|
||||||
|
result = json.loads(result)
|
||||||
|
if not Config().is_cache_rail_id_enabled():
|
||||||
|
self.session.cookies.update({
|
||||||
|
'RAIL_EXPIRATION': result.get('exp'),
|
||||||
|
'RAIL_DEVICEID': result.get('dfp'),
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
self.session.cookies.update({
|
||||||
|
'RAIL_EXPIRATION': Config().RAIL_EXPIRATION,
|
||||||
|
'RAIL_DEVICEID': Config().RAIL_DEVICEID,
|
||||||
|
})
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def wait_for_ready(cls):
|
def wait_for_ready(cls):
|
||||||
self = cls()
|
self = cls()
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import json
|
import base64
|
||||||
import pickle
|
import pickle
|
||||||
import re
|
import re
|
||||||
from os import path
|
from os import path
|
||||||
@@ -11,6 +11,7 @@ from py12306.helpers.event import Event
|
|||||||
from py12306.helpers.func import *
|
from py12306.helpers.func import *
|
||||||
from py12306.helpers.request import Request
|
from py12306.helpers.request import Request
|
||||||
from py12306.helpers.type import UserType
|
from py12306.helpers.type import UserType
|
||||||
|
from py12306.helpers.qrcode import print_qrcode
|
||||||
from py12306.log.order_log import OrderLog
|
from py12306.log.order_log import OrderLog
|
||||||
from py12306.log.user_log import UserLog
|
from py12306.log.user_log import UserLog
|
||||||
from py12306.log.common_log import CommonLog
|
from py12306.log.common_log import CommonLog
|
||||||
@@ -23,6 +24,7 @@ class UserJob:
|
|||||||
key = None
|
key = None
|
||||||
user_name = ''
|
user_name = ''
|
||||||
password = ''
|
password = ''
|
||||||
|
type = 'qr'
|
||||||
user = None
|
user = None
|
||||||
info = {} # 用户信息
|
info = {} # 用户信息
|
||||||
last_heartbeat = None
|
last_heartbeat = None
|
||||||
@@ -30,6 +32,7 @@ class UserJob:
|
|||||||
user_loaded = False # 用户是否已加载成功
|
user_loaded = False # 用户是否已加载成功
|
||||||
passengers = []
|
passengers = []
|
||||||
retry_time = 3
|
retry_time = 3
|
||||||
|
retry_count = 0
|
||||||
login_num = 0 # 尝试登录次数
|
login_num = 0 # 尝试登录次数
|
||||||
|
|
||||||
# Init page
|
# Init page
|
||||||
@@ -51,6 +54,7 @@ class UserJob:
|
|||||||
self.key = str(info.get('key'))
|
self.key = str(info.get('key'))
|
||||||
self.user_name = info.get('user_name')
|
self.user_name = info.get('user_name')
|
||||||
self.password = info.get('password')
|
self.password = info.get('password')
|
||||||
|
self.type = info.get('type')
|
||||||
|
|
||||||
def update_user(self):
|
def update_user(self):
|
||||||
from py12306.user.user import User
|
from py12306.user.user import User
|
||||||
@@ -111,7 +115,10 @@ class UserJob:
|
|||||||
if expire: UserLog.print_user_expired()
|
if expire: UserLog.print_user_expired()
|
||||||
self.is_ready = False
|
self.is_ready = False
|
||||||
UserLog.print_start_login(user=self)
|
UserLog.print_start_login(user=self)
|
||||||
return self.login()
|
if self.type == 'qr':
|
||||||
|
return self.qr_login()
|
||||||
|
else:
|
||||||
|
return self.login()
|
||||||
|
|
||||||
def login(self):
|
def login(self):
|
||||||
"""
|
"""
|
||||||
@@ -150,6 +157,82 @@ class UserJob:
|
|||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def qr_login(self):
|
||||||
|
self.request_device_id()
|
||||||
|
image_uuid, png_path = self.download_code()
|
||||||
|
while True:
|
||||||
|
data = {
|
||||||
|
'RAIL_DEVICEID': self.session.cookies.get('RAIL_DEVICEID'),
|
||||||
|
'RAIL_EXPIRATION': self.session.cookies.get('RAIL_EXPIRATION'),
|
||||||
|
'uuid': image_uuid,
|
||||||
|
'appid': 'otn'
|
||||||
|
}
|
||||||
|
response = self.session.post(API_AUTH_QRCODE_CHECK.get('url'), data)
|
||||||
|
result = response.json()
|
||||||
|
result_code = int(result.get('result_code'))
|
||||||
|
if result_code == 0:
|
||||||
|
time.sleep(2)
|
||||||
|
elif result_code == 1:
|
||||||
|
UserLog.add_quick_log('请确认登录').flush()
|
||||||
|
time.sleep(2)
|
||||||
|
elif result_code == 2:
|
||||||
|
break
|
||||||
|
elif result_code == 3:
|
||||||
|
try:
|
||||||
|
os.remove(png_path)
|
||||||
|
except Exception as e:
|
||||||
|
UserLog.add_quick_log('无法删除文件: {}'.format(e)).flush()
|
||||||
|
image_uuid = self.download_code()
|
||||||
|
try:
|
||||||
|
os.remove(png_path)
|
||||||
|
except Exception as e:
|
||||||
|
UserLog.add_quick_log('无法删除文件: {}'.format(e)).flush()
|
||||||
|
|
||||||
|
self.session.get(API_USER_LOGIN, allow_redirects=True)
|
||||||
|
new_tk = self.auth_uamtk()
|
||||||
|
user_name = self.auth_uamauthclient(new_tk)
|
||||||
|
self.update_user_info({'user_name': user_name})
|
||||||
|
self.session.get(API_USER_LOGIN, allow_redirects=True)
|
||||||
|
self.login_did_success()
|
||||||
|
return True
|
||||||
|
|
||||||
|
def download_code(self):
|
||||||
|
try:
|
||||||
|
UserLog.add_quick_log(UserLog.MESSAGE_QRCODE_DOWNLOADING).flush()
|
||||||
|
response = self.session.post(API_AUTH_QRCODE_BASE64_DOWNLOAD.get('url'), data={'appid': 'otn'})
|
||||||
|
result = response.json()
|
||||||
|
if result.get('result_code') == '0':
|
||||||
|
img_bytes = base64.b64decode(result.get('image'))
|
||||||
|
try:
|
||||||
|
os.mkdir(Config().USER_DATA_DIR + '/qrcode')
|
||||||
|
except FileExistsError:
|
||||||
|
pass
|
||||||
|
png_path = path.normpath(Config().USER_DATA_DIR + '/qrcode/%d.png' % time.time())
|
||||||
|
with open(png_path, 'wb') as file:
|
||||||
|
file.write(img_bytes)
|
||||||
|
file.close()
|
||||||
|
if os.name == 'nt':
|
||||||
|
os.startfile(png_path)
|
||||||
|
else:
|
||||||
|
print_qrcode(png_path)
|
||||||
|
UserLog.add_log(UserLog.MESSAGE_QRCODE_DOWNLOADED.format(png_path)).flush()
|
||||||
|
Notification.send_email_with_qrcode(Config().EMAIL_RECEIVER, '你有新的登录二维码啦!', png_path)
|
||||||
|
self.retry_count = 0
|
||||||
|
return result.get('uuid'), png_path
|
||||||
|
raise KeyError('获取二维码失败: {}'.format(result.get('result_message')))
|
||||||
|
except Exception as e:
|
||||||
|
UserLog.add_quick_log(
|
||||||
|
UserLog.MESSAGE_QRCODE_FAIL.format(e, self.retry_time)).flush()
|
||||||
|
self.retry_count = self.retry_count + 1
|
||||||
|
if self.retry_count == 20:
|
||||||
|
self.retry_count = 0
|
||||||
|
try:
|
||||||
|
os.remove(self.get_cookie_path())
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
time.sleep(self.retry_time)
|
||||||
|
return self.download_code()
|
||||||
|
|
||||||
def check_user_is_login(self):
|
def check_user_is_login(self):
|
||||||
response = self.session.get(API_USER_LOGIN_CHECK)
|
response = self.session.get(API_USER_LOGIN_CHECK)
|
||||||
is_login = response.json().get('data.is_login', False) == 'Y'
|
is_login = response.json().get('data.is_login', False) == 'Y'
|
||||||
@@ -189,11 +272,10 @@ class UserJob:
|
|||||||
try:
|
try:
|
||||||
result = json.loads(response.text)
|
result = json.loads(response.text)
|
||||||
headers = {
|
headers = {
|
||||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36"
|
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.61 Safari/537.36"
|
||||||
}
|
}
|
||||||
from base64 import b64decode
|
|
||||||
self.session.headers.update(headers)
|
self.session.headers.update(headers)
|
||||||
response = self.session.get(b64decode(result['id']).decode())
|
response = self.session.get(base64.b64decode(result['id']).decode())
|
||||||
if response.text.find('callbackFunction') >= 0:
|
if response.text.find('callbackFunction') >= 0:
|
||||||
result = response.text[18:-2]
|
result = response.text[18:-2]
|
||||||
result = json.loads(result)
|
result = json.loads(result)
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ class User:
|
|||||||
@classmethod
|
@classmethod
|
||||||
def run(cls):
|
def run(cls):
|
||||||
self = cls()
|
self = cls()
|
||||||
app_available_check()
|
# app_available_check() 用户系统不休息
|
||||||
self.start()
|
self.start()
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
-i http://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com
|
-i https://pypi.tuna.tsinghua.edu.cn/simple
|
||||||
appdirs==1.4.3
|
appdirs==1.4.3
|
||||||
beautifulsoup4==4.7.0
|
beautifulsoup4==4.7.0
|
||||||
bs4==0.0.1
|
bs4==0.0.1
|
||||||
@@ -12,7 +12,7 @@ Flask-JWT-Extended==3.15.0
|
|||||||
idna==2.8
|
idna==2.8
|
||||||
itsdangerous==1.1.0
|
itsdangerous==1.1.0
|
||||||
Jinja2==2.10
|
Jinja2==2.10
|
||||||
lxml==4.3.0
|
lxml==4.6.3
|
||||||
MarkupSafe==1.1.0
|
MarkupSafe==1.1.0
|
||||||
parse==1.9.0
|
parse==1.9.0
|
||||||
pyee==5.0.0
|
pyee==5.0.0
|
||||||
@@ -28,6 +28,7 @@ tqdm==4.28.1
|
|||||||
urllib3==1.24.2
|
urllib3==1.24.2
|
||||||
w3lib==1.19.0
|
w3lib==1.19.0
|
||||||
websockets==7.0
|
websockets==7.0
|
||||||
Werkzeug==0.15.3
|
Werkzeug==0.15.5
|
||||||
DingtalkChatbot==1.3.0
|
DingtalkChatbot==1.3.0
|
||||||
lightpush==0.1.3
|
lightpush==0.1.3
|
||||||
|
pypng
|
||||||
|
|||||||
Reference in New Issue
Block a user