Привет! Я Саша Омельяненко, тимлид в отделе Flutter в AGIMA. На одном из наших проектов мне нужно было сделать бота для Telegram. Я нашел инструкции, как создать только базового бота. Но как оформить его, добавить кнопки и повесить на них Listener, я не нашел.
Пришлось разбираться с документацией Telegram, а это занятие не из приятных. Чтобы избавить вас от этого удовольствия, я решил написать гайд по разработке функционального Telegram-бота на Dart. Мы пройдем весь процесс от регистрации бота до его развертывания на удаленном сервере. Эта инструкция будет полезна Flutter-разработчикам уровня Junior и Middle.
Что будет уметь наш бот
Мы разработаем бота, который будет имитировать регистрацию пользователя и отдавать токен.
При выполнении команды /start бот будет проверять, зарегистрирован пользователь или нет.
Если пользователь не зарегистрирован, бот будет отправлять текст приветствия и выводить кнопки «Регистрация» и «Инструкция».
Если пользователь нажимает кнопку «Регистрация», бот отправит запрос на бэк и получит токен.
Если пользователь нажимает кнопку «Инструкция», бот покажет сообщение с инструкцией.
Если пользователь зарегистрирован, то бот покажет текст и кнопку с инструкцией.
Теперь, когда мы определили функциональность нашего бота, приступим к его созданию.
1. Регистрация нового бота в Telegram
Для начала нужно зарегистрировать нового бота в Telegram. Для этого:
Откройте Telegram и найдите бота BotFather.
Напишите команду /newbot.
Следуйте инструкциям BotFather для создания нового бота. Укажите имя бота и его Username.
После успешной регистрации 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
Архитектура
Здесь определим, из каких частей будет состоять бот.
Инициализация.
Оформление.
Стартовые команды.
Listener.
Список команд.
Инициализация 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. Через бота.
Добавим описание бота:
Откройте BotFather и выберите команду
/setdescription
.Следуйте инструкциям для добавления описания вашему боту.
Добавим меню. Для этого используем команду /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-контейнера
Скопируйте ваш проект на сервер.
Перейдите в директорию проекта.
Соберите Docker-образ:
docker build -t my_telegram_bot .
Запустите Docker-контейнер:
docker run -d my_telegram_bot
Теперь ваш Telegram-бот должен запуститься и работать на удаленном сервере.
Заключение
Вот и всё! Вы создали и развернули Telegram бота на Dart. Надеюсь, этот гайд был полезен. Если у вас есть вопросы или предложения, пишите в комментариях.
Спасибо, что прочитали.
P. S. Интересные штуки по Flutter постит у себя в канале наш руководитель Саша Ворожищев. Из последнего — серия постов про Rive-анимации. Загляните!