Здравствуйте, сегодня мы создадим простого бота для Телеграм, который демонстрирует базовые возможности работы с 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 - конфигурация нашего бота
@Getter @Setter @NoArgsConstructor @Document(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; } @Getter @Setter @NoArgsConstructor public class Command { private String name; // /command private String description; // bla bla bla }
ChatConfig - Информация о чатах с пользователями
@Getter @Setter @NoArgsConstructor @RequiredArgsConstructor @Document(collection = "chats_config") public class ChatConfig { @Id private BigInteger id; @NonNull private Long chatId; @NonNull @Field(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
@Getter @Setter @NoArgsConstructor public class WeatherNow { private List<Weather> weather; private Main main; } @Getter @Setter @NoArgsConstructor public class Weather { private String main; private String description; } @Getter @Setter @NoArgsConstructor 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: \nЧтобы узнать, как мной пользоваться - введите /help"); } public String generateHelpMessage(){ message = ""; message = ":sunny: Вот мои доступные команды :sunny:\n\n"; botConfigService.getAllCommands() .forEach(command -> { message = message + command.getName() + " - " + command.getDescription() + "\n"; }); 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("Текущая погода\n\n" + "В городе " + city + " " + weatherNow.getWeather().get(0).getDescription() + "\n" + ":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; } } } }
