""" services/rate_limiter.py 频率控制、每日限额、冷却管理 """ import time import threading from datetime import datetime # ---- 操作记录:防重复 & 每日统计 ---- _op_history = { "commented_feeds": set(), # 已评论的 feed_id "replied_comments": set(), # 已回复的 comment_id "liked_feeds": set(), # 已点赞的 feed_id "favorited_feeds": set(), # 已收藏的 feed_id } _daily_stats = { "date": "", "comments": 0, "likes": 0, "favorites": 0, "publishes": 0, "replies": 0, "errors": 0, } # 每日操作上限 DAILY_LIMITS = { "comments": 30, "likes": 80, "favorites": 50, "publishes": 8, "replies": 40, } # 连续错误计数 → 冷却 _consecutive_errors = 0 _error_cooldown_until = 0.0 # 线程锁,保护 stats/history 并发写入 _stats_lock = threading.Lock() def _reset_daily_stats_if_needed(): """每天自动重置统计""" today = datetime.now().strftime("%Y-%m-%d") if _daily_stats["date"] != today: _daily_stats.update({ "date": today, "comments": 0, "likes": 0, "favorites": 0, "publishes": 0, "replies": 0, "errors": 0, }) # 每日重置历史记录(允许隔天重复互动) for k in _op_history: _op_history[k].clear() def _check_daily_limit(op_type: str) -> bool: """检查是否超出每日限额""" _reset_daily_stats_if_needed() limit = DAILY_LIMITS.get(op_type, 999) current = _daily_stats.get(op_type, 0) return current < limit def _increment_stat(op_type: str): """增加操作计数""" _reset_daily_stats_if_needed() _daily_stats[op_type] = _daily_stats.get(op_type, 0) + 1 def _record_error(log_fn=None): """记录错误,连续错误触发冷却。log_fn 可选,用于写入日志。""" global _consecutive_errors, _error_cooldown_until _consecutive_errors += 1 _daily_stats["errors"] = _daily_stats.get("errors", 0) + 1 if _consecutive_errors >= 3: cooldown = min(60 * _consecutive_errors, 600) # 最多冷却10分钟 _error_cooldown_until = time.time() + cooldown if log_fn: log_fn(f"⚠️ 连续 {_consecutive_errors} 次错误,冷却 {cooldown}s") def _clear_error_streak(): """操作成功后清除连续错误记录""" global _consecutive_errors _consecutive_errors = 0 def _is_in_cooldown() -> bool: """检查是否在错误冷却期""" return time.time() < _error_cooldown_until def _is_in_operating_hours(start_hour: int = 7, end_hour: int = 23) -> bool: """检查是否在运营时间段""" now_hour = datetime.now().hour return start_hour <= now_hour < end_hour def _get_stats_summary() -> str: """获取今日运营统计摘要""" _reset_daily_stats_if_needed() s = _daily_stats lines = [ f"📊 **今日运营统计** ({s['date']})", f"- 💬 评论: {s['comments']}/{DAILY_LIMITS['comments']}", f"- ❤️ 点赞: {s['likes']}/{DAILY_LIMITS['likes']}", f"- ⭐ 收藏: {s['favorites']}/{DAILY_LIMITS['favorites']}", f"- 🚀 发布: {s['publishes']}/{DAILY_LIMITS['publishes']}", f"- 💌 回复: {s['replies']}/{DAILY_LIMITS['replies']}", f"- ❌ 错误: {s['errors']}", ] return "\n".join(lines)