Эта статья написана для ламеров
Я как обычно учился кодить, и вдруг заметил что телеграм выпустил веб апи и теперь там есть фронт…
На тот момент я уже владел React, Flask и Django, и решил повысить планку с помощью FastAPI.
Веря в свои силы, я установил FastAPI и aiogram. Очевидно, мне следовало бы использовать нативные методы Telegram API, но aiogram предоставляет множество удобных функций, которые ускоряют разработку.
Проблема
Django и Flask используют шаблонизаторы (я использовал Jinja2), что означает, что код, выполняемый в браузере (HTML, JS, CSS, TS и т. д.), отправляется непосредственно с сервера. Таким образом, у такого приложения нет отдельного сервера для фронтэнда.
однако Fast-api и React, нуждаются в отдельных серверах …
Я понимаю, что найдутся люди, которые скажут: «Зачем использовать отдельный сервер для React, если можно добавить реактивность с помощью Knockout.js и создать веб-приложение на одном сервере?» — Да, это возможно, но мне кажется, что лучше использовать нормальные технологии, а не костыли на костылях, чтобы к моменту завершения проекта у вас был ценный опыт в резюме, а не повод для пенсии))
Я сталкивался с корпоративными проектами, написанными с использованием устаревшего Knockout,js и Python 2, все на одном монолите. Схема работает, к тому же можно нанимать джунов и платить им меньше, ведь всех обучают Django на курсах).
Решение
Вот у нас есть два сервера — FastAPI и React. Далее нам нужно их связать с Telegram API. Рассмотрим конкретный пример такой связи: разумеется, FastAPI и Telegram взаимодействуют через webhook. Чтобы их свести вместе, мы отправляем запрос Telegram на сервер, в котором указываем публичный адрес, полученный от ngrok, и токен бота из файла .env. В моем случае, я добавил адрес ngrok в .env, и FastAPI автоматически устанавливает связь с Telegram при запуске. Основная проблема заключается в том, что ngrok, даже в платной версии, поддерживает только один адрес, а у меня фронтэнд работает на отдельном порту. Однако здесь на помощь приходит localhost.run. Она имеет схожий функционал с ngrok, но настраивается чутка сложнее.
Вот план которого я бы придерживался
-
Запускаем localhost.run на порту 3000 и добавляем адрес в окружение бэка
-
запускаем React на порту 3000
-
Запускаем ngrok на порту 8000 и добавляем адрес в окружение
-
Запускаем Fast-api локально на порту 8000
План простой и надежный, как швейцарские часы.
1. localhost.run
Заходим на https://admin.localhost.run/ регаемся\логинимся.
Делаем shh ключ ориг. инструкция на англисском. Ключ состоит из 2х частей публичной и приватной. заходим в терминал и пишем
ssh-keygen -t rsa -b 2048 -C "<comment>"
*в комментарии рекомендуют указывать емейл
выбираем название ключа. и запоминаем путь. если не вводить имя то ключи будут сгенерированы с дефолтными именами id_rsa и id_rsa.pub
идем в директорию ключа, жмем ls -1 чтобы убедиться что пришли в нужное место и ключи готовы и ждут нас.
ls -1
id_rsa и id_rsa.pub это приватный и публичный ключи соответсвенно. Открываем id_rsa.pub с помощью vim или nano. Если нет vim то стаvim vim))
vim id_rsa.pub

