Телеграм бот прогноза погоды на Java Spring

МЕНЮ


Главная страница
Поиск
Регистрация на сайте
Помощь проекту
Архив новостей

ТЕМЫ


Новости ИИРазработка ИИВнедрение ИИРабота разума и сознаниеМодель мозгаРобототехника, БПЛАТрансгуманизмОбработка текстаТеория эволюцииДополненная реальностьЖелезоКиберугрозыНаучный мирИТ индустрияРазработка ПОТеория информацииМатематикаЦифровая экономика

Авторизация



RSS


RSS новости


Здравствуйте, сегодня мы создадим простого бота для Телеграм, который демонстрирует базовые возможности работы с Telegram API. Работать он будет следующим образом:

Регистрация бота в Telegram и получение токена

Тут всё довольно просто, необходимо написать @BotFather и следовать его инструкциям, если вы всё сделаете правильно, то получите сообщение такого вида:

Это и есть необходимый токен для бота.

Регистрация в openweather и получение ключа доступа

Заходим на сайт https://openweathermap.org/ и проходим регистрацию, ключ находится в разделе MyAPI keys. По бесплатному тарифу вам доступно до 60 звонков в минуту и до 1 000 000 в месяц.

Наш бот будет получать данные по текущей погоде, поэтому шаблон API ссылки будет такой - http://api.openweathermap.org/data/2.5/weather?q={city}&appid={key}&units=metric&lang=ru , где units=metric отвечает за единицу измерения температуры в цельсиях.

О других возможностях API можно почитать в документации на сайте сервиса.

Подготовка проекта

Далее создаем пустой проект Spring Boot с помощью https://start.spring.io/, если вы используете IntelliJ IDEA, то можете использовать встроенный инициализатор Spring Boot проекта.

После создания проекта добавляем необходимые зависимости:

pom.xml

<dependencies>      			<!--Драйвер для MongoDB-->         <dependency>             <groupId>org.springframework.boot</groupId>             <artifactId>spring-boot-starter-data-mongodb</artifactId>         </dependency>    				<!--Аннотации для оптимизации Java кода-->         <dependency>             <groupId>org.projectlombok</groupId>             <artifactId>lombok</artifactId>             <optional>true</optional>         </dependency>            			<!--Библиотека для удобной работы с Telegram API-->         <dependency>             <groupId>org.telegram</groupId>             <artifactId>telegrambots</artifactId>             <version>5.2.0</version>         </dependency>   			   			<!--Библиотека для парсинга эмоджи-->         <dependency>             <groupId>com.vdurmont</groupId>             <artifactId>emoji-java</artifactId>             <version>5.1.1</version>         </dependency>      			<dependency>             <groupId>org.springframework.boot</groupId>             <artifactId>spring-boot-starter-web</artifactId>         </dependency>     </dependencies>

Наш бот будет использовать MongoDB для хранения конфигурации, а также для хранения состояния относительно чатов.

Пройдёмся по необходимым сущностям (документам):

BotConfig - конфигурация нашего бота

