Telegram давно используют в рабочих целях: с помощью ботов можно автоматизировать большой спектр задач и даже делать интернет-магазины. В статье рассказываем, как разработать свою версию LAZZON прямо в мессенджере.
В рамках этой инструкции мы:
- научимся работать с тремя видами кнопок,
- реализуем интернет-магазин с корзиной,
- разработаем форму для сбора пользовательских данных,
- зальем интернет-магазин в облако.
С чего начать разработку
Сначала заведем два проекта: для разработки на front-end мы будем использовать React, а для back-end — node.js.
Начнем с того, что инициализируем react-приложение. Пока создается приложение в существующей папке, выберем путь через точку. Далее инициализируем backend, чтобы в файле .json появились базовые настройки.
Для удобной и актуальной разработки, как dev-зависимость устанавливаем пакет nodemon. Так мы сможем автоматически перезапускать бота после любых изменений в его коде.
Установим пакет API: лучше выбрать Node.js Telegram Bot API как более новую версию. Пока пакет устанавливается, можно перейти к BotFather и ввести команду
/newbot
.Вводим уникальное название бота (обязательно содержит Bot). В ответ BotFather отдаст токен, который лучше скопировать себе куда-то в блокнот (а лучше держать в секрете), поскольку он понадобится чуть позже.
Импортировать пакет API можно через
require
. Часть кода возьмем из документации, заменив демонстрационный токен на собственный.Разберем ситуацию, когда после команды/start
нужно отдать пользователю какие-то кнопки.
Для проверки напишем в боте команду/start
, и при нажатии на кнопку будет открываться окно с мини-браузером. Кроме этого, можно написать команду/setmenubutton
, выбрать бота, для которого будет использоваться кнопка, и прописать URL, куда она поведет.
Далее мы будем взаимодействовать с полем window.Telegram.WebApp.
import './App.css';
const tg = window.Telegram.WebApp;
function App() {
const onClose = () => {
f
}
return (
<div className="App">
work
<button onClick ={onClose}>Закрыть</button>
</div>
);
}
export default App;
Чтобы дебажить веб-приложение с Telegram-ботом онлайн, придется создать репозиторий на гите и привязать его в netlify. Получив ссылку на приложение, можно вставить ее в код.
Теперь кастомизируем кнопку. Изменим цвет, чтобы соответствовать айдентике мессенджера.
>>Header.jsx
import 'React' from 'react';
import Button from "../button/button";
import './Header.css';
const Header = () => {
const tg = window.Telegram.WebApp;
const onClose = () => {
tg.close()
}
return (
<div className={'header'}>
<button onClick={onClose}>Закрыть</button>
<span className={'username'}>{tg.initDataUnsafe?.user?.username}</span>
</div>
);
};
Каждый такой пуш будет заново триггерить сборку на netlify, a через 30 секунд мы получим обновленную версию приложения.
Теперь создадим отдельный хук для корректного получения объекта.
>>useTelegram.js
const tg = window.Telegram.WebApp;
export function useTelegram() {
const onClose = () => {
tg.close()
}
const onToggleButton = () => {
if(tg.MainButton.isVisible) {
tg.MainButton.show();
} else {
/основная кнопка взаимодействия с ботом/
}
return {
onClose,
tg,
user: tg.initDataUnsafe?.user,
}
}
Общение с ботом происходит при помощи метода
Telegram.WebApp.sendData
. Можно переходить к созданию страниц и маршрутов. >>Button.jsx
import React from 'react';
import './button.css';
const Button = (props) => {
return (
<button {...props} className={'button ' + props.className} />
);
};
export default Button;
Стили заносим в ProductList.css и переходим к следующему этапу.
>>ProductList.jsx
import React from 'react';
import './ProductList.css';
const ProductList = () => {
return (
<div>
ProductList
</div>
);
};
export default ProductList;
В index.js файле теперь нужно обернуть все наше приложение:
<React.StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</React.StrictMode>
Импортируем Route в App.js, выглядит это так:
import {Route, Routes} from 'react-router-dom';
/используется 6 версия/
Здесь же создаем две страницы для двух адресов:
<div className="App">
<Header />
<Routes>
<Route index element={<ProductList />} />
<Route path={‘form’} element={<Form />} />
</Routes>
</div>
Теперь в приложении по корневому пути будет открываться ProductList.
import React from 'react';
import './from.css';
const Form =() => {
return (
<div className={"form"}>
<h3>Введите ваши данные</h3>
<input className={'input'} type="text" placeholder={'Страна'} />
</div>
<select>
<option value ={'legal'}>Юр.лицо</option>
<option value ={'legal'}>Физ.лицо</option>
</select>
);
};
export default Form;
Форму можно взять отсюда, здесь все по стандарту. Чтобы форма теперь открывалась в боте, нужно дополнить путь в index.js:
[{text: ‘Заполнить форму’, web_app: {url: WebAppUrl + ‘./form’} }]
Теперь сделаем конфигурационный файл netlify.toml с опциями для редиректов. То есть, мы по любому маршруту делаем редирект в index.html.
[[redirects]]
from = "/*"
to = "/index.html"
status = 200
На этом этапе мы уже можем вводить данные в форму, но пока не можем их отправить.
Когда мы получаем данные из веб-приложения, мы можем их отправить. Заметьте, что функции, которые мы добавляем асинхронны.
>>index.js
await bot.SendMessage( chatId, text 'Спасибо за обратную связь!')
await bot.SendMessage( chatId, text 'Ваша страна: ' + data? .country );
await bot.SendMessage( chatId, text 'Ваша страна: ' + data? .street);
setTimeout(handler ()=> {
await bot.SendMessage( chatId, text 'Ваша страна: ' + data? .street);
} timeout 3000)
Отправка данных
Мы уже затрагивали использование метода
sendData
, а сейчас мы посмотрим как работать с теми данными, которые нам пришли. Здесь нам вместе с эффектом важно также прописать коллбек.useEffect( effect: ()=>)
сonst onSendData = useCallback( callback: () => {
}
Если данные приходят пустыми, проверьте массив зависимостей. Если в корзине есть хотя бы один товар, мы показываем кнопку.
if(NewItems.length === 0) {
tg.MainButton.show()
tg.MainButton.setParams( params {
text: ‘Купить ${}’
})
Чтобы посчитать общую стоимость товаров, добавим следующую строку в ProductList.jsx.
const getTotalPrice = (items) =>
return items.reduce((acc, item) => {
return acc += item.price
Так мы суммируем в функции стоимость товаров, чтобы потом получить getTotalPrice(newItems).
Осталось разобрать, как должна работать кнопка Купить, когда товары добавлены и стоимость рассчитана. Здесь нам поможет классический fetch-запрос.
fetch(input 'http://localhost:8000', init {
method ‘POST’.
headers: {
'Content-Type': 'application/json',
}
body: JSON.stringify(data)
}
На этом работу на front-end можно считать законченной, но нам еще нужно поднять сервер.
Серверная часть
Чтобы не было проблем с кросс-доменным запросами, бота мы будем деплоить на облачный сервер. Весь код, который нам потребуется можно найти в файле index.js. Воспользуемся документацией из этого раздела. Сейчас нас интересует вот эта часть:
app.post( path '/web-data, handlers (req, res) =>
const { queryId, products, totalPrice} = req.body;
const PORT = 8000
app.listen(PORT, callback () => console.log => ('server started on PORT ' + PORT))
QueryID в этом сценарии выполняет функцию связующего звена. С его помощью мы можем взаимодействовать с ботом.
app.post( path '/web-data, handlers (req, res) =>
const { queryId, products, totalPrice} = req.body;
try {
await bot.answerWebAppQuery (queryId, result {
type: 'article',
id: queryId,
title: 'Успешная покупка'
input.message.content {message_text 'Поздравляем с покупкой' +TotalPrice}
}
Это сообщение, которое мы уже будем отправлять пользователю, когда платеж пройдет. Для сообщения о неудачных оплатах можно скопировать этот код и вписать новый текст, который уведомит пользователя об ошибке. Разница в том, что в случае успеха http-запрос стоит завершить с кодом 200, в противном случае — 500.
В панели управления Selectel зайдем во вкладку Облачная платформа → Серверы и создадим новый.
Для деплоя бота есть отличный вариант — линейка виртуальных машин Shared Line. Это облачные серверы с гарантированной долей производительности ядра. Такое решение подходит пет-проектам, которым не нужна полная загрузка CPU. Зачем платить за целое ядро, если вся его мощность точно не потребуется?
Консоль для администрирования можно открыть прямо из панели. Сначала обновим инструмент работы с пакетами:
sudo apt update
Установим git:
sudo apt install git
, а затем клонируем проект при помощи обычной команды git clone <ссылка на репозиторий>
. Осталось установить npm, чтобы подтянуть все необходимые пакеты и node.js, чтобы запустить сервер. При необходимости, обновите версии sudo npm install -g n
. Далее лучше выполнить команду sudo n stable
, а не latest, чтобы все точно работало корректно.Теперь нужно установить зависимости с помощью классической команды npm install.
Дальше нам поможет менеджер процессов, чтобы, например, перезапускать сервер, если он упал или распараллелить процессы. Готово!
Запускаем бота командой
pm2 start index.js
— путь до нашего корневого файла. Во вкладке порты в панели Selectel можно увидеть IP-адрес, по которому бот будет доступен. Теперь в fetch
нужно заменить адрес localhost
на IP-адрес облачного сервера. Укажите также новый адрес до самого end-point. Заключение
Как мы видим, в разработке Telegram-бота с кнопками нет ничего сложного. Тем более для хостинга такого интернет-магазина не нужно оплачивать полную стоимость сервера.
Видеоверсия инструкции доступна по ссылке.
Комментарии (6)
goodmy
04.04.2023 16:26+1При первом открытие приложение спрашивает что будет расшарен IP, можно как нибудь управлять разрешениями? В официальных доках инфы не нашел.
chuikoffru
Вот бы еще скриншоты готового приложения…
erbanovanastasia Автор
Вы можете посмотреть на готовое приложение в видеоинструкции)