TelegramBot инструкция по созданию базового функционала для бота. (Часть 1)

  • Tutorial
Не так давно я предложил вам воспользоваться моим ботом для телеграмма.

Telegram Bot — помощник в планировании мероприятий

Получив много вопросов в личку, я понял, что вопрос создания ботов для Телеграм довольно популярен.

У меня спрашивали исходный код бота, просили поделиться конкретными примерами реализации тех или иных возможностей бота, просили подсказать тот или иной функционал по работе с ботом и сообщениями.

Я решил написать цикл статей, в которых хочу показать базовые вещи, которые будут нужны вам для написания своего бота.
Вас ждет Java, Maven, TelegramAPI, Lombok и Log4j.
Кому интересно — прошу под кат…

И сразу с козырей зайду :)

То, о чем просили в предыдущей статье.

Исходные коды по проекту вы можете найти тут .

В ветке "master" будет финальная версия бота, которого мы вместе с вами и напишем, в процессе появления новых статей.

Полный исходники по этой статье будет находится в ветке "Part1-Base".

В данной части мы создадим с вами базовый шаблон, который способен будет:

  1. регистрировать бота на серверах Телеграмм
  2. получать сообщения, написанные боту
  3. реагировать на команду /start

Создаем стандартный maven проект


(я упущу мануалы по работе со средой разработки и базовые вещи из Java программирования. Все эти вещи достаточно легко гугляться.А если не будет получаться — пишите вопросы).

Давайте посмотрим, какие зависимости нам будут нужны в этом проекте.

В первую очередь это Lombok. Что это и зачем — можно почитать, например, тут.

И обязательно добавим логгер. Я использую log4j и чуть дальше покажу вам как полезно его можно использовать при отладке данного приложения и более глубокого осмысления работы TelegramAPI.

<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.10</version>
            <scope>provided</scope>
        </dependency>
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>

И, т.к. мы пишем бота для Телеграм, то и саму библиотеку от Telegram:

<dependency>
            <groupId>org.telegram</groupId>
            <artifactId>telegrambots</artifactId>
            <version>3.5</version>
</dependency>

Структуру pom файла можно посмотреть тут
Структуру файла pom.xml можно увидеть тут.

Далее добавляем базовый класс Bot.java, который и будет реализовывать все наше взаимодействие с с сервисами Telegram. Работать мы будем с помощью LongPolling, т.к. этот метод не требует сертификатов шифрования и запускаться может с любой машины, которая имеет Java у себя на борту.

Для этого мы наследуемся от класса TelegramLongPollingBot и реализуем базовые методы:

public void onUpdateReceived(Update update)
public String getBotUsername()
public String getBotToken()


И от себя нам нужно будет добавить процедуру, которая будет подключать наш бот к TelegramAPI:

public void botConnect()

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.apache.log4j.Logger;
import org.telegram.telegrambots.TelegramBotsApi;
import org.telegram.telegrambots.api.objects.Update;
import org.telegram.telegrambots.bots.TelegramLongPollingBot;
import org.telegram.telegrambots.exceptions.TelegramApiRequestException;

@AllArgsConstructor
@NoArgsConstructor
public class Bot extends TelegramLongPollingBot {
    private static final Logger log = Logger.getLogger(Bot.class);

    final int RECONNECT_PAUSE =10000;

    @Setter
    @Getter
    String userName;
    @Setter
    @Getter
    String token;

    @Override
    public void onUpdateReceived(Update update) {
        // TODO: 11/18/2019 добавить обработку полученных сообщений
        log.debug("new Update recieve");
    }

    @Override
    public String getBotUsername() {
        return userName;
    }

    @Override
    public String getBotToken() {
        return token;
    }

    public void botConnect() {
        TelegramBotsApi telegramBotsApi = new TelegramBotsApi();
        try {
            telegramBotsApi.registerBot(this);
            log.info("TelegramAPI started. Look for messages");
        } catch (TelegramApiRequestException e) {
            log.error("Cant Connect. Pause " + RECONNECT_PAUSE / 1000 + "sec and try again. Error: " + e.getMessage());
            try {
                Thread.sleep(RECONNECT_PAUSE);
            } catch (InterruptedException e1) {
                e1.printStackTrace();
                return;
            }
            botConnect();
        }
    }
}

В принципе все. Данный код умеет подключаться к серверам Телеграмм и при получении адресованных ему сообщений будет фиксировать в лог файл информацию, что он получил Update.

Давайте создадим класс, который и будет запускать наше приложение.

Этим займется класс App.java. Ссылка на файл: тут

package com.example.telegrambot;

import com.example.telegrambot.bot.Bot;
import org.apache.log4j.Logger;
import org.telegram.telegrambots.ApiContextInitializer;

