zlwu 10 ماه پیش
والد
کامیت
5f4fc7d499
3فایلهای تغییر یافته به همراه370 افزوده شده و 0 حذف شده
  1. 144 0
      sendintlmsg/README.md
  2. 2 0
      sendintlmsg/requirements.txt
  3. 224 0
      sendintlmsg/send_international_sms.py

+ 144 - 0
sendintlmsg/README.md

@@ -0,0 +1,144 @@
+# 阿里云国际短信发送 Demo
+
+## 功能说明
+
+使用阿里云 SMS 服务发送国际短信的独立 Python 脚本。
+
+- ✅ 支持发送到美国、加拿大、新加坡等国家/地区
+- ✅ 支持 OTP(验证码)、NOTIFY(通知)、MKT(推广)三种类型
+- ✅ 完整的错误处理和响应解析
+
+## 安装依赖
+
+```bash
+pip install -r requirements.txt
+```
+
+或直接安装:
+
+```bash
+pip install alibabacloud-dysmsapi20180501
+```
+
+## 配置说明
+
+### 1. AccessKey 配置
+
+AccessKey 已直接配置在脚本中(`main()` 函数),无需额外配置环境变量。
+
+如需修改 AccessKey,请编辑 `send_international_sms.py` 文件中的以下变量:
+```python
+ACCESS_KEY_ID = 'LTAI5tQ6iFj9XMSZygRhKL4F'
+ACCESS_KEY_SECRET = 'eXEfRLenQfOCLWkj307jv48rBUWNHK'
+```
+
+### 2. Sender ID 注册要求
+
+| 国家/地区 | 注册要求 |
+|----------|---------|
+| 美国、加拿大 | 需完成 10 DLC 注册,使用运营商提供的 Sender ID |
+| 新加坡 | 需在 IMDA 完成 SSIR 注册的 Sender ID |
+| 其他地区 | 支持 Sender ID 发送,数字+字母最长11位,纯数字最长15位 |
+
+## 使用方法
+
+### 基本用法
+
+```python
+from send_international_sms import InternationalSMSClient
+
+# 创建客户端
+client = InternationalSMSClient(
+    access_key_id='your-access-key-id',
+    access_key_secret='your-access-key-secret'
+)
+
+# 发送验证码短信
+result = client.send_sms(
+    to="6212345****01",  # 国际区号+号码
+    message="Your verification code is 123456",
+    sender_id="Alicloud",  # 已注册的 Sender ID
+    sms_type=InternationalSMSClient.TYPE_OTP
+)
+
+if result["success"]:
+    print(f"发送成功! 消息ID: {result['message_id']}")
+else:
+    print(f"发送失败: {result['error']}")
+```
+
+### 运行示例
+
+```bash
+python send_international_sms.py
+```
+
+## API 参数说明
+
+### 请求参数
+
+| 参数 | 类型 | 必选 | 说明 |
+|-----|------|------|------|
+| To | String | 是 | 接收短信号码,格式:国际区号+号码,例如 "8521245567****" |
+| From | String | 是 | 发送方标识(Sender ID),需根据目标国家完成相应注册 |
+| Message | String | 是 | 短信完整内容(美国、加拿大需填写报备内容) |
+| Type | String | 是 | 短信类型:OTP(验证码)/ NOTIFY(通知)/ MKT(推广) |
+| TaskId | String | 否 | 任务 ID,长度不超过 255 个字符 |
+| ValidityPeriod | Integer | 否 | 短信有效时长,单位:秒 |
+| ChannelId | String | 否 | 通道 ID |
+
+### 返回数据
+
+成功响应示例:
+
+```json
+{
+    "success": true,
+    "response_code": "OK",
+    "response_description": "The SMS Send Request was accepted",
+    "message_id": "1008030300****",
+    "request_id": "F655A8D5-B967-440B-8683-DAD6FF8DE990",
+    "segments": "1",
+    "to": "8521245567****",
+    "from": "Alicloud321",
+    "number_detail": {
+        "carrier": "CMI",
+        "country": "Hongkong,China",
+        "region": "HongKong"
+    }
+}
+```
+
+失败响应示例:
+
+```json
+{
+    "success": false,
+    "error": "错误信息",
+    "request_id": "xxx"
+}
+```
+
+## 注意事项
+
+1. **计费说明**: 接口调用成功即按条计费,发送返回"失败"同样计费
+2. **QPS 限制**: 单用户 QPS 限制为 2000 次/秒
+3. **固定配置**: 
+   - region_id 必须为 `ap-southeast-1`(新加坡)
+   - endpoint 必须为 `dysmsapi.ap-southeast-1.aliyuncs.com`
+   - API 版本为 `2018-05-01`
+4. **内容审核**: 美国、加拿大发送的内容需提前报备
+5. **账户余额**: 套餐包在账户欠费时不可使用,请在调用接口前检查账户余额及套餐包余量
+
+## API 版本说明
+
+- **API 版本**: 2018-05-01
+- **服务地址**: 新加坡(ap-southeast-1)
+- **Endpoint**: dysmsapi.ap-southeast-1.aliyuncs.com
+- **QPS 限制**: 单用户 QPS 限制为 2000 次/秒
+
+## 参考文档
+
+- [阿里云国际短信 API 文档(2018-05-01)](https://next.api.aliyun.com/api/Dysmsapi/2018-05-01/SendMessageToGlobe)
+- [错误码查询](https://error-center.aliyun.com/status/product/Dysmsapi)
+

+ 2 - 0
sendintlmsg/requirements.txt

@@ -0,0 +1,2 @@
+alibabacloud-dysmsapi20180501>=1.0.0
+

+ 224 - 0
sendintlmsg/send_international_sms.py

@@ -0,0 +1,224 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""
+阿里云国际短信发送 Demo
+
+功能说明:
+- 使用阿里云 SMS 服务发送国际短信(2018-05-01 版本)
+- 服务地址:新加坡(ap-southeast-1)
+- 支持发送到美国、加拿大、新加坡等国家/地区
+- 支持 OTP(验证码)、NOTIFY(通知)、MKT(推广)三种类型
+
+使用前准备:
+1. 安装依赖: pip install alibabacloud-dysmsapi20180501
+2. 确保已注册 Sender ID(美国、加拿大需 10 DLC 注册,新加坡需 IMDA SSIR 注册)
+
+注意: AccessKey 已直接配置在脚本中
+
+参考文档:
+https://next.api.aliyun.com/api/Dysmsapi/2018-05-01/SendMessageToGlobe
+"""
+
+import sys
+import json
+from typing import Optional, Dict, Any
+
+try:
+    from alibabacloud_dysmsapi20180501.client import Client as Dysmsapi20180501Client
+    from alibabacloud_tea_openapi import models as open_api_models
+    from alibabacloud_dysmsapi20180501 import models as dysmsapi_20180501_models
+    from alibabacloud_tea_util import models as util_models
+except ImportError:
+    print("❌ 缺少依赖包,请先安装:")
+    print("  pip install alibabacloud-dysmsapi20180501")
+    sys.exit(1)
+
+
+class InternationalSMSClient:
+    """阿里云国际短信发送客户端(2018-05-01 版本)"""
+    
+    # 固定配置(根据文档要求,不可修改)
+    REGION_ID = 'ap-southeast-1'  # 新加坡
+    ENDPOINT = 'dysmsapi.ap-southeast-1.aliyuncs.com'
+    
+    # 短信类型
+    TYPE_OTP = 'OTP'      # 验证码
+    TYPE_NOTIFY = 'NOTIFY'  # 短信通知
+    TYPE_MKT = 'MKT'      # 推广短信
+    
+    def __init__(self, access_key_id: str, access_key_secret: str):
+        """
+        初始化客户端
+        
+        Args:
+            access_key_id: 阿里云 AccessKey ID
+            access_key_secret: 阿里云 AccessKey Secret
+        """
+        # 配置客户端(直接使用 AccessKey)
+        config = open_api_models.Config(
+            access_key_id=access_key_id,
+            access_key_secret=access_key_secret
+        )
+        config.endpoint = self.ENDPOINT
+        
+        self.client = Dysmsapi20180501Client(config)
+    
+    def send_sms(
+        self,
+        to: str,
+        message: str,
+        sender_id: str,
+        sms_type: str = TYPE_OTP,
+        task_id: Optional[str] = None,
+        validity_period: Optional[int] = None,
+        channel_id: Optional[str] = None
+    ) -> Dict[str, Any]:
+        """
+        发送国际短信
+        
+        Args:
+            to: 接收短信号码,格式为国际区号+号码,例如: "8521245567****"
+            message: 短信的完整内容(美国、加拿大需要填写报备的内容)
+            sender_id: 发送方标识(Sender ID)
+                      - 支持数字+字母,含有字母标识最长11位,纯数字标识支持15位
+                      - 美国、加拿大:需填写 10 DLC 注册后运营商提供的 Sender ID
+                      - 新加坡:需填写在 IMDA 完成 SSIR 注册的 Sender ID
+            sms_type: 短信类型,可选值: OTP/NOTIFY/MKT,默认 OTP
+            task_id: 任务 ID(可选),长度不超过 255 个字符
+            validity_period: 短信有效时长,单位:秒(可选)
+            channel_id: 通道 ID(可选)
+        
+        Returns:
+            包含发送结果的字典,格式:
+            {
+                "success": bool,
+                "response_code": str,      # 短信提交状态码,如 "OK"
+                "message_id": str,        # 成功时返回消息ID
+                "request_id": str,         # 请求ID
+                "segments": str,           # 短信计费条数
+                "response_description": str,  # 状态码描述
+                "to": str,                # 接收号码
+                "from": str,              # 发送方标识
+                "number_detail": {         # 号码明细(可选)
+                    "carrier": str,       # 运营商
+                    "country": str,       # 国家
+                    "region": str         # 地区
+                },
+                "error": str              # 失败时返回错误信息
+            }
+        """
+        # 验证短信类型
+        if sms_type not in [self.TYPE_OTP, self.TYPE_NOTIFY, self.TYPE_MKT]:
+            return {
+                "success": False,
+                "error": f"无效的短信类型: {sms_type},支持的类型: OTP/NOTIFY/MKT"
+            }
+        
+        # 创建请求对象
+        request = dysmsapi_20180501_models.SendMessageToGlobeRequest()
+        request.to = to
+        request.from_ = sender_id
+        request.message = message
+        request.type = sms_type
+        
+        if task_id:
+            request.task_id = task_id
+        if validity_period is not None:
+            request.validity_period = validity_period
+        if channel_id:
+            request.channel_id = channel_id
+        
+        # 运行时选项
+        runtime = util_models.RuntimeOptions()
+        
+        try:
+            # 发送请求
+            response = self.client.send_message_to_globe_with_options(request, runtime)
+            
+            # 解析响应
+            body = response.body
+            
+            # 检查响应状态码
+            if body.response_code == 'OK':
+                # 成功
+                result = {
+                    "success": True,
+                    "response_code": body.response_code,
+                    "response_description": body.response_description or '',
+                    "message_id": body.message_id or '',
+                    "request_id": body.request_id or '',
+                    "segments": body.segments or '',
+                    "to": body.to or '',
+                    "from": body.from_ or '',
+                }
+                
+                # 号码明细(可选)
+                if body.number_detail:
+                    result["number_detail"] = {
+                        "carrier": body.number_detail.carrier or '',
+                        "country": body.number_detail.country or '',
+                        "region": body.number_detail.region or ''
+                    }
+                
+                return result
+            else:
+                # 失败
+                return {
+                    "success": False,
+                    "response_code": body.response_code or '',
+                    "response_description": body.response_description or '',
+                    "request_id": body.request_id or '',
+                    "error": body.response_description or '未知错误'
+                }
+        
+        except Exception as e:
+            return {
+                "success": False,
+                "error": f"发送失败: {str(e)}"
+            }
+
+
+def main():
+    """主函数 - 示例用法"""
+    
+    # AccessKey 配置(直接写死在脚本中)
+    ACCESS_KEY_ID = 'LTAI5tQ6iFj9XMSZygRhKL4F'
+    ACCESS_KEY_SECRET = 'eXEfRLenQfOCLWkj307jv48rBUWNHK'
+    
+    # 创建客户端
+    sms_client = InternationalSMSClient(ACCESS_KEY_ID, ACCESS_KEY_SECRET)
+    
+    # 发送验证码短信
+    print("📤 发送验证码短信")
+    print("-" * 50)
+    
+    result = sms_client.send_sms(
+        to="447900613287",  # 接收号码(国际区号+号码)
+        message="Lyrria,Your verification code is 123456. Valid for 5 minutes.",
+        sender_id="Lyrria",  # Sender ID
+        sms_type=InternationalSMSClient.TYPE_OTP
+    )
+    
+    if result["success"]:
+        print(f"✅ 发送成功!")
+        print(f"   状态码: {result.get('response_code', '')}")
+        print(f"   状态描述: {result.get('response_description', '')}")
+        print(f"   消息ID: {result.get('message_id', '')}")
+        print(f"   请求ID: {result.get('request_id', '')}")
+        print(f"   计费条数: {result.get('segments', '')}")
+        if 'number_detail' in result:
+            detail = result['number_detail']
+            print(f"   运营商: {detail.get('carrier', '')}")
+            print(f"   国家: {detail.get('country', '')}")
+            print(f"   地区: {detail.get('region', '')}")
+    else:
+        print(f"❌ 发送失败: {result.get('error', '未知错误')}")
+        if result.get('response_code'):
+            print(f"   状态码: {result.get('response_code')}")
+        if result.get('response_description'):
+            print(f"   状态描述: {result.get('response_description')}")
+
+
+if __name__ == '__main__':
+    main()
+