Commit 845f3032 authored by Jalin's avatar Jalin

增加邮件通知

parent dba893e7
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
- [x] 分布式运行 - [x] 分布式运行
- [x] Docker 支持 - [x] Docker 支持
- [x] 动态修改配置文件 - [x] 动态修改配置文件
- [ ] 邮件通知 - [x] 邮件通知
- [ ] Web 管理页面 - [ ] Web 管理页面
## 使用 ## 使用
...@@ -49,7 +49,7 @@ cp env.py.example env.py ...@@ -49,7 +49,7 @@ cp env.py.example env.py
python main.py -t python main.py -t
``` ```
测试语音通知 -t -n 测试通知消息 (语音, 邮件) -t -n
```bash ```bash
# 默认不会进行通知测试,要对通知进行测试需要加上 -n 参数 # 默认不会进行通知测试,要对通知进行测试需要加上 -n 参数
python main.py -t -n python main.py -t -n
......
...@@ -37,7 +37,7 @@ AUTO_CODE_ACCOUNT = { ...@@ -37,7 +37,7 @@ AUTO_CODE_ACCOUNT = {
# 没找到比较好用的,现在用的这个是阿里云 API 市场上的,基本满足要求,价格也便宜 # 没找到比较好用的,现在用的这个是阿里云 API 市场上的,基本满足要求,价格也便宜
# 购买成功后到控制台找到 APPCODE 放在下面就可以了 # 购买成功后到控制台找到 APPCODE 放在下面就可以了
# 地址:https://market.aliyun.com/products/57126001/cmapi019902.html # 地址: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_API_APP_CODE = 'your app code'
NOTIFICATION_VOICE_CODE_PHONE = 'your phone' # 接受通知的手机号 NOTIFICATION_VOICE_CODE_PHONE = 'your phone' # 接受通知的手机号
...@@ -57,6 +57,14 @@ REDIS_HOST = 'localhost' # Redis host ...@@ -57,6 +57,14 @@ REDIS_HOST = 'localhost' # Redis host
REDIS_PORT = '6379' # Redis post REDIS_PORT = '6379' # Redis post
REDIS_PASSWORD = '' # # Redis 密码 没有可以留空 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 = [ QUERY_JOBS = [
{ {
......
...@@ -37,7 +37,7 @@ AUTO_CODE_ACCOUNT = { ...@@ -37,7 +37,7 @@ AUTO_CODE_ACCOUNT = {
# 没找到比较好用的,现在用的这个是阿里云 API 市场上的,基本满足要求,价格也便宜 # 没找到比较好用的,现在用的这个是阿里云 API 市场上的,基本满足要求,价格也便宜
# 购买成功后到控制台找到 APPCODE 放在下面就可以了 # 购买成功后到控制台找到 APPCODE 放在下面就可以了
# 地址:https://market.aliyun.com/products/57126001/cmapi019902.html # 地址: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_API_APP_CODE = 'your app code'
NOTIFICATION_VOICE_CODE_PHONE = 'your phone' # 接受通知的手机号 NOTIFICATION_VOICE_CODE_PHONE = 'your phone' # 接受通知的手机号
...@@ -54,6 +54,15 @@ REDIS_HOST = 'localhost' # Redis host ...@@ -54,6 +54,15 @@ REDIS_HOST = 'localhost' # Redis host
REDIS_PORT = '6379' # Redis post REDIS_PORT = '6379' # Redis post
REDIS_PASSWORD = '' # # Redis 密码 没有可以留空 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 = [ QUERY_JOBS = [
{ {
......
...@@ -93,6 +93,9 @@ class App: ...@@ -93,6 +93,9 @@ class App:
Notification.voice_code(Config().NOTIFICATION_VOICE_CODE_PHONE, '张三', Notification.voice_code(Config().NOTIFICATION_VOICE_CODE_PHONE, '张三',
OrderLog.MESSAGE_ORDER_SUCCESS_NOTIFICATION_OF_VOICE_CODE_CONTENT.format('北京', 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 @classmethod
def run_check(cls): def run_check(cls):
......
...@@ -53,6 +53,14 @@ class Config: ...@@ -53,6 +53,14 @@ class Config:
REDIS_PORT = '6379' REDIS_PORT = '6379'
REDIS_PASSWORD = '' REDIS_PASSWORD = ''
# 邮箱配置
EMAIL_ENABLED = 0
EMAIL_SENDER = ''
EMAIL_RECEIVER = ''
EMAIL_SERVER_HOST = ''
EMAIL_SERVER_USER = ''
EMAIL_SERVER_PASSWORD = ''
envs = [] envs = []
retry_time = 5 retry_time = 5
last_modify_time = 0 last_modify_time = 0
......
...@@ -20,6 +20,11 @@ class Notification(): ...@@ -20,6 +20,11 @@ class Notification():
self = cls() self = cls()
self.send_voice_code_of_yiyuan(phone, name=name, content=content) 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=''): def send_voice_code_of_yiyuan(self, phone, name='', content=''):
""" """
发送语音验证码 发送语音验证码
...@@ -54,6 +59,27 @@ class Notification(): ...@@ -54,6 +59,27 @@ class Notification():
else: else:
return CommonLog.add_quick_log(CommonLog.MESSAGE_VOICE_API_SEND_FAIL.format(response_message)).flush() 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__': 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)
...@@ -33,6 +33,7 @@ class OrderSeatType: ...@@ -33,6 +33,7 @@ class OrderSeatType:
@singleton @singleton
class SeatType: class SeatType:
NO_SEAT = 26
dicts = { dicts = {
'特等座': 25, '特等座': 25,
'商务座': 32, '商务座': 32,
...@@ -42,7 +43,5 @@ class SeatType: ...@@ -42,7 +43,5 @@ class SeatType:
'硬卧': 28, '硬卧': 28,
'动卧': 33, '动卧': 33,
'硬座': 29, '硬座': 29,
'无座': 26, '无座': NO_SEAT,
} }
...@@ -22,8 +22,13 @@ class CommonLog(BaseLog): ...@@ -22,8 +22,13 @@ class CommonLog(BaseLog):
MESSAGE_CHECK_EMPTY_USER_ACCOUNT = '请配置 12306 账号密码' MESSAGE_CHECK_EMPTY_USER_ACCOUNT = '请配置 12306 账号密码'
MESSAGE_TEST_SEND_VOICE_CODE = '正在测试发送语音验证码...' MESSAGE_TEST_SEND_VOICE_CODE = '正在测试发送语音验证码...'
MESSAGE_TEST_SEND_EMAIL = '正在测试发送邮件...'
MESSAGE_CONFIG_FILE_DID_CHANGED = '配置文件已修改,正在重新加载中\n' MESSAGE_CONFIG_FILE_DID_CHANGED = '配置文件已修改,正在重新加载中\n'
MESSAGE_API_RESPONSE_CAN_NOT_BE_HANDLE = '接口返回错误'
MESSAGE_SEND_EMAIL_SUCCESS = '邮件发送成功,请检查收件箱'
MESSAGE_SEND_EMAIL_FAIL = '邮件发送失败,请手动检查配置,错误原因 {}'
def __init__(self): def __init__(self):
super().__init__() super().__init__()
......
...@@ -18,13 +18,14 @@ class OrderLog(BaseLog): ...@@ -18,13 +18,14 @@ class OrderLog(BaseLog):
MESSAGE_CHECK_ORDER_INFO_SUCCESS = '检查订单成功' MESSAGE_CHECK_ORDER_INFO_SUCCESS = '检查订单成功'
MESSAGE_GET_QUEUE_COUNT_SUCCESS = '排队成功,你当前排在第 {} 位, 余票还剩余 {} 张' MESSAGE_GET_QUEUE_COUNT_SUCCESS = '排队成功,你当前排在第 {} 位, 余票还剩余 {} 张'
MESSAGE_GET_QUEUE_LESS_TICKET = '排队失败,目前排队人数已经超过余票张数'
MESSAGE_GET_QUEUE_COUNT_FAIL = '排队失败,错误原因 {}' MESSAGE_GET_QUEUE_COUNT_FAIL = '排队失败,错误原因 {}'
MESSAGE_CONFIRM_SINGLE_FOR_QUEUE_SUCCESS = '# 提交订单成功!#' 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_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_FAIL = '排队失败,错误原因 {}'
MESSAGE_QUERY_ORDER_WAIT_TIME_INFO = '第 {} 次排队,请耐心等待' MESSAGE_QUERY_ORDER_WAIT_TIME_INFO = '第 {} 次排队,请耐心等待'
...@@ -34,6 +35,9 @@ class OrderLog(BaseLog): ...@@ -34,6 +35,9 @@ class OrderLog(BaseLog):
MESSAGE_ORDER_SUCCESS_NOTIFICATION_OF_VOICE_CODE_START_SEND = '正在发送语音通知, 第 {} 次' MESSAGE_ORDER_SUCCESS_NOTIFICATION_OF_VOICE_CODE_START_SEND = '正在发送语音通知, 第 {} 次'
MESSAGE_ORDER_SUCCESS_NOTIFICATION_OF_VOICE_CODE_CONTENT = '你的车票 {} 到 {} 购买成功,请登录 12306 进行支付' MESSAGE_ORDER_SUCCESS_NOTIFICATION_OF_VOICE_CODE_CONTENT = '你的车票 {} 到 {} 购买成功,请登录 12306 进行支付'
MESSAGE_ORDER_SUCCESS_NOTIFICATION_OF_EMAIL_CONTENT = '订单号 {},请及时器登录12306,打开 \'未完成订单\',在30分钟内完成支付!'
MESSAGE_JOB_CLOSED = '当前任务已结束' MESSAGE_JOB_CLOSED = '当前任务已结束'
@classmethod @classmethod
......
...@@ -5,7 +5,8 @@ from py12306.config import Config ...@@ -5,7 +5,8 @@ from py12306.config import Config
from py12306.helpers.api import * from py12306.helpers.api import *
from py12306.helpers.func import * from py12306.helpers.func import *
from py12306.helpers.notification import Notification 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 from py12306.log.order_log import OrderLog
...@@ -51,6 +52,8 @@ class Order: ...@@ -51,6 +52,8 @@ class Order:
""" """
# Debug # Debug
if Config().IS_DEBUG: if Config().IS_DEBUG:
self.order_id = 'test'
self.order_did_success()
return random.randint(0, 10) > 7 return random.randint(0, 10) > 7
return self.normal_order() return self.normal_order()
...@@ -76,6 +79,9 @@ class Order: ...@@ -76,6 +79,9 @@ class Order:
def send_notification(self): def send_notification(self):
num = 0 # 通知次数 num = 0 # 通知次数
sustain_time = self.notification_sustain_time 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 后面直接查询有没有待支付的订单就可以 while sustain_time: # TODO 后面直接查询有没有待支付的订单就可以
num += 1 num += 1
if Config().NOTIFICATION_BY_VOICE_CODE: # 语音通知 if Config().NOTIFICATION_BY_VOICE_CODE: # 语音通知
...@@ -83,10 +89,12 @@ class Order: ...@@ -83,10 +89,12 @@ class Order:
Notification.voice_code(Config().NOTIFICATION_VOICE_CODE_PHONE, self.user_ins.get_name(), Notification.voice_code(Config().NOTIFICATION_VOICE_CODE_PHONE, self.user_ins.get_name(),
OrderLog.MESSAGE_ORDER_SUCCESS_NOTIFICATION_OF_VOICE_CODE_CONTENT.format( OrderLog.MESSAGE_ORDER_SUCCESS_NOTIFICATION_OF_VOICE_CODE_CONTENT.format(
self.query_ins.left_station, self.query_ins.arrive_station)) self.query_ins.left_station, self.query_ins.arrive_station))
else:
break
sustain_time -= self.notification_interval sustain_time -= self.notification_interval
sleep(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): def submit_order_request(self):
data = { data = {
...@@ -136,15 +144,23 @@ class Order: ...@@ -136,15 +144,23 @@ class Order:
response = self.session.post(API_CHECK_ORDER_INFO, data) response = self.session.post(API_CHECK_ORDER_INFO, data)
result = response.json() result = response.json()
if result.get('data.submitStatus'): # 成功 if result.get('data.submitStatus'): # 成功
# ifShowPassCode 需要验证码
OrderLog.add_quick_log(OrderLog.MESSAGE_CHECK_ORDER_INFO_SUCCESS).flush() OrderLog.add_quick_log(OrderLog.MESSAGE_CHECK_ORDER_INFO_SUCCESS).flush()
if result.get('data.ifShowPassCode') != 'N': if result.get('data.ifShowPassCode') != 'N':
self.is_need_auth_code = True self.is_need_auth_code = True
# if ( ticketInfoForPassengerForm.isAsync == ticket_submit_order.request_flag.isAsync & & ticketInfoForPassengerForm.queryLeftTicketRequestDTO.ypInfoDetail != "") { 不需要排队检测 js TODO
return True return True
else: else:
result_data = result.get('data', {}) error = CommonLog.MESSAGE_API_RESPONSE_CAN_NOT_BE_HANDLE
OrderLog.add_quick_log(OrderLog.MESSAGE_CHECK_ORDER_INFO_FAIL.format( if not result.get('data.isNoActive'):
result_data.get('errMsg', result.get('messages', '-')) error = result.get('data.errMsg')
)).flush() 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 return False
def get_queue_count(self): def get_queue_count(self):
...@@ -182,7 +198,7 @@ class Order: ...@@ -182,7 +198,7 @@ class Order:
} }
response = self.session.post(API_GET_QUEUE_COUNT, data) response = self.session.post(API_GET_QUEUE_COUNT, data)
result = response.json() result = response.json()
if result.get('data.countT') or result.get('data.ticket'): # 成功 if result.get('status', False): # 成功
""" """
"data": { "data": {
"count": "66", "count": "66",
...@@ -191,10 +207,22 @@ class Order: ...@@ -191,10 +207,22 @@ class Order:
"countT": "0", "countT": "0",
"op_1": "true" "op_1": "true"
} }
""" """
ticket = result.get('data.ticket').split(',') # 暂不清楚具体作用 # if result.get('isRelogin') == 'Y': # 重新登录 TODO
ticket_number = sum(map(int, ticket))
current_position = int(data.get('countT', 0)) 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.add_quick_log(
OrderLog.MESSAGE_GET_QUEUE_COUNT_SUCCESS.format(current_position, ticket_number)).flush() OrderLog.MESSAGE_GET_QUEUE_COUNT_SUCCESS.format(current_position, ticket_number)).flush()
return True return True
...@@ -275,7 +303,7 @@ class Order: ...@@ -275,7 +303,7 @@ class Order:
""" """
self.current_queue_wait = self.max_queue_wait self.current_queue_wait = self.max_queue_wait
while self.current_queue_wait: while self.current_queue_wait:
self.current_queue_wait -= 1 self.current_queue_wait -= self.wait_queue_interval
# TODO 取消超时订单,待优化 # TODO 取消超时订单,待优化
data = { # data = { #
'random': str(random.random())[2:], 'random': str(random.random())[2:],
...@@ -303,9 +331,10 @@ class Order: ...@@ -303,9 +331,10 @@ class Order:
order_id = result_data.get('orderId') order_id = result_data.get('orderId')
if order_id: # 成功 if order_id: # 成功
return order_id return order_id
elif result_data.get('waitTime') and result_data.get('waitTime') >= 0: elif result_data.get('waitTime') != -100:
OrderLog.add_quick_log( 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日您可继续使用订票功能。 elif result_data.get('msg'): # 失败 对不起,由于您取消次数过多,今日将不能继续受理您的订票请求。1月8日您可继续使用订票功能。
# TODO 需要增加判断 直接结束 # TODO 需要增加判断 直接结束
OrderLog.add_quick_log( OrderLog.add_quick_log(
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment