Ngrok и Telegram Webhook на localhost
На рабочем проекте мне поставили задачу по оптимизации работы эндпоинта вебхук от Telegram. Функционал уже был написан до меня и представлял из себя обычный Laravel API эндпоинт который парсит входящие данные и сохраняет в БД. Мне лишь требовалось проанализировать метрики нагрузки в Tideways и перевести парсинг на очереди, параллельно тестируя все локально.
Оcновные проблемы:
- Telegram не может отправлять запросы на localhost (бот работает только с публичными URL)
- Telegram требует HTTPS соединение (чтобы вебхук был защищенным)
Есть несколько вариантов решения. Выделю два более удобных на мой взгляд:
- Использовать Ngrok
- Настроить свой VPS и пробросить SSH порты на localhost
Ngrok в моем случае стал приоритетом, т.к. сервис сам создает публичный HTTPS-адрес который перенаправляет запросы на локальный сервер. Ниже опишу основные моменты которые стали полезны.
Установка и настройка Ngrok
Варианты установки можно найти на сайте Docs: Getting Started.
Мои примеры для Debian Linux.
Прежде всего нужно зарегистрироваться в сервисе ngrok.com.
Устанавливаем Ngrok.
curl -sSL https://ngrok-agent.s3.amazonaws.com/ngrok.asc \ | sudo tee /etc/apt/trusted.gpg.d/ngrok.asc >/dev/null \ && echo "deb https://ngrok-agent.s3.amazonaws.com buster main" \ | sudo tee /etc/apt/sources.list.d/ngrok.list \ && sudo apt update \ && sudo apt install ngrok
Проверяем версию.
Важно знать, т.к. от версии зависит структура
yml
конфига.
$ ngrok -v ngrok version 3.22.0
Добавляем в конфиг authtoken
.
authtoken
доступен в админке в разделе Getting Started: Your Authtoken.
Конфиг ngrok по умолчанию создается тут/home/your_name/.config/ngrok/ngrok.yml
, но можно разместить и в другом месте.
$ ngrok config add-authtoken $YOUR_AUTHTOKEN
Запускаем ngrok для проверки.
$ ngrok http http://localhost:80
ngrok (Ctrl+C to quit) Session Status online Account your_name@gmail.com (Plan: Free) Version 3.22.0 Region Europe (eu) Latency 73ms Web Interface http://127.0.0.1:4040 Forwarding https://e20b-188-114-192-46.ngrok-free.app -> http://localhost:80 Connections ttl opn rt1 rt5 p50 p90 0 0 0.00 0.00 0.00 0.00
На базовом уровне этого достаточно чтобы начать тестировать. Но в моем случае понадобилось решить еще несколько проблем:
- Настроить тестовый статический домен на стороне ngrok (по умолчанию создается динамический)
- Настроить домен для localhost (прописан в файле hosts для проекта)
- Настроить заголовок для Xdebug
Пример моего конфига:
version: "3" agent: authtoken: your-authtoken tunnels: tg_webhook: domain: tg-webhook-expert-test.ngrok-free.app addr: tg-webhook.local:80 proto: http host_header: "tg-webhook.local" inspect: false traffic_policy: inbound: - actions: - type: "add-headers" config: headers: cookie: "XDEBUG_SESSION=start"
Важно! Директивы конфига могут отличаться в зависимости от версии. В любом случае советую пользоваться разделом Docs.
domain
нужен для настройки статического домена, которые предварительно создается в админке в разделе Universal Gateway: Domains, иначе ngrok при каждом запуске формирует динамический домен который придется постоянно обновлять в Telegram (пример будет ниже).
addr
,proto
,host_header
,inspect
нужно для перенаправления на статический домен в файле hosts.
traffic_policy
и все что внутри, нужно для проброса заголовка с кукой для Xdebug.
Создание Telegram Bot
Telegram Bot который будет реагировать на сообщения в чате и пересылать их в наш сервис. Детально расписывать этот процесс не буду, т.к. в сети есть гайды на эту тему. Я начал с этих мест:
После создания бота мы получим token типа 110201543:AAHdqTcvCH1vGWJxfSeofSAs0K5PALDsaw
, который будем использовать для настройки нашего webhook.
Установливаем webhook.
$ curl -X POST "https://api.telegram.org/bot110201543:AAHdqTcvCH1vGWJxfSeofSAs0K5PALDsaw/setWebhook" \ -H "Content-Type: application/json" \ -d '{ "url": "https://tg-webhook-expert-test.ngrok-free.app/v1/telegram/webhook/", "max_connections": 40, "allowed_updates": ["message", "callback_query"] }'
{ "ok":true, "result":true, "description":"Webhook is already set" }
И проверяем настройки.
$ curl "https://api.telegram.org/bot110201543:AAHdqTcvCH1vGWJxfSeofSAs0K5PALDsaw/getWebhookInfo"
{ "ok":true, "result":{ "url":"https://tg-webhook-expert-test.ngrok-free.app/v1/telegram/webhook", "has_custom_certificate":false, "pending_update_count":0, "max_connections":40, "ip_address":"18.192.31.165", "allowed_updates":[ "message", "callback_query" ] } }
Тестирование на localhost
Запускаем ngrok.
ngrok start tg_webhook
ngrok (Ctrl+C to quit) Session Status online Account your_name@gmail.com (Plan: Free) Version 3.22.0 Region Europe (eu) Latency 73ms Web Interface http://127.0.0.1:4040 Forwarding https://tg-webhook-expert-test.ngrok-free.app -> http://tg-webhook.local:80 Connections ttl opn rt1 rt5 p50 p90 0 0 0.00 0.00 0.00 0.00
Проверяем webhook локально.
$ curl -X POST "http://tg-webhook.local/v1/telegram/webhook" -d '{"test":true}'
{"test":true}
Проверяем webhook через ngrok.
$ curl -X POST "https://tg-webhook-expert-test.ngrok-free.app/v1/telegram/webhook" -d '{"test":true}'
{"test":true}
Пишем сообщение в чате Telegram и Bot должен переслать его в наш сервис. Запрос будет примерно таким:
POST /v1/telegram/webhook HTTP/1.1 Content-Length: 409 X-Forwarded-Host: tg-webhook-expert-test.ngrok-free.app Accept-Encoding: gzip, deflate Connection: keep-alive Content-Type: application/json Cookie: XDEBUG_SESSION=start Host: tg-webhook.local X-Forwarded-For: 91.108.5.110 X-Forwarded-Proto: https { "update_id": 281145287, "message": { "message_id": 25, "from": { "id": 1624941878, "is_bot": false, "first_name": "Your", "last_name": "Username", "username": "your_username", "language_code": "en" }, "chat": { "id": 1624941878, "first_name": "Your", "last_name": "Username", "username": "your_username", "type": "private" }, "date": 1744737539, "text": "Test Message" } }