Skill: volcengine-skills/byted-sms-sender (version 1.0.0)

--- name: byted-sms-sender version: 1.2.0 author: volcengine-sms-team description: 火山引擎短信服务管理工具。在需要使用云通信能力，包括发送短信，查询消息组，模板信息，发送详情，状态以及整体发送统计时，可以使用这个能力。 homepage: https://www.volcengine.com/docs/6361/66704?lang=zh --- # Byted SMS Sender 火山引擎短信服务 API,版本 2026-01-01 ## 何时使用 当用户有以下需求时,使用本 skill: **发送短信场景:** - 需要发送验证码短信 - 需要发送通知类短信 - 需要发送营销类短信 - 用户说"发短信""发送验证码""发通知"时 **查询场景:** - 需要查询可用的消息组(子账号) - 需要查询已审核通过的短信签名 - 需要查询已审核通过的短信模板 - 需要查询短信发送记录 - 需要查询发送统计(成功率等) ## 使用前检查 检查是否已配置以下凭证: - `ARK_SKILL_API_KEY` - API 密钥 - `ARK_SKILL_API_BASE` - API 基础地址 这些凭证由 **ArkClaw** 预先配置在终端环境中，配置文件位置: `/root/.openclaw/.env` 检查方式: ```bash echo $ARK_SKILL_API_KEY echo $ARK_SKILL_API_BASE ``` 如果缺少凭证: 1. 检查配置文件 `/root/.openclaw/.env` 是否存在 2. 如果仍然找不到，请联系 **oncall** 获取帮助 ## 6个接口说明 ### 1. send\_sms - 发送短信 **场景:** 用户需要发送验证码、通知、营销短信 **使用方式:** ```bash python3 scripts/volc_sms.py send_sms \ --sub-account "消息组ID" \ --signature "签名" \ --template-id "模板ID" \ --mobiles "手机号" \ --template-param '{"code":"123456"}' ``` **参数说明:** - `--sub-account`: 消息组ID(必填),从 list\_sub\_account 获取 - `--signature`: 短信签名(必填),从 list\_signature 获取 - `--template-id`: 模板ID(必填),从 list\_sms\_template 获取 - `--mobiles`: 手机号(必填),多个用逗号分隔 - `--template-param`: 模板参数(可选),JSON格式 ### 2. list\_sub\_account - 查询消息组 **场景:** 需要知道可以用哪个消息组发送短信 **使用方式:** ```bash python3 scripts/volc_sms.py list_sub_account ``` **参数说明:** - `--sub-account-name`: 可选,按名称模糊搜索 ### 3. list\_signature - 查询签名 **场景:** 需要知道可以用哪个签名,或者查询签名是否审核通过 **使用方式:** ```bash python3 scripts/volc_sms.py list_signature --signature "火山引擎" ``` **参数说明:** - `--signature`: 可选,按签名模糊搜索 - `--sub-accounts`: 可选,按子账号过滤 - `--page`: 页码,默认1 - `--page-size`: 每页数量,默认20 ### 4. list\_sms\_template - 查询模板 **场景:** 需要知道可以用哪个模板,或者查询模板参数 **使用方式:** ```bash python3 scripts/volc_sms.py list_sms_template --signatures "火山引擎" ``` **参数说明:** - `--template-id`: 可选,按模板ID模糊搜索 - `--signatures`: 可选,按签名过滤 - `--sub-accounts`: 可选,按子账号过滤 - `--page`: 页码,默认1 - `--page-size`: 每页数量,默认20 ### 5. list\_sms\_send\_log - 查询发送记录 **场景:** 需要查看某条短信的发送状态,或批量查询发送历史 **使用方式:** ```bash python3 scripts/volc_sms.py list_sms_send_log \ --sub-account "消息组ID" \ --from-time 1773113285 \ --to-time 1773213285 ``` **参数说明:** - `--sub-account`: 必填,消息组ID - `--from-time`: 开始时间戳(秒) - `--to-time`: 结束时间戳(秒) - `--mobile`: 可选,按手机号过滤 - `--template-id`: 可选,按模板ID过滤 - `--signature`: 可选,按签名过滤 - `--message-id`: 可选,按消息ID精确查询 - `--page`: 页码,默认1 - `--page-size`: 每页数量,默认100 ### 6. list\_total\_send\_count\_stat - 查询发送统计 **场景:** 需要查看发送成功率、接收成功率等统计信息 **使用方式:** ```bash python3 scripts/volc_sms.py list_total_send_count_stat \ --start-time 1773113285 \ --end-time 1773213285 ``` **参数说明:** - `--start-time`: 必填,开始时间戳(秒) - `--end-time`: 必填,结束时间戳(秒) - `--sub-account`: 可选,按消息组过滤 - `--channel-type`: 可选,通道类型 - `--signature`: 可选,按签名过滤 - `--template-id`: 可选,按模板ID过滤 **返回字段:** - TotalSendCount: 总发送数 - TotalSendSuccessCount: 发送成功数 - TotalSendSuccessRate: 发送成功率 - TotalReceiptSuccessCount: 接收成功数 - TotalReceiptSuccessRate: 接收成功率 ## 典型使用流程 ### 第一次发送短信 1. **查询可用的消息组** ```bash python3 scripts/volc_sms.py list_sub_account ``` 2. **查询可用的签名** ```bash python3 scripts/volc_sms.py list_signature ``` 3. **查询可用的模板** ```bash python3 scripts/volc_sms.py list_sms_template --signatures "火山引擎" ``` 4. **发送短信** ```bash python3 scripts/volc_sms.py send_sms \ --sub-account "xxxx" \ --signature "xxx" \ --template-id "ST_xxxx" \ --mobiles "188xxxxxxx8" \ --template-param '{"code":"888888"}' ``` ### 查询发送状态 ```bash python3 scripts/volc_sms.py list_sms_send_log \ --sub-account "77da1acf" \ --from-time 1773113285 \ --to-time 1773213285 ``` ## 常见错误码 - `RE:0001`: 账号短信服务未开通 - `RE:0003`: 子账号不存在(消息组ID错误) - `RE:0004`: 签名错误(签名不存在或未审核通过) - `RE:0005`: 模板错误(模板不存在或未审核通过) - `RE:0006`: 手机号格式错误 - `RE:0010`: 账号欠费 - `ZJ10200`: 请求参数错误 ## 注意事项 1. **签名和模板**: 必须使用已审核通过的签名和模板 2. **手机号格式**: - 国内短信: 11位手机号或 +86开头 - 国际短信: 必须包含国际区号,符合 E.164 标准 3. **批量限制**: 单次最多200个手机号 4. **签名子账号匹配**: 签名和消息组需要匹配,可从 list\_signature 的 SubAccounts 字段确认 5. **模板签名匹配**: 模板和签名需要匹配,可从 list\_sms\_template 的 Signature 字段确认 ## 故障排查 - 缺少凭证: 检查 `/root/.openclaw/.env` 文件，如仍找不到请联系 oncall - 发送失败: 先用 list\_sub\_account、list\_signature、list\_sms\_template 确认参数正确 - 鉴权失败: 检查自己配置的 AK/SK 是否开通正确 - 权限错误: 检查凭证是否正确，如问题持续请联系 oncall - 欠费错误: 请联系 oncall 处理

