senweaver-sms


Namesenweaver-sms JSON
Version 0.1.2 PyPI version JSON
download
home_pagehttps://github.com/senweaver/senweaver-sms
Summary您的一站式 Python 短信发送解决方案
upload_time2025-08-26 07:38:22
maintainerNone
docs_urlNone
authorsenweaver
requires_python>=3.8
licenseNone
keywords sms message yunpian aliyun qcloud tencent senweaver
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # SenWeaver SMS

📲 您的一站式 Python 短信发送解决方案

> 告别繁琐的多平台适配!本库聚合了众多短信服务商,通过一致的调用方式、灵活的配置选项和内置的轮询策略,让您轻松实现高效、可靠的短信发送,并获得统一的响应结果用于监控。

[![PyPI version](https://badge.fury.io/py/senweaver-sms.svg)](https://badge.fury.io/py/senweaver-sms)

## 特点

1. 支持目前市面多家服务商
2. 一套写法兼容所有平台
3. 提供流畅的 Builder API,配置简单灵活
4. 内置多种服务商轮询策略、支持自定义轮询策略
5. 统一的返回值格式 (`SMSResponse`),便于日志与监控
6. 自动根据策略选择可用的服务商
7. 类型提示友好,代码健壮性高

## 平台支持

* 阿里云 (Aliyun)
* 腾讯云 (Qcloud)
* 百度智能云 (Baidu)
* 华为云 (Huawei)
* 天翼云 (Ctyun)
* UCloud
* 七牛云 (Qiniu)
* 云片 (Yunpian)
* 互亿无线 (Huyi)
* 聚合数据 (Juhe)
* 短信宝 (Smsbao)
* 持续添加中...

## 环境需求

* Python >= 3.8

## 安装

```bash
pip install senweaver-sms
```

## 使用 (推荐方式: SMSBuilder)

我们推荐使用 `SMSBuilder` 来构建和发送短信请求,它提供了链式调用和类型安全的配置方式。

```python
from senweaver_sms import SMSBuilder, PhoneNumber
from senweaver_sms.exception import GatewayErrorException, NoGatewayAvailableException

# 使用构建器配置网关
sms_request = SMSBuilder.builder() \
    .aliyun( # 添加阿里云网关
        access_key_id="YOUR_ALIYUN_ACCESS_KEY_ID",
        access_key_secret="YOUR_ALIYUN_ACCESS_KEY_SECRET",
        sign_name="你的签名"
    ) \
    .qcloud( # 添加腾讯云网关
        sdk_app_id="YOUR_TENCENT_SDK_APP_ID",
        secret_id="YOUR_TENCENT_SECRET_ID",
        secret_key="YOUR_TENCENT_SECRET_KEY",
        sign_name="你的签名"
    ) \
    .huawei( # 添加华为云网关
        app_key="YOUR_HUAWEI_APP_KEY", # 注意:华为云的app_key是控制台的APP Key
        app_secret="YOUR_HUAWEI_APP_SECRET", # 注意:华为云的app_secret是控制台的APP Secret
        channel="YOUR_HUAWEI_CHANNEL", # 短信通道号
        sign="你的签名" # 可选,不填则尝试用 channel
    ) \
    .yunpian( # 添加云片网关
        api_key="YOUR_YUNPIAN_API_KEY",
        sign="你的签名" # 可选
    ) \
    .timeout(10.0) # 设置全局请求超时时间 (秒)
    .strategy("order") # 设置网关选择策略 ('order', 'random' 或自定义)
    .debug(True) # 开启调试模式 (可选)
    .build() # 构建请求对象

# 发送短信
try:
    phone = "YOUR_PHONE_NUMBER"
    template_id = "YOUR_TEMPLATE_ID" # 不同平台模板ID可能不同
    data = {"code": "123456", "minutes": 5}
    
    print(f"向 {phone} 发送短信...")
    # send 方法参数:接收者手机号, content(可选,文本内容), template(可选,模板ID), data(可选,模板参数), gateways(可选,指定网关列表), strategy(可选,指定策略)
    response = sms_request.send(
        to=phone, 
        template=template_id, 
        data=data
    )
    
    print("\n发送成功!")
    print(f"网关: {response.gateway}")
    print(f"状态: {response.status.name}") # SUCCESS 或 FAILED
    print(f"消息ID: {response.message_id}") # 网关返回的消息标识
    print(f"原始响应: {response.raw_response}")

except (GatewayErrorException, NoGatewayAvailableException) as e:
    print(f"\n发送失败: {e}")
    if isinstance(e, GatewayErrorException):
        print(f"  错误网关: {e.gateway}")
        print(f"  错误代码: {e.code}")
        print(f"  错误详情: {e.data}")
    elif isinstance(e, NoGatewayAvailableException):
        print("  没有可用的网关成功发送短信。尝试详情:")
        if hasattr(e, 'details') and e.details and 'responses' in e.details:
            for failed_resp in e.details['responses']:
                print(f"    - 网关 {failed_resp.gateway} 失败: {failed_resp.error}")

except ValueError as e:
    print(f"\n配置或参数错误: {e}")
    
except Exception as e:
    print(f"\n发生未知错误: {e}")

# 发送国际短信 (使用PhoneNumber对象)
try:
    number = PhoneNumber('YOUR_PHONE_NUMBER', '31') # 31是荷兰的国家码
    print(f"\n向 {number.get_universal_format()} 发送国际短信...")
    response = sms_request.send(
        to=number,
        template=template_id,
        data=data
    )
    print("发送成功!")
    print(f"消息ID: {response.message_id}")
except Exception as e:
    print(f"发送失败: {e}")
    
# 批量发送 (同一内容给多人)
try:
    phones = ["YOUR_PHONE_NUMBER1", "YOUR_PHONE_NUMBER2"]
    print(f"\n向 {len(phones)} 个号码批量发送短信...")
    batch_response = sms_request.batch_send(
        to_list=phones,
        template=template_id,
        data=data
    )
    print(f"批量发送完成: {batch_response.success_count} 成功, {batch_response.failure_count} 失败")
    for resp in batch_response.get_failed_responses():
        print(f"  - {resp.phone_number} 发送失败: {resp.error}")
except Exception as e:
    print(f"批量发送失败: {e}")
```

## 网关配置 (`GatewayConfig`)

使用 `SMSBuilder` 添加网关时,可以通过特定网关的辅助方法(如 `.aliyun()`, `.qcloud()`)或通用的 `.gateway()` 方法进行配置。`GatewayConfig` 对象包含了所有可能的配置项:

* 通用认证: `app_id`, `app_key`, `app_secret`
* 通用选项: `sign`, `region`, `version`
* 通用高级: `timeout`, `ssl_verify`
* 特定网关: `invoke_id` (百度), `signature_id` (百度), `channel` (华为), `endpoint` (华为), `callback_url` (华为), `project_id` (UCloud)

详细的参数映射关系请参考 `senweaver_sms/config.py` 中的 `GatewayConfig` 类文档字符串。

## 短信内容 (`Message`)

发送短信时,可以通过 `content` 参数发送纯文本内容(适用于云片、短信宝等),或通过 `template` 和 `data` 参数使用模板发送(适用于阿里云、腾讯云等)。

```python
# 文本内容发送
sms_request.send(phone, content="【签名】您的验证码是1234")

# 模板发送
sms_request.send(phone, template="TEMPLATE_ID", data={"code": 1234})
```

`data` 参数可以是字典或列表(部分网关如腾讯云、UCloud会按顺序使用列表中的值)。

## 统一响应 (`SMSResponse` / `SMSBatchResponse`)

* **单条发送**: `send` 方法返回一个 `SMSResponse` 对象,包含:
    * `gateway` (str): 实际发送成功的网关名称。
    * `status` (SMSStatus): `SMSStatus.SUCCESS` 或 `SMSStatus.FAILED`。
    * `message_id` (Optional[str]): 网关返回的消息唯一标识 (如果可用)。
    * `raw_response` (Dict[str, Any]): 网关返回的原始响应数据。
    * `error` (Optional[SMSError]): 如果失败,包含错误代码和消息。
    * `fee` (int): 预估的计费条数。
    * `send_time` (datetime): 发送时间。
* **批量发送**: `batch_send` 方法返回一个 `SMSBatchResponse` 对象,包含一个 `SMSResponse` 列表以及成功和失败的统计。

## 异常处理

* `GatewayErrorException`: 特定网关发送失败时抛出,包含错误代码、消息和原始数据。
* `NoGatewayAvailableException`: 所有尝试的网关都发送失败时抛出。
* `NoGatewaySelectedException`: 没有配置任何可用网关时抛出。
* `InvalidArgumentException`: 配置或参数无效时抛出。
* `ValueError`: 配置验证失败时抛出。

## 自定义网关与策略

(这部分可以保持不变或根据需要简化)

...

## 贡献

欢迎提交 Pull Request 或 Issue!

## License

MIT License

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/senweaver/senweaver-sms",
    "name": "senweaver-sms",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": null,
    "keywords": "sms, message, yunpian, aliyun, qcloud, tencent, senweaver",
    "author": "senweaver",
    "author_email": null,
    "download_url": "https://files.pythonhosted.org/packages/10/fd/53b339f0bf3d96309494f13eb84d2eb494de31a75563178d2a1305ec7089/senweaver_sms-0.1.2.tar.gz",
    "platform": null,
    "description": "# SenWeaver SMS\n\n\ud83d\udcf2 \u60a8\u7684\u4e00\u7ad9\u5f0f Python \u77ed\u4fe1\u53d1\u9001\u89e3\u51b3\u65b9\u6848\n\n> \u544a\u522b\u7e41\u7410\u7684\u591a\u5e73\u53f0\u9002\u914d\uff01\u672c\u5e93\u805a\u5408\u4e86\u4f17\u591a\u77ed\u4fe1\u670d\u52a1\u5546\uff0c\u901a\u8fc7\u4e00\u81f4\u7684\u8c03\u7528\u65b9\u5f0f\u3001\u7075\u6d3b\u7684\u914d\u7f6e\u9009\u9879\u548c\u5185\u7f6e\u7684\u8f6e\u8be2\u7b56\u7565\uff0c\u8ba9\u60a8\u8f7b\u677e\u5b9e\u73b0\u9ad8\u6548\u3001\u53ef\u9760\u7684\u77ed\u4fe1\u53d1\u9001\uff0c\u5e76\u83b7\u5f97\u7edf\u4e00\u7684\u54cd\u5e94\u7ed3\u679c\u7528\u4e8e\u76d1\u63a7\u3002\n\n[![PyPI version](https://badge.fury.io/py/senweaver-sms.svg)](https://badge.fury.io/py/senweaver-sms)\n\n## \u7279\u70b9\n\n1. \u652f\u6301\u76ee\u524d\u5e02\u9762\u591a\u5bb6\u670d\u52a1\u5546\n2. \u4e00\u5957\u5199\u6cd5\u517c\u5bb9\u6240\u6709\u5e73\u53f0\n3. \u63d0\u4f9b\u6d41\u7545\u7684 Builder API\uff0c\u914d\u7f6e\u7b80\u5355\u7075\u6d3b\n4. \u5185\u7f6e\u591a\u79cd\u670d\u52a1\u5546\u8f6e\u8be2\u7b56\u7565\u3001\u652f\u6301\u81ea\u5b9a\u4e49\u8f6e\u8be2\u7b56\u7565\n5. \u7edf\u4e00\u7684\u8fd4\u56de\u503c\u683c\u5f0f (`SMSResponse`)\uff0c\u4fbf\u4e8e\u65e5\u5fd7\u4e0e\u76d1\u63a7\n6. \u81ea\u52a8\u6839\u636e\u7b56\u7565\u9009\u62e9\u53ef\u7528\u7684\u670d\u52a1\u5546\n7. \u7c7b\u578b\u63d0\u793a\u53cb\u597d\uff0c\u4ee3\u7801\u5065\u58ee\u6027\u9ad8\n\n## \u5e73\u53f0\u652f\u6301\n\n* \u963f\u91cc\u4e91 (Aliyun)\n* \u817e\u8baf\u4e91 (Qcloud)\n* \u767e\u5ea6\u667a\u80fd\u4e91 (Baidu)\n* \u534e\u4e3a\u4e91 (Huawei)\n* \u5929\u7ffc\u4e91 (Ctyun)\n* UCloud\n* \u4e03\u725b\u4e91 (Qiniu)\n* \u4e91\u7247 (Yunpian)\n* \u4e92\u4ebf\u65e0\u7ebf (Huyi)\n* \u805a\u5408\u6570\u636e (Juhe)\n* \u77ed\u4fe1\u5b9d (Smsbao)\n* \u6301\u7eed\u6dfb\u52a0\u4e2d...\n\n## \u73af\u5883\u9700\u6c42\n\n* Python >= 3.8\n\n## \u5b89\u88c5\n\n```bash\npip install senweaver-sms\n```\n\n## \u4f7f\u7528 (\u63a8\u8350\u65b9\u5f0f: SMSBuilder)\n\n\u6211\u4eec\u63a8\u8350\u4f7f\u7528 `SMSBuilder` \u6765\u6784\u5efa\u548c\u53d1\u9001\u77ed\u4fe1\u8bf7\u6c42\uff0c\u5b83\u63d0\u4f9b\u4e86\u94fe\u5f0f\u8c03\u7528\u548c\u7c7b\u578b\u5b89\u5168\u7684\u914d\u7f6e\u65b9\u5f0f\u3002\n\n```python\nfrom senweaver_sms import SMSBuilder, PhoneNumber\nfrom senweaver_sms.exception import GatewayErrorException, NoGatewayAvailableException\n\n# \u4f7f\u7528\u6784\u5efa\u5668\u914d\u7f6e\u7f51\u5173\nsms_request = SMSBuilder.builder() \\\n    .aliyun( # \u6dfb\u52a0\u963f\u91cc\u4e91\u7f51\u5173\n        access_key_id=\"YOUR_ALIYUN_ACCESS_KEY_ID\",\n        access_key_secret=\"YOUR_ALIYUN_ACCESS_KEY_SECRET\",\n        sign_name=\"\u4f60\u7684\u7b7e\u540d\"\n    ) \\\n    .qcloud( # \u6dfb\u52a0\u817e\u8baf\u4e91\u7f51\u5173\n        sdk_app_id=\"YOUR_TENCENT_SDK_APP_ID\",\n        secret_id=\"YOUR_TENCENT_SECRET_ID\",\n        secret_key=\"YOUR_TENCENT_SECRET_KEY\",\n        sign_name=\"\u4f60\u7684\u7b7e\u540d\"\n    ) \\\n    .huawei( # \u6dfb\u52a0\u534e\u4e3a\u4e91\u7f51\u5173\n        app_key=\"YOUR_HUAWEI_APP_KEY\", # \u6ce8\u610f\uff1a\u534e\u4e3a\u4e91\u7684app_key\u662f\u63a7\u5236\u53f0\u7684APP Key\n        app_secret=\"YOUR_HUAWEI_APP_SECRET\", # \u6ce8\u610f\uff1a\u534e\u4e3a\u4e91\u7684app_secret\u662f\u63a7\u5236\u53f0\u7684APP Secret\n        channel=\"YOUR_HUAWEI_CHANNEL\", # \u77ed\u4fe1\u901a\u9053\u53f7\n        sign=\"\u4f60\u7684\u7b7e\u540d\" # \u53ef\u9009\uff0c\u4e0d\u586b\u5219\u5c1d\u8bd5\u7528 channel\n    ) \\\n    .yunpian( # \u6dfb\u52a0\u4e91\u7247\u7f51\u5173\n        api_key=\"YOUR_YUNPIAN_API_KEY\",\n        sign=\"\u4f60\u7684\u7b7e\u540d\" # \u53ef\u9009\n    ) \\\n    .timeout(10.0) # \u8bbe\u7f6e\u5168\u5c40\u8bf7\u6c42\u8d85\u65f6\u65f6\u95f4 (\u79d2)\n    .strategy(\"order\") # \u8bbe\u7f6e\u7f51\u5173\u9009\u62e9\u7b56\u7565 ('order', 'random' \u6216\u81ea\u5b9a\u4e49)\n    .debug(True) # \u5f00\u542f\u8c03\u8bd5\u6a21\u5f0f (\u53ef\u9009)\n    .build() # \u6784\u5efa\u8bf7\u6c42\u5bf9\u8c61\n\n# \u53d1\u9001\u77ed\u4fe1\ntry:\n    phone = \"YOUR_PHONE_NUMBER\"\n    template_id = \"YOUR_TEMPLATE_ID\" # \u4e0d\u540c\u5e73\u53f0\u6a21\u677fID\u53ef\u80fd\u4e0d\u540c\n    data = {\"code\": \"123456\", \"minutes\": 5}\n    \n    print(f\"\u5411 {phone} \u53d1\u9001\u77ed\u4fe1...\")\n    # send \u65b9\u6cd5\u53c2\u6570\uff1a\u63a5\u6536\u8005\u624b\u673a\u53f7, content(\u53ef\u9009,\u6587\u672c\u5185\u5bb9), template(\u53ef\u9009,\u6a21\u677fID), data(\u53ef\u9009,\u6a21\u677f\u53c2\u6570), gateways(\u53ef\u9009,\u6307\u5b9a\u7f51\u5173\u5217\u8868), strategy(\u53ef\u9009,\u6307\u5b9a\u7b56\u7565)\n    response = sms_request.send(\n        to=phone, \n        template=template_id, \n        data=data\n    )\n    \n    print(\"\\n\u53d1\u9001\u6210\u529f!\")\n    print(f\"\u7f51\u5173: {response.gateway}\")\n    print(f\"\u72b6\u6001: {response.status.name}\") # SUCCESS \u6216 FAILED\n    print(f\"\u6d88\u606fID: {response.message_id}\") # \u7f51\u5173\u8fd4\u56de\u7684\u6d88\u606f\u6807\u8bc6\n    print(f\"\u539f\u59cb\u54cd\u5e94: {response.raw_response}\")\n\nexcept (GatewayErrorException, NoGatewayAvailableException) as e:\n    print(f\"\\n\u53d1\u9001\u5931\u8d25: {e}\")\n    if isinstance(e, GatewayErrorException):\n        print(f\"  \u9519\u8bef\u7f51\u5173: {e.gateway}\")\n        print(f\"  \u9519\u8bef\u4ee3\u7801: {e.code}\")\n        print(f\"  \u9519\u8bef\u8be6\u60c5: {e.data}\")\n    elif isinstance(e, NoGatewayAvailableException):\n        print(\"  \u6ca1\u6709\u53ef\u7528\u7684\u7f51\u5173\u6210\u529f\u53d1\u9001\u77ed\u4fe1\u3002\u5c1d\u8bd5\u8be6\u60c5:\")\n        if hasattr(e, 'details') and e.details and 'responses' in e.details:\n            for failed_resp in e.details['responses']:\n                print(f\"    - \u7f51\u5173 {failed_resp.gateway} \u5931\u8d25: {failed_resp.error}\")\n\nexcept ValueError as e:\n    print(f\"\\n\u914d\u7f6e\u6216\u53c2\u6570\u9519\u8bef: {e}\")\n    \nexcept Exception as e:\n    print(f\"\\n\u53d1\u751f\u672a\u77e5\u9519\u8bef: {e}\")\n\n# \u53d1\u9001\u56fd\u9645\u77ed\u4fe1 (\u4f7f\u7528PhoneNumber\u5bf9\u8c61)\ntry:\n    number = PhoneNumber('YOUR_PHONE_NUMBER', '31') # 31\u662f\u8377\u5170\u7684\u56fd\u5bb6\u7801\n    print(f\"\\n\u5411 {number.get_universal_format()} \u53d1\u9001\u56fd\u9645\u77ed\u4fe1...\")\n    response = sms_request.send(\n        to=number,\n        template=template_id,\n        data=data\n    )\n    print(\"\u53d1\u9001\u6210\u529f!\")\n    print(f\"\u6d88\u606fID: {response.message_id}\")\nexcept Exception as e:\n    print(f\"\u53d1\u9001\u5931\u8d25: {e}\")\n    \n# \u6279\u91cf\u53d1\u9001 (\u540c\u4e00\u5185\u5bb9\u7ed9\u591a\u4eba)\ntry:\n    phones = [\"YOUR_PHONE_NUMBER1\", \"YOUR_PHONE_NUMBER2\"]\n    print(f\"\\n\u5411 {len(phones)} \u4e2a\u53f7\u7801\u6279\u91cf\u53d1\u9001\u77ed\u4fe1...\")\n    batch_response = sms_request.batch_send(\n        to_list=phones,\n        template=template_id,\n        data=data\n    )\n    print(f\"\u6279\u91cf\u53d1\u9001\u5b8c\u6210: {batch_response.success_count} \u6210\u529f, {batch_response.failure_count} \u5931\u8d25\")\n    for resp in batch_response.get_failed_responses():\n        print(f\"  - {resp.phone_number} \u53d1\u9001\u5931\u8d25: {resp.error}\")\nexcept Exception as e:\n    print(f\"\u6279\u91cf\u53d1\u9001\u5931\u8d25: {e}\")\n```\n\n## \u7f51\u5173\u914d\u7f6e (`GatewayConfig`)\n\n\u4f7f\u7528 `SMSBuilder` \u6dfb\u52a0\u7f51\u5173\u65f6\uff0c\u53ef\u4ee5\u901a\u8fc7\u7279\u5b9a\u7f51\u5173\u7684\u8f85\u52a9\u65b9\u6cd5\uff08\u5982 `.aliyun()`, `.qcloud()`\uff09\u6216\u901a\u7528\u7684 `.gateway()` \u65b9\u6cd5\u8fdb\u884c\u914d\u7f6e\u3002`GatewayConfig` \u5bf9\u8c61\u5305\u542b\u4e86\u6240\u6709\u53ef\u80fd\u7684\u914d\u7f6e\u9879\uff1a\n\n* \u901a\u7528\u8ba4\u8bc1: `app_id`, `app_key`, `app_secret`\n* \u901a\u7528\u9009\u9879: `sign`, `region`, `version`\n* \u901a\u7528\u9ad8\u7ea7: `timeout`, `ssl_verify`\n* \u7279\u5b9a\u7f51\u5173: `invoke_id` (\u767e\u5ea6), `signature_id` (\u767e\u5ea6), `channel` (\u534e\u4e3a), `endpoint` (\u534e\u4e3a), `callback_url` (\u534e\u4e3a), `project_id` (UCloud)\n\n\u8be6\u7ec6\u7684\u53c2\u6570\u6620\u5c04\u5173\u7cfb\u8bf7\u53c2\u8003 `senweaver_sms/config.py` \u4e2d\u7684 `GatewayConfig` \u7c7b\u6587\u6863\u5b57\u7b26\u4e32\u3002\n\n## \u77ed\u4fe1\u5185\u5bb9 (`Message`)\n\n\u53d1\u9001\u77ed\u4fe1\u65f6\uff0c\u53ef\u4ee5\u901a\u8fc7 `content` \u53c2\u6570\u53d1\u9001\u7eaf\u6587\u672c\u5185\u5bb9\uff08\u9002\u7528\u4e8e\u4e91\u7247\u3001\u77ed\u4fe1\u5b9d\u7b49\uff09\uff0c\u6216\u901a\u8fc7 `template` \u548c `data` \u53c2\u6570\u4f7f\u7528\u6a21\u677f\u53d1\u9001\uff08\u9002\u7528\u4e8e\u963f\u91cc\u4e91\u3001\u817e\u8baf\u4e91\u7b49\uff09\u3002\n\n```python\n# \u6587\u672c\u5185\u5bb9\u53d1\u9001\nsms_request.send(phone, content=\"\u3010\u7b7e\u540d\u3011\u60a8\u7684\u9a8c\u8bc1\u7801\u662f1234\")\n\n# \u6a21\u677f\u53d1\u9001\nsms_request.send(phone, template=\"TEMPLATE_ID\", data={\"code\": 1234})\n```\n\n`data` \u53c2\u6570\u53ef\u4ee5\u662f\u5b57\u5178\u6216\u5217\u8868\uff08\u90e8\u5206\u7f51\u5173\u5982\u817e\u8baf\u4e91\u3001UCloud\u4f1a\u6309\u987a\u5e8f\u4f7f\u7528\u5217\u8868\u4e2d\u7684\u503c\uff09\u3002\n\n## \u7edf\u4e00\u54cd\u5e94 (`SMSResponse` / `SMSBatchResponse`)\n\n* **\u5355\u6761\u53d1\u9001**: `send` \u65b9\u6cd5\u8fd4\u56de\u4e00\u4e2a `SMSResponse` \u5bf9\u8c61\uff0c\u5305\u542b\uff1a\n    * `gateway` (str): \u5b9e\u9645\u53d1\u9001\u6210\u529f\u7684\u7f51\u5173\u540d\u79f0\u3002\n    * `status` (SMSStatus): `SMSStatus.SUCCESS` \u6216 `SMSStatus.FAILED`\u3002\n    * `message_id` (Optional[str]): \u7f51\u5173\u8fd4\u56de\u7684\u6d88\u606f\u552f\u4e00\u6807\u8bc6 (\u5982\u679c\u53ef\u7528)\u3002\n    * `raw_response` (Dict[str, Any]): \u7f51\u5173\u8fd4\u56de\u7684\u539f\u59cb\u54cd\u5e94\u6570\u636e\u3002\n    * `error` (Optional[SMSError]): \u5982\u679c\u5931\u8d25\uff0c\u5305\u542b\u9519\u8bef\u4ee3\u7801\u548c\u6d88\u606f\u3002\n    * `fee` (int): \u9884\u4f30\u7684\u8ba1\u8d39\u6761\u6570\u3002\n    * `send_time` (datetime): \u53d1\u9001\u65f6\u95f4\u3002\n* **\u6279\u91cf\u53d1\u9001**: `batch_send` \u65b9\u6cd5\u8fd4\u56de\u4e00\u4e2a `SMSBatchResponse` \u5bf9\u8c61\uff0c\u5305\u542b\u4e00\u4e2a `SMSResponse` \u5217\u8868\u4ee5\u53ca\u6210\u529f\u548c\u5931\u8d25\u7684\u7edf\u8ba1\u3002\n\n## \u5f02\u5e38\u5904\u7406\n\n* `GatewayErrorException`: \u7279\u5b9a\u7f51\u5173\u53d1\u9001\u5931\u8d25\u65f6\u629b\u51fa\uff0c\u5305\u542b\u9519\u8bef\u4ee3\u7801\u3001\u6d88\u606f\u548c\u539f\u59cb\u6570\u636e\u3002\n* `NoGatewayAvailableException`: \u6240\u6709\u5c1d\u8bd5\u7684\u7f51\u5173\u90fd\u53d1\u9001\u5931\u8d25\u65f6\u629b\u51fa\u3002\n* `NoGatewaySelectedException`: \u6ca1\u6709\u914d\u7f6e\u4efb\u4f55\u53ef\u7528\u7f51\u5173\u65f6\u629b\u51fa\u3002\n* `InvalidArgumentException`: \u914d\u7f6e\u6216\u53c2\u6570\u65e0\u6548\u65f6\u629b\u51fa\u3002\n* `ValueError`: \u914d\u7f6e\u9a8c\u8bc1\u5931\u8d25\u65f6\u629b\u51fa\u3002\n\n## \u81ea\u5b9a\u4e49\u7f51\u5173\u4e0e\u7b56\u7565\n\n(\u8fd9\u90e8\u5206\u53ef\u4ee5\u4fdd\u6301\u4e0d\u53d8\u6216\u6839\u636e\u9700\u8981\u7b80\u5316)\n\n...\n\n## \u8d21\u732e\n\n\u6b22\u8fce\u63d0\u4ea4 Pull Request \u6216 Issue\uff01\n\n## License\n\nMIT License\n",
    "bugtrack_url": null,
    "license": null,
    "summary": "\u60a8\u7684\u4e00\u7ad9\u5f0f Python \u77ed\u4fe1\u53d1\u9001\u89e3\u51b3\u65b9\u6848",
    "version": "0.1.2",
    "project_urls": {
        "Bug Reports": "https://github.com/senweaver/senweaver-sms/issues",
        "Homepage": "https://github.com/senweaver/senweaver-sms",
        "Source": "https://github.com/senweaver/senweaver-sms"
    },
    "split_keywords": [
        "sms",
        " message",
        " yunpian",
        " aliyun",
        " qcloud",
        " tencent",
        " senweaver"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "b23f1e807481229c04e79b25991cb8954db2ce9409617680ddf4d0b9d8bd7163",
                "md5": "cd433ef496029f69bf6d07c982b1688b",
                "sha256": "ba57b5b279b37765c8f0ede4dc61105e3790a2fe0080dac5c98cf19c1af9cec2"
            },
            "downloads": -1,
            "filename": "senweaver_sms-0.1.2-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "cd433ef496029f69bf6d07c982b1688b",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8",
            "size": 50062,
            "upload_time": "2025-08-26T07:38:21",
            "upload_time_iso_8601": "2025-08-26T07:38:21.178311Z",
            "url": "https://files.pythonhosted.org/packages/b2/3f/1e807481229c04e79b25991cb8954db2ce9409617680ddf4d0b9d8bd7163/senweaver_sms-0.1.2-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "10fd53b339f0bf3d96309494f13eb84d2eb494de31a75563178d2a1305ec7089",
                "md5": "d5d04a87fe963151609c9c9c8920867f",
                "sha256": "dfe1c39fb6e738bef36d2fa0051137fc710f95142f846e5c8145bd2ed4b97cd6"
            },
            "downloads": -1,
            "filename": "senweaver_sms-0.1.2.tar.gz",
            "has_sig": false,
            "md5_digest": "d5d04a87fe963151609c9c9c8920867f",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 32969,
            "upload_time": "2025-08-26T07:38:22",
            "upload_time_iso_8601": "2025-08-26T07:38:22.273889Z",
            "url": "https://files.pythonhosted.org/packages/10/fd/53b339f0bf3d96309494f13eb84d2eb494de31a75563178d2a1305ec7089/senweaver_sms-0.1.2.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-08-26 07:38:22",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "senweaver",
    "github_project": "senweaver-sms",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "senweaver-sms"
}
        
Elapsed time: 0.44116s