Как сделать бота для telegram на java?
Как вы выбираете кандидата, кому доверить задание? Я задался этим вопросом, после того, как отклонили мое предложение о разработке бота для «вконтакте». Это задело мое самолюбие. На работе я делаю куда более сложные вещи, чем разработка ботов. Как доказать тому человеку, который находится по ту сторону экрана, который незнает меня и не доверяет, что я могу сделать простейшего бота информатора? Ответ есть — сделать этого бота и задокументировать процесс его создания. Статья расчитана на новичков, желающих познакомится с новым стандартом java 15 и простейшим ботостроением. Итак, нам понадобятся:
- IntelliJ IDEA CE
- Java JDK_15_PREVIEW
- Библиотека для взаимодействия с телеграмом
С какими трудностями мы столкнемся? Для меня, самым сложным было настроить среду разработки для работы с джавой 15 превью версии. Нужно отдельно настроить gradle и выставить в настройках запуска проекта аргумент «—enable-preview».
Начнем по порядку с создания проекта:
Рис. 1 Создание нового проекта
Нажимаем на кнопку «New Project». Следом увидим вот такое меню:
Рис. 2 Выбор типа проекта
За основу я взял сборщик проектов Gradle. Выбираем Java и затем кнопку Next
Рис. 3 Задаем имя проекта
Теперь нужно дать имя проекту. В моем случае это «telegram-bot-example-java»
Рис.4 Ждем, пока проект проиндексируется
Какое-то время идея и gradle будут загружаться. Кстати, я уже допустил одну ошибку в конфигурации проекта, заметили, какую? Вернемся к этому позже.
Рис.5 Создание структуры java packages
Кликаем правой кнопкой по папке «src/main/java» -> New -> Package -> «org.example.tgbot»
Рис. 6 Создаем точку входа в программу
Теперь самое главное, без чего программа не запустится — точка входа и метод «main». Выбираем «org.example.tgbot» -> New -> Java Class. Называем новый класс Main.
Рис. 7 Файл Main.java
Вот такой код должен быть в файле «Main.java». Обратите внимание на две зеленые стрелки рядом с определением класса и метода «main». Если вы их видите, значит сделали все правильно и IDEA может запустить ваш проект.
Рис. 8 Тестовый запуск
Проверим, что все ок, запустив проект.
Рис. 9 Успешный запуск
Если все хорошо, вы должны увидеть «done». У меня он есть, значит, можно продолжать.
Рис. 10 Проверяем новую фичу java 15
Итак, вот мы дошли до ошибки, о которой я упоминал выше. В чем тут дело? Тип «record» был добавлен в java 15 и в превью версии должен присутствовать. Но я при запуске указал джаву восьмой версии. Что теперь делать? Можно сделать новый проект и указать правильную версию. Или можно исправить текущий проект. Сделать новый слишком просто, поэтому я исправлю этот (на самом деле нет, я попробовал, это не решило проблему).
Рис. 11 Настройки проекта
Исправляем проблему. Нужно поменять версию джавы.Открываем настройки проекта.
Рис. 12 Настройки версии java
Выбираем «Project SDK» -> 15. Если у вас ее нет, можно скачать ниже, в выпадающем списке.
В «Project language level» выбираем «15 (Preview) — Sealed types, records, patterns, local enums and interfaces». Сохраняем настройки.
Рис. 13 Record тип работает
Теперь все ок, можно наконец-то взяться за программирование? Увы, нет. IDEA распознает новые фичи, но кроме нее есть еще gradle, который не сможет скомпилировать этот код. Чтобы это проверить, создаим рядом с «Main.java «еще один файл — «Bot.java» в котором будет происходить обработка сообщений.
Рис. 14 Bot.java
У gradle будут проблемы со сборкой этого файла, а именно — из за 11 строки. Модификатор «sealed», как и «record», является экспериментальным. Проверим, соберем проект.
Рис. 15 Gradle error
Еще немного борьбы и мы запустим этот код. Нужно настроить сборку gradle и добавить аргумент «—enable-preview» при запуске.
Рис. 16 Gradle java 15 settings
Нужно добавить новую секцию, в которой будут задаваться флаги сборки «—enable-preview» и «-Xlint:Preview». Второй флаг не обязательный, нужен для отображения новых warnings. В комментарии пример, как можно задать все флаги одной строкой. Кроме этого, нужно добавить строку «jvmArgs([‘—enable-preview’])» в секцию «test». На этом с gradle закончили.
Рис. 17 Настройки сборки
Далее, нужно добавить аргумент для виртуальной машины java. Отрываем настройки.
Рис. 18 Открыть меню «Add VM options»
После чего у вас появится поле редактирование опций виртуальной машины.
Рис. 19 Редактор опций виртуальной машины
В пустое поле вписываем «—enable-preview». Также проверьте, что у вас стоит «java 15». Сохраняем настройки и собираем проект. У меня сборка и запуск прошли успешно. Теперь настроим прием сообщений и ответы.
Рис. 20 Bot.java
Добавляем следующий код в файл «Bot.java». В нем два метода, хотя можно было обойтись и одним, выбранная мною библиотека присилает обновления в виде массива, а не по одному. Ах да, я забыл показать, как добавить эту библиотеку.
Рис. 21 Добавляем зависимость в «build.gradle»
В секцию «dependencies» добавьте строку «implementation ‘com.github.pengrad:java-telegram-bot-api:5.0.1′» как показано на рисунке (13 строка). И финальный штрих, обновляем Main класс, чтобы запустить бота.
Рис. 22 Новый Main класс
Здесь я читаю BOT_TOKEN из переменных среды, это значит, ее нужно как то добавить. Это можно сделать глобально в системе или задать в IDEA. Я выбираю второй вариант.
Рис. 23 Снова открываем «Edit configurations»
Рис. 24 Редактирование переменных среды
В поле «Environment variables» вставьте строку «BOT_TOKEN=123», где 123 — ваш токен. А я вставлю свой Сохраняем настройки и запускаем проект.
Рис. 25 Бот успешно запущен
Бот работает! Пруф:
Рис. 25 Телеграм чат
Скорее всего, если вы захотите проверить моего бота, он вам не ответит. Потому что программа, которую мы написали, запущена локально, у меня на компьютере. Чтобы бот работал 24/7, программу нужно разметить на удаленном сервере (или просто держать компьютер всегда включенным всесте с запущенной программой). Это материал для другой статьи. Я также могу рассказать, как сделать ответы на команды в чате, отображать динамическую информацию, информировать клиентов. Или как написать бота на scala >_<. Пишите в комментариях, что у вас не получилось. До встречи в других статьях!
Из песочницы, Программирование, Open source, Java, Системы обмена сообщениями, Из песочницы
Рекомендация: подборка платных и бесплатных курсов Smm — https://katalog-kursov.ru/
Каждый разработчик (и не только), который использует Telegram в повседневной жизни, хотя бы раз задумывался о том, каково это — создать своего бота, на сколько это сложно и какой язык программирования лучше использовать.
На все эти вопросы могу дать самый простой (и, наверно, самый правильный) ответ: все зависит от вас самих, ваших знаний и намерений.
… Но в этой небольшой статье, я покажу, как можно создать своего бота на языке Java и что это довольно интересно и несложно.
Мы будем использовать библиотеку для работы с Telegram Bots API и ее расширение, позволяющее создавать свои команды (‘/custom_cmd
‘) и обрабатывать их простым способом.
Задачей бота будет являться регистрация пользователя и отправка сообщения от указанного им имени другим пользователям бота.
Создание нового проекта и подготовка
1. Добавление зависимостей в проект
Создадим новый maven-проект и отредактируем pom.xml
, добавив в него необходимые зависимости:
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>io.example</groupId>
<artifactId>anonymizerbot</artifactId>
<version>1.0-SNAPSHOT</version>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
```8</source>
<target>8</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<!-- Telegram API -->
<dependency>
<groupId>org.telegram</groupId>
<artifactId>telegrambots</artifactId>
<version>LATEST</version>
</dependency>
<dependency>
<groupId>org.telegram</groupId>
<artifactId>telegrambotsextensions</artifactId>
<version>LATEST</version>
</dependency>
...
</dependencies>
</project>
Telegram API — библиотека для работы с Telegram Bots API, содержит в себе классы и методы для взаимодействия с сервисами Telegram и некоторые расширения этих классов.
2. Создание аккаунта для бота
Для этого нам необходимо обратиться за помощью к боту BotFather:
- найдем бота в поиске;
- выполним команду «/start»;
- выполним команду «/newbot»;
- зададим какое-нибудь имя нашему боту (должно заканчиваться на «Bot»). Я назвал его «ExampleOfAnonymizerBot».
После выполнения этих команд мы получим токен, который нам понадобится для использования Bot API. (7xxxxxxx2:Axxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx0)
Реализация
1. Модель анонимного отправителя сообщений
Данные, необходимые нам от каждого пользователя:
- User mUser — информация о пользователе Telegram;
- Chat mChat — информация о чате пользователя и бота;
- String mDisplayedName — имя, от которого пользователь будет посылать сообщения другим пользователям бота.
Anonymous.java
package io.example.anonymizerbot.model;
import org.telegram.telegrambots.meta.api.objects.Chat;
import org.telegram.telegrambots.meta.api.objects.User;
public final class Anonymous {
private final User mUser;
private final Chat mChat;
private String mDisplayedName;
public Anonymous(User user, Chat chat) {
mUser = user;
mChat = chat;
}
@Override
public int hashCode() {
return mUser.hashCode();
}
@Override
public boolean equals(Object obj) {
return obj instanceof Anonymous && ((Anonymous) obj).getUser().equals(mUser);
}
public User getUser() {
return mUser;
}
public Chat getChat() {
return mChat;
}
public String getDisplayedName() {
return mDisplayedName;
}
public void setDisplayedName(String displayedName) {
mDisplayedName = displayedName;
}
}
Добавим сервис, содержащий часто используемые методы для манипулирования множеством анонимных пользователей.
AnonymousService.java
package io.example.anonymizerbot.service;
import io.example.anonymizerbot.model.Anonymous;
import org.telegram.telegrambots.meta.api.objects.User;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Stream;
public final class AnonymousService {
private final Set<Anonymous> mAnonymouses;
public AnonymousService() {
mAnonymouses = new HashSet<>();
}
public boolean setUserDisplayedName(User user, String name) {
if (!isDisplayedNameTaken(name)) {
mAnonymouses.stream().filter(a -> a.getUser().equals(user)).forEach(a -> a.setDisplayedName(name));
return true;
}
return false;
}
public boolean removeAnonymous(User user) {
return mAnonymouses.removeIf(a -> a.getUser().equals(user));
}
public boolean addAnonymous(Anonymous anonymous) {
return mAnonymouses.add(anonymous);
}
public boolean hasAnonymous(User user) {
return mAnonymouses.stream().anyMatch(a -> a.getUser().equals(user));
}
public String getDisplayedName(User user) {
Anonymous anonymous = mAnonymouses.stream().filter(a -> a.getUser().equals(user)).findFirst().orElse(null);
if (anonymous == null) {
return null;
}
return anonymous.getDisplayedName();
}
public Stream<Anonymous> anonymouses() {
return mAnonymouses.stream();
}
private boolean isDisplayedNameTaken(String name) {
return mAnonymouses.stream().anyMatch(a -> Objects.equals(a.getDisplayedName(), name));
}
}
2. Интерфейс бота
Любая кастомная команда должна наследоваться от BotCommand
и реализовывать метод
execute(AbsSender sender, User user, Chat chat, String[] strings)
, который используется для обработки команд пользователей.
После того как мы обработаем команду пользователя, мы можем послать ему ответ, используя метод execute
класса AbsSender
, который принимает на вход вышеупомянутый execute(AbsSender sender, User user, Chat chat, String[] strings)
.
Здесь и далее чтобы не оборачивать каждый раз метод AbsSender.execute
, который может выбросить исключение TelegramApiException
, в try-catch
, и для того чтобы не прописывать в каждой команде вывод однообразных логов, создадим класс AnonymizerCommand
, а наши кастомные команды будем уже наследовать от него (обработку исключений в этом примере оставим):
AnonymizerCommand.java
package io.example.anonymizerbot.command;
import io.example.anonymizerbot.logger.LogLevel;
import io.example.anonymizerbot.logger.LogTemplate;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import org.telegram.telegrambots.extensions.bots.commandbot.commands.BotCommand;
import org.telegram.telegrambots.meta.api.methods.send.SendMessage;
import org.telegram.telegrambots.meta.api.objects.User;
import org.telegram.telegrambots.meta.bots.AbsSender;
import org.telegram.telegrambots.meta.exceptions.TelegramApiException;
abstract class AnonymizerCommand extends BotCommand {
final Logger log = LogManager.getLogger(getClass());
AnonymizerCommand(String commandIdentifier, String description) {
super(commandIdentifier, description);
}
void execute(AbsSender sender, SendMessage message, User user) {
try {
sender.execute(message);
log.log(Level.getLevel(LogLevel.SUCCESS.getValue()), LogTemplate.COMMAND_SUCCESS.getTemplate(), user.getId(), getCommandIdentifier());
} catch (TelegramApiException e) {
log.error(LogTemplate.COMMAND_EXCEPTION.getTemplate(), user.getId(), getCommandIdentifier(), e);
}
}
}
Определим команды, на которые наш бот будет реагировать:
/start
— создаст новогоAnonymous
без имени и добавит его в коллекциюAnonymouses
;
StartCommand.java
package io.example.anonymizerbot.command;
import io.example.anonymizerbot.logger.LogLevel;
import io.example.anonymizerbot.logger.LogTemplate;
import io.example.anonymizerbot.model.Anonymous;
import io.example.anonymizerbot.service.AnonymousService;
import org.apache.logging.log4j.Level;
import org.telegram.telegrambots.meta.api.methods.send.SendMessage;
import org.telegram.telegrambots.meta.api.objects.Chat;
import org.telegram.telegrambots.meta.api.objects.User;
import org.telegram.telegrambots.meta.bots.AbsSender;
public final class StartCommand extends AnonymizerCommand {
private final AnonymousService mAnonymouses;
// обязательно нужно вызвать конструктор суперкласса,
// передав в него имя и описание команды
public StartCommand(AnonymousService anonymouses) {
super("start", "start using botn");
mAnonymouses = anonymouses;
}
/**
* реализованный метод класса BotCommand, в котором обрабатывается команда, введенная пользователем
* @param absSender - отправляет ответ пользователю
* @param user - пользователь, который выполнил команду
* @param chat - чат бота и пользователя
* @param strings - аргументы, переданные с командой
*/
@Override
public void execute(AbsSender absSender, User user, Chat chat, String[] strings) {
log.info(LogTemplate.COMMAND_PROCESSING.getTemplate(), user.getId(), getCommandIdentifier());
StringBuilder sb = new StringBuilder();
SendMessage message = new SendMessage();
message.setChatId(chat.getId().toString());
if (mAnonymouses.addAnonymous(new Anonymous(user, chat))) {
log.info("User {} is trying to execute '{}' the first time. Added to users' list.", user.getId(), getCommandIdentifier());
sb.append("Hi, ").append(user.getUserName()).append("! You've been added to bot users' list!n")
.append("Please execute command:n'/set_name <displayed_name>'nwhere <displayed_name> is the name you want to use to hide your real name.");
} else {
log.log(Level.getLevel(LogLevel.STRANGE.getValue()), "User {} has already executed '{}'. Is he trying to do it one more time?", user.getId(), getCommandIdentifier());
sb.append("You've already started bot! You can send messages if you set your name (/set_name).");
}
message.setText(sb.toString());
execute(absSender, message, user);
}
}
/help
— выведет пользователю информацию обо всех доступных командах (конструктор отличается от других тем, что в него необходимо передатьICommandRegistry
, который содержит все кастомные команды);
HelpCommand.java
package io.example.anonymizerbot.command;
import io.example.anonymizerbot.logger.LogTemplate;
import org.telegram.telegrambots.extensions.bots.commandbot.commands.ICommandRegistry;
import org.telegram.telegrambots.meta.api.methods.send.SendMessage;
import org.telegram.telegrambots.meta.api.objects.Chat;
import org.telegram.telegrambots.meta.api.objects.User;
import org.telegram.telegrambots.meta.bots.AbsSender;
public final class HelpCommand extends AnonymizerCommand {
private final ICommandRegistry mCommandRegistry;
public HelpCommand(ICommandRegistry commandRegistry) {
super("help", "list all known commandsn");
mCommandRegistry = commandRegistry;
}
@Override
public void execute(AbsSender absSender, User user, Chat chat, String[] strings) {
log.info(LogTemplate.COMMAND_PROCESSING.getTemplate(), user.getId(), getCommandIdentifier());
StringBuilder helpMessageBuilder = new StringBuilder("<b>Available commands:</b>nn");
mCommandRegistry.getRegisteredCommands().forEach(cmd -> helpMessageBuilder.append(cmd.toString()).append("n"));
SendMessage helpMessage = new SendMessage();
helpMessage.setChatId(chat.getId().toString());
helpMessage.enableHtml(true);
helpMessage.setText(helpMessageBuilder.toString());
execute(absSender, helpMessage, user);
}
}
/set_name
— задаст пользователю имя, от которого будут отправляться анонимные сообщения;
SetNameCommand.java
package io.example.anonymizerbot.command;
import io.example.anonymizerbot.logger.LogLevel;
import io.example.anonymizerbot.logger.LogTemplate;
import io.example.anonymizerbot.service.AnonymousService;
import org.apache.logging.log4j.Level;
import org.telegram.telegrambots.meta.api.methods.send.SendMessage;
import org.telegram.telegrambots.meta.api.objects.Chat;
import org.telegram.telegrambots.meta.api.objects.User;
import org.telegram.telegrambots.meta.bots.AbsSender;
public final class SetNameCommand extends AnonymizerCommand {
private final AnonymousService mAnonymouses;
public SetNameCommand(AnonymousService anonymouses) {
super("set_name", "set or change name that will be displayed with your messagesn");
mAnonymouses = anonymouses;
}
@Override
public void execute(AbsSender absSender, User user, Chat chat, String[] strings) {
log.info(LogTemplate.COMMAND_PROCESSING.getTemplate(), user.getId(), getCommandIdentifier());
SendMessage message = new SendMessage();
message.setChatId(chat.getId().toString());
if (!mAnonymouses.hasAnonymous(user)) {
log.log(Level.getLevel(LogLevel.STRANGE.getValue()), "User {} is trying to execute '{}' without starting the bot!", user.getId(), getCommandIdentifier());
message.setText("Firstly you should start the bot! Execute '/start' command!");
execute(absSender, message, user);
return;
}
String displayedName = getName(strings);
if (displayedName == null) {
log.log(Level.getLevel(LogLevel.STRANGE.getValue()), "User {} is trying to set empty name.", user.getId());
message.setText("You should use non-empty name!");
execute(absSender, message, user);
return;
}
StringBuilder sb = new StringBuilder();
if (mAnonymouses.setUserDisplayedName(user, displayedName)) {
if (mAnonymouses.getDisplayedName(user) == null) {
log.info("User {} set a name '{}'", user.getId(), displayedName);
sb.append("Your displayed name: '").append(displayedName)
.append("'. Now you can send messages to bot!");
} else {
log.info("User {} has changed name to '{}'", user.getId(), displayedName);
sb.append("Your new displayed name: '").append(displayedName).append("'.");
}
} else {
log.log(Level.getLevel(LogLevel.STRANGE.getValue()), "User {} is trying to set taken name '{}'", user.getId(), displayedName);
sb.append("Name ").append(displayedName).append(" is already in use! Choose another name!");
}
message.setText(sb.toString());
execute(absSender, message, user);
}
private String getName(String[] strings) {
if (strings == null || strings.length == 0) {
return null;
}
String name = String.join(" ", strings);
return name.replaceAll(" ", "").isEmpty() ? null : name;
}
}
/my_name
— отобразит текущее имя пользователя;
MyNameCommand.java
package io.example.anonymizerbot.command;
import io.example.anonymizerbot.logger.LogLevel;
import io.example.anonymizerbot.logger.LogTemplate;
import io.example.anonymizerbot.service.AnonymousService;
import org.apache.logging.log4j.Level;
import org.telegram.telegrambots.meta.api.methods.send.SendMessage;
import org.telegram.telegrambots.meta.api.objects.Chat;
import org.telegram.telegrambots.meta.api.objects.User;
import org.telegram.telegrambots.meta.bots.AbsSender;
public final class MyNameCommand extends AnonymizerCommand {
private final AnonymousService mAnonymouses;
public MyNameCommand(AnonymousService anonymouses) {
super("my_name", "show your current name that will be displayed with your messagesn");
mAnonymouses = anonymouses;
}
@Override
public void execute(AbsSender absSender, User user, Chat chat, String[] strings) {
log.info(LogTemplate.COMMAND_PROCESSING.getTemplate(), user.getId(), getCommandIdentifier());
StringBuilder sb = new StringBuilder();
SendMessage message = new SendMessage();
message.setChatId(chat.getId().toString());
if (!mAnonymouses.hasAnonymous(user)) {
sb.append("You are not in bot users' list! Send /start command!");
log.log(Level.getLevel(LogLevel.STRANGE.getValue()), "User {} is trying to execute '{}' without starting the bot.", user.getId(), getCommandIdentifier());
} else if(mAnonymouses.getDisplayedName(user) == null) {
sb.append("Currently you don't have a name.nSet it using command:n'/set_name <displayed_name>'");
log.log(Level.getLevel(LogLevel.STRANGE.getValue()), "User {} is trying to execute '{}' without having a name.", user.getId(), getCommandIdentifier());
} else {
log.info("User {} is executing '{}'. Name is '{}'.", user.getId(), getCommandIdentifier(), mAnonymouses.getDisplayedName(user));
sb.append("Your current name: ").append(mAnonymouses.getDisplayedName(user));
}
message.setText(sb.toString());
execute(absSender, message, user);
}
}
/stop
— удалит пользователя из коллекции анонимусов.
StopCommand.java
package io.example.anonymizerbot.command;
import io.example.anonymizerbot.logger.LogLevel;
import io.example.anonymizerbot.logger.LogTemplate;
import io.example.anonymizerbot.service.AnonymousService;
import org.apache.logging.log4j.Level;
import org.telegram.telegrambots.meta.api.methods.send.SendMessage;
import org.telegram.telegrambots.meta.api.objects.Chat;
import org.telegram.telegrambots.meta.api.objects.User;
import org.telegram.telegrambots.meta.bots.AbsSender;
public final class StopCommand extends AnonymizerCommand {
private final AnonymousService mAnonymouses;
public StopCommand(AnonymousService anonymouses) {
super("stop", "remove yourself from bot users' listn");
mAnonymouses = anonymouses;
}
@Override
public void execute(AbsSender absSender, User user, Chat chat, String[] strings) {
log.info(LogTemplate.COMMAND_PROCESSING.getTemplate(), user.getId(), getCommandIdentifier());
StringBuilder sb = new StringBuilder();
SendMessage message = new SendMessage();
message.setChatId(chat.getId().toString());
if (mAnonymouses.removeAnonymous(user)) {
log.info("User {} has been removed from users list!", user.getId());
sb.append("You've been removed from bot's users list! Bye!");
} else {
log.log(Level.getLevel(LogLevel.STRANGE.getValue()), "User {} is trying to execute '{}' without having executed 'start' before!", user.getId(), getCommandIdentifier());
sb.append("You were not in bot users' list. Bye!");
}
message.setText(sb.toString());
execute(absSender, message, user);
}
}
3. Инициализация и запуск бота
Класс бота, в котором производится регистрация всех кастомных команд, обработчика сообщений-не команд и неизвестных команд.
AnonymizerBot.java
package io.example.anonymizerbot.bot;
import io.example.anonymizerbot.command.*;
import io.example.anonymizerbot.logger.LogLevel;
import io.example.anonymizerbot.logger.LogTemplate;
import io.example.anonymizerbot.model.Anonymous;
import io.example.anonymizerbot.service.AnonymousService;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.telegram.telegrambots.bots.DefaultBotOptions;
import org.telegram.telegrambots.extensions.bots.commandbot.TelegramLongPollingCommandBot;
import org.telegram.telegrambots.meta.api.methods.send.SendMessage;
import org.telegram.telegrambots.meta.api.objects.Message;
import org.telegram.telegrambots.meta.api.objects.Update;
import org.telegram.telegrambots.meta.api.objects.User;
import org.telegram.telegrambots.meta.exceptions.TelegramApiException;
import java.util.stream.Stream;
public final class AnonymizerBot extends TelegramLongPollingCommandBot {
private static final Logger LOG = LogManager.getLogger(AnonymizerBot.class);
// имя бота, которое мы указали при создании аккаунта у BotFather
// и токен, который получили в результате
private static final String BOT_NAME = "AnonymizerBotExample";
private static final String BOT_TOKEN = "7xxxxxxx2:Axxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx0";
private final AnonymousService mAnonymouses;
public AnonymizerBot(DefaultBotOptions botOptions) {
super(botOptions, BOT_NAME);
LOG.info("Initializing Anonymizer Bot...");
LOG.info("Initializing anonymouses list...");
mAnonymouses = new AnonymousService();
// регистрация всех кастомных команд
LOG.info("Registering commands...");
LOG.info("Registering '/start'...");
register(new StartCommand( mAnonymouses));
LOG.info("Registering '/set_name'...");
register(new SetNameCommand(mAnonymouses));
LOG.info("Registering '/stop'...");
register(new StopCommand(mAnonymouses));
LOG.info("Registering '/my_name'...");
register(new MyNameCommand(mAnonymouses));
HelpCommand helpCommand = new HelpCommand(this);
LOG.info("Registering '/help'...");
register(helpCommand);
// обработка неизвестной команды
LOG.info("Registering default action'...");
registerDefaultAction(((absSender, message) -> {
LOG.log(Level.getLevel(LogLevel.STRANGE.getValue()), "User {} is trying to execute unknown command '{}'.", message.getFrom().getId(), message.getText());
SendMessage text = new SendMessage();
text.setChatId(message.getChatId());
text.setText(message.getText() + " command not found!");
try {
absSender.execute(text);
} catch (TelegramApiException e) {
LOG.error("Error while replying unknown command to user {}.", message.getFrom(), e);
}
helpCommand.execute(absSender, message.getFrom(), message.getChat(), new String[] {});
}));
}
@Override
public String getBotToken() {
return BOT_TOKEN;
}
// обработка сообщения не начинающегося с '/'
@Override
public void processNonCommandUpdate(Update update) {
LOG.info("Processing non-command update...");
if (!update.hasMessage()) {
LOG.error("Update doesn't have a body!");
throw new IllegalStateException("Update doesn't have a body!");
}
Message msg = update.getMessage();
User user = msg.getFrom();
LOG.info(LogTemplate.MESSAGE_PROCESSING.getTemplate(), user.getId());
if (!canSendMessage(user, msg)) {
return;
}
String clearMessage = msg.getText();
String messageForUsers = String.format("%s:n%s", mAnonymouses.getDisplayedName(user), msg.getText());
SendMessage answer = new SendMessage();
// отправка ответа отправителю о том, что его сообщение получено
answer.setText(clearMessage);
answer.setChatId(msg.getChatId());
replyToUser(answer, user, clearMessage);
// отправка сообщения всем остальным пользователям бота
answer.setText(messageForUsers);
Stream<Anonymous> anonymouses = mAnonymouses.anonymouses();
anonymouses.filter(a -> !a.getUser().equals(user))
.forEach(a -> {
answer.setChatId(a.getChat().getId());
sendMessageToUser(answer, a.getUser(), user);
});
}
// несколько проверок, чтобы можно было отправлять сообщения другим пользователям
private boolean canSendMessage(User user, Message msg) {
SendMessage answer = new SendMessage();
answer.setChatId(msg.getChatId());
if (!msg.hasText() || msg.getText().trim().length() == 0) {
LOG.log(Level.getLevel(LogLevel.STRANGE.getValue()), "User {} is trying to send empty message!", user.getId());
answer.setText("You shouldn't send empty messages!");
replyToUser(answer, user, msg.getText());
return false;
}
if(!mAnonymouses.hasAnonymous(user)) {
LOG.log(Level.getLevel(LogLevel.STRANGE.getValue()), "User {} is trying to send message without starting the bot!", user.getId());
answer.setText("Firstly you should start bot! Use /start command!");
replyToUser(answer, user, msg.getText());
return false;
}
if (mAnonymouses.getDisplayedName(user) == null) {
LOG.log(Level.getLevel(LogLevel.STRANGE.getValue()), "User {} is trying to send message without setting a name!", user.getId());
answer.setText("You must set a name before sending messages.nUse '/set_name <displayed_name>' command.");
replyToUser(answer, user, msg.getText());
return false;
}
return true;
}
private void sendMessageToUser(SendMessage message, User receiver, User sender) {
try {
execute(message);
LOG.log(Level.getLevel(LogLevel.SUCCESS.getValue()), LogTemplate.MESSAGE_RECEIVED.getTemplate(), receiver.getId(), sender.getId());
} catch (TelegramApiException e) {
LOG.error(LogTemplate.MESSAGE_LOST.getTemplate(), receiver.getId(), sender.getId(), e);
}
}
private void replyToUser(SendMessage message, User user, String messageText) {
try {
execute(message);
LOG.log(Level.getLevel(LogLevel.SUCCESS.getValue()), LogTemplate.MESSAGE_SENT.getTemplate(), user.getId(), messageText);
} catch (TelegramApiException e) {
LOG.error(LogTemplate.MESSAGE_EXCEPTION.getTemplate(), user.getId(), e);
}
}
}
Наконец, запуск бота:
BotInitializer.java
package io.example.anonymizerbot;
import io.example.anonymizerbot.bot.AnonymizerBot;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.telegram.telegrambots.ApiContextInitializer;
import org.telegram.telegrambots.bots.DefaultBotOptions;
import org.telegram.telegrambots.meta.ApiContext;
import org.telegram.telegrambots.meta.TelegramBotsApi;
import org.telegram.telegrambots.meta.exceptions.TelegramApiRequestException;
public final class BotInitializer {
private static final Logger LOG = LogManager.getLogger(BotInitializer.class);
private static final String PROXY_HOST = "xx.xx.xxx.xxx";
private static final int PROXY_PORT = 9999;
public static void main(String[] args) {
try {
LOG.info("Initializing API context...");
ApiContextInitializer.init();
TelegramBotsApi botsApi = new TelegramBotsApi();
LOG.info("Configuring bot options...");
DefaultBotOptions botOptions = ApiContext.getInstance(DefaultBotOptions.class);
botOptions.setProxyHost(PROXY_HOST);
botOptions.setProxyPort(PROXY_PORT);
botOptions.setProxyType(DefaultBotOptions.ProxyType.SOCKS4);
LOG.info("Registering Anonymizer...");
botsApi.registerBot(new AnonymizerBot(botOptions));
LOG.info("Anonymizer bot is ready for work!");
} catch (TelegramApiRequestException e) {
LOG.error("Error while initializing bot!", e);
}
}
}
Вот и все! Бот готов к первым тестам.
Пример использования
В качестве примера рассмотрим следующий сценарий:
- ‘A’ начинает работу с ботом (
/start
); - ‘A’ пытается отправить сообщение;
- ‘A’ пытается задать имя (
/set_name
); - ‘A’ задает имя (
/set_name Pendalf
); - ‘A’ посылает сообщение другим пользователям (которых нет);
- ‘B’ начинает работу с ботом (
/start
); - ‘B’ задает имя (
/set_name Chuck Norris
); - ‘B’ посылает сообщение другим пользователям;
- ‘A’ видит сообщение от ‘B’ и посылает сообщение в ответ;
- ‘B’ видит ответ от ‘A’ и больше ему не пишет…
Привет, читатель.
Важно для прочтения!
Если ты не против, то изучи ООП и Jav’y прежде, чем учить другие библиотеки. Ибо ты не будешь браться за физику, не выучив математику. Тут то же самое.
Сегодня мы будем писать простейшего бота Telegram, который будет отвечать на команды. Такая статья была, но писали бота на Питоне.
Почему я выбрал именно Jav’y
Java в плане ботов, серверов, плагинов да и вообще программ будет удобнее для меня.
1. Виртуальная машина хоть и долгая, но если обрабатывать события в несколько потоков, то будет работать быстро.(Вообще для многих серверов/мультиплеерных игр в одном потоке не всегда получится обрабатывать события, в Jav’e сделать это гораздо проще)
2. Наличие хорошей документации и Javadoc’ов, которые можно сделать для всех библиотек(не только для системных)
0. А что такое «бот»?
Бот — это профиль в соцсети/мессенджере(в данном случае Telegram) который отвечает на команды.( В любом случае все действия буду происходить после выполнения команды)
Тип чата с ботом — это переписка 1 на 1.
1.С чего же начинать?
Здесь качать библиотеку Telegram(обязательно with-dependiciens)
IDE можно выбрать любую, я бы порекомендовал eclipse.
Импотрируем библиотеку Телеграма и приступаем.
2.Наследование бота
Для того чтобы отвечать на команды, класс должен наследовать TelegramLongPillingBot
Создаём класс:
И пишем в него:
package ru.thematdev.bot;
import org.telegram.telegrambots.api.objects.Update;
import org.telegram.telegrambots.bots.TelegramLongPollingBot;
public class Example extends TelegramLongPollingBot{
public static void main(String[] args) {
ApiContextInitializer.init(); // Инициализируем апи
TelegramBotsApi botapi = new TelegramBotsApi();
try {
botapi.registerBot(new Bot());
} catch (TelegramApiException e) {
e.printStackTrace();
}
}
@Override
public String getBotUsername() {
return "USER";
//возвращаем юзера
}
@Override
public void onUpdateReceived(Update e) {
// Тут будет то, что выполняется при получении сообщения
}
@Override
public String getBotToken() {
return "YOUR_BOT_TOKEN";
//Токен бота
}
}
Получить токен и username можно вбив в поиск @BotFather и написать ему /newbot
3.Как же нам добавить в него что-нибудь?
В телеграме нет «приветствующего сообщения», но когда мы нажимаем кнопку «Start» чтобы начать общение с ботом, то автоматом прописывается команда «/start», поэтому для начала добавим именно её. В telegramapi нету метода отправить сообщение по типу send(строка), но мы его создадим, после всех войдов пишем:
@SuppressWarnings("deprecation") // Означает то, что в новых версиях метод уберут или заменят
private void sendMsg(Message msg, String text) {
SendMessage s = new SendMessage();
s.setChatId(msg.getChatId()); // Боту может писать не один человек, и поэтому чтобы отправить сообщение, грубо говоря нужно узнать куда его отправлять
s.setText(text);
try { //Чтобы не крашнулась программа при вылете Exception
sendMessage(s);
} catch (TelegramApiException e){
e.printStackTrace();
}
}
А в UpdateReceived дописываем это:
Message msg = e.getMessage(); // Это нам понадобится
String txt = msg.getText();
if (txt.equals("/start")) {
sendMsg(msg, "Hello, world! This is simple bot!");
}
4.Ура, всё получилось, как запустить?
Можно в Runnable JAR File так как у нас есть метод main, но лучше запускать прямо из IDE:
Когда мы запустили бота, зарегистрировали и получили токен у BotFather, то мы можем написать ему /start и увидеть что всё работает. Но бот будет работать только когда он запущен.(Много ему не нужно, за 50-100 рублей/месяц на хостинге можно держать). Также с помощью Telegram API можно отправлять картинки, создавать inline-ботов с кнопками и многое другое, но о этом уже в продолжении…
Спасибо всем за просмотр!
Автор: весёлый усач
Источник