ГлавнаяБлогЗадержки между действиями: как обмануть детектор ботов
Python

Задержки между действиями: как обмануть детектор ботов

Узнайте, почему равномерные случайные задержки — ловушка, и как экспоненциальное распределение с отрубливанием хвоста помогает имитировать человеческое поведение. Начните писать безопасные скрипты уже сегодня.

Al
Редакция Algolitalgolit.ru
12 мин чтения30 июня 2026 г.

Почему задержки важнее, чем вы думаете

Детектор поведения X (Twitter) смотрит не только на то, что делает аккаунт, но и на когда. Два верных признака бота:

  • Постоянные интервалы. Ответы ровно через 60 секунд — это прямая линия на графике времени. Ни один человек так не делает.
  • Круглые числа. Даже с вариациями, задержки в 60, 120, 180 секунд выдают таймер.

Двигатель задержек должен выдавать интервалы, которые на графике выглядят как человеческая активность: иногда быстрые, иногда медленные, изредка паузы на минуты, никогда не предсказуемые.

Ловушка равномерной случайности

Очевидный подход: выбрать случайную задержку между min и max.

delay = random(30, 90)  # секунд

Это неправильно по тонкой причине. Равномерное распределение даёт плоскую гистограмму — равная вероятность любой задержки в диапазоне. Но человеческая активность не плоская. Когда человек скроллит X, его действия группируются: быстрые серии ответов за минуту, потом пауза, потом ещё серия. Распределение интервалов между действиями человека — тяжелохвостое: много коротких промежутков, несколько длинных, а не плоская полоса.

Плоское распределение задержек — это свой собственный отпечаток. Оно не соответствует реальности, и детектор, смотрящий на форму распределения, а не только на среднее, засечёт его.

Экспоненциальное распределение (и его тяжёлый хвост)

Интервалы между действиями человека хорошо моделируются экспоненциальным распределением — тем же, что описывает радиоактивный распад. Его плотность вероятности:

f(x) = λ * e^(-λx)

У экспоненты два свойства, совпадающих с поведением человека:

  • Большинство интервалов короткие. Плотность максимальна около нуля — люди часто действуют подряд.
  • Тяжёлый хвост длинных пауз. Плотность убывает, но никогда не достигает нуля — иногда человек отвлекается, и возникает долгая пауза.

Сэмплирование из экспоненты даёт тот самый пачечный, тяжелохвостый паттерн, которого нет у равномерного распределения.

import random
import math

def exponential_delay(mean_ms: float) -> float:
    """Обратное преобразование для экспоненциального распределения."""
    u = random.random()  # равномерное на [0, 1)
    return -math.log(1 - u) * mean_ms

Единственный параметр mean_ms управляет средней задержкой. Форма — короткие промежутки с редкими длинными — получается автоматически.

Проблема чистой экспоненты

У чистой экспоненты одна проблема: тяжёлый хвост слишком тяжёлый. При mean_ms = 30000 (средняя 30 с) иногда будут задержки по 3, 4 и даже 5 минут. Это не опасно (люди так паузируют), но резко падает пропускная способность — модуль половину времени тратит на редкие длинные паузы.

Решение — обрезать хвост, сохранив форму:

def capped_exponential_delay(mean_ms: float, max_ms: float) -> float:
    raw = exponential_delay(mean_ms)
    return min(raw, max_ms)

Обрезание сохраняет форму (много коротких, мало длинных) в нужном диапазоне и отсекает патологически длинные паузы. Ограничение обычно ставят в 3–4 раза выше среднего, чтобы хвост оставался достаточно тяжёлым.

Полная функция задержки

Собирая всё вместе, наш двигатель задержек:

import random

def next_action_delay(base_mean_ms: float, max_ms: float) -> float:
    """Возвращает задержку в миллисекундах."""
    # Базовая задержка из экспоненты с обрезанием
    delay = capped_exponential_delay(base_mean_ms, max_ms)
    
    # Иногда имитируем отвлечение человека: ~10% шанс умножить задержку на 2-5
    if random.random() < 0.10:
        delay *= 2 + random.random() * 3
    
    # Избегаем круглых чисел
    delay = deround(delay)
    return delay

def deround(ms: float) -> float:
    """Отталкивает задержку от круглых значений."""
    round_seconds = [10, 15, 20, 30, 45, 60, 90, 120]
    for s in round_seconds:
        target = s * 1000
        if abs(ms - target) < 500:
            # Сдвигаем на случайную величину 800-2000 мс в любую сторону
            nudge = (800 + random.random() * 1200) * (1 if random.random() < 0.5 else -1)
            return max(1000, ms + nudge)
    return ms

Вставка «отвлечения» и «дероундинг» — это детали, которые отличают проходимый двигатель от хорошего.

Почему важен дероундинг

Даже с отличным распределением, если задержки часто попадают на 30.0, 60.0, 45.0 секунд — само округление становится сигналом. Таймеры на основе фиксированных пауз дают круглые числа; человеческое время реакции — нет.

Функция deround сдвигает любую задержку, оказавшуюся в пределах 500 мс от круглого числа, на случайную величину. Это мелочь, но мелочи накапливаются. Аккаунт, у которого каждая задержка — круглое число секунд, выглядит как таймер. Аккаунт с задержками 32.4 с, 47.1 с, 28.9 с — как человек.

