ИТ
December 1, 2024

Создание мини-приложения для Telegram: пошаговое руководство

Создание мини-приложения для Telegram: пошаговое руководство

Вы когда-нибудь хотели охватить миллионы пользователей без необходимости скачивать ваше приложение? Это именно то, что предлагают мини-приложения Telegram. Имея более 800 миллионов активных пользователей в месяц, Telegram предоставляет невероятную платформу для развертывания ваших приложений непосредственно в своей экосистеме.

Зачем создавать мини-приложение для Telegram?

- Мгновенный доступ: охватите миллионы пользователей без барьеров
в магазине приложений- Нативная интеграция: бесшовный опыт в интерфейсе
Telegram- Возможности в реальном времени: идеально подходит для приложений
для совместной работы- Отсутствие установки: пользователи могут сразу начать использовать ваше приложение

Во время недавнего проекта я разрабатывал приложение Telegram Mini и столкнулся с несколькими проблемами из-за ограниченного объема документации. Я решил поделиться тем, что узнал из этого.

В этом подробном руководстве мы создадим доску задач для совместной работы в группах Telegram с нуля. Вы узнаете, как:
- Настроить проект Next.js с помощью TypeScript
- Интегрировать Firebase для управления
данными в режиме реального времени- Развернуть свое мини-приложение в рабочей среде
- Справиться с распространенными ошибками и их решениями

Интересует создание миниапсов или ботов в телеграмме?

Хотите заказать или научиться делать сами?

Обращайтесь в наш канал Вокруг Крипты

Понимание объема проекта

Мы создаем систему управления задачами, которая позволяет участникам группы Telegram позволять:
- Создавать и управлять задачами вместе
- Отслеживать статус
выполнения задач- Удалять выполненные задачи
- Все это в режиме реального времени, прямо в Telegram

Архитектура проекта

Взаимодействие между различными компонентами
  1. Приложение Telegram Mini служит точкой входа для пользователей, обеспечивая бесшовную интеграцию с нативным интерфейсом Telegram. Он предлагает прямой доступ к функциям веб-приложения, позволяя пользователям взаимодействовать с приложением, не покидая среду Telegram.
  2. Next.js Web App — это ядро нашего приложения, обрабатывающее основную логику приложения с возможностями рендеринга на стороне сервера. Созданный с использованием TypeScript для повышения удобства обслуживания, он предоставляет полный набор функций и управляет всеми взаимодействиями с пользователем.
  3. Firebase действует как наша центральная система управления данными, обеспечивая возможности базы данных в режиме реального времени и выполняя аутентификацию. Он обеспечивает синхронизацию данных между всеми компонентами и предлагает надежные облачные функции для серверных операций.
  4. Telegram Bot управляет автоматическими уведомлениями и осуществляет прямую связь с Firebase. Он обрабатывает команды и предоставляет дополнительный функционал через интерфейс бота Telegram, дополняя опыт Mini App.

💡 Совет от > Pro: Этот урок предполагает базовые знания React и TypeScript. Если вы новичок в этих технологиях, сначала ознакомьтесь с их основами.

Давайте погрузимся и создадим что-то удивительное вместе!

Мини-приложение

Мы собираемся создать совместную доску заданий для групп Telegram. Это мини-приложение позволит членам группы создавать, назначать и управлять задачами вместе в интерфейсе Telegram.

Настройка проекта

Сначала мы создаем проект NextJs и устанавливаем необходимые пакеты:

npx create-next-app@latest task-board-telegramcd task-board-telegramnpm install @telegram-apps/sdk-react firebase @headlessui/react @heroicons/reactnpm install telegraf dotenv

Создание веб-приложения

После настройки нашего проекта давайте создадим простую доску задач, которая будет интегрироваться с группами Telegram. Сейчас мы просматриваем каждый файл. Если вы не хотите проходить, вы можете клонировать репозиторий GitHub по этой ссылке https://github.com/Tanguyvans/task-board-telegram

1. Конфигурация Firebase

Прежде чем мы сможем использовать Firebase в нашем приложении, нам нужно правильно его настроить:

a) Создайте проект Firebase:
- Перейдите в [Консоль Firebase](https://console.firebase.google.com/)
- Нажмите "Добавить проект"
- Назовите свой проект (например, "task-board-telegram")

Создание проекта Firebase

б) Включите Firestore:
- В консоли Firebase перейдите в раздел "Firestore Database"
- Нажмите "Создать базу данных"
- Выберите "Запустить в тестовом режиме" для разработки
- Выберите место, ближайшее к вашим пользователям

