Twilio通信Skill twilio-communications

这个技能用于通过Twilio API实现短信发送、语音通话、WhatsApp消息和用户验证(2FA)等通信功能。涵盖从简单通知到复杂IVR系统和多通道认证的全方位应用。关键考虑包括合规性、速率限制和错误处理,适用于需要集成通信服务的开发项目。关键词:Twilio、短信、语音通话、WhatsApp、用户验证、2FA、IVR、API集成、合规、错误处理、通信服务。

后端开发 0 次安装 0 次浏览 更新于 3/21/2026

name: twilio-communications description: “使用Twilio构建通信功能:短信消息、语音通话、WhatsApp商业API和用户验证(2FA)。涵盖从简单通知到复杂IVR系统和多通道认证的全方位应用。关键关注合规性、速率限制和错误处理。使用场景:twilio、发送短信、文本消息、语音通话、电话验证。” source: vibeship-spawner-skills (Apache 2.0)

Twilio通信

模式

短信发送模式

使用Twilio发送短信的基本模式。 处理基础事项:电话号码格式化、消息传递和传递状态回调。

关键考虑:

  • 电话号码必须为E.164格式(+1234567890)
  • 默认速率限制:每秒80条消息(MPS)
  • 超过160字符的消息会被分割(成本更高)
  • 运营商过滤可能屏蔽消息(尤其是美国号码)

使用时机:[‘向用户发送通知’, ‘事务性消息(订单确认、发货)’, ‘警报和提醒’]

from twilio.rest import Client
from twilio.base.exceptions import TwilioRestException
import os
import re

