{"id":337393,"date":"2022-08-22T21:00:05","date_gmt":"2022-08-22T21:00:05","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=337393"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=337393","title":{"rendered":"<span>\u041f\u0438\u0448\u0435\u043c Telegram \u0431\u043e\u0442 \u0442\u0435\u043a\u0443\u0449\u0435\u0439 \u043f\u043e\u0433\u043e\u0434\u044b \u043f\u043e IP \u0430\u0434\u0440\u0435\u0441\u0443 \u043d\u0430 Python aiogram<\/span>"},"content":{"rendered":"<div><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/565\/6ad\/7bf\/5656ad7bfaf28f011a16303ab19cc387.gif\" width=\"600\" height=\"490\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/565\/6ad\/7bf\/5656ad7bfaf28f011a16303ab19cc387.gif\"\/><figcaption><\/figcaption><\/figure>\n<p>\u0428\u0442\u043e\u0448. \u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443 \u0432\u0430\u043c, \u043a\u0430\u043a \u0441\u043e\u0437\u0434\u0430\u0442\u044c Telegram \u0431\u043e\u0442\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 \u0442\u0435\u043a\u0443\u0449\u0443\u044e \u043f\u043e\u0433\u043e\u0434\u0443 \u043f\u043e IP \u0430\u0434\u0440\u0435\u0441\u0443. \u041c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u044f\u0437\u044b\u043a Python \u0438 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u0443\u044e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 \u0434\u043b\u044f \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0441 Telegram Bot API &#8212; aiogram.<\/p>\n<p>\u0418\u0442\u0430\u043a, \u043a\u0430\u043a \u0436\u0435 \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0442\u0430\u043a\u043e\u0433\u043e \u0431\u043e\u0442\u0430? <\/p>\n<details class=\"spoiler\">\n<summary>TL;DR<\/summary>\n<div class=\"spoiler__content\">\n<p>\u0421\u043a\u043b\u043e\u043d\u0438\u0440\u0443\u0439\u0442\u0435 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439 <a href=\"https:\/\/github.com\/lesskop\/shtosh-weather-bot\" rel=\"noopener noreferrer nofollow\">shtosh-weather-bot<\/a> \u0438 \u043f\u0440\u043e\u0439\u0434\u0438\u0442\u0435 \u043f\u043e \u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u0438 \u0432 README.<\/p>\n<\/div>\n<\/details>\n<h2>\u0412\u044b\u0431\u0438\u0440\u0430\u0435\u043c \u043f\u043e\u0433\u043e\u0434\u043d\u044b\u0439 \u0441\u0435\u0440\u0432\u0438\u0441 \u0441 \u0431\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u044b\u043c API <\/h2>\n<p>\u0414\u0430\u043d\u043d\u044b\u0435 \u043e \u0442\u0435\u043a\u0443\u0449\u0435\u0439 \u043f\u043e\u0433\u043e\u0434\u0435 \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u043e\u0442\u043a\u0443\u0434\u0430-\u0442\u043e \u0431\u0440\u0430\u0442\u044c. \u0415\u0449\u0435 \u0436\u0435\u043b\u0430\u0442\u0435\u043b\u044c\u043d\u043e, \u0447\u0442\u043e\u0431\u044b \u044d\u0442\u043e \u0431\u044b\u043b\u043e \u0431\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u043e. \u0423 \u0441\u0430\u0439\u0442\u0430 <a href=\"https:\/\/github.com\/public-apis\/public-apis\" rel=\"noopener noreferrer nofollow\">OpenWeatherMap<\/a> \u0435\u0441\u0442\u044c \u043d\u0443\u0436\u043d\u044b\u0439 \u043d\u0430\u043c <a href=\"https:\/\/openweathermap.org\/current\" rel=\"noopener noreferrer nofollow\">API \u0442\u0435\u043a\u0443\u0449\u0435\u0439 \u043f\u043e\u0433\u043e\u0434\u044b.<\/a> \u0411\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u043e \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0441\u044b\u043b\u0430\u0442\u044c 1000 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u0432 \u0434\u0435\u043d\u044c.<\/p>\n<p><code>https:\/\/api.openweathermap.org\/data\/2.5\/weather?lat={lat}&amp;lon={lon}&amp;appid={API key}<\/code>  <\/p>\n<p>\u041a\u0441\u0442\u0430\u0442\u0438, \u0435\u0441\u043b\u0438 \u0432\u044b \u0438\u0449\u0435\u0442\u0435 \u043a\u0430\u043a\u043e\u0439-\u0442\u043e application user interface \u0434\u043b\u044f \u0441\u0432\u043e\u0435\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430, \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u044e \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439 <a href=\"https:\/\/github.com\/public-apis\/public-apis\" rel=\"noopener noreferrer nofollow\">public-apis.<\/a><\/p>\n<p>\u0418\u0442\u0430\u043a, \u0434\u043b\u044f \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u043d\u0443\u0436\u043d\u044b \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b \u0438 \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0439 \u043a\u043b\u044e\u0447, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c, \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u0432 \u0430\u043a\u043a\u0430\u0443\u043d\u0442. \u041d\u0443 \u044d\u0442\u043e \u0432\u043e\u043e\u0431\u0449\u0435 \u043d\u0435 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430, \u043c\u043e\u0436\u043d\u043e \u0437\u0430\u0440\u0435\u0433\u0430\u0442\u044c \u043d\u0430 <a href=\"https:\/\/tempail.com\/\" rel=\"noopener noreferrer nofollow\">\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e \u043f\u043e\u0447\u0442\u0443.<\/a> \u041a\u043e\u043d\u0435\u0447\u043d\u043e, \u0435\u0441\u043b\u0438 \u0432\u044b \u0441\u043e\u0431\u0438\u0440\u0430\u0435\u0442\u0435\u0441\u044c \u0441\u0435\u0440\u044c\u0435\u0437\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c API \u0438 \u043f\u043e\u043a\u0443\u043f\u0430\u0442\u044c \u0431\u043e\u043b\u044c\u0448\u0435 1000 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u0432 \u0434\u0435\u043d\u044c, \u043b\u0443\u0447\u0448\u0435 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0430\u043a\u043a\u0430\u0443\u043d\u0442 \u043d\u0430 \u0441\u0432\u043e\u044e \u043f\u043e\u0447\u0442\u0443. \u041a\u0430\u043f\u0438\u0442\u0430\u043d \u043e\u0447\u0435\u0432\u0438\u0434\u043d\u043e\u0441\u0442\u044c.<\/p>\n<p>\u0417\u0430\u0445\u043e\u0434\u0438\u043c \u0432 My API keys \u0438 \u0432\u0438\u0434\u0438\u043c \u0437\u0434\u0435\u0441\u044c \u0442\u043e\u0442 \u0441\u0430\u043c\u044b\u0439 \u043a\u043b\u044e\u0447. \u041c\u043e\u0436\u0435\u0442\u0435 \u0432\u0437\u044f\u0442\u044c \u043c\u043e\u0439, \u043c\u043d\u0435 \u043d\u0435 \u0436\u0430\u043b\u043a\u043e.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/cb9\/78f\/a05\/cb978fa05c87603c7e1513b3a66f1137.png\" width=\"553\" height=\"399\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/cb9\/78f\/a05\/cb978fa05c87603c7e1513b3a66f1137.png\"\/><figcaption><\/figcaption><\/figure>\n<p>\u0418\u0442\u0430\u043a, \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u0441\u0444\u043e\u0440\u043c\u0438\u0440\u0443\u0435\u043c \u0437\u0430\u043f\u0440\u043e\u0441. \u042f \u0432\u044b\u0431\u0440\u0430\u043b \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b \u041d\u044c\u044e-\u0419\u043e\u0440\u043a\u0430, \u043f\u0440\u043e\u0441\u0442\u043e \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0445\u043e\u0447\u0443 \u0438 \u043c\u043e\u0433\u0443. <\/p>\n<p><a href=\"https:\/\/api.openweathermap.org\/data\/2.5\/weather?lat=40,7143&amp;lon=-74,006&amp;appid=8537d9ef6386cb97156fd47d832f479c\" rel=\"noopener noreferrer nofollow\">https:\/\/api.openweathermap.org\/data\/2.5\/weather?lat=40,7143&amp;lon=-74,006&amp;appid=8537d9ef6386cb97156fd47d832f479c<\/a><\/p>\n<details class=\"spoiler\">\n<summary>\u0412\u043e\u0442 \u0442\u0430\u043a\u043e\u0439 json \u043c\u044b \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c. <\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"json\">{   \"coord\": {     \"lon\": -74.006,     \"lat\": 40.7143   },   \"weather\": [     {       \"id\": 501,       \"main\": \"Rain\",       \"description\": \"moderate rain\",       \"icon\": \"10d\"     }   ],   \"base\": \"stations\",   \"main\": {     \"temp\": 297.11,     \"feels_like\": 297.75,     \"temp_min\": 295.11,     \"temp_max\": 298.79,     \"pressure\": 1013,     \"humidity\": 84   },   \"visibility\": 10000,   \"wind\": {     \"speed\": 5.81,     \"deg\": 123,     \"gust\": 6.71   },   \"rain\": {     \"1h\": 2.83   },   \"clouds\": {     \"all\": 100   },   \"dt\": 1661183439,   \"sys\": {     \"type\": 2,     \"id\": 2039034,     \"country\": \"US\",     \"sunrise\": 1661163203,     \"sunset\": 1661211890   },   \"timezone\": -14400,   \"id\": 5128581,   \"name\": \"New York\",   \"cod\": 200 }<\/code><\/pre>\n<\/p>\n<\/p>\n<\/div>\n<\/details>\n<h2>\u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0431\u043e\u0442\u0430 \u0438 \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c \u0432\u0441\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0435<\/h2>\n<p>\u0421\u043e\u0437\u0434\u0430\u0439\u0442\u0435 Telegram \u0431\u043e\u0442\u0430 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <a href=\"https:\/\/web.telegram.org\/k\/#@BotFather\" rel=\"noopener noreferrer nofollow\">BotFather<\/a> \u0438 \u0432\u043e\u0437\u044c\u043c\u0438\u0442\u0435 \u0435\u0433\u043e \u0442\u043e\u043a\u0435\u043d.<\/p>\n<p>\u0418\u0437 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044f \u0432\u0438\u0434\u0435\u043e \u0432\u044b \u043c\u043e\u0433\u043b\u0438 \u0434\u043e\u0433\u0430\u0434\u0430\u0442\u044c\u0441\u044f, \u0447\u0442\u043e \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u044f\u0437\u044b\u043a Python \u0438 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 <a href=\"https:\/\/github.com\/aiogram\/aiogram\" rel=\"noopener noreferrer nofollow\">aiogram.<\/a> \u042f \u043d\u0430\u0434\u0435\u044e\u0441\u044c, \u0441 <a href=\"https:\/\/www.python.org\/downloads\/\" rel=\"noopener noreferrer nofollow\">\u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u043e\u0439 Python<\/a> \u0443 \u0432\u0430\u0441 \u043d\u0435 \u0432\u043e\u0437\u043d\u0438\u043a\u043d\u0435\u0442 \u043f\u0440\u043e\u0431\u043b\u0435\u043c. \u0421 aiogram \u0442\u043e\u0436\u0435.<\/p>\n<p><code>pip install aiogram<\/code><\/p>\n<h2>\u041b\u0438\u0440\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u043e\u0442\u0441\u0442\u0443\u043f\u043b\u0435\u043d\u0438\u0435<\/h2>\n<p>\u042f \u043c\u043d\u043e\u0433\u043e \u043f\u043e\u0437\u0430\u0438\u043c\u0441\u0442\u0432\u043e\u0432\u0430\u043b \u0443 <a href=\"https:\/\/github.com\/alexey-goloburdin\/weather\" rel=\"noopener noreferrer nofollow\">\u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/a> \u0410\u043b\u0435\u043a\u0441\u0435\u044f \u0413\u043e\u043b\u043e\u0431\u0443\u0440\u0434\u0438\u043d\u0430 &#8212; \u0430\u0432\u0442\u043e\u0440\u0430 YouTube \u043a\u0430\u043d\u0430\u043b\u0430 <a href=\"https:\/\/www.youtube.com\/c\/%D0%94%D0%B8%D0%B4%D0%B6%D0%B8%D1%82%D0%B0%D0%BB%D0%B8%D0%B7%D0%B8%D1%80%D1%83%D0%B9\" rel=\"noopener noreferrer nofollow\">&#171;\u0414\u0438\u0434\u0436\u0438\u0442\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0439!&#187;<\/a> \u041f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e \u0435\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442 \u043f\u0440\u0435\u0434\u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f macOS \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b \u0431\u0435\u0440\u0443\u0442\u0441\u044f \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0430 \u043a\u043e\u043c\u0430\u043d\u0434\u043d\u043e\u0439 \u0441\u0442\u0440\u043e\u043a\u0438 <a href=\"https:\/\/github.com\/robmathers\/WhereAmI\" rel=\"noopener noreferrer nofollow\">whereami.<\/a> \u041f\u0440\u0438\u043c\u0435\u0440 \u0432\u044b\u0432\u043e\u0434\u0430:<\/p>\n<pre><code>Latitude: 45.424807,  Longitude: -75.699234 Accuracy (m): 65.000000 Timestamp: 2019-09-28, 12:40:20 PM EDT<\/code><\/pre>\n<p>\u0422\u0430\u043a\u0436\u0435 \u0435\u0433\u043e \u0441\u043a\u0440\u0438\u043f\u0442 \u043f\u0440\u043e\u0441\u0442\u043e \u0432\u044b\u0432\u043e\u0434\u0438\u0442 \u0432\u0441\u044e \u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u0443\u044e \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u0432 \u0442\u0435\u0440\u043c\u0438\u043d\u0430\u043b, \u0445\u043e\u0442\u0435\u043b\u043e\u0441\u044c \u0431\u044b \u0438\u043c\u0435\u0442\u044c \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u043f\u043e\u043f\u0440\u0438\u044f\u0442\u043d\u0435\u0435 \u0438 \u0443\u0434\u043e\u0431\u043d\u0435\u0435.<\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/e81\/6a7\/c6e\/e816a7c6edf646684dcafa1dfac87577.png\" alt=\"\" title=\"\" width=\"495\" height=\"306\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/e81\/6a7\/c6e\/e816a7c6edf646684dcafa1dfac87577.png\"\/><figcaption><\/figcaption><\/figure>\n<p>\u042f \u0440\u0435\u0448\u0438\u043b, \u0447\u0442\u043e \u043c\u043e\u0436\u043d\u043e \u0434\u043e\u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0438\u0434\u0435\u044e \u0438 \u043e\u0445\u0432\u0430\u0442\u0438\u0442\u044c \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439, \u043f\u0440\u0435\u043d\u0435\u0431\u0440\u0435\u0433\u0430\u044f \u0442\u043e\u0447\u043d\u043e\u0441\u0442\u044c\u044e \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438.<\/p>\n<h2>\u041f\u0438\u0448\u0435\u043c \u043a\u043e\u0434. \u0424\u0430\u0439\u043b \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438<\/h2>\n<p>\u0418\u0442\u0430\u043a, \u0444\u0430\u0439\u043b <code>config.py<\/code> \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u043a\u043e\u043d\u0441\u0442\u0430\u043d\u0442\u044b:<\/p>\n<ul>\n<li>\n<p>\u0422\u043e\u043a\u0435\u043d \u0431\u043e\u0442\u0430 <code>BOT_API_TOKEN<\/code><\/p>\n<\/li>\n<li>\n<p>\u041a\u043b\u044e\u0447 OpenWeather <code>WEATHER_API_KEY<\/code><\/p>\n<\/li>\n<li>\n<p>\u0417\u0430\u043f\u0440\u043e\u0441 \u0442\u0435\u043a\u0443\u0449\u0435\u0439 \u043f\u043e\u0433\u043e\u0434\u044b <code>CURRENT_WEATHER_API_CALL<\/code><\/p>\n<\/li>\n<\/ul>\n<details class=\"spoiler\">\n<summary>config.py<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">BOT_API_TOKEN = '' WEATHER_API_KEY = ''  CURRENT_WEATHER_API_CALL = (         'https:\/\/api.openweathermap.org\/data\/2.5\/weather?'         'lat={latitude}&amp;lon={longitude}&amp;'         'appid=' + WEATHER_API_KEY + '&amp;units=metric' )<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u041a\u043e\u043d\u0435\u0447\u043d\u043e, \u0442\u0430\u043a\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0435, \u043a\u0430\u043a \u0442\u043e\u043a\u0435\u043d\u044b \u0438 \u043a\u043b\u044e\u0447\u0438 \u043d\u0443\u0436\u043d\u043e \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0432 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f, \u043d\u043e \u044d\u0442\u043e \u043f\u0435\u0442-\u043f\u0440\u043e\u0435\u043a\u0442, \u0434\u0435\u043f\u043b\u043e\u0438\u0442\u044c \u044f \u0435\u0433\u043e \u043d\u0435 \u0431\u0443\u0434\u0443, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043e\u0441\u043e\u0431\u043e \u043d\u0435 \u0437\u0430\u043c\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u044e\u0441\u044c.<\/p>\n<h2>\u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b<\/h2>\n<p>\u0414\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442 \u044f \u0441\u043e\u0437\u0434\u0430\u043b \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u043c\u043e\u0434\u0443\u043b\u044c. \u0414\u0430\u0442\u0430\u043a\u043b\u0430\u0441\u0441 <code>Coordinates<\/code> \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0448\u0438\u0440\u043e\u0442\u0443 \u0438 \u0434\u043e\u043b\u0433\u043e\u0442\u0443 \u0441 \u0442\u0438\u043f\u0430\u043c\u0438 float.<\/p>\n<pre><code class=\"python\">from dataclasses import dataclass  @dataclass(slots=True, frozen=True) class Coordinates:     latitude: float     longitude: float<\/code><\/pre>\n<p>\u041f\u043e IP \u0430\u0434\u0440\u0435\u0441\u0443 \u0438\u0445 \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0439\u0442\u0438 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <a href=\"https:\/\/ipinfo.io\/json\" rel=\"noopener noreferrer nofollow\">ipinfo.io\/json<\/a>. \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u0442\u0441\u044f \u0432\u043e\u0442 \u0442\u0430\u043a\u043e\u0439 \u043e\u0442\u0432\u0435\u0442. <\/p>\n<pre><code class=\"json\">{   \"ip\": \"228.228.228.228\",   \"city\": \"Moscow\",   \"region\": \"Moscow\",   \"country\": \"RU\",   \"loc\": \"55.7522,37.6156\",   \"org\": \"Starlink\",   \"postal\": \"101000\",   \"timezone\": \"Europe\/Moscow\",   \"readme\": \"https:\/\/ipinfo.io\/missingauth\" }<\/code><\/pre>\n<p>\u041d\u0430\u0441 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u0443\u0435\u0442 \u043a\u043b\u044e\u0447 <code>\"loc\"<\/code> \u0441\u043e\u043a\u0440\u0430\u0449\u0435\u043d\u043d\u043e \u043e\u0442 location. \u041e\u043f\u044f\u0442\u044c \u043a\u0430\u043f\u0438\u0442\u0430\u043d \u043e\u0447\u0435\u0432\u0438\u0434\u043d\u043e\u0441\u0442\u044c. \u0414\u0435\u043b\u0430\u0435\u043c \u0437\u0430\u043f\u0440\u043e\u0441 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u0438 <code>urlopen<\/code> \u043c\u043e\u0434\u0443\u043b\u044f <code>request<\/code> \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 <code>urllib.<\/code> \u0412\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u0441\u043b\u043e\u0432\u0430\u0440\u044c \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <code>json.load()<\/code> <\/p>\n<pre><code class=\"python\">from urllib.request import urlopen import json  def _get_ip_data() -> dict:     url = 'http:\/\/ipinfo.io\/json'     response = urlopen(url)     return json.load(response)<\/code><\/pre>\n<p>\u0412 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442 \u043f\u0430\u0440\u0441\u0438\u043c \u044d\u0442\u043e\u0442 \u0441\u043b\u043e\u0432\u0430\u0440\u044c \u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u0434\u0430\u0442\u0430\u043a\u043b\u0430\u0441\u0441 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442.<\/p>\n<pre><code class=\"python\">def get_coordinates() -> Coordinates:     \"\"\"Returns current coordinates using IP address\"\"\"     data = _get_ip_data()     latitude = data['loc'].split(',')[0]     longitude = data['loc'].split(',')[1]      return Coordinates(latitude=latitude, longitude=longitude)<\/code><\/pre>\n<details class=\"spoiler\">\n<summary>coordinates.py<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">from urllib.request import urlopen from dataclasses import dataclass import json   @dataclass(slots=True, frozen=True) class Coordinates:     latitude: float     longitude: float   def get_coordinates() -> Coordinates:     \"\"\"Returns current coordinates using IP address\"\"\"     data = _get_ip_data()     latitude = data['loc'].split(',')[0]     longitude = data['loc'].split(',')[1]      return Coordinates(latitude=latitude, longitude=longitude)   def _get_ip_data() -> dict:     url = 'http:\/\/ipinfo.io\/json'     response = urlopen(url)     return json.load(response)<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<h2>\u041f\u0430\u0440\u0441\u0438\u043c \u043e\u0442\u0432\u0435\u0442 OpenWeather API<\/h2>\n<p>\u0414\u0430\u043b\u0435\u0435 \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043c\u043e\u0434\u0443\u043b\u044c <code>api_service.<\/code> \u0412 \u043d\u0435\u043c \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u0432\u0441\u044f \u0441\u0443\u0435\u0442\u0430 \u0441 \u043f\u043e\u0433\u043e\u0434\u043e\u0439. \u0422\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u0430 \u0438\u0437\u043c\u0435\u0440\u044f\u0435\u0442\u0441\u044f \u0432 \u0433\u0440\u0430\u0434\u0443\u0441\u0430\u0445 \u0426\u0435\u043b\u044c\u0441\u0438\u044f, \u0447\u0435\u043c\u0443 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u043f\u0441\u0435\u0432\u0434\u043e\u043d\u0438\u043c <code>float<\/code> \u0447\u0438\u0441\u043b\u0430.<\/p>\n<pre><code class=\"python\">from typing import TypeAlias  Celsius: TypeAlias = float<\/code><\/pre>\n<p>\u041a\u0430\u043a \u0438\u0437\u0432\u0435\u0441\u0442\u043d\u043e, \u0433\u0440\u0430\u0434\u0443\u0441\u044b \u0424\u0430\u0440\u0435\u043d\u0433\u0435\u0439\u0442\u0430 \u0431\u044b\u043b\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u044b \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u0420\u044d\u0439 \u0411\u0440\u044d\u0434\u0431\u0435\u0440\u0438 \u0441\u043c\u043e\u0433 \u043a\u0440\u0430\u0441\u0438\u0432\u043e \u043d\u0430\u0437\u0432\u0430\u0442\u044c \u0441\u0432\u043e\u044e \u0430\u043d\u0442\u0438\u0443\u0442\u043e\u043f\u0438\u044e.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/424\/49f\/11b\/42449f11b9961968e6554f4bc265850f.jpg\" width=\"840\" height=\"1249\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/424\/49f\/11b\/42449f11b9961968e6554f4bc265850f.jpg\" data-blurred=\"true\"\/><figcaption><\/figcaption><\/figure>\n<p>\u0412 \u043e\u0442\u0432\u0435\u0442\u0435 API \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0432\u0435\u0442\u0440\u0430 \u0434\u0430\u0435\u0442\u0441\u044f \u0432 \u0433\u0440\u0430\u0434\u0443\u0441\u0430\u0445. \u042f \u0440\u0435\u0448\u0438\u043b \u043f\u0440\u0438\u0432\u0435\u0441\u0442\u0438 \u0438\u0445 \u0432 \u0431\u043e\u043b\u0435\u0435 \u0443\u0434\u043e\u0431\u043d\u044b\u0439 \u0444\u043e\u0440\u043c\u0430\u0442. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u044f \u0441\u043e\u0437\u0434\u0430\u043b \u043f\u0435\u0440\u0435\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u0435 \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0445 \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0439 \u0432\u0435\u0442\u0440\u0430. <\/p>\n<pre><code class=\"python\">from enum import IntEnum  class WindDirection(IntEnum):     North = 0     Northeast = 45     East = 90     Southeast = 135     South = 180     Southwest = 225     West = 270     Northwest = 315<\/code><\/pre>\n<p>\u0412 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u043f\u0430\u0440\u0441\u0438\u043d\u0433\u0430 \u043e\u043a\u0440\u0443\u0433\u043b\u0435\u043d\u0438\u0435 \u043f\u043e 45 \u0433\u0440\u0430\u0434\u0443\u0441\u043e\u0432 \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0442\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c: \u0434\u0435\u043b\u0438\u043c \u0433\u0440\u0430\u0434\u0443\u0441\u044b \u043d\u0430 45, \u043e\u043a\u0440\u0443\u0433\u043b\u044f\u0435\u043c \u0438 \u0443\u043c\u043d\u043e\u0436\u0430\u0435\u043c \u043e\u0431\u0440\u0430\u0442\u043d\u043e \u043d\u0430 45. \u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u043c\u043e\u0436\u0435\u0442 \u043e\u043a\u0440\u0443\u0433\u043b\u0438\u0442\u044c\u0441\u044f \u0434\u043e 360 \u0433\u0440\u0430\u0434\u0443\u0441\u043e\u0432, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u043c \u044d\u0442\u043e\u0442 \u0441\u043b\u0443\u0447\u0430\u0439.<\/p>\n<pre><code class=\"python\">def _parse_wind_direction(openweather_dict: dict) -> str:     degrees = openweather_dict['wind']['deg']     degrees = round(degrees \/ 45) * 45     if degrees == 360:         degrees = 0     return WindDirection(degrees).name<\/code><\/pre>\n<p>\u0412\u0441\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u043e \u043f\u043e\u0433\u043e\u0434\u0435 \u0431\u0443\u0434\u0443\u0442 \u0445\u0440\u0430\u043d\u0438\u0442\u044c\u0441\u044f \u0432 \u0434\u0430\u0442\u0430\u043a\u043b\u0430\u0441\u0441\u0435. \u041f\u0440\u0438 \u0436\u0435\u043b\u0430\u043d\u0438\u0438 \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0441\u044e\u0434\u0430 \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u0443\u044e \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u0438\u0437 \u043e\u0442\u0432\u0435\u0442\u0430 OpenWeather, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 \u0430\u0442\u043c\u043e\u0441\u0444\u0435\u0440\u043d\u043e\u0435 \u0434\u0430\u0432\u043b\u0435\u043d\u0438\u0435, \u0447\u0430\u0441\u043e\u0432\u043e\u0439 \u043f\u043e\u044f\u0441, \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u0443\u044e \u0438 \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u0443\u044e \u0437\u0430\u0444\u0438\u043a\u0441\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u0443\u044e \u0432 \u0434\u0430\u043d\u043d\u044b\u0439 \u043c\u043e\u043c\u0435\u043d\u0442 \u0442\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u0443.<\/p>\n<pre><code class=\"python\">@dataclass(slots=True, frozen=True) class Weather:     location: str     temperature: Celsius     temperature_feeling: Celsius     description: str     wind_speed: float     wind_direction: str     sunrise: datetime     sunset: datetime<\/code><\/pre>\n<p>\u0412 \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u043e\u043c \u043d\u0438\u0447\u0435\u0433\u043e \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e\u0433\u043e \u0432 \u043c\u043e\u0434\u0443\u043b\u0435 \u043d\u0435 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442, \u043f\u0440\u043e\u0441\u0442\u043e \u043f\u0430\u0440\u0441\u0438\u043d\u0433 json.<\/p>\n<details class=\"spoiler\">\n<summary>api_service.py<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">from typing import Literal, TypeAlias from urllib.request import urlopen from dataclasses import dataclass from datetime import datetime from enum import IntEnum import json  from coordinates import Coordinates import config  Celsius: TypeAlias = float   class WindDirection(IntEnum):     North = 0     Northeast = 45     East = 90     Southeast = 135     South = 180     Southwest = 225     West = 270     Northwest = 315   @dataclass(slots=True, frozen=True) class Weather:     location: str     temperature: Celsius     temperature_feeling: Celsius     description: str     wind_speed: float     wind_direction: str     sunrise: datetime     sunset: datetime   def get_weather(coordinates=Coordinates) -> Weather:     \"\"\"Requests the weather in OpenWeather API and returns it\"\"\"     openweather_response = _get_openweather_response(         longitude=coordinates.longitude, latitude=coordinates.latitude     )     weather = _parse_openweather_response(openweather_response)     return weather   def _get_openweather_response(latitude: float, longitude: float) -> str:     url = config.CURRENT_WEATHER_API_CALL.format(latitude=latitude, longitude=longitude)     return urlopen(url).read()   def _parse_openweather_response(openweather_response: str) -> Weather:     openweather_dict = json.loads(openweather_response)     return Weather(         location=_parse_location(openweather_dict),         temperature=_parse_temperature(openweather_dict),         temperature_feeling=_parse_temperature_feeling(openweather_dict),         description=_parse_description(openweather_dict),         sunrise=_parse_sun_time(openweather_dict, 'sunrise'),         sunset=_parse_sun_time(openweather_dict, 'sunset'),         wind_speed=_parse_wind_speed(openweather_dict),         wind_direction=_parse_wind_direction(openweather_dict)     )   def _parse_location(openweather_dict: dict) -> str:     return openweather_dict['name']   def _parse_temperature(openweather_dict: dict) -> Celsius:     return openweather_dict['main']['temp']   def _parse_temperature_feeling(openweather_dict: dict) -> Celsius:     return openweather_dict['main']['feels_like']   def _parse_description(openweather_dict) -> str:     return str(openweather_dict['weather'][0]['description']).capitalize()   def _parse_sun_time(openweather_dict: dict, time: Literal[\"sunrise\", \"sunset\"]) -> datetime:     return datetime.fromtimestamp(openweather_dict['sys'][time])   def _parse_wind_speed(openweather_dict: dict) -> float:     return openweather_dict['wind']['speed']   def _parse_wind_direction(openweather_dict: dict) -> str:     degrees = openweather_dict['wind']['deg']     degrees = round(degrees \/ 45) * 45     if degrees == 360:         degrees = 0     return WindDirection(degrees).name<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<h2>\u0414\u0435\u043b\u0430\u0435\u043c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u0431\u043e\u0442\u0430<\/h2>\n<p>\u0412 \u043c\u043e\u0434\u0443\u043b\u0435 messages \u0441\u043e\u0431\u0440\u0430\u043d\u044b \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u0431\u043e\u0442\u0430 \u043f\u043e \u043a\u043e\u043c\u0430\u043d\u0434\u0430\u043c. \u0421\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u043e \u043f\u043e\u0433\u043e\u0434\u0435 <code>\/weather<\/code> \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u043b\u043e\u043a\u0430\u0446\u0438\u044e, \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u043f\u043e\u0433\u043e\u0434\u044b, \u0442\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u0443 \u0438 \u0435\u0435 \u043e\u0449\u0443\u0449\u0435\u043d\u0438\u0435. <\/p>\n<pre><code class=\"python\">from coordinates import get_coordinates from api_service import get_weather  def weather() -> str:     \"\"\"Returns a message about the temperature and weather description\"\"\"     wthr = get_weather(get_coordinates())     return f'{wthr.location}, {wthr.description}\\n' \\            f'Temperature is {wthr.temperature}\u00b0C, feels like {wthr.temperature_feeling}\u00b0C'<\/code><\/pre>\n<p>\u0421\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u043e \u0432\u0435\u0442\u0440\u0435 <code>\/wind<\/code> \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 \u0435\u0433\u043e \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0438 \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u044c \u0432 \u043c\u0435\u0442\u0440\u0430\u0445 \u0432 \u0441\u0435\u043a\u0443\u043d\u0434\u0443. <\/p>\n<pre><code class=\"python\">def wind() -> str:     \"\"\"Returns a message about wind direction and speed\"\"\"     wthr = get_weather(get_coordinates())     return f'{wthr.wind_direction} wind {wthr.wind_speed} m\/s'<\/code><\/pre>\n<p>\u041d\u0443 \u0438 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0432\u043e\u0441\u0445\u043e\u0434\u0430 \u0438 \u0437\u0430\u043a\u0430\u0442\u0430 \u0441\u043e\u043b\u043d\u0446\u0430 <code>\/sun_time<\/code>. \u0417\u0434\u0435\u0441\u044c datetime \u043e\u0431\u044a\u0435\u043a\u0442 \u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u0432 \u0447\u0430\u0441\u044b \u0438 \u043c\u0438\u043d\u0443\u0442\u044b, \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u043e\u0435 \u0432 \u0434\u0430\u043d\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043d\u0435\u0432\u0430\u0436\u043d\u043e. <\/p>\n<pre><code class=\"python\">def sun_time() -> str:     \"\"\"Returns a message about the time of sunrise and sunset\"\"\"     wthr = get_weather(get_coordinates())     return f'Sunrise: {wthr.sunrise.strftime(\"%H:%M\")}\\n' \\            f'Sunset: {wthr.sunset.strftime(\"%H:%M\")}\\n'<\/code><\/pre>\n<p>\u041d\u0443\u0436\u043d\u043e \u0437\u0430\u043c\u0435\u0442\u0438\u0442\u044c, \u0447\u0442\u043e \u043f\u0440\u0438 \u043a\u0430\u0436\u0434\u043e\u043c \u0432\u044b\u0437\u043e\u0432\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0441\u043e\u0437\u0434\u0430\u0435\u0442\u0441\u044f \u043d\u043e\u0432\u044b\u0439 API \u0437\u0430\u043f\u0440\u043e\u0441. \u041f\u043e\u0447\u0435\u043c\u0443 \u044d\u0442\u043e \u043d\u0443\u0436\u043d\u043e \u0437\u0430\u043c\u0435\u0442\u0438\u0442\u044c? <s>\u041f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0441\u043d\u0430\u0447\u0430\u043b\u0430 \u044f \u0441\u0434\u0435\u043b\u0430\u043b \u0431\u043e\u0442\u0430 \u0441 \u043e\u0434\u043d\u0438\u043c \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u043c \u0438 \u043d\u0435\u0434\u043e\u0443\u043c\u0435\u0432\u0430\u043b, \u043f\u043e\u0447\u0435\u043c\u0443 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u043d\u0435 \u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f \u0447\u0435\u0440\u0435\u0437 \u0432\u0440\u0435\u043c\u044f.<\/s> \u041f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0432 \u0438\u0434\u0435\u0430\u043b\u0435 \u0434\u0435\u043b\u0430\u0442\u044c \u043e\u0434\u0438\u043d \u0437\u0430\u043f\u0440\u043e\u0441 \u0432 5 \u0438\u043b\u0438 10 \u043c\u0438\u043d\u0443\u0442, \u0437\u0430 \u044d\u0442\u043e \u0432\u0440\u0435\u043c\u044f \u043f\u043e\u0433\u043e\u0434\u0430 \u043d\u0435 \u043e\u0441\u043e\u0431\u043e \u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f, \u0434\u0430 \u0438 \u0434\u0430\u043d\u043d\u044b\u0435 OpenWeather \u0442\u043e\u0436\u0435 \u043d\u0435 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u043a\u0430\u0436\u0434\u0443\u044e \u0441\u0435\u043a\u0443\u043d\u0434\u0443.<\/p>\n<details class=\"spoiler\">\n<summary>messages.py<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">from coordinates import get_coordinates from api_service import get_weather   def weather() -> str:     \"\"\"Returns a message about the temperature and weather description\"\"\"     wthr = get_weather(get_coordinates())     return f'{wthr.location}, {wthr.description}\\n' \\            f'Temperature is {wthr.temperature}\u00b0C, feels like {wthr.temperature_feeling}\u00b0C'   def wind() -> str:     \"\"\"Returns a message about wind direction and speed\"\"\"     wthr = get_weather(get_coordinates())     return f'{wthr.wind_direction} wind {wthr.wind_speed} m\/s'   def sun_time() -> str:     \"\"\"Returns a message about the time of sunrise and sunset\"\"\"     wthr = get_weather(get_coordinates())     return f'Sunrise: {wthr.sunrise.strftime(\"%H:%M\")}\\n' \\            f'Sunset: {wthr.sunset.strftime(\"%H:%M\")}\\n'<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<h2>Inline \u043a\u043b\u0430\u0432\u0438\u0430\u0442\u0443\u0440\u0430<\/h2>\n<p>\u041c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c reply \u043a\u043b\u0430\u0432\u0438\u0430\u0442\u0443\u0440\u0443, \u043d\u043e \u043c\u043d\u0435 \u0431\u043e\u043b\u044c\u0448\u0435 \u043f\u043e \u0434\u0443\u0448\u0435 Inline. 3 \u043a\u043d\u043e\u043f\u043a\u0438 \u0434\u043b\u044f 3 \u043a\u043e\u043c\u0430\u043d\u0434. <\/p>\n<pre><code class=\"python\">from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup  BTN_WEATHER = InlineKeyboardButton('Weather', callback_data='weather') BTN_WIND = InlineKeyboardButton('Wind', callback_data='wind') BTN_SUN_TIME = InlineKeyboardButton('Sunrise and sunset', callback_data='sun_time')<\/code><\/pre>\n<p>4 \u043a\u043b\u0430\u0432\u0438\u0430\u0442\u0443\u0440\u044b \u0434\u043b\u044f 4 \u043a\u043e\u043c\u0430\u043d\u0434, \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043a\u043e\u043c\u0430\u043d\u0434\u0430 \u043f\u043e\u043c\u043e\u0449\u0438. \u0412 \u0447\u0435\u043c \u0441\u0443\u0442\u044c? \u041f\u043e\u0441\u043b\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u043f\u043e\u0433\u043e\u0434\u044b \u043d\u0430\u043c \u043d\u0435 \u043d\u0443\u0436\u043d\u043e \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u0435\u0435 \u043a\u043d\u043e\u043f\u043a\u0443. \u0422\u0430\u043a\u0430\u044f \u0436\u0435 \u043b\u043e\u0433\u0438\u043a\u0430 \u0434\u043b\u044f \u0432\u0441\u0435\u0445 \u0434\u0440\u0443\u0433\u0438\u0445 \u043a\u043e\u043c\u0430\u043d\u0434, \u043a\u0440\u043e\u043c\u0435 \u043f\u043e\u043c\u043e\u0449\u0438. \u0414\u043b\u044f \u043d\u0435\u0435 \u0432\u044b\u0432\u043e\u0434\u044f\u0442\u0441\u044f \u043a\u043d\u043e\u043f\u043a\u0438 \u0432\u0441\u0435\u0445 3 \u043a\u043e\u043c\u0430\u043d\u0434.<\/p>\n<pre><code class=\"python\">WEATHER = InlineKeyboardMarkup().add(BTN_WIND, BTN_SUN_TIME) WIND = InlineKeyboardMarkup().add(BTN_WEATHER).add(BTN_SUN_TIME) SUN_TIME = InlineKeyboardMarkup().add(BTN_WEATHER, BTN_WIND) HELP = InlineKeyboardMarkup().add(BTN_WEATHER, BTN_WIND).add(BTN_SUN_TIME)<\/code><\/pre>\n<details class=\"spoiler\">\n<summary>inline_keyboard.py<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup  BTN_WEATHER = InlineKeyboardButton('Weather', callback_data='weather') BTN_WIND = InlineKeyboardButton('Wind', callback_data='wind') BTN_SUN_TIME = InlineKeyboardButton('Sunrise and sunset',                                      callback_data='sun_time')  WEATHER = InlineKeyboardMarkup().add(BTN_WIND, BTN_SUN_TIME) WIND = InlineKeyboardMarkup().add(BTN_WEATHER).add(BTN_SUN_TIME) SUN_TIME = InlineKeyboardMarkup().add(BTN_WEATHER, BTN_WIND) HELP = InlineKeyboardMarkup().add(BTN_WEATHER, BTN_WIND).add(BTN_SUN_TIME)<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<h2>\u0413\u043b\u0430\u0432\u043d\u044b\u0439 \u043c\u043e\u0434\u0443\u043b\u044c \u0431\u043e\u0442\u0430<\/h2>\n<p>\u041d\u0443 \u0438 \u0432 \u0433\u043b\u0430\u0432\u043d\u043e\u043c \u043c\u043e\u0434\u0443\u043b\u0435 \u0431\u043e\u0442\u0430 \u043f\u0440\u0438\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u0430\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430, \u0445\u044d\u043d\u0434\u043b\u0435\u0440\u044b \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 \u0438 \u043a\u043e\u043b\u043b\u0431\u044d\u043a\u043e\u0432 \u0434\u043b\u044f inline \u043a\u043d\u043e\u043f\u043e\u043a, \u043d\u0438\u0447\u0435\u0433\u043e \u0441\u0432\u0435\u0440\u0445\u044a\u0435\u0441\u0442\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0433\u043e.<\/p>\n<p>\u041d\u0443\u0436\u043d\u043e \u0445\u043e\u0442\u044c \u0447\u0442\u043e-\u043d\u0438\u0431\u0443\u0434\u044c \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u0430\u0442\u044c. \u041f\u043e\u0434 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u043e\u0439 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u043e\u0439 aiogram \u043f\u043e\u0434\u0440\u0430\u0437\u0443\u043c\u0435\u0432\u0430\u0435\u0442\u0441\u044f \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u0431\u043b\u043e\u043a \u043a\u043e\u0434\u0430:<\/p>\n<pre><code class=\"python\">import logging  from aiogram import Bot, Dispatcher, executor, types  import config  logging.basicConfig(level=logging.INFO)  bot = Bot(token=config.BOT_API_TOKEN) dp = Dispatcher(bot)<\/code><\/pre>\n<p>\u0425\u044d\u043d\u0434\u043b\u0435\u0440 \u0434\u043b\u044f \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 <code>\/start<\/code> \u0438 <code>\/weather<\/code> \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c. \u0412\u0441\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043c\u0430\u0433\u0438\u0438 \u0434\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440\u043e\u0432 aiogram.<\/p>\n<pre><code class=\"python\">@dp.message_handler(commands=['start', 'weather']) async def show_weather(message: types.Message):     await message.answer(text=messages.weather(),                          reply_markup=inline_keyboard.WEATHER)<\/code><\/pre>\n<p>\u0425\u044d\u043d\u0434\u043b\u0435\u0440 \u043a\u043e\u043b\u043b\u0431\u044d\u043a\u0430 \u0434\u043b\u044f \u0438\u043d\u043b\u0430\u0439\u043d-\u043a\u043d\u043e\u043f\u043a\u0438 \u043f\u043e\u0433\u043e\u0434\u044b:<\/p>\n<pre><code class=\"python\">@dp.callback_query_handler(text='weather') async def process_callback_weather(callback_query: types.CallbackQuery):     await bot.answer_callback_query(callback_query.id)     await bot.send_message(         callback_query.from_user.id,         text=messages.weather(),         reply_markup=inline_keyboard.WEATHER)<\/code><\/pre>\n<p>\u0417\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u0441\u043a\u0440\u0438\u043f\u0442 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0442\u0430\u043a\u043e\u0439 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u0438:<\/p>\n<pre><code class=\"python\">if __name__ == '__main__':     executor.start_polling(dp, skip_updates=True)<\/code><\/pre>\n<details class=\"spoiler\">\n<summary>bot.py<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">import logging  from aiogram import Bot, Dispatcher, executor, types  import inline_keyboard import messages import config  logging.basicConfig(level=logging.INFO)  bot = Bot(token=config.BOT_API_TOKEN) dp = Dispatcher(bot)   @dp.message_handler(commands=['start', 'weather']) async def show_weather(message: types.Message):     await message.answer(text=messages.weather(),                          reply_markup=inline_keyboard.WEATHER)   @dp.message_handler(commands='help') async def show_help_message(message: types.Message):     await message.answer(         text=f'This bot can get the current weather from your IP address.',         reply_markup=inline_keyboard.HELP)   @dp.message_handler(commands='wind') async def show_wind(message: types.Message):     await message.answer(text=messages.wind(),                           reply_markup=inline_keyboard.WIND)   @dp.message_handler(commands='sun_time') async def show_sun_time(message: types.Message):     await message.answer(text=messages.sun_time(),                           reply_markup=inline_keyboard.SUN_TIME)   @dp.callback_query_handler(text='weather') async def process_callback_weather(callback_query: types.CallbackQuery):     await bot.answer_callback_query(callback_query.id)     await bot.send_message(         callback_query.from_user.id,         text=messages.weather(),         reply_markup=inline_keyboard.WEATHER     )   @dp.callback_query_handler(text='wind') async def process_callback_wind(callback_query: types.CallbackQuery):     await bot.answer_callback_query(callback_query.id)     await bot.send_message(         callback_query.from_user.id,         text=messages.wind(),         reply_markup=inline_keyboard.WIND     )   @dp.callback_query_handler(text='sun_time') async def process_callback_sun_time(callback_query: types.CallbackQuery):     await bot.answer_callback_query(callback_query.id)     await bot.send_message(         callback_query.from_user.id,         text=messages.sun_time(),         reply_markup=inline_keyboard.SUN_TIME     )   if __name__ == '__main__':     executor.start_polling(dp, skip_updates=True)<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<h2>\u0417\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u0431\u043e\u0442\u0430<\/h2>\n<p>\u0421\u043c\u043e\u0442\u0440\u0438\u043c \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435, \u0432\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u0443\u0432\u0438\u0434\u0435\u0442\u044c 3 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f:<\/p>\n<pre><code>INFO:aiogram:Bot: superultramegaweatherbot [@superultramegaweatherbot] WARNING:aiogram:Updates were skipped successfully. INFO:aiogram.dispatcher.dispatcher:Start polling.<\/code><\/pre>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/398\/c7e\/25e\/398c7e25e7e7d2e66a85082f2f6e9690.png\" width=\"738\" height=\"465\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/398\/c7e\/25e\/398c7e25e7e7d2e66a85082f2f6e9690.png\"\/><figcaption><\/figcaption><\/figure>\n<p>\u041f\u043e\u043a\u0430 \u0447\u0442\u043e \u0432\u0441\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442, \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043f\u043e IP \u0438\u0437 \u0413\u0435\u0440\u043c\u0430\u043d\u0438\u0438.<\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/45a\/662\/0aa\/45a6620aafb234bbbe5eccbacf31590e.png\" width=\"367\" height=\"340\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/45a\/662\/0aa\/45a6620aafb234bbbe5eccbacf31590e.png\"\/><figcaption><\/figcaption><\/figure>\n<p>\u0411\u044b\u0432\u0430\u044e\u0442 \u0442\u0430\u043a\u0438\u0435 \u0441\u043b\u0443\u0447\u0430\u0438, \u043a\u043e\u0433\u0434\u0430 \u0437\u0430\u043f\u0440\u043e\u0441 \u0434\u043e\u043b\u0433\u043e \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442\u0441\u044f. \u042f \u043d\u0435 \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u043b \u043e\u0448\u0438\u0431\u043a\u0438 \u0438 \u043d\u0435 \u0434\u0435\u043b\u0430\u043b \u0434\u043b\u044f \u043d\u0438\u0445 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439, \u0431\u043e\u0442 \u043f\u0440\u043e\u0441\u0442\u043e \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u0434\u0435\u043b\u0430\u0435\u0442 \u0432 \u0442\u0430\u043a\u0438\u0445 \u0441\u043b\u0443\u0447\u0430\u044f\u0445. \u042f \u043f\u043e\u0441\u0447\u0438\u0442\u0430\u043b, \u0447\u0442\u043e \u0443\u0436\u0435 \u0438 \u0442\u0430\u043a \u0445\u043e\u0440\u043e\u0448\u043e. \u041a\u0430\u043a \u0433\u043e\u0432\u043e\u0440\u0438\u0442\u0441\u044f:<\/p>\n<ul>\n<li>\n<p>\u041b\u0443\u0447\u0448\u0435\u0435 &#8212; \u0432\u0440\u0430\u0433 \u0445\u043e\u0440\u043e\u0448\u0435\u0433\u043e<\/p>\n<\/li>\n<li>\n<p>\u0420\u0430\u0431\u043e\u0442\u0430\u0435\u0442 &#8212; \u043d\u0435 \u0442\u0440\u043e\u0433\u0430\u0439<\/p>\n<\/li>\n<li>\n<p>\u0415\u0449\u0435 \u0441\u043e\u0442\u043d\u044f \u0444\u0440\u0430\u0437 \u0434\u043b\u044f \u043e\u043f\u0440\u0430\u0432\u0434\u0430\u043d\u0438\u044f \u043b\u0435\u043d\u0438<\/p>\n<\/li>\n<li>\n<p>\u0415\u0449\u0435 \u0442\u044b\u0441\u044f\u0447\u0430 \u0443\u0441\u043f\u043e\u043a\u0430\u0438\u0432\u0430\u044e\u0449\u0438\u0445 \u0444\u0440\u0430\u0437 \u0434\u043b\u044f \u043f\u0435\u0440\u0444\u0435\u043a\u0446\u0438\u043e\u043d\u0438\u0441\u0442\u043e\u0432<\/p>\n<\/li>\n<\/ul>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/412\/d53\/20f\/412d5320f7e6f24ec27398a62c5fe38c.jpg\" width=\"1024\" height=\"768\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/412\/d53\/20f\/412d5320f7e6f24ec27398a62c5fe38c.jpg\" data-blurred=\"true\"\/><figcaption><\/figcaption><\/figure>\n<p>\u0422\u0430\u043a\u0436\u0435 \u043c\u043e\u0436\u043d\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442 \u0447\u0435\u0440\u0435\u0437 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0433\u0435\u043e\u043b\u043e\u043a\u0430\u0446\u0438\u0438 \u0431\u043e\u0442\u0443, \u0442\u043e\u0433\u0434\u0430 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u0441\u044f \u0432 \u0440\u0430\u0437\u044b \u0442\u043e\u0447\u043d\u0435\u0435.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/cfd\/3f9\/ce0\/cfd3f9ce03dd0eb1f8638e42e06b5f8b.png\" alt=\"https:\/\/web.telegram.org\/k\/#@WeathersBot\" title=\"https:\/\/web.telegram.org\/k\/#@WeathersBot\" width=\"655\" height=\"250\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/cfd\/3f9\/ce0\/cfd3f9ce03dd0eb1f8638e42e06b5f8b.png\"\/><figcaption>https:\/\/web.telegram.org\/k\/#@WeathersBot<\/figcaption><\/figure>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/9f0\/469\/f40\/9f0469f401f67576c1f8366bbe2d4e77.jpeg\" width=\"640\" height=\"480\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/9f0\/469\/f40\/9f0469f401f67576c1f8366bbe2d4e77.jpeg\" data-blurred=\"true\"\/><figcaption><\/figcaption><\/figure>\n<p>\u0428\u0442\u043e\u0448. \u0421\u043f\u0430\u0441\u0438\u0431\u043e \u0437\u0430 \u043f\u0440\u043e\u0447\u0442\u0435\u043d\u0438\u0435. \u041d\u0430\u0434\u0435\u044e\u0441\u044c \u043d\u0430 \u043e\u0442\u0437\u044b\u0432\u044b, \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0438 \u0438 \u043a\u0440\u0438\u0442\u0438\u043a\u0443.<\/p>\n<hr\/>\n<div class=\"tm-iframe_temp\" data-src=\"https:\/\/embedd.srv.habr.com\/iframe\/6303b6ba3a1c1c33c7856ceb\" data-style=\"\" id=\"6303b6ba3a1c1c33c7856ceb\" width=\"\"><\/div>\n<p><a href=\"https:\/\/github.com\/lesskop\/shtosh-weather-bot\" rel=\"noopener noreferrer nofollow\">GitHub \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439 shtosh-weather-bot<\/a><\/p>\n<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"v-portal\" style=\"display:none;\"><\/div>\n<\/div>\n<p> <!----> <!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/post\/684038\/\"> https:\/\/habr.com\/ru\/post\/684038\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<p>\u0428\u0442\u043e\u0448. \u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443 \u0432\u0430\u043c, \u043a\u0430\u043a \u0441\u043e\u0437\u0434\u0430\u0442\u044c Telegram \u0431\u043e\u0442\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 \u0442\u0435\u043a\u0443\u0449\u0443\u044e \u043f\u043e\u0433\u043e\u0434\u0443 \u043f\u043e IP \u0430\u0434\u0440\u0435\u0441\u0443. \u041c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u044f\u0437\u044b\u043a Python \u0438 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u0443\u044e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 \u0434\u043b\u044f \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0441 Telegram Bot API &#8212; aiogram.<\/p>\n<p>\u0418\u0442\u0430\u043a, \u043a\u0430\u043a \u0436\u0435 \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0442\u0430\u043a\u043e\u0433\u043e \u0431\u043e\u0442\u0430? <\/p>\n<details class=\"spoiler\">\n<summary>TL;DR<\/summary>\n<div class=\"spoiler__content\">\n<p>\u0421\u043a\u043b\u043e\u043d\u0438\u0440\u0443\u0439\u0442\u0435 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439 <a href=\"https:\/\/github.com\/lesskop\/shtosh-weather-bot\" rel=\"noopener noreferrer nofollow\">shtosh-weather-bot<\/a> \u0438 \u043f\u0440\u043e\u0439\u0434\u0438\u0442\u0435 \u043f\u043e \u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u0438 \u0432 README.<\/p>\n<\/div>\n<\/details>\n<h2>\u0412\u044b\u0431\u0438\u0440\u0430\u0435\u043c \u043f\u043e\u0433\u043e\u0434\u043d\u044b\u0439 \u0441\u0435\u0440\u0432\u0438\u0441 \u0441 \u0431\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u044b\u043c API <\/h2>\n<p>\u0414\u0430\u043d\u043d\u044b\u0435 \u043e \u0442\u0435\u043a\u0443\u0449\u0435\u0439 \u043f\u043e\u0433\u043e\u0434\u0435 \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u043e\u0442\u043a\u0443\u0434\u0430-\u0442\u043e \u0431\u0440\u0430\u0442\u044c. \u0415\u0449\u0435 \u0436\u0435\u043b\u0430\u0442\u0435\u043b\u044c\u043d\u043e, \u0447\u0442\u043e\u0431\u044b \u044d\u0442\u043e \u0431\u044b\u043b\u043e \u0431\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u043e. \u0423 \u0441\u0430\u0439\u0442\u0430 <a href=\"https:\/\/github.com\/public-apis\/public-apis\" rel=\"noopener noreferrer nofollow\">OpenWeatherMap<\/a> \u0435\u0441\u0442\u044c \u043d\u0443\u0436\u043d\u044b\u0439 \u043d\u0430\u043c <a href=\"https:\/\/openweathermap.org\/current\" rel=\"noopener noreferrer nofollow\">API \u0442\u0435\u043a\u0443\u0449\u0435\u0439 \u043f\u043e\u0433\u043e\u0434\u044b.<\/a> \u0411\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u043e \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0441\u044b\u043b\u0430\u0442\u044c 1000 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u0432 \u0434\u0435\u043d\u044c.<\/p>\n<p><code>https:\/\/api.openweathermap.org\/data\/2.5\/weather?lat={lat}&amp;lon={lon}&amp;appid={API key}<\/code>  <\/p>\n<p>\u041a\u0441\u0442\u0430\u0442\u0438, \u0435\u0441\u043b\u0438 \u0432\u044b \u0438\u0449\u0435\u0442\u0435 \u043a\u0430\u043a\u043e\u0439-\u0442\u043e application user interface \u0434\u043b\u044f \u0441\u0432\u043e\u0435\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430, \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u044e \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439 <a href=\"https:\/\/github.com\/public-apis\/public-apis\" rel=\"noopener noreferrer nofollow\">public-apis.<\/a><\/p>\n<p>\u0418\u0442\u0430\u043a, \u0434\u043b\u044f \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u043d\u0443\u0436\u043d\u044b \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b \u0438 \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0439 \u043a\u043b\u044e\u0447, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c, \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u0432 \u0430\u043a\u043a\u0430\u0443\u043d\u0442. \u041d\u0443 \u044d\u0442\u043e \u0432\u043e\u043e\u0431\u0449\u0435 \u043d\u0435 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430, \u043c\u043e\u0436\u043d\u043e \u0437\u0430\u0440\u0435\u0433\u0430\u0442\u044c \u043d\u0430 <a href=\"https:\/\/tempail.com\/\" rel=\"noopener noreferrer nofollow\">\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e \u043f\u043e\u0447\u0442\u0443.<\/a> \u041a\u043e\u043d\u0435\u0447\u043d\u043e, \u0435\u0441\u043b\u0438 \u0432\u044b \u0441\u043e\u0431\u0438\u0440\u0430\u0435\u0442\u0435\u0441\u044c \u0441\u0435\u0440\u044c\u0435\u0437\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c API \u0438 \u043f\u043e\u043a\u0443\u043f\u0430\u0442\u044c \u0431\u043e\u043b\u044c\u0448\u0435 1000 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u0432 \u0434\u0435\u043d\u044c, \u043b\u0443\u0447\u0448\u0435 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0430\u043a\u043a\u0430\u0443\u043d\u0442 \u043d\u0430 \u0441\u0432\u043e\u044e \u043f\u043e\u0447\u0442\u0443. \u041a\u0430\u043f\u0438\u0442\u0430\u043d \u043e\u0447\u0435\u0432\u0438\u0434\u043d\u043e\u0441\u0442\u044c.<\/p>\n<p>\u0417\u0430\u0445\u043e\u0434\u0438\u043c \u0432 My API keys \u0438 \u0432\u0438\u0434\u0438\u043c \u0437\u0434\u0435\u0441\u044c \u0442\u043e\u0442 \u0441\u0430\u043c\u044b\u0439 \u043a\u043b\u044e\u0447. \u041c\u043e\u0436\u0435\u0442\u0435 \u0432\u0437\u044f\u0442\u044c \u043c\u043e\u0439, \u043c\u043d\u0435 \u043d\u0435 \u0436\u0430\u043b\u043a\u043e.<\/p>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<p>\u0418\u0442\u0430\u043a, \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u0441\u0444\u043e\u0440\u043c\u0438\u0440\u0443\u0435\u043c \u0437\u0430\u043f\u0440\u043e\u0441. \u042f \u0432\u044b\u0431\u0440\u0430\u043b \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b \u041d\u044c\u044e-\u0419\u043e\u0440\u043a\u0430, \u043f\u0440\u043e\u0441\u0442\u043e \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0445\u043e\u0447\u0443 \u0438 \u043c\u043e\u0433\u0443. <\/p>\n<p><a href=\"https:\/\/api.openweathermap.org\/data\/2.5\/weather?lat=40,7143&amp;lon=-74,006&amp;appid=8537d9ef6386cb97156fd47d832f479c\" rel=\"noopener noreferrer nofollow\">https:\/\/api.openweathermap.org\/data\/2.5\/weather?lat=40,7143&amp;lon=-74,006&amp;appid=8537d9ef6386cb97156fd47d832f479c<\/a><\/p>\n<details class=\"spoiler\">\n<summary>\u0412\u043e\u0442 \u0442\u0430\u043a\u043e\u0439 json \u043c\u044b \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c. <\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"json\">{   \"coord\": {     \"lon\": -74.006,     \"lat\": 40.7143   },   \"weather\": [     {       \"id\": 501,       \"main\": \"Rain\",       \"description\": \"moderate rain\",       \"icon\": \"10d\"     }   ],   \"base\": \"stations\",   \"main\": {     \"temp\": 297.11,     \"feels_like\": 297.75,     \"temp_min\": 295.11,     \"temp_max\": 298.79,     \"pressure\": 1013,     \"humidity\": 84   },   \"visibility\": 10000,   \"wind\": {     \"speed\": 5.81,     \"deg\": 123,     \"gust\": 6.71   },   \"rain\": {     \"1h\": 2.83   },   \"clouds\": {     \"all\": 100   },   \"dt\": 1661183439,   \"sys\": {     \"type\": 2,     \"id\": 2039034,     \"country\": \"US\",     \"sunrise\": 1661163203,     \"sunset\": 1661211890   },   \"timezone\": -14400,   \"id\": 5128581,   \"name\": \"New York\",   \"cod\": 200 }<\/code><\/pre>\n<\/p>\n<\/p>\n<\/div>\n<\/details>\n<h2>\u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0431\u043e\u0442\u0430 \u0438 \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c \u0432\u0441\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0435<\/h2>\n<p>\u0421\u043e\u0437\u0434\u0430\u0439\u0442\u0435 Telegram \u0431\u043e\u0442\u0430 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <a href=\"https:\/\/web.telegram.org\/k\/#@BotFather\" rel=\"noopener noreferrer nofollow\">BotFather<\/a> \u0438 \u0432\u043e\u0437\u044c\u043c\u0438\u0442\u0435 \u0435\u0433\u043e \u0442\u043e\u043a\u0435\u043d.<\/p>\n<p>\u0418\u0437 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044f \u0432\u0438\u0434\u0435\u043e \u0432\u044b \u043c\u043e\u0433\u043b\u0438 \u0434\u043e\u0433\u0430\u0434\u0430\u0442\u044c\u0441\u044f, \u0447\u0442\u043e \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u044f\u0437\u044b\u043a Python \u0438 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 <a href=\"https:\/\/github.com\/aiogram\/aiogram\" rel=\"noopener noreferrer nofollow\">aiogram.<\/a> \u042f \u043d\u0430\u0434\u0435\u044e\u0441\u044c, \u0441 <a href=\"https:\/\/www.python.org\/downloads\/\" rel=\"noopener noreferrer nofollow\">\u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u043e\u0439 Python<\/a> \u0443 \u0432\u0430\u0441 \u043d\u0435 \u0432\u043e\u0437\u043d\u0438\u043a\u043d\u0435\u0442 \u043f\u0440\u043e\u0431\u043b\u0435\u043c. \u0421 aiogram \u0442\u043e\u0436\u0435.<\/p>\n<p><code>pip install aiogram<\/code><\/p>\n<h2>\u041b\u0438\u0440\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u043e\u0442\u0441\u0442\u0443\u043f\u043b\u0435\u043d\u0438\u0435<\/h2>\n<p>\u042f \u043c\u043d\u043e\u0433\u043e \u043f\u043e\u0437\u0430\u0438\u043c\u0441\u0442\u0432\u043e\u0432\u0430\u043b \u0443 <a href=\"https:\/\/github.com\/alexey-goloburdin\/weather\" rel=\"noopener noreferrer nofollow\">\u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/a> \u0410\u043b\u0435\u043a\u0441\u0435\u044f \u0413\u043e\u043b\u043e\u0431\u0443\u0440\u0434\u0438\u043d\u0430 &#8212; \u0430\u0432\u0442\u043e\u0440\u0430 YouTube \u043a\u0430\u043d\u0430\u043b\u0430 <a href=\"https:\/\/www.youtube.com\/c\/%D0%94%D0%B8%D0%B4%D0%B6%D0%B8%D1%82%D0%B0%D0%BB%D0%B8%D0%B7%D0%B8%D1%80%D1%83%D0%B9\" rel=\"noopener noreferrer nofollow\">&#171;\u0414\u0438\u0434\u0436\u0438\u0442\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0439!&#187;<\/a> \u041f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e \u0435\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442 \u043f\u0440\u0435\u0434\u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f macOS \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b \u0431\u0435\u0440\u0443\u0442\u0441\u044f \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0430 \u043a\u043e\u043c\u0430\u043d\u0434\u043d\u043e\u0439 \u0441\u0442\u0440\u043e\u043a\u0438 <a href=\"https:\/\/github.com\/robmathers\/WhereAmI\" rel=\"noopener noreferrer nofollow\">whereami.<\/a> \u041f\u0440\u0438\u043c\u0435\u0440 \u0432\u044b\u0432\u043e\u0434\u0430:<\/p>\n<pre><code>Latitude: 45.424807,  Longitude: -75.699234 Accuracy (m): 65.000000 Timestamp: 2019-09-28, 12:40:20 PM EDT<\/code><\/pre>\n<p>\u0422\u0430\u043a\u0436\u0435 \u0435\u0433\u043e \u0441\u043a\u0440\u0438\u043f\u0442 \u043f\u0440\u043e\u0441\u0442\u043e \u0432\u044b\u0432\u043e\u0434\u0438\u0442 \u0432\u0441\u044e \u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u0443\u044e \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u0432 \u0442\u0435\u0440\u043c\u0438\u043d\u0430\u043b, \u0445\u043e\u0442\u0435\u043b\u043e\u0441\u044c \u0431\u044b \u0438\u043c\u0435\u0442\u044c \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u043f\u043e\u043f\u0440\u0438\u044f\u0442\u043d\u0435\u0435 \u0438 \u0443\u0434\u043e\u0431\u043d\u0435\u0435.<\/p>\n<figure class=\"\"><figcaption><\/figcaption><\/figure>\n<p>\u042f \u0440\u0435\u0448\u0438\u043b, \u0447\u0442\u043e \u043c\u043e\u0436\u043d\u043e \u0434\u043e\u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0438\u0434\u0435\u044e \u0438 \u043e\u0445\u0432\u0430\u0442\u0438\u0442\u044c \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439, \u043f\u0440\u0435\u043d\u0435\u0431\u0440\u0435\u0433\u0430\u044f \u0442\u043e\u0447\u043d\u043e\u0441\u0442\u044c\u044e \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438.<\/p>\n<h2>\u041f\u0438\u0448\u0435\u043c \u043a\u043e\u0434. \u0424\u0430\u0439\u043b \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438<\/h2>\n<p>\u0418\u0442\u0430\u043a, \u0444\u0430\u0439\u043b <code>config.py<\/code> \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u043a\u043e\u043d\u0441\u0442\u0430\u043d\u0442\u044b:<\/p>\n<ul>\n<li>\n<p>\u0422\u043e\u043a\u0435\u043d \u0431\u043e\u0442\u0430 <code>BOT_API_TOKEN<\/code><\/p>\n<\/li>\n<li>\n<p>\u041a\u043b\u044e\u0447 OpenWeather <code>WEATHER_API_KEY<\/code><\/p>\n<\/li>\n<li>\n<p>\u0417\u0430\u043f\u0440\u043e\u0441 \u0442\u0435\u043a\u0443\u0449\u0435\u0439 \u043f\u043e\u0433\u043e\u0434\u044b <code>CURRENT_WEATHER_API_CALL<\/code><\/p>\n<\/li>\n<\/ul>\n<details class=\"spoiler\">\n<summary>config.py<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">BOT_API_TOKEN = '' WEATHER_API_KEY = ''  CURRENT_WEATHER_API_CALL = (         'https:\/\/api.openweathermap.org\/data\/2.5\/weather?'         'lat={latitude}&amp;lon={longitude}&amp;'         'appid=' + WEATHER_API_KEY + '&amp;units=metric' )<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u041a\u043e\u043d\u0435\u0447\u043d\u043e, \u0442\u0430\u043a\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0435, \u043a\u0430\u043a \u0442\u043e\u043a\u0435\u043d\u044b \u0438 \u043a\u043b\u044e\u0447\u0438 \u043d\u0443\u0436\u043d\u043e \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0432 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f, \u043d\u043e \u044d\u0442\u043e \u043f\u0435\u0442-\u043f\u0440\u043e\u0435\u043a\u0442, \u0434\u0435\u043f\u043b\u043e\u0438\u0442\u044c \u044f \u0435\u0433\u043e \u043d\u0435 \u0431\u0443\u0434\u0443, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043e\u0441\u043e\u0431\u043e \u043d\u0435 \u0437\u0430\u043c\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u044e\u0441\u044c.<\/p>\n<h2>\u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b<\/h2>\n<p>\u0414\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442 \u044f \u0441\u043e\u0437\u0434\u0430\u043b \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u043c\u043e\u0434\u0443\u043b\u044c. \u0414\u0430\u0442\u0430\u043a\u043b\u0430\u0441\u0441 <code>Coordinates<\/code> \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0448\u0438\u0440\u043e\u0442\u0443 \u0438 \u0434\u043e\u043b\u0433\u043e\u0442\u0443 \u0441 \u0442\u0438\u043f\u0430\u043c\u0438 float.<\/p>\n<pre><code class=\"python\">from dataclasses import dataclass  @dataclass(slots=True, frozen=True) class Coordinates:     latitude: float     longitude: float<\/code><\/pre>\n<p>\u041f\u043e IP \u0430\u0434\u0440\u0435\u0441\u0443 \u0438\u0445 \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0439\u0442\u0438 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <a href=\"https:\/\/ipinfo.io\/json\" rel=\"noopener noreferrer nofollow\">ipinfo.io\/json<\/a>. \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u0442\u0441\u044f \u0432\u043e\u0442 \u0442\u0430\u043a\u043e\u0439 \u043e\u0442\u0432\u0435\u0442. <\/p>\n<pre><code class=\"json\">{   \"ip\": \"228.228.228.228\",   \"city\": \"Moscow\",   \"region\": \"Moscow\",   \"country\": \"RU\",   \"loc\": \"55.7522,37.6156\",   \"org\": \"Starlink\",   \"postal\": \"101000\",   \"timezone\": \"Europe\/Moscow\",   \"readme\": \"https:\/\/ipinfo.io\/missingauth\" }<\/code><\/pre>\n<p>\u041d\u0430\u0441 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u0443\u0435\u0442 \u043a\u043b\u044e\u0447 <code>\"loc\"<\/code> \u0441\u043e\u043a\u0440\u0430\u0449\u0435\u043d\u043d\u043e \u043e\u0442 location. \u041e\u043f\u044f\u0442\u044c \u043a\u0430\u043f\u0438\u0442\u0430\u043d \u043e\u0447\u0435\u0432\u0438\u0434\u043d\u043e\u0441\u0442\u044c. \u0414\u0435\u043b\u0430\u0435\u043c \u0437\u0430\u043f\u0440\u043e\u0441 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u0438 <code>urlopen<\/code> \u043c\u043e\u0434\u0443\u043b\u044f <code>request<\/code> \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 <code>urllib.<\/code> \u0412\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u0441\u043b\u043e\u0432\u0430\u0440\u044c \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <code>json.load()<\/code> <\/p>\n<pre><code class=\"python\">from urllib.request import urlopen import json  def _get_ip_data() -> dict:     url = 'http:\/\/ipinfo.io\/json'     response = urlopen(url)     return json.load(response)<\/code><\/pre>\n<p>\u0412 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442 \u043f\u0430\u0440\u0441\u0438\u043c \u044d\u0442\u043e\u0442 \u0441\u043b\u043e\u0432\u0430\u0440\u044c \u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u0434\u0430\u0442\u0430\u043a\u043b\u0430\u0441\u0441 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442.<\/p>\n<pre><code class=\"python\">def get_coordinates() -> Coordinates:     \"\"\"Returns current coordinates using IP address\"\"\"     data = _get_ip_data()     latitude = data['loc'].split(',')[0]     longitude = data['loc'].split(',')[1]      return Coordinates(latitude=latitude, longitude=longitude)<\/code><\/pre>\n<details class=\"spoiler\">\n<summary>coordinates.py<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">from urllib.request import urlopen from dataclasses import dataclass import json   @dataclass(slots=True, frozen=True) class Coordinates:     latitude: float     longitude: float   def get_coordinates() -> Coordinates:     \"\"\"Returns current coordinates using IP address\"\"\"     data = _get_ip_data()     latitude = data['loc'].split(',')[0]     longitude = data['loc'].split(',')[1]      return Coordinates(latitude=latitude, longitude=longitude)   def _get_ip_data() -> dict:     url = 'http:\/\/ipinfo.io\/json'     response = urlopen(url)     return json.load(response)<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<h2>\u041f\u0430\u0440\u0441\u0438\u043c \u043e\u0442\u0432\u0435\u0442 OpenWeather API<\/h2>\n<p>\u0414\u0430\u043b\u0435\u0435 \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043c\u043e\u0434\u0443\u043b\u044c <code>api_service.<\/code> \u0412 \u043d\u0435\u043c \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u0432\u0441\u044f \u0441\u0443\u0435\u0442\u0430 \u0441 \u043f\u043e\u0433\u043e\u0434\u043e\u0439. \u0422\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u0430 \u0438\u0437\u043c\u0435\u0440\u044f\u0435\u0442\u0441\u044f \u0432 \u0433\u0440\u0430\u0434\u0443\u0441\u0430\u0445 \u0426\u0435\u043b\u044c\u0441\u0438\u044f, \u0447\u0435\u043c\u0443 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u043f\u0441\u0435\u0432\u0434\u043e\u043d\u0438\u043c <code>float<\/code> \u0447\u0438\u0441\u043b\u0430.<\/p>\n<pre><code class=\"python\">from typing import TypeAlias  Celsius: TypeAlias = float<\/code><\/pre>\n<p>\u041a\u0430\u043a \u0438\u0437\u0432\u0435\u0441\u0442\u043d\u043e, \u0433\u0440\u0430\u0434\u0443\u0441\u044b \u0424\u0430\u0440\u0435\u043d\u0433\u0435\u0439\u0442\u0430 \u0431\u044b\u043b\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u044b \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u0420\u044d\u0439 \u0411\u0440\u044d\u0434\u0431\u0435\u0440\u0438 \u0441\u043c\u043e\u0433 \u043a\u0440\u0430\u0441\u0438\u0432\u043e \u043d\u0430\u0437\u0432\u0430\u0442\u044c \u0441\u0432\u043e\u044e \u0430\u043d\u0442\u0438\u0443\u0442\u043e\u043f\u0438\u044e.<\/p>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<p>\u0412 \u043e\u0442\u0432\u0435\u0442\u0435 API \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0432\u0435\u0442\u0440\u0430 \u0434\u0430\u0435\u0442\u0441\u044f \u0432 \u0433\u0440\u0430\u0434\u0443\u0441\u0430\u0445. \u042f \u0440\u0435\u0448\u0438\u043b \u043f\u0440\u0438\u0432\u0435\u0441\u0442\u0438 \u0438\u0445 \u0432 \u0431\u043e\u043b\u0435\u0435 \u0443\u0434\u043e\u0431\u043d\u044b\u0439 \u0444\u043e\u0440\u043c\u0430\u0442. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u044f \u0441\u043e\u0437\u0434\u0430\u043b \u043f\u0435\u0440\u0435\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u0435 \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0445 \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0439 \u0432\u0435\u0442\u0440\u0430. <\/p>\n<pre><code class=\"python\">from enum import IntEnum  class WindDirection(IntEnum):     North = 0     Northeast = 45     East = 90     Southeast = 135     South = 180     Southwest = 225     West = 270     Northwest = 315<\/code><\/pre>\n<p>\u0412 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u043f\u0430\u0440\u0441\u0438\u043d\u0433\u0430 \u043e\u043a\u0440\u0443\u0433\u043b\u0435\u043d\u0438\u0435 \u043f\u043e 45 \u0433\u0440\u0430\u0434\u0443\u0441\u043e\u0432 \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0442\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c: \u0434\u0435\u043b\u0438\u043c \u0433\u0440\u0430\u0434\u0443\u0441\u044b \u043d\u0430 45, \u043e\u043a\u0440\u0443\u0433\u043b\u044f\u0435\u043c \u0438 \u0443\u043c\u043d\u043e\u0436\u0430\u0435\u043c \u043e\u0431\u0440\u0430\u0442\u043d\u043e \u043d\u0430 45. \u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u043c\u043e\u0436\u0435\u0442 \u043e\u043a\u0440\u0443\u0433\u043b\u0438\u0442\u044c\u0441\u044f \u0434\u043e 360 \u0433\u0440\u0430\u0434\u0443\u0441\u043e\u0432, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u043c \u044d\u0442\u043e\u0442 \u0441\u043b\u0443\u0447\u0430\u0439.<\/p>\n<pre><code class=\"python\">def _parse_wind_direction(openweather_dict: dict) -> str:     degrees = openweather_dict['wind']['deg']     degrees = round(degrees \/ 45) * 45     if degrees == 360:         degrees = 0     return WindDirection(degrees).name<\/code><\/pre>\n<p>\u0412\u0441\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u043e \u043f\u043e\u0433\u043e\u0434\u0435 \u0431\u0443\u0434\u0443\u0442 \u0445\u0440\u0430\u043d\u0438\u0442\u044c\u0441\u044f \u0432 \u0434\u0430\u0442\u0430\u043a\u043b\u0430\u0441\u0441\u0435. \u041f\u0440\u0438 \u0436\u0435\u043b\u0430\u043d\u0438\u0438 \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0441\u044e\u0434\u0430 \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u0443\u044e \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u0438\u0437 \u043e\u0442\u0432\u0435\u0442\u0430 OpenWeather, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 \u0430\u0442\u043c\u043e\u0441\u0444\u0435\u0440\u043d\u043e\u0435 \u0434\u0430\u0432\u043b\u0435\u043d\u0438\u0435, \u0447\u0430\u0441\u043e\u0432\u043e\u0439 \u043f\u043e\u044f\u0441, \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u0443\u044e \u0438 \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u0443\u044e \u0437\u0430\u0444\u0438\u043a\u0441\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u0443\u044e \u0432 \u0434\u0430\u043d\u043d\u044b\u0439 \u043c\u043e\u043c\u0435\u043d\u0442 \u0442\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u0443.<\/p>\n<pre><code class=\"python\">@dataclass(slots=True, frozen=True) class Weather:     location: str     temperature: Celsius     temperature_feeling: Celsius     description: str     wind_speed: float     wind_direction: str     sunrise: datetime     sunset: datetime<\/code><\/pre>\n<p>\u0412 \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u043e\u043c \u043d\u0438\u0447\u0435\u0433\u043e \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e\u0433\u043e \u0432 \u043c\u043e\u0434\u0443\u043b\u0435 \u043d\u0435 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442, \u043f\u0440\u043e\u0441\u0442\u043e \u043f\u0430\u0440\u0441\u0438\u043d\u0433 json.<\/p>\n<details class=\"spoiler\">\n<summary>api_service.py<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">from typing import Literal, TypeAlias from urllib.request import urlopen from dataclasses import dataclass from datetime import datetime from enum import IntEnum import json  from coordinates import Coordinates import config  Celsius: TypeAlias = float   class WindDirection(IntEnum):     North = 0     Northeast = 45     East = 90     Southeast = 135     South = 180     Southwest = 225     West = 270     Northwest = 315   @dataclass(slots=True, frozen=True) class Weather:     location: str     temperature: Celsius     temperature_feeling: Celsius     description: str     wind_speed: float     wind_direction: str     sunrise: datetime     sunset: datetime   def get_weather(coordinates=Coordinates) -> Weather:     \"\"\"Requests the weather in OpenWeather API and returns it\"\"\"     openweather_response = _get_openweather_response(         longitude=coordinates.longitude, latitude=coordinates.latitude     )     weather = _parse_openweather_response(openweather_response)     return weather   def _get_openweather_response(latitude: float, longitude: float) -> str:     url = config.CURRENT_WEATHER_API_CALL.format(latitude=latitude, longitude=longitude)     return urlopen(url).read()   def _parse_openweather_response(openweather_response: str) -> Weather:     openweather_dict = json.loads(openweather_response)     return Weather(         location=_parse_location(openweather_dict),         temperature=_parse_temperature(openweather_dict),         temperature_feeling=_parse_temperature_feeling(openweather_dict),         description=_parse_description(openweather_dict),         sunrise=_parse_sun_time(openweather_dict, 'sunrise'),         sunset=_parse_sun_time(openweather_dict, 'sunset'),         wind_speed=_parse_wind_speed(openweather_dict),         wind_direction=_parse_wind_direction(openweather_dict)     )   def _parse_location(openweather_dict: dict) -> str:     return openweather_dict['name']   def _parse_temperature(openweather_dict: dict) -> Celsius:     return openweather_dict['main']['temp']   def _parse_temperature_feeling(openweather_dict: dict) -> Celsius:     return openweather_dict['main']['feels_like']   def _parse_description(openweather_dict) -> str:     return str(openweather_dict['weather'][0]['description']).capitalize()   def _parse_sun_time(openweather_dict: dict, time: Literal[\"sunrise\", \"sunset\"]) -> datetime:     return datetime.fromtimestamp(openweather_dict['sys'][time])   def _parse_wind_speed(openweather_dict: dict) -> float:     return openweather_dict['wind']['speed']   def _parse_wind_direction(openweather_dict: dict) -> str:     degrees = openweather_dict['wind']['deg']     degrees = round(degrees \/ 45) * 45     if degrees == 360:         degrees = 0     return WindDirection(degrees).name<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<h2>\u0414\u0435\u043b\u0430\u0435\u043c <\/h><\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-337393","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/337393","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=337393"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/337393\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=337393"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=337393"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=337393"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}