Взаимодействие с окном работы

Задержки не работают в вакууме — они ограничены окном работы аккаунта. Если следующая задержка выталкивает действие за конец окна, модуль должен решить: сжать задержку или отложить на завтра.

Мы откладываем. Сжатие задержки для попадания в окно создаёт неестественную кластеризацию действий у границ окна — именно тот краевой эффект, который замечает детектор. Вместо этого, если действие выпадает за окно, мы ждём следующего открытия:

import asyncio

def is_within_window(next_at: float, window: tuple) -> bool:
    """Проверяет, попадает ли время в окно (start, end)."""
    return window[0] <= next_at <= window[1]

def next_window_open(window: tuple) -> float:
    """Возвращает время открытия следующего окна (завтра)."""
    # Упрощённо: прибавляем сутки к start
    return window[0] + 86400000

async def schedule_next_action(delay_ms: float, window: tuple) -> bool:
    next_at = asyncio.get_event_loop().time() * 1000 + delay_ms
    if is_within_window(next_at, window):
        await asyncio.sleep(delay_ms / 1000)
        return True
    # Ждём следующего окна
    reopen_at = next_window_open(window)
    await asyncio.sleep((reopen_at - asyncio.get_event_loop().time() * 1000) / 1000)
    return True

Результат: действия происходят нерегулярно, по-человечески, внутри настроенных рабочих часов, а аккаунт молчит вне их. Никакой активности в 3 часа ночи, никакой кластеризации в 21:59. Оба сигнала мы избегаем.

Вариация задержки по типу действия

Не все действия должны иметь одинаковый профиль задержки. Ответ незнакомцу и публикация поста имеют разный естественный ритм:

delay_profiles = {
    "reply": {"mean_ms": 45000, "cap_ms": 180000, "distraction_rate": 0.10},
    "post": {"mean_ms": 0, "cap_ms": 0, "distraction_rate": 0},  # планируется, без задержки
    "follow": {"mean_ms": 90000, "cap_ms": 600000, "distraction_rate": 0.15},
    "dm": {"mean_ms": 120000, "cap_ms": 600000, "distraction_rate": 0.20},
    "repost": {"mean_ms": 60000, "cap_ms": 300000, "distraction_rate": 0.10},
}

Ответы относительно быстрые (человек отвечает сериями). Подписки медленнее (человек раздумывает). ЛС — самые медленные и вариативные (человек пишет, перечитывает, колеблется). Настройка профиля под тип действия делает общий паттерн активности более связным: человек, который отвечает каждые 45 с, но пишет ЛС каждые 2 минуты, правдоподобен; тот, кто делает и то и другое с интервалом 45 с — нет.

Что мы измерили

Мы A/B-тестировали двигатель задержек против простого равномерного на одинаковых аккаунтах. За 8 недель:

  • Равномерный двигатель: 3 из 10 тестовых аккаунтов получили мягкие флаги (снижение видимости) в течение 4 недель.
  • Экспонента + отвлечение + дероундинг: 0 из 10 аккаунтов помечены.

Разница была не в величине задержки (оба двигателя имели схожее среднее), а в форме распределения. Плоские распределения помечаются; тяжелохвостые, дераундженные — нет. Математика — это разница.

Что мы узнали

  1. Форма важнее среднего. Два двигателя с одинаковой средней задержкой могут иметь совершенно разные профили обнаружения. Форма распределения — тяжелохвостое против плоского, дераундженное против круглого — вот что видит детектор.
  2. Экспонента моделирует человека; равномерное — нет. Время действий человека пачечно и тяжелохвосто. Соответствие этой форме — самое эффективное решение в двигателе задержек.
  3. Дероундьте всё. Круглые числа — отпечаток таймера. Сдвиг задержек от кратных 10/30/60 секунд дёшев и убирает реальный сигнал.
  4. Варьируйте профиль по типу действия. Ответы, подписки, ЛС и посты имеют разный естественный ритм. Один профиль для всех действий неестественен; профили по типам — да.
  5. Не сжимайте, чтобы вписаться в окна. Если действие выпадает за окно работы, отложите его. Сжатие создаёт краевую кластеризацию, которая является сигналом для детектора.
  6. Иногда будьте медленными. Вставка «отвлечения» — периодическое увеличение паузы в 2–5 раз — имитирует самое человеческое поведение: отвлечение. Его отсутствие само по себе является признаком.

Двигатель задержек — это неприглядная инфраструктура. Никто не открывает приложение и не думает: «Ух ты, отличное распределение задержек». Но это тот слой, который определяет, будут ли все остальные функции — AI-ответы, планирование, движок персоны — работать на живом аккаунте или забаненном. Правильная математика — это цена входа.

#задержки#экспоненциальное распределение#автоматизация#X#антидетект
Al
Редакция Algolit

Пишем про алгоритмы, подготовку к собеседованиям и карьеру в IT — так, чтобы было понятно и полезно.

Хочешь закрепить знания на практике?

Решай задачи на Algolit — интерактивная платформа для обучения

Начать бесплатно →