(collection = "bot_config") public class BotConfig {     @Id     private BigInteger id;    	//имя бота, которое вы указали при регистрации     private String name;    	//токен     private String accessToken;    	//http://api.openweathermap.org/data/2.5/weather?q={city}&appid=ВАШ_КЛЮЧ&units=metric&lang=ru     private String nowWeatherApiTemp;    	//подробнее о данной ссылке ниже   	//https://api.telegram.org/bot{token}/answerCallbackQuery?callback_query_id={id}     private String telegramCallbackAnswerTemp;      private List<Command> commands; } public class Command {     private String name;    // /command     private String description;  //  bla bla bla }

ChatConfig - Информация о чатах с пользователями

(collection = "chats_config") public class ChatConfig {     @Id     private BigInteger id;      @NonNull     private Long chatId;(targetType = FieldType.STRING)     private BotState botState;        	//стандартный город для пользователя     private String city; }

Также при разработке нам понадобятся три enum:

BotState - Состояния бота

public enum BotState {     DEFAULT,SEARCH_NOW,SEARCH_PREDICT,NOW,PREDICT,SET_CITY }

KeyboardType - Группы кнопок в Телеграм чате, в нашем случае понадобится только одна

public enum KeyboardType {     CITY_CHOOSE }

MainCommand - Команды, которые бот будет воспринимать, находясь в состоянии DEFAULT

public enum MainCommand {     START,HELP,CITY,SETCITY,NOW,CANCEL }

Создание компонентов для работы с базой данных и API Openweather

Далее необходимо создать репозитории и сервисы для документов

BotConfigService - Сервис для работы с конфигурацией бота

@Service public class BotConfigService {     @Autowired    	//пустой интерфейс, наследуемый от MongoRepository<BotConfig, BigInteger>     private BotConfigRepo botConfigRepo;       public String getTelegramCallbackAnswerTemp(){         return this.botConfigRepo.findAll().get(0).getTelegramCallbackAnswerTemp();     }      public String getNowApiTemp(){         return this.botConfigRepo.findAll().get(0).getNowWeatherApiTemp();     }      public List<Command> getAllCommands(){         return botConfigRepo.findAll().get(0).getCommands();     }      public String getBotUsername(){         return botConfigRepo.findAll().get(0).getName();     }      public String getBotAccessToken(){         return botConfigRepo.findAll().get(0).getAccessToken();     } }

ChatConfigRepo и ChatConfigService

public interface ChatConfigRepo extends MongoRepository<ChatConfig, BigInteger> {     ChatConfig findAllByChatId(Long chatId);     void deleteByChatId(Long chatId); }   @Service public class ChatConfigService {     @Autowired     private ChatConfigRepo chatConfigRepo;      public boolean isChatInit(Long chatId){         return chatConfigRepo.findAllByChatId(chatId) != null;     }    	//создание нового чата     public void initChat(Long chatId){         chatConfigRepo.save(new ChatConfig(chatId, BotState.DEFAULT));     }      public void deleteChat(Long chatId){         chatConfigRepo.deleteByChatId(chatId);     }      public void setBotState(Long chatId,BotState botState){         ChatConfig chatConfig = chatConfigRepo.findAllByChatId(chatId);         chatConfig.setBotState(botState);         chatConfigRepo.save(chatConfig);     }      public BotState getBotState(Long chatId){         return chatConfigRepo.findAllByChatId(chatId).getBotState();     }      public void setCity(Long chatId,String city){         ChatConfig chatConfig = chatConfigRepo.findAllByChatId(chatId);         chatConfig.setCity(city);         chatConfigRepo.save(chatConfig);     }      public String getCity(Long chatId){         return chatConfigRepo.findAllByChatId(chatId).getCity();     } }

Теперь давайте создадим классы для работы с API погоды. При запросе нам приходит ответ вида:

current weather json response

{     "coord": {         "lon": 37.6156,         "lat": 55.7522     },     "weather": [         {             "id": 500,             "main": "Rain",             "description": "небольшой дождь",             "icon": "10n"         }     ],     "base": "stations",     "main": {         "temp": 13.78,         "feels_like": 13.69,         "temp_min": 12.37,         "temp_max": 14.24,         "pressure": 1013,         "humidity": 95,         "sea_level": 1013,         "grnd_level": 995     },     "visibility": 10000,     "wind": {         "speed": 3.52,         "deg": 43,         "gust": 9.4     },     "rain": {         "1h": 0.22     },     "clouds": {         "all": 100     },     "dt": 1623359195,     "sys": {         "type": 2,         "id": 2000314,         "country": "RU",         "sunrise": 1623372350,         "sunset": 1623435164     },     "timezone": 10800,     "id": 524901,     "name": "Москва",     "cod": 200 }

Из всего этого огромного количества полей мы будем использовать всего несколько: weather.main, weather.description, main.temp, main.feels_like.

Создадим модель для ответа:

WeatherNow

 public class WeatherNow {     private List<Weather> weather;     private Main main; } public class Weather {     private String main;     private String description; } public class Main {     private Integer temp;      @JsonProperty("feels_like")     private Integer feelsLike; }

Далее создадим класс, методы которого будут делать запросы на API, а также сервис для него

WeatherRestMap

@Component public class WeatherRestMap {     @Autowired     private RestTemplate restTemplate;     @Autowired     private BotConfigService botConfigService;  		//получение текущей погоды     public WeatherNow getNowWeather(String city){         try {             return restTemplate.getForObject(botConfigService.getNowApiTemp()             										.replace("{city}",city),              										WeatherNow.class);         }catch (Exception e){             e.printStackTrace();             return null;         }     }  		//проверка существования города     public boolean isCity(String city) throws IOException {          URL weatherApiUrl = new URL(botConfigService.getNowApiTemp().replace("{city}",city));                  HttpURLConnection weatherApiConnection = (HttpURLConnection)weatherApiUrl.openConnection();         weatherApiConnection.setRequestMethod("GET");         weatherApiConnection.connect();         return weatherApiConnection.getResponseCode() == HttpURLConnection.HTTP_OK;     } }

WeatherService

@Service public class WeatherService {     @Autowired     private WeatherRestMap weatherRestMap;      public boolean isCity(String city) throws IOException {         return weatherRestMap.isCity(city);     }      public WeatherNow getCurrentWeather(String city){         return weatherRestMap.getNowWeather(city);     } }

Создание логики по взаимодействию с Telegram API

Для начала создадим основной класс нашего бота, наследуемый от TelegramLongPollingBot из библиотеки для работы с Telegram API

Класс WeatherBot

@Component public class WeatherBot extends TelegramLongPollingBot {     @Autowired     private BotConfigService botConfigService;     @Autowired     private WeatherBotFacade weatherBotFacade;      @Override     public String getBotUsername() {         return botConfigService.getBotUsername();     }      @Override     public String getBotToken() {         return botConfigService.getBotAccessToken();     }    	     @SneakyThrows //отслеживание Exceptions     @Override     public void onUpdateReceived(Update update) {         weatherBotFacade.handleUpdate(update);     } }

Метод onUpdateReceived получает с Telegram API так называемые апдейты, это может быть как сообщение, поступившее боту, так и какое-либо другое изменение в чате с ботом (изменение сообщения, удаление чата и.т.д.)

Также необходимо инициализировать нашего бота после запуска приложения

Класс BotInit

@Component public class BotInit {     @Autowired     private WeatherBot weatherBot;          	//после того, как приложение полностью запущено     @EventListener({ApplicationReadyEvent.class})     public void init() throws TelegramApiException {         TelegramBotsApi telegramBotsApi = new TelegramBotsApi(           																			DefaultBotSession.class);         try {             telegramBotsApi.registerBot(weatherBot);         } catch (TelegramApiRequestException e) {             e.printStackTrace();         }     } }

Для создания класса WeatherBotFacade, в котором будет реализована основная логика по взаимодействию с Telegram API, необходимо создать несколько вспомогательных классов:

Первый - сервис, который будет возвращать строки с сообщениями от бота:

MessageGenerator

@Service public class MessageGenerator {     @Autowired     private BotConfigService botConfigService;     @Autowired     private WeatherService weatherService;      private String message;      public String generateStartMessage(String name){         return EmojiParser.parseToUnicode("Привет, " + name + " :wave:  Чтобы узнать, как мной пользоваться - введите /help");     }      public String generateHelpMessage(){         message = "";         message = ":sunny: Вот мои доступные команды :sunny:  ";         botConfigService.getAllCommands()                 .forEach(command -> {                     message = message + command.getName() + " - " + command.getDescription() + " ";                 });         return EmojiParser.parseToUnicode(message);     }      public String generateSuccessCancel(){         return EmojiParser.parseToUnicode(":white_check_mark: Активная команда успешно отклонена");     }      public String generateSuccessSetCity(String city){         return EmojiParser.parseToUnicode(":white_check_mark: Новый стандартный город - " + city);     }      public String generateErrorCity(){         return EmojiParser.parseToUnicode(":x: Такого города не существует");     }      public String generateSuccessGetCity(String city){         return EmojiParser.parseToUnicode(":cityscape: Стандартный город - " + city);     }      public String generateErrorGetCity(){         return EmojiParser.parseToUnicode(":x: Стандартный город не назначен");     }      public String generateCurrentWeather(String city){         WeatherNow weatherNow = weatherService.getCurrentWeather(city);         return EmojiParser.parseToUnicode("Текущая погода  " +                 "В городе " + city + " " + weatherNow.getWeather().get(0).getDescription() + " " +                 ":thermometer: Температура: " + weatherNow.getMain().getTemp() + "°C, ощущается как " + weatherNow.getMain().getFeelsLike() + "°C");     } } 

Здесь мы используем библиотеку для парсинга иконок, код вида :icon: для любого эмоджи можно найти на сайте https://emojipedia.org/

Далее создаём класс, который будет создавать кнопки в сообщениях от бота

KeyboardService

@Service public class KeyboardService {     @Autowired     private ChatConfigService chatConfigService;      private final InlineKeyboardMarkup keyboard = new InlineKeyboardMarkup();       public InlineKeyboardMarkup setChooseCityKeyboard(Long chatId){         List<InlineKeyboardButton> keyboardRow = new ArrayList<>();         InlineKeyboardButton button1 = new InlineKeyboardButton();               	//текст на кнопке       	button1.setText(chatConfigService.getCity(chatId));              	//сообщение, которое она возвращает         button1.setCallbackData(getCurrentCityNowButton(chatConfigService                                                         .getCity(chatId)));          InlineKeyboardButton button2 = new InlineKeyboardButton();         button2.setText("Другой");         button2.setCallbackData(getChooseCityNowButtonData());          keyboardRow.add(button1);         keyboardRow.add(button2);         keyboard.setKeyboard(Arrays.asList(keyboardRow));          return keyboard;     }      public String getChooseCityNowButtonData(){         return "Введите необходимый город";     }      public String getCurrentCityNowButton(String city){         return "Сейчас " + city;     } } 

Кнопки с CallbackData часто используются для перенаправления пользователей на сторонние ресурсы, например для совершения оплаты, в нашем случае они будут просто возвращать сообщение, однако согласно документации Telegram API, необходимо вернуть callbackAnswer, содержащий поле callback_query_id. Подробнее о методе и его полях - https://core.telegram.org/bots/api#answercallbackquery

Если не вернуть callbackAnswer, то у кнопки будет состояние загрузки до окончания таймаута (около 15 секунд), что может ввести в заблуждение пользователя

Загрузка кнопки

Для использования callbackAnswer создадим одноименный класс, в котором будем делать одиночный HTTP запрос на нужный метод - https://api.telegram.org/bot{token}/answerCallbackQuery?callback_query_id={id}

Класс CallbackAnswer

@Service public class CallbackAnswer {     @Autowired     private BotConfigService botConfigService;      public void callbackAnswer(String callbackId) throws IOException, InterruptedException {         HttpClient telegramApiClient = HttpClient.newHttpClient();         HttpRequest telegramCallbackAnswerReq = HttpRequest.newBuilder(URI                             .create(botConfigService                 								.getTelegramCallbackAnswerTemp()                 								.replace("{token}",botConfigService.getBotAccessToken())                 								.replace("{id}",callbackId)))                     				.GET().build();          telegramApiClient.send(telegramCallbackAnswerReq, HttpResponse.BodyHandlers                                		.ofString());     } }

Теперь же приступим к основному классу WeatherBotFacade

Для начала создадим метод для отправки сообщения ботом:

void sendMessage

		private Long setChatIdToMessageBuilder(Update update, SendMessage.SendMessageBuilder messageBuilder){         Long chatId = null;         if (update.hasMessage()) {             chatId = update.getMessage().getChatId();             messageBuilder.chatId(update.getMessage().getChatId().toString());         } else if (update.hasChannelPost()) {             chatId = update.getChannelPost().getChatId();             messageBuilder.chatId(update.getChannelPost().getChatId().toString());         }else if (update.hasCallbackQuery()){             chatId = update.getCallbackQuery().getMessage().getChatId();             messageBuilder.chatId(update.getCallbackQuery().getMessage().getChatId().toString());         }         return chatId;     }  		private void sendMessage(Update update,String messageText){         SendMessage.SendMessageBuilder messageBuilder = SendMessage.builder();          Long chatId = setChatIdToMessageBuilder(update,messageBuilder);          messageBuilder.text(messageText);          try {             weatherBot.execute(messageBuilder.build());         }catch (TelegramApiException telegramApiException){             telegramApiException.printStackTrace();         }     }      private void sendMessage(Update update, String messageText, KeyboardType keyboardType) {         SendMessage.SendMessageBuilder messageBuilder = SendMessage.builder();          Long chatId = setChatIdToMessageBuilder(update, messageBuilder);          messageBuilder.text(messageText);          switch (keyboardType) {             case CITY_CHOOSE: {               	//устанавливаем кнопки, созданные выше                 messageBuilder.replyMarkup(keyboardService.setChooseCityKeyboard(chatId));                 break;             }         }          try {             weatherBot.execute(messageBuilder.build());         }catch (TelegramApiException telegramApiException){             telegramApiException.printStackTrace();         }     }  

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

void handleUpdate

public void handleUpdate(Update update) throws IOException, InterruptedException {         String messageText;         Long chatId;         String userFirstName = ""; 				   			//если сообщение пришло в лс боту         if (update.hasMessage()) {             chatId = update.getMessage().getChatId();             messageText = update.getMessage().getText().toUpperCase(Locale.ROOT).replace("/","");             userFirstName = update.getMessage().getChat().getFirstName();         }    			//если пришло сообщение с кнопок, которые мы создавали выше         else if (update.hasCallbackQuery()){             callbackAnswer.callbackAnswer(update.getCallbackQuery().getId());              chatId = update.getCallbackQuery().getMessage().getChatId();             messageText = update.getCallbackQuery().getData().toUpperCase(Locale.ROOT);             sendMessage(update,update.getCallbackQuery().getData());              if (messageText.equals(keyboardService.getChooseCityNowButtonData().toUpperCase(Locale.ROOT))){                 chatConfigService.setBotState(chatId,BotState.SEARCH_NOW);                 return;             }              else if (messageText.equals(keyboardService.getCurrentCityNowButton(chatConfigService.getCity(chatId)).toUpperCase(Locale.ROOT))){                 chatConfigService.setBotState(chatId,BotState.NOW);             }         }    			//если человек присоединился к чату или покинул его         else if (update.hasMyChatMember()) {           	//удаляем данные о чате из бд, если пользователь покинул чат с ботом             if (update.getMyChatMember().getNewChatMember().getStatus().equals("kicked")){                 chatConfigService.deleteChat(update.getMyChatMember().getChat().getId());             }              return;         }else {              return;         }    			//создаём запись о чате в бд и возвращаем приветствие          if (!chatConfigService.isChatInit(chatId)){             chatConfigService.initChat(chatId);             sendMessage(update, messageGenerator.generateStartMessage(userFirstName));         }else{           	//отслеживаем состояние бота относительно текущего чата             handleBotState(update,chatId,messageText,userFirstName);         }     }

Ну и последний метод, который нам понадобится будет отслеживать состояние бота относительно чата и возвращать нужные сообщения:

void handleBotState

private void handleBotState(Update update,Long chatId,String messageText,String userFirstName) throws IOException {         BotState botState = chatConfigService.getBotState(chatId); 				   			// /start - Приветствие         if (messageText.equals(MainCommand.START.name())) {             chatConfigService.setBotState(chatId,BotState.DEFAULT);             sendMessage(update,messageGenerator.generateStartMessage(userFirstName));             return;         }    			// /cancel Возвращение бота в состояние DEFAULT (отмена текущей команды)         if (messageText.equals(MainCommand.CANCEL.name())){             if (botState == BotState.DEFAULT){                 sendMessage(update,"Нет активной команды для отклонения");             }else {                 chatConfigService.setBotState(chatId,BotState.DEFAULT);                 sendMessage(update,messageGenerator.generateSuccessCancel());                 return;             }         }          switch (botState) {             case DEFAULT: {                	// /help - Список команд                 if (messageText.equals(MainCommand.HELP.name())) {                     sendMessage(update, messageGenerator.generateHelpMessage());                 }                	// /setcity - Установка стандартного города                 else if (messageText.equals(MainCommand.SETCITY.name())) {                     chatConfigService.setBotState(chatId, BotState.SET_CITY);                     sendMessage(update, "Введите новый стандартный город");                 }                	// /city - Текущий стандартный город для чата                  else if (messageText.equals(MainCommand.CITY.name())) {                     if (chatConfigService.getCity(chatId) != null && !chatConfigService.getCity(chatId).equals("")) sendMessage(update, messageGenerator.generateSuccessGetCity(chatConfigService.getCity(chatId)));                     else sendMessage(update, messageGenerator.generateErrorGetCity());                 } 								               	// /now - Узнать текущую погоду                 else if (messageText.equals(MainCommand.NOW.name())) {                     chatConfigService.setBotState(chatId, BotState.NOW);                     sendMessage(update, "Выберите город", KeyboardType.CITY_CHOOSE);                 }                  break;             }              case SET_CITY: { 								               	//проверка - существует ли введенный пользователем город                 if (weatherService.isCity(messageText.toLowerCase(Locale.ROOT))) {                     chatConfigService.setCity(chatId, messageText.charAt(0)+messageText.substring(1).toLowerCase(Locale.ROOT));                     chatConfigService.setBotState(chatId, BotState.DEFAULT);                     sendMessage(update, messageGenerator.generateSuccessSetCity(chatConfigService.getCity(chatId)));                 }                  else sendMessage(update, messageGenerator.generateErrorCity());                  break;             }              case NOW: {                	// если выбран не стандартный город                 if (messageText.equals(keyboardService.getChooseCityNowButtonData().toUpperCase(Locale.ROOT)))                 {                     chatConfigService.setBotState(chatId,BotState.SEARCH_NOW);                 }                	// погода для стандартного города                 else {                     chatConfigService.setBotState(chatId,BotState.DEFAULT);                     sendMessage(update,messageGenerator.generateCurrentWeather(chatConfigService.getCity(chatId)));                 }                 break;             }              case SEARCH_NOW: {               	// проверка на существование города                 if (!weatherService.isCity(messageText)){                     sendMessage(update,messageGenerator.generateErrorCity());                 }                	// погода для введенного города                 else {                     sendMessage(update,messageGenerator.generateCurrentWeather(messageText.charAt(0) + messageText.substring(1).toLowerCase(Locale.ROOT)));                     chatConfigService.setBotState(chatId,BotState.DEFAULT);                 }                  break;             }         }     }

Полный код класса WeatherBotFacade:

WeatherBotFacade

@Component public class WeatherBotFacade {     @Autowired     private ChatConfigService chatConfigService;     @Autowired     private MessageGenerator messageGenerator;     @Autowired     private WeatherService weatherService;     @Autowired     private KeyboardService keyboardService;     @Autowired     private WeatherBot weatherBot;     @Autowired     private CallbackAnswer callbackAnswer;       public void handleUpdate(Update update) throws IOException, InterruptedException {         String messageText;         Long chatId;         String userFirstName = "";          if (update.hasMessage()) {             chatId = update.getMessage().getChatId();             messageText = update.getMessage().getText().toUpperCase(Locale.ROOT).replace("/","");             userFirstName = update.getMessage().getChat().getFirstName();         }          else if (update.hasChannelPost()){             chatId = update.getChannelPost().getChatId();             messageText = update.getChannelPost().getText().toUpperCase(Locale.ROOT).replace("/","");             userFirstName = update.getChannelPost().getChat().getFirstName();         }          else if (update.hasCallbackQuery()){             callbackAnswer.callbackAnswer(update.getCallbackQuery().getId());              chatId = update.getCallbackQuery().getMessage().getChatId();             messageText = update.getCallbackQuery().getData().toUpperCase(Locale.ROOT);             sendMessage(update,update.getCallbackQuery().getData());              if (messageText.equals(keyboardService.getChooseCityNowButtonData().toUpperCase(Locale.ROOT))){                 chatConfigService.setBotState(chatId,BotState.SEARCH_NOW);                 return;             }              else if (messageText.equals(keyboardService.getCurrentCityNowButton(chatConfigService.getCity(chatId)).toUpperCase(Locale.ROOT))){                 chatConfigService.setBotState(chatId,BotState.NOW);             }         }          else if (update.hasMyChatMember()) {             if (update.getMyChatMember().getNewChatMember().getStatus().equals("kicked")){                 chatConfigService.deleteChat(update.getMyChatMember().getChat().getId());             }              return;         }else {              return;         }          if (!chatConfigService.isChatInit(chatId)){             chatConfigService.initChat(chatId);             sendMessage(update, messageGenerator.generateStartMessage(userFirstName));         }else{             handleBotState(update,chatId,messageText,userFirstName);         }     }       private Long setChatIdToMessageBuilder(Update update, SendMessage.SendMessageBuilder messageBuilder){         Long chatId = null;         if (update.hasMessage()) {             chatId = update.getMessage().getChatId();             messageBuilder.chatId(update.getMessage().getChatId().toString());         } else if (update.hasChannelPost()) {             chatId = update.getChannelPost().getChatId();             messageBuilder.chatId(update.getChannelPost().getChatId().toString());         }else if (update.hasCallbackQuery()){             chatId = update.getCallbackQuery().getMessage().getChatId();             messageBuilder.chatId(update.getCallbackQuery().getMessage().getChatId().toString());         }         return chatId;     }      private void sendMessage(Update update,String messageText){         SendMessage.SendMessageBuilder messageBuilder = SendMessage.builder();          Long chatId = setChatIdToMessageBuilder(update,messageBuilder);          messageBuilder.text(messageText);          try {             weatherBot.execute(messageBuilder.build());         }catch (TelegramApiException telegramApiException){             telegramApiException.printStackTrace();         }     }      private void sendMessage(Update update, String messageText, KeyboardType keyboardType) {         SendMessage.SendMessageBuilder messageBuilder = SendMessage.builder();          Long chatId = setChatIdToMessageBuilder(update, messageBuilder);          messageBuilder.text(messageText);          switch (keyboardType) {             case CITY_CHOOSE: {                 messageBuilder.replyMarkup(keyboardService.setChooseCityKeyboard(chatId));                 break;             }         }          try {             weatherBot.execute(messageBuilder.build());         }catch (TelegramApiException telegramApiException){             telegramApiException.printStackTrace();         }     }      private void handleBotState(Update update,Long chatId,String messageText,String userFirstName) throws IOException {         BotState botState = chatConfigService.getBotState(chatId);          if (messageText.equals(MainCommand.START.name())) {             chatConfigService.setBotState(chatId,BotState.DEFAULT);             sendMessage(update,messageGenerator.generateStartMessage(userFirstName));             return;         }          if (messageText.equals(MainCommand.CANCEL.name())){             if (botState == BotState.DEFAULT){                 sendMessage(update,"Нет активной команды для отклонения");             }else {                 chatConfigService.setBotState(chatId,BotState.DEFAULT);                 sendMessage(update,messageGenerator.generateSuccessCancel());                 return;             }         }          switch (botState) {             case DEFAULT: {                  if (messageText.equals(MainCommand.HELP.name())) {                     sendMessage(update, messageGenerator.generateHelpMessage());                 }                  else if (messageText.equals(MainCommand.SETCITY.name())) {                     chatConfigService.setBotState(chatId, BotState.SET_CITY);                     sendMessage(update, "Введите новый стандартный город");                 }                  else if (messageText.equals(MainCommand.CITY.name())) {                     if (chatConfigService.getCity(chatId) != null && !chatConfigService.getCity(chatId).equals("")) sendMessage(update, messageGenerator.generateSuccessGetCity(chatConfigService.getCity(chatId)));                     else sendMessage(update, messageGenerator.generateErrorGetCity());                 }                  else if (messageText.equals(MainCommand.NOW.name())) {                     chatConfigService.setBotState(chatId, BotState.NOW);                     sendMessage(update, "Выберите город", KeyboardType.CITY_CHOOSE);                 }                  break;             }              case SET_CITY: {                  if (weatherService.isCity(messageText.toLowerCase(Locale.ROOT))) {                     chatConfigService.setCity(chatId, messageText.charAt(0)+messageText.substring(1).toLowerCase(Locale.ROOT));                     chatConfigService.setBotState(chatId, BotState.DEFAULT);                     sendMessage(update, messageGenerator.generateSuccessSetCity(chatConfigService.getCity(chatId)));                 }                  else sendMessage(update, messageGenerator.generateErrorCity());                  break;             }              case NOW: {                  if (messageText.equals(keyboardService.getChooseCityNowButtonData().toUpperCase(Locale.ROOT)))                 {                     chatConfigService.setBotState(chatId,BotState.SEARCH_NOW);                 }                  else {                     chatConfigService.setBotState(chatId,BotState.DEFAULT);                     sendMessage(update,messageGenerator.generateCurrentWeather(chatConfigService.getCity(chatId)));                 }                 break;             }              case SEARCH_NOW: {                 if (!weatherService.isCity(messageText)){                     sendMessage(update,messageGenerator.generateErrorCity());                 }                  else {                     sendMessage(update,messageGenerator.generateCurrentWeather(messageText.charAt(0) + messageText.substring(1).toLowerCase(Locale.ROOT)));                     chatConfigService.setBotState(chatId,BotState.DEFAULT);                 }                  break;             }         }     } }

Источник: m.vk.com

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