新增腾讯短信(Tencent SMS)渠道扩展,支持在 Yuxi 平台中集成腾讯云短信渠道。 包含以下功能模块: - client: 腾讯云短信 API 客户端封装 - plugin: 渠道插件核心 - config: 渠道配置管理 - gateway: SSE/WebSocket 网关接入 - webhook: Webhook 事件处理 - outbound: 外发消息管理 - streaming: 流式消息处理 - security: 安全校验 - dedupe: 消息去重 - delivery: 送达状态回调 - compliance: 合规管理 - frequency: 频率控制 - templates: 短信模板 - agent_prompt: Agent 提示词 - types: 类型定义
58 lines
2.2 KiB
Python
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)
|