path: scripts/volc_sms.py
content: # Copyright (c) 2025 Beijing Volcano Engine Technology Co., Ltd. and/or its affiliates.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

#!/usr/bin/env python3
"""火山引擎短信发送 API - openclaw专用接口 (2026-01-01).

Version: 2026-01-01
分组: openclaw专用

6个接口:
    - send_sms: 发送短信
    - list_sms_send_log: 查询发送记录
    - list_sub_account: 查询消息组列表
    - list_signature: 查询签名列表
    - list_sms_template: 查询模板列表
    - list_total_send_count_stat: 查询发送统计

Usage:
    python volc_sms.py <action> [options]

Examples:
    # 发送短信
    python volc_sms.py send_sms \
        --sub-account 77da1acf --signature "火山引擎" --template-id "ST_xxx" \
        --mobiles "13800138000" --template-param '{"code":"123456"}'

    # 查询消息组
    python volc_sms.py list_sub_account

    # 查询签名
    python volc_sms.py list_signature --signature "火山引擎"

    # 查询模板
    python volc_sms.py list_sms_template --signatures "火山引擎"

    # 查询发送记录
    python volc_sms.py list_sms_send_log \
        --sub-account 77da1acf --from-time 1773113285 --to-time 1773213285

    # 查询发送统计
    python volc_sms.py list_total_send_count_stat \
        --start-time 1773113285 --end-time 1773213285
"""

