Mini Apps (или же Web Apps) - это относительно новый и удобный способ добавления веб приложения прямо в интерфейсе Telegram. Используя JavaScript, становится возможным создавать бесконечное множество интерфейсов, которые смогут заменить полноценный веб-сайт.
Особенность Mini Apps заключается в том, что они поддерживают авторизацию, платежи одной кнопкой и возможность работать с данными пользователя, открывшего мини-приложение.
И сегодня мы попробуем создать приложение, взаимодействующее с данными пользователя, и развернем бота вместе с сайтом в облаке.
В этой статье мы
Инициализируем БД;
Напишем простой веб-сайт со взаимодействием с API Telegram, которое подключим в коде;
Стилизуем страницу под тему приложения Telegram с помощью API;
Научимся получать и обрабатывать данные со страницы;
Задеплоим сайт и бота в облако.
Так как это обучающий материал, мы не будем использовать различные фреймворки для фронта - сейчас это просто не нужно.
Создание бота, базовые настройки и деплой Web App сайта
Не думаю, что все нуждаются в объяснении процесса создания бота через @BotFather, поэтому сразу перейдем к включению Mini Apps.
Для этого используем команду /mybots и выбираем нового бота.
![](https://habrastorage.org/getpro/habr/upload_files/52a/c3e/5aa/52ac3e5aa1bb19bb577429c302a86fd3.png)
Нажимаем кнопку Bot Settings, далее Configure Mini App
В сообщении видим, что Mini App отключен для бота. Исправляем ситуацию, нажав на кнопку “Enable Mini App”
![](https://habrastorage.org/getpro/habr/upload_files/9a0/a9a/4c7/9a0a9a4c79f94e8f10ea44f090649553.png)
Бот попросит отправить ссылку на MiniApp, который будет открываться по кнопке “Открыть” в боте.
Тут самое интересное: для безопасности требуется развернуть сайт с безопасным протоколом https. В этом нам поможет Amvera Cloud, где предоставляется возможность получить бесплатный сертификат Let’s Encrypt при развертывании веб приложения. Помимо этого, есть и другие преимущества при использовании Amvera:
Удобная доставка обновлений кода через git. Обновление проекта на проде делается всего тремя командами в вашей IDE. Это проще, чем настройка VPS.
Вам не нужно делать никаких доп. настроек, сайт можно развернуть буквально за 5 минут. Задаете пару полей в конфигурации, загружаете файлы проекта и все само настраивается и запускается.
Бесплатный баланс при регистрации 111 рублей.
Деньги никуда не улетят, даже если вы не будете некоторое время пользоваться сервисом: вы платите только за работающее приложение.
Но довольно рекламы, перейдем к деплою пока пустого сайта.
Подготовка к деплою mini app
Создадим index.html с базовой структурой и текстом:
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div class="Main">
Lorem ipsum dolor sit amet consectetur adipisicing elit. Earum, temporibus ad mollitia hic vel repudiandae sit labore quaerat voluptatum id quisquam deleniti officia dicta harum sapiente, praesentium recusandae quas et?
</div>
</body>
</html>
Далее рекомендую следовать инструкциям нашей недавней статьи, где показан процесс деплоя простого сайта буквально за 10 минут.
Когда приложение будет готово, вам нужно будет активировать дефолтное доменное имя во вкладке “Настройки” проекта, поставив переключатель в положение “Включено”, после чего скопировать ссылку на сайт, предварительно проверив работоспособность.
Завершение настройки Web App
Теперь, когда есть ссылка на работающее приложение, вам остается лишь отправить ссылку бота и получить подтверждение, что все прошло успешно.
Проверим: кнопка “открыть приложение” отображается в боте!
![Проверка работоспособности mini app Проверка работоспособности mini app](https://habrastorage.org/getpro/habr/upload_files/a8f/769/8d8/a8f7698d84bdc7d94a81e376f3536b0c.png)
Работа с Telegram API внутри Mini App
Стилизация страницы под установленную тему клиента Telegram
Давайте добавим немного контента в index.html и подключим скрипт для работы с Telegram:
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
color: var(--tg-theme-text-color);
background: var(--tg-theme-bg-color);
}
.Main {
width: 100%;
padding: 25px;
text-align: center;
}
h1 {
margin-top: 40px;
margin-bottom: 10px;
}
img {
width: 70px;
margin: 30px auto;
}
.btn {
border: 0;
border-radius: 5px;
margin-top: 50px;
height: 60px;
width: 200px;
font-style: 20px;
font-weight: 500;
cursor: pointer;
color: var(--tg-theme-button-text-color);
background: var(--tg-theme-button-color);
}
</style>
</head>
<body>
<div class="Main">
<h1>Тестовое приложение</h1>
<img src="{{ url_for('static', filename='bot.png') }}" alt="123">
<p></p>
<button class="btn">Кнопка</button>
</div>
<script src="https://telegram.org/js/telegram-web-app.js"></script>
</body>
</html>
Разберем самое интересное из кода:
У блока btn и body появились стили color и background со значениями из переменных. Именно так выглядит подключение стилизации цветов под тему клиента Telegram. Например, если в приложении включена темная тема, то и в WebApp будет темная тема. Полный список подобных переменных вы можете посмотреть в официальной документации Telegram.
Добавлено подключение требуемого скрипта.
Стилизированы элементы и добавлено изображение по пути static/bot.png.
Вот так Mini App выглядит, если открыть его в темной и светлой теме:
![Темная тема Темная тема](https://habrastorage.org/getpro/habr/upload_files/40c/2e9/38a/40c2e938ad574618ff04582d1e69d4ab.png)
![Светлая тема Светлая тема](https://habrastorage.org/getpro/habr/upload_files/019/e80/5c1/019e805c1322787a98d8042778fcb0f8.png)
Часть из доступных переменных:
color-scheme: var(--tg-color-scheme)
- Устанавливает цветовую схемуvar(--tg-viewport-height)
- Текущая высота видимой области мини-приложенияvar(--tg-viewport-stable-height)
- Высота видимой области Mini App в его последнем стабильном состоянииbackground: var(--tg-theme-bg-color)
- Цвет заднего фона под тему Telegramcolor: var(--tg-theme-text-color)
- Цвет текста под тему Telegramvar(--tg-theme-link-color)
- Цвет ссылки под тему Telegramи другие
Все переменные можно без проблем добавить и использовать в CSS.
Функционирование Web App мини-приложения
Добавим новую форму, которая будет открываться после нажатия на кнопку. В этой форме мы чуть позже добавим метод доставки данных, записанных в поля input. Сейчас займемся функционированием кнопки и стилизацией формы.
По задумке мы сможем через MiniApp создавать мини-статьи с заголовком, описанием и основным текстом
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
color: var(--tg-theme-text-color);
background: var(--tg-theme-bg-color);
}
.Main {
width: 100%;
padding: 25px;
text-align: center;
}
h1 {
margin-top: 40px;
margin-bottom: 10px;
}
img {
width: 70px;
margin: 30px auto;
}
.btn {
border: 0;
border-radius: 5px;
margin-top: 50px;
height: 60px;
width: 200px;
font-style: 20px;
font-weight: 500;
cursor: pointer;
color: var(--tg-theme-button-text-color);
background: var(--tg-theme-button-color);
}
form {
display: none;
text-align: center;
}
input {
outline: none;
border-radius: 5px;
border: 2px solid #535353;
padding: 15px 10px;
margin: 10px 0 0;
background: var(--tg-theme-section-separator-color);
color: var(--tg-theme-text-color);
transition: all .2s;
}
input:focus {
border-color: var(--tg-theme-secondary-bg-color)
}
</style>
</head>
<body>
<div class="Main">
<h1>Тестовое приложение</h1>
<img src="{{ url_for('static', filename='bot.png') }}" alt="">
<p></p>
<button class="btn f-btn">Тест отправки данных</button>
</div>
<form class="test-form">
<input type="text" placeholder="Введите заголовок" class="title-inp">
<input type="text" placeholder="Введите описание" class="desc-inp">
<input type="text" placeholder="Введите текст" class="text-inp">
<button class="btn s-btn">Отправить</button>
</form>
<script src="https://telegram.org/js/telegram-web-app.js"></script>
<script>
let tg = window.Telegram.WebApp;
let fBtn = document.getElementsByClassName("f-btn")[0]
let sBtn = document.getElementsByClassName("s-btn")[0]
fBtn.addEventListener("click", () => {
document.getElementsByClassName("Main")[0].style.display = "none";
document.getElementsByClassName("test-form")[0].style.display = "block";
});
sBtn.addEventListener("click", () => {
tg.close();
});
</script>
</body>
</html>
Так, помимо новых стилей я добавил небольшой код в java script в котором самое главное - объявление переменной tg. Именно объект window.Telegram.WebApp позволяет нам получать данные от пользователя, инициализировать userdata, добавлять кнопки и многое другое, что может помочь нам работать с Mini App.
Здесь описаны все методы объекта.
Вот примеры как получать данные пользователя с помощью объекта tg:
tg.initDataUnsafe.user.username
- получение usernametg.initDataUnsafe.user.first_name
- получение first_name юзера
Описание основных параметров:
tg.initData - получение данных от пользователя как строку
tg.initDataUnsafe - получение данных от пользователя как объект
tg.isExpanded - проверяет открыто ли мини-приложение полностью по высоте (true/false)
tg.sendData(data) - отправка данных из приложения (в нашем случае из полей input)
tg.expand() - растянет окно во всю высоту
tg.close() - закрытие приложения
Важно отметить, что такие параметры как initData и initDataUnsafe работают, только если их запустить из меню команд бота, а sendData - только если через keyboard button. Под это приходится адаптироваться.
Доработаем обработчик нажатия на кнопку sBtn:
sBtn.addEventListener("click", () => {
let title = document.getElementsByClassName("title-inp")[0];
let description = document.getElementsByClassName("desc-inp")[0];
let text = document.getElementsByClassName("text-inp")[0];
let data = {
title: title.value,
desc: description.value,
text: text.value
}
tg.sendData(JSON.stringify(data));
});
Здесь я занес для удобства все данные внутри полей в переменные, добавил их словарь data и отправил форматированные данные в JSON.
Теперь, если нажать на кнопку, приложение закроется, а в диалоге появится системное сообщение, что мы успешно передали данные боту.
![](https://habrastorage.org/getpro/habr/upload_files/c26/e73/e98/c26e73e9867bec60e4b86a344aa01520.png)
Обработка данных ботом
Теперь все готово к написанию бота, который сможет преобразовать все данные в мини-статью.
Для начала создадим файл bot.py и напишем бота, который по команде /start отправит ReplyKeyboard, по которому мы и будем открыть web app.
Содержимое bot.py на данный момент
import asyncio
import logging
import os
from aiogram import Bot, Dispatcher, types
from aiogram.utils.keyboard import ReplyKeyboardBuilder
logging.basicConfig(level=logging.INFO)
bot = Bot(os.getenv("TOKEN"))
dp = Dispatcher()
@dp.message()
async def start(message: types.Message):
webAppInfo = types.WebAppInfo(url="your-webapp-url")
builder = ReplyKeyboardBuilder()
builder.add(types.KeyboardButton(text='Отправить данные', web_app=webAppInfo))
await message.answer(text='Привет!', reply_markup=builder.as_markup())
async def main():
await bot.delete_webhook(drop_pending_updates=True)
await dp.start_polling(bot)
if __name__ == "__main__":
asyncio.run(main())
Это самая стандартная структура асинхронного бота на aiogram 3 с добавленным методом отправки сообщения с ReplyKeyboard. Не забываем указать ссылку на webapp при объявлении webAppInfo.
Также в этом коде мы используем переменные окружения для безопасного хранения токена. Если вы будете использовать файл .env как хранилище переменных, вам нужно будет добавить следующие импорты и вызов функции (предварительно установив модуль python-dotenv):
from dotenv import find_dotenv, load_dotenv
load_dotenv(find_dotenv())
Добавим логику обработки и получения данных из Web App
Добавим новый хендлер, который будет обрабатывать data только из webApp
@dp.message(F.content_type == ContentType.WEB_APP_DATA)
async def parse_data(message: types.Message):
data = json.loads(message.web_app_data.data)
await message.answer(f'<b>{data["title"]}</b>\n\n<code>{data["desc"]}</code>\n\n{data["text"]}', parse_mode=ParseMode.HTML)
В этом коде мы схватываем данные из полученного json и отправляем в ответ сообщение с полученными данными.
Все! Теперь все готово, остается лишь запустить бота и проверить работоспособность
![](https://habrastorage.org/getpro/habr/upload_files/5be/9fd/50f/5be9fd50f8615cec2424f2086103383f.png)
Все работает! Теперь можно переходить к заключающему этапу - деплою бота.
Перед этим хотел бы сказать, что то, что мы сделали в этой статье - лишь малая часть возможного. Также мы сегодня не рассмотрели mainButton - одна из главных частей MiniApps. Все это с полученными сегодня знаниями вы сможете изучить самостоятельно в документации или почитать другие статьи.
Весь код
Здесь я собрал код чтобы вы могли быстро скопировать его и перейти к деплою
Web App
index.html:
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
color: var(--tg-theme-text-color);
background: var(--tg-theme-bg-color);
}
.Main {
width: 100%;
padding: 25px;
text-align: center;
}
h1 {
margin-top: 40px;
margin-bottom: 10px;
}
img {
width: 70px;
margin: 30px auto;
}
.btn {
border: 0;
border-radius: 5px;
margin-top: 50px;
height: 60px;
width: 200px;
font-style: 20px;
font-weight: 500;
cursor: pointer;
color: var(--tg-theme-button-text-color);
background: var(--tg-theme-button-color);
}
form {
display: none;
text-align: center;
}
input {
outline: none;
border-radius: 5px;
border: 2px solid #535353;
padding: 15px 10px;
margin: 10px 0 0;
background: var(--tg-theme-section-separator-color);
color: var(--tg-theme-text-color);
transition: all .2s;
}
input:focus {
border-color: var(--tg-theme-secondary-bg-color)
}
</style>
</head>
<body>
<div class="Main">
<h1>Тестовое приложение</h1>
<img src="{{ url_for('static', filename='bot.png') }}" alt="">
<p></p>
<button class="btn f-btn">Тест отправки данных</button>
</div>
<form class="test-form">
<input type="text" placeholder="Введите заголовок" class="title-inp">
<input type="text" placeholder="Введите описание" class="desc-inp">
<input type="text" placeholder="Введите текст" class="text-inp">
<button class="btn s-btn">Отправить</button>
</form>
<script src="https://telegram.org/js/telegram-web-app.js"></script>
<script>
let tg = window.Telegram.WebApp;
let fBtn = document.getElementsByClassName("f-btn")[0]
let sBtn = document.getElementsByClassName("s-btn")[0]
fBtn.addEventListener("click", () => {
document.getElementsByClassName("Main")[0].style.display = "none";
document.getElementsByClassName("test-form")[0].style.display = "block";
});
sBtn.addEventListener("click", () => {
let title = document.getElementsByClassName("title-inp")[0];
let description = document.getElementsByClassName("desc-inp")[0];
let text = document.getElementsByClassName("text-inp")[0];
let data = {
title: title.value,
desc: description.value,
text: text.value
}
tg.sendData(JSON.stringify(data));
});
</script>
</body>
</html>
app.py
from flask import Flask, render_template
app = Flask(__name__, template_folder='.')
@app.route("/")
def web():
return render_template('index.html')
if __name__ == "__main__":
app.run(debug=True, host="0.0.0.0", port='80')
Картинка bot.png в папке static
Бот
import asyncio
import logging
import json
import os
from aiogram import Bot, Dispatcher, types, F
from aiogram.utils.keyboard import ReplyKeyboardBuilder
from aiogram.enums.content_type import ContentType
from aiogram.filters import CommandStart
from aiogram.enums.parse_mode import ParseMode
logging.basicConfig(level=logging.INFO)
bot = Bot(os.getenv("TOKEN"))
dp = Dispatcher()
@dp.message(CommandStart())
async def start(message: types.Message):
webAppInfo = types.WebAppInfo(url="your-webapp-url")
builder = ReplyKeyboardBuilder()
builder.add(types.KeyboardButton(text='Отправить данные', web_app=webAppInfo))
await message.answer(text='Привет!', reply_markup=builder.as_markup())
@dp.message(F.content_type == ContentType.WEB_APP_DATA)
async def parse_data(message: types.Message):
data = json.loads(message.web_app_data.data)
await message.answer(f'<b>{data["title"]}</b>\n\n<code>{data["desc"]}</code>\n\n{data["text"]}', parse_mode=ParseMode.HTML)
async def main():
await bot.delete_webhook(drop_pending_updates=True)
await dp.start_polling(bot)
if __name__ == "__main__":
asyncio.run(main())
Деплой бота в облако Amvera
Регистрация в Amvera
Переходим по ссылке, нажимаем "регистрация" и вводим требуемые данные, подтверждаем email и номер телефона.
После подтверждения всех данных, мы получаем бесплатный баланс в размере 111 рублей, этого более чем достаточно для тестирования и деплоя нашего небольшого проекта!
Подготовка кода к деплою
Чтобы загрузить требуемые библиотеки в облако, нам нужно создать специальный файл зависимостей (requirements.txt), где будут перечислены все зависимости и их версии:
aiogram==3.10.0
В нашем проекте требуется установить только aiogram, все остальные библиотеки либо установятся вместе с aiogram, либо уже под капотом в Python.
Также понадобится создать файл конфигурации amvera.yml
Лучше всего воспользоваться графическим инструментом генерации amvera.yml.
Итого для деплоя бота нам понадобятся следующие файлы:
amvera.yml
requirements.txt
Создание проекта
Если все готово - идем в личный кабинет, чтобы открыть личный кабинет со всеми проектами.
В правом верхнем углу нажимаем кнопку “Создать” для начала процесса создания проекта.
Выбираем название проекта, его тариф.
![](https://habrastorage.org/getpro/habr/upload_files/f60/bbf/35a/f60bbf35a93967f935e024be9e89712d.png)
В следующем окне можно загрузить данные через интерфейс или инициализировать git. Пока можно пропустить. Также пропускаем создание конфигурации.
Настройка проекта и отправка кода в репозиторий Amvera
Когда проект создастся, нам нужно открыть его и перейти во вкладку “Переменные”, где мы зададим секрет - TOKEN.
![Создание секрета Создание секрета](https://habrastorage.org/getpro/habr/upload_files/1cd/b7c/684/1cdb7c68491061db09694caf501ff519.png)
Это все, что нужно было настроить. Можно отправлять код!
Настройка git и подключение к удаленному репозиторию Amvera
Как говорилось в начале статьи, Amvera использует git для доставки кода. Это удобно тем, что мы можем доставить правки буквально за 3 команды в терминале, даже не переходя на сайт.
К делу:
-
Инициализируйте локальный git репозиторий в корне проекта
git init
-
Привяжите локальный репозиторий к удаленному, по ссылке, которую вы сможете получить во вкладке “Репозиторий” проекта
git remote add amvera https://git.amvera.ru/ваш_юзернейм/ваш_проект
-
Добавляем файлы и делаем первый commit
git add . git commit -m "Коммит"
Отправляем код в Amvera
git push amvera master
Если вы добавляли конфигурацию через сайт, то, возможно, понадобится прописать команду git pull amvera master
Заключение
Если вы все сделали правильно, ваш проект в скором времени соберется и запустится! За этот небольшой урок мы рассмотрели основы Mini Apps в Telegram и показали на практике один из проектов для обучения.
Если что-то не получилось или произошла ошибка, вы всегда можете установить причину во вкладке “Лог приложения” или “Лог сборки” в зависимости от того, на каком этапе произошла ошибка.
Готово, мы написали и развернули Mini App в Telegram!
Упомянутые и релевантные статьи
Комментарии (9)
antirek
25.08.2024 13:40как проверить что веб-приложение выполняется в телеграме, для авторизованного пользователя телеграма?
anonymous
НЛО прилетело и опубликовало эту надпись здесь
MarkovM Автор
Думаю, ему сейчас пригодится не это
anonymous
НЛО прилетело и опубликовало эту надпись здесь
M_AJ
Предупреждать нужно, когда даете ссылки на контент опасный для мозга.
MountainGoat
Точно вылетел, а не влетел?
ArturN
Главное нам пригодится