Всім привіт! Тема створення ботів для телеграм дещо побита, і гайдів написано дуже багато (наприклад, ось цей ). Тому ми краще розглянемо докладніше роботу з будь-яким стороннім API, оскільки це критично важлива навичка для будь-якого веб-розробника. Відразу скажу, що додаток не ставив за мету надати максимально функціональний і корисний прогноз, конкурувати з погодними сайтами немає сенсу, важливо було навчитися працювати з віддаленими з'єднаннями та парсингом даних засобами 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. Дякую, якщо подужали до кінця, приймаю розумну критику, бо тільки навчаюсь і напрацьовую простенькі проекти на гітхаб, щоб конкурувати з голодними талантами зі свого міста :) Всім поки що! PS Вважаю, що тут можуть бути друкарські помилки, тому можете все надсилати в особу, ну, або в коментарі, якщо ви дуже злі :)