Привет! Я Саша Омельяненко, тимлид в отделе Flutter в AGIMA. На одном из наших проектов мне нужно было сделать бота для Telegram. Я нашел инструкции, как создать только базового бота. Но как оформить его, добавить кнопки и повесить на них Listener, я не нашел.

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

Что будет уметь наш бот

Мы разработаем бота, который будет имитировать регистрацию пользователя и отдавать токен.

  1. При выполнении команды /start бот будет проверять, зарегистрирован пользователь или нет.

  2. Если пользователь не зарегистрирован, бот будет отправлять текст приветствия и выводить кнопки «Регистрация» и «Инструкция».

  3. Если пользователь нажимает кнопку «Регистрация», бот отправит запрос на бэк и получит токен.

  4. Если пользователь нажимает кнопку «Инструкция», бот покажет сообщение с инструкцией.

  5. Если пользователь зарегистрирован, то бот покажет текст и кнопку с инструкцией.

Теперь, когда мы определили функциональность нашего бота, приступим к его созданию.

1. Регистрация нового бота в Telegram

Для начала нужно зарегистрировать нового бота в Telegram. Для этого:

  1. Откройте Telegram и найдите бота BotFather.

  2. Напишите команду /newbot.

  3. Следуйте инструкциям BotFather для создания нового бота. Укажите имя бота и его Username.

  4. После успешной регистрации BotFather выдаст вам токен доступа (API Token). Сохраните его, он понадобится для взаимодействия с Telegram API.

2. Разработка бота

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

Создаем новый Dart-проект

dart create -t console-full dart_test_habr_bot

Устанавливаем зависимости

Добавьте зависимость teledart в ваш pubspec.yaml:

dependencies:
teledart: ^0.6.1

Архитектура

Здесь определим, из каких частей будет состоять бот.

  1. Инициализация.

  2. Оформление.

  3. Стартовые команды.

  4. Listener.

  5. Список команд.

Инициализация bin/dart_test_habr_bot.dart

  late TeleDart _teleDart;
  final telegram = Telegram(YOUR_BOT_TOKEN_FROM_BOT_FATHER);
  final username = (await telegram.getMe()).username;

  _teleDart = TeleDart(YOUR_BOT_TOKEN_FROM_BOT_FATHER, Event(username!));
  final repository = Repository();

  _teleDart.start();

Оформление

Тут мы можем пойти двумя путями. Оформить бота в проекте или использовать BotFather.

1. Через бота.

Добавим описание бота:

  1. Откройте BotFather и выберите команду /setdescription.

  2. Следуйте инструкциям для добавления описания вашему боту.

Добавим меню. Для этого используем команду /setcommands в BotFather. Укажите команды и их описания.

2. В проекте.

Добавим описание бота:

_teleDart.setMyShortDescription(
'Это бот для регистрации и управления аккаунтом', 'ru');

Добавление меню:

_teleDart.setMyCommands([
    BotCommand(command: '/start', description: '? Начать'),
    BotCommand(command: '/information', description: '?Инструкция'),
  ]);

Стартовые команды

Как я уже писал выше, у бота будет 2 стартовые команды — «Старт» и «Инструкция». Создаем отдельный файл lib/start_bot/start_bot.dart и добавляем туда 2 команды и Listener. Для команд и лисенера я создал mixin lib/start_bot/start_bot_mixin.dar

 late TeleDart _teleDart;
    _teleDart = teleDart;

    //Кнопка старт
    tabStart(teleDart: _teleDart);

    //Кнопка Инструкция
    tabInstruction(teleDart: _teleDart);

    //Слушатель
    listener(teleDart: _teleDart);

Для корректной работы в mixin я добавил 4 переменных, позже мы их инициализируем:

  var msgId = 0;
  var userName = '';
  var telegramTag = '';
  var token = '';

Кнопка «Старт»

 void tabStart({
    required TeleDart teleDart,
  }) {
    teleDart.onCommand('start').listen(
      (message) async {
        msgId = message.chat.id;
        userName = message.chat.firstName!;
         final Map<String, dynamic> response = 
         await repository.checkUser(chatId: msgId);
         final isRegist = response['isRegist'];
    
        
        if (!isRegist) {
          final button1 = InlineKeyboardButton(
            text: '?Регистрация',
            callbackData: 'regist',
          );
          final button2 = InlineKeyboardButton(
            text: '?Инструкция',
            callbackData: 'instruction',
          );

          teleDart.sendMessage(
            msgId,
            'Рады приветствовать вас $userName!?\n\nВы еще не зарегистрированны\n\n?Хотите зарегистрироваться? Жмите?',
            replyMarkup: InlineKeyboardMarkup(
              inlineKeyboard: [
                [button1],
                [button2],
              ],
            ),
          );
        } else {
          final button1 = InlineKeyboardButton(
            text: '?Инструкция',
            callbackData: 'instruction',
          );

          teleDart.sendMessage(
            msgId,
            'Привет, $userName!?\n\nВы уже проходили регистрацию\n\n?Хотите ознакомиться с инструкцией? Жмите?',
            replyMarkup: InlineKeyboardMarkup(
              inlineKeyboard: [
                [button1],          
              ],
            ),
          );
        }
      },
    );
  }