копируем все что внутри(начиная с shh и до конца текста)
дальше выходим из вима для этого жмем “:” и печатаем q! и жмем ентыр.
идем в браузере на https://admin.localhost.run/#/keys вводим там этот ключ
дальше идем в консоль
ssh -R 80:localhost:3000 localhost.run
Что означет эта команда можнопочтитать в докуменции сервиса
Получаем наш белый адрес на порту 3000
добавляем полученный адрес в .env бэка
теперь настало время REACT
2. REACT
Итак чтобы чтобы обходиться без танцев, я сделяль виззард скрипт. Отмечу что он дает выбрать js/jsx, yarn/npm, и создает докер и докер компоуз с прокинутыми портами, а весь код вынесен в валиум поэтому проект не требует постоянной пересборки.
Итак, создаем create_react_compose_app.sh или скачиваем с github.
Делаем его исполняемым , на мак это делается вот так:
chmod +x create_react_compose_app.sh
Запустим скрипт:
bash create_react_copmpose_app.sh
Запускаем докер, после вводим в консоль
docker-compose up
И вот приложение открыто на порту 3000, теперь браузере идем по адресу который получили в localhost.run
Идем в
Открываем index.html и добавляем туда библиотеку телеграм
<script src="https://telegram.org/js/telegram-web-app.js"></script>
И заменим
<p> Edit <code>src/App.js</code> and save to reload.</p> <a className="App-link" href="https://reactjs.org" target="_blank" rel="noopener noreferrer" > Learn React </a>
На
<p> Hellow Habr. </p>
В src/App.js
проверяем по адресу который дал localhost.run
3. Ngrok
идем в браузере на https://ngrok.com/ Регаемся/логинимся
идем в https://dashboard.ngrok.com/get-started/setup
делам все по инструкции:
-
разархивировать zip
-
подставить ключ в конфиг, удобно что в документации сразу даются персональные команды с подставленным ключом
не надо ничего подставлять это готовая команда. оставлю тут ссылку на документацию ngrok -
запускаем ngrok на порту 8000
ngrok http 8000
4. Fast-API
заходим в исходную папку
создадим папку Fast-API используя команду
mkdir Fast-API
передем в нее
cd Fast-API
создадим main.py и README.md, .env c помощью уже известной команды touch. Легко запомнить про не есть песня у Daft Punk — Touch
создаем фаил и кладем туда адрес из localhost.run , ngrok , токенбота …
touch .env
Создаем venv
Да, нормальные пацаны юзают Poetry, но этот текст исключительно для ламеров, если готовы по уму делать сразу то вперед, тут будет через venv.
python3 -m venv venv
активируем его
source venv/bin/activate
обновляем
pip install --upgrade pip
ставим uvicorn
pip install uvicorn
ставим Fast-api
pip install fastapi
ставим aiogram
pip install aiogram
ставим python-dotenv, это один из тех пакетов которые в pip называются не так как импортируются
pip install python-dotenv
в README.md прописываем команду старта
$ uvicorn main:app --reload
открываем main.py
прописываем импорты и загружаем переменные из .env
from dotenv import load_dotenv from fastapi import FastAPI load_dotenv() # загружем .env в оперативку TOKEN = os.environ["TELEGRAM_TOKEN"] # получаем занчения переменных BACK_URL = os.environ["BACK_URL"] REACT_URL = os.environ["REACT_URL"] WEBHOOK_PATH = f"/bot/{TOKEN}" # формируем системные переменные из переменных окружения WEBHOOK_URL = BACK_URL + WEBHOOK_PATH
создаем приложение и прописываем проверочную функцию
app = FastAPI() @app.get("/") async def root(): return {"message": "Hello habr"}
запускаем сервер uvicorn с приложением app = FastAPI командой из README.md
uvicorn main:app --reload
переходим на BACK_URL в браузере , проверяем что все работает и мы правильно поставили и импортировали .env фаила также верное запустили FastAPI
импортируем aiogram и создаем функции обработчики сообщений И так далее
@app.on_event("startup") #обработчик запуска приложения, засылает вебхук в телеграм async def on_startup(): webhook_info = await bot.get_webhook_info() if webhook_info.url != WEBHOOK_URL: await bot.set_webhook( url=WEBHOOK_URL ) @app.post(WEBHOOK_PATH) #обработчик событий телгарма async def bot_webhook(update: dict): telegram_update = types.Update(**update) Dispatcher.set_current(dp) Bot.set_current(bot) await dp.process_update(telegram_update)
добавляем обработчик команды старь в телеграме
@dp.message_handler(commands="start") async def new_message(message: types.Message): text = 'REACT' keyboard = types.InlineKeyboardMarkup() keyboard.add(types.InlineKeyboardButton('Launch react', web_app=WebAppInfo(url=REACT_URL))) await bot.send_message(message.chat.id, text, reply_markup=keyboard) @app.on_event("shutdown") #и обработчик закрытия сесии async def on_shutdown(): await bot.get_session() await bot.session.close() logging.info("Bot stopped")
добавлю favicon.iso и вот полный код
import logging import os from aiogram import types, Dispatcher, Bot from aiogram.types import WebAppInfo from dotenv import load_dotenv from fastapi import FastAPI from fastapi.responses import FileResponse load_dotenv() TOKEN = os.environ["TELEGRAM_TOKEN"] BACK_URL = os.environ["BACK_URL"] REACT_URL = os.environ["REACT_URL"] WEBHOOK_PATH = f"/bot/{TOKEN}" WEBHOOK_URL = BACK_URL + WEBHOOK_PATH bot = Bot(token=TOKEN) dp = Dispatcher(bot) app = FastAPI() favicon_path = 'favicon.ico' @app.get('/favicon.ico', include_in_schema=False) async def favicon(): return FileResponse(favicon_path) @app.get("/") async def root(): return {"message": "Hello habr"} @app.on_event("startup") async def on_startup(): webhook_info = await bot.get_webhook_info() if webhook_info.url != WEBHOOK_URL: await bot.set_webhook( url=WEBHOOK_URL ) @app.post(WEBHOOK_PATH) async def bot_webhook(update: dict): telegram_update = types.Update(**update) Dispatcher.set_current(dp) Bot.set_current(bot) await dp.process_update(telegram_update) @app.on_event("shutdown") async def on_shutdown(): await bot.get_session() await bot.session.close() logging.info("Bot stopped") @dp.message_handler(commands="start") async def new_message(message: types.Message): text = 'REACT' keyboard = types.InlineKeyboardMarkup() keyboard.add(types.InlineKeyboardButton('Launch react', web_app=WebAppInfo(url=REACT_URL))) await bot.send_message(message.chat.id, text, reply_markup=keyboard) @app.post(WEBHOOK_PATH) async def bot_webhook(update: dict): telegram_update = types.Update(**update) Dispatcher.set_current(dp) Bot.set_current(bot) await dp.process_update(telegram_update)
сохраняем, запускаем, проверяем
В данной статье я описал настройку веб-приложения в контексте #Telegram и #Web_Apps for Bots. При желании, можно также включить использование FAST-API в контейнере с помощью #poetry, но это уже не для новичков. Кроме того, мы также коснулись вопросов #ssh ключей, #react, #python, #fast-api, #docker, #docker-compose, #ngrok, #localhost.run, #venv, #uvicorn и использовали #shell-script.
ссылка на оригинал статьи https://habr.com/ru/articles/743676/
Добавить комментарий