Всем привет! Да-да тема не очень актуальна и на просторах habr-a есть очень много статей про то, как написать простейшего telegram bota, но я все-таки рискну написать еще одну. Может кому-то она будет интересна и полезна.
Для написания бота нужен аккаунт в telegram.
И так начнем: наш бот будет "ходить" на официальную страницу национального банка и выдавать текущий официальный курс к запрашиваемой валюте.
Создадим проект, я это сделаю с помощью https://start.spring.io/
Генерируем и открываем в среде разработки.
Сейчас нужно зайти в telegram и найти там @BotFather.
Пишем команду /newbot и вводим имя для нашего нового бота. Имя должно быть уникальным и заканчиваться на bot. По данному имени нас будут искать в telegram.
После этого BotFather сгенерирует нам уникальный токен с помощью которого мы получим доступ к нему. Данный токен лучше держать в секрете и никому не показывать, потому что узнав его можно получить доступ к этому боту. Конечно после написания данной статьи я его поменяю.
Далее идем в наш проект и добавляем зависимости для telegram, lombok и json. Библиотеку json будем использовать для парсинга jsona, который будем получать через официальный API банка.
<dependency>
<groupId>org.telegram</groupId>
<artifactId>telegrambots</artifactId>
<version>6.5.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.26</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20220924</version>
</dependency>
В application.properties прописываем наши данные для доступу к боту.
bot.name=OfficialRateOfBYNBot
bot.token=5888893679:AAGQWAbS0zCMtnQrTeWrVYz9KHj2nUlyVIw
Создадим папку config, и там создадим класс BotConfig. Он нужен для использования имени и токена нашего бота для подключения к нему.
@Configuration
@Data
@PropertySource("application.properties")
public class BotConfig {
@Value("${bot.name}")
String botName;
@Value("${bot.token}")
String token;
}
Сейчас поговорим про API через которую мы будем получать данные о курсах валют. Я нашел такой https://www.nbrb.by/apihelp/exrates на официальном сайте национального банка.
Отправив get запрос на данный API через postman мы получим вот такой json.
Чтобы легче обрабатывать json создадим класс CurrencyModel в папке model
@Data
public class CurrencyModel {
Integer cur_ID;
Date date;
String cur_Abbreviation;
Integer cur_Scale;
String cur_Name;
Double cur_OfficialRate;
}
Далее создадим класс CurrencyService в папке service.
public class CurrencyService {
public static String getCurrencyRate(String message, CurrencyModel model) throws IOException, ParseException {
URL url = new URL("https://www.nbrb.by/api/exrates/rates/" + message + "?parammode=2");
Scanner scanner = new Scanner((InputStream) url.getContent());
String result = "";
while (scanner.hasNext()){
result +=scanner.nextLine();
}
JSONObject object = new JSONObject(result);
model.setCur_ID(object.getInt("Cur_ID"));
model.setDate(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss").parse(object.getString("Date")));
model.setCur_Abbreviation(object.getString("Cur_Abbreviation"));
model.setCur_Scale(object.getInt("Cur_Scale"));
model.setCur_Name(object.getString("Cur_Name"));
model.setCur_OfficialRate(object.getDouble("Cur_OfficialRate"));
return "Official rate of BYN to " + model.getCur_Abbreviation() + "\n" +
"on the date: " + getFormatDate(model) + "\n" +
"is: " + model.getCur_OfficialRate() + " BYN per " + model.getCur_Scale() + " " + model.getCur_Abbreviation();
}
private static String getFormatDate(CurrencyModel model) {
return new SimpleDateFormat("dd MMM yyyy").format(model.getDate());
}
}
В данном классе мы напишем статический метод getCurrencyRate, делаем его статическим чтобы обращаться к нему без создания объекта данного класса. В данном методе мы «идем» на сайт НБ и там по запрашиваемой валюте находим её курс и сохраняем полученную информацию в нашу модель CurrencyModel. На выходе выдаем данные, из этой модели уже в таком виде, в котором их будут видеть наши пользователи.
Далее создаем класс TelegramBot и наследуемся от TelegramLongPollingBot.
@Component
@AllArgsConstructor
public class TelegramBot extends TelegramLongPollingBot {
private final BotConfig botConfig;
@Override
public String getBotUsername() {
return botConfig.getBotName();
}
@Override
public String getBotToken() {
return botConfig.getToken();
}
@Override
public void onUpdateReceived(Update update) {
CurrencyModel currencyModel = new CurrencyModel();
String currency = "";
if(update.hasMessage() && update.getMessage().hasText()){
String messageText = update.getMessage().getText();
long chatId = update.getMessage().getChatId();
switch (messageText){
case "/start":
startCommandReceived(chatId, update.getMessage().getChat().getFirstName());
break;
default:
try {
currency = CurrencyService.getCurrencyRate(messageText, currencyModel);
} catch (IOException e) {
sendMessage(chatId, "We have not found such a currency." + "\n" +
"Enter the currency whose official exchange rate" + "\n" +
"you want to know in relation to BYN." + "\n" +
"For example: USD");
} catch (ParseException e) {
throw new RuntimeException("Unable to parse date");
}
sendMessage(chatId, currency);
}
}
}
private void startCommandReceived(Long chatId, String name) {
String answer = "Hi, " + name + ", nice to meet you!" + "\n" +
"Enter the currency whose official exchange rate" + "\n" +
"you want to know in relation to BYN." + "\n" +
"For example: USD";
sendMessage(chatId, answer);
}
private void sendMessage(Long chatId, String textToSend){
SendMessage sendMessage = new SendMessage();
sendMessage.setChatId(String.valueOf(chatId));
sendMessage.setText(textToSend);
try {
execute(sendMessage);
} catch (TelegramApiException e) {
}
}
}
В данном классе внедряем класс BotConfig с нашими настройками для подключения к боту. Переопределяем три метода класса TelegramLongPollingBot:
getBotUsername() — возвращаем имя нашего бота.
getBotToken() — возвращаем токен нашего бота.
onUpdateReceived(Update update) — данный метод вызывается каждый раз при отправке сообщения пользователем. Он вызывается с параметром update, с помощью которого мы можем получить текст сообщения, id чата для отправки ответного сообщения и другую информацию.
Также я написал дополнительно два метода:
startCommandReceived(Long chatId, String name) — данный метод отправляет приветствие после набора команды /start в telegram.
sendMessage(Long chatId, String textToSend) — данный метод через id чата отправляет пользователю сообщение в telegram.
И последний класс, который мы создадим BotInitializer в папке config.
@Component
public class BotInitializer {
private final TelegramBot telegramBot;
@Autowired
public BotInitializer(TelegramBot telegramBot) {
this.telegramBot = telegramBot;
}
@EventListener({ContextRefreshedEvent.class})
public void init()throws TelegramApiException{
TelegramBotsApi telegramBotsApi = new TelegramBotsApi(DefaultBotSession.class);
try{
telegramBotsApi.registerBot(telegramBot);
} catch (TelegramApiException e){
}
}
}
В данном классе создаем сессию и регистрируем нашего telegram бота.
Наш бот готов! Будем тестировать!
Находим нашего бота в telegram.
Запускаем проект.
Переходим в telegram и нажимаем start или набираем /start и получаем
Можем тестировать дальше. Наш функционал работает.
Спасибо Всем кто читал данную статью. Пока.
Комментарии (8)
LeshaRB
00.00.0000 00:00-2Ты уже во всех чатах Беларуси со своим ботом отметился, и решил на Хабр выйти?!
Так сказать российский рынок подмять....
MiSta1984 Автор
00.00.0000 00:00Спасибо за комментарий. Данное API для курсов валют было использовано, так как до него можно достучаться без каких-либо дополнительных регистраций, и исключительно для примера. Никакой "Теории заговора" тут нет)))
Kubig
00.00.0000 00:00Спасибо за статью.
Нет желание развить тему с кнопочками?
MiSta1984 Автор
00.00.0000 00:00Спасибо за комментарий. Пока в планах нет такого, но я подумаю))) Хотя тут уже есть каркас, на который при желании и с учетом того, что хотите получить на выходе, можно дальше "пилить": кнопки, базу данных подключать и т.д.
HemulGM
А я уместился в 52 строки (+14 строк модель) на Delphi. Но это вряд ли кому-то интересно. Что вы хотели этой статьей сказать?
Код
MiSta1984 Автор
Спасибо за комментарий. Классный пример на Delphi