Создание базы данных Firestore

c) Получите конфигурацию Firebase:
- Перейдите в настройки проекта (⚙️ иконка)
- На вкладке "Общие" прокрутите до "Ваши приложения"
- Нажмите на веб-иконку (</>)
- Зарегистрируйте свое приложение с псевдонимом
- Скопируйте объект firebaseConfig

Получить конфигурацию Firebase

г) Настройте переменные среды:
Создайте файл '.env.local' в корне вашего проекта:

NEXT_PUBLIC_FIREBASE_API_KEY=your_api_keyNEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=your_project.firebaseapp.comNEXT_PUBLIC_FIREBASE_DATABASE_URL=https://your_project.firebaseio.comNEXT_PUBLIC_FIREBASE_PROJECT_ID=your_project_idNEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=your_project.appspot.comNEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=your_sender_idNEXT_PUBLIC_FIREBASE_APP_ID=your_app_idNEXT_PUBLIC_FIREBASE_MEASUREMENT_ID=your_measurement_id

Во-первых, нам нужно настроить Firebase. Создадим новый файл 'app/lib/firebase.ts':

'use client';import { initializeApp } from "firebase/app";import { getFirestore, Firestore } from "firebase/firestore";const firebaseConfig = { apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY, authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN, databaseURL: process.env.NEXT_PUBLIC_FIREBASE_DATABASE_URL, projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID, storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET, messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID, appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID, measurementId: process.env.NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID};// Initialize Firebase only on client sidelet app;let db: Firestore;if (typeof window !== 'undefined') { app = initializeApp(firebaseConfig); db = getFirestore(app);}export { db };

2. Создание интерфейса задания

Создайте 'app/types/task.ts':

export interface Task {    id: string;    title: string;    completed: boolean;    createdAt: Date;    groupId: string;  }

3. Сборка компонентов

Создайте 'app/components/TaskForm.tsx':

"use client";import { useState } from 'react';import { addDoc, collection } from 'firebase/firestore';import { db } from '@/app/lib/firebase';interface TaskFormProps {  groupId: string;}export default function TaskForm({ groupId }: TaskFormProps) {  const [title, setTitle] = useState('');  const [error, setError] = useState<string | null>(null);  const handleSubmit = async (e: React.FormEvent) => {    e.preventDefault();    if (!title.trim() || !db) return;    try {      await addDoc(collection(db, 'tasks'), {        title: title.trim(),        completed: false,        createdAt: new Date(),        groupId,      });      setTitle('');      setError(null);    } catch (err) {      console.error('Error adding task:', err);      setError('Failed to add task');    }  };  return (    <form onSubmit={handleSubmit} className="space-y-4">      <input        type="text"        value={title}        onChange={(e) => setTitle(e.target.value)}        placeholder="Add a new task"        className="w-full px-4 py-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"      />      <button        type="submit"        className="w-full bg-blue-500 text-white py-2 rounded-md hover:bg-blue-600 transition-colors"      >        Add Task      </button>    </form>  );}
Создайте 'app/components/TaskItem.tsx':
"use client";import { useState } from 'react';import { updateDoc, doc, deleteDoc } from 'firebase/firestore';import { db } from '@/app/lib/firebase';import { Task } from '@/app/types/task';interface TaskItemProps {  task: Task;}export default function TaskItem({ task }: TaskItemProps) {  const [isCompleted, setIsCompleted] = useState(task.completed);  const toggleComplete = async () => {    setIsCompleted(!isCompleted);    await updateDoc(doc(db, 'tasks', task.id), {      completed: !isCompleted,    });  };  const deleteTask = async () => {    await deleteDoc(doc(db, 'tasks', task.id));  };  return (    <li className="flex items-center justify-between p-4 bg-white rounded-lg shadow">      <div className="flex items-center space-x-4">        <input          type="checkbox"          checked={isCompleted}          onChange={toggleComplete}          className="form-checkbox h-5 w-5 text-blue-600"        />        <span className={isCompleted ? 'line-through text-gray-500' : ''}>          {task.title}        </span>      </div>      <button        onClick={deleteTask}        className="text-red-500 hover:text-red-700"      >        Delete      </button>    </li>  );}

