diff --git a/README.md b/README.md index 2bd6c42..567cf27 100644 --- a/README.md +++ b/README.md @@ -362,7 +362,8 @@ Open Glider Network maintains a unified tracking platform for gliders, drones an Read more about OGN at https://www.glidernet.org/ FLOG module supports showing field logs for OGN receivers and can display live -field log in a room. +field log in a room. It can also show latest known location of an aircraft +using !sar command. It uses FlightBook instance at https://flightbook.glidernet.org/ @@ -391,6 +392,7 @@ Commands and examples: * !flog status - print status of this room * !flog live - enable live field log for this room * !flog rmlive - disable live field log for this room +* !sar OH-123 - Send latest known location of aircraft OH-123 NOTE: disabled by default @@ -776,7 +778,17 @@ class Bot: :param blob_content_type: Content type of the image in case of binary content :return: """ - + async def send_location(self, room, body, latitude, longitude, bot_ignore=False): + """ + + :param room: A MatrixRoom the html should be send to + :param html: Html content of the message + :param body: Plaintext content of the message + :param latitude: Latitude in WGS84 coordinates (float) + :param longitude: Longitude in WGS84 coordinates (float) + :param bot_ignore: Flag to mark the message to be ignored by the bot + :return: + """ ``` ### Logging @@ -803,9 +815,9 @@ You probably want to call it during `matrix_start`: ```python class MatrixModule(BotModule): - def matrix_start(self, bot): - super().matrix_start(bot) - self.add_module_aliases(bot, ['new_name', 'another_name']) + def matrix_start(self, bot): + super().matrix_start(bot) + self.add_module_aliases(bot, ['new_name', 'another_name']) ``` Then you can call this module with its original name, `!new_name`, or `!another_name` diff --git a/bot.py b/bot.py index d4bfe48..90b9d98 100755 --- a/bot.py +++ b/bot.py @@ -177,6 +177,24 @@ class Bot: msg["org.vranki.hemppa.ignore"] = "true" await self.client.room_send(room.room_id, 'm.room.message', msg) + async def send_location(self, room, body, latitude, longitude, bot_ignore=False): + """ + + :param room: A MatrixRoom the html should be send to + :param html: Html content of the message + :param body: Plaintext content of the message + :param latitude: Latitude in WGS84 coordinates (float) + :param longitude: Longitude in WGS84 coordinates (float) + :param bot_ignore: Flag to mark the message to be ignored by the bot + :return: + """ + locationmsg = { + "body": str(body), + "geo_uri": 'geo:' + str(latitude) + ',' + str(longitude), + "msgtype": "m.location", + } + await self.client.room_send(room.room_id, 'm.room.message', locationmsg) + async def send_image(self, room, url, body, mimetype=None, width=None, height=None, size=None): """ @@ -396,7 +414,7 @@ class Bot: module = reload(module) cls = getattr(module, 'MatrixModule') return cls(modulename) - except ModuleNotFoundError: + except Exception: self.logger.error(f'Module {modulename} failed to load!') traceback.print_exc(file=sys.stderr) return None diff --git a/modules/flog.py b/modules/flog.py index 14d51b6..b6d45d2 100644 --- a/modules/flog.py +++ b/modules/flog.py @@ -10,6 +10,7 @@ from random import randrange from modules.common.module import BotModule +# API docs at: https://gitlab.com/lemoidului/ogn-flightbook/-/blob/master/doc/API.md class FlightBook: def __init__(self): self.base_url = 'https://flightbook.glidernet.org/api' @@ -18,13 +19,27 @@ class FlightBook: 'Paraglider', 'Powered', 'Jet', 'UFO', 'Balloon', \ 'Airship', 'UAV', '?', 'Static object' ] self.logged_flights = dict() # station -> [index of flight] + self.device_cache = dict() # Registration -> device address def get_flights(self, icao): response = urllib.request.urlopen(self.base_url + "/logbook/" + icao) data = json.loads(response.read().decode("utf-8")) # print(json.dumps(data, sort_keys=True, indent=4)) + self.update_device_cache(data) return data + def update_device_cache(self, data): + devices = data['devices'] + for device in devices: + if device["address"] and device["registration"]: + self.device_cache[device["registration"]] = device["address"] + + def address_for_registration(self, registration): + for reg in self.device_cache.keys(): + if reg.lower() == registration.lower(): + return self.device_cache[reg] + return None + def format_time(self, time): if not time: return '··:··' @@ -71,6 +86,10 @@ class MatrixModule(BotModule): self.enabled = False self.fb = FlightBook() + def matrix_start(self, bot): + super().matrix_start(bot) + self.add_module_aliases(bot, ['sar']) + async def matrix_poll(self, bot, pollcount): if pollcount % (6 * 5) == 0: # Poll every 5 min await self.poll_implementation(bot) @@ -103,14 +122,14 @@ class MatrixModule(BotModule): async def matrix_message(self, bot, room, event): args = event.body.split() - if len(args) == 1: + if len(args) == 1 and args[0] == "!flog": if room.room_id in self.station_rooms: station = self.station_rooms[room.room_id] await self.show_flog(bot, room, station) else: await bot.send_text(room, 'No OGN station set for this room - set it first.') - elif len(args) == 2: + elif len(args) == 2 and args[0] == "!flog": if args[1] == 'rmstation': bot.must_be_admin(room, event) del self.station_rooms[room.room_id] @@ -119,6 +138,7 @@ class MatrixModule(BotModule): elif args[1] == 'status': print(self.logged_flights) + print(self.fb.device_cache) bot.must_be_admin(room, event) await bot.send_text(room, f'OGN station for this room: {self.station_rooms.get(room.room_id)}, live updates enabled: {room.room_id in self.live_rooms}') @@ -142,8 +162,18 @@ class MatrixModule(BotModule): # Assume parameter is a station name station = args[1] await self.show_flog(bot, room, station) + elif len(args) == 2 and args[0] == "!sar": + registration = args[1] + address = self.fb.address_for_registration(registration) + coords = None + if address: + coords = self.get_coords_for_address(address) + if coords: + await bot.send_location(room, f'{registration} ({coords["utc"]})', coords["lat"], coords["lng"]) + else: + await bot.send_text(room, f'No Flarm ID found for {registration}!') - elif len(args) == 3: + elif len(args) == 3 and args[0] == "!flog": if args[1] == 'station': bot.must_be_admin(room, event) @@ -154,6 +184,16 @@ class MatrixModule(BotModule): bot.save_settings() await bot.send_text(room, f'Set OGN station {station} to this room') + + def get_coords_for_address(self, address): + # https://flightbook.glidernet.org/api/live/address/~91DADF5B86 + url = self.fb.base_url + "/live/address/" + address + response = urllib.request.urlopen(self.fb.base_url + "/live/address/" + address) + data = json.loads(response.read().decode("utf-8")) + # print(json.dumps(data, sort_keys=True, indent=4)) + return data + + def text_flog(self, data, showtow): out = "" if len(data["flights"]) == 0: diff --git a/modules/loc.py b/modules/loc.py index ffa7679..5e50737 100644 --- a/modules/loc.py +++ b/modules/loc.py @@ -55,12 +55,7 @@ class MatrixModule(BotModule): location = geolocator.geocode(query) self.logger.info('loc rx %s', location) if location: - locationmsg = { - "body": str(location.address), - "geo_uri": 'geo:' + str(location.latitude) + ',' + str(location.longitude), - "msgtype": "m.location", - } - await bot.client.room_send(room.room_id, 'm.room.message', locationmsg) + await bot.send_location(room.room_id, location.address, location.latitude, location.longitude) else: await bot.send_text(room, "Can't find " + query + " on map!")