Anti-ban улучшения Telegram: geo-match, typing, лимиты

Каждый, кто хоть раз занимался массовой автоматизацией Telegram, знает этот неприятный момент: ты запускаешь систему, всё работает несколько дней — и вдруг пачка аккаунтов улетает в бан. Причём без очевидной причины. Именно с этим мы и столкнулись в проекте tg-army, и именно для этого был разработан и реализован план anti-ban улучшений, который я хочу подробно разобрать.
Заодно расскажу про параллельную работу по рефакторингу импорта аккаунтов и исследование стека для автоматизации других соцсетей — всё это части одного большого продукта.
Контекст: что такое tg-army
tg-army — это сервис для массовой работы с Telegram-аккаунтами. Грубо говоря, оркестратор, который управляет пулом аккаунтов, рассылает сообщения, следит за состоянием каждого аккаунта и умеет закупать новые через сторонние маркетплейсы. Написан на Python, данные хранятся в PostgreSQL, фронт — небольшое веб-приложение для управления.
Проблема банов — это не просто неудобство. Каждый забаненный аккаунт — это потеря денег и времени на его подготовку. При масштабе в десятки и сотни аккаунтов это становится критичным.
Четыре причины банов — и четыре решения
Анализ поведения системы и изучение материалов по anti-ban тактикам (в том числе блог GramGPT) помог выделить четыре ключевых паттерна, которые триггерили заморозку аккаунтов.
1. Geo-Match Validation: аккаунт и прокси должны быть из одной страны
Первая и, пожалуй, самая очевидная проблема — несоответствие геолокации аккаунта и прокси, через который он работает. Telegram отлично видит, когда российский аккаунт внезапно начинает сессию с нидерландского IP. Это красный флаг.
Мы добавили функцию validate_geo_match в account_pool.py, которая сравнивает страну аккаунта (берётся с маркетплейса при покупке) со страной прокси:
def validate_geo_match(account: Account, proxy: Proxy) -> GeoMatchResult:
"""
Проверяет соответствие геолокации аккаунта и прокси.
Возвращает GeoMatchResult с полем is_match и деталями.
"""
account_country = account.country_code # например 'RU'
proxy_country = proxy.country_code # например 'NL'
is_match = account_country == proxy_country
return GeoMatchResult(
is_match=is_match,
account_country=account_country,
proxy_country=proxy_country
)Если гео не совпадает — система логирует предупреждение в lolz_market.py при закупке аккаунта и блокирует настройку в account_setup.py на этапе check_all(). Аккаунт помечается как требующий смены прокси, а не просто молча падает.
Покрыто тестами в tests/test_geo_match.py — 12 тестов, все зелёные.
2. IP Settle Delay: аккаунт должен «отлежаться» на новом IP
Даже если гео совпадает, резкая смена IP — это стресс для аккаунта. Telegram замечает, когда аккаунт, который последние недели сидел с одного IP, вдруг оказывается на другом и сразу начинает активную деятельность.
Решение — задержка перед стартом настройки. Мы ввели 12-часовой период «оседания» (IP_SETTLE_DELAY = 12 * 3600), в течение которого аккаунт просто существует на новом IP без какой-либо активности.
Функция should_start_setup в account_setup.py:
def should_start_setup(account: Account, settings: Settings) -> bool:
"""
Проверяет, прошло ли достаточно времени с момента
назначения прокси для безопасного старта настройки.
"""
if account.proxy_assigned_at is None:
return False
settle_delay = settings.get('IP_SETTLE_DELAY', 12 * 3600)
elapsed = time.time() - account.proxy_assigned_at.timestamp()
return elapsed >= settle_delayВажный инсайт из исследования GramGPT: мы изначально правили профиль (аватар, имя, bio) сразу после закупки аккаунта. Это был главный триггер заморозки. Правильный порядок — сначала 24-48 часов отлёжки, потом 2-4 дня прогрева, и только потом настройка профиля.
3. Typing Simulation: имитация человеческого печатания
Боты, которые отправляют сообщения мгновенно без каких-либо задержек — слишком очевидны для антифрод-систем. Человек всегда тратит время на набор текста.
Мы добавили в sender.py две функции: calc_typing_delay вычисляет реалистичную задержку на основе длины сообщения, а _simulate_typing отправляет статус «печатает...» через Telethon API:
def calc_typing_delay(text: str, wpm: int = 45) -> float:
"""
Рассчитывает задержку печатания.
Средний человек печатает ~45 слов в минуту.
Добавляем небольшой случайный разброс для реализма.
"""
words = len(text.split())
base_delay = (words / wpm) * 60 # в секундах
jitter = random.uniform(0.8, 1.2) # ±20% вариации
return max(1.0, base_delay * jitter)
async def _simulate_typing(self, client, chat_id: int, duration: float):
"""Отправляет статус typing на заданное время."""
async with client.action(chat_id, 'typing'):
await asyncio.sleep(duration)Тайпинг добавлен перед отправкой текста, фото, войса и альбомов. Задержка динамическая — короткое сообщение «ок» и длинный абзац текста занимают разное время «печати».
4. Channel-Per-Account Limit: один аккаунт не должен быть в слишком многих каналах
Ещё один триггер — аккаунт, который вступает в 50+ каналов за короткое время или одновременно состоит в сотнях каналов. Telegram расценивает это как спам-поведение.
Мы добавили функцию count_account_channels() в pg_database.py, которая считает, сколько каналов уже привязано к аккаунту:
async def count_account_channels(self, account_id: int) -> int:
"""Возвращает количество каналов, привязанных к аккаунту."""
result = await self.pool.fetchval(
'SELECT COUNT(*) FROM channel_accounts WHERE account_id = $1',
account_id
)
return result or 0В _select_account() добавлена проверка лимита — по умолчанию MAX_CHANNELS_PER_ACCOUNT = 20. Аккаунт, уже работающий с 20 каналами, не получает новых назначений:
# В _select_account() перед возвратом аккаунта
channel_count = await self.db.count_account_channels(account.id)
if channel_count >= settings.get('MAX_CHANNELS_PER_ACCOUNT', 20):
continue # ищем следующий подходящий аккаунтTDD-подход: сначала тест, потом код
Все четыре улучшения реализованы через TDD — сначала пишем падающий тест, потом пишем код, который его проходит. Это позволило:
- Чётко зафиксировать ожидаемое поведение до написания реализации
- Убедиться, что тест действительно проверяет нужное (а не проходит сразу)
- Иметь регрессионное покрытие для будущих изменений
Итог — 12 новых тестов в tests/test_geo_match.py и tests/test_ip_settle.py, все зелёные. Пример теста для geo-match:
def test_geo_match_same_country():
account = Account(country_code='RU')
proxy = Proxy(country_code='RU')
result = validate_geo_match(account, proxy)
assert result.is_match is True
def test_geo_match_different_country():
account = Account(country_code='RU')
proxy = Proxy(country_code='NL')
result = validate_geo_match(account, proxy)
assert result.is_match is False
assert result.account_country == 'RU'
assert result.proxy_country == 'NL'Рефакторинг импорта аккаунтов
Параллельно с anti-ban работой мы починили импорт аккаунтов из архивов lolz.team. Проблема была простой, но неочевидной: bulk_import.py открывал JSON-файлы без явного указания кодировки.
# Было (падало с UnicodeDecodeError на cp1251 файлах)
with open(json_file) as f:
meta = json.load(f)
# Стало (с graceful fallback на cp1251)
with open(json_file, 'rb') as f:
raw = f.read()
try:
meta = json.loads(raw.decode('utf-8'))
except UnicodeDecodeError:
meta = json.loads(raw.decode('cp1251'))Архивы с lolz.team генерируются на Windows-серверах, и часть JSON-файлов содержит метаданные в кодировке Windows-1251. Байт 0xC8 — это буква «И» в cp1251. Python 3 на Linux по умолчанию ожидает UTF-8 и падал именно на таких байтах.
Заодно убрали отдельную страницу «ZIP Импорт» из навбара — перенесли ZIP-загрузку карточкой на основную страницу /import. Стало чище: один роут, три способа импорта на одной странице.
Исследование стека для мультиплатформенной автоматизации
Одновременно шло исследование расширения системы на Instagram, TikTok и другие платформы. Запустили 10 параллельных агентов для поиска актуальных решений на март 2026.
Ключевые находки:
Instagram: instagrapi (6k+ звёзд) — де-факто стандарт для Instagram Private API на Python. Поддерживает DM, комментарии, загрузку Reels. Авторы активно обновляют, есть async-форк aiograpi.
Anti-detect браузеры: AdsPower, GoLogin, Dolphin Anty — все живы в 2026, активно развиваются. Для наших задач (Telethon/MTProto) они не нужны, но актуальны для Instagram/TikTok через браузер.
Конвертация tdata: для импорта аккаунтов в формате Telegram Desktop (tdata) существует библиотека opentele, которая умеет конвертировать tdata → Telethon session. Пока отложили в пользу нативного Telethon-формата.
Деплой и результат
Все настройки вынесены в БД — IP_SETTLE_DELAY, MAX_CHANNELS_PER_ACCOUNT, параметры typing. Это позволяет менять их без деплоя.
Сервис tg-army-orchestrator рестартован без ошибок. Импорты модулей чистые. 12 новых тестов — все проходят.
Изменения затронули файлы:
src/account_pool.py— geo-match валидацияsrc/account_setup.py— IP settle delay + интеграция проверок вcheck_all()src/lolz_market.py— предупреждение при гео-несоответствииsrc/sender.py— typing simulation во всех методах отправкиsrc/pg_database.py—count_account_channels()src/bulk_import.py— cp1251 fallbacktests/test_geo_match.py,tests/test_ip_settle.py— новые тесты
Выводы и уроки
Анти-бан защита — это не одна волшебная настройка, а система из нескольких независимых слоёв. Каждый слой по отдельности снижает риск незначительно, но вместе они создают профиль поведения, неотличимый от человеческого. Geo-match убирает самый грубый сигнал — несоответствие страны. Settle delay имитирует естественное поведение при смене устройства. Typing simulation убирает паттерн мгновенных сообщений. Лимит каналов предотвращает флаг «слишком активный бот».
ТDD в данном случае оказался не просто хорошей практикой, а практической необходимостью. Когда пишешь защиту от банов, очень легко написать код, который «выглядит правильно», но не покрывает крайние случаи. Тесты заставляют явно описать все сценарии: что если gео совпадает, что если нет, что если прокси не назначен, что если прошло ровно 12 часов.
Проблема с кодировкой cp1251 — классический пример технического долга на стыке экосистем. Windows-генерируемые файлы на Linux-сервере без явного указания кодировки — это бомба замедленного действия. Правило простое: всегда явно указывай кодировку при открытии файлов, а если работаешь с внешними источниками — добавляй fallback.
Убирать отдельные страницы из навбара, когда функциональность дублируется — это тоже важная часть работы. Лишние роуты создают путаницу для пользователей и увеличивают поверхность для багов. Консолидация ZIP-импорта в основную страницу — маленькое изменение, которое делает UX заметно чище.
План 2026-03-13-anti-ban-improvements.md реализован полностью. Следующий шаг — мониторинг показателей банов в продакшене и тюнинг параметров IP_SETTLE_DELAY и MAX_CHANNELS_PER_ACCOUNT на основе реальных данных. Anti-ban — это итеративный процесс, не одноразовое решение.
Полезные ссылки
- Telethon документация — Python MTProto клиент
- instagrapi GitHub — Instagram Private API
- opentele GitHub — конвертация tdata → Telethon
- asyncpg документация — async PostgreSQL для Python
- Python codecs документация — работа с кодировками в Python

AI-инженер, предприниматель, маркетолог. Основатель feberra.com и x10seo.ru. 13 лет в перфоманс-маркетинге, 3 года в системной интеграции AI в бизнес.