Создайте 'app/components/TaskList.tsx':

"use client";import { useState, useEffect } from 'react';import { collection, query, where, onSnapshot } from 'firebase/firestore';import { db } from '@/app/lib/firebase';import TaskItem from '@/app/components/TaskItem';import { Task } from '@/app/types/task';interface TaskListProps {  groupId: string;}export default function TaskList({ groupId }: TaskListProps) {  const [tasks, setTasks] = useState<Task[]>([]);  useEffect(() => {    const q = query(      collection(db, 'tasks'),      where('groupId', '==', groupId)    );        const unsubscribe = onSnapshot(q, (querySnapshot) => {      const taskList: Task[] = [];      querySnapshot.forEach((doc) => {        taskList.push({ id: doc.id, ...doc.data() } as Task);      });      setTasks(taskList);    });    return () => unsubscribe();  }, [groupId]);  return (    <ul className="space-y-4">      {tasks.map((task) => (        <TaskItem key={task.id} task={task} />      ))}    </ul>  );}

4. Развертывание на Vercel

Развернуть приложение Telegram Mini на Vercel очень просто:

a) Подготовьтесь к развертыванию:
- Убедитесь, что ваш код отправлен на GitHub
- Создайте [учетную запись Vercel](https://vercel.com)

b) Разверните приложение:
- Перейдите в [Vercel Dashboard](https://vercel.com/dashboard)
- Нажмите "Новый проект"
- Импортируйте свой репозиторий
GitHub- Настройте проект:
— Предустановка фреймворка: Next.js
— Корневой каталог: ./
— Команда сборки: следующая сборка
— Выходной каталог: .next

в) Переменные среды:
- В настройках проекта Vercel перейдите в раздел "Переменные среды"
- Добавьте все переменные среды Firebase:

NEXT_PUBLIC_FIREBASE_API_KEY=your_api_keyNEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=your_project.firebaseapp.comNEXT_PUBLIC_FIREBASE_DATABASE_URL=https://your_project.firebaseio.comNEXT_PUBLIC_FIREBASE_PROJECT_ID=your_project_idNEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=your_project.appspot.comNEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=your_sender_idNEXT_PUBLIC_FIREBASE_APP_ID=your_app_idNEXT_PUBLIC_FIREBASE_MEASUREMENT_ID=your_measurement_id

d) Развертывание:
- Нажмите "Развернуть"
- Дождитесь завершения
сборки - Ваше приложение будет доступно по адресу: "https://your-project.vercel.app"

Создание Telegram-бота

1. Создание бота с помощью BotFather

