Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
P
py12306
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
python
py12306
Commits
845f3032
Commit
845f3032
authored
Jan 11, 2019
by
Jalin
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
增加邮件通知
parent
dba893e7
Changes
10
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
114 additions
and
23 deletions
+114
-23
README.md
README.md
+2
-2
env.docker.py.example
env.docker.py.example
+9
-1
env.py.example
env.py.example
+10
-1
app.py
py12306/app.py
+3
-0
config.py
py12306/config.py
+8
-0
notification.py
py12306/helpers/notification.py
+27
-1
type.py
py12306/helpers/type.py
+2
-3
common_log.py
py12306/log/common_log.py
+5
-0
order_log.py
py12306/log/order_log.py
+6
-2
order.py
py12306/order/order.py
+42
-13
No files found.
README.md
View file @
845f3032
...
...
@@ -15,7 +15,7 @@
-
[
x
]
分布式运行
-
[
x
]
Docker 支持
-
[
x
]
动态修改配置文件
-
[
]
邮件通知
-
[
x
]
邮件通知
-
[
]
Web 管理页面
## 使用
...
...
@@ -49,7 +49,7 @@ cp env.py.example env.py
python main.py
-t
```
测试
语音通知
-t -n
测试
通知消息 (语音, 邮件)
-t -n
```
bash
# 默认不会进行通知测试,要对通知进行测试需要加上 -n 参数
python main.py
-t
-n
...
...
env.docker.py.example
View file @
845f3032
...
...
@@ -37,7 +37,7 @@ AUTO_CODE_ACCOUNT = {
# 没找到比较好用的,现在用的这个是阿里云 API 市场上的,基本满足要求,价格也便宜
# 购买成功后到控制台找到 APPCODE 放在下面就可以了
# 地址:https://market.aliyun.com/products/57126001/cmapi019902.html
NOTIFICATION_BY_VOICE_CODE = 1 # 开启语音
验证码
NOTIFICATION_BY_VOICE_CODE = 1 # 开启语音
通知
NOTIFICATION_API_APP_CODE = 'your app code'
NOTIFICATION_VOICE_CODE_PHONE = 'your phone' # 接受通知的手机号
...
...
@@ -57,6 +57,14 @@ REDIS_HOST = 'localhost' # Redis host
REDIS_PORT = '6379' # Redis post
REDIS_PASSWORD = '' # # Redis 密码 没有可以留空
# 邮箱配置
EMAIL_ENABLED = 0 # 是否开启邮件通知
EMAIL_SENDER = 'sender@example.com' # 邮件发送者
EMAIL_RECEIVER = 'receiver@example.com' # 邮件接受者 # 可以多个 [email1@gmail.com, email2@gmail.com]
EMAIL_SERVER_HOST = 'localhost' # 邮件服务 host
EMAIL_SERVER_USER = ''
EMAIL_SERVER_PASSWORD = ''
# 查询任务
QUERY_JOBS = [
{
...
...
env.py.example
View file @
845f3032
...
...
@@ -37,7 +37,7 @@ AUTO_CODE_ACCOUNT = {
# 没找到比较好用的,现在用的这个是阿里云 API 市场上的,基本满足要求,价格也便宜
# 购买成功后到控制台找到 APPCODE 放在下面就可以了
# 地址:https://market.aliyun.com/products/57126001/cmapi019902.html
NOTIFICATION_BY_VOICE_CODE = 1 # 开启语音
验证码
NOTIFICATION_BY_VOICE_CODE = 1 # 开启语音
通知
NOTIFICATION_API_APP_CODE = 'your app code'
NOTIFICATION_VOICE_CODE_PHONE = 'your phone' # 接受通知的手机号
...
...
@@ -54,6 +54,15 @@ REDIS_HOST = 'localhost' # Redis host
REDIS_PORT = '6379' # Redis post
REDIS_PASSWORD = '' # # Redis 密码 没有可以留空
# 邮箱配置
EMAIL_ENABLED = 0 # 是否开启邮件通知
EMAIL_SENDER = 'sender@example.com' # 邮件发送者
EMAIL_RECEIVER = 'receiver@example.com' # 邮件接受者 # 可以多个 [email1@gmail.com, email2@gmail.com]
EMAIL_SERVER_HOST = 'localhost' # 邮件服务 host
EMAIL_SERVER_USER = ''
EMAIL_SERVER_PASSWORD = ''
# 查询任务
QUERY_JOBS = [
{
...
...
py12306/app.py
View file @
845f3032
...
...
@@ -93,6 +93,9 @@ class App:
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'
)
@
classmethod
def
run_check
(
cls
):
...
...
py12306/config.py
View file @
845f3032
...
...
@@ -53,6 +53,14 @@ class Config:
REDIS_PORT
=
'6379'
REDIS_PASSWORD
=
''
# 邮箱配置
EMAIL_ENABLED
=
0
EMAIL_SENDER
=
''
EMAIL_RECEIVER
=
''
EMAIL_SERVER_HOST
=
''
EMAIL_SERVER_USER
=
''
EMAIL_SERVER_PASSWORD
=
''
envs
=
[]
retry_time
=
5
last_modify_time
=
0
...
...
py12306/helpers/notification.py
View file @
845f3032
...
...
@@ -20,6 +20,11 @@ class Notification():
self
=
cls
()
self
.
send_voice_code_of_yiyuan
(
phone
,
name
=
name
,
content
=
content
)
@
classmethod
def
send_email
(
cls
,
to
,
title
=
''
,
content
=
''
):
self
=
cls
()
self
.
send_email_by_smtp
(
to
,
title
,
content
)
def
send_voice_code_of_yiyuan
(
self
,
phone
,
name
=
''
,
content
=
''
):
"""
发送语音验证码
...
...
@@ -54,6 +59,27 @@ class Notification():
else
:
return
CommonLog
.
add_quick_log
(
CommonLog
.
MESSAGE_VOICE_API_SEND_FAIL
.
format
(
response_message
))
.
flush
()
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'
]
=
'service@pjialin.com'
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
.
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
()
if
__name__
==
'__main__'
:
Notification
.
voice_code
(
'13065667742'
,
'张三'
,
'你的车票 广州 到 深圳 购买成功,请登录 12306 进行支付'
)
name
=
'张三3'
content
=
'你的车票 广州 到 深圳 购买成功,请登录 12306 进行支付'
# Notification.voice_code('13800138000', name, content)
Notification
.
send_email
(
'admin@pjialin.com'
,
name
,
content
)
py12306/helpers/type.py
View file @
845f3032
...
...
@@ -33,6 +33,7 @@ class OrderSeatType:
@
singleton
class
SeatType
:
NO_SEAT
=
26
dicts
=
{
'特等座'
:
25
,
'商务座'
:
32
,
...
...
@@ -42,7 +43,5 @@ class SeatType:
'硬卧'
:
28
,
'动卧'
:
33
,
'硬座'
:
29
,
'无座'
:
26
,
'无座'
:
NO_SEAT
,
}
py12306/log/common_log.py
View file @
845f3032
...
...
@@ -22,8 +22,13 @@ class CommonLog(BaseLog):
MESSAGE_CHECK_EMPTY_USER_ACCOUNT
=
'请配置 12306 账号密码'
MESSAGE_TEST_SEND_VOICE_CODE
=
'正在测试发送语音验证码...'
MESSAGE_TEST_SEND_EMAIL
=
'正在测试发送邮件...'
MESSAGE_CONFIG_FILE_DID_CHANGED
=
'配置文件已修改,正在重新加载中
\n
'
MESSAGE_API_RESPONSE_CAN_NOT_BE_HANDLE
=
'接口返回错误'
MESSAGE_SEND_EMAIL_SUCCESS
=
'邮件发送成功,请检查收件箱'
MESSAGE_SEND_EMAIL_FAIL
=
'邮件发送失败,请手动检查配置,错误原因 {}'
def
__init__
(
self
):
super
()
.
__init__
()
...
...
py12306/log/order_log.py
View file @
845f3032
...
...
@@ -18,13 +18,14 @@ class OrderLog(BaseLog):
MESSAGE_CHECK_ORDER_INFO_SUCCESS
=
'检查订单成功'
MESSAGE_GET_QUEUE_COUNT_SUCCESS
=
'排队成功,你当前排在第 {} 位, 余票还剩余 {} 张'
MESSAGE_GET_QUEUE_LESS_TICKET
=
'排队失败,目前排队人数已经超过余票张数'
MESSAGE_GET_QUEUE_COUNT_FAIL
=
'排队失败,错误原因 {}'
MESSAGE_CONFIRM_SINGLE_FOR_QUEUE_SUCCESS
=
'# 提交订单成功!#'
MESSAGE_CONFIRM_SINGLE_FOR_QUEUE_ERROR
=
'
提交订单出错
,错误原因 {}'
MESSAGE_CONFIRM_SINGLE_FOR_QUEUE_ERROR
=
'
出票失败
,错误原因 {}'
MESSAGE_CONFIRM_SINGLE_FOR_QUEUE_FAIL
=
'提交订单失败,错误原因 {}'
MESSAGE_QUERY_ORDER_WAIT_TIME_WAITING
=
'排队等待中,预计还需要 {} 秒'
MESSAGE_QUERY_ORDER_WAIT_TIME_WAITING
=
'排队等待中,
排队人数 {},
预计还需要 {} 秒'
MESSAGE_QUERY_ORDER_WAIT_TIME_FAIL
=
'排队失败,错误原因 {}'
MESSAGE_QUERY_ORDER_WAIT_TIME_INFO
=
'第 {} 次排队,请耐心等待'
...
...
@@ -34,6 +35,9 @@ class OrderLog(BaseLog):
MESSAGE_ORDER_SUCCESS_NOTIFICATION_OF_VOICE_CODE_START_SEND
=
'正在发送语音通知, 第 {} 次'
MESSAGE_ORDER_SUCCESS_NOTIFICATION_OF_VOICE_CODE_CONTENT
=
'你的车票 {} 到 {} 购买成功,请登录 12306 进行支付'
MESSAGE_ORDER_SUCCESS_NOTIFICATION_OF_EMAIL_CONTENT
=
'订单号 {},请及时器登录12306,打开
\'
未完成订单
\'
,在30分钟内完成支付!'
MESSAGE_JOB_CLOSED
=
'当前任务已结束'
@
classmethod
...
...
py12306/order/order.py
View file @
845f3032
...
...
@@ -5,7 +5,8 @@ from py12306.config import Config
from
py12306.helpers.api
import
*
from
py12306.helpers.func
import
*
from
py12306.helpers.notification
import
Notification
from
py12306.helpers.type
import
UserType
from
py12306.helpers.type
import
UserType
,
SeatType
from
py12306.log.common_log
import
CommonLog
from
py12306.log.order_log
import
OrderLog
...
...
@@ -51,6 +52,8 @@ class Order:
"""
# Debug
if
Config
()
.
IS_DEBUG
:
self
.
order_id
=
'test'
self
.
order_did_success
()
return
random
.
randint
(
0
,
10
)
>
7
return
self
.
normal_order
()
...
...
@@ -76,6 +79,9 @@ class Order:
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
))
while
sustain_time
:
# TODO 后面直接查询有没有待支付的订单就可以
num
+=
1
if
Config
()
.
NOTIFICATION_BY_VOICE_CODE
:
# 语音通知
...
...
@@ -83,10 +89,12 @@ class Order:
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
)
OrderLog
.
add_quick_log
(
OrderLog
.
MESSAGE_JOB_CLOSED
)
.
flush
()
def
submit_order_request
(
self
):
data
=
{
...
...
@@ -136,15 +144,23 @@ class Order:
response
=
self
.
session
.
post
(
API_CHECK_ORDER_INFO
,
data
)
result
=
response
.
json
()
if
result
.
get
(
'data.submitStatus'
):
# 成功
# ifShowPassCode 需要验证码
OrderLog
.
add_quick_log
(
OrderLog
.
MESSAGE_CHECK_ORDER_INFO_SUCCESS
)
.
flush
()
if
result
.
get
(
'data.ifShowPassCode'
)
!=
'N'
:
self
.
is_need_auth_code
=
True
# if ( ticketInfoForPassengerForm.isAsync == ticket_submit_order.request_flag.isAsync & & ticketInfoForPassengerForm.queryLeftTicketRequestDTO.ypInfoDetail != "") { 不需要排队检测 js TODO
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
()
error
=
CommonLog
.
MESSAGE_API_RESPONSE_CAN_NOT_BE_HANDLE
if
not
result
.
get
(
'data.isNoActive'
):
error
=
result
.
get
(
'data.errMsg'
)
else
:
if
result
.
get
(
'data.checkSeatNum'
):
error
=
'无法提交您的订单! '
+
result
.
get
(
'data.errMsg'
)
else
:
error
=
'出票失败! '
+
result
.
get
(
'data.errMsg'
)
OrderLog
.
add_quick_log
(
OrderLog
.
MESSAGE_CHECK_ORDER_INFO_FAIL
.
format
(
error
))
.
flush
()
return
False
def
get_queue_count
(
self
):
...
...
@@ -182,7 +198,7 @@ class Order:
}
response
=
self
.
session
.
post
(
API_GET_QUEUE_COUNT
,
data
)
result
=
response
.
json
()
if
result
.
get
(
'
data.countT'
)
or
result
.
get
(
'data.ticket'
):
# 成功
if
result
.
get
(
'
status'
,
False
):
# 成功
"""
"data": {
"count": "66",
...
...
@@ -191,10 +207,22 @@ class Order:
"countT": "0",
"op_1": "true"
}
"""
ticket
=
result
.
get
(
'data.ticket'
)
.
split
(
','
)
# 暂不清楚具体作用
ticket_number
=
sum
(
map
(
int
,
ticket
))
current_position
=
int
(
data
.
get
(
'countT'
,
0
))
# if result.get('isRelogin') == 'Y': # 重新登录 TODO
ticket
=
result
.
get
(
'data.ticket'
)
.
split
(
','
)
# 余票列表
# 这里可以判断 是真实是 硬座还是无座,避免自动分配到无座
ticket_number
=
ticket
[
0
]
# 余票
if
ticket_number
!=
'充足'
or
int
(
ticket_number
)
<=
0
:
if
self
.
query_ins
.
current_seat
==
SeatType
.
NO_SEAT
:
# 允许无座
ticket_number
=
ticket
[
1
]
if
result
.
get
(
'data.op_2'
)
==
'true'
:
OrderLog
.
add_quick_log
(
OrderLog
.
MESSAGE_GET_QUEUE_LESS_TICKET
)
.
flush
()
return
False
current_position
=
int
(
result
.
get
(
'data.countT'
,
0
))
OrderLog
.
add_quick_log
(
OrderLog
.
MESSAGE_GET_QUEUE_COUNT_SUCCESS
.
format
(
current_position
,
ticket_number
))
.
flush
()
return
True
...
...
@@ -275,7 +303,7 @@ class Order:
"""
self
.
current_queue_wait
=
self
.
max_queue_wait
while
self
.
current_queue_wait
:
self
.
current_queue_wait
-=
1
self
.
current_queue_wait
-=
self
.
wait_queue_interval
# TODO 取消超时订单,待优化
data
=
{
#
'random'
:
str
(
random
.
random
())[
2
:],
...
...
@@ -303,9 +331,10 @@ class Order:
order_id
=
result_data
.
get
(
'orderId'
)
if
order_id
:
# 成功
return
order_id
elif
result_data
.
get
(
'waitTime'
)
and
result_data
.
get
(
'waitTime'
)
>=
0
:
elif
result_data
.
get
(
'waitTime'
)
!=
-
10
0
:
OrderLog
.
add_quick_log
(
OrderLog
.
MESSAGE_QUERY_ORDER_WAIT_TIME_WAITING
.
format
(
result_data
.
get
(
'waitTime'
)))
.
flush
()
OrderLog
.
MESSAGE_QUERY_ORDER_WAIT_TIME_WAITING
.
format
(
result_data
.
get
(
'waitCount'
,
0
),
result_data
.
get
(
'waitTime'
)))
.
flush
()
elif
result_data
.
get
(
'msg'
):
# 失败 对不起,由于您取消次数过多,今日将不能继续受理您的订票请求。1月8日您可继续使用订票功能。
# TODO 需要增加判断 直接结束
OrderLog
.
add_quick_log
(
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment