Всем привет! Тема создания ботов для Телеграмм несколько избита, и гайдов написано очень много (например, вот этот). Поэтому, мы лучше рассмотрим подробнее работу со каким-нибудь сторонним API, так как это критически важный навык для любого web-разработчика.
Сразу скажу, что приложение не ставило целью предоставить максимально функциональный и полезный прогноз, конкурировать с погодными сайтами нет смысла, важно было научиться работать с удалёнными соединениями и парсингом данных средствами Java.
Итак, выясним для начала, что нам нужно. Наше приложение, по сути, состоит из трёх логических частей:
- принять сообщение от пользователя
- обработать сообщение, и, если это валидная команда, подготовить данные для ответа. В нашем случае, подготовить прогноз погоды, если пользователь ввёл корректный город
- отправить готовую информацию пользователю в чат
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, справа - структурированный:
Видно что ответ состоит из 5 JSON-объектов высшего уровня, 2 из которых сложные и являются узлами для следующих ветвей. Интересующие нас данные хранятся в узле list. Внутри list - массив из 38 JSON-строк, в каждой описана погода в определённое время. То есть это такая себе древовидная структура, где есть корень, ветви, веточки и даже листья :) А на узлах как раз и проходит разветвление. К счастью, Jackson умеет представлять любой валидный JSON в виде дерева. Таким образом, зная как называется нужный нам атрибут(например, температура воздуха), и на каком уровне дерева он находится, достать его не составит особых проблем.
Для начала я извлёк все строчки из массива "list" и добавил их в отдельный список. Я, грубо говоря, порезал простыню с данными на куски, каждый из которых - это отдельный прогноз. Маленькие части проще держать в голове и оперировать ими.
//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 Полагаю, что тут могут быть опечатки, поэтому можете всё присылать в личку, ну, или в комментарии, если вы прям очень злые :)
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