Кнопка «Инструкция»

void tabInstruction({
    required TeleDart teleDart,
  }) {
  final button1 = InlineKeyboardButton(
            text: '?Регистрация',
            callbackData: 'regist',
          );
          
  teleDart.sendMessage(
            msgId,
            'Кажется Вам нужна помощь!\n\nВсе просто\n\nЕсли Вы не зарегистрованны, для регистрации нажмите кнопку "Зарегистрироваться"',
            replyMarkup: InlineKeyboardMarkup(
              inlineKeyboard: [
                [button1],          
              ],
            ),
          );
  }

Listener

Теперь давайте добавим Listenеr, чтобы понимать, куда нажимает пользователь.

void listener({
    required TeleDart teleDart,
  }) =>
      teleDart.onCallbackQuery().listen(
        (onData) async {
          final data = onData.data!;
          switch (data) {
            case 'regist':
              _handleRegist(
                teleDart: teleDart,
                onData: onData,
              );
              break;
            case 'instruction':
              _handleInstruction(
                teleDart: teleDart,
                onData: onData,
              );
              break;            
            default:
              break;
          }
        },
      );

Список команд

Теперь давайте реализуем две наши команды.

// Кнопка регистрации
void _handleRegist({
    required TeleDart teleDart,
    required TeleDartCallbackQuery onData,
  }) {
    final Map<String, dynamic> response = 
         await repository.registUser(chatId: msgId);
         final token = response['token'];
    
    teleDart.sendMessage(
      onData.message!.chat.id,
      '?Вы активировали аккаунт\n\n?Вот Ваш токен?\n\n$token',      
    );
  }

// Кнопка инструкция
void _handleInstruction({
    required TeleDart teleDart,
    required TeleDartCallbackQuery onData,
  }) {
   final button1 = InlineKeyboardButton(
            text: '?Регистрация',
            callbackData: 'regist',
          );
          
  teleDart.sendMessage(
            msgId,
            'Кажется Вам нужна помощь!\n\nВсе просто\n\nЕсли Вы не зарегистрованны, для регистрации нажмите кнопку "Зарегистрироваться"',
            replyMarkup: InlineKeyboardMarkup(
              inlineKeyboard: [
                [button1],          
              ],
            ),
          );
  }

На этом этап разработки закончен. Я не стал описывать методы работы с сетью, т. к. это база. Теперь нам осталось упаковать всё в Docker и выложить наш проект на сервер.

3. Docker

Создайте файл Dockerfile в корне вашего проекта:

# Use the official Dart image from the Docker Hub
FROM google/dart:latest

# Set the working directory
WORKDIR /app

# Copy the pubspec.* files to the working directory
COPY pubspec.* ./

# Install the dependencies
RUN dart pub get

# Copy the rest of the application code to the working directory
COPY . .

# Build the application
RUN dart compile exe bin/main.dart -o bin/main

# Command to run the application
CMD ["bin/main"]

4. Подключение к удаленному VDS-серверу

Для подключения к удаленному серверу используйте SSH. Например, для подключения к серверу с IP-адресом 123.45.67.89 и пользователем user выполните команду:

ssh user@123.45.67.89

5. Запуск проекта на сервере

Сборка и запуск Docker-контейнера

  1. Скопируйте ваш проект на сервер.

  2. Перейдите в директорию проекта.

  3. Соберите Docker-образ:
    docker build -t my_telegram_bot .

  4. Запустите Docker-контейнер:
    docker run -d my_telegram_bot

Теперь ваш Telegram-бот должен запуститься и работать на удаленном сервере.

Заключение

Вот и всё! Вы создали и развернули Telegram бота на Dart. Надеюсь, этот гайд был полезен. Если у вас есть вопросы или предложения, пишите в комментариях.

Спасибо, что прочитали.

P. S. Интересные штуки по Flutter постит у себя в канале наш руководитель Саша Ворожищев. Из последнего — серия постов про Rive-анимации. Загляните!

Что еще почитать

Комментарии (0)