2021-07-19 00:03:48 +03:00
|
|
|
from logging import log
|
2020-05-03 23:27:39 +03:00
|
|
|
import sys
|
|
|
|
import traceback
|
|
|
|
import json
|
|
|
|
import time
|
|
|
|
import datetime
|
2021-07-19 00:03:48 +03:00
|
|
|
import requests
|
|
|
|
import urllib3
|
2020-05-03 23:27:39 +03:00
|
|
|
|
|
|
|
from datetime import datetime, timedelta
|
|
|
|
from random import randrange
|
|
|
|
|
|
|
|
from modules.common.module import BotModule
|
|
|
|
|
2021-07-19 00:03:48 +03:00
|
|
|
urllib3.disable_warnings()
|
|
|
|
|
2021-04-25 03:00:52 +03:00
|
|
|
# API docs at: https://gitlab.com/lemoidului/ogn-flightbook/-/blob/master/doc/API.md
|
2021-04-10 00:31:54 +03:00
|
|
|
class FlightBook:
|
|
|
|
def __init__(self):
|
|
|
|
self.base_url = 'https://flightbook.glidernet.org/api'
|
|
|
|
self.AC_TYPES = [ '?', 'Glider', 'Towplane', \
|
|
|
|
'Helicopter', 'Parachute', 'Drop plane', 'Hang glider', \
|
|
|
|
'Paraglider', 'Powered', 'Jet', 'UFO', 'Balloon', \
|
|
|
|
'Airship', 'UAV', '?', 'Static object' ]
|
2021-04-14 23:02:50 +03:00
|
|
|
self.logged_flights = dict() # station -> [index of flight]
|
2021-07-19 00:03:48 +03:00
|
|
|
self.device_cache = dict() # Registration -> [address, CN]
|
2021-04-10 00:31:54 +03:00
|
|
|
|
|
|
|
def get_flights(self, icao):
|
2021-07-19 00:03:48 +03:00
|
|
|
log_url = f'{self.base_url}/logbook/{icao}'
|
|
|
|
data = None
|
|
|
|
with requests.Session() as session:
|
|
|
|
response = session.get(log_url, headers={'Connection': 'close'}, verify=False)
|
|
|
|
data = response.json()
|
|
|
|
|
2021-04-10 00:31:54 +03:00
|
|
|
# print(json.dumps(data, sort_keys=True, indent=4))
|
2021-04-25 03:00:52 +03:00
|
|
|
self.update_device_cache(data)
|
2021-04-10 00:31:54 +03:00
|
|
|
return data
|
|
|
|
|
2021-04-25 03:00:52 +03:00
|
|
|
def update_device_cache(self, data):
|
|
|
|
devices = data['devices']
|
|
|
|
for device in devices:
|
|
|
|
if device["address"] and device["registration"]:
|
2021-07-19 00:03:48 +03:00
|
|
|
cache_entry = [device["address"], device["competition"]]
|
|
|
|
self.device_cache[device["registration"]] = cache_entry
|
2021-04-25 03:00:52 +03:00
|
|
|
|
|
|
|
def address_for_registration(self, registration):
|
|
|
|
for reg in self.device_cache.keys():
|
|
|
|
if reg.lower() == registration.lower():
|
2021-07-19 00:03:48 +03:00
|
|
|
return self.device_cache[reg][0]
|
|
|
|
return None
|
|
|
|
|
|
|
|
def address_for_cn(self, cn):
|
|
|
|
for reg in self.device_cache.keys():
|
|
|
|
if self.device_cache[reg][1] == cn.upper():
|
|
|
|
return self.device_cache[reg][0]
|
2021-04-25 03:00:52 +03:00
|
|
|
return None
|
|
|
|
|
2021-04-10 00:31:54 +03:00
|
|
|
def format_time(self, time):
|
|
|
|
if not time:
|
|
|
|
return '··:··'
|
|
|
|
time = time.replace('h', ':')
|
|
|
|
return time
|
|
|
|
|
|
|
|
def flight2string(self, flight, data):
|
|
|
|
devices = data['devices']
|
|
|
|
device = devices[flight['device']]
|
|
|
|
start = self.format_time(flight["start"])
|
|
|
|
end = self.format_time(flight["stop"])
|
|
|
|
duration = ' '
|
|
|
|
if flight["duration"]:
|
|
|
|
duration = time.strftime('%H:%M', time.gmtime(flight["duration"]))
|
|
|
|
maxalt = ''
|
|
|
|
if flight["max_alt"]:
|
|
|
|
maxalt = str(flight["max_alt"]) + 'm'
|
|
|
|
|
|
|
|
identity = f'{device.get("registration") or ""} {device.get("aircraft") or ""} {device.get("competition") or ""} {maxalt}'
|
|
|
|
identity = ' '.join(identity.split())
|
|
|
|
return f'{start} - {end} {duration} {identity}'
|
|
|
|
|
|
|
|
def print_flights(self, data, showtow=False):
|
|
|
|
print(f'✈ Flights at {data["airfield"]["name"]} ({data["airfield"]["code"]}) {data["date"]}:')
|
|
|
|
flights = data['flights']
|
|
|
|
for flight in flights:
|
|
|
|
if not showtow and flight["towing"]:
|
|
|
|
continue
|
|
|
|
print(self.flight2string(flight, data))
|
|
|
|
|
|
|
|
def test():
|
|
|
|
fb = FlightBook()
|
|
|
|
data = fb.get_flights('LFMX')
|
|
|
|
fb.print_flights(data)
|
|
|
|
|
2020-05-03 23:27:39 +03:00
|
|
|
class MatrixModule(BotModule):
|
|
|
|
def __init__(self, name):
|
|
|
|
super().__init__(name)
|
|
|
|
self.service_name = 'FLOG'
|
|
|
|
self.station_rooms = dict() # Roomid -> ogn station
|
|
|
|
self.live_rooms = [] # Roomid's with live enabled
|
2021-04-10 23:22:11 +03:00
|
|
|
self.logged_flights = dict() # Station -> number of flights
|
2020-05-03 23:27:39 +03:00
|
|
|
self.first_poll = True
|
2020-11-15 21:55:59 +02:00
|
|
|
self.enabled = False
|
2021-04-10 00:31:54 +03:00
|
|
|
self.fb = FlightBook()
|
2020-05-03 23:27:39 +03:00
|
|
|
|
2021-04-25 03:00:52 +03:00
|
|
|
def matrix_start(self, bot):
|
|
|
|
super().matrix_start(bot)
|
|
|
|
self.add_module_aliases(bot, ['sar'])
|
|
|
|
|
2020-05-03 23:27:39 +03:00
|
|
|
async def matrix_poll(self, bot, pollcount):
|
2021-04-10 00:31:54 +03:00
|
|
|
if pollcount % (6 * 5) == 0: # Poll every 5 min
|
|
|
|
await self.poll_implementation(bot)
|
2020-05-03 23:27:39 +03:00
|
|
|
|
|
|
|
async def poll_implementation(self, bot):
|
|
|
|
for roomid in self.live_rooms:
|
|
|
|
station = self.station_rooms[roomid]
|
2021-04-10 00:31:54 +03:00
|
|
|
data = self.fb.get_flights(station)
|
2021-07-19 00:03:48 +03:00
|
|
|
if not data:
|
|
|
|
self.logger.warning(f"FLOG: Failed to get flights at {station}!")
|
|
|
|
return
|
2021-04-10 00:31:54 +03:00
|
|
|
flights = data['flights']
|
2021-04-10 23:22:11 +03:00
|
|
|
|
|
|
|
if len(flights) == 0 or (not station in self.logged_flights):
|
2021-04-14 23:02:50 +03:00
|
|
|
self.logged_flights[station] = []
|
|
|
|
#print('Reset flight count for station ' + station)
|
2021-04-10 23:22:11 +03:00
|
|
|
# else:
|
|
|
|
# print(f'Got {len(flights)} flights at {station}')
|
|
|
|
|
|
|
|
flightindex = 0
|
2020-05-03 23:27:39 +03:00
|
|
|
for flight in flights:
|
2021-04-10 00:31:54 +03:00
|
|
|
if flight["towing"]:
|
|
|
|
continue
|
|
|
|
if flight["stop"]:
|
2021-04-14 23:02:50 +03:00
|
|
|
if not flightindex in self.logged_flights[station]:
|
2021-04-10 23:22:11 +03:00
|
|
|
if not self.first_poll:
|
|
|
|
await bot.send_text(bot.get_room_by_id(roomid), self.fb.flight2string(flight, data))
|
2021-04-14 23:02:50 +03:00
|
|
|
self.logged_flights[station].append(flightindex)
|
|
|
|
# print(f'Logged flights at {station} now {self.logged_flights[station]}')
|
2021-04-10 23:22:11 +03:00
|
|
|
flightindex = flightindex + 1
|
2020-05-03 23:27:39 +03:00
|
|
|
self.first_poll = False
|
|
|
|
|
|
|
|
async def matrix_message(self, bot, room, event):
|
|
|
|
args = event.body.split()
|
2021-04-25 03:00:52 +03:00
|
|
|
if len(args) == 1 and args[0] == "!flog":
|
2020-05-03 23:27:39 +03:00
|
|
|
if room.room_id in self.station_rooms:
|
|
|
|
station = self.station_rooms[room.room_id]
|
2020-05-03 23:39:27 +03:00
|
|
|
await self.show_flog(bot, room, station)
|
2020-05-03 23:27:39 +03:00
|
|
|
else:
|
|
|
|
await bot.send_text(room, 'No OGN station set for this room - set it first.')
|
|
|
|
|
2021-04-25 03:00:52 +03:00
|
|
|
elif len(args) == 2 and args[0] == "!flog":
|
2020-05-03 23:27:39 +03:00
|
|
|
if args[1] == 'rmstation':
|
|
|
|
bot.must_be_admin(room, event)
|
|
|
|
del self.station_rooms[room.room_id]
|
|
|
|
self.live_rooms.remove(room.room_id)
|
|
|
|
await bot.send_text(room, f'Cleared OGN station for this room')
|
|
|
|
|
2020-05-03 23:39:27 +03:00
|
|
|
elif args[1] == 'status':
|
2021-04-14 23:02:50 +03:00
|
|
|
print(self.logged_flights)
|
2021-04-25 03:00:52 +03:00
|
|
|
print(self.fb.device_cache)
|
2020-05-03 23:27:39 +03:00
|
|
|
bot.must_be_admin(room, event)
|
2021-04-10 00:31:54 +03:00
|
|
|
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}')
|
2020-05-03 23:27:39 +03:00
|
|
|
|
2020-05-03 23:39:27 +03:00
|
|
|
elif args[1] == 'poll':
|
2020-05-03 23:27:39 +03:00
|
|
|
bot.must_be_admin(room, event)
|
|
|
|
await self.poll_implementation(bot)
|
|
|
|
|
2020-05-03 23:39:27 +03:00
|
|
|
elif args[1] == 'live':
|
2020-05-03 23:27:39 +03:00
|
|
|
bot.must_be_admin(room, event)
|
|
|
|
self.live_rooms.append(room.room_id)
|
|
|
|
bot.save_settings()
|
|
|
|
await bot.send_text(room, f'Sending live updates for station {self.station_rooms.get(room.room_id)} to this room')
|
|
|
|
|
2020-05-03 23:39:27 +03:00
|
|
|
elif args[1] == 'rmlive':
|
2020-05-03 23:27:39 +03:00
|
|
|
bot.must_be_admin(room, event)
|
|
|
|
self.live_rooms.remove(room.room_id)
|
|
|
|
bot.save_settings()
|
|
|
|
await bot.send_text(room, f'Not sending live updates for station {self.station_rooms.get(room.room_id)} to this room anymore')
|
|
|
|
|
2020-05-03 23:39:27 +03:00
|
|
|
else:
|
|
|
|
# Assume parameter is a station name
|
|
|
|
station = args[1]
|
|
|
|
await self.show_flog(bot, room, station)
|
2021-04-25 03:00:52 +03:00
|
|
|
elif len(args) == 2 and args[0] == "!sar":
|
|
|
|
registration = args[1]
|
|
|
|
address = self.fb.address_for_registration(registration)
|
2021-07-19 00:03:48 +03:00
|
|
|
if not address:
|
|
|
|
cn = args[1]
|
|
|
|
address = self.fb.address_for_cn(cn)
|
|
|
|
|
2021-04-25 03:00:52 +03:00
|
|
|
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}!')
|
2020-05-03 23:39:27 +03:00
|
|
|
|
2021-04-25 03:00:52 +03:00
|
|
|
elif len(args) == 3 and args[0] == "!flog":
|
2020-05-03 23:27:39 +03:00
|
|
|
if args[1] == 'station':
|
|
|
|
bot.must_be_admin(room, event)
|
|
|
|
|
|
|
|
station = args[2]
|
|
|
|
self.station_rooms[room.room_id] = station
|
|
|
|
self.logger.info(f'Station now for this room {self.station_rooms.get(room.room_id)}')
|
|
|
|
|
|
|
|
bot.save_settings()
|
|
|
|
await bot.send_text(room, f'Set OGN station {station} to this room')
|
|
|
|
|
2021-04-25 03:00:52 +03:00
|
|
|
|
|
|
|
def get_coords_for_address(self, address):
|
|
|
|
# https://flightbook.glidernet.org/api/live/address/~91DADF5B86
|
2021-07-19 00:03:48 +03:00
|
|
|
url = f'{self.fb.base_url}/live/address/{address}'
|
|
|
|
data = None
|
|
|
|
with requests.Session() as session:
|
|
|
|
response = session.get(url, headers={'Connection': 'close'}, verify=False)
|
|
|
|
data = response.json()
|
|
|
|
|
2021-04-25 03:00:52 +03:00
|
|
|
# print(json.dumps(data, sort_keys=True, indent=4))
|
|
|
|
return data
|
|
|
|
|
|
|
|
|
2021-04-10 00:31:54 +03:00
|
|
|
def text_flog(self, data, showtow):
|
|
|
|
out = ""
|
|
|
|
if len(data["flights"]) == 0:
|
|
|
|
out = f'No known flights today at {data["airfield"]["name"]}'
|
|
|
|
else:
|
|
|
|
out = f'Flights at {data["airfield"]["name"]} ({data["airfield"]["code"]}) {data["date"]}:' + "\n"
|
|
|
|
flights = data['flights']
|
|
|
|
for flight in flights:
|
|
|
|
if not showtow and flight["towing"]:
|
|
|
|
continue
|
|
|
|
out = out + self.fb.flight2string(flight, data) + "\n"
|
|
|
|
return out
|
2020-05-03 23:27:39 +03:00
|
|
|
|
2021-04-10 00:31:54 +03:00
|
|
|
def html_flog(self, data, showtow):
|
2020-05-03 23:39:27 +03:00
|
|
|
out = ""
|
2021-04-10 00:31:54 +03:00
|
|
|
if len(data["flights"]) == 0:
|
|
|
|
out = f'No known flights today at {data["airfield"]["name"]}'
|
2020-05-03 23:39:27 +03:00
|
|
|
else:
|
2021-04-10 00:31:54 +03:00
|
|
|
out = f'<b>✈ Flights at {data["airfield"]["name"]} ({data["airfield"]["code"]}) {data["date"]}:' + "</b>\n"
|
|
|
|
flights = data['flights']
|
|
|
|
out = out + "<ul>"
|
|
|
|
for flight in flights:
|
|
|
|
if not showtow and flight["towing"]:
|
|
|
|
continue
|
|
|
|
out = out + "<li>" + self.fb.flight2string(flight, data) + "</li>\n"
|
|
|
|
out = out + "</ul>"
|
|
|
|
return out
|
|
|
|
|
|
|
|
async def show_flog(self, bot, room, station):
|
|
|
|
data = self.fb.get_flights(station)
|
2021-07-19 00:03:48 +03:00
|
|
|
if data:
|
|
|
|
await bot.send_html(room, self.html_flog(data, False), self.text_flog(data, False))
|
|
|
|
else:
|
|
|
|
await bot.send_text(room, f"Failed to get flight log for {station}")
|
2020-05-03 23:39:27 +03:00
|
|
|
|
2020-05-03 23:27:39 +03:00
|
|
|
def get_settings(self):
|
|
|
|
data = super().get_settings()
|
|
|
|
data['station_rooms'] = self.station_rooms
|
|
|
|
data['live_rooms'] = self.live_rooms
|
|
|
|
return data
|
|
|
|
|
|
|
|
def set_settings(self, data):
|
|
|
|
super().set_settings(data)
|
|
|
|
if data.get('station_rooms'):
|
|
|
|
self.station_rooms = data['station_rooms']
|
|
|
|
if data.get('live_rooms'):
|
|
|
|
self.live_rooms = data['live_rooms']
|
|
|
|
|
|
|
|
def help(self):
|
|
|
|
return ('Open Glider Network Field Log')
|