Всем привет! Тема создания ботов для Телеграмм несколько избита, и гайдов написано очень много (например, вот этот). Поэтому, мы лучше рассмотрим подробнее работу со каким-нибудь сторонним API, так как это критически важный навык для любого web-разработчика. Сразу скажу, что приложение не ставило целью предоставить максимально функциональный и полезный прогноз, конкурировать с погодными сайтами нет смысла, важно было научиться работать с удалёнными соединениями и парсингом данных средствами Java. Итак, выясним для начала, что нам нужно. Наше приложение, по сути, состоит из трёх логических частей:
  • принять сообщение от пользователя
  • обработать сообщение, и, если это валидная команда, подготовить данные для ответа. В нашем случае, подготовить прогноз погоды, если пользователь ввёл корректный город
  • отправить готовую информацию пользователю в чат
Первый и третий пункт довольно прост, и касается только работы с Telegram API, желающие могут изучить ссылку, оставленную выше. Мы же заострим внимание на втором пункте. API используют тогда, когда одни разработчики хотят предоставить доступ к своим данным другим разработчика. Возьмём, к примеру, Вконтакте. У каждого есть список друзей, он хранится где-то в базе данных на серверах ВК. Предположим, что какой-то программист решил создать игру в шашки с друзьями. Чтобы его приложение работало правильно, программа должна уметь получать список друзей любого игрока. Для этого программист находит документацию к ВК API, и смотрит, какой запрос нужно сделать, чтобы это список получить. Этот запрос называется HTTP-запросом. А два самых распространённых HTTP-запроса — это GET и POST. Про них в сети тоже достаточно, останавливать не буду. Для наших целей, а именно получения данных прогноза погоды будет достаточно простого GET-запроса. Если обратиться с GET запросом к обычному веб-серверу, он, зачастую, вернёт html-код, который браузер преобразует в удобную для пользователя страницу, применив стили, скрипты и пр. Если же мы обращаемся с таким запросом к API серверу, в ответ обычно возвращаются только сырые данные без стилей и скриптов. В окне браузера это выглядит примерно так: Эти данные предназначены не для людей, а для других программ, поэтому ничего лишнего, кроме самой информации в таких страницах нет. Сырые данные чаще всего пересылают по одному из двух стандартов: JSON или XML. У каждого свои плюсы и минусы, однако, важно разбираться в обоих. JSON вы уже видели на скрине выше, а ХМL выглядит вот так: После недолгого поиска, был найдёт англоязычный проект Open Weather Map, который отдаёт данные бесплатно, если не делать больше 50 запросов в минуту. Для нас этого вполне достаточно, регистрируемся, получаем уникальный токен(код) по которому сервер будет знать, что мы не самозванцы, а приличные будущие разработчики. Заходим на страницу с API-документацией (тыц), и выясняем, что прогноз на 5 дней по любому городу можно получить, если отправить запрос вида.
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 Полагаю, что тут могут быть опечатки, поэтому можете всё присылать в личку, ну, или в комментарии, если вы прям очень злые :)