Во-первых, нам нужно создать нового бота с помощью BotFather от Telegram:
1. Откройте Telegram и выполните поиск по запросу [@BotFather](https://t.me/botfather)
2. Отправьте команду
'/newbot' 3. Выберите имя для вашего бота
4. Выберите имя пользователя (должно заканчиваться на 'bot')5
. Важно: Сохраните предоставленный токен API, он вам понадобится позже

Создание Telegram Bot

2. Настройка команд бота

Настройте команды бота с помощью команды '/setcommands' от BotFather:

Настройка команд в Telegram Bot

3. Строительный бот

Создайте новую директорию 'bot' в корне вашего проекта:

mkdir botcd botmkdir commands types

Обновление .env.local

NEXT_PUBLIC_FIREBASE_API_KEY=your_api_keyNEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=your_project.firebaseapp.comNEXT_PUBLIC_FIREBASE_DATABASE_URL=https://your_project.firebaseio.comNEXT_PUBLIC_FIREBASE_PROJECT_ID=your_project_idNEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=your_project.appspot.comNEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=your_sender_idNEXT_PUBLIC_FIREBASE_APP_ID=your_app_idNEXT_PUBLIC_FIREBASE_MEASUREMENT_ID=your_measurement_idBOT_TOKEN= bot_tokenWEBAPP_URL= your_vercel_url

Создадим 'bot/config.ts':

const dotenv = require('dotenv');const path = require('path');dotenv.config({ path: path.resolve(__dirname, '../.env.local') });module.exports = {  BOT_TOKEN: process.env.BOT_TOKEN,  WEBAPP_URL: process.env.WEBAPP_URL};

Создайте 'bot/index.ts':

const { Telegraf } = require('telegraf');const { BOT_TOKEN, WEBAPP_URL } = require('./config');if (!BOT_TOKEN) {  throw new Error('BOT_TOKEN must be provided!');}const bot = new Telegraf(BOT_TOKEN);// Basic commandsbot.command('start', (ctx: any) => {  ctx.reply('Welcome to TaskVaultBot! 🚀\nUse /help to see available commands.');});bot.command('help', (ctx: any) => {  ctx.reply(    'Available commands:\n' +    '/start - Start the bot\n' +    '/help - Show this help message\n' +    '/webapp - Open the Mini App'  );});bot.command('webapp', (ctx: any) => {  ctx.reply('Open Web App', {    reply_markup: {      inline_keyboard: [[        { text: "Open App", web_app: { url: WEBAPP_URL || '' }}      ]]    }  });});bot.launch().then(() => {  console.log('Bot is running...');});// Enable graceful stopprocess.once('SIGINT', () => bot.stop('SIGINT'));process.once('SIGTERM', () => bot.stop('SIGTERM'));

Запуск бота

Добавьте в свой 'package.json':

{  "scripts": {    "bot:dev": "ts-node bot/index.ts"  }}

Запустите бота:

npm run bot:dev
Взаимодействие с Telegram-ботом

Текущие ограничения

На этом шаге у вас должно быть полностью работающее приложение. Тем не менее, есть две основные проблемы:

Разрешения группового чата: бот не будет отвечать на команду «/webapp» в группах Telegram

Отсутствует идентификатор группы: при открытии мини-приложения вы получите сообщение о том, что мы не предоставили идентификатор группы.

Групповой чат Telegram

Интеграция с группами обработки

Чтобы сделать наше веб-приложение доступным в группах Telegram, нам необходимо зарегистрировать его как мини-приложение:

Создание мини-приложения с помощью BotFather

Чтобы сделать наше веб-приложение доступным в группах Telegram, нам нужно зарегистрировать его как Mini App:
— Открыть @BotFather в Telegram
— Отправить команду
'/newapp' — Выбрать своего бота из списка

Telegram создает новое приложение

— Введите имя для вашего мини-приложения
— Укажите URL-адрес вашего веб-приложения (из развертывания Vercel)
— Выберите короткое имя (необязательно)
— Загрузите фотографию профиля (необязательно)

Telegram Новый URL приложения

После завершения настройки BotFather предоставит вам URL-адрес мини-приложения.

Обновление кода

Обновите файл .env.local, указав новый URL-адрес мини-приложения:

WEBAPP_URL=https://t.me/YourBotName/Home  # Replace with your Mini App URL

Измените команду webapp в 'bot/index.ts', чтобы использовать URL-адрес мини-приложения:

bot.command('webapp', (ctx: any) => {  ctx.reply('Open Web App', {    reply_markup: {      inline_keyboard: [[        { text: "Open App", url: WEBAPP_URL || '' }      ]]    }  });});

💡 > **Профессиональный совет**: Обязательно перезапустите бота после обновления переменных среды, чтобы изменения вступили в силу.

Интеграция идентификатора группы

Чтобы наше мини-приложение работало в группах Telegram, нам нужно правильно передать и обработать идентификатор группы. Это позволяет нам управлять задачами, специфичными для каждой группы.

Обновление бота

Для начала модифицируем нашего бота так, чтобы он передавал идентификатор чата группы в мини-приложение:

bot.command('webapp', (ctx: any) => {  const chatId = ctx.chat.id;  // Encode le chatId en base64  const encodedGroupId = Buffer.from(chatId.toString()).toString('base64');    console.log('Chat ID:', chatId);  console.log('Encoded Group ID:', encodedGroupId);    ctx.reply('Open Web App', {    reply_markup: {      inline_keyboard: [[        { text: "Open App", url: `${WEBAPP_URL}?startapp=${encodedGroupId}` }      ]]    }  });});

💡 > **Примечание**: Мы используем кодировку base64 для безопасной передачи идентификатора чата через URL.

Обработка идентификатора группы в веб-приложении

Далее нам нужно обновить наше веб-приложение, чтобы получать и обрабатывать этот параметр. В Telegram Mini Apps мы можем получать параметры только через параметр 'startapp'.

Вот наша обновленная страница «page.tsx»:

"use client";import { Suspense, useEffect, useState } from 'react';import Image from "next/image";import TaskList from "./components/TaskList";import TaskForm from "./components/TaskForm";import { useLaunchParams } from "@telegram-apps/sdk-react";import dynamic from 'next/dynamic';// Créer un composant client-only pour le TaskBoardconst TaskBoardClient = dynamic(() => Promise.resolve(TaskBoard), {  ssr: false});function TaskBoard() {  const [groupId, setGroupId] = useState<string | null>(null);  const [error, setError] = useState<string | null>(null);  const [isLoading, setIsLoading] = useState<boolean>(true);  const launchParams = useLaunchParams();  useEffect(() => {    const initializeComponent = async () => {      try {        if (launchParams?.startParam) {          const encodedGroupId = launchParams.startParam;          try {            const decodedGroupId = atob(encodedGroupId);            console.log("Decoded Group ID:", decodedGroupId);            setGroupId(decodedGroupId);          } catch (error) {            console.error("Error decoding group ID:", error);            setError("Invalid group ID format");          }        } else {          console.log("No start_param available");          setError("No group ID provided");        }      } catch (error) {        console.error("Error in initializeComponent:", error);        setError("An error occurred while initializing the component");      } finally {        setIsLoading(false);      }    };    initializeComponent();  }, [launchParams]);  if (isLoading) {    return <div className="p-8">Loading...</div>;  }  if (error) {    return <div className="p-8 text-red-500">{error}</div>;  }  if (!groupId) {    return <div className="p-8">Please provide a valid group ID</div>;  }  return (    <div className="grid grid-rows-[auto_1fr_auto] min-h-screen p-8 gap-8">      <header className="flex items-center justify-between">        <Image          className="dark:invert"          src="/next.svg"          alt="Next.js logo"          width={100}          height={20}          priority        />        <h1 className="text-2xl font-bold">Task Board - Group {groupId}</h1>      </header>      <main className="flex flex-col gap-8">        <TaskForm groupId={groupId} />        <TaskList groupId={groupId} />      </main>      <footer className="flex justify-center text-sm text-gray-500">        Powered by Next.js      </footer>    </div>  );}export default function Home() {  return (    <Suspense fallback={<div className="p-8">Loading...</div>}>      <TaskBoardClient />    </Suspense>  );}

Основные детали реализации

1. Bot Side:
— Кодирует идентификатор чата группы в base64
— Передает его через параметр
URL startapp — Регистрирует идентификаторы для отладки

2. Сторона веб-приложения:
— Использует 'useLaunchParams' для получения параметра
'startapp' — Декодирует идентификатор группы из base64
— Обрабатывает различные состояния (загрузка, ошибка, успех)
— Рендеринг только на стороне клиента для предотвращения проблем с SSR

Результат

При правильной интеграции наше мини-приложение теперь легко справляется с задачами, специфичными для группы. Давайте рассмотрим, как это работает на практике:

Выполнение команд

При запуске команды «/webapp» в любой группе Telegram генерируется уникальная ссылка, содержащая закодированный идентификатор этой группы. Это позволяет пользователям получать доступ к мини-приложению непосредственно из группового чата, сохраняя групповой контекст на протяжении всего сеанса.

Особенности, специфичные для групп

Приложение Mini App теперь работает в режиме полного учета групп. Все задачи автоматически связываются с текущей группой, что гарантирует, что пользователи видят и управляют только задачами, относящимися к их группе. Обновления в режиме реального времени ограничиваются определенной группой, поддерживая изоляцию данных между различными группами Telegram.

Визуальная верификация

Для обеспечения прозрачности и отладки идентификатор группы отображается в заголовке мини-приложения. Этот визуальный индикатор помогает проверить, работают ли пользователи в правильном контексте группы. Когда пользователи переключаются между разными группами, они будут видеть отдельные списки задач, соответствующие действиям каждой группы.

Telegram Mini App в разных групповых чатах

💡 > **Совет от профессионала**: Протестируйте свое мини-приложение в нескольких группах, чтобы обеспечить надлежащую изоляцию задач между группами.

Заключение

Мы успешно создали мини-приложение для Telegram, которое демонстрирует ключевые концепции интеграции: создание мини-приложения, его интеграцию с группами Telegram и обработку параметров между ботом и веб-приложением. Этот пример дает прочную основу для понимания того, как работают мини-приложения Telegram и как разработать свои собственные.

Интересует создание миниапсов или ботов в телеграмме?

Хотите заказать или научиться делать сами?

Обращайтесь в наш канал Вокруг Крипты