Telegram bot на Java? Быстро и легко
Публикаций по данной теме не мало, на самом хабре есть дюжина страниц с полезной информацией. Зачем я решил написать очередную статью о создании бота? Для начинающих в изучении java это хороший вариант познакомиться с Maven и Telegram API, для людей далеких от IT — возможность создать бота с нуля. Ниже я попытаюсь описать создание простейшего бота. Программа будет иметь несложный функционал, по моей задумке бот будет хранить в себе различные цитаты великих людей и по запросу пользователя, отправлять случайные из них. Что нам понадобится? Аккаунт в telegram и среда разработки, я использую Intellij IDEA. Описывать процесс установки не буду, в интернете есть достаточно подробные инструкции.
Шаг перый — регистрация бота в системе
Создание любого бота начинается с обращения к самому главному из них.
- Заходим в телеграмм и ищем @BotFather.
- Пишем ему команду /newbot
- Далее он спросит название будущего бота (грубо говоря, это заголовок в чате, который будут видеть пользователи) и его имя. Главное условие для имени — оно должно оканчиваться на ‘bot’.
Инициализация закончена, BotFather отправит нам ссылку на нашего будущего бота и, самое главное, выдаст нам уникальный токен, необходимый для авторизации в системе. По сути бот готов, уже можно его найти в общем поиске по имени и начать ему писать, но логика у нас еще не готова
Шаг второй — создание проекта в среде разработки и добавление зависимостей
Создаем новый проект и в процессе настройки обязательно выбираем maven, он необходим для автоматизации сборки проектов. С помощью этого инструмента мы на порядок облегчим себе жизнь, добавление сторонних библиотек не составит труда.
Для использования Telegram API, cкачивать и подключать извне ничего не нужно, достаточно зайти в pom.xml в корневом каталоге и написать несколько строк. С помощью конструкции
4.0.0 org.example TgBotQuote 1.0-SNAPSHOT 16 16 org.telegram telegrambots 5.3.0
Шаг третий — логика
Наш бот будет уметь хранить и отправлять случайные фразы великих людей. Я создал три класса: Main, Bot, Storage, начнем с последнего, с хранилища. Создаем коллекцию quoteList, инициализируем в конструкторе и добавляем несколько фраз. Также создаем функцию, котораю выдает случайную фразу из нашего хранилища getRandQuote().
public class Storage < private ArrayListquoteList; Storage() < quoteList = new ArrayList<>(); quoteList.add("Начинать всегда стоит с того, что сеет сомнения. \n\nБорис Стругацкий."); quoteList.add("80% успеха - это появиться в нужном месте в нужное время.\n\nВуди Аллен"); quoteList.add("Мы должны признать очевидное: понимают лишь те,кто хочет понять.\n\nБернар Вербер"); > String getRandQuote() < //получаем случайное значение в интервале от 0 до самого большого индекса int randValue = (int)(Math.random() * quoteList.size()); //Из коллекции получаем цитату со случайным индексом и возвращаем ее return quoteList.get(randValue); >>
Далее, создаем класс Bot и наследум его от TelegramLongPollingBot, предварительно создав две константы BOT_TOKEN(токен выдал BotFather), BOT_NAME и экземпляр класса Storage. Среда попросит реализовать три метода от материнского класса getBotUserName(), getBotToken() и onUpdateReceived(). Первые два возращают имя бота и его токен соответственно, а последний вызывается при каждой отправке сообщения пользователем, он нас и интересует. Метод onUpdateReceived(Update update) получает на вход объект update, из которого мы можем получить сообщение, текст и id чата, необходмые для отправки ответного сообщения. Также я написал метод parseMessage(), который принимает текст сообщения пользователя, обрабатывает (сравнивает текст с возможными командами) и выдает ответ
Дабы не усложнять учебный процесс, ограничимся только двумя командами. При первом запуске любого бота автоматически отправляется команда /start . Советую всегда реализовывать ответ на это сообщение, в нашем случае это простое приветствие и краткое объяснение функционала. Вторая команда, которую умеет определять наш бот — /get. В ответ на сообщение мы обращаемся к нашему хранилищу storage и методом getRandQuote() получаем случайную цитату и отправляем ее дальше. На самом деле необязательно использовать косую черту и латиницу, количеством и функционалом команд вы ограничены только своим воображением
public class Bot extends TelegramLongPollingBot < //создаем две константы, присваиваем им значения токена и имя бота соответсвтенно //вместо звездочек подставляйте свои данные final private String BOT_TOKEN = "***"; final private String BOT_NAME = "***"; Storage storage; Bot() < storage = new Storage(); >@Override public String getBotUsername() < return BOT_NAME; >@Override public String getBotToken() < return BOT_TOKEN; >@Override public void onUpdateReceived(Update update) < try< if(update.hasMessage() && update.getMessage().hasText()) < //Извлекаем из объекта сообщение пользователя Message inMess = update.getMessage(); //Достаем из inMess id чата пользователя String chatId = inMess.getChatId().toString(); //Получаем текст сообщения пользователя, отправляем в написанный нами обработчик String response = parseMessage(inMess.getText()); //Создаем объект класса SendMessage - наш будущий ответ пользователю SendMessage outMess = new SendMessage(); //Добавляем в наше сообщение id чата а также наш ответ outMess.setChatId(chatId); outMess.setText(response); //Отправка в чат execute(outMess); >> catch (TelegramApiException e) < e.printStackTrace(); >> public String parseMessage(String textMsg) < String response; //Сравниваем текст пользователя с нашими командами, на основе этого формируем ответ if(textMsg.equals("/start")) response = "Приветствую, бот знает много цитат. Жми /get, чтобы получить случайную из них"; else if(textMsg.equals("/get")) response = storage.getRandQuote(); else response = "Сообщение не распознано"; return response; >>
В главном классе создаем сессию и регистрируем бота
public class Main < public static void main(String args[]) < try < TelegramBotsApi telegramBotsApi = new TelegramBotsApi(DefaultBotSession.class); telegramBotsApi.registerBot(new Bot()); >catch (TelegramApiException e) < e.printStackTrace(); >> >
Жмем run. После запуска заходим в мессенджер, находим нашего бота, начинаем чат и вуаля, бот готов к общению. Прямо из среды разработки наша программа доступна к использованию в telegram. Основная цель на статью выполнена — мы познакомились со структурой программы, с основными методами, быстро и легко получили рабочий шаблон бота, на основе которого теперь вы можете поробовать написать своего элементарного робота. Теперь хотелось бы сделать его чуточку удобнее. Ну и разнообразить наш контент не помешало бы
Шаг четвертый — добавляем клавиатуру
Для этого в классе Bot создадим экземпляр класса ReplyKeyboardMarkup, грубо говоря это шаблон ответов, который мы прикрепляем к нашему сообщению. Он состоит из объектов KeyboardRow, которые в свою очередь представяют ряды кнопок. Все это я реализовал в отдельном методе и вызвал его в конструкторе класса Bot
void initKeyboard() < //Создаем объект будущей клавиатуры и выставляем нужные настройки replyKeyboardMarkup = new ReplyKeyboardMarkup(); replyKeyboardMarkup.setResizeKeyboard(true); //подгоняем размер replyKeyboardMarkup.setOneTimeKeyboard(false); //скрываем после использования //Создаем список с рядами кнопок ArrayListkeyboardRows = new ArrayList<>(); //Создаем один ряд кнопок и добавляем его в список KeyboardRow keyboardRow = new KeyboardRow(); keyboardRows.add(keyboardRow); //Добавляем одну кнопку с текстом "Просвяти" наш ряд keyboardRow.add(new KeyboardButton("Просвяти")); //добавляем лист с одним рядом кнопок в главный объект replyKeyboardMarkup.setKeyboard(keyboardRows); >
Нам хватит всего одной кнопки для реализации задумки. Нет ни каких проблем методом add() добавить необходимое количество рядов и клавиши на них. Вы можете создавать различные клавиатуры, скрывать и показывать их пользователю в разных ситуациях. Также существует второй класс для создания клавиатур — InlineKeyboard, позволяющий делать более сложные действия, но мы оставим первый вариант
Теперь, в методе onUpdateReceived() к исходящему от нас сообщению добавляем клавиатуру.
outMess.setReplyMarkup(replyKeyboardMarkup);
Так как нажатие на кнопку отправляет в чат то, что на ней написано, немного изменим наш обработчик. Теперь бот понимает сообщение «Просвяти» и реагирует на него, как на команду /get
else if(textMsg.equals("/get") || textMsg.equals("Просвяти")) response = storage.getRandQuote();
После запуска мы видим, что все прекрасно работает. Изучение telegram API в этой публикации на этом заканчивается. Я только поверхностно рассказал об основных возможностях этой библиотеки, для более подробных знаний есть достаточно подробная документацию. Стоило бы закончить статью, но чего-то нашему боту не хватает — разнообразия в контенте. Три цитаты это слишком несерьезно — значит пишем парсер
Шаг пятый — пишим простой парсер
Что такое парсер? Парсер — программа, которая извлекает и анализирует данные с веб страниц. Данный шаг не относится к непосредсвенному написанию ботов, поэтому вдаваться в подробности не буду, этой теме можно посвятить отдельную статью.
Для начала в файле pom.xml прописываем еще одну зависимость. Библиотека jsoup позволяет нам работать с веб страницами, располагает очень удобными методами для обращения к определенным элементам на сайтах.
Взяв первый попавшийся сайт с цитатами, я приступил к написанию парсера. Для этого в классе Storage я написал метод parser(String strURL), который принимает на вход ссылку искомой страницы. Изучив страницу сайта через исходный код в браузере, я нашел блок, в котором находятся цитаты — «su-note-inner su-u-clearfix su-u-trim».
void parser(String strURL) < String classNmae = "su-note-inner su-u-clearfix su-u-trim"; Document doc = null; try < //Получаем документ нужной нам страницы doc = Jsoup.connect(strURL).maxBodySize(0).get(); >catch (IOException e) < e.printStackTrace(); >//Получаем группу объектов, обращаясь методом из Jsoup к определенному блоку Elements elQuote = doc.getElementsByClass(classNmae); //Достаем текст из каждого объекта поочереди и добавляем в наше хранилище elQuote.forEach(el -> < quoteList.add(el.text()); >); >
Осталось только где-то вызвать наш метод. Сделал это я в конструкторе класса Storage, чтобы наполнить нашу коллекцию на этапе запуска программы и инициализации объектов. Код, в котором мы вручную добавляли цитаты в хранилище, я удалил. Вместо них — метод вызова парсера, которому мы передаем ссылку на сайт.
parser("https://citatnica.ru/citaty/mudrye-tsitaty-velikih-lyudej");
Запускаем! Теперь вместо скудного запаса из трех высказываний, бот знает 200 цитат и это только с одной страницы.