]> jfr.im git - erebus.git/commitdiff
add avweather module to pull METAR from aviationweather.gov
authorJohn Runyon <redacted>
Sun, 26 Nov 2023 00:09:48 +0000 (17:09 -0700)
committerJohn Runyon <redacted>
Sun, 26 Nov 2023 00:09:48 +0000 (17:09 -0700)
modules/avweather.py [new file with mode: 0644]

diff --git a/modules/avweather.py b/modules/avweather.py
new file mode 100644 (file)
index 0000000..cc6837d
--- /dev/null
@@ -0,0 +1,116 @@
+# Erebus IRC bot - Author: Erebus Team
+# vim: fileencoding=utf-8
+# weather module (from aviationweather.gov)
+# This file is released into the public domain; see http://unlicense.org/
+
+# module info
+modinfo = {
+       'author': 'Erebus Team',
+       'license': 'public domain',
+       'compatible': [0],
+       'depends': ['userinfo'],
+       'softdeps': ['help'],
+}
+
+# preamble
+import modlib
+lib = modlib.modlib(__name__)
+modstart = lib.modstart
+modstop = lib.modstop
+
+# module code
+import json
+import sys
+import re
+
+if sys.version_info.major < 3:
+       from urllib import urlopen, quote_plus
+else:
+       from urllib.request import urlopen
+       from urllib.parse import quote_plus
+
+def location(person, default=None): return lib.mod('userinfo').get(person, 'location', default=None)
+
+def _dayofweek(dayname):
+       return ['mon','tue','wed','thu','fri','sat','sun'].index(dayname.lower())
+
+def _time_adjust(d):
+       t = d['current']['observation_time']
+       #XXX
+       #mo = re.match(r"(\d\d):(\d\d) (AM|PM)", t)
+       #if mo:
+       #       return 
+       return t + ' UTC'
+
+def _c2f(celsius):
+       return round(celsius * 9.0/5 + 32, 2)
+
+def _kmh2mph(kmh):
+       return round(kmh / 1.60934, 2)
+
+def _weather(place):
+       if not lib.parent.cfg.get('weatherstack_weather', 'key'):
+               return "Weather is not enabled - please set the API key in the config file"
+
+       if place is not None:
+               url = 'http://api.weatherstack.com/current?access_key=%s&query=%s' % (lib.parent.cfg.get('weatherstack_weather', 'key'), quote_plus(place))
+               if sys.version_info.major < 3:
+                       url = url.encode('utf8')
+               weather = json.load(urlopen(url))
+               if lib.parent.cfg.getboolean('debug', 'weather'):
+                       lib.parent.log('*', "?", repr(weather))
+               if 'error' in weather:
+                       return "Error from WeatherStack: (%d) %s" % (weather['error']['code'], weather['error']['info'])
+
+               return u"Weather in %(location)s, %(region)s, %(country)s: As of %(time)s, %(conditions)s, %(cel)s°C (%(far)s°F) (feels like %(flcel)s°C (%(flfar)s°F)). Wind %(windk)skm/h (%(windm)smph) %(winddir)s." % {
+                       'location': weather['location']['name'],
+                       'region': weather['location']['region'],
+                       'country': weather['location']['country'],
+                       'time': _time_adjust(weather),
+                       'conditions': ', '.join(weather['current']['weather_descriptions']),
+                       'cel': weather['current']['temperature'], 'far': _c2f(weather['current']['temperature']),
+                       'flcel': weather['current']['feelslike'], 'flfar': _c2f(weather['current']['feelslike']),
+                       'windk': weather['current']['wind_speed'], 'windm': _kmh2mph(weather['current']['wind_speed']),
+                       'winddir': weather['current']['wind_dir'],
+               }
+       else:
+               return "I don't know where to look! Try %ssetinfo location <your location>" % (lib.parent.trigger,)
+
+
+def _get_metar(place):
+       url = 'https://aviationweather.gov/cgi-bin/data/metar.php?ids=%s&hours=0&order=id%%2C-obs&sep=true&format=raw' % (quote_plus(place))
+       if sys.version_info.major < 3:
+               url = url.encode('utf8')
+       return urlopen(url).read().decode('utf8').strip()
+
+METAR_REGEX = re.compile('(?P<location>[A-Z]{4}) (?P<time>\d{6}Z) (?P<auto>AUTO )?(?P<corrected>COR|CCA )?(?P<winddir>(?:VRB|\d\d\d)(?:V\d\d\d)?)(?P<windspeed>\d{2})(?:G(?P<windgust>\d{2}))?KT (?:\d{3}V\d{3} )?(?:(?:(?P<visibilityus>\d+(?: \d+/\d+)?)SM )|(?:(?P<visibilitymetric>\d+) )).*')
+def _reformat_metar(metar):
+       # http://www.dixwx.com/wxdecoding.htm
+       # https://aviationweather.gov/cgi-bin/data/metar.php?ids=EFHK&hours=0&order=id%2C-obs&sep=true&format=raw
+       # https://aviationweather.gov/cgi-bin/data/metar.php?ids=KCTB&hours=0&order=id%2C-obs&sep=true&format=raw
+       # Need to implement: everything after visibility lol
+       res = METAR_REGEX.fullmatch(metar)
+       if res is None:
+               return "Failed to parse METAR: %s" % (metar)
+       else:
+               return repr(res.groupdict())
+
+#@lib.hook(('avweather','avw'), needchan=False, wantchan=True)
+#@lib.help('<ICAO code>', 'show weather at an airport')
+@lib.argsEQ(1)
+def avweather(bot, user, chan, realtarget, *args):
+       if chan is None:
+               chan = user
+       if len(args[0]) != 4:
+               bot.msg(chan, "You must use a 4-character ICAO code.")
+       bot.msg(chan, _reformat_metar(_get_metar(args[0])))
+
+@lib.hook(needchan=False, wantchan=True)
+@lib.help('<ICAO code>', 'show raw METAR for an airport')
+@lib.argsEQ(1)
+def metar(bot, user, chan, realtarget, *args):
+       if chan is None:
+               chan = user
+       if len(args[0]) != 4:
+               bot.msg(chan, "You must use a 4-character ICAO code.")
+       bot.msg(chan, _get_metar(args[0]))