import argparse
import json
import os
import sys

SERVICE = "volcSMS"
VERSION = "2026-01-01"


def get_credentials() -> tuple:
    [REDACTED_SECRET]"ARK_SKILL_API_KEY")
    api_base = os.getenv("ARK_SKILL_API_BASE")
    if not api_key or not api_base:
        raise ValueError(
            "未找到凭证，请设置环境变量 ARK_SKILL_API_KEY 和 ARK_SKILL_API_BASE\n"
            "配置文件位置: /root/.openclaw/.env\n"
            "示例:\n"
            "  ARK_SKILL_API_KEY=sk-xxxx\n"
            "  ARK_SKILL_API_BASE=http://xxx"
        )
    return api_key, api_base


def call_api(action: str, body: dict) -> dict:
    try:
        import requests
    except ImportError:
        print("Error: requests not installed. Run: pip install requests", file=sys.stderr)
        sys.exit(1)

    api_key, api_base = get_credentials()
    
    api_base = api_base.rstrip("/")
    
    url = f"{api_base}?Action={action}&Version={VERSION}"
    
    headers = {
        "Content-Type": "application/json",
        "ServiceName": SERVICE,
        "Authorization": f"Bearer {api_key}",
    }
    
    body_str = json.dumps(body, ensure_ascii=False)
    
    response = requests.post(url, headers=headers, data=body_str.encode("utf-8"), timeout=30)
    response.raise_for_status()
    return response.json()


def send_sms(args) -> dict:
    """发送短信 (SendSmsForAgent).

    Request:
        "Account": "",
        "SubAccount": "",
        "Signature": "",
        "TemplateId": "",
        "Mobiles": "",
        "TemplateParam": ""

    Response:
        "MessageIds": ["xxx", "xxx"]
    """
    body = {
        "SubAccount": args.sub_account,
        "Signature": args.signature,
        "TemplateId": args.template_id,
        "Mobiles": args.mobiles,
    }

    if args.account:
        body["Account"] = args.account
    if args.template_param:
        body["TemplateParam"] = args.template_param

    return call_api("SendSmsForAgent", body)


def list_sms_send_log(args) -> dict:
    """查询发送记录 (ListSmsSendLogForAgent).

    Request:
        "SubAccount": "",
        "FromTime": 1773113285,
        "ToTime": 1773113285,
        "Mobile": "",
        "TemplateId": "",
        "Signature": "",
        "MessageId": "",
        "Page": 1,
        "PageSize": 100

    Response:
        "Total": 123,
        "List": [...]
    """
    body = {
        "SubAccount": args.sub_account,
        "Page": args.page,
        "PageSize": args.page_size,
    }

    if args.from_time:
        body["FromTime"] = args.from_time
    if args.to_time:
        body["ToTime"] = args.to_time
    if args.mobile:
        body["Mobile"] = args.mobile
    if args.template_id:
        body["TemplateId"] = args.template_id
    if args.signature:
        body["Signature"] = args.signature
    if args.message_id:
        body["MessageId"] = args.message_id

    return call_api("ListSmsSendLogForAgent", body)


