Skip to Content
Жора К

13 мин чтения


Как обрабатывать данные Telegram WebApp Bot без дополнительного HTTP сервера используя aiogram python библиотеку?

В статье разбираемся как написать telegram webapp бота на python? какими способами можно запустить telegram mini-app? как создать webapp с помощью aiogram? как обрабатывать данные telegram webapp без внешнего http-сервера? как работает tg.sendData?


Введение

В Telegram существует несколько способов обработки данных MiniApp (WebApp-приложений), которые зависят от способа загрузки приложения в Telegram-боте. Каждый из способов имеет свои особенности и ограничения, которые необходимо учитывать при разработке приложения. Некоторым способам требуется обработка callback на стороннем сервере для валидации данных, а другим достаточно просто отправить данные в бота с помощью JavaScript-функции и обработать их там. В этой заметке мы рассмотрим, как отправить данные в Telegram-боте с помощью aiogram без использования внешнего HTTP-сервера, каким приложениям подходит этот способ, а также какие ограничения существуют при его использовании. Весь исходный код приложения доступен на GitHub по ссылке в репозитории JoraDevLab/aiogram-webapp-keyboard-launch-starter, который можно использовать в качестве шаблона для создания своего бота.

Подготовка

Это руководство идеально подойдет, если вы уже умеете создавать ботов в Telegram с помощью BotFather, а также имеете базовые знания Python и принципов работы aiogram. В этой заметке мы сосредоточимся на механизмах отправки данных с помощью aiogram, а не на создании самого бота.

Для успешного запуска примеров кода вам понадобятся следующие инструменты:

  • Python 3.8+
  • aiogram 3.20+
  • PyCharm или другая удобная для вас IDE для разработки на Python
  • VSCode 2023.2+ или любая другая IDE для веб-разработки
  • Node.js 18.0+ и npm 8.0+ для локального тестирования webapp
  • Telegram-аккаунт

Способы запуска webapp

Веб-приложение может быть загружено в Telegram-боте несколькими способами:

  • через inline-клавиатуру (inline keyboard button),
  • через кнопку в меню бота (menu button),
  • через custom-клавиатуру (custom keyboard button).

