1-Python - 短信服务

about

在项目中集成短信功能,如短信验证登录、发送短信通知,现在非常普遍,所以本篇记录下如何在项目中集成短信功能。
由于监管严格,有的短信服务商开通短信服务的资质也越来越严格,导致我们在开发阶段,不得不尝试其它更多厂家的,目前(2023/5/16)据我了解,不确认以后会不会放开个人资质认证:

  • 腾讯云,现在开通短信服务要么你要有公司资质、要么是已经上线并且备案的小程序这些,所以开通门槛有点高了。
  • 容联云,之前还支持个人认证,目前(2023/5/24)只支持公司认证了,不支持个人认证,但它提供了一个测试号码,也就是我们开发阶段使用这个测试号码也是可以的。
  • 互亿无线的106短信,这个今天我测了下,现在还能支持个人认证,总之开通和认证都简单、代码也不难,配置也相对简单,如果是个人项目开发阶段,用这个也行。

容联云短信

这里演示下如何开通容联云的短信测试账号,保证我们开发阶段的短信服务正常使用。

配置过程

1. 注册,没啥好说的了,注册地址:https://console.yuntongxun.com/user/reg/init

2. 注册成功,在控制台首页(https://console.yuntongxun.com/member/main),就能获取到以下信息

3. 添加测试号码,这就是未通过认证,我们只能在开发中,在这里添加最多3个测试号码来进行开发中使用

4. 短信模板
短信模板这块,由于是测试使用,我们只能使用固定的短信模板了,我写这部分博客时,发出的短信长这样:

【云通讯】您使用的是云通讯短信模板,您的验证码是4653,请于2分钟内正确输入(变量仅支持1-4位数字)

要想更换短信模板,就要充钱了.....所以,我们还是凑活用吧,上线了再改一样。
所以,这里我们能知道的就是一个模板ID为1,这个后面发短信要用到,记好了。
5. 发送短信示例
各语言参考手册:

来个Python的示例,先下载对应的模块:

pip install ronglian_sms_sdk

然后来上demo示例,当然这个示例完全可以集成到你的项目中:

import random
import json
from ronglian_sms_sdk import SmsSDK
def send_sms(mobile, datas, tid=1):
    """
    发送短信
    @params tid: 模板ID,默认测试使用1
    @params mobile: 接收短信的手机号,多个手机号使用都逗号隔开
            单个号码: mobile="13312345678"
            多个号码: mobile="13312345678,13312345679,...."
    @params datas: 短信模板的参数列表
            例如短信模板为: 【云通讯】您的验证码是{1},请于{2}分钟内正确输入。
            则datas=("3456",2,)
            最终发送的短信为:【云通讯】您的验证码是{3456},请于{5}分钟内正确输入。
    """
    RONGLIANYUN_CONFIG = {
        # 下面这三个值都可以从控制台首页的开发者主账号中进行获取
        "accId": '2c94811c87fb71111881e50a8ed0b39',  # 对应:ACCOUNT SID
        "accToken": '23d2e5f9f7111e888eb2a6848dae42',  # 对应:AUTH TOKEN
        "appId": '2c94811c87fb11101881e50aa210b40',  # 对应:AppID(默认)
        "reg_tid": 1,       # 注册短信验证码的模板ID,测试阶段固定为1
        "sms_expire": 120,  # 短信有效期,单位:秒(s),这是真正的超时时间,注意和datas=("3456",2,)中第二个参数进行换算,保持一致
        "sms_interval": 60,  # 短信发送的冷却时间,单位:秒(s)
    }

    sdk = SmsSDK(RONGLIANYUN_CONFIG.get("accId"), RONGLIANYUN_CONFIG.get("accToken"), RONGLIANYUN_CONFIG.get("appId"))
    resp = sdk.sendMessage(str(tid), mobile, datas)
    response = json.loads(resp)
    print(response, type(response))
    """
    Sign plaintext:  2c94811c87fb7ec601881e50a8ed0b3923d2e5f9f7694d9e888eb2a6848dae4220230524140059
    Authorization plaintext: 2c94811c87fb7ec601881e50a8ed0b39:20230524140059
    Request url:  https://app.cloopen.com:8883/2013-12-26/Accounts/2c94811c87fb7ec601881e50a8ed0b39/SMS/TemplateSMS?sig=6FB880CDC9671A41674C17DE348D300B
    Request headers:  {'Content-Type': 'application/json;charset=utf-8', 'Accept': 'application/json', 'Accept-Charset': 'UTF-8', 'Authorization': b'MmM5NDgxMWM4N2ZiN2VjNjAxODgxZTUwYThlZDBiMzk6MjAyMzA1MjQxNDAwNTk='}
    Request body:  {"to": "18211101742", "appId": "2c94811c87fb7ec601881e50aa210b40", "templateId": "1", "datas": [4653, 2]}
    Response body:  {"statusCode":"000000","templateSMS":{"smsMessageSid":"8bcc77c455084a6b8d52983e95f10977","dateCreated":"20230524140100"}}
    {'statusCode': '000000', 'templateSMS': {'smsMessageSid': '8bcc77c455084a6b8d52983e95f10977', 'dateCreated': '20230524140100'}} <class 'dict'>
    """
    # statusCode是'000000'表示发送成功
    # 手机上接收到的短信长这样
    """
    【云通讯】您使用的是云通讯短信模板,您的验证码是4653,请于2分钟内正确输入(变量仅支持1-4位数字)
    """
    return response.get("statusCode") == "000000"

def get_code(num=4):
    """
    生成指定位数的验证码,如果不传值,就默认生成4位的验证码
    :param num: 要生成几位的验证码
    :return: 生成的验证码
    """
    return random.randint(
        int('1{}'.format('0' * (num - 1))),
        int('9{}'.format('9' * (num - 1)))
    )

if __name__ == '__main__':

    send_sms(
        mobile='18211101111',   # 这个手机号必须在你在容联云中填写的那个3个测试手机号中
        datas=(get_code(4), 2),  # 元组的第一个参数是4位的验证码,注意免费测试的只支持发送4位的验证码,不支持6位的,第二个参数是短信模板中替换为:请于{2}分钟内正确输入
        tid=1                   # 这个模板id目前测试阶段固定为1
    )

常见报错

{'statusCode': '161125', 'statusMsg': '请输入1到4位的数字'}

这个报错很明显是规定只能发送4位的验证码,你却发送了6的验证码,所以报错了,解决办法就是生成4位验证码即可。

互亿无线106短信

PS:注册后,互亿无线的客服会给你打电话问一下使用用途,说个人项目开发中使用即可,好用的话,会在公司中推荐正式使用的。
然后跟着我下面的流程走就行了,非常简单。

注册及认证

1. 实名注册:https://user.ihuyi.com/new/register.html?e=400

2. 注册成功后,会自动跳转到控制台,没自动跳转就手动点击网址:https://user.ihuyi.com/new/console

3. 点击认证,这里进行个人认证,然后按照下图提示在手机上进行微信认证。

4. 手机上进行微信认证,按照微信中的引导即可。

5. 浏览器中回到控制台的账号信息页面,发现认证通过了。https://user.ihuyi.com/new/console/account/infomation?jumpto=certify

现在认证完事了。

发送短信验证码基础版

当你注册认证通过之后,会得到10条免费短信,也会有个默认的短信模板(充值之后可以创建自定义模板)。

我们可以尝试使用默认的模板和免费短信来写个demo示例,示例中还需要两个参数需要我们提前准备好。

#定义请求的数据
values = {
    'account': 'C05062023',   			      # 这个是对应的APIID
    'password': 'aff16e275e46618efd14f21b00ad12391',  # 这个是对应的APIKEY
    'mobile': mobile,  # 你要发给谁
    'content': '您的验证码是:7835。请不要把验证码泄露给其他人。',  # 没有购买套餐的,这个模板只能使用默认的,即你收到的短信长这样:【互亿无线】您的验证码是:7835。请不要把验证码泄露给其他人。
    'format': 'json',
}

上面请求参数中,我们需要准备的是APIIDAPIKEY,这俩在如下图所示的位置可以找到:https://user.ihuyi.com/new/sms/overview

下面的示例是基于Python3的示例:

#python3
#接口类型:互亿无线触发短信接口,支持发送验证码短信、订单通知短信等。
#账户注册:请通过该地址开通账户https://user.ihuyi.com/new/register.html
#注意事项:
#(1)调试期间,请用默认的模板进行测试,默认模板详见接口文档;
#(2)请使用 用户名 及 APIkey来调用接口,APIkey在会员中心可以获取;
#(3)该代码仅供接入互亿无线短信接口参考使用,客户可根据实际需要自行编写;

import random
import urllib.parse
import urllib.request

def get_code(num=4):
    """
    生成指定位数的验证码,如果不传值,就默认生成4位的验证码
    :param num: 要生成几位的验证码
    :return: 生成的验证码
    """
    return random.randint(
        int('1{}'.format('0' * (num - 1))),
        int('9{}'.format('9' * (num - 1)))
    )
    
    
def send_sms(mobile, code_num=4):
    """
    发送短信验证码
    :param mobile: 你要发给谁
    :param code_num: 发送几位的验证码
    :return:
    """
    # 接口地址,咱们不需要更改
    url = 'http://106.ihuyi.com/webservice/sms.php?method=Submit'

    # 定义请求的数据
    values = {
        'account': 'APIID',    # 这个是对应的APIID
        'password': 'APIKEY',  # 这个是对应的APIKEY
        'mobile': mobile,      # 发给谁
        'content': '您的验证码是:{}。请不要把验证码泄露给其他人。'.format(get_code()),  # 没有购买套餐的,这个模板只能使用默认的,即你收到的短信长这样:【互亿无线】您的验证码是:7835。请不要把验证码泄露给其他人。
        'format': 'json',  # 不要动
    }

    # 将数据进行编码,下面代码不要动
    data = urllib.parse.urlencode(values).encode(encoding='UTF8')

    # 发起请求,下面代码不要动
    req = urllib.request.Request(url, data)
    response = urllib.request.urlopen(req)
    res = response.read()

    # 打印结果,然后你的手机应该就能接到短信了
    print(res.decode("utf8"))  # {"code":2,"msg":"提交成功","smsid":"16842079209571524017"}


if __name__ == '__main__':
    send_sms('接收短信的手机号')

腾讯云短信

python3.9
腾讯云短信服务官网:https://cloud.tencent.com/product/sms
腾讯云短信文档: https://cloud.tencent.com/document/product/382

具体的注册流程参考:https://pythonav.com/wiki/detail/10/81/

SDK方式发送短信

python3.9
短信模板:https://console.cloud.tencent.com/smsv2/csms-template
签名信息:https://console.cloud.tencent.com/smsv2/csms-sign
应用 ID:https://console.cloud.tencent.com/smsv2/app-manage
API密钥:https://console.cloud.tencent.com/cam/capi

前提(截止到2023/2/6):

下载相应的SDK
通过pip下载SDK:

# 国内如果能访问GitHub的话可以通过这个命令下载,否则用下面的Gitee的也行
pip install --upgrade tencentcloud-sdk-python

# Gitee
pip install --upgrade tencentcloud-sdk-python

发送短信代码示例

请无脑的参考下面的流程就行了,让你去哪找就去哪找,让你改啥你就改啥,不让你改啥,别瞎动。

import random
import string
from tencentcloud.common import credential
from tencentcloud.sms.v20210111 import sms_client, models

# 注意,下面写法是固定的,你只需要按照上面给的链接中,获取对应的值,来修改即可

# 下面这俩值从这个页面中获取:https://console.cloud.tencent.com/cam/capi
SecretId = "AKIDQdZFemCGdlcCo9sQ9sKkwriIq776rUu423"
SecretKey = "FEvLZWBZgX7JFzRcW8VbgU7UZOvCuc3123"


cred = credential.Credential(
    secret_id=SecretId,    # 注意必须以关键字的形式传参
    secret_key=SecretKey   # 注意必须以关键字的形式传参
)  # secretId secretKey
client = sms_client.SmsClient(cred, "ap-guangzhou")  # 固定写法无需变动

req = models.SendSmsRequest()

# 应用id,注意这个是你创建的应用id,是这个链接:https://console.cloud.tencent.com/smsv2/app-manage 中的应用ID
# 强调,不是密钥页面的APPID,既不是这个页面的ID:https://console.cloud.tencent.com/cam/capi
req.SmsSdkAppId = "1400793123"  

# 模板签名:https://console.cloud.tencent.com/smsv2/csms-sign 也就是签名管理菜单中,找到你的签名内容
req.SignName = "张开与老虎"   # 模板签名

# 模板id从这个地址找:https://console.cloud.tencent.com/smsv2/csms-template, 也就是正文模板管理菜单
req.TemplateId = "5750123"   # 模板id(可以有多套模板进行更换,这样发送的短信内容就可以改变了)

# 下面就是结合模板填写具体要发送的验证码和手机号了
code = ''.join(random.sample(string.digits, 6))  # 随机生成的6位验证码
print(code)  # 671093

# 如果短信模板中只有一个值需要填充,那么列表中就放一个值就好了,注意,数字的话,必须是转成字符串类型。
req.TemplateParamSet = [code]  # 发送的6位验证码
# 如果你要发送的短信模板是这样的(您正在申请手机注册,验证码为:{1},[2}分钟内有效!),有多个值需要填充,就依次填写到列表中就好了,注意,数字的话,必须是转成字符串类型。
# req.TemplateParamSet = [code, '5']  # 发送的6位验证码

req.PhoneNumberSet = ["+8618211111111"]   # 目标手机号

resp = client.SendSms(req)  # 发送
print(resp)
"""
{
	"SendStatusSet": [
		{
			"SerialNo": "2433:326237685316756579077330174", 
			"PhoneNumber": "+8618211101111", 
			"Fee": 1, "SessionContext": "", 
			"Code": "Ok", "Message": "send success", "IsoCode": "CN"
			}
	], 
	"RequestId": "25147cb8-9abd-4100-bdbb-bb61cd3c2828"
}

# 你的手机号,应该接收到了短信:
【张开与老虎】验证码为:671093,您正在登录,若非本人操作,请勿泄露。
"""

更详细的示例参考官网:https://cloud.tencent.com/document/product/382/43196
错误码:https://cloud.tencent.com/document/product/382/38780

api形式实现短信发送

python3.9

常见报错

tencentcloud.common.exception.tencent_cloud_sdk_exception.TencentCloudSDKException: [TencentCloudSDKException] code:ClientNetworkError message:HTTPSConnectionPool(host='sms.tencentcloudapi.com', port=443): Max retries exceeded with url: / (Caused by ProxyError('Cannot connect to proxy.', timeout('_ssl.c:1114: The handshake operation timed out'))) requestId:None

如果你发短信的代码运行后,等了一段时间,然后报了超时的错误,而你又能正常的上网,那么优先检查你的电脑是否开着代理,有代理,短信服务就不可用。

tencentcloud.common.exception.tencent_cloud_sdk_exception.TencentCloudSDKException: [TencentCloudSDKException] code:ClientNetworkError message:HTTPSConnectionPool(host='sms.tencentcloudapi.com', port=443): Max retries exceeded with url: / (Caused by ProxyError('Cannot connect to proxy.', timeout('_ssl.c:1114: The handshake operation timed out'))) requestId:None

关闭代理再重发,就应该没问题了。

("SendstatusSet": [{"Seriao": "n, "phonenumben": "+8618211101111", "Fee": 0, "SessionContext": "n, "Code": "FailedOperation.InsufficientBalanceInSmsPackage", "Message": "insufficient balance in SMS package", "IsoCode": mny], "RequestId".85e31f2c-9507-49c3-a288-7ed741afbf16"}

这个错误根据官网的错误码,说是套餐包余量不足,需要充值了。

tencentcloud.common.exception.tencent_cloud_sdk_exception.TencentCloudSDKException: [TencentCloudSDKException] code:UnauthorizedOperation.SmsSdkAppIdVerifyFail message:request SmsSdk AppId verify fail requestId:6e63e34e-fa46-40ee-9281-320446e80c1a

这个报错没啥好说的,就是req.SmsSdkAppId的值获取错了,应该获取的是应用ID。
当然也要检查其他的参数是否填写正确。

腾讯短信发送成功,但手机接收不到短信

按照上面腾讯云短信的示例,发送短信成功了,打印结果也是:

{"SendStatusSet": [{"SerialNo": "2640:63003780816991009901530174", "PhoneNumber": "+8618211101742", "Fee": 1, "SessionContext": "", "Code": "Ok", "Message": "send success", "IsoCode": "CN"}], "RequestId": "959d08ca-9af8-4974-ab82-307a4dcee0a0"}

看发送结果肯定是没问题的,但是手机就是收不到消息,去腾讯云官档查统计分析记录,发现,被运营商拦截了:

解决办法就是腾讯云的人工服务吧。

posted @ 2022-09-09 14:07  听雨危楼  阅读(1176)  评论(0编辑  收藏  举报