Skip to content

Commit

Permalink
tc_sms: 简单易用的 腾讯云SMS短信发送 SDK
Browse files Browse the repository at this point in the history
  • Loading branch information
fritx committed Aug 2, 2024
1 parent 8a8a2c3 commit 8708621
Show file tree
Hide file tree
Showing 5 changed files with 190 additions and 0 deletions.
104 changes: 104 additions & 0 deletions tc_sms/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# tc_sms

> 简单易用的 腾讯云SMS短信发送 SDK
### 基础示例

```ini
# .env
TENCENTCLOUD_SECRET_ID=AKIDxxxxxx
TENCENTCLOUD_SECRET_KEY=APxxxxxx
TC_SDK_APP_ID=1xxxxxx
```

```go
// sms_job.go
import (
// 自动加载.env环境变量
_ "github.com/joho/godotenv/autoload"
// 引入tc_sms
"gitee.com/we-mid/go/tc_sms"
)

var (
// 腾讯云-短信-国内短信-正文模板管理
// https://console.cloud.tencent.com/smsv2/csms-template
templateId = "2197493" // 运营数据通知
template = `{1} 近期运营数据:
*{2} PV:[{3}] UV:[{4}] 留存:[{5}]
*{6} PV:[{7}] UV:[{8}] 留存:[{9}]
*{10} PV:[{11}] UV:[{12}] 留存:[{13}]
*{14} PV:[{15}] UV:[{16}] 留存:[{17}]`
feeLimit = 3 // 单条消息发送fee上限设定
sigName = "XX应用" // 短信签名
phones = []string{"+8613xxxx", "+8618xxxx", "+8613xxxx"} // 接收手机号码
)

func smsJob() {
// 组装参数
var params []string
// ...
// 发送前预览完整文本,自动计算fee
result, n, fee := tc_sms.SmsPreview(template, params)
log.Println("smsJob preview:", result)
log.Printf("smsJob preview: n=%d, fee=%d\n", n, fee)
// 针对fee设置风控门槛
if fee > feeLimit {
log.Printf("smsJob fee exceeded! n=%d, fee=%d\n", n, fee)
return
}
// 发送短信
res, err := tc_sms.SmsSend(signName, templateId, params, phones)
if err != nil {
log.Println("smsJob error:", err)
return
}
// 检查返回结果是否全部发送成功
str := res.ToJsonString()
if strings.Count(str, "send success") < len(phones) {
log.Println("smsJob some failed", str)
return
}
log.Println("smsJob all success:", str)
}
```

### 错误码 InvalidParameterValue.TemplateParameterLengthLimit 及文本截断

```go
// 注意:错误码 InvalidParameterValue.TemplateParameterLengthLimit
// 非验证码短信:每个变量取值最多支持6个字符。
// https://cloud.tencent.cn/document/product/382/52075
const paramLimit = 6

func smsJob() {
// ...
for i, v := range params {
// ❌ 注意:错误的截断方式
// if len(v) > paramLimit {
// params[i] = v[:paramLimit]
// }
// ✅ 注意:正确处理中文字符长度
if len([]rune(v)) > paramLimit {
params[i] = truncateString(v, paramLimit) // 直接截断
// ...或进行其他自定义的智能截断处理
}
}
// ...
}

func truncateString(s string, length int) string {
runes := []rune(s)
if len(runes) <= length {
return s
}
return string(runes[:length])
}
```

### 参考链接

- 腾讯云-短信-国内短信-正文模板管理:https://console.cloud.tencent.com/smsv2/csms-template
- 关于单条短信计费fee:https://console.cloud.tencent.com/smsv2/csms-template/create
- 非验证码短信:每个变量取值最多支持6个字符:https://cloud.tencent.cn/document/product/382/52075
- 参考了GitHub上的代码:https://github.com/ixre/go2o/blob/2c7f7c875501432b008b84636ab41cdac5527bd1/core/sp/tencent/tecent_sms.go
8 changes: 8 additions & 0 deletions tc_sms/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module gitee.com/we-mid/go/tc_sms