Способы запуска webapp (картинка с сайта https://core.telegram.org/bots/webapps)

Какими способами можно получить данные пользователя в webapp?

  • initData / initDataUnsafe: строка, содержащая информацию о пользователе и его настройках. Она передается в webapp при его запуске и может быть использована для авторизации пользователя. Доступна только при запуске webapp через inline-клавиатуру или кнопку меню.
  • sendData: метод, позволяющий отправить данные из webapp в бота. Он может быть использован для передачи данных, собранных в webapp, например, формы или других пользовательских данных. Доступен при запуске webapp через custom-кнопку клавиатуры.

NOTE: initData и sendData

initData и sendData - это два разных способа передачи данных между webapp и ботом в Telegram, которые не могут быть использованы одновременно!

Как использовать initData в webapp?

При запуске WebApp в Telegram через inline-клавиатуру или кнопку меню (что с точки зрения Telegram API является разновидностью inline-кнопки) Telegram передает информацию о пользователе, query_id и API answerWebAppQuery для многократной отправки данных в бекенд бота. Упомянутые данные пользователя доступны в контексте JavaScript-приложения через объект window.Telegram.WebApp.initData и window.Telegram.WebApp.initDataUnsafe. Однако для корректной авторизации пользователя информация из initDataUnsafe должна быть отправлена на бекенд-сервер бота для проверки и валидации данных пользователя с использованием hash-алгоритма по hashId сообщения и токену бота (подробнее об алгоритме можно узнать по ссылке на официальную документацию Telegram). Таким образом, в этом случае необходимо использовать сторонний HTTP-сервер для валидации данных пользователя бота.

Шаблон приложения с запуском webapp по кнопке меню и авторизацией пользователей через отдельный HTTP-сервер можно найти в документации aiogram по ссылке github.com/aiogram/aiogram/tree/dev-3.x/examples/web_app.

Как использовать sendData в webapp?

В этом случае необходимо использовать метод window.Telegram.WebApp.sendData для передачи данных из webapp в бота. Этот метод позволяет отправить данные из webapp в бота без необходимости использования стороннего HTTP сервера. При этом данные передаются в виде строки и могут быть обработаны обычным обработчиком сообщений в боте.

Какие ограничения существуют при запуске webapp через custom keyboard button?

  • Информация пользователя не доступна в UI: в ui нет доступа ни к tg.initData, ни к tg.initDataUnsafe, соответственно невозможно отобразить данные пользователя в интерфейсе или использовать данные пользователя для 3rd-party авторизации.
  • Отправить данные из webapp можно только 1 раз: после старта webapp в боте отправить данные в бота с помощью tg.sendData можно только 1 раз за сессию вызова webapp.
  • Ограничение в 4 Кб на данные: максимальный размер данных, которые можно отправить в бота с помощью tg.sendData, составляет 4096 байт. Это ограничение связано с тем, что данные передаются через URL и могут быть ограничены длиной URL.

Таким образом способ с custom клавиатурой подходит для простых приложений, в которых интерфейс используется для заполнения форм и однократной отправки данных в бота для дальнейшей обработки. Если такое решение вам подходит, то предлагаю рассмотреть минимальную необходимую конфигурацию для создания подобного бота в дальнейших шагах.

Step 1. Создаем бота в BotFather

В первую очедерь нужно создать нового бота в @BotFather и получите токен доступа. Пропустите этот шаг, если у вас уже есть бот и токен доступа к нему. Для этого необходимо выполнить несколько простых шагов:

  • начать диалог с https://t.me/BotFather
  • ввести команду /newbot
  • ввести имя бота
  • ввести username бота
  • получить токен доступа к боту и сохранить его для следующих шагов.

Step 2. Пишем код бота на aiogram

В этом шаге создадим бота на aiogram, который будет обрабатывать данные, отправленные из webapp. Для запуска бота из примеров в этой заметке можно просто активировать необходимый venv(при наличии) и установить необходимые зависимости из requirements.txt:

pip install -r requirements.txt

При создании нового проекта на Python с нуля и установим необходимые зависимости:

pip install -U aiogram
pip install -U python-dotenv

Создадим файл .env в корне проекта и добавим туда токен доступа к боту полученный на предыдущем шаге и URL(который мы заполним позднее) для запуска webapp:

# .env
BOT_TOKEN=...
WEB_APP_URL=...

Создадим файл web_app_bot.py и добавим туда код бота.

В этом коде мы создаем бота, который обрабатывает команду /start и отправляет пользователю клавиатуру с кнопкой для запуска webapp. При нажатии на кнопку запускается webapp, который мы создадим в следующем шаге. При нажатии на кнопку "ℹ️ Information" бот отправляет пользователю сообщение с информацией о боте. При нажатии на кнопку "🚀 Launch WebApp" запускается webapp, который мы создадим в следующем шаге.

import asyncio
import logging
from os import getenv
 
import dotenv
from aiogram import Bot, Dispatcher, types, F
from aiogram.filters import CommandStart
from aiogram.types import WebAppInfo, ReplyKeyboardMarkup
 
dotenv.load_dotenv()
 
API_TOKEN = getenv('BOT_TOKEN')
WEBAPP_URL = getenv('WEB_APP_URL')
 
bot = Bot(token=API_TOKEN)
dp = Dispatcher()
logging.basicConfig(level=logging.INFO)
 
 
@dp.message(CommandStart())
async def start(message: types.Message):
    """Handle the /start command from user and print custom keyboard."""
    kb = [
        [
            types.KeyboardButton(text="🚀 Launch WebApp", web_app=WebAppInfo(url=WEBAPP_URL))
        ],
        [
            types.KeyboardButton(text="ℹ️ Information")
        ],
    ]
    keyboard = ReplyKeyboardMarkup(
        keyboard=kb,
        resize_keyboard=True,
        input_field_placeholder="Choose an action"
    )
    await message.answer("Welcome! Choose an action:", reply_markup=keyboard)
 
 
@dp.message(F.text == "ℹ️ Information")
async def info(message: types.Message):
    """Handle the information button and print predefined message."""
    await message.answer("This is a Telegram bot with a WebApp interface. Click the first button to launch the WebApp.")
 
 
#@dp.message(F.content_type == types.ContentType.WEB_APP_DATA)
@dp.message(F.web_app_data)
async def web_app_data_handler(message: types.Message):
    """Handle data sent from the WebApp."""
    await message.answer(f"✅ Data received from WebApp:\n{message.web_app_data.data}")
 
 
async def main() -> None:
    """Start the bot."""
    await dp.start_polling(bot)
 
 
if __name__ == "__main__":
    asyncio.run(main())

Step 3. Создаем UI

Для создания UI webapp можно использовать любой фреймворк для создания веб-приложений, например React, Vue.js или просто HTML и CSS. В этом примере мы создадим простое HTML-приложение с формой, которая будет отправлять данные в бота при нажатии на кнопку "Отправить" на чистом javascript. Создадим файл webapp.html в папке static и добавим туда следующий код:

Подключаем скрипт telegram-web-app.js в хедере страницы и создаем кнопку для отправки данных в бота.

<script src="https://telegram.org/js/telegram-web-app.js"></script>

Инициализируем объект tg и используем функуию tg.sendData для отправки заполненной формы и текущего времени в бота. Полный код страницы представлен ниже.

NOTE: --tg- CSS префикс

Переменные с префиксом --tg- (например, --tg-theme-bg-color, --tg-theme-text-color) — это пользовательские CSS-свойства (также известные как CSS-переменные), предоставляемые JavaScript-библиотекой Telegram WebApp. Эти переменные динамически задаются Telegram для отражения текущей темы приложения Telegram (например, светлой или темной) и позволяют разработчикам стилизовать свои WebApp в соответствии с интерфейсом Telegram.

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Telegram WebApp</title>
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <style>
    body {
      font-family: sans-serif;
      display: flex;
      flex-direction: column;
      align-items: center;
      padding-top: 50px;
      background-color: var(--tg-theme-bg-color, #ffffff);
      color: var(--tg-theme-text-color, #000000);
    }
    input {
      padding: 0.5em;
      font-size: 16px;
      margin-bottom: 20px;
      border: 1px solid var(--tg-theme-hint-color, #cccccc);
      border-radius: 4px;
      width: 80%;
      max-width: 300px;
    }
    button {
      padding: 1em 2em;
      font-size: 16px;
      background: var(--tg-theme-button-color, #2ea6ff);
      color: var(--tg-theme-button-text-color, white);
      border: none;
      border-radius: 8px;
      cursor: pointer;
    }
    button:hover {
      opacity: 0.9;
    }
  </style>
  <script src="https://telegram.org/js/telegram-web-app.js"></script>
</head>
<body>
  <h2>Telegram WebApp</h2>
  <input id="user-input" type="text" placeholder="Enter some data" />
  <button id="send-btn">Send Data</button>
  <script>
    const tg = window.Telegram.WebApp;
    tg.ready();
 
    document.getElementById('send-btn').addEventListener('click', () => {
      const userInput = document.getElementById('user-input').value;
      // const user = tg.initDataUnsafe.user; NOT SUPPORTED DURING INLINE BUTTON LAUNCH
 
      tg.sendData(JSON.stringify({
        timestamp: Date.now(),
        userInput: userInput
      }));
    });
  </script>
</body>
</html>

Интерфейс нашего webapp, открытого в Telegram

Step 4. Обработка web_app данных в боте

Напишем обработчик для данных, отправленных из webapp в бота, в файле web_app_bot.py на aiogram. За это отвечает фильтр F.web_app_data.

@dp.message(F.web_app_data)
async def web_app_data_handler(message: types.Message):
    """Handle data sent from the WebApp."""
    await message.answer(f"✅ Data received from WebApp:\n{message.web_app_data.data}")

В этом обработчике мы просто отправляем обратно данные, которые были отправлены из webapp в бота. В реальном приложении вы можете использовать эти данные для дальнейшей обработки, например, сохранить их в базе данных или отправить пользователю сообщение с результатами обработки.

NOTE: Фильтры F.web_app_data и F.content_type

Разница между использованием F.content_type == types.ContentType.WEB_APP_DATA и фильтром F.web_app_data в aiogram заключается в механизме фильтрации входящих обновлений. F.content_type == types.ContentType.WEB_APP_DATA - это более низкоуровневый фильтр, который проверяет тип контента сообщения. Он может быть использован для фильтрации сообщений по различным типам контента, включая текстовые сообщения, фотографии и т.д. В данном случае он проверяет, является ли сообщение типа WEB_APP_DATA. F.web_app_data - это более высокоуровневый фильтр, который специально предназначен для обработки данных, отправленных из WebApp. Он автоматически обрабатывает входящие сообщения, если они содержат поле web_app_data. Последний способ более универсален, так как наличие этого поля в сообщении гарантирует, что данные из webapp были получены.

Step 5. Как настроить локальный запуск Telegram WebApp?

Запускаем локально статический сервер из папки static с помощью npm-библиотеки serve:

cd static
npx serve

Проверяем, что сервер запущен и доступен по адресу http://localhost:3000/webapp.html. Если все работает, переходим к следующему шагу.

Теперь нужно сделать так, чтобы бот знал, по какому адресу доступен webapp. Telegram не позволяет запускать webapp по локальному адресу, поэтому нужно использовать публичный адрес по HTTPS-протоколу. Для тестирования нужно использовать сервис для порт-форвардинга, чтобы сделать локальный сервер доступным из интернета. Используем для этого VSCode Port Forwarding. Для этого откроем наш проект в Visual Studio Code. Переходим во вкладку PORTS, как на скриншоте, и нажимаем на кнопку "Forward a Port", вводим 3000. После этого внизу появится ссылка на ваш локальный сервер, который будет доступен из интернета. Переходим на пункт меню visibility и выбираем Public. Полученную ссылку вставляем в .env файл в переменную WEBAPP_URL.

Настраиваем форвардинг портов в VSCode

НЕ ЗАБУДЬТЕ ВЫКЛЮЧИТЬ ПОРТ-ФОРВАРДИНГ ПОСЛЕ ТЕСТИРОВАНИЯ, ЧТОБЫ НЕ ПОДВЕРГАТЬ СВОЙ КОМПЬЮТЕР РИСКУ.

Step 6. Запускаем бота и тестируем

  • Запустите бота командой python web_app_bot.py в терминале или в вашей IDE.
  • Начните диалог с ботом командой /start и перейдите в меню бота.
  • Нажмите на кнопку 🚀 Launch WebApp, чтобы открыть веб-приложение.
  • Введите данные в форму и нажмите кнопку "Отправить".
  • Проверьте, что данные были успешно отправлены в бота и обработаны - подтверждением этого будет служить системное сообщение в чате с ботом Data from the "🚀 Launch WebApp" button was transferred to the bot. как на скриншоте.

Пример использования webapp в Telegram

PROFIT!

Заключение

Спасибо что прочитали эту заметку до конца! Исходный код приложения доступен на GitHub по ссылке в репозитории JoraDevLab/aiogram-webapp-keyboard-launch-starter. Если у вас есть вопросы или предложения, не стесняйтесь открывать issue в репозитории, а также пожалуйста поставьте звезду ⭐ репозиторию если статья была для вас полезна. Удачи в разработке ваших Telegram WebApp ботов!