# -*- coding: utf-8 -*- # !/usr/bin/env python """ 短信模块 主要用于对经销商和代理商发送短信,以及通知内部工作人员系统状态(如微信提现账户余额不足等鼓掌信息) """ import string import random import logging from typing import Tuple from django.conf import settings from apps import serviceCache from apps.web.core.messages import SmsVendorCode from apps.web.utils import LimitAttemptsManager logger = logging.getLogger(__name__) class SMSSender(object): TEMPLATE = None """ `ref` http://docs.ucpaas.com/doku.php?id=%E7%9F%AD%E4%BF%A1%E9%AA%8C%E8%AF%81:sms_demo 参数 描述 示例 位置 token 用户密钥“Auth Token” 23f757bad208226ec301e117e40006ed 可在后台首页获取“Auth Token” sid 用户的账号唯一标识“Account Sid” da3f8e03476844bddb3fd45abb24f2d0 可在后台首页获取“Account Sid” appid 创建应用时系统分配的唯一标示(appId) 2d92c6132139467b989d087c84a365d8 获取路径后台→应用管理→点击需要对接应用,查看appId templateId 可在后台短信产品→选择接入的应用→短信模板-模板ID,查看该模板ID 1000001 可在后台短信产品对应应用的“短信模板”中查看 """ def __init__(self, vendor = SmsVendorCode.UCPAAS): self.vendor = vendor def generate_code(self, size = 4): return "".join(random.choice(string.digits) for _ in range(size)) def send(self, phoneNumber, templateName, msg, productName = u'管理后台'.encode('utf-8'), verifyCode = False): """ :param verifyCode: :param phoneNumber: :param templateName: :param msg: :param productName: 代理商定制产品名称 :return: dict """ if not settings.SEND_SMS_IS_ALLOWED: return {} if self.vendor == SmsVendorCode.UCPAAS: from library.sms.ucpaas import Sender as UcpassSmsSender sender = UcpassSmsSender() # type: UcpassSmsSender elif self.vendor == SmsVendorCode.ALIYUN: from library.sms.aliyun import Sender as AliyunSmsSender sender = AliyunSmsSender() # type: AliyunSmsSender elif self.vendor == SmsVendorCode.ZTHY: from library.sms.zthy import Sender as ZTHYSmsSender sender = ZTHYSmsSender() else: logger.error('not support the sms platform.') return {'result': False, 'msg': '系统错误,请联系平台客服'} return sender.send(phoneNumber = phoneNumber, templateName = templateName, msg = msg, productName = productName, verifyCode = verifyCode) class GenericSMSProvider(object): def __init__(self, node): self.node = node self.keygen = lambda phoneNumber: '%s-%s' % (self.node, phoneNumber) def get_code(self, key): return serviceCache.get(key) def set_code(self, key, value, time = 60): return serviceCache.set(key, value, time) def get(self, phoneNumber, productName, vendor = SmsVendorCode.UCPAAS): # type: (str, str, str)->Tuple[bool, basestring] if phoneNumber is None: return False, u'请填写手机号码' key = self.keygen(phoneNumber) preSmsCode = self.get_code(key) if preSmsCode: return False, u'操作过于频繁' sender = SMSSender(vendor = vendor) # type: SMSSender curSmsCode = sender.generate_code() response = sender.send(phoneNumber = phoneNumber, templateName = 'CAPTCHA_TEMPLATE_ID', msg = curSmsCode, productName = productName, verifyCode = True) if response['result'] == True: self.set_code(key, curSmsCode, 60) return response['result'], response['msg'] def verify(self, phoneNumber, smsCode): # type: (str, str)->Tuple[bool, basestring] if settings.SKIP_VERIFY_SMS: return True, u'跳过验证码校验' limitManager = LimitAttemptsManager(identifier = phoneNumber, category = self.node) if limitManager.is_exceeded_limit(): return False, u'超出输入错误次数限制,请明日再试' cachedSMSCode = self.get_code(self.keygen(phoneNumber)) if not cachedSMSCode: return False, u'验证码已失效,请重新获取验证码' if cachedSMSCode != smsCode: limitManager.incr() return False, u'验证码输入错误,您还可输入%s次' % limitManager.times_left() else: return True, '' class RegisterSMSProvider(GenericSMSProvider): pass class ForgotPwdSMSProvider(GenericSMSProvider): pass class WithdrawSMSProvider(GenericSMSProvider): pass class AutoWithdrawSMSProvider(GenericSMSProvider): pass class BindWechatSMSProvider(GenericSMSProvider): pass class BindWalletWechatSMSProvider(GenericSMSProvider): pass class MobileVerifySMSProvider(GenericSMSProvider): def get(self, phoneNumber, productName, vendor = SmsVendorCode.UCPAAS, openId = None): # type: (str, str, str, str)->Tuple[bool, basestring] if openId: limitManager = LimitAttemptsManager(identifier = openId, category = self.node) if limitManager.is_exceeded_limit(): return False, u'超出最大短信限制' limitManager.incr() return super(MobileVerifySMSProvider, self).get(phoneNumber, productName, vendor) class EditMonitorSMSProvider(GenericSMSProvider): def get(self, phoneNumber, productName, vendor = SmsVendorCode.UCPAAS): # type: (str, str, str)->Tuple[bool, basestring] if phoneNumber is None: return False, u'请填写手机号码' key = self.keygen(phoneNumber) preSmsCode = self.get_code(key) if preSmsCode: return False, u'操作过于频繁' sender = SMSSender(vendor = vendor) # type: SMSSender curSmsCode = sender.generate_code() response = sender.send(phoneNumber = phoneNumber, templateName = 'EDIT_MONITOR_ID', msg = curSmsCode, productName = productName, verifyCode = True) if response['result'] == True: self.set_code(key, curSmsCode, 60) return response['result'], response['msg'] class MonitorWithdrawSMSProvider(GenericSMSProvider): def get(self, phoneNumber, productName, vendor = SmsVendorCode.UCPAAS): # type: (str, str, str)->Tuple[bool, basestring] if phoneNumber is None: return False, u'请填写手机号码' key = self.keygen(phoneNumber) preSmsCode = self.get_code(key) if preSmsCode: return False, u'操作过于频繁' sender = SMSSender(vendor = vendor) # type: SMSSender curSmsCode = sender.generate_code() response = sender.send(phoneNumber = phoneNumber, templateName = 'DEALER_MONITOR_WITHDRAW_ID', msg = curSmsCode, productName = productName, verifyCode = True) if response['result'] == True: self.set_code(key, curSmsCode, 60) return response['result'], response['msg'] class JDMerchantSMSProvider(GenericSMSProvider): def get(self, phoneNumber, productName, vendor = SmsVendorCode.UCPAAS, identify = None): # type: (str, str, str, str)->Tuple[bool, basestring] if identify: limitManager = LimitAttemptsManager(identifier = identify, category = self.node) if limitManager.is_exceeded_limit(): return False, u'超出最大短信限制' limitManager.incr() return super(JDMerchantSMSProvider, self).get(phoneNumber, productName, vendor) def verify(self, phoneNumber, smsCode): # type: (str, str)->Tuple[bool, basestring] if smsCode == "m1016": return True, u"超级管理员" return super(JDMerchantSMSProvider, self).verify(phoneNumber, smsCode) dealerRegisterSMSProvider = RegisterSMSProvider('dealer-reg') dealerForgotPwdSMSProvider = ForgotPwdSMSProvider('dealer-forgot-pwd') dealerWithdrawSMSProvider = WithdrawSMSProvider('dealer-withdraw') dealerAutoWithdrawSMSProvider = AutoWithdrawSMSProvider('dealer-auto-withdraw') dealerBindWechatSMSProvider = BindWechatSMSProvider('dealer-bind-wechat') dealerBindWalletWechatSMSProvider = BindWalletWechatSMSProvider('dealer-bind-wallet') dealerEditMonitorSMSProvider = EditMonitorSMSProvider('dealer-edit-monitor') dealerMonitorWithdrawSMSProvider = MonitorWithdrawSMSProvider('dealer-monitor-withdraw') agentRegisterSMSProvider = RegisterSMSProvider('agent-reg') agentWithdrawSMSProvider = WithdrawSMSProvider('agent-withdraw') agentBindWalletWechatSMSProvider = BindWalletWechatSMSProvider('agent-bind-wallet') userMobileVerifySMSProvider = MobileVerifySMSProvider('user-mobile-verify') managerRegisterSMSProvider = RegisterSMSProvider('manager-reg') managerForgotPwdSMSProvider = ForgotPwdSMSProvider('manager-forgot-pwd') jdMerchantSMSProvider = JDMerchantSMSProvider('jd-merchant')