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)