From 3a756a74891063b66008959a9a80227f317c250f Mon Sep 17 00:00:00 2001 From: dece Date: Fri, 17 Sep 2021 12:44:25 +0200 Subject: [PATCH] meteo_france: add plugin Also remove the silly Python 3.7 requirement. --- Pipfile | 3 - Pipfile.lock | 116 +++++++++++++------------------- config.json.example | 15 +++++ edmond/plugins/meteo_france.py | 107 +++++++++++++++++++++++++++++ edmond/plugins/requirements.txt | 3 + 5 files changed, 172 insertions(+), 72 deletions(-) create mode 100644 edmond/plugins/meteo_france.py diff --git a/Pipfile b/Pipfile index 0819fbd..42fefaf 100644 --- a/Pipfile +++ b/Pipfile @@ -8,6 +8,3 @@ verify_ssl = true [packages] irc = "*" requests = "*" - -[requires] -python_version = "3.7" diff --git a/Pipfile.lock b/Pipfile.lock index 5170c2f..ef87aaa 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,12 +1,10 @@ { "_meta": { "hash": { - "sha256": "b9ea2a09600a7aba7d67761e0e8338ba3711a93442f9be86e950621b70be216f" + "sha256": "cd3456f770a7bd477ad79cd1ce56fd05fb3e3b08167b430021c3aee8fa4b3fcf" }, "pipfile-spec": 6, - "requires": { - "python_version": "3.7" - }, + "requires": {}, "sources": [ { "name": "pypi", @@ -18,32 +16,26 @@ "default": { "certifi": { "hashes": [ - "sha256:5930595817496dd21bb8dc35dad090f1c2cd0adfaf21204bf6732ca5d8ee34d3", - "sha256:8fc0819f1f30ba15bdb34cceffb9ef04d99f420f68eb75d901e9560b8749fc41" + "sha256:2bbf76fd432960138b3ef6dda3dde0544f27cbf8546c458e60baf371917ba9ee", + "sha256:50b1e4f8446b06f41be7dd6338db18e0990601dce795c2b1686458aa7e8fa7d8" ], - "version": "==2020.6.20" + "version": "==2021.5.30" }, - "chardet": { + "charset-normalizer": { "hashes": [ - "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", - "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" + "sha256:7098e7e862f6370a2a8d1a6398cd359815c45d12626267652c3f13dec58e2367", + "sha256:fa471a601dfea0f492e4f4fca035cd82155e65dc45c9b83bf4322dfab63755dd" ], - "version": "==3.0.4" + "markers": "python_version >= '3'", + "version": "==2.0.5" }, "idna": { "hashes": [ - "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6", - "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0" - ], - "version": "==2.10" - }, - "importlib-metadata": { - "hashes": [ - "sha256:77a540690e24b0305878c37ffd421785a6f7e53c8b5720d211b211de8d0e95da", - "sha256:cefa1a2f919b866c5beb7c9f7b0ebb4061f30a8a9bf16d609b000e2dfaceb9c3" + "sha256:14475042e284991034cb48e06f6851428fb14c4dc953acd9be9a5e95c7b6dd7a", + "sha256:467fbad99067910785144ce333826c71fb0e63a425657295239737f7ecd125f3" ], - "markers": "python_version < '3.8'", - "version": "==2.0.0" + "markers": "python_version >= '3'", + "version": "==3.2" }, "irc": { "hashes": [ @@ -55,95 +47,81 @@ }, "jaraco.classes": { "hashes": [ - "sha256:116429c2047953f525afdcae165475c4589c7b14870e78b2d068ecb01018827e", - "sha256:c38698ff8ef932eb33d91c0e8fc192ad7c44ecee03f7f585afd4f35aeaef7aab" + "sha256:22ac35313cf4b145bf7b217cc51be2d98a3d2db1c8558a30ca259d9f0b9c0b7d", + "sha256:ed54b728af1937dc16b7236fbaf34ba561ba1ace572b03fffa5486ed363ecf34" ], - "version": "==3.1.0" + "version": "==3.2.1" }, "jaraco.collections": { "hashes": [ - "sha256:a7889f28c80c4875bd6256d9924e8526dacfef22cd7b80ff8469b4d312f9f144", - "sha256:be570ef4f2e7290b757449395238fa63d70a9255574624e73c5ff9f1ee554721" + "sha256:344d14769d716e7496af879ac71b3c6ebdd46abc64bd9ec21d15248365aa3ac9", + "sha256:6fdf48b6268d44b589a9d7359849f5c4ea6447b59845e489da261996fbc41b79" ], - "version": "==3.0.0" + "version": "==3.4.0" }, "jaraco.functools": { "hashes": [ - "sha256:9fedc4be3117512ca3e03e1b2ffa7a6a6ffa589bfb7d02bfb324e55d493b94f4", - "sha256:d3dc9f6c1a1d45d7f59682a3bf77aceb685c1a60891606c7e4161e72ecc399ad" + "sha256:7c788376d69cf41da675b186c85366fe9ac23c92a70697c455ef9135c25edf31", + "sha256:bfcf7da71e2a0e980189b0744b59dba6c1dcf66dcd7a30f8a4413e478046b314" ], - "version": "==3.0.1" + "version": "==3.3.0" }, "jaraco.logging": { "hashes": [ - "sha256:31716fe84d3d5df39d95572942513bd4bf8ae0a478f64031eff4c2ea9e83434e", - "sha256:b05ed07101883997a30e05c2472798d86129803d9961a0d1081a3236ad37c52a" + "sha256:150dc8701207b28bc65a16f0e91c07250a8d1b9da324ce674c0e375774944f13", + "sha256:ec4720a1ed8d7e4667a5f0178c4864df1a2f085aad5ba47355c41fcb2f9d68cd" ], - "version": "==3.0.0" + "version": "==3.1.0" }, "jaraco.stream": { "hashes": [ - "sha256:287e1cba9f278e0146fdded6bc40518930813a5584579769aeaa1d0bfd178a73", - "sha256:a42357141288bbd55938b9ff464173c078038374ce5ffa1bf31895138acc0f30" + "sha256:0ede0988de595d15a72fa95f1eec4dee20fdede30e8d2ddee1b9c2a963eb67ef", + "sha256:86c57fedffd4d5a4b18817f99ddf62ac8ed0a1bc31a1c41b9a88df9c6bb56e0b" ], - "version": "==3.0.0" + "version": "==3.0.2" }, "jaraco.text": { "hashes": [ - "sha256:c87569c9afae14f71b2e1c57f316770ab6981ab675d9c602be1c7981161bacdd", - "sha256:e5078b1126cc0f166c7859aa75103a56c0d0f39ebcafc21695615472e0f810ec" + "sha256:dc900b7916cefdaf943fbd43870abc8b0a6ff68f2c8c33e212fd51139219f68d", + "sha256:ede4e9103443b62b3d1d193257dfb85aab7c69a6cef78a0887d64bb307a03bc3" ], - "version": "==3.2.0" + "version": "==3.5.1" }, "more-itertools": { "hashes": [ - "sha256:6f83822ae94818eae2612063a5101a7311e68ae8002005b5e05f03fd74a86a20", - "sha256:9b30f12df9393f0d28af9210ff8efe48d10c94f73e5daf886f10c4b0b0b4f03c" + "sha256:70401259e46e216056367a0a6034ee3d3f95e0bf59d3aa6a4eb77837171ed996", + "sha256:8c746e0d09871661520da4f1241ba6b908dc903839733c8203b552cffaf173bd" ], - "version": "==8.5.0" + "version": "==8.9.0" }, "pytz": { "hashes": [ - "sha256:a494d53b6d39c3c6e44c3bec237336e14305e4f29bbf800b599253057fbb79ed", - "sha256:c35965d010ce31b23eeb663ed3cc8c906275d6be1a34393a1d73a41febf4a048" + "sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da", + "sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798" ], - "version": "==2020.1" + "version": "==2021.1" }, "requests": { "hashes": [ - "sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b", - "sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898" + "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24", + "sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7" ], "index": "pypi", - "version": "==2.24.0" - }, - "six": { - "hashes": [ - "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259", - "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced" - ], - "version": "==1.15.0" + "version": "==2.26.0" }, "tempora": { "hashes": [ - "sha256:599a3a910b377f2b544c7b221582ecf4cb049b017c994b37f2b1a9ed1099716e", - "sha256:9f46de767be7dd21d9602a8a5b0978fd55abc70af3e2a7814c85c00d7a8fffa3" + "sha256:c54da0f05405f04eb67abbb1dff4448fd91428b58cb00f0f645ea36f6a927950", + "sha256:ef2d8bb35902d5ea7da95df33456685a6d305b97f311725c12e55c13d85c0938" ], - "version": "==4.0.0" + "version": "==4.1.1" }, "urllib3": { "hashes": [ - "sha256:91056c15fa70756691db97756772bb1eb9678fa585d9184f24534b100dc60f4a", - "sha256:e7983572181f5e1522d9c98453462384ee92a0be7fac5f1413a1e35c56cc0461" + "sha256:39fb8672126159acb139a7718dd10806104dec1e2f0f6c88aab05d17df10c8d4", + "sha256:f57b4c16c62fa2760b7e3d97c35b255512fb6b59a259730f36ba32ce9f8e342f" ], - "version": "==1.25.10" - }, - "zipp": { - "hashes": [ - "sha256:64ad89efee774d1897a58607895d80789c59778ea02185dd846ac38394a8642b", - "sha256:eed8ec0b8d1416b2ca33516a37a08892442f3954dee131e92cfd92d8fe3e7066" - ], - "version": "==3.3.0" + "version": "==1.26.6" } }, "develop": {} diff --git a/config.json.example b/config.json.example index 3c7bbe5..b883631 100644 --- a/config.json.example +++ b/config.json.example @@ -62,6 +62,21 @@ "commands": ["journée mondiale"], "url": "https://www.journee-mondiale.com/" }, + "meteofrance": { + "commands": ["météo"], + "aliases": { + "météo": ["meteo"] + }, + "result_message": "{nearest_s}; {today_s}.", + "nearest_message": "({city} @ {hour}) {weather}, {temp}, {wind} and {rain}", + "temp_format": "{temp_c}°C (windchill {temp_wc})", + "wind_format": "wind {speed} km/h to {direction}", + "unknown_direction": "unknown", + "rain_chance_format": "{p}% over {h}", + "all_rain_format": "rain chances {all_percs}", + "today_message": "today {temp_minmax}, {rain}, and sunset at {sunset}", + "minmax_temp_format": "max {t_max} min {t_min}" + }, "miscreactions": { "reactions": [ "stop", "king", "mmm", "ooo", "detector", "question_marks", diff --git a/edmond/plugins/meteo_france.py b/edmond/plugins/meteo_france.py new file mode 100644 index 0000000..2aa8721 --- /dev/null +++ b/edmond/plugins/meteo_france.py @@ -0,0 +1,107 @@ +import time + +try: + import meteofrance_api as mf + DEPENDENCIES_FOUND = True +except ImportError: + DEPENDENCIES_FOUND = False + +from edmond.plugin import Plugin + + +class MeteoFrancePlugin(Plugin): + + REQUIRED_CONFIGS = [ + "commands", "result_message", "nearest_message", "temp_format", + "rain_chance_format", "all_rain_format", "today_message", + "minmax_temp_format" + ] + + def __init__(self, bot): + super().__init__(bot) + self.client = None + + def on_welcome(self, event): + self.client = mf.MeteoFranceClient() + + def on_pubmsg(self, event): + if not self.should_handle_command(event.arguments[0]): + return False + if self.command.ident == self.config["commands"][0]: + self.tell_weather(event) + return True + + def tell_weather(self, event): + places = self.client.search_places(self.command.content) + if not places: + self.signal_failure(event.target) + return + place = places[0] # Assume first result is the good one. + + try: + forecast = self.client.get_forecast_for_place(place) + except: # really unsure about that unofficial API + self.signal_failure(event.target) + return + + nearest_f = forecast.nearest_forecast + today_f = forecast.today_forecast + try: + nearest_s = self.format_nearest_forecast(nearest_f, place) + today_s = self.format_today_forecast(today_f) + except KeyError: + self.signal_failure(event.target) + return + + result = self.config["result_message"].format( + nearest_s=nearest_s, today_s=today_s) + self.bot.say(event.target, result) + + def format_nearest_forecast(self, nearest, place): + city = place.name + hour = format_ts_hour(nearest["dt"]) + temp = self.format_temperature(nearest["T"]) + weather = format_weather(nearest["weather"]) + wind = self.format_wind(nearest["wind"]) + rain = self.format_rain_perc(nearest["rain"]) + return self.config["nearest_message"].format( + city=city, hour=hour, weather=weather, temp=temp, wind=wind, + rain=rain + ) + + def format_temperature(self, temperature): + temp_c = temperature.get("value", "?") + temp_wc = temperature.get("windchill", "?") + return self.config["temp_format"].format( + temp_c=temp_c, temp_wc=temp_wc) + + def format_rain_perc(self, rain_percs): + perc_fmt = self.config["rain_chance_format"] + all_percs = ', '.join( + perc_fmt.format(p=p, h=h) + for h, p in rain_percs.items() + ) + return self.config["all_rain_format"].format(all_percs=all_percs) + + def format_wind(self, wind): + speed = wind.get('speed') + direction = wind.get('icon') + if direction == "Variable": + direction = self.config["unknown_direction"] + return self.config["wind_format"].format( + speed=speed, direction=direction) + + def format_today_forecast(self, today_f): + temp = today_f['T'] + temp_minmax = self.config["minmax_temp_format"].format( + t_max=temp["max"], t_min=temp["min"]) + rain = self.format_rain_perc(today_f['precipitation']) + sunset = format_ts_hour(today_f['sun']['set']) + return self.config["today_message"].format( + temp_minmax=temp_minmax, rain=rain, sunset=sunset) + +def format_ts_hour(timestamp): + return time.strftime("%H:%M", time.localtime(timestamp)) + +def format_weather(weather): + return weather.get("desc", "?").lower() diff --git a/edmond/plugins/requirements.txt b/edmond/plugins/requirements.txt index 35d2571..41e047e 100644 --- a/edmond/plugins/requirements.txt +++ b/edmond/plugins/requirements.txt @@ -15,3 +15,6 @@ scaruffi==0.0.3 # WolframAlpha wolframalpha + +# Météo France +meteofrance-api~=1.0.2