class TwilioSMS:
    """
    短信发送,带有适当的错误处理和验证。
    """

    def __init__(self):
        self.client = Client(
            os.environ["TWILIO_ACCOUNT_SID"],
            os.environ["TWILIO_AUTH_TOKEN"]
        )
        self.from_number = os.environ["TWILIO_PHONE_NUMBER"]

    def validate_e164(self, phone: str) -> bool:
        """验证电话号码是否为E.164格式。"""
        pattern = r'^\+[1-9]\d{1,14}$'
        return bool(re.match(pattern, phone))

    def send_sms(
        self,
        to: str,
        body: str,
        status_callback: str = None
    ) -> dict:
        """
        发送短信消息。

        参数:
            to: 收件人电话号码,E.164格式
            body: 消息文本(160字符=1个分段)
            status_callback: 传递状态Webhook的URL

        返回:
            消息SID和状态
        """
        # 验证电话号码格式
        if not self.validate_e164(to):
            return {
                "success": False,
                "error": "电话号码必须为E.164格式(+1234567890)"
            }

        # 检查消息长度(警告分段)
        segment_count = (len(body) + 159) // 160
        if segment_count > 1:
            print(f"警告:消息将作为{segment_count}个分段发送")

        try:
            message = self.client.messages.create(
                to=to,
                from_=self.from_number,
                body=body,
                status_callback=status_callback
            )

            return {
                "success": True,
                "message_sid": message.sid,
                "status": message.status,
                "segments": segment_count
            }

        except TwilioRestException as e:
            return self._handle_error(e)

    def _handle_error(self, error: Twilio

Twilio验证模式(2FA/OTP)

使用Twilio验证进行电话号码验证和2FA。 处理代码生成、传递、速率限制和防欺诈。

相比自制OTP的优势:

  • Twilio管理代码生成和过期
  • 内置防欺诈(已为客户节省8200万+美元,阻止7.47亿次尝试)
  • 自动处理速率限制
  • 多通道:短信、语音、邮件、推送、WhatsApp

Google发现短信2FA阻止了“100%的自动化机器人、96%的批量网络钓鱼攻击和76%的目标攻击。”

使用时机:[‘用户注册时的电话号码验证’, ‘双因素认证(2FA)’, ‘密码重置验证’, ‘高价值交易确认’]

from twilio.rest import Client
from twilio.base.exceptions import TwilioRestException
import os
from enum import Enum
from typing import Optional

class VerifyChannel(Enum):
    SMS = "sms"
    CALL = "call"
    EMAIL = "email"
    WHATSAPP = "whatsapp"

class TwilioVerify:
    """
    使用Twilio验证进行电话验证。
    永远不要存储OTP代码——Twilio处理它。
    """

    def __init__(self, verify_service_sid: str = None):
        self.client = Client(
            os.environ["TWILIO_ACCOUNT_SID"],
            os.environ["TWILIO_AUTH_TOKEN"]
        )
        # 首先在Twilio控制台中创建验证服务
        self.service_sid = verify_service_sid or os.environ["TWILIO_VERIFY_SID"]

    def send_verification(
        self,
        to: str,
        channel: VerifyChannel = VerifyChannel.SMS,
        locale: str = "en"
    ) -> dict:
        """
        向电话/邮箱发送验证码。

        参数:
            to: 电话号码(E.164)或邮箱
            channel: 短信、通话、邮件或whatsapp
            locale: 消息的语言代码

        返回:
            验证状态
        """
        try:
            verification = self.client.verify \
                .v2 \
                .services(self.service_sid) \
                .verifications \
                .create(
                    to=to,
                    channel=channel.value,
                    locale=locale
                )

            return {
                "success": True,
                "status": verification.status,  # "pending"
                "channel": channel.value,
                "valid": verification.valid
            }

        except TwilioRestException as e:
            return self._handle_verify_error(e)

    def check_verification(self, to: str, code: str) -> dict:
        """
        检查验证码是否正确。

        参数:
            to: 接收代码的电话号码或邮箱
            code: 用户输入的代码

        R

TwiML IVR模式

使用TwiML构建交互式语音响应(IVR)系统。 TwiML(Twilio标记语言)是XML,告诉Twilio在接收通话时该做什么。

核心TwiML动词:

  • <Say>: 文本转语音
  • <Play>: 播放音频文件
  • <Gather>: 收集键盘/语音输入
  • <Dial>: 连接到另一个号码
  • <Record>: 记录呼叫者语音
  • <Redirect>: 重定向到另一个TwiML端点

关键洞察:Twilio向您的Webhook发出HTTP请求,您返回TwiML,Twilio执行它。无状态,因此使用URL参数或会话。

使用时机:[‘电话菜单系统(按1联系销售…)’, ‘自动化客户支持’, ‘带有确认的预约提醒’, ‘语音邮件系统’]

from flask import Flask, request, Response
from twilio.twiml.voice_response import VoiceResponse, Gather
from twilio.request_validator import RequestValidator
import os

app = Flask(__name__)

def validate_twilio_request(f):
    """装饰器,验证请求是否来自Twilio。"""
    def wrapper(*args, **kwargs):
        validator = RequestValidator(os.environ["TWILIO_AUTH_TOKEN"])

        # 获取请求详情
        url = request.url
        params = request.form.to_dict()
        signature = request.headers.get("X-Twilio-Signature", "")

        if not validator.validate(url, params, signature):
            return "无效请求", 403

        return f(*args, **kwargs)
    wrapper.__name__ = f.__name__
    return wrapper

@app.route("/voice/incoming", methods=["POST"])
@validate_twilio_request
def incoming_call():
    """处理来电,带有IVR菜单。"""
    response = VoiceResponse()

    # 收集数字,带超时
    gather = Gather(
        num_digits=1,
        action="/voice/menu-selection",
        method="POST",
        timeout=5
    )
    gather.say(
        "欢迎来到Acme公司。"
        "按1键联系销售。"
        "按2键联系支持。"
        "按3键留言。"
    )
    response.append(gather)

    # 如果没有输入,重复
    response.redirect("/voice/incoming")

    return Response(str(response), mimetype="text/xml")

@app.route("/voice/menu-selection", methods=["POST"])
@validate_twilio_request
def menu_selection():
    """基于菜单选择路由。"""
    response = VoiceResponse()
    digit = request.form.get("Digits", "")

    if digit == "1":
        # 转接至销售
        response.say("正在为您连接销售。")
        response.dial(os.environ["SALES_PHONE"])

    elif digit == "2":
        # 转接至支持
        response.say("正在为您连接支持。")
        response.dial(os.environ["SUPPORT_PHONE"])

    elif digit == "3":
        # 语音邮件
        response.say("请在哔声后留言,

⚠️ 注意事项

问题 严重性 解决方案
问题 ## 在数据库中跟踪退出状态
问题 ## 为临时故障实施重试逻辑
问题 ## 注册A2P 10DLC(美国要求)
问题 严重 ## 始终验证签名
问题 ## 跟踪每个用户的会话窗口
问题 严重 ## 切勿硬编码凭据
问题 ## 也实施应用级速率限制