sms.py 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. # -*- coding: utf-8 -*-
  2. # !/usr/bin/env python
  3. """
  4. 短信模块
  5. 主要用于对经销商和代理商发送短信,以及通知内部工作人员系统状态(如微信提现账户余额不足等鼓掌信息)
  6. """
  7. import string
  8. import random
  9. import logging
  10. from typing import Tuple
  11. from django.conf import settings
  12. from apps import serviceCache
  13. from apps.web.core.messages import SmsVendorCode
  14. from apps.web.utils import LimitAttemptsManager
  15. logger = logging.getLogger(__name__)
  16. class SMSSender(object):
  17. TEMPLATE = None
  18. """
  19. `ref` http://docs.ucpaas.com/doku.php?id=%E7%9F%AD%E4%BF%A1%E9%AA%8C%E8%AF%81:sms_demo
  20. 参数 描述 示例 位置
  21. token 用户密钥“Auth Token” 23f757bad208226ec301e117e40006ed 可在后台首页获取“Auth Token”
  22. sid 用户的账号唯一标识“Account Sid” da3f8e03476844bddb3fd45abb24f2d0 可在后台首页获取“Account Sid”
  23. appid 创建应用时系统分配的唯一标示(appId) 2d92c6132139467b989d087c84a365d8 获取路径后台→应用管理→点击需要对接应用,查看appId
  24. templateId 可在后台短信产品→选择接入的应用→短信模板-模板ID,查看该模板ID 1000001 可在后台短信产品对应应用的“短信模板”中查看
  25. """
  26. def __init__(self, vendor = SmsVendorCode.UCPAAS):
  27. self.vendor = vendor
  28. def generate_code(self, size = 4):
  29. return "".join(random.choice(string.digits) for _ in range(size))
  30. def send(self, phoneNumber, templateName, msg, productName = u'管理后台'.encode('utf-8'), verifyCode = False):
  31. """
  32. :param verifyCode:
  33. :param phoneNumber:
  34. :param templateName:
  35. :param msg:
  36. :param productName: 代理商定制产品名称
  37. :return: dict
  38. """
  39. if not settings.SEND_SMS_IS_ALLOWED:
  40. return {}
  41. if self.vendor == SmsVendorCode.UCPAAS:
  42. from library.sms.ucpaas import Sender as UcpassSmsSender
  43. sender = UcpassSmsSender() # type: UcpassSmsSender
  44. elif self.vendor == SmsVendorCode.ALIYUN:
  45. from library.sms.aliyun import Sender as AliyunSmsSender
  46. sender = AliyunSmsSender() # type: AliyunSmsSender
  47. elif self.vendor == SmsVendorCode.ZTHY:
  48. from library.sms.zthy import Sender as ZTHYSmsSender
  49. sender = ZTHYSmsSender()
  50. else:
  51. logger.error('not support the sms platform.')
  52. return {'result': False, 'msg': '系统错误,请联系平台客服'}
  53. return sender.send(phoneNumber = phoneNumber,
  54. templateName = templateName,
  55. msg = msg,
  56. productName = productName,
  57. verifyCode = verifyCode)
  58. class GenericSMSProvider(object):
  59. def __init__(self, node):
  60. self.node = node
  61. self.keygen = lambda phoneNumber: '%s-%s' % (self.node, phoneNumber)
  62. def get_code(self, key):
  63. return serviceCache.get(key)
  64. def set_code(self, key, value, time = 60):
  65. return serviceCache.set(key, value, time)
  66. def get(self, phoneNumber, productName, vendor = SmsVendorCode.UCPAAS):
  67. # type: (str, str, str)->Tuple[bool, basestring]
  68. if phoneNumber is None:
  69. return False, u'请填写手机号码'
  70. key = self.keygen(phoneNumber)
  71. preSmsCode = self.get_code(key)
  72. if preSmsCode:
  73. return False, u'操作过于频繁'
  74. sender = SMSSender(vendor = vendor) # type: SMSSender
  75. curSmsCode = sender.generate_code()
  76. response = sender.send(phoneNumber = phoneNumber,
  77. templateName = 'CAPTCHA_TEMPLATE_ID',
  78. msg = curSmsCode,
  79. productName = productName,
  80. verifyCode = True)
  81. if response['result'] == True:
  82. self.set_code(key, curSmsCode, 60)
  83. return response['result'], response['msg']
  84. def verify(self, phoneNumber, smsCode):
  85. # type: (str, str)->Tuple[bool, basestring]
  86. if settings.SKIP_VERIFY_SMS:
  87. return True, u'跳过验证码校验'
  88. limitManager = LimitAttemptsManager(identifier = phoneNumber, category = self.node)
  89. if limitManager.is_exceeded_limit():
  90. return False, u'超出输入错误次数限制,请明日再试'
  91. cachedSMSCode = self.get_code(self.keygen(phoneNumber))
  92. if not cachedSMSCode:
  93. return False, u'验证码已失效,请重新获取验证码'
  94. if cachedSMSCode != smsCode:
  95. limitManager.incr()
  96. return False, u'验证码输入错误,您还可输入%s次' % limitManager.times_left()
  97. else:
  98. return True, ''
  99. class RegisterSMSProvider(GenericSMSProvider):
  100. pass
  101. class ForgotPwdSMSProvider(GenericSMSProvider):
  102. pass
  103. class WithdrawSMSProvider(GenericSMSProvider):
  104. pass
  105. class AutoWithdrawSMSProvider(GenericSMSProvider):
  106. pass
  107. class BindWechatSMSProvider(GenericSMSProvider):
  108. pass
  109. class BindWalletWechatSMSProvider(GenericSMSProvider):
  110. pass
  111. class MobileVerifySMSProvider(GenericSMSProvider):
  112. def get(self, phoneNumber, productName, vendor = SmsVendorCode.UCPAAS,
  113. openId = None): # type: (str, str, str, str)->Tuple[bool, basestring]
  114. if openId:
  115. limitManager = LimitAttemptsManager(identifier = openId, category = self.node)
  116. if limitManager.is_exceeded_limit():
  117. return False, u'超出最大短信限制'
  118. limitManager.incr()
  119. return super(MobileVerifySMSProvider, self).get(phoneNumber, productName, vendor)
  120. class EditMonitorSMSProvider(GenericSMSProvider):
  121. def get(self, phoneNumber, productName, vendor = SmsVendorCode.UCPAAS):
  122. # type: (str, str, str)->Tuple[bool, basestring]
  123. if phoneNumber is None:
  124. return False, u'请填写手机号码'
  125. key = self.keygen(phoneNumber)
  126. preSmsCode = self.get_code(key)
  127. if preSmsCode:
  128. return False, u'操作过于频繁'
  129. sender = SMSSender(vendor = vendor) # type: SMSSender
  130. curSmsCode = sender.generate_code()
  131. response = sender.send(phoneNumber = phoneNumber,
  132. templateName = 'EDIT_MONITOR_ID',
  133. msg = curSmsCode,
  134. productName = productName,
  135. verifyCode = True)
  136. if response['result'] == True:
  137. self.set_code(key, curSmsCode, 60)
  138. return response['result'], response['msg']
  139. class MonitorWithdrawSMSProvider(GenericSMSProvider):
  140. def get(self, phoneNumber, productName, vendor = SmsVendorCode.UCPAAS):
  141. # type: (str, str, str)->Tuple[bool, basestring]
  142. if phoneNumber is None:
  143. return False, u'请填写手机号码'
  144. key = self.keygen(phoneNumber)
  145. preSmsCode = self.get_code(key)
  146. if preSmsCode:
  147. return False, u'操作过于频繁'
  148. sender = SMSSender(vendor = vendor) # type: SMSSender
  149. curSmsCode = sender.generate_code()
  150. response = sender.send(phoneNumber = phoneNumber,
  151. templateName = 'DEALER_MONITOR_WITHDRAW_ID',
  152. msg = curSmsCode,
  153. productName = productName,
  154. verifyCode = True)
  155. if response['result'] == True:
  156. self.set_code(key, curSmsCode, 60)
  157. return response['result'], response['msg']
  158. dealerRegisterSMSProvider = RegisterSMSProvider('dealer-reg')
  159. dealerForgotPwdSMSProvider = ForgotPwdSMSProvider('dealer-forgot-pwd')
  160. dealerWithdrawSMSProvider = WithdrawSMSProvider('dealer-withdraw')
  161. dealerAutoWithdrawSMSProvider = AutoWithdrawSMSProvider('dealer-auto-withdraw')
  162. dealerBindWechatSMSProvider = BindWechatSMSProvider('dealer-bind-wechat')
  163. dealerBindWalletWechatSMSProvider = BindWalletWechatSMSProvider('dealer-bind-wallet')
  164. dealerEditMonitorSMSProvider = EditMonitorSMSProvider('dealer-edit-monitor')
  165. dealerMonitorWithdrawSMSProvider = MonitorWithdrawSMSProvider('dealer-monitor-withdraw')
  166. agentRegisterSMSProvider = RegisterSMSProvider('agent-reg')
  167. agentWithdrawSMSProvider = WithdrawSMSProvider('agent-withdraw')
  168. agentBindWalletWechatSMSProvider = BindWalletWechatSMSProvider('agent-bind-wallet')
  169. userMobileVerifySMSProvider = MobileVerifySMSProvider('user-mobile-verify')
  170. managerRegisterSMSProvider = RegisterSMSProvider('manager-reg')
  171. managerForgotPwdSMSProvider = ForgotPwdSMSProvider('manager-forgot-pwd')