92 lines
3.1 KiB
Python
92 lines
3.1 KiB
Python
"""拟人行为模拟
|
||
|
||
使用高斯分布随机延迟、偶尔长休息、工作时间限制等策略,
|
||
模拟真人操作节奏,降低被检测风险。
|
||
"""
|
||
|
||
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))
|