public class App {
    private static final Logger log = Logger.getLogger(App.class);

    public static void main(String[] args) {
        ApiContextInitializer.init();
        Bot test_habr_bot = new Bot("test_habr_bot", "1012522162:AAHLvPVqKF48LdqnsvS3l5YrJfvFey6dBa0");
        test_habr_bot.botConnect();
    }
}

Как вы видите, для работы бота нам понадобятся только «имя бота» и «токен», которые мы и передадим в конструкторе нашего класса Bot.

Имя и токен для бота вы можете получить у отца всех ботов :), написав ему в телеграмме.
Его ник: @BotFather
Общение с ним очень простое и лаконичное.

По команде
/newbot

В результате у вас должен получиться такой диалог:



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

Вы скажете, что я не очень аккуратен… Возможно, но отец ботов позволяет нам менять токены и отзывать те, что попали в публичный доступ. Потому с попробовав подключиться с моими данными, вы получите такое сообщение:

2019-12-04 13:44:25,252[DEBUG][main            ][ com.example.telegrambot.bot.Bot] - Bot token: 1012522162:AAF5D49N4NreEJfCk0aOsC-UnmIu_GLkoyc
2019-12-04 13:44:26,613[ERROR][main            ][ com.example.telegrambot.bot.Bot] - Cant Connect. Pause 10sec and try again. Error: Error removing old webhook

Так что с моими данными вам подключиться не получится. Регистрируйте свои. Новые данные укажите в классе App.java

Теперь запустив метод main в классе App мы позволим нашему боту подключиться и ждать новых сообщений. О своих действиях и попытках он напишет нам в консоле.

Log4j


Помните, выше я говорил о полезности библиотеки log4j. И вот тут для любопытных открывается клондайк полезных данных.

В файле log4j.properties уже прописаны базовые настройки для логирования и конкретные уровни логирования для сторонних классов и для тех, которые работают в нашем боте.

log4j.rootLogger= error, file
log4j.logger.com.example.telegrambot.App = debug, stdout
log4j.logger.com.example.telegrambot.bot.Bot = debug, stdout

Базовый уровень логирования у нас указан как Error.

Для классов Bot и App указан вывод логов на уровне Debug.

Что это за уровни и чем отличаются — вы легко найдете в гугле.

Если хотите посмотреть, как и в каком порядке отрабатывает библиотека Телеграм все ваши действия — переключите режим общего логирования на уровень ниже: info или debug.

И при запуске приложения вы в консоли увидите, сколько всего интересного происходит в процессе подключения, работы. Сколько раз опрашиваются ваши функции с именем и токеном бота.

Уровни логирования меняются вот этой строчкой:
Чтобы получить логи по уровню info, ставите вот так:

log4j.rootLogger= info, file

Для уровня debug, вот так:

log4j.rootLogger= debug, file

И в заключение


Сейчас у нас бот: подключается, показывает нам красивые логи и сообщает о том, что ему приходят сообщения.

Но выше я вам обещал, что бот будет отзываться на команду старт. Давайте немного изменим процедуру, ответственную за обработку полученных сообщений.

@Override
    public void onUpdateReceived(Update update) {
        log.debug("Receive new Update. updateID: " + update.getUpdateId());

        Long chatId = update.getMessage().getChatId();
        String inputText = update.getMessage().getText();

        if (inputText.startsWith("/start")) {
            SendMessage message = new SendMessage();
            message.setChatId(chatId);
            message.setText("Hello. This is start message");
            try {
                execute(message);
            } catch (TelegramApiException e) {
                e.printStackTrace();
            }
        }
    }

Как видно из кода бот делает следующее:

  • Получает апдейт и фиксирует в лог его айди номер.
  • Достает из переданного сообщения ID чата и собственно текст сообщения
  • Проверяет, не является ли этот текст командой "/start"
  • И в случае успешного сравнения создает сообщение в ответ и отправляет его в чат, с полученным ранее ID

Надеюсь вам было полезно и интересно.

В дальнейшем я покажу вам, как в моих ботах реализуется:

  • использование различных команд
  • привязка этих команд к обработчикам
  • многопоточность: независимое получение и отправка сообщений
  • форматирование отправляемых сообщений
  • работа с кнопками и колбеками
  • редактирование старых сообщений

На этой базе в паблик доступе у меня сейчас работают 2 моих бота.

Одного вы знаете — это помощник в планировании мероприятий.

А второй — Дерзкий киноман. Он очень любит фильмы и очень много о них знает. А еще больше он любит делится с вами своими знаниями. Он ведет свой канал в телеграмме, в котором сам решает, какие фильмы ему нравятся и предлагает их вам. Он умеет формировать индивидуальные подборки по жанрам и создавать подписку на персональные рекомендации. Он умеет находить такие шедевры, о которых вы никогда не слышали и даже не знали как такие фильмы вообще можно найти.

