2019-10-15 17:37:13 -04:00
|
|
|
"""
|
2019-12-07 17:26:55 -05:00
|
|
|
QRZ extension for qrm
|
2019-10-15 17:37:13 -04:00
|
|
|
---
|
2020-01-06 23:27:48 -05:00
|
|
|
Copyright (C) 2019-2020 Abigail Gold, 0x5c
|
2019-10-15 17:37:13 -04:00
|
|
|
|
2020-02-15 06:27:48 -05:00
|
|
|
This file is part of qrm2 and is released under the terms of
|
2020-01-31 06:50:50 -05:00
|
|
|
the GNU General Public License, version 2.
|
2019-10-15 17:37:13 -04:00
|
|
|
"""
|
2020-01-31 06:50:50 -05:00
|
|
|
|
|
|
|
|
2019-10-19 22:55:53 -04:00
|
|
|
from io import BytesIO
|
2019-10-15 17:37:13 -04:00
|
|
|
|
|
|
|
import aiohttp
|
2019-10-19 22:55:53 -04:00
|
|
|
from lxml import etree
|
2019-10-15 17:37:13 -04:00
|
|
|
|
2020-02-05 07:09:08 -05:00
|
|
|
from discord.ext import commands, tasks
|
|
|
|
|
2019-12-06 01:19:42 -05:00
|
|
|
import common as cmn
|
2020-02-05 07:09:08 -05:00
|
|
|
|
2019-12-07 17:13:06 -05:00
|
|
|
import data.keys as keys
|
2019-12-06 01:19:42 -05:00
|
|
|
|
2019-10-15 17:37:13 -04:00
|
|
|
|
|
|
|
class QRZCog(commands.Cog):
|
|
|
|
def __init__(self, bot: commands.Bot):
|
|
|
|
self.bot = bot
|
2020-01-07 05:36:09 -05:00
|
|
|
self.session = aiohttp.ClientSession(connector=bot.qrm.connector)
|
2019-10-18 11:13:24 -04:00
|
|
|
self._qrz_session_init.start()
|
2019-10-15 17:37:13 -04:00
|
|
|
|
2019-12-06 01:19:42 -05:00
|
|
|
@commands.command(name="call", aliases=["qrz"], category=cmn.cat.lookup)
|
2020-01-04 14:40:08 -05:00
|
|
|
async def _qrz_lookup(self, ctx: commands.Context, callsign: str, *flags):
|
2020-02-15 04:59:25 -05:00
|
|
|
"""Looks up a callsign on [QRZ.com](https://www.qrz.com/). Add `--link` to only link the QRZ page."""
|
2020-01-04 14:40:08 -05:00
|
|
|
flags = [f.lower() for f in flags]
|
|
|
|
|
2020-01-30 06:15:42 -05:00
|
|
|
if keys.qrz_user == "" or keys.qrz_pass == "" or "--link" in flags:
|
|
|
|
await ctx.send(f"http://qrz.com/db/{callsign}")
|
2019-10-15 17:37:13 -04:00
|
|
|
return
|
2019-10-16 17:23:56 -04:00
|
|
|
|
2020-02-25 20:36:21 -05:00
|
|
|
async with ctx.typing():
|
|
|
|
try:
|
|
|
|
await qrz_test_session(self.key, self.session)
|
|
|
|
except ConnectionError:
|
2019-10-18 11:13:24 -04:00
|
|
|
await self.get_session()
|
2020-02-25 20:36:21 -05:00
|
|
|
|
|
|
|
url = f"http://xmldata.qrz.com/xml/current/?s={self.key};callsign={callsign}"
|
|
|
|
async with self.session.get(url) as resp:
|
|
|
|
if resp.status != 200:
|
|
|
|
raise ConnectionError(f"Unable to connect to QRZ (HTTP Error {resp.status})")
|
|
|
|
with BytesIO(await resp.read()) as resp_file:
|
|
|
|
resp_xml = etree.parse(resp_file).getroot()
|
|
|
|
|
|
|
|
resp_xml_session = resp_xml.xpath("/x:QRZDatabase/x:Session", namespaces={"x": "http://xmldata.qrz.com"})
|
|
|
|
resp_session = {el.tag.split("}")[1]: el.text for el in resp_xml_session[0].getiterator()}
|
|
|
|
if "Error" in resp_session:
|
|
|
|
if "Session Timeout" in resp_session["Error"]:
|
|
|
|
await self.get_session()
|
|
|
|
await self._qrz_lookup(ctx, callsign)
|
|
|
|
return
|
|
|
|
if "Not found" in resp_session["Error"]:
|
|
|
|
embed = cmn.embed_factory(ctx)
|
|
|
|
embed.title = f"QRZ Data for {callsign.upper()}"
|
|
|
|
embed.colour = cmn.colours.bad
|
|
|
|
embed.description = "No data found!"
|
|
|
|
await ctx.send(embed=embed)
|
|
|
|
return
|
|
|
|
raise ValueError(resp_session["Error"])
|
|
|
|
|
|
|
|
resp_xml_data = resp_xml.xpath("/x:QRZDatabase/x:Callsign", namespaces={"x": "http://xmldata.qrz.com"})
|
|
|
|
resp_data = {el.tag.split("}")[1]: el.text for el in resp_xml_data[0].getiterator()}
|
|
|
|
|
|
|
|
embed = cmn.embed_factory(ctx)
|
|
|
|
embed.title = f"QRZ Data for {resp_data['call']}"
|
|
|
|
embed.colour = cmn.colours.good
|
|
|
|
embed.url = f"http://www.qrz.com/db/{resp_data['call']}"
|
|
|
|
if "image" in resp_data:
|
|
|
|
embed.set_thumbnail(url=resp_data["image"])
|
|
|
|
|
|
|
|
data = qrz_process_info(resp_data)
|
|
|
|
|
|
|
|
for title, val in data.items():
|
|
|
|
if val is not None:
|
|
|
|
embed.add_field(name=title, value=val, inline=True)
|
|
|
|
await ctx.send(embed=embed)
|
2019-10-15 17:37:13 -04:00
|
|
|
|
2019-10-18 11:13:24 -04:00
|
|
|
async def get_session(self):
|
2019-10-18 11:26:06 -04:00
|
|
|
"""Session creation and caching."""
|
2019-12-06 01:19:42 -05:00
|
|
|
self.key = await qrz_login(keys.qrz_user, keys.qrz_pass, self.session)
|
2020-01-30 06:15:42 -05:00
|
|
|
with open("data/qrz_session", "w") as qrz_file:
|
2019-10-18 11:26:06 -04:00
|
|
|
qrz_file.write(self.key)
|
|
|
|
|
2019-10-18 11:37:24 -04:00
|
|
|
@tasks.loop(count=1)
|
2019-10-18 11:26:06 -04:00
|
|
|
async def _qrz_session_init(self):
|
|
|
|
"""Helper task to allow obtaining a session at cog instantiation."""
|
2019-10-18 11:13:24 -04:00
|
|
|
try:
|
2020-01-30 06:15:42 -05:00
|
|
|
with open("data/qrz_session") as qrz_file:
|
2019-10-18 11:13:24 -04:00
|
|
|
self.key = qrz_file.readline().strip()
|
2019-10-18 15:27:35 -04:00
|
|
|
await qrz_test_session(self.key, self.session)
|
2019-10-18 11:26:06 -04:00
|
|
|
except (FileNotFoundError, ConnectionError):
|
2019-10-18 12:45:26 -04:00
|
|
|
await self.get_session()
|
2019-10-18 11:13:24 -04:00
|
|
|
|
2019-10-15 17:37:13 -04:00
|
|
|
|
2019-10-18 15:27:35 -04:00
|
|
|
async def qrz_login(user: str, passwd: str, session: aiohttp.ClientSession):
|
2020-01-30 06:15:42 -05:00
|
|
|
url = f"http://xmldata.qrz.com/xml/current/?username={user};password={passwd};agent=discord-qrm2"
|
2019-10-18 15:27:35 -04:00
|
|
|
async with session.get(url) as resp:
|
|
|
|
if resp.status != 200:
|
2020-01-30 06:15:42 -05:00
|
|
|
raise ConnectionError(f"Unable to connect to QRZ (HTTP Error {resp.status})")
|
2019-12-31 03:18:30 -05:00
|
|
|
with BytesIO(await resp.read()) as resp_file:
|
|
|
|
resp_xml = etree.parse(resp_file).getroot()
|
2019-10-15 17:37:13 -04:00
|
|
|
|
2020-01-30 06:15:42 -05:00
|
|
|
resp_xml_session = resp_xml.xpath("/x:QRZDatabase/x:Session", namespaces={"x": "http://xmldata.qrz.com"})
|
|
|
|
resp_session = {el.tag.split("}")[1]: el.text for el in resp_xml_session[0].getiterator()}
|
|
|
|
if "Error" in resp_session:
|
|
|
|
raise ConnectionError(resp_session["Error"])
|
|
|
|
if resp_session["SubExp"] == "non-subscriber":
|
|
|
|
raise ConnectionError("Invalid QRZ Subscription")
|
|
|
|
return resp_session["Key"]
|
2019-10-15 17:37:13 -04:00
|
|
|
|
|
|
|
|
2019-10-18 15:27:35 -04:00
|
|
|
async def qrz_test_session(key: str, session: aiohttp.ClientSession):
|
2020-01-30 06:15:42 -05:00
|
|
|
url = f"http://xmldata.qrz.com/xml/current/?s={key}"
|
2019-10-18 15:27:35 -04:00
|
|
|
async with session.get(url) as resp:
|
|
|
|
if resp.status != 200:
|
2020-01-30 06:15:42 -05:00
|
|
|
raise ConnectionError(f"Unable to connect to QRZ (HTTP Error {resp.status})")
|
2019-12-31 03:18:30 -05:00
|
|
|
with BytesIO(await resp.read()) as resp_file:
|
|
|
|
resp_xml = etree.parse(resp_file).getroot()
|
2019-10-16 17:23:56 -04:00
|
|
|
|
2020-01-30 06:15:42 -05:00
|
|
|
resp_xml_session = resp_xml.xpath("/x:QRZDatabase/x:Session", namespaces={"x": "http://xmldata.qrz.com"})
|
|
|
|
resp_session = {el.tag.split("}")[1]: el.text for el in resp_xml_session[0].getiterator()}
|
|
|
|
if "Error" in resp_session:
|
|
|
|
raise ConnectionError(resp_session["Error"])
|
2019-10-16 17:23:56 -04:00
|
|
|
|
|
|
|
|
2019-10-17 22:29:53 -04:00
|
|
|
def qrz_process_info(data: dict):
|
2020-01-30 06:15:42 -05:00
|
|
|
if "name" in data:
|
|
|
|
if "fname" in data:
|
|
|
|
name = data["fname"] + " " + data["name"]
|
2019-10-16 17:23:56 -04:00
|
|
|
else:
|
2020-01-30 06:15:42 -05:00
|
|
|
name = data["name"]
|
2019-10-16 17:23:56 -04:00
|
|
|
else:
|
|
|
|
name = None
|
2020-01-30 06:15:42 -05:00
|
|
|
if "state" in data:
|
|
|
|
state = f", {data['state']}"
|
2019-10-16 17:23:56 -04:00
|
|
|
else:
|
2020-01-30 06:15:42 -05:00
|
|
|
state = ""
|
|
|
|
address = data.get("addr1", "") + "\n" + data.get("addr2", "") + state + " " + data.get("zip", "")
|
2019-12-23 11:47:37 -05:00
|
|
|
address = address.strip()
|
2020-01-30 06:15:42 -05:00
|
|
|
if address == "":
|
2019-12-23 11:47:37 -05:00
|
|
|
address = None
|
2020-01-30 06:15:42 -05:00
|
|
|
if "eqsl" in data:
|
|
|
|
eqsl = "Yes" if data["eqsl"] == 1 else "No"
|
2019-10-16 17:23:56 -04:00
|
|
|
else:
|
2020-01-30 06:15:42 -05:00
|
|
|
eqsl = "Unknown"
|
|
|
|
if "mqsl" in data:
|
|
|
|
mqsl = "Yes" if data["mqsl"] == 1 else "No"
|
2019-10-16 17:23:56 -04:00
|
|
|
else:
|
2020-01-30 06:15:42 -05:00
|
|
|
mqsl = "Unknown"
|
|
|
|
if "lotw" in data:
|
|
|
|
lotw = "Yes" if data["lotw"] == 1 else "No"
|
2019-10-16 17:23:56 -04:00
|
|
|
else:
|
2020-01-30 06:15:42 -05:00
|
|
|
lotw = "Unknown"
|
|
|
|
|
2020-03-30 18:54:33 -04:00
|
|
|
return {"Name": name,
|
|
|
|
"Country": data.get("country", None),
|
|
|
|
"Address": address,
|
|
|
|
"Grid Square": data.get("grid", None),
|
|
|
|
"County": data.get("county", None),
|
|
|
|
"CQ Zone": data.get("cqzone", None),
|
|
|
|
"ITU Zone": data.get("ituzone", None),
|
|
|
|
"IOTA Designator": data.get("iota", None),
|
|
|
|
"Expires": data.get("expdate", None),
|
|
|
|
"Aliases": data.get("aliases", None),
|
|
|
|
"Previous Callsign": data.get("p_call", None),
|
|
|
|
"License Class": data.get("class", None),
|
|
|
|
"Trustee": data.get("trustee", None),
|
|
|
|
"eQSL?": eqsl,
|
|
|
|
"Paper QSL?": mqsl,
|
|
|
|
"LotW?": lotw,
|
|
|
|
"QSL Info": data.get("qslmgr", None),
|
|
|
|
"Born": data.get("born", None)}
|
2019-10-16 17:23:56 -04:00
|
|
|
|
|
|
|
|
2019-10-15 17:37:13 -04:00
|
|
|
def setup(bot):
|
|
|
|
bot.add_cog(QRZCog(bot))
|