tornado 短信验证码
Python3接入云通讯短信
API接入:三个.py文件放在一个文件夹下,启动一下SendTemplateSMS.py文件就行。

SendTemplateSMS.py
1 from CCPRestSDK import REST 2 3 # import ConfigParser 4 5 # 主帐号 6 accountSid = '***' 7 8 # 主帐号Token 9 accountToken = '***' 10 11 # 应用Id 12 appId = '***' 13 14 # 请求地址,格式如下,不需要写http:// 15 serverIP = 'app.cloopen.com' 16 17 # 请求端口 18 serverPort = '8883' 19 20 # REST版本号 21 softVersion = '2013-12-26' 22 23 24 # 发送模板短信 25 # @param to 手机号码 26 # @param datas 内容数据 格式为数组 例如:{'12','34'},如不需替换请填 '' 27 # @param $tempId 模板Id 28 29 class CCP(object): 30 def __init__(self): 31 self.rest = REST(serverIP, serverPort, softVersion) 32 self.rest.setAccount(accountSid, accountToken) 33 self.rest.setAppId(appId) 34 35 @staticmethod 36 def instance(): 37 if not hasattr(CCP, "_instance"): 38 CCP._instance = CCP() 39 return CCP._instance 40 41 def sendTemplateSMS(self, to, datas, tempId): 42 try: 43 result = self.rest.sendTemplateSMS(to, datas, tempId) 44 except Exception as e: 45 # logging.error(e) 46 raise e 47 # print result 48 # for k, v in result.iteritems(): 49 # if k == 'templateSMS': 50 # for k, s in v.iteritems(): 51 # print '%s:%s' % (k, s) 52 # else: 53 # print '%s:%s' % (k, v) 54 success = "<statusCode>000000</statusCode>" 55 if success in result: 56 return True 57 else: 58 return False 59 60 ccp = CCP.instance() 61 62 if __name__ == "__main__": 63 ccp = CCP.instance() 64 # 1代表模板ID,下载SDK的官网api文档有说明 65 ret = ccp.sendTemplateSMS("***", ["aaaa", "1"], 1) 66 67 print(ret)
CCPRestSDK.py
1 # -*- coding: UTF-8 -*- 2 # Copyright (c) 2014 The CCP project authors. All Rights Reserved. 3 # 4 # Use of this source code is governed by a Beijing Speedtong Information Technology Co.,Ltd license 5 # that can be found in the LICENSE file in the root of the web site. 6 # 7 # http://www.yuntongxun.com 8 # 9 # An additional intellectual property rights grant can be found 10 # in the file PATENTS. All contributing project authors may 11 # be found in the AUTHORS file in the root of the source tree. 12 13 from hashlib import md5 14 import base64 15 import datetime 16 import urllib.request 17 import requests 18 import json 19 from xmltojson import xmltojson 20 from xml.dom import minidom 21 22 23 class REST: 24 AccountSid = '' 25 AccountToken = '' 26 AppId = '' 27 SubAccountSid = '' 28 SubAccountToken = '' 29 ServerIP = '' 30 ServerPort = '' 31 SoftVersion = '' 32 Iflog = True # 是否打印日志 33 Batch = '' # 时间戳 34 BodyType = 'xml' # 包体格式,可填值:json 、xml 35 36 # 初始化 37 # @param serverIP 必选参数 服务器地址 38 # @param serverPort 必选参数 服务器端口 39 # @param softVersion 必选参数 REST版本号 40 def __init__(self, ServerIP, ServerPort, SoftVersion): 41 42 self.ServerIP = ServerIP 43 self.ServerPort = ServerPort 44 self.SoftVersion = SoftVersion 45 46 # 设置主帐号 47 # @param AccountSid 必选参数 主帐号 48 # @param AccountToken 必选参数 主帐号Token 49 50 def setAccount(self, AccountSid, AccountToken): 51 self.AccountSid = AccountSid 52 self.AccountToken = AccountToken 53 54 # 设置子帐号 55 56 # 57 # @param SubAccountSid 必选参数 子帐号 58 # @param SubAccountToken 必选参数 子帐号Token 59 60 def setSubAccount(self, SubAccountSid, SubAccountToken): 61 self.SubAccountSid = SubAccountSid 62 self.SubAccountToken = SubAccountToken 63 64 # 设置应用ID 65 66 # 67 # @param AppId 必选参数 应用ID 68 69 def setAppId(self, AppId): 70 self.AppId = AppId 71 72 def log(self, url, body, data): 73 print('这是请求的URL:') 74 print(url) 75 print('这是请求包体:') 76 print(body) 77 print('这是响应包体:') 78 print(data) 79 print('********************************') 80 81 # 创建子账号 82 # @param friendlyName 必选参数 子帐号名称 83 def CreateSubAccount(self, friendlyName): 84 85 self.accAuth() 86 nowdate = datetime.datetime.now() 87 self.Batch = nowdate.strftime("%Y%m%d%H%M%S") 88 # 生成sig 89 signature = self.AccountSid + self.AccountToken + self.Batch 90 sig = md5(signature.encode()).hexdigest().upper() 91 # 拼接URL 92 url = "https://" + self.ServerIP + ":" + self.ServerPort + "/" + self.SoftVersion + "/Accounts/" + self.AccountSid + "/SubAccounts?sig=" + sig 93 # 生成auth 94 src = self.AccountSid + ":" + self.Batch 95 auth = base64.encodestring(src.encode()).strip().decode() 96 req = urllib.request.Request(url) 97 self.setHttpHeader(req) 98 req.add_header("Authorization", auth) 99 # xml格式 100 body = '''<?xml version="1.0" encoding="utf-8"?><SubAccount><appId>%s</appId>\ 101 <friendlyName>%s</friendlyName>\ 102 </SubAccount>\ 103 ''' % (self.AppId, friendlyName) 104 105 if self.BodyType == 'json': 106 # json格式 107 body = '''{"friendlyName": "%s", "appId": "%s"}''' % (friendlyName, self.AppId) 108 data = '' 109 req.data = body 110 try: 111 # res = urllib.request.urlopen(req) 112 # data = res.read() 113 # res.close() 114 res = requests.post(url, headers=req.headers, data=req.data) 115 # print(res.status_code, res.content, res.text, ) 116 data = res.text 117 # if self.BodyType == 'json': 118 # # json格式 119 # locations = json.loads(data) 120 # else: 121 # # xml格式 122 # xtj = xmltojson() 123 # locations = xtj.main(data) 124 if self.Iflog: 125 self.log(url, body, data) 126 return data 127 except Exception as error: 128 if self.Iflog: 129 self.log(url, body, data) 130 return {'172001': '网络错误'} 131 132 # 获取子帐号 133 # @param startNo 可选参数 开始的序号,默认从0开始 134 # @param offset 可选参数 一次查询的最大条数,最小是1条,最大是100条 135 def getSubAccounts(self, startNo, offset): 136 137 self.accAuth() 138 nowdate = datetime.datetime.now() 139 self.Batch = nowdate.strftime("%Y%m%d%H%M%S") 140 # 生成sig 141 signature = self.AccountSid + self.AccountToken + self.Batch 142 sig = md5(signature.encode()).hexdigest().upper() 143 # 拼接URL 144 url = "https://" + self.ServerIP + ":" + self.ServerPort + "/" + self.SoftVersion + "/Accounts/" + self.AccountSid + "/GetSubAccounts?sig=" + sig 145 # 生成auth 146 src = self.AccountSid + ":" + self.Batch 147 auth = base64.encodestring(src.encode()).strip().decode() 148 req = urllib.request.Request(url) 149 self.setHttpHeader(req) 150 req.add_header("Authorization", auth) 151 # xml格式 152 body = '''<?xml version="1.0" encoding="utf-8"?><SubAccount><appId>%s</appId>\ 153 <startNo>%s</startNo><offset>%s</offset>\ 154 </SubAccount>\ 155 ''' % (self.AppId, startNo, offset) 156 157 if self.BodyType == 'json': 158 # json格式 159 body = '''{"appId": "%s", "startNo": "%s", "offset": "%s"}''' % (self.AppId, startNo, offset) 160 data = '' 161 req.data = body 162 try: 163 res = requests.get(url, headers=req.headers) 164 # print(res.status_code, res.content, res.text, ) 165 data = res.text 166 167 # if self.BodyType == 'json': 168 # # json格式 169 # locations = json.loads(data) 170 # else: 171 # # xml格式 172 # xtj = xmltojson() 173 # locations = xtj.main(data) 174 if self.Iflog: 175 self.log(url, body, data) 176 return data 177 except Exception as error: 178 if self.Iflog: 179 self.log(url, body, data) 180 return {'172001': '网络错误'} 181 182 # 子帐号信息查询 183 # @param friendlyName 必选参数 子帐号名称 184 185 def querySubAccount(self, friendlyName): 186 187 self.accAuth() 188 nowdate = datetime.datetime.now() 189 self.Batch = nowdate.strftime("%Y%m%d%H%M%S") 190 # 生成sig 191 signature = self.AccountSid + self.AccountToken + self.Batch 192 sig = md5(signature.encode()).hexdigest().upper() 193 # 拼接URL 194 url = "https://" + self.ServerIP + ":" + self.ServerPort + "/" + self.SoftVersion + "/Accounts/" + self.AccountSid + "/QuerySubAccountByName?sig=" + sig 195 # 生成auth 196 src = self.AccountSid + ":" + self.Batch 197 auth = base64.encodestring(src.encode()).strip().decode() 198 req = urllib.request.Request(url) 199 self.setHttpHeader(req) 200 201 req.add_header("Authorization", auth) 202 203 # 创建包体 204 body = '''<?xml version="1.0" encoding="utf-8"?><SubAccount><appId>%s</appId>\ 205 <friendlyName>%s</friendlyName>\ 206 </SubAccount>\ 207 ''' % (self.AppId, friendlyName) 208 if self.BodyType == 'json': 209 body = '''{"friendlyName": "%s", "appId": "%s"}''' % (friendlyName, self.AppId) 210 data = '' 211 req.data = body 212 try: 213 # res = urllib.request.urlopen(req) 214 # data = res.read() 215 # res.close() 216 res = requests.get(url, headers=req.headers, data=req.data) 217 # print(res.status_code, res.content, res.text, ) 218 data = res.text 219 220 # if self.BodyType == 'json': 221 # # json格式 222 # locations = json.loads(data) 223 # else: 224 # # xml格式 225 # xtj = xmltojson() 226 # locations = xtj.main(data) 227 if self.Iflog: 228 self.log(url, body, data) 229 return data 230 except Exception as error: 231 if self.Iflog: 232 self.log(url, body, data) 233 return {'172001': '网络错误'} 234 235 # 发送模板短信 236 # @param to 必选参数 短信接收彿手机号码集合,用英文逗号分开 237 # @param datas 可选参数 内容数据 238 # @param tempId 必选参数 模板Id 239 def sendTemplateSMS(self, to, datas, tempId): 240 241 self.accAuth() 242 nowdate = datetime.datetime.now() 243 self.Batch = nowdate.strftime("%Y%m%d%H%M%S") 244 # 生成sig 245 signature = self.AccountSid + self.AccountToken + self.Batch 246 sig = md5(signature.encode()).hexdigest().upper() 247 # 拼接URL 248 url = "https://" + self.ServerIP + ":" + self.ServerPort + "/" + self.SoftVersion + "/Accounts/" + self.AccountSid + "/SMS/TemplateSMS?sig=" + sig 249 # 生成auth 250 src = self.AccountSid + ":" + self.Batch 251 auth = base64.encodestring(src.encode()).strip().decode() 252 req = urllib.request.Request(url) 253 self.setHttpHeader(req) 254 req.add_header("Authorization", auth) 255 # print(req.headers) #组装请求头 256 # 创建包体 257 b = '' 258 for a in datas: 259 b += '<data>%s</data>' % (a) 260 261 body = '<?xml version="1.0" encoding="utf-8"?><SubAccount><datas>' + b + '</datas><to>%s</to><templateId>%s</templateId><appId>%s</appId>\ 262 </SubAccount>\ 263 ' % (to, tempId, self.AppId) 264 if self.BodyType == 'json': 265 # if this model is Json ..then do next code 266 b = '[' 267 for a in datas: 268 b += '"%s",' % (a) 269 b += ']' 270 body = '''{"to": "%s", "datas": %s, "templateId": "%s", "appId": "%s"}''' % (to, b, tempId, self.AppId) 271 req.data = body 272 # print(req.data) #组装请求体 273 # 确认请求体和请求头 274 req_headers = req.headers 275 req_data = req.data 276 # print(req_headers,req_data) 277 # print("_______"*20) 278 data = '' 279 try: 280 # res = urllib.request.urlopen(req) 281 res = requests.post(url, headers=req_headers, data=req_data) 282 # print(res.status_code,res.content,res.text,) 283 data = res.text 284 # data = res.read() 285 # res.close() 286 # if self.BodyType == 'json': 287 # # json格式 288 # locations = json.loads(data) 289 # else: 290 # # xml格式 291 # xtj = xmltojson() 292 # locations = xtj.main(data) 293 if self.Iflog: 294 self.log(url, body, data) 295 return data 296 except Exception as error: 297 if self.Iflog: 298 self.log(url, body, data) 299 return {'172001': '网络错误'} 300 301 # 外呼通知 302 # @param to 必选参数 被叫号码 303 # @param mediaName 可选参数 语音文件名称,格式 wav。与mediaTxt不能同时为空。当不为空时mediaTxt属性失效。 304 # @param mediaTxt 可选参数 文本内容 305 # @param displayNum 可选参数 显示的主叫号码 306 # @param playTimes 可选参数 循环播放次数,1-3次,默认播放1次。 307 # @param respUrl 可选参数 外呼通知状态通知回调地址,云通讯平台将向该Url地址发送呼叫结果通知。 308 # @param userData 可选参数 用户私有数据 309 # @param maxCallTime 可选参数 最大通话时长 310 # @param speed 可选参数 发音速度 311 # @param volume 可选参数 音量 312 # @param pitch 可选参数 音调 313 # @param bgsound 可选参数 背景音编号 314 315 def landingCall(self, to, mediaName, mediaTxt, displayNum, playTimes, respUrl, userData, maxCallTime, speed, volume, 316 pitch, bgsound): 317 318 self.accAuth() 319 nowdate = datetime.datetime.now() 320 self.Batch = nowdate.strftime("%Y%m%d%H%M%S") 321 # 生成sig 322 signature = self.AccountSid + self.AccountToken + self.Batch 323 sig = md5(signature.encode()).hexdigest().upper() 324 # 拼接URL 325 url = "https://" + self.ServerIP + ":" + self.ServerPort + "/" + self.SoftVersion + "/Accounts/" + self.AccountSid + "/Calls/LandingCalls?sig=" + sig 326 # 生成auth 327 src = self.AccountSid + ":" + self.Batch 328 auth = base64.encodestring(src.encode()).strip().decode() 329 req = urllib.request.Request(url) 330 self.setHttpHeader(req) 331 req.add_header("Authorization", auth) 332 333 # 创建包体 334 body = '''<?xml version="1.0" encoding="utf-8"?><LandingCall>\ 335 <to>%s</to><mediaName>%s</mediaName><mediaTxt>%s</mediaTxt><appId>%s</appId><displayNum>%s</displayNum>\ 336 <playTimes>%s</playTimes><respUrl>%s</respUrl><userData>%s</userData><maxCallTime>%s</maxCallTime><speed>%s</speed> 337 <volume>%s</volume><pitch>%s</pitch><bgsound>%s</bgsound></LandingCall>\ 338 ''' % ( 339 to, mediaName, mediaTxt, self.AppId, displayNum, playTimes, respUrl, userData, maxCallTime, speed, volume, 340 pitch, bgsound) 341 if self.BodyType == 'json': 342 body = '''{"to": "%s", "mediaName": "%s","mediaTxt": "%s","appId": "%s","displayNum": "%s","playTimes": "%s","respUrl": "%s","userData": "%s","maxCallTime": "%s","speed": "%s","volume": "%s","pitch": "%s","bgsound": "%s"}''' % ( 343 to, mediaName, mediaTxt, self.AppId, displayNum, playTimes, respUrl, userData, maxCallTime, speed, 344 volume, 345 pitch, bgsound) 346 req.data = body 347 data = '' 348 try: 349 # res = urllib.request.urlopen(req) 350 # data = res.read() 351 # res.close() 352 res = requests.post(url, headers=req.headers, data=req.data) 353 # print(res.status_code, res.content, res.text, ) 354 data = res.text 355 356 # if self.BodyType == 'json': 357 # # json格式 358 # locations = json.loads(data) 359 # else: 360 # # xml格式 361 # xtj = xmltojson() 362 # locations = xtj.main(data) 363 if self.Iflog: 364 self.log(url, body, data) 365 return data 366 except Exception as error: 367 if self.Iflog: 368 self.log(url, body, data) 369 return {'172001': '网络错误'} 370 371 # 语音验证码 372 # @param verifyCode 必选参数 验证码内容,为数字和英文字母,不区分大小写,长度4-8位 373 # @param playTimes 可选参数 播放次数,1-3次 374 # @param to 必选参数 接收号码 375 # @param displayNum 可选参数 显示的主叫号码 376 # @param respUrl 可选参数 语音验证码状态通知回调地址,云通讯平台将向该Url地址发送呼叫结果通知 377 # @param lang 可选参数 语言类型 378 # @param userData 可选参数 第三方私有数据 379 380 def voiceVerify(self, verifyCode, playTimes, to, displayNum, respUrl, lang, userData): 381 382 self.accAuth() 383 nowdate = datetime.datetime.now() 384 self.Batch = nowdate.strftime("%Y%m%d%H%M%S") 385 # 生成sig 386 signature = self.AccountSid + self.AccountToken + self.Batch 387 sig = md5(signature.encode()).hexdigest().upper() 388 # 拼接URL 389 url = "https://" + self.ServerIP + ":" + self.ServerPort + "/" + self.SoftVersion + "/Accounts/" + self.AccountSid + "/Calls/VoiceVerify?sig=" + sig 390 # 生成auth 391 src = self.AccountSid + ":" + self.Batch 392 auth = base64.encodestring(src.encode()).strip().decode() 393 req = urllib.request.Request(url) 394 self.setHttpHeader(req) 395 396 req.add_header("Authorization", auth) 397 398 # 创建包体 399 body = '''<?xml version="1.0" encoding="utf-8"?><VoiceVerify>\ 400 <appId>%s</appId><verifyCode>%s</verifyCode><playTimes>%s</playTimes><to>%s</to><respUrl>%s</respUrl>\ 401 <displayNum>%s</displayNum><lang>%s</lang><userData>%s</userData></VoiceVerify>\ 402 ''' % (self.AppId, verifyCode, playTimes, to, respUrl, displayNum, lang, userData) 403 if self.BodyType == 'json': 404 # if this model is Json ..then do next code 405 body = '''{"appId": "%s", "verifyCode": "%s","playTimes": "%s","to": "%s","respUrl": "%s","displayNum": "%s","lang": "%s","userData": "%s"}''' % ( 406 self.AppId, verifyCode, playTimes, to, respUrl, displayNum, lang, userData) 407 req.data = body 408 data = '' 409 try: 410 # res = urllib.request.urlopen(req) 411 # data = res.read() 412 # res.close() 413 res = requests.post(url, headers=req.headers, data=req.data) 414 # print(res.status_code, res.content, res.text, ) 415 data = res.text 416 # if self.BodyType == 'json': 417 # # json格式 418 # locations = json.loads(data) 419 # else: 420 # # xml格式 421 # xtj = xmltojson() 422 # locations = xtj.main(data) 423 if self.Iflog: 424 self.log(url, body, data) 425 return data 426 except Exception as error: 427 if self.Iflog: 428 self.log(url, body, data) 429 return {'172001': '网络错误'} 430 431 # IVR外呼 432 # @param number 必选参数 待呼叫号码,为Dial节点的属性 433 # @param userdata 可选参数 用户数据,在<startservice>通知中返回,只允许填写数字字符,为Dial节点的属性 434 # @param record 可选参数 是否录音,可填项为true和false,默认值为false不录音,为Dial节点的属性 435 436 def ivrDial(self, number, userdata, record): 437 438 self.accAuth() 439 nowdate = datetime.datetime.now() 440 self.Batch = nowdate.strftime("%Y%m%d%H%M%S") 441 # 生成sig 442 signature = self.AccountSid + self.AccountToken + self.Batch 443 sig = md5(signature.encode()).hexdigest().upper() 444 # 拼接URL 445 url = "https://" + self.ServerIP + ":" + self.ServerPort + "/" + self.SoftVersion + "/Accounts/" + self.AccountSid + "/ivr/dial?sig=" + sig 446 # 生成auth 447 src = self.AccountSid + ":" + self.Batch 448 auth = base64.encodestring(src.encode()).strip().decode() 449 req = urllib.request.Request(url) 450 req.add_header("Accept", "application/xml") 451 req.add_header("Content-Type", "application/xml;charset=utf-8") 452 req.add_header("Authorization", auth) 453 454 # 创建包体 455 body = '''<?xml version="1.0" encoding="utf-8"?> 456 <Request> 457 <Appid>%s</Appid> 458 <Dial number="%s" userdata="%s" record="%s"></Dial> 459 </Request> 460 ''' % (self.AppId, number, userdata, record) 461 req.data = body 462 data = '' 463 try: 464 # res = urllib.request.urlopen(req) 465 # data = res.read() 466 # res.close() 467 res = requests.post(url, headers=req.headers, data=req.data) 468 # print(res.status_code, res.content, res.text, ) 469 data = res.text 470 # xtj = xmltojson() 471 # locations = xtj.main(data) 472 if self.Iflog: 473 self.log(url, body, data) 474 return data 475 except Exception as error: 476 if self.Iflog: 477 self.log(url, body, data) 478 return {'172001': '网络错误'} 479 480 # 话单下载 481 # @param date 必选参数 day 代表前一天的数据(从00:00 – 23:59),目前只支持按天查询 482 # @param keywords 可选参数 客户的查询条件,由客户自行定义并提供给云通讯平台。默认不填忽略此参数 483 def billRecords(self, date, keywords): 484 485 self.accAuth() 486 nowdate = datetime.datetime.now() 487 self.Batch = nowdate.strftime("%Y%m%d%H%M%S") 488 # 生成sig 489 signature = self.AccountSid + self.AccountToken + self.Batch 490 sig = md5(signature.encode()).hexdigest().upper() 491 # 拼接URL 492 url = "https://" + self.ServerIP + ":" + self.ServerPort + "/" + self.SoftVersion + "/Accounts/" + self.AccountSid + "/BillRecords?sig=" + sig 493 # 生成auth 494 src = self.AccountSid + ":" + self.Batch 495 auth = base64.encodestring(src.encode()).strip().decode() 496 req = urllib.request.Request(url) 497 self.setHttpHeader(req) 498 req.add_header("Authorization", auth) 499 500 # 创建包体 501 body = '''<?xml version="1.0" encoding="utf-8"?><BillRecords>\ 502 <appId>%s</appId><date>%s</date><keywords>%s</keywords>\ 503 </BillRecords>\ 504 ''' % (self.AppId, date, keywords) 505 if self.BodyType == 'json': 506 # if this model is Json ..then do next code 507 body = '''{"appId": "%s", "date": "%s","keywords": "%s"}''' % (self.AppId, date, keywords) 508 req.data = body 509 data = '' 510 try: 511 # res = urllib.request.urlopen(req) 512 # data = res.read() 513 # res.close() 514 res = requests.post(url, headers=req.headers, data=req.data) 515 # print(res.status_code, res.content, res.text, ) 516 data = res.text 517 518 # if self.BodyType == 'json': 519 # # json格式 520 # locations = json.loads(data) 521 # else: 522 # # xml格式 523 # xtj = xmltojson() 524 # locations = xtj.main(data) 525 if self.Iflog: 526 self.log(url, body, data) 527 return data 528 except Exception as error: 529 if self.Iflog: 530 self.log(url, body, data) 531 return {'172001': '网络错误'} 532 533 # 主帐号信息查询 534 535 def queryAccountInfo(self): 536 537 self.accAuth() 538 nowdate = datetime.datetime.now() 539 self.Batch = nowdate.strftime("%Y%m%d%H%M%S") 540 # 生成sig 541 signature = self.AccountSid + self.AccountToken + self.Batch 542 sig = md5(signature.encode()).hexdigest().upper() 543 # 拼接URL 544 url = "https://" + self.ServerIP + ":" + self.ServerPort + "/" + self.SoftVersion + "/Accounts/" + self.AccountSid + "/AccountInfo?sig=" + sig 545 # 生成auth 546 src = self.AccountSid + ":" + self.Batch 547 auth = base64.encodestring(src.encode()).strip().decode() 548 req = urllib.request.Request(url) 549 self.setHttpHeader(req) 550 body = '' 551 req.add_header("Authorization", auth) 552 data = '' 553 try: 554 # res = urllib.request.urlopen(req) 555 # data = res.read() 556 # res.close() 557 res = requests.post(url, headers=req.headers, data=req.data) 558 # print(res.status_code, res.content, res.text, ) 559 data = res.text 560 # if self.BodyType == 'json': 561 # # json格式 562 # locations = json.loads(data) 563 # else: 564 # # xml格式 565 # xtj = xmltojson() 566 # locations = xtj.main(data) 567 if self.Iflog: 568 self.log(url, body, data) 569 return data 570 except Exception as error: 571 if self.Iflog: 572 self.log(url, body, data) 573 return {'172001': '网络错误'} 574 575 # 短信模板查询 576 # @param templateId 必选参数 模板Id,不带此参数查询全部可用模板 577 578 def QuerySMSTemplate(self, templateId): 579 580 self.accAuth() 581 nowdate = datetime.datetime.now() 582 self.Batch = nowdate.strftime("%Y%m%d%H%M%S") 583 # 生成sig 584 signature = self.AccountSid + self.AccountToken + self.Batch 585 sig = md5(signature.encode()).hexdigest().upper() 586 # 拼接URL 587 url = "https://" + self.ServerIP + ":" + self.ServerPort + "/" + self.SoftVersion + "/Accounts/" + self.AccountSid + "/SMS/QuerySMSTemplate?sig=" + sig 588 # 生成auth 589 src = self.AccountSid + ":" + self.Batch 590 auth = base64.encodestring(src.encode()).strip().decode() 591 req = urllib.request.Request(url) 592 self.setHttpHeader(req) 593 594 req.add_header("Authorization", auth) 595 596 # 创建包体 597 body = '''<?xml version="1.0" encoding="utf-8"?><Request>\ 598 <appId>%s</appId><templateId>%s</templateId></Request> 599 ''' % (self.AppId, templateId) 600 if self.BodyType == 'json': 601 # if this model is Json ..then do next code 602 body = '''{"appId": "%s", "templateId": "%s"}''' % (self.AppId, templateId) 603 req.data = body 604 data = '' 605 try: 606 # res = urllib.request.urlopen(req) 607 # data = res.read() 608 # res.close() 609 res = requests.post(url, headers=req.headers, data=req.data) 610 # print(res.status_code, res.content, res.text, ) 611 data = res.text 612 # if self.BodyType == 'json': 613 # # json格式 614 # locations = json.loads(data) 615 # else: 616 # # xml格式 617 # xtj = xmltojson() 618 # locations = xtj.main2(data) 619 if self.Iflog: 620 self.log(url, body, data) 621 return data 622 except Exception as error: 623 if self.Iflog: 624 self.log(url, body, data) 625 return {'172001': '网络错误'} 626 627 # 呼叫结果查询 628 # @param callsid 必选参数 呼叫ID 629 630 def CallResult(self, callSid): 631 632 self.accAuth() 633 nowdate = datetime.datetime.now() 634 self.Batch = nowdate.strftime("%Y%m%d%H%M%S") 635 # 生成sig 636 signature = self.AccountSid + self.AccountToken + self.Batch 637 sig = md5(signature.encode()).hexdigest().upper() 638 # 拼接URL 639 url = "https://" + self.ServerIP + ":" + self.ServerPort + "/" + self.SoftVersion + "/Accounts/" + self.AccountSid + "/CallResult?sig=" + sig + "&callsid=" + callSid 640 # 生成auth 641 src = self.AccountSid + ":" + self.Batch 642 auth = base64.encodestring(src.encode()).strip().decode() 643 req = urllib.request.Request(url) 644 self.setHttpHeader(req) 645 body = '' 646 req.add_header("Authorization", auth) 647 data = '' 648 try: 649 # res = urllib.request.urlopen(req) 650 # data = res.read() 651 # res.close() 652 res = requests.post(url, headers=req_headers, data=req_data) 653 # print(res.status_code, res.content, res.text, ) 654 data = res.text 655 656 # if self.BodyType == 'json': 657 # # json格式 658 # locations = json.loads(data) 659 # else: 660 # # xml格式 661 # xtj = xmltojson() 662 # locations = xtj.main(data) 663 if self.Iflog: 664 self.log(url, body, data) 665 return data 666 except Exception as error: 667 if self.Iflog: 668 self.log(url, body, data) 669 return {'172001': '网络错误'} 670 671 # 呼叫状态查询 672 # @param callid 必选参数 一个由32个字符组成的电话唯一标识符 673 # @param action 可选参数 查询结果通知的回调url地址 674 def QueryCallState(self, callid, action): 675 676 self.accAuth() 677 nowdate = datetime.datetime.now() 678 self.Batch = nowdate.strftime("%Y%m%d%H%M%S") 679 # 生成sig 680 signature = self.AccountSid + self.AccountToken + self.Batch 681 sig = md5(signature.encode()).hexdigest().upper() 682 # 拼接URL 683 url = "https://" + self.ServerIP + ":" + self.ServerPort + "/" + self.SoftVersion + "/Accounts/" + self.AccountSid + "/ivr/call?sig=" + sig + "&callid=" + callid 684 # 生成auth 685 src = self.AccountSid + ":" + self.Batch 686 auth = base64.encodestring(src.encode()).strip().decode() 687 req = urllib.request.Request(url) 688 self.setHttpHeader(req) 689 req.add_header("Authorization", auth) 690 691 # 创建包体 692 body = '''<?xml version="1.0" encoding="utf-8"?><Request>\ 693 <Appid>%s</Appid><QueryCallState callid="%s" action="%s"/>\ 694 </Request>\ 695 ''' % (self.AppId, callid, action) 696 if self.BodyType == 'json': 697 # if this model is Json ..then do next code 698 body = '''{"Appid":"%s","QueryCallState":{"callid":"%s","action":"%s"}}''' % (self.AppId, callid, action) 699 req.data = body 700 data = '' 701 try: 702 # res = urllib.request.urlopen(req) 703 # data = res.read() 704 # res.close() 705 res = requests.post(url, headers=req.headers, data=req.data) 706 # print(res.status_code, res.content, res.text, ) 707 data = res.text 708 709 # if self.BodyType == 'json': 710 # # json格式 711 # locations = json.loads(data) 712 # else: 713 # # xml格式 714 # xtj = xmltojson() 715 # locations = xtj.main(data) 716 if self.Iflog: 717 self.log(url, body, data) 718 return data 719 except Exception as error: 720 if self.Iflog: 721 self.log(url, body, data) 722 return {'172001': '网络错误'} 723 724 # 语音文件上传 725 # @param filename 必选参数 文件名 726 # @param body 必选参数 二进制串 727 def MediaFileUpload(self, filename, body): 728 729 self.accAuth() 730 nowdate = datetime.datetime.now() 731 self.Batch = nowdate.strftime("%Y%m%d%H%M%S") 732 # 生成sig 733 signature = self.AccountSid + self.AccountToken + self.Batch 734 sig = md5(signature.encode()).hexdigest().upper() 735 # 拼接URL 736 url = "https://" + self.ServerIP + ":" + self.ServerPort + "/" + self.SoftVersion + "/Accounts/" + self.AccountSid + "/Calls/MediaFileUpload?sig=" + sig + "&appid=" + self.AppId + "&filename=" + filename 737 # 生成auth 738 src = self.AccountSid + ":" + self.Batch 739 auth = base64.encodestring(src.encode()).strip().decode() 740 req = urllib.request.Request(url) 741 req.add_header("Authorization", auth) 742 if self.BodyType == 'json': 743 req.add_header("Accept", "application/json") 744 req.add_header("Content-Type", "application/octet-stream") 745 746 else: 747 req.add_header("Accept", "application/xml") 748 req.add_header("Content-Type", "application/octet-stream") 749 750 # 创建包体 751 req.data = body 752 753 try: 754 # res = urllib.request.urlopen(req) 755 # data = res.read() 756 # res.close() 757 res = requests.post(url, headers=req.headers, data=req.data) 758 # print(res.status_code, res.content, res.text, ) 759 data = res.text 760 # if self.BodyType == 'json': 761 # # json格式 762 # locations = json.loads(data) 763 # else: 764 # # xml格式 765 # xtj = xmltojson() 766 # locations = xtj.main(data) 767 if self.Iflog: 768 self.log(url, body, data) 769 return data 770 except Exception as error: 771 if self.Iflog: 772 self.log(url, body, data) 773 return {'172001': '网络错误'} 774 775 # 子帐号鉴权 776 def subAuth(self): 777 if (self.ServerIP == ""): 778 print('172004') 779 print('IP为空') 780 781 if (self.ServerPort <= 0): 782 print('172005') 783 print('端口错误(小于等于0)') 784 785 if (self.SoftVersion == ""): 786 print('172013') 787 print('版本号为空') 788 789 if (self.SubAccountSid == ""): 790 print('172008') 791 print('子帐号为空') 792 793 if (self.SubAccountToken == ""): 794 print('172009') 795 print('子帐号令牌为空') 796 797 if (self.AppId == ""): 798 print('172012') 799 print('应用ID为空') 800 801 # 主帐号鉴权 802 def accAuth(self): 803 if (self.ServerIP == ""): 804 print('172004') 805 print('IP为空') 806 807 if (int(self.ServerPort) <= 0): 808 print('172005') 809 print('端口错误(小于等于0)') 810 811 if (self.SoftVersion == ""): 812 print('172013') 813 print('版本号为空') 814 815 if (self.AccountSid == ""): 816 print('172006') 817 print('主帐号为空') 818 819 if (self.AccountToken == ""): 820 print('172007') 821 print('主帐号令牌为空') 822 823 if (self.AppId == ""): 824 print('172012') 825 print('应用ID为空') 826 827 # 设置包头 828 def setHttpHeader(self, req): 829 if self.BodyType == 'json': 830 req.add_header("Accept", "application/json") 831 req.add_header("Content-Type", "application/json;charset=utf-8") 832 833 else: 834 req.add_header("Accept", "application/xml") 835 req.add_header("Content-Type", "application/xml;charset=utf-8")
xmltojson.py
1 import os 2 import xml.etree.ElementTree as ET 3 from xml.dom import minidom 4 5 class xmltojson: 6 # global var 7 # show log 8 SHOW_LOG = True 9 # XML file 10 XML_PATH = None 11 a = {} 12 m = [] 13 14 def get_root(self, path): 15 '''parse the XML file,and get the tree of the XML file 16 finally,return the root element of the tree. 17 if the XML file dose not exist,then print the information''' 18 # if os.path.exists(path): 19 # if SHOW_LOG: 20 # print('start to parse the file : [{}]'.format(path)) 21 tree = ET.fromstring(path) 22 return tree 23 # else: 24 # print('the path [{}] dose not exist!'.format(path)) 25 26 def get_element_tag(self, element): 27 '''return the element tag if the element is not None.''' 28 if element is not None: 29 30 return element.tag 31 else: 32 print('the element is None!') 33 34 def get_element_attrib(self, element): 35 '''return the element attrib if the element is not None.''' 36 if element is not None: 37 38 return element.attrib 39 else: 40 print('the element is None!') 41 42 def get_element_text(self, element): 43 '''return the text of the element.''' 44 if element is not None: 45 return element.text 46 else: 47 print('the element is None!') 48 49 def get_element_children(self, element): 50 '''return the element children if the element is not None.''' 51 if element is not None: 52 53 return [c for c in element] 54 else: 55 print('the element is None!') 56 57 def get_elements_tag(self, elements): 58 '''return the list of tags of element's tag''' 59 if elements is not None: 60 tags = [] 61 for e in elements: 62 tags.append(e.tag) 63 return tags 64 else: 65 print('the elements is None!') 66 67 def get_elements_attrib(self, elements): 68 '''return the list of attribs of element's attrib''' 69 if elements is not None: 70 attribs = [] 71 for a in elements: 72 attribs.append(a.attrib) 73 return attribs 74 else: 75 print('the elements is None!') 76 77 def get_elements_text(self, elements): 78 '''return the dict of element''' 79 if elements is not None: 80 text = [] 81 for t in elements: 82 text.append(t.text) 83 return dict(zip(self.get_elements_tag(elements), text)) 84 else: 85 print('the elements is None!') 86 87 def main(self, xml): 88 # root 89 root = self.get_root(xml) 90 91 # children 92 children = self.get_element_children(root) 93 94 children_tags = self.get_elements_tag(children) 95 96 children_attribs = self.get_elements_attrib(children) 97 98 i = 0 99 100 # 获取二级元素的每一个子节点的名称和值 101 for c in children: 102 p = 0 103 c_children = self.get_element_children(c) 104 dict_text = self.get_elements_text(c_children) 105 if dict_text: 106 # print (children_tags[i]) 107 if children_tags[i] == 'TemplateSMS': 108 self.a['templateSMS'] = dict_text 109 else: 110 if children_tags[i] == 'SubAccount': 111 k = 0 112 113 for x in children: 114 if children_tags[k] == 'totalCount': 115 self.m.append(dict_text) 116 self.a['SubAccount'] = self.m 117 p = 1 118 k = k + 1 119 if p == 0: 120 self.a[children_tags[i]] = dict_text 121 else: 122 self.a[children_tags[i]] = dict_text 123 124 125 else: 126 self.a[children_tags[i]] = c.text 127 i = i + 1 128 return self.a 129 130 def main2(self, xml): 131 # root 132 root = self.get_root(xml) 133 134 # children 135 children = self.get_element_children(root) 136 137 children_tags = self.get_elements_tag(children) 138 139 children_attribs = self.get_elements_attrib(children) 140 141 i = 0 142 143 # 获取二级元素的每一个子节点的名称和值 144 for c in children: 145 p = 0 146 c_children = self.get_element_children(c) 147 dict_text = self.get_elements_text(c_children) 148 if dict_text: 149 if children_tags[i] == 'TemplateSMS': 150 k = 0 151 152 for x in children: 153 if children_tags[k] == 'totalCount': 154 self.m.append(dict_text) 155 self.a['TemplateSMS'] = self.m 156 p = 1 157 k = k + 1 158 if p == 0: 159 self.a[children_tags[i]] = dict_text 160 else: 161 self.a[children_tags[i]] = dict_text 162 163 else: 164 self.a[children_tags[i]] = c.text 165 i = i + 1 166 return self.a
短信验证码运用,和tornado 图形验证码结合使用。例:
register.html
1 <div class="input-group"> 2 <div class="input-group-addon"><i class="fa fa-envelope-o fa-lg fa-fw"></i></div> 3 <input type="text" class="form-control" name="phonecode" id="phonecode" placeholder="短信验证码" required> 4 <div class="phonecode input-group-addon"> 5 <a class="phonecode-a" href="javascript:;" onclick="sendSMSCode();">获取验证码</a> 6 </div> 7 </div>
register.js
1 function sendSMSCode() { 2 $(".phonecode-a").removeAttr("onclick"); //避免连续点击两次时造成两次调用 3 var mobile = $("#mobile").val(); 4 if (!mobile) { 5 $("#mobile-err span").html("请填写正确的手机号!"); 6 $("#mobile-err").show(); 7 $(".phonecode-a").attr("onclick", "sendSMSCode();"); 8 return; 9 } 10 var imageCode = $("#imagecode").val(); 11 if (!imageCode) { 12 $("#image-code-err span").html("请填写验证码!"); 13 $("#image-code-err").show(); 14 $(".phonecode-a").attr("onclick", "sendSMSCode();"); 15 return; 16 } 17 18 var data = {mobile:mobile, piccode:imageCode, piccode_id:imageCodeId}; 19 $.ajax({ 20 url: "/api/smscode", 21 method: "POST", 22 data: JSON.stringify(data), 23 contentType: "application/json", 24 dataType: "json", 25 success: function (data) { 26 // data = { 27 // errcode 28 // errmsg 29 // } 30 if ("0" == data.errcode) { 31 var duration = 60; 32 var timeObj = setInterval(function () { 33 duration = duration - 1; 34 $(".phonecode-a").html(duration+"秒"); 35 if (1 == duration) { 36 clearInterval(timeObj) 37 $(".phonecode-a").html("获取验证码"); 38 $(".phonecode-a").attr("onclick", "sendSMSCode();") 39 } 40 }, 1000, 60) 41 } else { 42 $("#image-code-err span").html(data.errmsg); 43 $("#image-code-err").show(); 44 $(".phonecode-a").attr("onclick", "sendSMSCode();") 45 if (data.errcode == "4002" || data.errcode == "4004") { 46 generateImageCode(); 47 } 48 } 49 } 50 })
VerifyCode.py
1 from .BaseHandler import BaseHandler 2 import logging 3 import io 4 import re 5 import random 6 7 from utils.captcha.captcha import create_validate_code 8 from constants import * 9 from utils.response_code import RET 10 from utils.yuntongxun.SendTemplateSMS import ccp 11 12 """短信验证码""" 13 class SMSCodeHandler(BaseHandler): 14 def post(self): 15 mobile=self.json_args.get("mobile") 16 piccode=self.json_args.get("piccode") 17 piccode_id=self.json_args.get("piccode_id") 18 19 # 参数校验 20 if not all((mobile,piccode,piccode_id)): 21 return self.write(dict(errcode=RET.PARAMERR,errmsg='参数缺失')) 22 if not re.match(r'^1\d{10}$',mobile): 23 return self.write(dict(errcode=RET.PARAMERR,errmsg='手机号格式错误')) 24 25 # 获取图片验证码真实值 26 global real_piccode 27 try: 28 real_piccode=self.redis.get("pic_code_%s" % piccode_id) 29 except Exception as e: 30 logging.error(e) 31 self.write(dict(errcode=RET.DBERR,errmsg='查询验证码错误')) 32 if not real_piccode: #real_piccode要定义为全局变量,不然会报错 33 return self.write(dict(errcode=RET.NODATA, errmsg="验证码过期")) 34 35 #判断图形验证码正确性 36 if real_piccode.decode('utf-8').lower() != piccode.lower(): ##redis数据real_piccode要解码 37 # print(real_piccode.lower()) 38 # print(piccode.lower()) 39 return self.write(dict(errcode=RET.DATAERR, errmsg="验证码错误")) 40 41 #检查手机号码是否存在 42 43 #生成随机短信验证码 44 sms_code="%04d" %random.randint(0,9999) 45 try: 46 self.redis.setex("sms_code_%s" % mobile, SMS_CODE_EXPIRES_SECONDS, sms_code) 47 except Exception as e: 48 logging.error(e) 49 self.write(dict(errcode=RET.DBERR, errmsg='数据库出错')) 50 51 #发送短信验证码 52 global result 53 try: 54 result=ccp.sendTemplateSMS(mobile,[sms_code,SMS_CODE_EXPIRES_SECONDS/60],1) 55 except Exception as e: 56 logging.error(e) 57 self.write(dict(errcode=RET.THIRDERR, errmsg='发送短信失败')) 58 if result: 59 self.write(dict(errcode=RET.OK, errmsg='发送成功')) 60 else: 61 self.write(dict(errcode=RET.UNKOWNERR, errmsg='发送失败'))


参考文档:
https://blog.csdn.net/DCclient/article/details/102870028
https://blog.csdn.net/zsx1314lovezyf/article/details/99176682
posted on 2020-04-15 20:19 cherry_ning 阅读(153) 评论(0) 收藏 举报
浙公网安备 33010602011771号