def list_sub_account(args) -> dict:
    """查询消息组列表 (ListSubAccountForAgent).

    说明: 只返回审核通过的消息组

    Request:
        "SubAccountName": ""

    Response:
        "Total": 123,
        "List": [{"SubAccountName": "", "SubAccount": ""}]
    """
    body = {}
    if args.sub_account_name:
        body["SubAccountName"] = args.sub_account_name
    return call_api("ListSubAccountForAgent", body)


def list_signature(args) -> dict:
    """查询签名列表 (ListSignatureForAgent).

    说明: 返回审核通过的签名

    Request:
        "Signature": "qm",
        "SubAccounts": ["a", "b"],
        "Page": 1,
        "PageSize": 2

    Response:
        "Total": 100,
        "List": [{"Signature": "", "Description": "", "Status": 1, ...}]
    """
    body = {
        "Page": args.page,
        "PageSize": args.page_size,
    }

    if args.signature:
        body["Signature"] = args.signature
    if args.sub_accounts:
        body["SubAccounts"] = args.sub_accounts.split(",")

    return call_api("ListSignatureForAgent", body)


def list_sms_template(args) -> dict:
    """查询模板列表 (ListSmsTemplateForAgent).

    说明: 返回审核通过的模板信息

    Request:
        "TemplateId": "qm",
        "SubAccounts": ["a", "b"],
        "Signatures": ["aqm", "bqm"],
        "Page": 1,
        "PageSize": 2

    Response:
        "Total": 100,
        "List": [{"TemplateId": "", "SecondTemplateId": "", ...}]
    """
    body = {
        "Page": args.page,
        "PageSize": args.page_size,
    }

    if args.template_id:
        body["TemplateId"] = args.template_id
    if args.sub_accounts:
        body["SubAccounts"] = args.sub_accounts.split(",")
    if args.signatures:
        body["Signatures"] = args.signatures.split(",")

    return call_api("ListSmsTemplateForAgent", body)


def list_total_send_count_stat(args) -> dict:
    """查询发送统计 (ListTotalSendCountStatForAgent).

    Request:
        "StartTime": 16934224000,
        "EndTime": 16934224000,
        "SubAccount": "",
        "ChannelType": "",
        "Signature": "",
        "TemplateId": ""

    Response:
        "TotalSendCount": 1122,
        "TotalSendSuccessCount": 120,
        "TotalAllSendCount": 123,
        "TotalReceiptSuccessCount": 123,
        "TotalReceiptFailureCount": 123,
        "TotalSendSuccessRate": 0.87,
        "TotalReceiptSuccessRate": 0.99
    """
    body = {
        "StartTime": args.start_time,
        "EndTime": args.end_time,
    }

    if args.sub_account:
        body["SubAccount"] = args.sub_account
    if args.channel_type:
        body["ChannelType"] = args.channel_type
    if args.signature:
        body["Signature"] = args.signature
    if args.template_id:
        body["TemplateId"] = args.template_id

    return call_api("ListTotalSendCountStatForAgent", body)


