wechat_msg_clicker/wechat_clicker/human_like.py
2026-04-22 19:28:54 +08:00

92 lines
3.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""拟人行为模拟
使用高斯分布随机延迟、偶尔长休息、工作时间限制等策略,
模拟真人操作节奏,降低被检测风险。
"""
import logging
import random
import time
from .config import Config
logger = logging.getLogger("wechat_clicker.human_like")
class HumanBehavior:
"""拟人行为引擎"""
def __init__(self, config: Config):
self.config = config
self._cycle_count = 0
self._break_probability = 0.05 # 5% 概率触发长休息
self._break_min = 30
self._break_max = 120
def delay(self, delay_name: str):
"""执行指定名称的随机延迟。"""
seconds = self.config.get_delay(delay_name)
logger.debug(f"延迟 {delay_name}: {seconds:.1f}s")
time.sleep(seconds)
def random_delay(self, min_s: float, max_s: float):
"""执行指定范围内的随机延迟(高斯分布)。"""
mid = (min_s + max_s) / 2
std = (max_s - min_s) / 4
value = random.gauss(mid, std)
value = max(min_s, min(max_s, value))
time.sleep(value)
def micro_jitter(self):
"""极短延迟0.1-0.5 秒),用于连续操作之间。"""
time.sleep(random.uniform(0.1, 0.5))
def scan_interval_with_jitter(self) -> float:
"""返回带 ±20% 抖动的扫描间隔时间(秒)。"""
base = self.config.scan_interval
jitter = base * 0.2
return random.uniform(base - jitter, base + jitter)
# ----------------------------------------------------------------
# 长休息
# ----------------------------------------------------------------
def should_take_break(self) -> bool:
"""判断是否应该触发一次长休息。
每个循环有固定概率触发,模拟真人偶尔离开电脑。
"""
self._cycle_count += 1
return random.random() < self._break_probability
def long_break(self):
"""执行一次长休息30-120 秒)。"""
duration = random.uniform(self._break_min, self._break_max)
logger.info(f"模拟长休息: {duration:.0f}s")
time.sleep(duration)
# ----------------------------------------------------------------
# 工作时间
# ----------------------------------------------------------------
def is_off_hours(self) -> bool:
"""检查是否在非工作时间(应暂停操作)。"""
return not self.config.is_within_working_hours()
# ----------------------------------------------------------------
# 随机子集
# ----------------------------------------------------------------
def random_subset_count(self, total: int, max_count: int) -> int:
"""决定本次处理多少个项目。
不总是处理 max_count 个,偶尔少处理一些,模拟真人不会每次都处理所有内容。
"""
if total <= 0:
return 0
upper = min(total, max_count)
# 80% 概率处理全部上限内20% 概率减少 1-2 个
if random.random() < 0.8:
return upper
return max(1, upper - random.randint(1, 2))