Создание файлов
Структура проекта
Для корректной работы на BotHost создайте следующую структуру:
repository/
├── app.js # Серверный код Node.js
├── package.json # Настройки проекта
└── public/ # Папка с клиентскими файлами
├── index.html # HTML-страница
├── style.css # Стили
├── script.js # Клиентский JavaScript
└── images/ # Папка для изображений
Важно!
Структура папок критически важна! HTML/CSS/JS файлы должны быть внутри папки public, а не в корне.
Серверная часть (app.js)
Файл app.js отвечает за раздачу статических файлов:
// Сервер для раздачи статических файлов
const http = require('http');
const fs = require('fs');
const path = require('path');
// Порт из переменных окружения BotHost или 3000 по умолчанию
const PORT = process.env.PORT || 3000;
// MIME-типы для разных файлов
const mimeTypes = {
'.html': 'text/html',
'.css': 'text/css',
'.js': 'text/javascript',
'.json': 'application/json',
'.png': 'image/png',
'.jpg': 'image/jpeg',
'.gif': 'image/gif'
};
// Создаем HTTP-сервер
const server = http.createServer((req, res) => {
console.log(`Запрос: ${req.method} ${req.url}`);
// Нормализуем URL
let url = req.url;
if (url === '/' || url === '') {
url = '/index.html';
}
// Определяем путь к файлу
const filePath = path.join(__dirname, 'public', url);
const extname = path.extname(filePath);
const contentType = mimeTypes[extname] || 'text/plain';
// Читаем файл
fs.readFile(filePath, (error, content) => {
if (error) {
if (error.code === 'ENOENT') {
// Файл не найден
res.writeHead(404);
res.end('Файл не найден');
} else {
// Другие ошибки сервера
res.writeHead(500);
res.end(`Ошибка сервера: ${error.code}`);
}
} else {
// Отдаем файл
res.writeHead(200, { 'Content-Type': contentType });
res.end(content, 'utf-8');
}
});
});
// Запускаем сервер
server.listen(PORT, '0.0.0.0', () => {
console.log(`✅ Сервер запущен на порту ${PORT}`);
});
Настройки проекта (package.json)
Файл package.json описывает ваш проект:
{
"name": "bothost-miniapp",
"version": "1.0.0",
"main": "app.js",
"scripts": {
"start": "node app.js"
}
}
Для мини-апп достаточно минимального package.json без дополнительных зависимостей.
HTML-страница (public/index.html)
Основной файл, который увидят пользователи:
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Мой Mini App</title>
<!-- Обязательный скрипт для работы с Telegram -->
<script src="https://telegram.org/js/telegram-web-app.js"></script>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="container">
<header>
<h1>Мой Mini App</h1>
<p>Созданно на BotHost</p>
</header>
<main>
<p>Добро пожаловать в мое мини-приложение!</p>
<button id="main-button">Нажми меня</button>
</main>
</div>
<script src="script.js"></script>
</body>
</html>
Обязательно!
Скрипт <script src="https://telegram.org/js/telegram-web-app.js"></script> должен присутствовать в <head>.
Стили (public/style.css)
CSS с поддержкой темной и светлой темы Telegram:
:root {
/* Резервные цвета, если открыть не в Telegram */
--bg-color: #ffffff;
--text-color: #222222;
--hint-color: #999999;
--button-color: #9C32FF;
--button-text-color: #ffffff;
}
body {
/* Используем переменные Telegram для темы */
background-color: var(--tg-theme-bg-color, var(--bg-color));
color: var(--tg-theme-text-color, var(--text-color));
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
margin: 0;
padding: 0;
font-size: 16px;
}
.container {
max-width: 450px;
margin: 0 auto;
padding: 20px 16px;
}
header {
text-align: center;
margin-bottom: 30px;
}
h1 {
margin-bottom: 8px;
}
p {
color: var(--tg-theme-hint-color, var(--hint-color));
}
button {
background-color: var(--tg-theme-button-color, var(--button-color));
color: var(--tg-theme-button-text-color, var(--button-text-color));
border: none;
border-radius: 8px;
padding: 12px 20px;
font-size: 16px;
cursor: pointer;
margin-top: 20px;
}
JavaScript (public/script.js)
Основной скрипт для взаимодействия с Telegram:
// Инициализация Telegram Mini App
const tg = window.Telegram.WebApp;
// Сообщаем Telegram, что приложение готово
tg.ready();
// Расширяем на весь экран
tg.expand();
// Настраиваем кнопки
document.addEventListener('DOMContentLoaded', function() {
const mainButton = document.getElementById('main-button');
if (mainButton) {
mainButton.addEventListener('click', function() {
// Вибрация при нажатии
if (tg.HapticFeedback) {
tg.HapticFeedback.impactOccurred('medium');
}
// Показать нативную кнопку Telegram
tg.MainButton.setText('ГОТОВО');
tg.MainButton.show();
tg.MainButton.onClick(function() {
tg.close();
});
});
}
});
// Логирование
console.log('Mini App загружено');
console.log('Тема: ' + tg.colorScheme);
Продвинутые функции
Использование MainButton
Telegram предоставляет специальную кнопку внизу экрана:
// Настройка главной кнопки
tg.MainButton.setText('ОТПРАВИТЬ');
tg.MainButton.show();
// Обработка нажатия
tg.MainButton.onClick(function() {
// Ваш код
// Отправка данных боту
tg.sendData(JSON.stringify({
action: 'submit',
data: { /* ... */ }
}));
});
Тактильная обратная связь
Для лучшего UX используйте виброотклик:
// Доступно на iOS/Android
if (tg.HapticFeedback) {
// Легкая вибрация при нажатии
tg.HapticFeedback.impactOccurred('light');
// Уведомление об успехе
tg.HapticFeedback.notificationOccurred('success');
}
Обмен данными с ботом
Mini App может отправлять данные боту:
// Отправка данных в бота
document.getElementById('submit').addEventListener('click', function() {
const data = {
name: document.getElementById('name').value,
email: document.getElementById('email').value
};
// Отправляем данные
tg.sendData(JSON.stringify(data));
// Закрываем Mini App
tg.close();
});
На стороне бота вы получите эти данные через событие web_app_data.