def main():
    parser = argparse.ArgumentParser(
        description="火山引擎短信发送 API (openclaw专用)\nVersion: 2026-01-01",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog=__doc__
    )
    parser.add_argument("action", help="操作: send_sms, list_sms_send_log, list_sub_account, list_signature, list_sms_template, list_total_send_count_stat")

    # send_sms 参数
    parser.add_argument("--account", help="账号")
    parser.add_argument("--sub-account", help="子账号/消息组ID")
    parser.add_argument("--signature", help="短信签名")
    parser.add_argument("--template-id", help="短信模板ID")
    parser.add_argument("--mobiles", help="手机号(逗号分隔)")
    parser.add_argument("--template-param", help="模板参数JSON")

    # list_sms_send_log 参数
    parser.add_argument("--from-time", type=int, help="开始时间戳")
    parser.add_argument("--to-time", type=int, help="结束时间戳")
    parser.add_argument("--mobile", help="手机号")
    parser.add_argument("--message-id", help="消息ID")
    parser.add_argument("--page", type=int, default=1, help="页码")
    parser.add_argument("--page-size", type=int, default=100, help="每页数量")

    # list_sub_account 参数
    parser.add_argument("--sub-account-name", help="消息组名称(模糊匹配)")

    # list_signature 参数
    parser.add_argument("--sub-accounts", help="子账号列表(逗号分隔)")

    # list_sms_template 参数
    parser.add_argument("--signatures", help="签名列表(逗号分隔)")

    # list_total_send_count_stat 参数
    parser.add_argument("--start-time", type=int, help="开始时间戳")
    parser.add_argument("--end-time", type=int, help="结束时间戳")
    parser.add_argument("--channel-type", help="通道类型")

    args = parser.parse_args()

    action_map = {
        "send_sms": send_sms,
        "list_sms_send_log": list_sms_send_log,
        "list_sub_account": list_sub_account,
        "list_signature": list_signature,
        "list_sms_template": list_sms_template,
        "list_total_send_count_stat": list_total_send_count_stat,
    }

    if args.action not in action_map:
        print(f"Error: 未知的操作 {args.action}", file=sys.stderr)
        print(f"支持的操作为: {', '.join(action_map.keys())}", file=sys.stderr)
        sys.exit(1)

    try:
        result = action_map[args.action](args)
    except ValueError as exc:
        print(f"Error: {exc}", file=sys.stderr)
        sys.exit(1)
    except Exception as exc:
        response = getattr(exc, "response", None)
        if response is not None:
            print(f"HTTP Error: {exc}", file=sys.stderr)
            response_text = getattr(response, "text", None)
            if response_text:
                print(response_text, file=sys.stderr)
        else:
            print(f"Error: {exc}", file=sys.stderr)
        sys.exit(1)

    print(json.dumps(result, indent=2, ensure_ascii=False))


if __name__ == "__main__":
    main()


path: references/setup-guide.md
content: # Byted SMS Sender — 凭证配置指南

本文档说明 Byted SMS Sender 所需的 API 凭证配置。

## 凭证说明

本技能使用以下两个环境变量进行认证：

- `ARK_SKILL_API_KEY` - API 密钥（Bearer Token）
- `ARK_SKILL_API_BASE` - API 基础地址



```bash
cat /root/.openclaw/.env
```

### 凭证示例

```
ARK_SKILL_API_KEY=sk-xxxx
ARK_SKILL_API_BASE=http://xxx
```

## 凭证缺失处理

如果检查后发现凭证缺失：

1. 确认配置文件 `/root/.openclaw/.env` 是否存在
2. 确认环境变量是否正确加载
3. 如果仍然找不到，请联系 **oncall** 获取帮助

## 验证配置

```bash
cd skills/byted-sms-sender
python3 scripts/volc_sms.py list_sub_account
```

如果返回消息组列表，说明配置正确。

## 认证方式说明

所有 API 请求使用 Bearer Token 认证，请求格式如下：

```bash
curl -X POST 'https://<ARK_SKILL_API_BASE>?Action=ListSubAccountForAgent&Version=2026-01-01' \
  -H 'Content-Type: application/json' \
  -H 'ServiceName: volcSMS' \
  -H 'Authorization: Bearer <ARK_SKILL_API_KEY>' \
  -d '{}'
```

### 请求头说明

| Header | 说明 |
|--------|------|
| `Content-Type` | 固定为 `application/json` |
| `ServiceName` | 固定为 `volcSMS` |
| `Authorization` | 格式为 `Bearer <ARK_SKILL_API_KEY>` |

## 常见问题

### Q: 环境变量未生效怎么办?

A: 检查以下几点：
1. 确认配置文件路径正确：`/root/.openclaw/.env`
2. 确认环境变量名称正确（区分大小写）
3. 尝试重新加载配置：`source /root/.openclaw/.env`

### Q: 找不到凭证怎么办?

A: 凭证由 ArkClaw 预先配置，如果找不到请联系 oncall 获取帮助。

### Q: API 调用返回认证错误?

A: 检查：
1. 自己配置的 AK/SK 是否正确
2. 凭证是否已过期，如过期请联系 oncall


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (CRITICAL)