go 1.21.1

require (
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.975
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/sms v1.0.975
)
4 changes: 4 additions & 0 deletions tc_sms/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.975 h1:MvrNR+UtZhinneKgDngVlsm2kzuqOQ25rfwhaxedptE=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.975/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/sms v1.0.975 h1:nFBhBNjeQO7TgMdMgYg1d7JhOamooper7WJ7OQYjHKI=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/sms v1.0.975/go.mod h1:XXAOh54ULn6wv0KPpFX9DI26lBujGeldB/sQgMMHn98=
39 changes: 39 additions & 0 deletions tc_sms/preview.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package tc_sms

import (
"math"
"regexp"
"strconv"
"strings"
)

// 注意:关于单条短信计费fee -- 1. 汉字、字母、数字、标点符号(不区分全角/半角)以及空格等,都按1个字计算
// 2. 国内短信短信长度(签名+正文)不超过70字时,按照1条短信计费;超过70字即为长短信时,按67字/条分隔成多条计费,但会在1条短信内呈现。例如,短信长度为150字,则按照67字/67字/16字分隔成3条计费
// https://console.cloud.tencent.com/smsv2/csms-template/create
const nPerFee = 67

// 发送前预览完整文本,自动计算fee
func SmsPreview(template string, params []string) (string, int, int) {
out := replacePlaceholders(template, params)
n := len([]rune(out)) // 注意是 字符数
return out, n, int(math.Ceil(float64(n) / float64(nPerFee)))
}

func replacePlaceholders(s string, params []string) string {
// 正则表达式匹配形如 {数字} 的模式
reTmpl := regexp.MustCompile(`\{(\d+)\}`)
return reTmpl.ReplaceAllStringFunc(s, func(match string) string {
// 提取数字部分并转换为整型
indexStr := strings.Trim(match, "{}")
index, err := strconv.Atoi(indexStr)
index -= 1
if err != nil {
return match // 如果转换失败,返回原样
}
// 确保索引在切片范围内
if index < 0 || index >= len(params) {
return match // 如果索引越界,返回原样
}
return params[index]
})
}
35 changes: 35 additions & 0 deletions tc_sms/send.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package tc_sms

import (
"os"

"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/regions"
sms "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/sms/v20210111"
)

// 参考了GitHub上的代码
// https://github.com/ixre/go2o/blob/2c7f7c875501432b008b84636ab41cdac5527bd1/core/sp/tencent/tecent_sms.go
func SmsSend(signName, templateId string, params, phones []string) (*sms.SendSmsResponse, error) {
// 硬编码密钥到代码中有可能随代码泄露而暴露,有安全隐患,并不推荐。
// 为了保护密钥安全,建议将密钥设置在环境变量中或者配置文件中,请参考本文凭证管理章节。
credential := common.NewCredential(
os.Getenv("TENCENTCLOUD_SECRET_ID"),
os.Getenv("TENCENTCLOUD_SECRET_KEY"),
)
cpf := profile.NewClientProfile()
cpf.HttpProfile.ReqMethod = "POST"
cpf.HttpProfile.Endpoint = "sms.tencentcloudapi.com"
cpf.SignMethod = "HmacSHA1"
client, _ := sms.NewClient(credential, regions.Guangzhou, cpf)

req := sms.NewSendSmsRequest()
// 配置签名和应用Id
req.SmsSdkAppId = common.StringPtr(os.Getenv("TC_SDK_APP_ID"))
req.SignName = common.StringPtr(signName)
req.TemplateId = common.StringPtr(templateId)
req.TemplateParamSet = common.StringPtrs(params)
req.PhoneNumberSet = common.StringPtrs(phones)
return client.SendSms(req)
}

0 comments on commit 8708621

Please sign in to comment.