ForcePilot/backend/package/yuxi/channel/extensions/tencent_sms/frequency.py
Kris 944c9cdbb6 feat(channel): 添加腾讯短信渠道扩展
新增腾讯短信(Tencent SMS)渠道扩展,支持在 Yuxi 平台中集成腾讯云短信渠道。

包含以下功能模块:
- client: 腾讯云短信 API 客户端封装
- plugin: 渠道插件核心
- config: 渠道配置管理
- gateway: SSE/WebSocket 网关接入
- webhook: Webhook 事件处理
- outbound: 外发消息管理
- streaming: 流式消息处理
- security: 安全校验
- dedupe: 消息去重
- delivery: 送达状态回调
- compliance: 合规管理
- frequency: 频率控制
- templates: 短信模板
- agent_prompt: Agent 提示词
- types: 类型定义
2026-05-21 11:50:32 +08:00

58 lines
2.2 KiB
Python

from __future__ import annotations
import time
from collections import defaultdict
from .types import SmsScene, TencentSmsAccount
class SmsFrequencyGuard:
def __init__(self, window_seconds: int = 60):
self._window = window_seconds
self._records: dict[str, list[tuple[SmsScene, float]]] = defaultdict(list)
async def check_and_record(
self,
phone: str,
scene: SmsScene,
account: TencentSmsAccount,
) -> tuple[bool, str]:
now = time.time()
records = self._records[phone]
interval_check = [t for t, _ in records if now - t < account.interval_seconds]
if interval_check:
wait = account.interval_seconds - (now - interval_check[-1])
return False, f"\u53d1\u9001\u95f4\u9694\u4e0d\u8db3\uff0c\u8fd8\u9700\u7b49\u5f85 {wait:.0f}s"
today_start = now - (now % 86400)
today_records = [(t, s) for t, s in records if t >= today_start]
if len(today_records) >= account.phone_daily_limit:
return (
False,
f"\u8be5\u53f7\u7801\u5df2\u8fbe\u65e5\u53d1\u9001\u4e0a\u9650 {account.phone_daily_limit} \u6761",
)
hours_records = [(t, s) for t, s in today_records if now - t < 3600]
if len(hours_records) >= 5:
return False, f"\u8be5\u53f7\u7801 1 \u5c0f\u65f6\u5185\u5df2\u53d1\u9001 {len(hours_records)} \u6761"
window_records = [(t, s) for t, s in records if now - t < self._window]
if window_records and any(s == scene for _, s in window_records):
return False, "\u540c\u4e00\u573a\u666f 60s \u5185\u5df2\u53d1\u9001\u8fc7"
records.append((now, scene))
expired = [(t, s) for t, s in records if now - t > 86400]
for e in expired:
records.remove(e)
return True, "ok"
def remaining(self, phone: str, account: TencentSmsAccount) -> int:
now = time.time()
today_start = now - (now % 86400)
today_count = sum(1 for t, _ in self._records.get(phone, []) if t >= today_start)
return max(0, account.phone_daily_limit - today_count)
def reset(self, phone: str) -> None:
self._records.pop(phone, None)