Например, как вы думаете, существуют ли фильмы с такой «дикой» комбинацией жанров, как: документальный, ужасы, комедия.

А вот бот по команде

фильм комедия документальный ужасы топ 2010-

Скажет, что таких фильмов с 2010 по текущий момент сняли аж 6 штук :)

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

Спасибо вам за внимание. Программируйте в удовольствие :)
Поделиться публикацией
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

Комментарии 17

    +2
    При этом подходе, если бот будет заниматься чем-то «тяжелым» — все остальные пользователи будут ждать пока не обработается сообщение первого, т.е. бот будет не отзывчивый.
    Либо асинхронную обработку включать, либо вебхуки.

    Самое сложное в ботах — реализация конечного автомата, оно же FSM и управление контекстом с привязкой к chat id
      0
      Все верно.
      На примере моего бота по фильмам расскажу, что у него работа с апи, подборы фильмов занимают очень приличный кусок времени. Все это связано с тем, что в апишку я могу делать запросы не так часто, ответы получаю не быстро.

      В данной статье заложен базис под бота. А дальше я хочу показать как разделить потоки. Как обработку входящий сообщений отделить от отправки, как запустить выполнение различных задачь так же в разные потоки, которые не будут мешать друг другу.

      Спасибо за комментарий
      +1
      а почему на java? обычно же выбирают питон
        +1
        Я джавист, мне джава ближе :)
          0
          я тоже) но я думала, что выбор языка более обоснован будет))
            +1

            А куда уж более? Люблю этот язык, работаю на нем — естественно и ботов на нем писать начал :)

          0
          «Обычно» не означает, что Python подходит лучше. Это лишь может означать, что ботов пишут в основном питонисты, не более. Нет никаких преимуществ у питона перед остальными языками в написании ботов. Вообще нет.
            0
            та не, просто работа со структурами данных, да и я язык в целом позволяют лаконичнее писать выражения (проще и короче), без бойлерплейта (я джавист)
              0
              Какие структуры данных применяются при написании ботов? Везде одна и та же паста из if/else в любых ботах, какие я видел. Ну если в этом смысле, то да, питон лаконичнее, но я не думаю, что это преимущество языка. Это недостаток разработчиков.
                0
                Ну про структуры данных же можно говорить в зависимости от функционала бота. Понятно, что если это простейшие команды на искуственном интеллекте обычных if/else — то никакой структуры по данным то и не нужно.

                А если хочешь какую-то легко расширяемуя систему, легкое добавление команд, какую-то хитрую обработку — тут уже без данных не обойтись.
                Если ты что-то сохранять и фиксировать начинаешь — тоже стуктуры уже нужны.

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

                А это уже достаточно полноценное приложение.

                Я считаю, что выбор языка в первую очередь зависит от того, на чем программисту удобнее работать. Если ты можешь выдать качественный продукт, который потом легко поддерживать, код легко читать и понять — почему нужно упираться в то, что в основном ботов пишут на питоне? У телеграма есть библиотека под джаву. Сделана она качественно, пользоваться её одно удовольствие и не вижу никакой проблемы в том, что бот работает на джаве.
                Хотя на другом ресурсе мне один нудот в коментариях начал писать зачем-то, что я микроскопом гвозди забиваю.
                  0
                  Я с вами полностью согласен. Я к этому и веду, что питон не является самым лучшим языком в данном случае, но часто вижу, как автора бота спрашивают: «а почему не на питоне?». И все примеры ботов, которые были на питоне, были выполнены в духе if/else, а вот на джаве и пхп мне попадались очень неплохие проекты, в которых код можно было очень легко расширять, добавив в коллекцию команду, которую хэндлер сам вызовет при обращении к боту.
                    0
                    сложность начинается при создании fsm, кнопки назад, работе с контекстом.
                      0
                      кнопки назад

                      Храните последнюю команду в базе.
                      работе с контекстом

                      С каким контекстом?
                        0
                        >>Храните последнюю команду в базе.
                        ну нет же, это как кнопка Undo

                        >>С каким контекстом?
                        при диалоге с пользователем, введеные параметры хранятся в контексте, оно же сессия.
          +1
          Спасибо за мануал. Ждем еще на эту тему!
            +1
            Огромное спасибо за туториал, надеюсь будет много статей на эту тему именно по джаве(круто если б еще и на котлин). И вообще про мл на джаве/котлин
              0
              Ну возможно до ML доберемся )))) Всякое бывает. Когда начинал заниматься ботом была идея сделать самообучающегося чат-бота. Потому чем черт не шутит, возможно в эту тему и полезем с помощью хабра :)

              Спасибо вам за комментарий :)

            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

            Самое читаемое