Парсинг приватных Telegram-каналов через Telethon для наполнения MAX-канала

Парсинг приватных Telegram-каналов через Telethon в Content Pilot: что технически делает userbot, какие юридические границы (ToS Telegram, авторские права, 152-ФЗ), какой surface area вы отдаёте платформе вместе со StringSession, и какие защиты у нас стоят. Параноидальный разбор от CTO для разработчиков, агентств и авторов агрегаторов.

Я Артём Голубев, CTO Content Pilot. Эта статья — не маркетинг и не «как накрутить агрегатор за пять минут». Это попытка честно разложить, что такое парсинг приватных Telegram-каналов через Telethon, что вы реально отдаёте сторонней платформе, когда подключаете свою сессию, и где именно проходят юридические красные линии. Я работаю с этой инфраструктурой каждый день и видел достаточно сожжённых аккаунтов, чтобы относиться к теме параноидально.

Если вам обещали «волшебную кнопку, которая сольёт любой приватный канал», закройте эту вкладку. Никакой магии тут нет. Есть MTProto, есть userbot-логика, есть Terms of Service Telegram, есть Гражданский кодекс с авторским правом и 152-ФЗ «О персональных данных». Все четверо работают одновременно, и каждый может вас укусить.

Что технически делает Telethon

Telethon — это open-source Python-библиотека, реализующая клиент протокола MTProto. Подчеркну: это не официальный SDK Telegram. Telegram официально поддерживает Bot API (HTTP, ограниченный набор методов) и MTProto-клиенты для своих приложений (TDesktop, TDLib). Telethon — независимая реализация поверх опубликованной MTProto-схемы. Это означает две вещи. Первое: всё, что вы делаете через Telethon, выглядит для серверов Telegram как действия обычного пользователя — не бота. Второе: Telegram имеет полное право в любой момент классифицировать конкретный паттерн поведения как «automated abuse» и заморозить аккаунт без предупреждения.

Чтобы Telethon вообще запустился, нужны два значения: api_id и api_hash. Их выдают на my.telegram.org после авторизации тем номером телефона, под которым потом будет работать клиент. Дальше Telethon делает то же, что делает Telegram Desktop при первом запуске: подключается к ближайшему DC, запрашивает SMS- или app-код, обменивает его на session — внутреннее состояние клиента, которое включает auth_key, server_salt, идентификатор DC, кэш сущностей. У Telethon это состояние можно сериализовать в строку (StringSession) и хранить где угодно — в файле, в переменной окружения, в БД.

Когда Content Pilot парсит публичный канал, Telethon из-под нашего сервисного аккаунта вызывает метод channels.getFullChannel, затем messages.getHistory с указанным peer и постранично забирает сообщения. Для приватных каналов отличается ровно одно: peer должен быть тем, в кого вы вступили. Если вы — то есть ваш userbot — не состоите в канале, сервер вернёт CHANNEL_PRIVATE и никаких сообщений не отдаст. Никакого способа «обойти» это в честной MTProto-схеме нет. Любые публичные «решения» либо мошенничество, либо ведут через слитые сессии с чужих аккаунтов, что уже криминал.

Поэтому вся логика парсинга приватных каналов в Content Pilot строится на одной предпосылке: вы вступили в канал лично, и вы добровольно отдаёте нам свою сессию для чтения того, к чему у вас есть доступ. Не больше и не меньше.

Как это выглядит в нашей архитектуре

Пользователь подключает свою Telegram-сессию через бот: /start → ⚙️ Настройки аккаунта → 📱 Telegram-сессия. Сценарий внутри:

  1. Юзер регистрирует приложение на my.telegram.org и получает api_id/api_hash. Мы их не видим до этого шага, и я настоятельно рекомендую заводить отдельное приложение под Content Pilot, а не переиспользовать ключи от своего личного клиента.
  2. Юзер вводит номер телефона. Telegram присылает код в другое приложение Telegram, которое уже залогинено на этом номере (это стандартное поведение MTProto, не наша придумка).
  3. Юзер передаёт код нам. Если включена 2FA — ещё и пароль второго фактора.
  4. Telethon на нашей стороне завершает авторизацию, получает StringSession, и мы немедленно шифруем её симметричным ключом Fernet (cryptography lib) и кладём в Postgres. Ключ FERNET_KEY хранится в .env на сервере, в БД он не лежит — даже у того, кто стянет дамп Postgres, не будет ничего читаемого.
  5. В таблице users поднимается флаг подключения StringSession — на этот флаг смотрит UI, чтобы показать «сессия активна, можно добавлять приватные источники».

Дальше при инициальном парсинге источника мы достаём из БД зашифрованную сессию пользователя именно того, кто этот источник добавил, расшифровываем в памяти LLM-сервиса, поднимаем Telethon-клиент и забираем 100 последних постов. Инкрементальный парсинг устроен иначе — это шедулер-джоб monitoring.py в отдельном контейнере content-pilot-scheduler. Раз в N минут он проходит по активным источникам и для каждого забирает только сообщения, появившиеся после last_checked_at. На уровне БД стоит уникальный констрейнт (source_id, external_post_id), который не даст одному и тому же посту записаться дважды, даже если шедулер по любой причине запустится с пересечением. Посты старше MAX_POST_AGE_DAYS = 7 отфильтровываются — мы целенаправленно не лезем в архивы каналов, и об этом ниже отдельно.

Каждый источник хранит error_type со списком возможных значений: CHANNEL_NOT_FOUND, CHANNEL_PRIVATE, RATE_LIMITED, FEED_NOT_FOUND, TIMEOUT, HTTP_ERROR. Юзер видит конкретную причину в UI и понимает, надо ли ему вступить в канал, поменять прокси, или источник вообще удалён. Это важная штука именно с точки зрения безопасности: пользователь не должен гадать, почему парсер молчит, иначе он будет пытаться «починить» руками — и тут же словит ограничения за подозрительную активность.

Какой surface area вы отдаёте вместе со StringSession

Этот раздел самый неприятный, и его обычно пропускают. Я не буду.

StringSession — это не «токен на чтение каналов». Это полноценная сессия Telegram-аккаунта, эквивалентная по правам той, которую имеет ваше приложение Telegram Desktop. С технической стороны человек, у которого есть ваша расшифрованная StringSession, может через MTProto делать всё, что можете делать вы:

  • читать ваши личные переписки, включая «Избранное» и секретные чаты, которые синхронизируются (не end-to-end);
  • отправлять сообщения от вашего имени кому угодно;
  • создавать и удалять каналы, выходить из чатов, банить участников там, где вы админ;
  • смотреть ваш список контактов, экспортировать его, инициировать звонки;
  • менять настройки безопасности, в том числе пароль 2FA, если знает текущий;
  • создавать новые сессии и отвязывать старые.

Это не уязвимость Telethon и не особенность нашей реализации. Это устройство MTProto: сессия — суверенный объект, она равна аккаунту. Любая платформа, которая принимает у вас StringSession, должна относиться к ней как к ядерному коду, и если она не пишет об этом честно — задайте им вопрос, почему.

Мы пишем. И поэтому наша архитектура устроена так, чтобы технически минимизировать доверие, которое юзер вынужден нам оказать.

Изоляция по юзеру. Сессия одного пользователя никогда не используется для парсинга источников другого. В коде LLM-сервиса парсер получает user_id владельца источника и поднимает Telethon-клиент именно с его сессией. Нет глобального «пула сессий», нет балансировки между чужими аккаунтами, нет никаких причин, по которым сессия пользователя А оказалась бы в контексте задачи пользователя Б.

Шифрование at rest. Fernet — это AES-128-CBC + HMAC-SHA256, симметричный, с ключом из переменной окружения. В Postgres лежит только зашифрованный байт-массив. Дамп БД сам по себе сессии не выдаёт. Чтобы расшифровать, нужен и дамп, и доступ к серверному .env. Это не защита от инсайдера с root, но это вполне защита от утечки бэкапа, от ошибочного экспорта в логи, от случайно расшаренного слепка таблицы.

Минимальный набор операций. Технически наша сессия может всё, что описано выше. Фактически наш код вызывает только методы чтения сообщений из тех каналов, которые юзер сам добавил как источник. Мы не отправляем сообщения от имени пользователя, не вступаем за него в чаты, не читаем его личные переписки, не трогаем 2FA, не создаём каналы, не дёргаем список контактов. Это самоограничение в коде, а не в протоколе — и здесь честный ответ единственный: проверить, что мы это действительно соблюдаем, можно только аудитом исходников. Разработчикам, которые хотят такой аудит, мы готовы его дать.

Отзыв сессии в один клик. Юзер может в любой момент отвязать сессию через тот же путь в боте. Внутри это делает auth.logOut на стороне Telegram и стирает зашифрованный StringSession из нашей БД. Параллельно я всем рекомендую периодически открывать в нативном приложении Telegram «Настройки → Устройства» и проверять активные сессии руками — это уже не наша зона, это базовая гигиена.

Юридические границы: четыре документа, которые работают одновременно

Технически возможное и юридически допустимое — разные множества. Парсинг приватного Telegram-канала через свою сессию находится в пересечении нескольких регуляций сразу. Я не юрист, и ниже не индивидуальная консультация — но контур я обязан обозначить, потому что разработчики и агентства часто думают только про «не поймали бы за руку», а проблема приходит с другой стороны.

1. Terms of Service Telegram

Telegram ToS не запрещают читать каналы, в которые вы вступили. Это просто использование клиента — никакого нарушения нет. Но ToS прямо запрещают «massive automated abuse»: массовую регистрацию аккаунтов, автоматический спам, использование сервиса для атак. Граница размытая, и проходит она по поведенческим паттернам:

  • один аккаунт читает десятки каналов с интервалом в секунды → флаг автоматизации, риск ограничения;
  • аккаунт зарегистрирован на виртуальный номер, никогда не отправлял сообщений, только читает → высокий риск freeze при первой же модерации;
  • аккаунт сидит в 500+ каналах и держит постоянную сессию → как минимум rate limiting, как максимум блокировка.

Реальная защита — поведенчески выглядеть как живой человек. Это значит: не более одной активной парсинг-сессии на номер, разумные задержки между вызовами, аккаунт со «следами жизни» (есть переписки, отправлены сообщения хотя бы изредка). Content Pilot со своей стороны старается не превышать частоту чтения, но если вы подсадили на платформу аккаунт, зарегистрированный сегодня на eSIM из «купи-номер.ru», — это ваше окно риска, не наше.

2. Авторское право на контент в каналах

Тексты в Telegram-каналах — это произведения. По умолчанию они защищены авторским правом по факту создания, без какой-либо регистрации (ст. 1255 ГК РФ). Подключение канала как источника в Content Pilot и автоматическая перепубликация этого контента в свой канал без согласия автора — это классический рерайт чужого произведения. Даже если LLM перефразирует текст до неузнаваемости, факт переработки производного произведения никуда не девается (ст. 1260 ГК РФ).

В реальности правообладатели жалуются на агрегаторы редко — пока агрегатор маленький. Когда канал-агрегатор перерастает по охвату оригинальный источник, претензии прилетают быстро. Самые рисковые сценарии:

  • копирование «один в один» через FROM_SOURCES rewrite=False — это точное воспроизведение, в зоне риска даже без LLM;
  • рерайт длинных аналитических статей — суды смотрят на охраноспособные элементы, и сюжетная структура с фактологией охраняется тоже;
  • копирование контента из платных приватных каналов и закрытых клубов — это уже не только авторское право, это нарушение договора подписки, к которому вы присоединились при оплате доступа.

Если вы строите бизнес на агрегаторе, единственный нормальный путь — договариваться с источниками. Купить лицензию, оформить партнёрство, согласовать формат пересказа. Альтернатива — рисковать и закладывать в P&L бюджет на досудебные претензии.

3. 152-ФЗ «О персональных данных»

Это пункт, про который забывают чаще всего. 152-ФЗ применяется не только к базам клиентов и анкетам. Он применяется к любой обработке персональных данных физических лиц российскими операторами. Если в постах источника есть имена, фотографии, контактные данные людей — это персональные данные. Если вы автоматически собираете их и публикуете в своём канале для коммерческой деятельности, вы становитесь оператором обработки ПДн. И тогда возникает целый шлейф: согласия субъектов, уведомление РКН, локализация хранения на территории РФ, политика обработки ПДн на сайте.

Это особенно бьёт по тематике «новостных» агрегаторов, где постов с упоминаниями людей много по определению. Юридическая защита тут грубо одна: либо соблюдаете 152-ФЗ полным контуром, либо ограничиваетесь сюжетами, где идентифицируемых физлиц нет, либо работаете под лицензией СМИ, у которой свой режим. Content Pilot — инструмент. Ответственность за то, что именно вы публикуете и как, лежит на операторе канала, то есть на вас.

4. Условия платных подписок

Перепубликация контента из канала, в который вы вступили по платной подписке, почти всегда нарушает условия этой подписки — даже если про авторское право говорить не хочется. Платные приватные клубы, инвестиционные каналы, закрытые сообщества часто прямым текстом запрещают «вынос» материалов. Стандартный исход — вас выгоняют, деньги не возвращают, и в неприятных случаях на вас подают иск об упущенной выгоде. Технически Content Pilot не отличает «вы вступили в обычный приватный канал» от «вы вступили в платный канал и теперь сливаете его наружу» — это решение принимает пользователь, и рисует ему его собственная репутация, не наша.

Известные технические ограничения

Наконец, чисто инженерная часть — что у вас всё равно не получится, даже если по юридической линии всё чисто.

Rate limits Telegram. MTProto не публикует точных лимитов, но они есть и адаптивные. На практике userbot, который читает с интервалом меньше секунды между запросами, начнёт получать FloodWait с растущим временем ожидания. Content Pilot обрабатывает FloodWait через retry с экспоненциальным бэкоффом, фиксирует RATE_LIMITED в error_type источника и пишет об этом в UI. Если флуд-вейты приходят постоянно, это сигнал: либо у источников слишком высокая частота обновлений для одной сессии, либо аккаунт уже на грани, либо ваш IP попал в плохой пул.

Прокси для парсера. На уровне инфраструктуры мы поддерживаем подключение прокси через админку (/admin/proxies) — это нужно, чтобы распределить трафик по IP, не палить все запросы с одного хоста и не упираться в региональные ограничения. Шедулер-джоб check_proxies.py по cron проверяет работоспособность каждого прокси, и нерабочие автоматически выводятся из ротации. Хорошие резидентные прокси стоят денег, плохие — это путь в чёрный список Telegram быстрее, чем без прокси вообще.

Риск блокировки аккаунта. Это must-know. Telegram заблокирует userbot-аккаунт, если посчитает паттерн подозрительным, и обжаловать это сложно. Самые частые причины блокировки в моей практике:

  • аккаунт используется только для чтения, никогда ничего не отправлял;
  • аккаунт зарегистрирован на номер из «горячего» пула виртуальных операторов;
  • сессия живёт на сервере с IP, который уже отметился у Telegram как источник автоматики;
  • резкое массовое вступление в каналы за короткое время после регистрации.

Никогда не подключайте к парсеру свой основной личный аккаунт. Заведите рабочий, дайте ему «настояться» — пусть он несколько дней живёт обычной жизнью с перепиской, прежде чем вы выльете на него парсинг 50 источников. Этот совет не специфичен для Content Pilot, он универсальный для любой работы через Telethon, и я повторяю его на каждой консультации.

Архивы старше недели. MAX_POST_AGE_DAYS = 7 — это намеренный продуктовый выбор. Мы не ходим в архив каналов, потому что (а) для генерации ленты это не нужно, актуальные посты дают лучший результат, (б) массовое вычитывание истории каналов — самый явный паттерн «парсера», на который реагирует анти-абуз Telegram, (в) это и юридически грязнее, потому что включает контент, который мог быть удалён автором или отредактирован.

Дедупликация и идемпотентность. Уникальный констрейнт (source_id, external_post_id) страхует БД от двойных записей, но не страхует от двойного парсинга. Если шедулер запустился внахлёст и оба инстанса дёрнули MTProto — Telegram увидит это как два почти одновременных messages.getHistory с одной сессии. Лимиты от этого только растут. Поэтому в проде у нас один инстанс шедулера, не два, и я бы советовал держать это правило везде, где работает Telethon.

Закрытые группы и комментарии. Telethon в принципе позволяет читать сообщения в группах и тредах комментариев, но Content Pilot этого не делает. Источник у нас — это канал, не группа. Парсинг чатов — это совсем другая зона ответственности с гораздо более жёсткой границей по 152-ФЗ (там точно есть персональные данные — имена, юзернеймы, аватары участников), и мы туда сознательно не лезем.

Что в итоге

Я не пытался отговорить вас использовать парсинг приватных каналов. Это легитимный сценарий — для собственных каналов в своём управлении, для отраслевых пабликов, в которые вы вступили по работе, для информационных лент, где вы агрегируете с согласия источников или в рамках разумного цитирования. Я пытался показать, что инструмент серьёзный и работает на трёх стыках одновременно: технический (MTProto и его лимиты), безопасностный (StringSession как ключ от всего аккаунта), юридический (ToS, авторское право, 152-ФЗ, договоры подписки).

Что мы делаем со своей стороны: шифруем сессии Fernet, изолируем их по юзеру, ограничиваемся методами чтения, не отправляем сообщения от имени пользователя, фильтруем посты по семидневному окну, мониторим прокси, обрабатываем rate limits и показываем пользователю конкретную причину ошибки на каждом источнике. Что остаётся за вами: выбрать аккаунт, который не жалко, не лить туда платные подписки в нарушение их условий, понимать, что коммерческий агрегатор по 152-ФЗ — это операторская роль, и периодически проверять активные сессии в нативном клиенте Telegram.

Если у вас есть конкретные технические вопросы по нашей реализации Telethon-интеграции — пишите в поддержку прямо из бота, я лично отсматриваю тикеты с тегом «инфра». Архитектурные обсуждения и аудит исходников — отдельный разговор, и я к нему всегда готов.

Похожие статьи