Всем привет! Тема создания ботов для Телеграмм несколько избита, и гайдов написано очень много (например, вот этот). Поэтому, мы лучше рассмотрим подробнее работу со каким-нибудь сторонним API, так как это критически важный навык для любого web-разработчика.
Сразу скажу, что приложение не ставило целью предоставить максимально функциональный и полезный прогноз, конкурировать с погодными сайтами нет смысла, важно было научиться работать с удалёнными соединениями и парсингом данных средствами Java.
Итак, выясним для начала, что нам нужно. Наше приложение, по сути, состоит из трёх логических частей:
Эти данные предназначены не для людей, а для других программ, поэтому ничего лишнего, кроме самой информации в таких страницах нет.
Сырые данные чаще всего пересылают по одному из двух стандартов: JSON или XML. У каждого свои плюсы и минусы, однако, важно разбираться в обоих.
JSON вы уже видели на скрине выше, а ХМL выглядит вот так:
После недолгого поиска, был найден англоязычный проект Open Weather Map, который отдаёт данные бесплатно, если не делать больше 50 запросов в минуту. Для нас этого вполне достаточно, регистрируемся, получаем уникальный токен(код) по которому сервер будет знать, что мы не самозванцы, а приличные будущие разработчики. Заходим на страницу с API-документацией (тыц), и выясняем, что прогноз на 5 дней по любому городу можно получить, если отправить запрос вида.
Видно что ответ состоит из 5 JSON-объектов высшего уровня, 2 из которых сложные и являются узлами для следующих ветвей. Интересующие нас данные хранятся в узле list. Внутри list - массив из 38 JSON-строк, в каждой описана погода в определённое время. То есть это такая себе древовидная структура, где есть корень, ветви, веточки и даже листья :) А на узлах как раз и проходит разветвление. К счастью, Jackson умеет представлять любой валидный JSON в виде дерева. Таким образом, зная как называется нужный нам атрибут(например, температура воздуха), и на каком уровне дерева он находится, достать его не составит особых проблем.
Для начала я извлёк все строчки из массива "list" и добавил их в отдельный список. Я, грубо говоря, порезал простыню с данными на куски, каждый из которых - это отдельный прогноз. Маленькие части проще держать в голове и оперировать ими.
- принять сообщение от пользователя
- обработать сообщение, и, если это валидная команда, подготовить данные для ответа. В нашем случае, подготовить прогноз погоды, если пользователь ввёл корректный город
- отправить готовую информацию пользователю в чат
![Создаём простой погодный бот для Telegram за несколько вечеров - 1](https://cdn.javarush.com/images/article/20550be6-4530-4f91-bbc2-ebe81b50966c/1024.jpeg)
![Создаём простой погодный бот для Telegram за несколько вечеров - 2](https://cdn.javarush.com/images/article/7e1ac603-5f2e-4449-8338-7400d4bff9d5/1024.jpeg)
https://api.openweathermap.org/data/2.5/forecast?q=(город)&APPID=(уникальный токен, полученный при регистрации)
Можно посмотреть как ответ на такой запрос выглядит в браузере тыц
Мы уже выяснили, что, по сути, нужно просто перейти по правильной ссылке и сервер выдаст нужные данные. Осталось научиться это делать средствами Java. Простой GET запрос на Java выглядит так:
//создаём строку со ссылкой на нужную страницу,
//я тут её склеиваю из заранее определённых констант, меняя только сам город
String urlString = API_CALL_TEMPLATE + city + API_KEY_TEMPLATE;
//создаём объект который будет содержать ссылку
URL urlObject = new URL(urlString);
//создаём соединение, используя объект
HttpURLConnection connection = (HttpURLConnection) urlObject.openConnection();
//выбираем тип запроса (GET)
connection.setRequestMethod("GET");
//тут мы указываем, данные о себе, что мы можем принять всё то,
//что примет и любой современный браузер
connection.setRequestProperty("User-Agent", "Mozilla/5.0");
//В начало ответа сервер всегда вставляет число, по которому можно судить, прошло ли всё хорошо.
//200 - значит OK
int responseCode = connection.getResponseCode();
//на несуществующий город или город с опечаткой, сервер выдаст код ответа 404,
//бросаем на него исключение, чтобы обработать на уровне повыше и предложить
//пользователю ввести город заново
if (responseCode == 404) {
throw new IllegalArgumentException();
}
// создаём поток, вычитываем все строки, и склеиваем в одну большую строку,
//которую будем потом обрабатывать в других методах
BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();
return response.toString();
Если запрос был корректным, а сервер - доступным, мы получим простыню данных, в которой полезная информация перемешана с той, которая сейчас не нужна. Чтобы удобно извлекать из JSON и XML нужные данные, под Java написано тонны библиотек на любой вкус. Так как я предпочёл JSON, для его обработки выбрал весьма популярную библиотеку под названием Jackson. Она, кстати немного изучается на JavaRush на каких-то высоких уровнях.
Для обработки больших объёмов JSON-данных важно понимать структуру документа. На помощь приходят полезные сайты вроде этого .
Слева у нас оригинальный JSON, справа - структурированный:
![Создаём простой погодный бот для Telegram за несколько вечеров - 3](https://cdn.javarush.com/images/article/a9b49861-aab2-44cd-991c-145e3879f185/1024.jpeg)
//JsonNode - это один из узлов в древовидной иерархии, от которого идут ветви
//получаем узел, который называется "list"
JsonNode arrNode = new ObjectMapper().readTree(data).get("list");
//если это действительно массив узлов
if (arrNode.isArray()) {
//выполняем для каждого узла, который содержится в массиве
for (final JsonNode objNode : arrNode) {
//в атрибуте "dt_txt" каждого маленького узла хранилось время прогноза, я отобрал данные за 9 утра и 6 вечера
String forecastTime = objNode.get("dt_txt").toString();
if (forecastTime.contains("09:00") || forecastTime.contains("18:00")) {
weatherList.add(objNode.toString());
}
}
}
Так мы получили список строк, в котором каждая строка представляет собой JSON-сводку погоды в определённое время. Осталось извлечь желаемое и отформатировать. Если у нас есть такая строка:
"main":{"temp":261.45,"temp_min":259.086,"temp_max":261.45,"pressure":1023.48,"sea_level":1045.39,"grnd_level":1023.48,"humidity":79,"temp_kf":2.37}
то, это узел под названием "main". Чтобы получить из него любые данные, к примеру уровень моря, достаточно такого кода:
ObjectMapper objectMapper = new ObjectMapper();
//line - это наша JSON-строка
mainNode = objectMapper.readTree(line).get("main");
String seaLevel = mainNode.get("sea_level");
Jackson позволяет сразу представлять данные в числовом формате:
double seaLevel = mainNode.get("sea_level").asDouble();
Теперь мы можем извлекать любые данные из прогноза, и осталось только их склеить, как пожелается, и отправить пользователю в Telegram.
Полный исходный код у меня на гитхабе, а вот попробовать бота в действии можно по ссылке, либо найдя @denifoBot в поиске Телеграмм. Название города нужно писать в латинской транслитерации, например "Kyiv" или "Moscow".
Спасибо, если осилили до конца, принимаю разумную критику, так как только учусь и нарабатываю простенькие проекты на гитхаб, чтобы конкурировать с голодными талантами из своего города :) Всем пока!
P.S Полагаю, что тут могут быть опечатки, поэтому можете всё присылать в личку, ну, или в комментарии, если вы прям очень злые :)
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