Skip to content

Commit

Permalink
Merge pull request #412 from Gardelll/feature/qr_login
Browse files Browse the repository at this point in the history
添加扫码登录功能
  • Loading branch information
pjialin authored Sep 27, 2021
2 parents 49d35aa + 4f3abc9 commit db34583
Show file tree
Hide file tree
Showing 11 changed files with 246 additions and 21 deletions.
6 changes: 4 additions & 2 deletions env.docker.py.example
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ USER_ACCOUNTS = [
{
'key': 0, # 如使用多个账号 key 不能重复
'user_name': 'your user name',
'password': 'your password'
'password': '忽略',
'type': 'qr' # qr 为扫码登录,填写其他为密码登录
},
# {
# 'key': 'wangwu',
# 'user_name': '[email protected]',
# 'password': 'wangwu'
# 'password': 'wangwu',
# 'type': ''
# }
]

Expand Down
6 changes: 4 additions & 2 deletions env.py.example
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ USER_ACCOUNTS = [
{
'key': 0, # 如使用多个账号 key 不能重复
'user_name': 'your user name',
'password': 'your password'
'password': '忽略',
'type': 'qr' # qr 为扫码登录,填写其他为密码登录
},
# {
# 'key': 'wangwu',
# 'user_name': '[email protected]',
# 'password': 'wangwu'
# 'password': 'wangwu',
# 'type': ''
# }
]

Expand Down
10 changes: 6 additions & 4 deletions py12306/app.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
# -*- coding: utf-8 -*-
import os
import signal
import sys

Expand All @@ -11,16 +10,19 @@


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:
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, 6)
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


Expand Down
14 changes: 12 additions & 2 deletions py12306/helpers/api.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
# coding=utf-8
# 查询余票
import time

HOST_URL_OF_12306 = 'kyfw.12306.cn'
BASE_URL_OF_12306 = 'https://' + HOST_URL_OF_12306
Expand All @@ -15,6 +13,18 @@

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 = {
'url': BASE_URL_OF_12306 + '/passport/captcha/captcha-image?login_site=E&module=login&rand=sjrand&_={random}'
}
Expand Down
106 changes: 106 additions & 0 deletions py12306/helpers/qrcode.py
Original file line number Diff line number Diff line change
@@ -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)
3 changes: 3 additions & 0 deletions py12306/log/user_log.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ class UserLog(BaseLog):
MESSAGE_DOWNLAODING_THE_CODE = '正在下载验证码...'
MESSAGE_CODE_AUTH_FAIL = '验证码验证失败 错误原因: {}'
MESSAGE_CODE_AUTH_SUCCESS = '验证码验证成功 开始登录...'
MESSAGE_QRCODE_DOWNLOADING = '正在下载二维码...'
MESSAGE_QRCODE_DOWNLOADED = '二维码保存在: {},请使用手机客户端扫描'
MESSAGE_QRCODE_FAIL = '二维码获取失败: {}, {} 秒后重试'
MESSAGE_LOGIN_FAIL = '登录失败 错误原因: {}'
MESSAGE_LOADED_USER = '正在尝试恢复用户: {}'
MESSAGE_LOADED_USER_SUCCESS = '用户恢复成功: {}'
Expand Down
5 changes: 2 additions & 3 deletions py12306/query/job.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import sys
from datetime import timedelta
from datetime import datetime

Expand Down Expand Up @@ -154,14 +153,14 @@ def judge_date_legal(self, date):
QueryLog.add_quick_log(msg).flush(publish=False)
raise RuntimeError(msg)
else:
pass
return date_query.strftime("%Y-%m-%d")

def query_by_date(self, date):
"""
通过日期进行查询
:return:
"""
self.judge_date_legal(date)
date = self.judge_date_legal(date)
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,
Expand Down
30 changes: 29 additions & 1 deletion py12306/query/query.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
from base64 import b64decode
from py12306.config import Config
from py12306.cluster.cluster import Cluster
from py12306.app import app_available_check
from py12306.helpers.func import *
from py12306.helpers.request import Request
from py12306.log.query_log import QueryLog
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
Expand All @@ -29,6 +30,7 @@ class Query:

def __init__(self):
self.session = Request()
self.request_device_id()
self.cluster = Cluster()
self.update_query_interval()
self.update_query_jobs()
Expand Down Expand Up @@ -117,6 +119,32 @@ def init_job(self, job):
self.jobs.append(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
def wait_for_ready(cls):
self = cls()
Expand Down
Loading

0 comments on commit db34583

Please sign in to comment.