Telegram Mini App — это веб-приложения, которые открываются внутри мессенджера Telegram. Эти приложения создаются с использованием стандартных веб-технологий, таких как HTML, CSS и JavaScript, и выглядят как обыкновенные сайты.
Более подробно о том, что такое Telegram Mini App, мы рассказали в предыдущей статье, а сегодня рассмотрим, как создать собственный Mini App, используя React.
Для разработки Mini App понадобится установленный Node.js. Если Node.js не установлен, его можно скачать по ссылке. Рекомендуется оставить путь установки по умолчанию, чтобы избежать потенциальных проблем в будущем.
cloud
Теперь приступим к разработке Mini App. В качестве примера создадим карточную игру на память, где нужно будет находить одинаковые цвета и зарабатывать очки. Также будет возможность делиться своими очками в чатах Telegram.
Консоль: Запустите консоль Windows или другой терминал.
Выбор пути: Перейдите в директорию, где хотите создать проект. Например, для рабочего стола введите команду:
cd Desktop
Создание проекта: Выполните команду для создания нового проекта React:
npx create-react-app memory-game
Здесь memory-game
— название папки проекта. Можно выбрать другое название по вашему желанию.
Редактор: Откройте проект в редакторе кода. Рекомендую использовать Visual Studio Code (VS Code), но можно использовать другой редактор по вашему выбору.
В процессе разработки Telegram Mini App все выполняемые команды для установки библиотек и запуска проекта должны выполняться внутри директории проекта, поэтому, если у вас возникает какая-либо проблема при установке или запуске, проверьте, в какой директории вы находитесь.
Структура созданной папки выглядит таким образом. Необходимые для разработки файлы будем создавать в папке src
.
Введите в консоли npm start
, чтобы удостовериться, что React-приложение запускается локально. После ввода этой команды начнется процесс запуска проекта, и в браузере откроется новая вкладка.
Теперь установите библиотеку @telegram-apps/sdk
с помощью команды:
npm install @telegram-apps/sdk
В первую очередь нужно создать эмуляцию функциональности Telegram Mini App. Это нужно, чтобы в процессе разработки не было никаких ошибок из-за того, что код выполняется не в среде Mini App. Благодаря этому, в консоли браузера будет отображаться одна некритичная ошибка, и это предотвратит ситуацию, когда React-приложение не будет работать и будет выдавать длинный список ошибок. Откройте файл index.js
и пропишите следующий код:
import React from 'react';
import { createRoot } from 'react-dom/client';
import App from './App';
import { initMiniApp, mockTelegramEnv, parseInitData } from '@telegram-apps/sdk';
const initializeTelegramSDK = async () => {
try {
// Попытка инициализировать настоящее окружение Telegram
console.log("Инициализация окружения Telegram");
const [miniApp] = initMiniApp();
await miniApp.ready();
} catch (error) {
// В случае ошибки инициализируем фейковое окружение
console.error('Ошибка при инициализации Telegram:', error);
const initDataRaw = new URLSearchParams([
['user', JSON.stringify({
id: 99281932,
first_name: 'Andrew',
last_name: 'Rogue',
username: 'rogue',
language_code: 'en',
is_premium: true,
allows_write_to_pm: true,
})],
['hash', '89d6079ad6762351f38c6dbbc41bb53048019256a9443988af7a48bcad16ba31'],
['auth_date', '1716922846'],
['start_param', 'debug'],
['chat_type', 'sender'],
['chat_instance', '8428209589180549439'],
]).toString();
mockTelegramEnv({
themeParams: {
accentTextColor: '#6ab2f2',
bgColor: '#17212b',
buttonColor: '#5288c1',
buttonTextColor: '#ffffff',
destructiveTextColor: '#ec3942',
headerBgColor: '#fcb69f',
hintColor: '#708499',
linkColor: '#6ab3f3',
secondaryBgColor: '#232e3c',
sectionBgColor: '#17212b',
sectionHeaderTextColor: '#6ab3f3',
subtitleTextColor: '#708499',
textColor: '#f5f5f5',
},
initData: parseInitData(initDataRaw),
initDataRaw,
version: '7.2',
platform: 'tdesktop',
});
console.log('Mock Telegram environment initialized');
}
};
// Инициализация SDK
initializeTelegramSDK();
const container = document.getElementById('root');
const root = createRoot(container);
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
После этих действий можно начать писать главный код, в котором будет логика нашей игры.
Откройте файл App.js
. Добавим логику для карточек и колоды. Будем отслеживать состояние игры, прибавлять очки в случае выигрыша и сохранять эти очки в локальном хранилище, чтобы при закрытии Mini App они не сбрасывались. Еще будет ограниченное количество ходов — 15. Также будут окна выигрыша и проигрыша, которые будут появляться в определенные моменты.
import React, { useReducer, useEffect } from 'react';
import './App.css';
const generateDeck = () => {
const colors = ['#FF6347', '#4682B4', '#32CD32', '#FFD700', '#FF69B4', '#8A2BE2'];
const deck = [];
// Каждому цвету добавляем две карточки
for (let color of colors) {
deck.push({ color, matched: false });
deck.push({ color, matched: false });
}
// Перемешиваем колоду
return deck.sort(() => Math.random() - 0.5);
};
const initialState = {
deck: generateDeck(),
flipped: [],
matched: [],
turns: 0,
score: 0,
pendingReset: false,
gameOver: false,
};
const gameReducer = (state, action) => {
switch (action.type) {
case 'FLIP_CARD':
// Переворачиваем карточку
if (state.flipped.length < 2 && !state.flipped.includes(action.index) && !state.matched.includes(state.deck[action.index].color)) {
return { ...state, flipped: [...state.flipped, action.index] };
}
return state;
case 'CHECK_MATCH':
// Проверяем совпадение перевернутых карточек
const [first, second] = state.flipped;
if (state.deck[first].color === state.deck[second].color) {
const newMatched = [...state.matched, state.deck[first].color];
const isGameOver = newMatched.length === state.deck.length / 2;
return {
...state,
matched: newMatched,
score: isGameOver ? state.score + 1 : state.score,
flipped: [],
pendingReset: false,
gameOver: isGameOver,
};
} else {
return { ...state, pendingReset: true };
}
case 'RESET_FLIPPED':
// Сбрасываем перевернутые карточки
return { ...state, flipped: [], pendingReset: false };
case 'INCREMENT_TURN':
// Увеличиваем счетчик попыток
return { ...state, turns: state.turns + 1 };
case 'RESET_GAME':
// Сбрасываем состояние игры
return {
...initialState,
deck: generateDeck(),
};
default:
return state;
}
};
const App = () => {
const [state, dispatch] = useReducer(gameReducer, initialState);
// Проверка на совпадение перевернутых карточек
useEffect(() => {
if (state.flipped.length === 2) {
dispatch({ type: 'CHECK_MATCH' });
dispatch({ type: 'INCREMENT_TURN' });
}
}, [state.flipped]);
// Таймер для сброса перевернутых карточек
useEffect(() => {
if (state.pendingReset) {
const timer = setTimeout(() => {
dispatch({ type: 'RESET_FLIPPED' });
}, 1000);
return () => clearTimeout(timer);
}
}, [state.pendingReset]);
// Обработка клика на карточку
const handleCardClick = (index) => {
if (!state.gameOver && state.flipped.length < 2 && !state.flipped.includes(index)) {
dispatch({ type: 'FLIP_CARD', index });
}
};
const handlePlayAgain = () => {
dispatch({ type: 'RESET_GAME' });
};
return (
<div className="App">
<h1>Memory Game</h1>
<div className="info">
<p>Очки: {state.score}</p>
<p>Попытки: {state.turns}/15</p>
</div>
<div className="deck">
{state.deck.map((card, index) => (
<div
key={index}
className={`card ${state.flipped.includes(index) || state.matched.includes(card.color) ? 'flipped show' : ''}`}
style={{ '--card-color': card.color }}
onClick={() => handleCardClick(index)}
/>
))}
</div>
{state.gameOver && (
<>
<div className="overlay" />
<div className="game-over">
<h2>Вы выиграли!</h2>
<button onClick={handlePlayAgain}>Заново</button>
</div>
</>
)}
{!state.gameOver && state.turns >= 15 && (
<>
<div className="overlay" />
<div className="game-over">
<h2>Игра окончена!</h2>
<button onClick={handlePlayAgain}>Заново</button>
</div>
</>
)}
</div>
);
};
export default App;
Также нужно написать CSS-стили, чтобы игра выглядела красиво. При написании CSS нужно понимать, что размещать элементы желательно в середине либо привязывать элементы к краям экрана, чтобы при их сдвиге сдвигались также и элементы приложения, ведь Mini App — это своего рода сайт, который оптимизирован под экраны телефонов.
Откройте файл App.css
. Заполните его следующим контентом:
body {
font-family: 'Arial', sans-serif;
background-color: #f0f0f0;
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
background: linear-gradient(to right, #ffecd2 0%, #fcb69f 100%);
}
.App {
text-align: center;
position: relative;
}
h1 {
font-size: 2.5rem;
margin-bottom: 5px;
color: #333;
}
.deck {
display: grid;
grid-template-columns: repeat(3, 100px);
grid-gap: 10px;
justify-content: center;
margin-bottom: 20px;
}
.card {
width: 100px;
height: 100px;
background-color: #fff;
border-radius: 10px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
cursor: pointer;
transition: transform 0.3s ease;
position: relative;
overflow: hidden;
}
.card.flipped {
transform: rotateY(180deg);
}
.card.show {
background-color: var(--card-color);
}
.info {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 20px;
font-size: 1.2rem;
}
.game-over {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: rgba(0, 0, 0, 0.8);
color: #fff;
padding: 20px;
border-radius: 10px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
z-index: 2;
}
.game-over h2 {
margin: 0 0 10px 0;
}
.game-over button {
background-color: #ff6347;
color: #fff;
border: none;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
transition: background-color 0.3s ease;
}
.game-over button:hover {
background-color: #ff4500;
}
.overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
z-index: 1;
}
После этого можно запустить React-приложение, введя npm start
в консоли вашего редактора. Вы увидите работающую игру. Карточки переворачиваются и, если совпадают цвета, остаются в раскрытом положении, а если нет, то скрываются через одну секунду. Очки прибавляются после того, как игра завершена. Попытки также правильно обрабатываются при ходе. Элементы окна проигрыша и выигрыша появляются в нужные моменты.
При создании Telegram Mini App нужно учитывать тот факт, что приложение должно быть оптимизировано под определённый размер экрана, и чтобы контролировать этот размер, можно проверять его с помощью DevTools в браузере. Чтобы открыть DevTools, достаточно нажать на клавишу F12. После этого нажмите на значок, на котором изображен ноутбук и телефон.
Теперь приложение будет выглядеть как сайт, который открыли на телефоне. Нужно немного подправить размеры экрана. В ширину (первая строка) введите 350, а в высоту (вторая строка) введите 670. Этот размер и будет примерным разрешением Mini App.
На данный момент в документации @telegram-apps/sdk
нет expand
. Он предназначен для того, чтобы при открытии Telegram Mini App на телефоне, оно открывалось во весь свой размер, а не на половину.
Чтобы добавить expand
, откройте файл index.html
, который располагается в папке public
, и добавьте в его начало после открывающего тега head
этот код:
<script src="https://telegram.org/js/telegram-web-app.js"></script>
<script>
Telegram.WebApp.expand();
</script>
Тут происходит подключение к библиотеке для создания Mini App напрямую через скрипт, а также используется expand
из этой библиотеки.
Добавим немного цвета header
-заголовку в Telegram Mini App, для этого добавим следующий код после инициализации Mini App в index.js
:
miniApp.setHeaderColor('#fcb69f');
Теперь header
выглядит следующим образом:
Еще добавим возможность поделиться своим счетом в других чатах Telegram с помощью MainButton
, которую предоставляет SDK. Изменим index.js
.
Импорт теперь выглядит следующим образом:
import { initMiniApp, initMainButton, mockTelegramEnv, parseInitData, initUtils } from '@telegram-apps/sdk';
И после установки цвета заголовка нужно добавить следующий код для того, чтобы брались очки из игры и подставлялись в сообщение. Это сообщение можно будет переслать в другие чаты в Telegram.
// Инициализация главной кнопки
const [mainButton] = initMainButton();
mainButton.setParams({
backgroundColor: '#aa1388',
text: 'Поделиться очками',
isVisible: true,
isEnabled: true,
});
mainButton.show();
const utils = initUtils();
// Установка обработчика нажатия на главную кнопку
mainButton.on('click', () => {
try {
// Получение текущих очков из localStorage
const score = localStorage.getItem('memory-game-score') || 0;
utils.shareURL(`Посмотрите! У меня ${score} очков в игре!`);
console.log('Окно выбора чата открыто для отправки сообщения.');
} catch (error) {
console.error('Ошибка при открытии окна выбора чата:', error);
}
});
Дальше нам нужно сделать приложение доступным для других пользователей. Для этого загрузим его код на GitHub, а после развернем приложение на сервере.
После того как Mini App будет готов, нужно будет загрузить файлы из папки на GitHub. Создайте новый приватный репозиторий на GitHub и затем вернитесь к вашему проекту. С помощью консоли (убедившись заранее, что находитесь в папке с проектом) загрузите все файлы в ваш репозиторий.
Вводите эти команды последовательно:
Инициализируем новый Git-репозиторий в текущей директории:
git init
Добавляем все изменения (новые, измененные и удаленные файлы) в текущей директории к следующему коммиту. Вводить команду нужно именно с точкой на конце.
git add .
Создаем коммит с сообщением «first commit», фиксируя все изменения, добавленные командой git add
.
git commit -m "first commit"
Переименовываем текущую ветку в main
и устанавливаем ее в качестве основной (default). Флаг -M
означает «переименовать».
git branch -M main
Добавляем удаленный репозиторий под именем origin
и связываем его с указанным URL. Вместо моей ссылки укажите ссылку на ваш новый созданный репозиторий.
git remote add origin https://github.com/Tulopex/memory-game
Отправляем изменения из локальной ветки main в удаленную ветку main
на репозитории origin
. Флаг -u
устанавливает связь между локальной и удаленной ветками, так что в дальнейшем можно будет просто использовать git push
без дополнительных параметров.
git push -u origin main
После загрузки наших файлов на GitHub можно переходить к загрузке приложения на сервер. Я буду запускать Mini App на React с помощью сервиса Apps от Timeweb Cloud. Если вы ещё не клиент Timeweb Cloud, предварительно нужно зарегистрироваться.
Для начала нужно создать новый проект. Введите его название. Также при желании можно добавить описание и аватарку проекту.
После того, как проект был создан, нужно создать Apps. Либо с главного экрана проекта, либо с помощью бокового меню. При создании нужно выбрать тип Frontend и фреймворк React.
Затем понадобится привязать GitHub-профиль для того, чтобы можно было загружать проекты. После привязки введите название репозитория, который вы указывали при создании. В «Регионе» выбираете тот, который располагается ближе к вам, где меньше всего пинг.
В конфигурации выбираете лимит запросов, который нужен. В настройках приложения ничего не меняйте.
В информации о приложении укажите его название, комментарий, если требуется, а также выберите проект, к которому будет привязано ваше приложение.
Далее можно нажимать на кнопку «Запустить деплой», после чего начнется автоматическое развёртывание вашего React-приложения на сервере. Через какое-то время приложение запустится, и в случае успешного развертывания проекта в логах деплоя будет написано “Deployment successfully completed”.
Когда приложение запустится, у вас также появится бесплатный домен, который будет привязан к приложению. Найти его можно в разделе «Настройки» вашего приложения.
Чтобы перейти по домену и проверить, всё ли работает, достаточно скопировать его и вставить в адресную строку вашего браузера.
Нужно создать и настроить Telegram-бота, для того, чтобы Mini App открывался внутри Telegram. Переходим в официального бота BotFather и запускаем его. После чего вводим команду /newbot
. Бот ответит, что нужно ввести название для бота. Когда ввели название, бот попросит ввести username. При этом, нужно, чтобы в конце должно быть слово bot. Например, если у вашего бота название Tetris, то username может быть таким:
Если вы введете занятый username, то бот ответит, что его использовать нельзя и нужно ввести другой, поэтому старайтесь придумывать уникальные названия вашему продукту.
Если все успешно, бот пришлет вам следующее сообщение.
При реальном создании ботов в Telegram никогда не сообщайте token бота людям, которым вы не доверяете.
Теперь можно настроить созданного бота. Введите команду /mybots
. Бот пришлет список ваших созданных ботов. Выберите нужный с помощью инлайн-кнопок. Откроется вот такое меню:
Нажав на Edit Bot, можно будет добавить аватарку, описание бота в пункт «О (about)», которое отображается при входе в бота, фотографию для описания, а также изменить имя бота.
Описание бота и его аватарка
Описание при входе в бота и фотография в описании
Эти действия можно пропустить, если приложение еще в разработке.
Нужно сразу добавить кнопку для открытия Telegram Mini App. Переходим в пункт «Bot Settings», в котором выбираем «Menu Button», а внутри «Configure menu button». Когда нажмете на эти кнопки, бот попросит прислать ему ссылку на ваше Mini App. Возвращаемся в Timeweb Cloud и берем ссылку, по которой располагается ваше приложение. Когда отправите ссылку, бот попросит ввести название для кнопки.
Теперь можно перейти в бота и нажать на кнопку «Запустить», чтобы отправилась команда «Start». Естественно бот ничего не ответит, ведь для этого нет написанного кода, но нас интересует не это, а кнопка в левом нижнем углу. При нажатии на нее Telegram Mini App откроется.
Также недавно появилась возможность добавлять скриншоты вашего приложения в описание вашего бота. На данный момент эта функция доступна лишь на телефоне и только спустя определенное время после создания бота (и только при условии, что вашим Mini App постоянно пользуются люди).
Вы создали Telegram Mini App и хотели бы показать его другим и выпустить глобально, но что делать, если вы хотите собирать статистику со своего Mini App? Помимо аналитики, если ваш Telegram Mini App связан с игрой, вам бы хотелось добавить монетизацию, чтобы люди смотрели рекламу и получали что-то взамен, а вы зарабатывали.
Недавно, появился сервис, который специализируется на сборе аналитики с Telegram Mini App. Называется он Telemetree. С его помощью можно собирать различную информацию:
На данный момент этот сервис находится в активной разработке, поэтому не исключено, что в будущем появятся новые отслеживания для разных событий.
Чтобы начать собирать информацию, необходимо зарегистрироваться на сайте проекта. После регистрации вам выдадут Application ID и Project ID. Эти ключи нельзя никому передавать и показывать.
Теперь нужно установить их библиотеку с помощью команды:
npm install @tonsolutions/telemetree-react --save
После установки откройте файл index.js
и добавьте импорт:
import { TwaAnalyticsProvider } from '@tonsolutions/telemetree-react';
Теперь обновите рендер таким образом, чтобы после рендера вашего приложения статистика начинала собираться. Замените projectId
и apiKey
на те значения, которые вам выдали при регистрации, а в appName
впишите название вашего продукта:
root.render(
<React.StrictMode>
<TwaAnalyticsProvider
projectId='YOUR_PROJECT_ID'
apiKey='YOUR_API_KEY'
appName='YOUR_APPLICATION_NAME'
>
<App />
</TwaAnalyticsProvider>
</React.StrictMode>
);
Выполнив эти действия, загрузите изменения на сервер с помощью git и, когда данные обновятся, а в Timeweb Cloud появится новая сборка, запустите ваш Telegram Mini App и нажмите на парочку кнопок, чтобы аналитика начала собираться. Вернитесь на сайт и перезагрузите страницу, а затем нажмите на сайте кнопку Proceed. Теперь статистика начнет собираться.
Для подключения монетизации в Telegram Mini App нужно воспользоваться сервисом Adsgram. Он выплачивает вознаграждение за просмотр рекламы в Ton.
Перейдите на сайт и создайте профиль как владелец приложений, нажав на кнопку «For app owner». После регистрации в левом верхнем углу нажмите «Create…» и выберите пункт «platform». В открывшемся окне нужно ввести название приложения, ссылку на Telegram Mini App и ссылку на сайт, на котором расположено приложение.
Для получения ссылки на Telegram Mini App нужно снова вернутся в BotFather и ввести команду /newapp
. Откроется список ваших ботов, где нужно выбрать того бота, к которому привязана ссылка на открытие Telegram Mini App. Бот попросит ввести название вашего приложения, краткое описание и отправить фотографию. Далее можно отправить GIF для приложения, но этот пункт можно пропустить, нажав на команду /empty
. Теперь бот попросит отправить ссылку на сайт, на котором развернуто ваше приложение. Для этого возвращайтесь в Timeweb Cloud и снова копируйте ссылку на сайт, на котором развернуто ваше Telegram Mini App. Теперь бот попросит отправить короткое имя для открытия Telegram Mini App.
Возвращайтесь на сайт Adsgram и в поле «Telegram Direct Link» введите эту ссылку для прямого открытия Mini App, которую получили у BotFather.
После создания платформы в верхнем правом углу нажмите на «Add ad block» для создания рекламного блока. Введите название для блока. В «Minimum cost per mine» советую не менять значение с «Do not fill in» на «Fill in». Также можно выбрать тип рекламы: она будет в виде видео либо в виде рекламного поста.
Когда создадите рекламный блок, откройте файл index.js
и добавьте в тег head
:
<script src="https://sad.adsgram.ai/js/sad.min.js"></script>
Теперь создайте новый файл под названием useAdsgram.js
. В него поместите следующий код:
import { useCallback, useEffect, useRef } from 'react';
export function useAdsgram({ blockId, onReward, onError }) {
const AdControllerRef = useRef(undefined);
useEffect(() => {
AdControllerRef.current = window.Adsgram?.init({ blockId });
}, [blockId]);
return useCallback(async () => {
if (AdControllerRef.current) {
AdControllerRef.current
.show()
.then(() => {
onReward();
})
.catch((result) => {
onError?.(result);
});
} else {
onError?.({
error: true,
done: false,
state: 'load',
description: 'Adsgram script not loaded',
});
}
}, [onError, onReward]);
}
После этого откройте файл app.js
, в котором находится вся логика игры. Мы добавим новый импорт, а также изменим логику игры для того, чтобы при появлении меню проигрыша было две кнопки: одна для рестарта игры, а другая для дополнительных ходов за просмотр рекламы. Теперь можно как начать играть заново, так и посмотреть рекламу, и за это добавится 5 дополнительных ходов.
Добавьте новый импорт для новосозданного хука:
import { useAdsgram } from './useAdsgram'; // Импортируем созданный хук
Далее в конце gameReducer
добавьте следующее:
case 'ADD_EXTRA_TURNS':
return {
...state,
turns: state.turns - 5,
};
А в компоненты игры, в const App
, добавьте:
const showAd = useAdsgram({
blockId: 'YOUR_BLOCK_ID',
onReward: () => dispatch({ type: 'ADD_EXTRA_TURNS' }),
onError: (error) => console.error('Error showing ad:', error),
});
Здесь в blockId
замените значение на ваш ID блока, которое можно получить, нажав на «Show code» в меню созданного блока.
Затем добавьте новую кнопку в меню проигрыша:
<div className="game-over">
<button onClick={showAd}>+5 Ходов</button>
Теперь после проигрыша помимо кнопки «Играть заново» можно будет нажать ещё и на кнопку «+5 Ходов», благодаря которой будет показываться реклама, а пользователю будет прибавляться 5 дополнительных ходов.
По новым правилам площадки Adsgram, чтобы в вашем Telegram Mini App показывалась реклама, нужно пройти модерацию, написав в личные сообщения поддержки в Telegram.
Чтобы пройти модерацию, необходимо:
Приложить скриншот из BotFather, подобный этому:
Указать ссылку на вашу платформу в формате https://partner.adsgram.ai/platforms/xxx/
Платформы, которые не пройдут модерацию:
Подготовили для вас выгодные тарифы на облачные серверы
Создание Telegram Mini App с помощью React предлагает разработчикам возможности для создания интерактивных и удобных приложений, интегрированных в экосистему Telegram. В статье был представлен пошаговый процесс создания простой карточной игры на память, включая настройку окружения, написание основного кода, стилизацию приложения, подключение аналитики и монетизации.
Применяя эти знания, вы сможете не только создавать интересные и полезные Mini App, но и эффективно использовать ресурсы Telegram для их распространения и монетизации. Аналитика и возможности рекламной интеграции, рассмотренные в статье, помогут в дальнейшем развитии и улучшении ваших приложений.
Спасибо за статью! Все расписано подробно и грамотно!