Мой телеграм-бот не мог дозвониться до Telegram. Из Москвы

Сервер в России, домен в России, всё по закону. Бот написан, протестирован, развёрнут — и молчит. Дальше три часа, четыре тупика и одно решение, которое оказалось бесплатным. Если у вас российский хостинг, вы однажды пройдёте этот путь. Вот карта.

Я делаю ThoughtCheck — сервис, который показывает, есть ли за текстом понимание. Не «написал ли это ИИ» (честных детекторов ИИ не существует), а другое: если вычеркнуть из текста все обороты, которые ничего не утверждают, — что останется?

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

Бот был написан за вечер. Дальше начались приключения.

Ошибка первая: Network is unreachable

Мой телеграм-бот не мог дозвониться до Telegram. Из Москвы

Первая мысль — IPv6. Мысль хорошая: у api.telegram.org есть AAAA-запись, а в docker-сети IPv6 по умолчанию нет. Python берёт первый адрес из getaddrinfo, видит 2001:67c:4e8:f004::9, идёт туда и получает «нет маршрута» ещё до того, как что-либо отправит.

Лечится тремя строками:

Мой телеграм-бот не мог дозвониться до Telegram. Из Москвы

Подмена глобальной функции — приём грубый, но бот ходит ровно к двум хостам, и оба доступны по IPv4. Аккуратные способы требуют переписать половину http.client и ломаются на каждом обновлении Python.

Выкатил. Не помогло.

Errno 101 против Errno 110: разница, которая решает всё

Тут я сделал то, с чего надо было начинать: перестал гадать и написал диагностику. Не «работает / не работает», а по шагам — имя, маршрут до каждого адреса, TLS, сам API.

Мой телеграм-бот не мог дозвониться до Telegram. Из Москвы

И вот тут всё встало на места. Смотрите на первую строку: не «нет маршрута», а «истекло время ожидания». Это два разных диагноза, и путать их нельзя.

Мой телеграм-бот не мог дозвониться до Telegram. Из Москвы

Мой IPv4-фикс лечил первую болезнь. У меня была вторая. Провайдер не выпускает наружу на Telegram — не отбивает соединение, а просто роняет пакеты в тишину.

Час я чинил не ту проблему, потому что смотрел на текст ошибки, а не на её номер. Номер был во второй строке всё это время.

Тупик первый: MTProto-прокси не спасут

Естественный следующий шаг — прокси. Открываю телеграм, нахожу канал с бесплатными прокси, их там сотни, обновляются каждый день. Беру первый.

Не работает и работать не может. И это ошибка, на которую легко потратить вечер, поэтому объясню.

MTProto — протокол, по которому с серверами Telegram разговаривает приложение. Bot API — это обычный HTTPS на api.telegram.org. Разные протоколы, разные порты, разное назначение. Ссылка вида tg://proxy?server=…&secret=… включает прокси в вашем клиенте телеграма и на бота не влияет вообще никак.

Для бота нужен обычный HTTP-прокси с поддержкой CONNECT. Такие тоже есть бесплатно, но живут они дни, и в тот день, когда прокси выключат, бот замолчит без предупреждения. Не хотелось строить дом на песке.

Разворот: пусть Telegram приходит сам

И тут доходит очевидное. У задачи есть вторая сторона.

Бот не обязан ходить к Telegram. Bot API поддерживает вебхуки: Telegram сам делает HTTPS-запрос на ваш адрес. У меня есть домен, настоящий сертификат от Let's Encrypt и Caddy, который всё это уже обслуживает. Исходящие наружу в такой схеме не нужны совсем.

Но есть тонкость, о которой знают не все, а она тут ключевая: ответить Telegram можно прямо в теле HTTP-ответа. Не «принять обновление и потом позвонить в API» — а вернуть JSON с полем method, и Telegram выполнит его как обычный вызов:

{ "method": "sendMessage", "chat_id": 12345, "text": "Вода: 77 из 100", "parse_mode": "HTML" }

То есть исходящих соединений к Telegram не требуется вообще ни одного. Схема замкнутая.

Плата известна и ровно одна: один вызов на одно обновление. Отсюда две мелочи, которые пришлось принять. «Печатает…» не показывается — оно съело бы единственный слот. И кнопки под сообщением после нажатия не убираются: на это нужен второй вызов. От повторного голосования это всё равно не защищало — защищает одноразовый токен, а исчезновение кнопок было косметикой.

Мелочь, которая сэкономит вам полчаса.Регистрация вебхука (setWebhook) — единственное действие, которое требует выхода наружу. С заблокированного сервера она не пройдёт. Но делается она один раз, и откуда угодно: откройте ссылку в браузере на домашнем компьютере.

Настроил. Секретный путь, секрет в заголовке X-Telegram-Bot-Api-Secret-Token (без него любой, кто угадает путь, сможет слать боту что угодно от имени кого угодно). Caddy проксирует один маршрут на контейнер. Проверил curl'ом — 403, то есть запрос дошёл до бота и был отбит из-за отсутствия секрета. Всё правильно.

Пишу боту. Тишина.

Тупик второй: блокировка оказалась двусторонней

У Bot API есть метод getWebhookInfo, и он честно рассказывает, что Telegram думает о вашем сервере. Открываю:

{"ok":true,"result":{ "url":"https://thoughtcheck.ru/tg/cbf…", "pending_update_count":2,"last_error_message":"Connection timed out", "ip_address":"31.56.208.12" }}

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

Причём сайт при этом прекрасно открывается из-за границы — я проверил через check-host.net, бесплатно, за минуту. Значит режут не сервер, а именно Telegram. Это важное различие: оно определяет, какое решение брать.

Решение: посредник, который видит обе стороны

Если A не видит B, нужен C, который видит обоих. Cloudflare видит.

Telegram → Worker (Cloudflare) → thoughtcheck.ruи ответ тем же путём обратно

Cloudflare Workers: бесплатный тариф — 100 000 запросов в сутки, карта при регистрации не нужна, DNS трогать не надо, домен остаётся где был. Worker получает обновление, передаёт его на мой сервер, забирает ответ и отдаёт его Telegram. Весь код:

Мой телеграм-бот не мог дозвониться до Telegram. Из Москвы

Обратите внимание, чего здесь нет: логики. Worker не проверяет секрет, не разбирает обновление, ничего не решает и не хранит. Он передаёт запрос как есть и возвращает ответ как есть.

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

Тридцать строк, кнопка Deploy, переустановка вебхука на адрес *.workers.dev — и бот ожил. Настройки самого бота при этом не поменялись ни на строку: он по-прежнему в режиме вебхука и по-прежнему проверяет тот же секрет. Изменился только адрес, который знает Telegram.

Что я из этого вынес

1.Читайте номер ошибки, а не текст.«Network is unreachable» и «Connection timed out» на глаз одинаково безнадёжны. На деле это разные диагнозы с разным лечением, и отличаются они одной цифрой.

2.Диагностика окупается быстрее, чем кажется.Я потратил полчаса на команду--check, которая проходит путь по шагам и говорит, где оборвалось. Она окупилась на первом же запуске: сразу стало видно, что я чинил не ту беду. До неё я гадал.

3.Инструмент, которым нельзя воспользоваться под давлением, бесполезен.Моя проверка черезcurlтребовала двух подстановок и трёх видов кавычек. Она трижды подряд разъехалась при вставке в терминал и выдала «000» вместо ответа. Пришлось переписать в одну команду без аргументов

4.У задачи бывает вторая сторона.Я час искал способ выйти наружу, хотя можно было не выходить вовсе. Разворот направления решил задачу целиком — и решил бы её бесплатно с самого начала.5Не путайте протоколы, даже когда названия похожи.MTProto-прокси и Bot API живут в разных мирах. Проверить это стоило пять минут чтения документации, а не вечер экспериментов.

Собственно, ради чего всё затевалось

ThoughtCheck берёт текст и вычёркивает из него всё, что не является утверждением: обороты, связки, «безусловно, следует отметить». Показывает, что осталось. Обычно остаётся меньше, чем ожидаешь.

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

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

Бот работает без регистрации: пришлите или перешлите ему любой текст. Он же, по иронии, и есть герой этой статьи.

2