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
|
|
|
---
|
|
|
|
Copyright (C) 2019 Abigail Gold, 0x5c
|
|
|
|
|
2019-12-08 15:35:58 -05:00
|
|
|
This file is part of discord-qrm2 and is released under the terms of the GNU
|
2019-10-15 17:37:13 -04:00
|
|
|
General Public License, version 2.
|
|
|
|
"""
|
|
|
|
from collections import OrderedDict
|
2019-10-16 17:23:56 -04:00
|
|
|
from datetime import datetime
|
2019-10-19 22:55:53 -04:00
|
|
|
from io import BytesIO
|
2019-10-15 17:37:13 -04:00
|
|
|
|
|
|
|
import discord
|
2019-10-18 11:13:24 -04:00
|
|
|
from discord.ext import commands, tasks
|
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
|
|
|
|
2019-12-06 01:19:42 -05:00
|
|
|
import common as cmn
|
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
|
2019-10-18 15:27:35 -04:00
|
|
|
self.session = aiohttp.ClientSession()
|
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)
|
|
|
|
async def _qrz_lookup(self, ctx: commands.Context, callsign: str):
|
|
|
|
'''Look up a callsign on [QRZ.com](https://www.qrz.com/).'''
|
|
|
|
if keys.qrz_user == '' or keys.qrz_pass == '':
|
|
|
|
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
|
|
|
|
2019-10-15 17:37:13 -04:00
|
|
|
try:
|
2019-10-18 15:27:35 -04:00
|
|
|
await qrz_test_session(self.key, self.session)
|
2019-10-16 17:23:56 -04:00
|
|
|
except ConnectionError:
|
2019-10-18 11:13:24 -04:00
|
|
|
await self.get_session()
|
2019-10-16 17:23:56 -04:00
|
|
|
|
2019-12-06 01:19:42 -05:00
|
|
|
url = f'http://xmldata.qrz.com/xml/current/?s={self.key};callsign={callsign}'
|
2019-10-18 15:27:35 -04:00
|
|
|
async with self.session.get(url) as resp:
|
|
|
|
if resp.status != 200:
|
|
|
|
raise ConnectionError(f'Unable to connect to QRZ (HTTP Error {resp.status})')
|
2019-10-19 22:55:53 -04:00
|
|
|
resp_xml = etree.parse(BytesIO(await resp.read())).getroot()
|
|
|
|
|
|
|
|
resp_xml_session = resp_xml.xpath('/x:QRZDatabase/x:Session',
|
2019-10-20 14:44:29 -04:00
|
|
|
namespaces={'x': 'http://xmldata.qrz.com'})
|
2019-10-19 22:55:53 -04:00
|
|
|
resp_session = {el.tag.split('}')[1]: el.text for el in resp_xml_session[0].getiterator()}
|
2019-10-15 17:37:13 -04:00
|
|
|
if 'Error' in resp_session:
|
2019-10-16 17:45:11 -04:00
|
|
|
if 'Session Timeout' in resp_session['Error']:
|
2019-10-18 11:13:24 -04:00
|
|
|
await self.get_session()
|
2019-12-06 01:19:42 -05:00
|
|
|
await self._qrz_lookup(ctx, callsign)
|
2019-10-20 14:44:29 -04:00
|
|
|
return
|
|
|
|
if 'Not found' in resp_session['Error']:
|
2019-12-06 01:19:42 -05:00
|
|
|
embed = discord.Embed(title=f"QRZ Data for {callsign.upper()}",
|
|
|
|
colour=cmn.colours.bad,
|
2019-10-20 14:44:29 -04:00
|
|
|
description='No data found!',
|
|
|
|
timestamp=datetime.utcnow())
|
|
|
|
embed.set_footer(text=ctx.author.name,
|
|
|
|
icon_url=str(ctx.author.avatar_url))
|
|
|
|
await ctx.send(embed=embed)
|
|
|
|
return
|
2019-10-15 17:37:13 -04:00
|
|
|
raise ValueError(resp_session['Error'])
|
|
|
|
|
2019-10-20 14:44:29 -04:00
|
|
|
resp_xml_data = resp_xml.xpath('/x:QRZDatabase/x:Callsign',
|
2019-10-27 10:49:19 -04:00
|
|
|
namespaces={'x': 'http://xmldata.qrz.com'})
|
2019-10-20 14:44:29 -04:00
|
|
|
resp_data = {el.tag.split('}')[1]: el.text for el in resp_xml_data[0].getiterator()}
|
|
|
|
|
2019-10-15 17:37:13 -04:00
|
|
|
embed = discord.Embed(title=f"QRZ Data for {resp_data['call']}",
|
2019-12-06 01:19:42 -05:00
|
|
|
colour=cmn.colours.good,
|
2019-10-15 17:37:13 -04:00
|
|
|
url=f'http://www.qrz.com/db/{resp_data["call"]}',
|
|
|
|
timestamp=datetime.utcnow())
|
|
|
|
embed.set_footer(text=ctx.author.name,
|
|
|
|
icon_url=str(ctx.author.avatar_url))
|
|
|
|
if 'image' in resp_data:
|
2019-11-11 21:20:25 -05:00
|
|
|
embed.set_thumbnail(url=resp_data['image'])
|
2019-10-15 17:37:13 -04:00
|
|
|
|
2019-10-17 22:29:53 -04:00
|
|
|
data = qrz_process_info(resp_data)
|
2019-10-16 17:23:56 -04:00
|
|
|
|
2019-10-15 17:37:13 -04:00
|
|
|
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-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)
|
2019-10-18 11:26:06 -04:00
|
|
|
with open('data/qrz_session', 'w') as qrz_file:
|
|
|
|
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:
|
|
|
|
with open('data/qrz_session') as qrz_file:
|
|
|
|
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):
|
2019-12-08 15:35:58 -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:
|
|
|
|
raise ConnectionError(f'Unable to connect to QRZ (HTTP Error {resp.status})')
|
2019-10-19 22:55:53 -04:00
|
|
|
resp_xml = etree.parse(BytesIO(await resp.read())).getroot()
|
2019-10-15 17:37:13 -04:00
|
|
|
|
2019-10-19 22:55:53 -04:00
|
|
|
resp_xml_session = resp_xml.xpath('/x:QRZDatabase/x:Session',
|
2019-10-27 10:49:19 -04:00
|
|
|
namespaces={'x': 'http://xmldata.qrz.com'})
|
2019-10-19 22:55:53 -04:00
|
|
|
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':
|
2019-10-15 17:37:13 -04:00
|
|
|
raise ConnectionError('Invalid QRZ Subscription')
|
2019-10-19 22:55:53 -04:00
|
|
|
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):
|
2019-10-16 17:23:56 -04: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:
|
|
|
|
raise ConnectionError(f'Unable to connect to QRZ (HTTP Error {resp.status})')
|
2019-10-19 22:55:53 -04:00
|
|
|
resp_xml = etree.parse(BytesIO(await resp.read())).getroot()
|
2019-10-16 17:23:56 -04:00
|
|
|
|
2019-10-19 22:55:53 -04:00
|
|
|
resp_xml_session = resp_xml.xpath('/x:QRZDatabase/x:Session',
|
2019-10-27 10:49:19 -04:00
|
|
|
namespaces={'x': 'http://xmldata.qrz.com'})
|
2019-10-19 22:55:53 -04:00
|
|
|
resp_session = {el.tag.split('}')[1]: el.text for el in resp_xml_session[0].getiterator()}
|
2019-10-16 17:23:56 -04:00
|
|
|
if 'Error' in resp_session:
|
|
|
|
raise ConnectionError(resp_session['Error'])
|
|
|
|
|
|
|
|
|
2019-10-17 22:29:53 -04:00
|
|
|
def qrz_process_info(data: dict):
|
2019-10-16 17:23:56 -04:00
|
|
|
if 'name' in data:
|
|
|
|
if 'fname' in data:
|
|
|
|
name = data['fname'] + ' ' + data['name']
|
|
|
|
else:
|
|
|
|
name = data['name']
|
|
|
|
else:
|
|
|
|
name = None
|
|
|
|
if 'state' in data:
|
|
|
|
state = f', {data["state"]}'
|
|
|
|
else:
|
|
|
|
state = ''
|
|
|
|
address = data.get('addr1', '') + '\n' + data.get('addr2', '') + \
|
|
|
|
state + ' ' + data.get('zip', '')
|
|
|
|
if 'eqsl' in data:
|
|
|
|
eqsl = 'Yes' if data['eqsl'] == 1 else 'No'
|
|
|
|
else:
|
|
|
|
eqsl = 'Unknown'
|
|
|
|
if 'mqsl' in data:
|
|
|
|
mqsl = 'Yes' if data['mqsl'] == 1 else 'No'
|
|
|
|
else:
|
|
|
|
mqsl = 'Unknown'
|
|
|
|
if 'lotw' in data:
|
|
|
|
lotw = 'Yes' if data['lotw'] == 1 else 'No'
|
|
|
|
else:
|
|
|
|
lotw = 'Unknown'
|
|
|
|
|
|
|
|
return OrderedDict([('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)),
|
|
|
|
('eQSL?', eqsl),
|
|
|
|
('Paper QSL?', mqsl),
|
|
|
|
('LotW?', lotw),
|
|
|
|
('QSL Info', data.get('qslmgr', None)),
|
|
|
|
('CQ Zone', data.get('cqzone', None)),
|
|
|
|
('ITU Zone', data.get('ituzone', None)),
|
|
|
|
('IOTA Designator', data.get('iota', None)),
|
|
|
|
('Born', data.get('born', None)),
|
|
|
|
])
|
|
|
|
|
|
|
|
|
2019-10-15 17:37:13 -04:00
|
|
|
def setup(bot):
|
|
|
|
bot.add_cog(QRZCog(bot))
|