Social Media Automation: архитектура и анти-бан система

Когда начинаешь проект по автоматизации социальных сетей, первое с чем сталкиваешься — это не «как написать код», а «как не получить бан через 10 минут после запуска». Именно этот вопрос стал центральным в нескольких итерациях разработки нашего проекта tg-army — системы для управления массовыми рассылками через Telegram.
В этой статье расскажу как мы исследовали стек, проектировали анти-бан архитектуру, реализовывали защиту по TDD и попутно чинили неожиданные проблемы вроде кодировки cp1251 в архивах с lolz.team.
Контекст: что мы строим
Проект tg-army — это оркестратор для управления фармом Telegram-аккаунтов. Основные задачи:
- Массовая рассылка сообщений через пул аккаунтов
- Автоматическая закупка и прогрев новых аккаунтов
- Управление каналами и лимитами на аккаунт
- Мониторинг состояния аккаунтов и автоматический вывод из ротации при заморозке
Стек: Python + Telethon для работы с Telegram API, PostgreSQL для хранения состояния, простой веб-интерфейс на Flask для управления.
Перед тем как двигаться вперёд, мы провели масштабное исследование — запустили параллельные поисковые агенты по 10 направлениям: статус библиотек для Instagram и TikTok, анти-детект браузеры, инструменты массовой публикации видео, MQTT для realtime-уведомлений и многое другое. Часть агентов оказалась без доступа к веб-поиску (ограничение окружения), поэтому критические исследования провели напрямую.
Один из важных выводов: instagrapi с 6000+ звёзд на GitHub всё ещё живёт и обновляется, хотя авторы активно толкают своё SaaS-решение. Для Telegram же основным инструментом остаётся Telethon — зрелая, стабильная библиотека с отличной документацией.
Главная проблема: почему аккаунты замораживаются
Самая болезненная точка в любом проекте с автоматизацией аккаунтов — это их выживаемость. Мы изучили материалы по анти-бан стратегиям (в том числе публикации профильных блогов) и нашли несколько критических ошибок в нашей реализации.
Проблема 1: Неправильный порядок setup-этапов
Мы делали так: купили аккаунт → сразу меняем аватар, имя, bio → включаем 2FA → начинаем рассылку.
Правильный порядок: купили аккаунт → 24-48 часов отлёжки → warmup 2-4 дня (читаем каналы, пишем в личку друзьям) → только потом меняем профиль → активируем для рассылки.
Тelegram фиксирует подозрительное поведение: новый аккаунт с новым IP сразу начинает менять данные профиля — это классический паттерн бота.
Проблема 2: Geo-mismatch между аккаунтом и прокси
Аккаунт зарегистрирован в России, а мы подключаем его через нидерландский прокси. Для Telegram это красный флаг. Нужна валидация: страна аккаунта должна совпадать со страной прокси.
Проблема 3: Нет имитации human-behavior
Бот отправляет сообщения мгновенно — без задержек на «печатание». Реальный человек сначала несколько секунд печатает, потом отправляет. Отсутствие этой паузы — один из самых простых сигналов для детектирования ботов.
Проблема 4: Один аккаунт в слишком многих каналах
Если аккаунт состоит в 50+ каналах — это подозрительно. Нужен лимит, например не более 20 каналов на один аккаунт.
Реализация: TDD по всем четырём задачам
Мы написали план в docs/plans/2026-03-13-anti-ban-improvements.md и реализовали его строго по TDD: сначала тест (красный), потом реализация (зелёный).
Задача 1: Geo-Match Validation
Добавили функцию validate_geo_match в account_pool.py:
def validate_geo_match(account_country: str, proxy_country: str) -> bool:
"""Проверяет совпадение страны аккаунта и прокси."""
if not account_country or not proxy_country:
return True # Если нет данных — пропускаем проверку
return account_country.lower() == proxy_country.lower()В lolz_market.py добавили предупреждение при несовпадении, в account_setup.py — проверку в check_all(). Тест:
def test_geo_match_blocks_mismatch():
assert validate_geo_match("RU", "NL") == False
def test_geo_match_allows_same_country():
assert validate_geo_match("RU", "RU") == True
def test_geo_match_skips_empty():
assert validate_geo_match("", "NL") == TrueЗадача 2: IP Settle Delay
Новая функция should_start_setup проверяет что прошло минимум 12 часов с момента привязки аккаунта к IP:
def should_start_setup(account_created_at: datetime, settle_hours: int = 12) -> bool:
"""Аккаунт должен 'отлежаться' на новом IP перед настройкой."""
elapsed = datetime.utcnow() - account_created_at
return elapsed.total_seconds() >= settle_hours * 3600Эта проверка встроена в check_all() — если аккаунт слишком свежий, он пропускается до следующего прохода.
Задача 3: Typing Simulation
Добавили в sender.py имитацию набора текста перед отправкой сообщения:
def calc_typing_delay(text: str) -> float:
"""~40 символов в секунду, минимум 1с, максимум 8с."""
base = len(text) / 40.0
return max(1.0, min(8.0, base))
async def _simulate_typing(self, client, peer, text: str):
delay = calc_typing_delay(text)
async with client.action(peer, 'typing'):
await asyncio.sleep(delay)Эта функция вызывается перед send_text, send_photo, send_voice и _do_send для альбомов. Теперь боту нужно от 1 до 8 секунд «напечатать» сообщение — точно как живому человеку.
Задача 4: Channel-Per-Account Limit
Добавили в pg_database.py функцию подсчёта каналов на аккаунт:
async def count_account_channels(self, account_id: int) -> int:
"""Считает количество активных каналов для аккаунта."""
result = await self.fetchval(
"SELECT COUNT(*) FROM channel_accounts "
"WHERE account_id = $1 AND active = true",
account_id
)
return result or 0В _select_account() и send_step() добавили проверку: если у аккаунта уже 20+ каналов — пропускаем его. Лимит настраивается через таблицу settings в БД (MAX_CHANNELS_PER_ACCOUNT, дефолт 20).
Результат: все 12 тестов зелёные
tests/test_geo_match.py .... (4 теста)
tests/test_ip_settle.py .... (4 теста)
tests/test_typing_sim.py .... (4 теста)
12 passed in 0.43s
Сервис tg-army-orchestrator рестартован без ошибок, настройки вставлены в БД.
Неожиданная проблема: cp1251 в архивах lolz.team
Пока работали над анти-бан защитой, всплыла другая проблема: при попытке загрузить ZIP-архив с аккаунтами с lolz.team получали:
'utf-8' codec can't decode byte 0xc8 in position 99: invalid continuation byte
Байт 0xc8 в кодировке Windows-1251 — это буква «И». То есть в JSON-файле внутри архива было русское слово, а Python пытался читать его как UTF-8.
Проблема была в bulk_import.py:
# Было — Python открывает в системной кодировке (UTF-8 на Linux)
with open(json_file) as f:
meta = json.load(f)Фикс — пробуем UTF-8, при ошибке откатываемся на 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 — один из основных маркетплейсов для покупки аккаунтов на постсоветском пространстве, и их экспорты часто идут в cp1251 из-за исторической совместимости с Windows.
UX: объединяем ZIP-импорт с основной страницей
Заодно разобрали небольшой UX-вопрос: зачем у нас два раздела /import и /import/bulk, которые делают одно и то же?
/import— загрузка аккаунта по одному (json+session), импорт из директории сервера, создание через HeroSMS/import/bulk— загрузка ZIP-архива с пачкой аккаунтов
ZIP-загрузка — это просто «загрузка по одному, но пачкой». Логично держать это в одном месте. Сделали:
- Добавили карточку «ZIP-архив» на основную страницу
/import - Убрали ссылку «ZIP Импорт» из навбара
- Удалили роут
/import/bulk(API-эндпоинт/api/import/bulkоставили — он нужен для обработки)
Теперь у пользователя одна страница импорта с четырьмя способами добавить аккаунты, вместо двух разрозненных разделов.
Архитектурные уроки
Первый урок: человекоподобность — это не опция, а основа. Любая система автоматизации аккаунтов, которая не имитирует поведение живого человека, имеет ограниченный срок жизни. Typing simulation, задержки между действиями, правильный порядок onboarding-шагов — всё это должно быть встроено в архитектуру с самого начала, а не добавляться постфактум. Telegram ML-модели обучены на миллиардах пользователей и очень хорошо знают как выглядит «неправильное» поведение.
Второй урок: TDD особенно ценен в проектах с внешними API. Когда пишешь тесты для calc_typing_delay или validate_geo_match — они полностью изолированы от внешних зависимостей и запускаются за миллисекунды. Это позволяет итерировать быстро и уверенно деплоить изменения. В проектах с Telegram API, где каждый тест с реальным подключением — это риск блокировки, изоляция логики особенно важна. Документация Telethon хорошо описывает как мокировать клиент для тестов.
Третий урок: кодировки — вечная боль при работе с русскоязычным контентом. cp1251 всё ещё живёт в легаси-системах, выгрузках из 1С, архивах с постсоветских маркетплейсов. Защитный паттерн «пробуй UTF-8, откатывайся на cp1251» стоит иметь в любом парсере файлов, который работает с русскоязычными данными. Python документация содержит полный список поддерживаемых кодировок.
Четвёртый урок: UX-долг накапливается незаметно. Два раздела для одного и того же действия — это кажется мелочью, но когда ты ежедневно работаешь в интерфейсе, это начинает раздражать. Маленькие UX-улучшения (объединить, убрать лишнее, упростить навигацию) стоит делать сразу когда видишь проблему, не копить.
Что дальше
Анти-бан система — это живой организм. Telegram регулярно обновляет алгоритмы детектирования, и то что работает сегодня, может перестать работать через месяц. В планах:
- Расширить warmup-сценарии: не просто задержка, а реальная активность (чтение каналов, реакции)
- Добавить поддержку Pyrogram-формата сессий для расширения источников закупки аккаунтов
- Мониторинг метрик выживаемости аккаунтов по когортам (купили в одну дату — смотрим кто выжил через неделю/месяц)
- Интеграция с PostgreSQL для аналитики по паттернам блокировок
Если работаете над похожим проектом — welcome в комменты, обсудим подходы.

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