Normalised string quote type

- "" instead of '', except for dictionary lookups in f-strings

Element of #142
New year lint removal
This commit is contained in:
0x5c 2020-01-30 06:15:42 -05:00
parent 29e75c38e1
commit 29d0440d3d
No known key found for this signature in database
GPG Key ID: 82039FC95E3FE970
18 changed files with 491 additions and 491 deletions

View File

@ -34,24 +34,24 @@ colours = SimpleNamespace(good=0x43B581,
neutral=0x7289DA,
bad=0xF04747)
# meow
cat = SimpleNamespace(lookup='Information Lookup',
fun='Fun',
maps='Mapping',
ref='Reference',
study='Exam Study',
weather='Land and Space Weather',
admin='Bot Control')
cat = SimpleNamespace(lookup="Information Lookup",
fun="Fun",
maps="Mapping",
ref="Reference",
study="Exam Study",
weather="Land and Space Weather",
admin="Bot Control")
emojis = SimpleNamespace(check_mark='',
x='',
warning='⚠️',
question='',
no_entry='',
bangbang='‼️',
a='🇦',
b='🇧',
c='🇨',
d='🇩')
emojis = SimpleNamespace(check_mark="",
x="",
warning="⚠️",
question="",
no_entry="",
bangbang="‼️",
a="🇦",
b="🇧",
c="🇨",
d="🇩")
paths = SimpleNamespace(data=Path("./data/"),
resources=Path("./resources/"),
@ -117,7 +117,7 @@ class GlobalChannelConverter(commands.IDConverter):
async def convert(self, ctx: commands.Context, argument: str):
bot = ctx.bot
guild = ctx.guild
match = self._get_id_match(argument) or re.match(r'<#([0-9]+)>$', argument)
match = self._get_id_match(argument) or re.match(r"<#([0-9]+)>$", argument)
result = None
if match is None:
# not a mention/ID
@ -150,7 +150,7 @@ def error_embed_factory(ctx: commands.Context, exception: Exception, debug_mode:
fmtd_ex = traceback.format_exception_only(exception.__class__, exception)
embed = embed_factory(ctx)
embed.title = "⚠️ Error"
embed.description = "```\n" + '\n'.join(fmtd_ex) + "```"
embed.description = "```\n" + "\n".join(fmtd_ex) + "```"
embed.colour = colours.bad
return embed

View File

@ -29,16 +29,16 @@ class AE7QCog(commands.Cog):
@commands.group(name="ae7q", aliases=["ae"], category=cmn.cat.lookup)
async def _ae7q_lookup(self, ctx: commands.Context):
'''Look up a callsign, FRN, or Licensee ID on [ae7q.com](http://ae7q.com/).'''
"""Look up a callsign, FRN, or Licensee ID on [ae7q.com](http://ae7q.com/)."""
if ctx.invoked_subcommand is None:
await ctx.send_help(ctx.command)
@_ae7q_lookup.command(name="call", aliases=["c"], category=cmn.cat.lookup)
async def _ae7q_call(self, ctx: commands.Context, callsign: str):
'''Look up the history of a callsign on [ae7q.com](http://ae7q.com/).'''
"""Look up the history of a callsign on [ae7q.com](http://ae7q.com/)."""
with ctx.typing():
callsign = callsign.upper()
desc = ''
desc = ""
base_url = "http://ae7q.com/query/data/CallHistory.php?CALL="
embed = cmn.embed_factory(ctx)
@ -56,20 +56,20 @@ class AE7QCog(commands.Cog):
if len(table[0]) == 1:
for row in table:
desc += " ".join(row.getText().split())
desc += '\n'
desc = desc.replace(callsign, f'`{callsign}`')
desc += "\n"
desc = desc.replace(callsign, f"`{callsign}`")
table = tables[1]
table_headers = table[0].find_all("th")
first_header = ''.join(table_headers[0].strings) if len(table_headers) > 0 else None
first_header = "".join(table_headers[0].strings) if len(table_headers) > 0 else None
# catch if the wrong table was selected
if first_header is None or first_header != 'Entity Name':
if first_header is None or first_header != "Entity Name":
embed.title = f"AE7Q History for {callsign}"
embed.colour = cmn.colours.bad
embed.url = base_url + callsign
embed.description = desc
embed.description += f'\nNo records found for `{callsign}`'
embed.description += f"\nNo records found for `{callsign}`"
await ctx.send(embed=embed)
return
@ -82,18 +82,18 @@ class AE7QCog(commands.Cog):
# add the first three rows of the table to the embed
for row in table[0:3]:
header = f'**{row[0]}** ({row[1]})' # **Name** (Applicant Type)
body = (f'Class: *{row[2]}*\n'
f'Region: *{row[3]}*\n'
f'Status: *{row[4]}*\n'
f'Granted: *{row[5]}*\n'
f'Effective: *{row[6]}*\n'
f'Cancelled: *{row[7]}*\n'
f'Expires: *{row[8]}*')
header = f"**{row[0]}** ({row[1]})" # **Name** (Applicant Type)
body = (f"Class: *{row[2]}*\n"
f"Region: *{row[3]}*\n"
f"Status: *{row[4]}*\n"
f"Granted: *{row[5]}*\n"
f"Effective: *{row[6]}*\n"
f"Cancelled: *{row[7]}*\n"
f"Expires: *{row[8]}*")
embed.add_field(name=header, value=body, inline=False)
if len(table) > 3:
desc += f'\nRecords 1 to 3 of {len(table)}. See ae7q.com for more...'
desc += f"\nRecords 1 to 3 of {len(table)}. See ae7q.com for more..."
embed.description = desc
@ -101,10 +101,10 @@ class AE7QCog(commands.Cog):
@_ae7q_lookup.command(name="trustee", aliases=["t"], category=cmn.cat.lookup)
async def _ae7q_trustee(self, ctx: commands.Context, callsign: str):
'''Look up the licenses for which a licensee is trustee on [ae7q.com](http://ae7q.com/).'''
"""Look up the licenses for which a licensee is trustee on [ae7q.com](http://ae7q.com/)."""
with ctx.typing():
callsign = callsign.upper()
desc = ''
desc = ""
base_url = "http://ae7q.com/query/data/CallHistory.php?CALL="
embed = cmn.embed_factory(ctx)
@ -123,12 +123,12 @@ class AE7QCog(commands.Cog):
embed.colour = cmn.colours.bad
embed.url = base_url + callsign
embed.description = desc
embed.description += f'\nNo records found for `{callsign}`'
embed.description += f"\nNo records found for `{callsign}`"
await ctx.send(embed=embed)
return
table_headers = table[0].find_all("th")
first_header = ''.join(table_headers[0].strings) if len(table_headers) > 0 else None
first_header = "".join(table_headers[0].strings) if len(table_headers) > 0 else None
# catch if the wrong table was selected
if first_header is None or not first_header.startswith("With"):
@ -136,7 +136,7 @@ class AE7QCog(commands.Cog):
embed.colour = cmn.colours.bad
embed.url = base_url + callsign
embed.description = desc
embed.description += f'\nNo records found for `{callsign}`'
embed.description += f"\nNo records found for `{callsign}`"
await ctx.send(embed=embed)
return
@ -149,18 +149,18 @@ class AE7QCog(commands.Cog):
# add the first three rows of the table to the embed
for row in table[0:3]:
header = f'**{row[0]}** ({row[3]})' # **Name** (Applicant Type)
body = (f'Name: *{row[2]}*\n'
f'Region: *{row[1]}*\n'
f'Status: *{row[4]}*\n'
f'Granted: *{row[5]}*\n'
f'Effective: *{row[6]}*\n'
f'Cancelled: *{row[7]}*\n'
f'Expires: *{row[8]}*')
header = f"**{row[0]}** ({row[3]})" # **Name** (Applicant Type)
body = (f"Name: *{row[2]}*\n"
f"Region: *{row[1]}*\n"
f"Status: *{row[4]}*\n"
f"Granted: *{row[5]}*\n"
f"Effective: *{row[6]}*\n"
f"Cancelled: *{row[7]}*\n"
f"Expires: *{row[8]}*")
embed.add_field(name=header, value=body, inline=False)
if len(table) > 3:
desc += f'\nRecords 1 to 3 of {len(table)}. See ae7q.com for more...'
desc += f"\nRecords 1 to 3 of {len(table)}. See ae7q.com for more..."
embed.description = desc
@ -168,11 +168,11 @@ class AE7QCog(commands.Cog):
@_ae7q_lookup.command(name="applications", aliases=["a"], category=cmn.cat.lookup)
async def _ae7q_applications(self, ctx: commands.Context, callsign: str):
'''Look up the application history for a callsign on [ae7q.com](http://ae7q.com/).'''
"""Look up the application history for a callsign on [ae7q.com](http://ae7q.com/)."""
"""
with ctx.typing():
callsign = callsign.upper()
desc = ''
desc = ""
base_url = "http://ae7q.com/query/data/CallHistory.php?CALL="
embed = cmn.embed_factory(ctx)
@ -190,14 +190,14 @@ class AE7QCog(commands.Cog):
if len(table[0]) == 1:
for row in table:
desc += " ".join(row.getText().split())
desc += '\n'
desc = desc.replace(callsign, f'`{callsign}`')
desc += "\n"
desc = desc.replace(callsign, f"`{callsign}`")
# select the last table to get applications
table = tables[-1]
table_headers = table[0].find_all("th")
first_header = ''.join(table_headers[0].strings) if len(table_headers) > 0 else None
first_header = "".join(table_headers[0].strings) if len(table_headers) > 0 else None
# catch if the wrong table was selected
if first_header is None or not first_header.startswith("Receipt"):
@ -205,7 +205,7 @@ class AE7QCog(commands.Cog):
embed.colour = cmn.colours.bad
embed.url = base_url + callsign
embed.description = desc
embed.description += f'\nNo records found for `{callsign}`'
embed.description += f"\nNo records found for `{callsign}`"
await ctx.send(embed=embed)
return
@ -218,16 +218,16 @@ class AE7QCog(commands.Cog):
# add the first three rows of the table to the embed
for row in table[0:3]:
header = f'**{row[1]}** ({row[3]})' # **Name** (Callsign)
body = (f'Received: *{row[0]}*\n'
f'Region: *{row[2]}*\n'
f'Purpose: *{row[5]}*\n'
f'Last Action: *{row[7]}*\n'
f'Application Status: *{row[8]}*\n')
header = f"**{row[1]}** ({row[3]})" # **Name** (Callsign)
body = (f"Received: *{row[0]}*\n"
f"Region: *{row[2]}*\n"
f"Purpose: *{row[5]}*\n"
f"Last Action: *{row[7]}*\n"
f"Application Status: *{row[8]}*\n")
embed.add_field(name=header, value=body, inline=False)
if len(table) > 3:
desc += f'\nRecords 1 to 3 of {len(table)}. See ae7q.com for more...'
desc += f"\nRecords 1 to 3 of {len(table)}. See ae7q.com for more..."
embed.description = desc
@ -238,7 +238,7 @@ class AE7QCog(commands.Cog):
@_ae7q_lookup.command(name="frn", aliases=["f"], category=cmn.cat.lookup)
async def _ae7q_frn(self, ctx: commands.Context, frn: str):
'''Look up the history of an FRN on [ae7q.com](http://ae7q.com/).'''
"""Look up the history of an FRN on [ae7q.com](http://ae7q.com/)."""
"""
NOTES:
- 2 tables: callsign history and application history
@ -260,21 +260,21 @@ class AE7QCog(commands.Cog):
embed.title = f"AE7Q History for FRN {frn}"
embed.colour = cmn.colours.bad
embed.url = base_url + frn
embed.description = f'No records found for FRN `{frn}`'
embed.description = f"No records found for FRN `{frn}`"
await ctx.send(embed=embed)
return
table = tables[0]
table_headers = table[0].find_all("th")
first_header = ''.join(table_headers[0].strings) if len(table_headers) > 0 else None
first_header = "".join(table_headers[0].strings) if len(table_headers) > 0 else None
# catch if the wrong table was selected
if first_header is None or not first_header.startswith('With Licensee'):
if first_header is None or not first_header.startswith("With Licensee"):
embed.title = f"AE7Q History for FRN {frn}"
embed.colour = cmn.colours.bad
embed.url = base_url + frn
embed.description = f'No records found for FRN `{frn}`'
embed.description = f"No records found for FRN `{frn}`"
await ctx.send(embed=embed)
return
@ -287,25 +287,25 @@ class AE7QCog(commands.Cog):
# add the first three rows of the table to the embed
for row in table[0:3]:
header = f'**{row[0]}** ({row[3]})' # **Callsign** (Applicant Type)
body = (f'Name: *{row[2]}*\n'
f'Class: *{row[4]}*\n'
f'Region: *{row[1]}*\n'
f'Status: *{row[5]}*\n'
f'Granted: *{row[6]}*\n'
f'Effective: *{row[7]}*\n'
f'Cancelled: *{row[8]}*\n'
f'Expires: *{row[9]}*')
header = f"**{row[0]}** ({row[3]})" # **Callsign** (Applicant Type)
body = (f"Name: *{row[2]}*\n"
f"Class: *{row[4]}*\n"
f"Region: *{row[1]}*\n"
f"Status: *{row[5]}*\n"
f"Granted: *{row[6]}*\n"
f"Effective: *{row[7]}*\n"
f"Cancelled: *{row[8]}*\n"
f"Expires: *{row[9]}*")
embed.add_field(name=header, value=body, inline=False)
if len(table) > 3:
embed.description = f'Records 1 to 3 of {len(table)}. See ae7q.com for more...'
embed.description = f"Records 1 to 3 of {len(table)}. See ae7q.com for more..."
await ctx.send(embed=embed)
@_ae7q_lookup.command(name="licensee", aliases=["l"], category=cmn.cat.lookup)
async def _ae7q_licensee(self, ctx: commands.Context, licensee_id: str):
'''Look up the history of a licensee ID on [ae7q.com](http://ae7q.com/).'''
"""Look up the history of a licensee ID on [ae7q.com](http://ae7q.com/)."""
with ctx.typing():
licensee_id = licensee_id.upper()
base_url = "http://ae7q.com/query/data/LicenseeIdHistory.php?ID="
@ -323,21 +323,21 @@ class AE7QCog(commands.Cog):
embed.title = f"AE7Q History for Licensee {licensee_id}"
embed.colour = cmn.colours.bad
embed.url = base_url + licensee_id
embed.description = f'No records found for Licensee `{licensee_id}`'
embed.description = f"No records found for Licensee `{licensee_id}`"
await ctx.send(embed=embed)
return
table = tables[0]
table_headers = table[0].find_all("th")
first_header = ''.join(table_headers[0].strings) if len(table_headers) > 0 else None
first_header = "".join(table_headers[0].strings) if len(table_headers) > 0 else None
# catch if the wrong table was selected
if first_header is None or not first_header.startswith('With FCC'):
if first_header is None or not first_header.startswith("With FCC"):
embed.title = f"AE7Q History for Licensee {licensee_id}"
embed.colour = cmn.colours.bad
embed.url = base_url + licensee_id
embed.description = f'No records found for Licensee `{licensee_id}`'
embed.description = f"No records found for Licensee `{licensee_id}`"
await ctx.send(embed=embed)
return
@ -350,19 +350,19 @@ class AE7QCog(commands.Cog):
# add the first three rows of the table to the embed
for row in table[0:3]:
header = f'**{row[0]}** ({row[3]})' # **Callsign** (Applicant Type)
body = (f'Name: *{row[2]}*\n'
f'Class: *{row[4]}*\n'
f'Region: *{row[1]}*\n'
f'Status: *{row[5]}*\n'
f'Granted: *{row[6]}*\n'
f'Effective: *{row[7]}*\n'
f'Cancelled: *{row[8]}*\n'
f'Expires: *{row[9]}*')
header = f"**{row[0]}** ({row[3]})" # **Callsign** (Applicant Type)
body = (f"Name: *{row[2]}*\n"
f"Class: *{row[4]}*\n"
f"Region: *{row[1]}*\n"
f"Status: *{row[5]}*\n"
f"Granted: *{row[6]}*\n"
f"Effective: *{row[7]}*\n"
f"Cancelled: *{row[8]}*\n"
f"Expires: *{row[9]}*")
embed.add_field(name=header, value=body, inline=False)
if len(table) > 3:
embed.description = f'Records 1 to 3 of {len(table)}. See ae7q.com for more...'
embed.description = f"Records 1 to 3 of {len(table)}. See ae7q.com for more..."
await ctx.send(embed=embed)
@ -372,13 +372,13 @@ async def process_table(table: list):
table_contents = []
for tr in table:
row = []
for td in tr.find_all('td'):
for td in tr.find_all("td"):
cell_val = td.getText().strip()
row.append(cell_val if cell_val else '-')
row.append(cell_val if cell_val else "-")
# take care of columns that span multiple rows by copying the contents rightward
if 'colspan' in td.attrs and int(td.attrs['colspan']) > 1:
for i in range(int(td.attrs['colspan']) - 1):
if "colspan" in td.attrs and int(td.attrs["colspan"]) > 1:
for i in range(int(td.attrs["colspan"]) - 1):
row.append(row[-1])
# get rid of ditto marks by copying the contents from the previous row

View File

@ -24,7 +24,7 @@ import data.options as opt
class QrmHelpCommand(commands.HelpCommand):
def __init__(self):
super().__init__(command_attrs={'help': 'Shows help about qrm or a command', 'aliases': ['h']})
super().__init__(command_attrs={"help": "Shows help about qrm or a command", "aliases": ["h"]})
self.verify_checks = True
async def get_bot_mapping(self):
@ -32,7 +32,7 @@ class QrmHelpCommand(commands.HelpCommand):
mapping = {}
for cmd in await self.filter_commands(bot.commands, sort=True):
cat = cmd.__original_kwargs__.get('category', None)
cat = cmd.__original_kwargs__.get("category", None)
if cat in mapping:
mapping[cat].append(cmd)
else:
@ -42,27 +42,27 @@ class QrmHelpCommand(commands.HelpCommand):
async def get_command_signature(self, command):
parent = command.full_parent_name
if command.aliases != []:
aliases = ', '.join(command.aliases)
aliases = ", ".join(command.aliases)
fmt = command.name
if parent:
fmt = f'{parent} {fmt}'
fmt = f"{parent} {fmt}"
alias = fmt
return f'{opt.prefix}{alias} {command.signature}\n *Aliases:* {aliases}'
alias = command.name if not parent else f'{parent} {command.name}'
return f'{opt.prefix}{alias} {command.signature}'
return f"{opt.prefix}{alias} {command.signature}\n *Aliases:* {aliases}"
alias = command.name if not parent else f"{parent} {command.name}"
return f"{opt.prefix}{alias} {command.signature}"
async def send_error_message(self, error):
embed = cmn.embed_factory(self.context)
embed.title = 'qrm Help Error'
embed.title = "qrm Help Error"
embed.description = error
embed.colour = cmn.colours.bad
await self.context.send(embed=embed)
async def send_bot_help(self, mapping):
embed = cmn.embed_factory(self.context)
embed.title = 'qrm Help'
embed.description = (f'For command-specific help and usage, use `{opt.prefix}help [command name]`'
'. Many commands have shorter aliases.')
embed.title = "qrm Help"
embed.description = (f"For command-specific help and usage, use `{opt.prefix}help [command name]`."
" Many commands have shorter aliases.")
mapping = await mapping
for cat, cmds in mapping.items():
@ -70,9 +70,9 @@ class QrmHelpCommand(commands.HelpCommand):
continue
names = sorted([cmd.name for cmd in cmds])
if cat is not None:
embed.add_field(name=cat.title(), value=', '.join(names), inline=False)
embed.add_field(name=cat.title(), value=", ".join(names), inline=False)
else:
embed.add_field(name='Other', value=', '.join(names), inline=False)
embed.add_field(name="Other", value=", ".join(names), inline=False)
await self.context.send(embed=embed)
async def send_command_help(self, command):
@ -112,27 +112,27 @@ class BaseCog(commands.Cog):
embed.add_field(name="Authors", value=", ".join(info.authors))
embed.add_field(name="License", value=info.license)
embed.add_field(name="Version", value=f'v{info.release}')
embed.add_field(name="Version", value=f"v{info.release}")
embed.add_field(name="Contributing", value=info.contributing, inline=False)
embed.add_field(name="Official Server", value=info.bot_server, inline=False)
embed.set_thumbnail(url=str(self.bot.user.avatar_url))
await ctx.send(embed=embed)
@commands.command(name="ping", aliases=['beep'])
@commands.command(name="ping", aliases=["beep"])
async def _ping(self, ctx: commands.Context):
"""Show the current latency to the discord endpoint."""
embed = cmn.embed_factory(ctx)
content = ''
content = ""
if ctx.invoked_with == "beep":
embed.title = "**Boop!**"
else:
content = ctx.message.author.mention if random.random() < 0.05 else ''
content = ctx.message.author.mention if random.random() < 0.05 else ""
embed.title = "🏓 **Pong!**"
embed.description = f'Current ping is {self.bot.latency*1000:.1f} ms'
embed.description = f"Current ping is {self.bot.latency*1000:.1f} ms"
await ctx.send(content, embed=embed)
@commands.command(name="changelog", aliases=["clog"])
async def _changelog(self, ctx: commands.Context, version: str = 'latest'):
async def _changelog(self, ctx: commands.Context, version: str = "latest"):
"""Show what has changed in a bot version."""
embed = cmn.embed_factory(ctx)
embed.title = "qrm Changelog"
@ -144,25 +144,25 @@ class BaseCog(commands.Cog):
version = version.lower()
if version == 'latest':
if version == "latest":
version = info.release
if version == 'unreleased':
version = 'Unreleased'
if version == "unreleased":
version = "Unreleased"
try:
log = changelog[version]
except KeyError:
embed.title += ": Version Not Found"
embed.description += '\n\n**Valid versions:** latest, '
embed.description += ', '.join(vers)
embed.description += "\n\n**Valid versions:** latest, "
embed.description += ", ".join(vers)
embed.colour = cmn.colours.bad
await ctx.send(embed=embed)
return
if 'date' in log:
embed.description += f'\n\n**v{version}** ({log["date"]})'
if "date" in log:
embed.description += f"\n\n**v{version}** ({log['date']})"
else:
embed.description += f'\n\n**v{version}**'
embed.description += f"\n\n**v{version}**"
embed = await format_changelog(log, embed)
await ctx.send(embed=embed)
@ -190,36 +190,36 @@ class BaseCog(commands.Cog):
def parse_changelog():
changelog = OrderedDict()
ver = ''
heading = ''
ver = ""
heading = ""
with open('CHANGELOG.md') as changelog_file:
with open("CHANGELOG.md") as changelog_file:
for line in changelog_file.readlines():
if line.strip() == '':
if line.strip() == "":
continue
if re.match(r'##[^#]', line):
ver_match = re.match(r'\[(.+)\](?: - )?(\d{4}-\d{2}-\d{2})?', line.lstrip('#').strip())
if re.match(r"##[^#]", line):
ver_match = re.match(r"\[(.+)\](?: - )?(\d{4}-\d{2}-\d{2})?", line.lstrip("#").strip())
if ver_match is not None:
ver = ver_match.group(1)
changelog[ver] = dict()
if ver_match.group(2):
changelog[ver]['date'] = ver_match.group(2)
elif re.match(r'###[^#]', line):
heading = line.lstrip('#').strip()
changelog[ver]["date"] = ver_match.group(2)
elif re.match(r"###[^#]", line):
heading = line.lstrip("#").strip()
changelog[ver][heading] = []
elif ver != '' and heading != '':
if line.startswith('-'):
changelog[ver][heading].append(line.lstrip('-').strip())
elif ver != "" and heading != "":
if line.startswith("-"):
changelog[ver][heading].append(line.lstrip("-").strip())
return changelog
async def format_changelog(log: dict, embed: discord.Embed):
for header, lines in log.items():
formatted = ''
if header != 'date':
formatted = ""
if header != "date":
for line in lines:
formatted += f'- {line}\n'
embed.add_field(name=f'**{header}**', value=formatted, inline=False)
formatted += f"- {line}\n"
embed.add_field(name=f"**{header}**", value=formatted, inline=False)
return embed

View File

@ -17,37 +17,37 @@ import common as cmn
class FunCog(commands.Cog):
def __init__(self, bot: commands.Bot):
self.bot = bot
with open('resources/words') as words_file:
with open("resources/words") as words_file:
self.words = words_file.read().lower().splitlines()
@commands.command(name="xkcd", aliases=['x'], category=cmn.cat.fun)
@commands.command(name="xkcd", aliases=["x"], category=cmn.cat.fun)
async def _xkcd(self, ctx: commands.Context, number: str):
'''Look up an xkcd by number.'''
await ctx.send('http://xkcd.com/' + number)
"""Look up an xkcd by number."""
await ctx.send("http://xkcd.com/" + number)
@commands.command(name="tar", category=cmn.cat.fun)
async def _tar(self, ctx: commands.Context):
'''Returns an xkcd about tar.'''
await ctx.send('http://xkcd.com/1168')
"""Returns an xkcd about tar."""
await ctx.send("http://xkcd.com/1168")
@commands.command(name="xd", hidden=True, category=cmn.cat.fun)
async def _xd(self, ctx: commands.Context):
'''ecks dee'''
await ctx.send('ECKS DEE :smirk:')
"""ecks dee"""
await ctx.send("ECKS DEE :smirk:")
@commands.command(name="funetics", aliases=['fun'], category=cmn.cat.fun)
@commands.command(name="funetics", aliases=["fun"], category=cmn.cat.fun)
async def _funetics_lookup(self, ctx: commands.Context, *, msg: str):
'''Get fun phonetics for a word or phrase.'''
"""Get fun phonetics for a word or phrase."""
with ctx.typing():
result = ''
result = ""
for char in msg.lower():
if char.isalpha():
result += random.choice([word for word in self.words if word[0] == char])
else:
result += char
result += ' '
result += " "
embed = cmn.embed_factory(ctx)
embed.title = f'Funetics for {msg}'
embed.title = f"Funetics for {msg}"
embed.description = result.title()
embed.colour = cmn.colours.good
await ctx.send(embed=embed)

View File

@ -20,27 +20,27 @@ class GridCog(commands.Cog):
@commands.command(name="grid", category=cmn.cat.maps)
async def _grid_sq_lookup(self, ctx: commands.Context, lat: str, lon: str):
'''Calculates the grid square for latitude and longitude coordinates,
with negative being latitude South and longitude West.'''
"""Calculates the grid square for latitude and longitude coordinates,
with negative being latitude South and longitude West."""
with ctx.typing():
grid = "**"
latf = float(lat) + 90
lonf = float(lon) + 180
if 0 <= latf <= 180 and 0 <= lonf <= 360:
grid += chr(ord('A') + int(lonf / 20))
grid += chr(ord('A') + int(latf / 10))
grid += chr(ord('0') + int((lonf % 20)/2))
grid += chr(ord('0') + int((latf % 10)/1))
grid += chr(ord('a') + int((lonf - (int(lonf/2)*2)) / (5/60)))
grid += chr(ord('a') + int((latf - (int(latf/1)*1)) / (2.5/60)))
grid += chr(ord("A") + int(lonf / 20))
grid += chr(ord("A") + int(latf / 10))
grid += chr(ord("0") + int((lonf % 20)/2))
grid += chr(ord("0") + int((latf % 10)/1))
grid += chr(ord("a") + int((lonf - (int(lonf/2)*2)) / (5/60)))
grid += chr(ord("a") + int((latf - (int(latf/1)*1)) / (2.5/60)))
grid += "**"
embed = cmn.embed_factory(ctx)
embed.title = f'Maidenhead Grid Locator for {float(lat):.6f}, {float(lon):.6f}'
embed.title = f"Maidenhead Grid Locator for {float(lat):.6f}, {float(lon):.6f}"
embed.description = grid
embed.colour = cmn.colours.good
else:
embed = cmn.embed_factory(ctx)
embed.title = f'Error generating grid square for {lat}, {lon}.'
embed.title = f"Error generating grid square for {lat}, {lon}."
embed.description = ("Coordinates out of range.\n"
"The valid ranges are:\n"
"- Latitude: `-90` to `+90`\n"
@ -48,29 +48,29 @@ with negative being latitude South and longitude West.'''
embed.colour = cmn.colours.bad
await ctx.send(embed=embed)
@commands.command(name="ungrid", aliases=['loc'], category=cmn.cat.maps)
@commands.command(name="ungrid", aliases=["loc"], category=cmn.cat.maps)
async def _location_lookup(self, ctx: commands.Context, grid: str, grid2: str = None):
'''Calculates the latitude and longitude for the center of a grid square.
If two grid squares are given, the distance and azimuth between them is calculated.'''
"""Calculates the latitude and longitude for the center of a grid square.
If two grid squares are given, the distance and azimuth between them is calculated."""
with ctx.typing():
if grid2 is None or grid2 == '':
if grid2 is None or grid2 == "":
try:
grid = grid.upper()
loc = get_coords(grid)
embed = cmn.embed_factory(ctx)
embed.title = f'Latitude and Longitude for {grid}'
embed.title = f"Latitude and Longitude for {grid}"
embed.colour = cmn.colours.good
if len(grid) >= 6:
embed.description = f'**{loc[0]:.5f}, {loc[1]:.5f}**'
embed.url = f'https://www.openstreetmap.org/#map=13/{loc[0]:.5f}/{loc[1]:.5f}'
embed.description = f"**{loc[0]:.5f}, {loc[1]:.5f}**"
embed.url = f"https://www.openstreetmap.org/#map=13/{loc[0]:.5f}/{loc[1]:.5f}"
else:
embed.description = f'**{loc[0]:.1f}, {loc[1]:.1f}**'
embed.url = f'https://www.openstreetmap.org/#map=10/{loc[0]:.1f}/{loc[1]:.1f}'
embed.description = f"**{loc[0]:.1f}, {loc[1]:.1f}**"
embed.url = f"https://www.openstreetmap.org/#map=10/{loc[0]:.1f}/{loc[1]:.1f}"
except Exception as e:
embed = cmn.embed_factory(ctx)
embed.title = f'Error generating latitude and longitude for grid {grid}.'
embed.title = f"Error generating latitude and longitude for grid {grid}."
embed.description = str(e)
embed.colour = cmn.colours.bad
else:
@ -101,12 +101,12 @@ If two grid squares are given, the distance and azimuth between them is calculat
bearing = (math.degrees(math.atan2(y_dist, x_dist)) + 360) % 360
embed = cmn.embed_factory(ctx)
embed.title = f'Great Circle Distance and Bearing from {grid} to {grid2}'
embed.description = f'**Distance:** {d:.1f} km ({d_mi:.1f} mi)\n**Bearing:** {bearing:.1f}°'
embed.title = f"Great Circle Distance and Bearing from {grid} to {grid2}"
embed.description = f"**Distance:** {d:.1f} km ({d_mi:.1f} mi)\n**Bearing:** {bearing:.1f}°"
embed.colour = cmn.colours.good
except Exception as e:
embed = cmn.embed_factory(ctx)
embed.title = f'Error generating great circle distance and bearing from {grid} and {grid2}.'
embed.title = f"Error generating great circle distance and bearing from {grid} and {grid2}."
embed.description = str(e)
embed.colour = cmn.colours.bad
await ctx.send(embed=embed)
@ -114,23 +114,23 @@ If two grid squares are given, the distance and azimuth between them is calculat
def get_coords(grid: str):
if len(grid) < 3:
raise ValueError('The grid locator must be at least 4 characters long.')
raise ValueError("The grid locator must be at least 4 characters long.")
if not grid[0:2].isalpha() or not grid[2:4].isdigit():
if len(grid) <= 4:
raise ValueError('The grid locator must be of the form AA##.')
raise ValueError("The grid locator must be of the form AA##.")
if len(grid) >= 6 and not grid[5:7].isalpha():
raise ValueError('The grid locator must be of the form AA##AA.')
raise ValueError("The grid locator must be of the form AA##AA.")
lon = ((ord(grid[0]) - ord('A')) * 20) - 180
lat = ((ord(grid[1]) - ord('A')) * 10) - 90
lon += ((ord(grid[2]) - ord('0')) * 2)
lat += ((ord(grid[3]) - ord('0')) * 1)
lon = ((ord(grid[0]) - ord("A")) * 20) - 180
lat = ((ord(grid[1]) - ord("A")) * 10) - 90
lon += ((ord(grid[2]) - ord("0")) * 2)
lat += ((ord(grid[3]) - ord("0")) * 1)
if len(grid) >= 6:
# have subsquares
lon += ((ord(grid[4])) - ord('A')) * (5/60)
lat += ((ord(grid[5])) - ord('A')) * (2.5/60)
lon += ((ord(grid[4])) - ord("A")) * (5/60)
lat += ((ord(grid[5])) - ord("A")) * (2.5/60)
# move to center of subsquare
lon += (2.5/60)
lat += (1.25/60)

View File

@ -21,9 +21,9 @@ class HamCog(commands.Cog):
def __init__(self, bot: commands.Bot):
self.bot = bot
@commands.command(name="qcode", aliases=['q'], category=cmn.cat.ref)
@commands.command(name="qcode", aliases=["q"], category=cmn.cat.ref)
async def _qcode_lookup(self, ctx: commands.Context, qcode: str):
'''Look up a Q Code.'''
"""Look up a Q Code."""
with ctx.typing():
qcode = qcode.upper()
embed = cmn.embed_factory(ctx)
@ -32,49 +32,49 @@ class HamCog(commands.Cog):
embed.description = qcodes.qcodes[qcode]
embed.colour = cmn.colours.good
else:
embed.title = f'Q Code {qcode} not found'
embed.title = f"Q Code {qcode} not found"
embed.colour = cmn.colours.bad
await ctx.send(embed=embed)
@commands.command(name="phonetics", aliases=['ph', 'phoneticize', 'phoneticise', 'phone'], category=cmn.cat.ref)
@commands.command(name="phonetics", aliases=["ph", "phoneticize", "phoneticise", "phone"], category=cmn.cat.ref)
async def _phonetics_lookup(self, ctx: commands.Context, *, msg: str):
'''Get phonetics for a word or phrase.'''
"""Get phonetics for a word or phrase."""
with ctx.typing():
result = ''
result = ""
for char in msg.lower():
if char.isalpha():
result += phonetics.phonetics[char]
else:
result += char
result += ' '
result += " "
embed = cmn.embed_factory(ctx)
embed.title = f'Phonetics for {msg}'
embed.title = f"Phonetics for {msg}"
embed.description = result.title()
embed.colour = cmn.colours.good
await ctx.send(embed=embed)
@commands.command(name="utc", aliases=['z'], category=cmn.cat.ref)
@commands.command(name="utc", aliases=["z"], category=cmn.cat.ref)
async def _utc_lookup(self, ctx: commands.Context):
'''Gets the current time in UTC.'''
"""Gets the current time in UTC."""
with ctx.typing():
now = datetime.utcnow()
result = '**' + now.strftime('%Y-%m-%d %H:%M') + 'Z**'
result = "**" + now.strftime("%Y-%m-%d %H:%M") + "Z**"
embed = cmn.embed_factory(ctx)
embed.title = 'The current time is:'
embed.title = "The current time is:"
embed.description = result
embed.colour = cmn.colours.good
await ctx.send(embed=embed)
@commands.command(name="prefixes", aliases=["vanity", "pfx", "vanities", "prefix"], category=cmn.cat.ref)
async def _vanity_prefixes(self, ctx: commands.Context, country: str = None):
'''Lists valid prefixes for countries.'''
"""Lists valid prefixes for countries."""
if country is None:
await ctx.send_help(ctx.command)
return
embed = cmn.embed_factory(ctx)
if country.lower() not in callsign_info.options:
embed.title = f'{country} not found!',
embed.description = f'Valid countries: {", ".join(callsign_info.options.keys())}',
embed.title = f"{country} not found!",
embed.description = f"Valid countries: {', '.join(callsign_info.options.keys())}",
embed.colour = cmn.colours.bad
else:
embed.title = callsign_info.options[country.lower()][0]

View File

@ -18,8 +18,8 @@ import common as cmn
class ImageCog(commands.Cog):
gl_url = ('http://www.fourmilab.ch/cgi-bin/uncgi/Earth?img=NOAAtopo.evif'
'&imgsize=320&dynimg=y&opt=-p&lat=&lon=&alt=&tle=&date=0&utc=&jd=')
gl_url = ("http://www.fourmilab.ch/cgi-bin/uncgi/Earth?img=NOAAtopo.evif"
"&imgsize=320&dynimg=y&opt=-p&lat=&lon=&alt=&tle=&date=0&utc=&jd=")
def __init__(self, bot: commands.Bot):
self.bot = bot
@ -27,17 +27,17 @@ class ImageCog(commands.Cog):
self.maps = cmn.ImagesGroup(cmn.paths.maps / "meta.json")
self.session = aiohttp.ClientSession(connector=bot.qrm.connector)
@commands.command(name="bandplan", aliases=['plan', 'bands'], category=cmn.cat.ref)
async def _bandplan(self, ctx: commands.Context, region: str = ''):
'''Posts an image of Frequency Allocations.'''
@commands.command(name="bandplan", aliases=["plan", "bands"], category=cmn.cat.ref)
async def _bandplan(self, ctx: commands.Context, region: str = ""):
"""Posts an image of Frequency Allocations."""
async with ctx.typing():
arg = region.lower()
embed = cmn.embed_factory(ctx)
if arg not in self.bandcharts:
desc = 'Possible arguments are:\n'
desc = "Possible arguments are:\n"
for key, img in self.bandcharts.items():
desc += f'`{key}`: {img.name}{(" " + img.emoji if img.emoji else "")}\n'
embed.title = f'Bandplan Not Found!'
desc += f"`{key}`: {img.name}{(' ' + img.emoji if img.emoji else '')}\n"
embed.title = f"Bandplan Not Found!"
embed.description = desc
embed.colour = cmn.colours.bad
await ctx.send(embed=embed)
@ -51,20 +51,20 @@ class ImageCog(commands.Cog):
embed.add_field(name="Source", value=metadata.source)
embed.title = metadata.long_name + (" " + metadata.emoji if metadata.emoji else "")
embed.colour = cmn.colours.good
embed.set_image(url='attachment://' + metadata.filename)
embed.set_image(url="attachment://" + metadata.filename)
await ctx.send(embed=embed, file=img)
@commands.command(name="map", category=cmn.cat.maps)
async def _map(self, ctx: commands.Context, map_id: str = ''):
'''Posts an image of a ham-relevant map.'''
async def _map(self, ctx: commands.Context, map_id: str = ""):
"""Posts an image of a ham-relevant map."""
async with ctx.typing():
arg = map_id.lower()
embed = cmn.embed_factory(ctx)
if arg not in self.maps:
desc = 'Possible arguments are:\n'
desc = "Possible arguments are:\n"
for key, img in self.maps.items():
desc += f'`{key}`: {img.name}{(" " + img.emoji if img.emoji else "")}\n'
embed.title = 'Map Not Found!'
desc += f"`{key}`: {img.name}{(' ' + img.emoji if img.emoji else '')}\n"
embed.title = "Map Not Found!"
embed.description = desc
embed.colour = cmn.colours.bad
await ctx.send(embed=embed)
@ -78,22 +78,22 @@ class ImageCog(commands.Cog):
embed.add_field(name="Source", value=metadata.source)
embed.title = metadata.long_name + (" " + metadata.emoji if metadata.emoji else "")
embed.colour = cmn.colours.good
embed.set_image(url='attachment://' + metadata.filename)
embed.set_image(url="attachment://" + metadata.filename)
await ctx.send(embed=embed, file=img)
@commands.command(name="grayline", aliases=['greyline', 'grey', 'gray', 'gl'], category=cmn.cat.maps)
@commands.command(name="grayline", aliases=["greyline", "grey", "gray", "gl"], category=cmn.cat.maps)
async def _grayline(self, ctx: commands.Context):
'''Posts a map of the current greyline, where HF propagation is the best.'''
"""Posts a map of the current greyline, where HF propagation is the best."""
async with ctx.typing():
embed = cmn.embed_factory(ctx)
embed.title = 'Current Greyline Conditions'
embed.title = "Current Greyline Conditions"
embed.colour = cmn.colours.good
async with self.session.get(self.gl_url) as resp:
if resp.status != 200:
raise cmn.BotHTTPError(resp)
data = io.BytesIO(await resp.read())
embed.set_image(url=f'attachment://greyline.jpg')
await ctx.send(embed=embed, file=discord.File(data, 'greyline.jpg'))
embed.set_image(url=f"attachment://greyline.jpg")
await ctx.send(embed=embed, file=discord.File(data, "greyline.jpg"))
def setup(bot: commands.Bot):

View File

@ -19,48 +19,48 @@ class LookupCog(commands.Cog):
def __init__(self, bot):
self.bot = bot
try:
self.cty = BigCty('./data/cty.json')
self.cty = BigCty("./data/cty.json")
except OSError:
self.cty = BigCty()
# TODO: See #107
# @commands.command(name="sat", category=cmn.cat.lookup)
# async def _sat_lookup(self, ctx: commands.Context, sat_name: str, grid1: str, grid2: str = None):
# '''Links to info about satellite passes on satmatch.com.'''
# now = datetime.utcnow().strftime('%Y-%m-%d%%20%H:%M')
# if grid2 is None or grid2 == '':
# await ctx.send(f'http://www.satmatch.com/satellite/{sat_name}/obs1/{grid1}'
# f'?search_start_time={now}&duration_hrs=24')
# """Links to info about satellite passes on satmatch.com."""
# now = datetime.utcnow().strftime("%Y-%m-%d%%20%H:%M")
# if grid2 is None or grid2 == "":
# await ctx.send(f"http://www.satmatch.com/satellite/{sat_name}/obs1/{grid1}"
# f"?search_start_time={now}&duration_hrs=24")
# else:
# await ctx.send(f'http://www.satmatch.com/satellite/{sat_name}/obs1/{grid1}'
# f'/obs2/{grid2}?search_start_time={now}&duration_hrs=24')
# await ctx.send(f"http://www.satmatch.com/satellite/{sat_name}/obs1/{grid1}"
# f"/obs2/{grid2}?search_start_time={now}&duration_hrs=24")
@commands.command(name="dxcc", aliases=['dx'], category=cmn.cat.lookup)
@commands.command(name="dxcc", aliases=["dx"], category=cmn.cat.lookup)
async def _dxcc_lookup(self, ctx: commands.Context, query: str):
'''Gets info about a DXCC prefix.'''
"""Gets info about a DXCC prefix."""
with ctx.typing():
query = query.upper()
full_query = query
embed = cmn.embed_factory(ctx)
embed.title = f'DXCC Info for '
embed.description = f'*Last Updated: {self.cty.formatted_version}*'
embed.title = f"DXCC Info for "
embed.description = f"*Last Updated: {self.cty.formatted_version}*"
embed.colour = cmn.colours.bad
while query:
if query in self.cty.keys():
data = self.cty[query]
embed.add_field(name="Entity", value=data['entity'])
embed.add_field(name="CQ Zone", value=data['cq'])
embed.add_field(name="ITU Zone", value=data['itu'])
embed.add_field(name="Continent", value=data['continent'])
embed.add_field(name="Entity", value=data["entity"])
embed.add_field(name="CQ Zone", value=data["cq"])
embed.add_field(name="ITU Zone", value=data["itu"])
embed.add_field(name="Continent", value=data["continent"])
embed.add_field(name="Time Zone",
value=f'+{data["tz"]}' if data['tz'] > 0 else str(data['tz']))
value=f"+{data['tz']}" if data["tz"] > 0 else str(data["tz"]))
embed.title += query
embed.colour = cmn.colours.good
break
else:
query = query[:-1]
else:
embed.title += full_query + ' not found'
embed.title += full_query + " not found"
embed.colour = cmn.colours.bad
await ctx.send(embed=embed)

View File

@ -17,63 +17,63 @@ class MorseCog(commands.Cog):
def __init__(self, bot: commands.Bot):
self.bot = bot
@commands.command(name="morse", aliases=['cw'], category=cmn.cat.ref)
@commands.command(name="morse", aliases=["cw"], category=cmn.cat.ref)
async def _morse(self, ctx: commands.Context, *, msg: str):
"""Converts ASCII to international morse code."""
with ctx.typing():
result = ''
result = ""
for char in msg.upper():
try:
result += morse.morse[char]
except KeyError:
result += '<?>'
result += ' '
result += "<?>"
result += " "
embed = cmn.embed_factory(ctx)
embed.title = f'Morse Code for {msg}'
embed.description = '**' + result + '**'
embed.title = f"Morse Code for {msg}"
embed.description = "**" + result + "**"
embed.colour = cmn.colours.good
await ctx.send(embed=embed)
@commands.command(name="unmorse", aliases=['demorse', 'uncw', 'decw'], category=cmn.cat.ref)
@commands.command(name="unmorse", aliases=["demorse", "uncw", "decw"], category=cmn.cat.ref)
async def _unmorse(self, ctx: commands.Context, *, msg: str):
'''Converts international morse code to ASCII.'''
"""Converts international morse code to ASCII."""
with ctx.typing():
result = ''
result = ""
msg0 = msg
msg = msg.split('/')
msg = msg.split("/")
msg = [m.split() for m in msg]
for word in msg:
for char in word:
try:
result += morse.ascii[char]
except KeyError:
result += '<?>'
result += ' '
result += "<?>"
result += " "
embed = cmn.embed_factory(ctx)
embed.title = f'ASCII for {msg0}'
embed.title = f"ASCII for {msg0}"
embed.description = result
embed.colour = cmn.colours.good
await ctx.send(embed=embed)
@commands.command(name="cwweight", aliases=["weight", 'cww'], category=cmn.cat.ref)
@commands.command(name="cwweight", aliases=["weight", "cww"], category=cmn.cat.ref)
async def _weight(self, ctx: commands.Context, *, msg: str):
'''Calculates the CW Weight of a callsign or message.'''
"""Calculates the CW Weight of a callsign or message."""
embed = cmn.embed_factory(ctx)
with ctx.typing():
msg = msg.upper()
weight = 0
for char in msg:
try:
cw_char = morse.morse[char].replace('-', '==')
cw_char = morse.morse[char].replace("-", "==")
weight += len(cw_char) * 2 + 2
except KeyError:
embed.title = 'Error in calculation of CW weight'
embed.description = f'Unknown character `{char}` in message'
embed.title = "Error in calculation of CW weight"
embed.description = f"Unknown character `{char}` in message"
embed.colour = cmn.colours.bad
await ctx.send(embed=embed)
return
embed.title = f'CW Weight of {msg}'
embed.description = f'The CW weight is **{weight}**'
embed.title = f"CW Weight of {msg}"
embed.description = f"The CW weight is **{weight}**"
embed.colour = cmn.colours.good
await ctx.send(embed=embed)

View File

@ -26,11 +26,11 @@ class QRZCog(commands.Cog):
@commands.command(name="call", aliases=["qrz"], category=cmn.cat.lookup)
async def _qrz_lookup(self, ctx: commands.Context, callsign: str, *flags):
'''Look up a callsign on [QRZ.com](https://www.qrz.com/). Add `--link` to only link the QRZ page.'''
"""Look up a callsign on [QRZ.com](https://www.qrz.com/). Add `--link` to only link the QRZ page."""
flags = [f.lower() for f in flags]
if keys.qrz_user == '' or keys.qrz_pass == '' or '--link' in flags:
await ctx.send(f'http://qrz.com/db/{callsign}')
if keys.qrz_user == "" or keys.qrz_pass == "" or "--link" in flags:
await ctx.send(f"http://qrz.com/db/{callsign}")
return
try:
@ -38,38 +38,38 @@ class QRZCog(commands.Cog):
except ConnectionError:
await self.get_session()
url = f'http://xmldata.qrz.com/xml/current/?s={self.key};callsign={callsign}'
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})')
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']:
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']:
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!'
embed.description = "No data found!"
await ctx.send(embed=embed)
return
raise ValueError(resp_session['Error'])
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()}
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'])
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)
@ -81,14 +81,14 @@ class QRZCog(commands.Cog):
async def get_session(self):
"""Session creation and caching."""
self.key = await qrz_login(keys.qrz_user, keys.qrz_pass, self.session)
with open('data/qrz_session', 'w') as qrz_file:
with open("data/qrz_session", "w") as qrz_file:
qrz_file.write(self.key)
@tasks.loop(count=1)
async def _qrz_session_init(self):
"""Helper task to allow obtaining a session at cog instantiation."""
try:
with open('data/qrz_session') as qrz_file:
with open("data/qrz_session") as qrz_file:
self.key = qrz_file.readline().strip()
await qrz_test_session(self.key, self.session)
except (FileNotFoundError, ConnectionError):
@ -96,86 +96,86 @@ class QRZCog(commands.Cog):
async def qrz_login(user: str, passwd: str, session: aiohttp.ClientSession):
url = f'http://xmldata.qrz.com/xml/current/?username={user};password={passwd};agent=discord-qrm2'
url = f"http://xmldata.qrz.com/xml/current/?username={user};password={passwd};agent=discord-qrm2"
async with session.get(url) as resp:
if resp.status != 200:
raise ConnectionError(f'Unable to connect to QRZ (HTTP Error {resp.status})')
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:
raise ConnectionError(resp_session['Error'])
if resp_session['SubExp'] == 'non-subscriber':
raise ConnectionError('Invalid QRZ Subscription')
return resp_session['Key']
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"]
async def qrz_test_session(key: str, session: aiohttp.ClientSession):
url = f'http://xmldata.qrz.com/xml/current/?s={key}'
url = f"http://xmldata.qrz.com/xml/current/?s={key}"
async with session.get(url) as resp:
if resp.status != 200:
raise ConnectionError(f'Unable to connect to QRZ (HTTP Error {resp.status})')
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:
raise ConnectionError(resp_session['Error'])
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"])
def qrz_process_info(data: dict):
if 'name' in data:
if 'fname' in data:
name = data['fname'] + ' ' + data['name']
if "name" in data:
if "fname" in data:
name = data["fname"] + " " + data["name"]
else:
name = data['name']
name = data["name"]
else:
name = None
if 'state' in data:
state = f', {data["state"]}'
if "state" in data:
state = f", {data['state']}"
else:
state = ''
address = data.get('addr1', '') + '\n' + data.get('addr2', '') + state + ' ' + data.get('zip', '')
state = ""
address = data.get("addr1", "") + "\n" + data.get("addr2", "") + state + " " + data.get("zip", "")
address = address.strip()
if address == '':
if address == "":
address = None
if 'eqsl' in data:
eqsl = 'Yes' if data['eqsl'] == 1 else 'No'
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'
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'
mqsl = "Unknown"
if "lotw" in data:
lotw = "Yes" if data["lotw"] == 1 else "No"
else:
lotw = 'Unknown'
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)),
('Trustee', data.get('trustee', 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))])
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)),
("Trustee", data.get("trustee", 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))])
def setup(bot):

View File

@ -21,17 +21,17 @@ from resources import study
class StudyCog(commands.Cog):
choices = {cmn.emojis.a: 'A', cmn.emojis.b: 'B', cmn.emojis.c: 'C', cmn.emojis.d: 'D'}
choices = {cmn.emojis.a: "A", cmn.emojis.b: "B", cmn.emojis.c: "C", cmn.emojis.d: "D"}
def __init__(self, bot: commands.Bot):
self.bot = bot
self.lastq = dict()
self.source = 'Data courtesy of [HamStudy.org](https://hamstudy.org/)'
self.source = "Data courtesy of [HamStudy.org](https://hamstudy.org/)"
self.session = aiohttp.ClientSession(connector=bot.qrm.connector)
@commands.command(name="hamstudy", aliases=['rq', 'randomquestion', 'randomq'], category=cmn.cat.study)
async def _random_question(self, ctx: commands.Context, country: str = '', level: str = ''):
'''Gets a random question from [HamStudy's](https://hamstudy.org) question pools.'''
@commands.command(name="hamstudy", aliases=["rq", "randomquestion", "randomq"], category=cmn.cat.study)
async def _random_question(self, ctx: commands.Context, country: str = "", level: str = ""):
"""Gets a random question from [HamStudy's](https://hamstudy.org) question pools."""
with ctx.typing():
embed = cmn.embed_factory(ctx)
@ -52,7 +52,7 @@ class StudyCog(commands.Cog):
embed.description = "Possible arguments are:"
embed.colour = cmn.colours.bad
for cty in study.pool_names:
levels = '`, `'.join(study.pool_names[cty].keys())
levels = "`, `".join(study.pool_names[cty].keys())
embed.add_field(name=f"**Country: `{cty}` {study.pool_emojis[cty]}**",
value=f"Levels: `{levels}`", inline=False)
embed.add_field(name="**Random**", value="To select a random pool or country, use `random` or `r`")
@ -70,7 +70,7 @@ class StudyCog(commands.Cog):
embed.description = "Possible arguments are:"
embed.colour = cmn.colours.bad
for cty in study.pool_names:
levels = '`, `'.join(study.pool_names[cty].keys())
levels = "`, `".join(study.pool_names[cty].keys())
embed.add_field(name=f"**Country: `{cty}` {study.pool_emojis[cty]}**",
value=f"Levels: `{levels}`", inline=False)
embed.add_field(name="**Random**", value="To select a random pool or country, use `random` or `r`")
@ -99,7 +99,7 @@ class StudyCog(commands.Cog):
embed.description = "Possible arguments are:"
embed.colour = cmn.colours.bad
for cty in study.pool_names:
levels = '`, `'.join(study.pool_names[cty].keys())
levels = "`, `".join(study.pool_names[cty].keys())
embed.add_field(name=f"**Country: `{cty}` {study.pool_emojis[cty]}**",
value=f"Levels: `{levels}`", inline=False)
embed.add_field(name="**Random**", value="To select a random pool or country, use `random` or `r`")
@ -108,31 +108,31 @@ class StudyCog(commands.Cog):
pool_meta = pools[pool]
async with self.session.get(f'https://hamstudy.org/pools/{pool}') as resp:
async with self.session.get(f"https://hamstudy.org/pools/{pool}") as resp:
if resp.status != 200:
raise cmn.BotHTTPError(resp)
pool = json.loads(await resp.read())['pool']
pool = json.loads(await resp.read())["pool"]
# Select a question
pool_section = random.choice(pool)['sections']
pool_questions = random.choice(pool_section)['questions']
pool_section = random.choice(pool)["sections"]
pool_questions = random.choice(pool_section)["questions"]
question = random.choice(pool_questions)
embed.title = f"{study.pool_emojis[country]} {pool_meta['class']} {question['id']}"
embed.description = self.source
embed.add_field(name='Question:', value=question['text'], inline=False)
embed.add_field(name='Answers:',
embed.add_field(name="Question:", value=question["text"], inline=False)
embed.add_field(name="Answers:",
value=(f"**{cmn.emojis.a}** {question['answers']['A']}"
f"\n**{cmn.emojis.b}** {question['answers']['B']}"
f"\n**{cmn.emojis.c}** {question['answers']['C']}"
f"\n**{cmn.emojis.d}** {question['answers']['D']}"),
inline=False)
embed.add_field(name='To Answer:',
value=('Answer with reactions below. If not answered within 10 minutes,'
' the answer will be revealed.'),
embed.add_field(name="To Answer:",
value=("Answer with reactions below. If not answered within 10 minutes,"
" the answer will be revealed."),
inline=False)
if 'image' in question:
image_url = f'https://hamstudy.org/_1330011/images/{pool.split("_",1)[1]}/{question["image"]}'
if "image" in question:
image_url = f"https://hamstudy.org/_1330011/images/{pool.split('_',1)[1]}/{question['image']}"
embed.set_image(url=image_url)
q_msg = await ctx.send(embed=embed)
@ -148,13 +148,13 @@ class StudyCog(commands.Cog):
and str(reaction.emoji) in self.choices.keys())
try:
reaction, user = await self.bot.wait_for('reaction_add', timeout=600.0, check=check)
reaction, user = await self.bot.wait_for("reaction_add", timeout=600.0, check=check)
except asyncio.TimeoutError:
embed.remove_field(2)
embed.add_field(name="Answer:", value=f"Timed out! The correct answer was **{question['answer']}**.")
await q_msg.edit(embed=embed)
else:
if self.choices[str(reaction.emoji)] == question['answer']:
if self.choices[str(reaction.emoji)] == question["answer"]:
embed.remove_field(2)
embed.add_field(name="Answer:", value=f"Correct! The answer was **{question['answer']}**.")
embed.colour = cmn.colours.good
@ -166,7 +166,7 @@ class StudyCog(commands.Cog):
await q_msg.edit(embed=embed)
async def hamstudy_get_pools(self):
async with self.session.get('https://hamstudy.org/pools/') as resp:
async with self.session.get("https://hamstudy.org/pools/") as resp:
if resp.status != 200:
raise cmn.BotHTTPError(resp)
else:

View File

@ -25,23 +25,23 @@ class WeatherCog(commands.Cog):
self.bot = bot
self.session = aiohttp.ClientSession(connector=bot.qrm.connector)
@commands.command(name="bandconditions", aliases=['cond', 'condx', 'conditions'], category=cmn.cat.weather)
@commands.command(name="bandconditions", aliases=["cond", "condx", "conditions"], category=cmn.cat.weather)
async def _band_conditions(self, ctx: commands.Context):
'''Posts an image of HF Band Conditions.'''
"""Posts an image of HF Band Conditions."""
async with ctx.typing():
embed = cmn.embed_factory(ctx)
embed.title = 'Current Solar Conditions'
embed.title = "Current Solar Conditions"
embed.colour = cmn.colours.good
async with self.session.get('http://www.hamqsl.com/solarsun.php') as resp:
async with self.session.get("http://www.hamqsl.com/solarsun.php") as resp:
if resp.status != 200:
raise cmn.BotHTTPError(resp)
data = io.BytesIO(await resp.read())
embed.set_image(url=f'attachment://condx.png')
await ctx.send(embed=embed, file=discord.File(data, 'condx.png'))
embed.set_image(url=f"attachment://condx.png")
await ctx.send(embed=embed, file=discord.File(data, "condx.png"))
@commands.group(name="weather", aliases=['wttr'], category=cmn.cat.weather)
@commands.group(name="weather", aliases=["wttr"], category=cmn.cat.weather)
async def _weather_conditions(self, ctx: commands.Context):
'''Posts an image of Local Weather Conditions from [wttr.in](http://wttr.in/).
"""Posts an image of Local Weather Conditions from [wttr.in](http://wttr.in/).
*Supported location types:*
city name: `paris`
@ -51,71 +51,71 @@ class WeatherCog(commands.Cog):
domain name `@stackoverflow.com`
area codes: `12345`
GPS coordinates: `-78.46,106.79`
'''
"""
if ctx.invoked_subcommand is None:
await ctx.send_help(ctx.command)
@_weather_conditions.command(name='forecast', aliases=['fc', 'future'], category=cmn.cat.weather)
@_weather_conditions.command(name="forecast", aliases=["fc", "future"], category=cmn.cat.weather)
async def _weather_conditions_forecast(self, ctx: commands.Context, *, location: str):
'''Posts an image of Local Weather Conditions for the next three days from [wttr.in](http://wttr.in/).
See help for weather command for possible location types. Add a `-c` or `-f` to use Celcius or Fahrenheit.'''
"""Posts an image of Local Weather Conditions for the next three days from [wttr.in](http://wttr.in/).
See help for weather command for possible location types. Add a `-c` or `-f` to use Celcius or Fahrenheit."""
async with ctx.typing():
try:
units_arg = re.search(self.wttr_units_regex, location).group(1)
except AttributeError:
units_arg = ''
if units_arg.lower() == 'f':
units = 'u'
elif units_arg.lower() == 'c':
units = 'm'
units_arg = ""
if units_arg.lower() == "f":
units = "u"
elif units_arg.lower() == "c":
units = "m"
else:
units = ''
units = ""
loc = self.wttr_units_regex.sub('', location).strip()
loc = self.wttr_units_regex.sub("", location).strip()
embed = cmn.embed_factory(ctx)
embed.title = f'Weather Forecast for {loc}'
embed.description = 'Data from [wttr.in](http://wttr.in/).'
embed.title = f"Weather Forecast for {loc}"
embed.description = "Data from [wttr.in](http://wttr.in/)."
embed.colour = cmn.colours.good
loc = loc.replace(' ', '+')
async with self.session.get(f'http://wttr.in/{loc}_{units}pnFQ.png') as resp:
loc = loc.replace(" ", "+")
async with self.session.get(f"http://wttr.in/{loc}_{units}pnFQ.png") as resp:
if resp.status != 200:
raise cmn.BotHTTPError(resp)
data = io.BytesIO(await resp.read())
embed.set_image(url=f'attachment://wttr_forecast.png')
await ctx.send(embed=embed, file=discord.File(data, 'wttr_forecast.png'))
embed.set_image(url=f"attachment://wttr_forecast.png")
await ctx.send(embed=embed, file=discord.File(data, "wttr_forecast.png"))
@_weather_conditions.command(name='now', aliases=['n'], category=cmn.cat.weather)
@_weather_conditions.command(name="now", aliases=["n"], category=cmn.cat.weather)
async def _weather_conditions_now(self, ctx: commands.Context, *, location: str):
'''Posts an image of current Local Weather Conditions from [wttr.in](http://wttr.in/).
See help for weather command for possible location types. Add a `-c` or `-f` to use Celcius or Fahrenheit.'''
"""Posts an image of current Local Weather Conditions from [wttr.in](http://wttr.in/).
See help for weather command for possible location types. Add a `-c` or `-f` to use Celcius or Fahrenheit."""
async with ctx.typing():
try:
units_arg = re.search(self.wttr_units_regex, location).group(1)
except AttributeError:
units_arg = ''
if units_arg.lower() == 'f':
units = 'u'
elif units_arg.lower() == 'c':
units = 'm'
units_arg = ""
if units_arg.lower() == "f":
units = "u"
elif units_arg.lower() == "c":
units = "m"
else:
units = ''
units = ""
loc = self.wttr_units_regex.sub('', location).strip()
loc = self.wttr_units_regex.sub("", location).strip()
embed = cmn.embed_factory(ctx)
embed.title = f'Current Weather for {loc}'
embed.description = 'Data from [wttr.in](http://wttr.in/).'
embed.title = f"Current Weather for {loc}"
embed.description = "Data from [wttr.in](http://wttr.in/)."
embed.colour = cmn.colours.good
loc = loc.replace(' ', '+')
async with self.session.get(f'http://wttr.in/{loc}_0{units}pnFQ.png') as resp:
loc = loc.replace(" ", "+")
async with self.session.get(f"http://wttr.in/{loc}_0{units}pnFQ.png") as resp:
if resp.status != 200:
raise cmn.BotHTTPError(resp)
data = io.BytesIO(await resp.read())
embed.set_image(url=f'attachment://wttr_now.png')
await ctx.send(embed=embed, file=discord.File(data, 'wttr_now.png'))
embed.set_image(url=f"attachment://wttr_now.png")
await ctx.send(embed=embed, file=discord.File(data, "wttr_now.png"))
def setup(bot: commands.Bot):

View File

@ -24,5 +24,5 @@ authors = ("@ClassAbbyAmplifier#2229", "@0x5c#0639")
description = """A bot with various useful ham radio-related functions, written in Python."""
license = "Released under the GNU General Public License v2"
contributing = "Check out the source on GitHub, contributions welcome: https://github.com/classabbyamp/discord-qrm2"
release = '2.1.0'
bot_server = 'https://discord.gg/Ntbg3J4'
release = "2.1.0"
bot_server = "https://discord.gg/Ntbg3J4"

View File

@ -167,7 +167,7 @@ async def on_command_error(ctx: commands.Context, err: commands.CommandError):
await cmn.add_react(ctx.message, cmn.emojis.bangbang)
elif isinstance(err, (commands.CommandInvokeError, commands.ConversionError)):
# Emulating discord.py's default beaviour.
print('Ignoring exception in command {}:'.format(ctx.command), file=sys.stderr)
print("Ignoring exception in command {}:".format(ctx.command), file=sys.stderr)
traceback.print_exception(type(err), err, err.__traceback__, file=sys.stderr)
embed = cmn.error_embed_factory(ctx, err.original, bot.qrm.debug_mode)
@ -176,7 +176,7 @@ async def on_command_error(ctx: commands.Context, err: commands.CommandError):
await ctx.send(embed=embed)
else:
# Emulating discord.py's default beaviour. (safest bet)
print('Ignoring exception in command {}:'.format(ctx.command), file=sys.stderr)
print("Ignoring exception in command {}:".format(ctx.command), file=sys.stderr)
traceback.print_exception(type(err), err, err.__traceback__, file=sys.stderr)
await cmn.add_react(ctx.message, cmn.emojis.warning)
@ -221,7 +221,7 @@ async def _ensure_activity_fixed():
# --- Run ---
for ext in opt.exts:
bot.load_extension(ext_dir + '.' + ext)
bot.load_extension(ext_dir + "." + ext)
try:

View File

@ -11,45 +11,45 @@ from collections import OrderedDict
us_calls_title = "Valid US Vanity Callsigns"
us_calls_desc = ('#x# is the number of letters in the prefix and suffix of a callsign. '
'E.g., WY4RC would be a 2x2 callsign, with prefix WY and suffix RC.')
us_calls = OrderedDict([('**Group A** (Extra Only)', ('**Any:** K, N, W (1x2)\n'
' AA-AL, KA-KZ, NA-NZ, WA-WZ (2x1)\n'
' AA-AL (2x2)\n'
'*Except*\n'
'**Alaska:** AL, KL, NL, WL (2x1)\n'
'**Caribbean:** KP, NP, WP (2x1)\n'
'**Pacific:** AH, KH, NH, WH (2x1)')),
('**Group B** (Advanced and Extra Only)', ('**Any:** KA-KZ, NA-NZ, WA-WZ (2x2)\n'
'*Except*\n'
'**Alaska:** AL (2x2)\n'
'**Caribbean:** KP (2x2)\n'
'**Pacific:** AH (2x2)')),
('**Group C** (Technician, General, Advanced, Extra Only)', ('**Any Region:** K, N, W (1x3)\n'
'*Except*\n'
'**Alaska:** KL, NL, WL (2x2)\n'
'**Caribbean:** NP, WP (2x2)\n'
'**Pacific:** KH, NH, WH (2x2)')),
('**Group D** (Any License Class)', ('**Any Region:** KA-KZ, WA-WZ (2x3)\n'
'*Except*\n'
'**Alaska:** KL, WL (2x3)\n'
'**Caribbean:** KP, WP (2x3)\n'
'**Pacific:** KH, WH (2x3)')),
('**Unavailable**', ('- KA2AA-KA9ZZ: US Army in Japan\n'
'- KC4AAA-KC4AAF: NSF in Antartica\n'
'- KC4USA-KC4USZ: US Navy in Antartica\n'
'- KG4AA-KG4ZZ: US Navy in Guantanamo Bay\n'
'- KL9KAA-KL9KHZ: US military in Korea\n'
'- KC6AA-KC6ZZ: Former US (Eastern and Western Caroline Islands), '
'now Federated States of Micronesia (V6) and Republic of Palau (T8)\n'
'- KX6AA-KX6ZZ: Former US (Marshall Islands), '
'now Republic of the Marshall Islands (V73)\n'
'- Any suffix SOS or QRA-QUZ\n'
'- Any 2x3 with X as the first suffix letter\n'
'- Any 2x3 with AF, KF, NF, or WF prefix and suffix EMA: FEMA\n'
'- Any 2x3 with AA-AL, NA-NZ, WC, WK, WM, WR, or WT prefix: "Group X"\n'
'- Any 2x1, 2x2, or 2x3 with KP, NP, WP prefix and 0, 6, 7, 8, 9 number\n'
'- Any 1x1 callsign: Special Event'))])
us_calls_desc = ("#x# is the number of letters in the prefix and suffix of a callsign. "
"E.g., WY4RC would be a 2x2 callsign, with prefix WY and suffix RC.")
us_calls = OrderedDict([("**Group A** (Extra Only)", ("**Any:** K, N, W (1x2)\n"
" AA-AL, KA-KZ, NA-NZ, WA-WZ (2x1)\n"
" AA-AL (2x2)\n"
"*Except*\n"
"**Alaska:** AL, KL, NL, WL (2x1)\n"
"**Caribbean:** KP, NP, WP (2x1)\n"
"**Pacific:** AH, KH, NH, WH (2x1)")),
("**Group B** (Advanced and Extra Only)", ("**Any:** KA-KZ, NA-NZ, WA-WZ (2x2)\n"
"*Except*\n"
"**Alaska:** AL (2x2)\n"
"**Caribbean:** KP (2x2)\n"
"**Pacific:** AH (2x2)")),
("**Group C** (Technician, General, Advanced, Extra Only)", ("**Any Region:** K, N, W (1x3)\n"
"*Except*\n"
"**Alaska:** KL, NL, WL (2x2)\n"
"**Caribbean:** NP, WP (2x2)\n"
"**Pacific:** KH, NH, WH (2x2)")),
("**Group D** (Any License Class)", ("**Any Region:** KA-KZ, WA-WZ (2x3)\n"
"*Except*\n"
"**Alaska:** KL, WL (2x3)\n"
"**Caribbean:** KP, WP (2x3)\n"
"**Pacific:** KH, WH (2x3)")),
("**Unavailable**", ("- KA2AA-KA9ZZ: US Army in Japan\n"
"- KC4AAA-KC4AAF: NSF in Antartica\n"
"- KC4USA-KC4USZ: US Navy in Antartica\n"
"- KG4AA-KG4ZZ: US Navy in Guantanamo Bay\n"
"- KL9KAA-KL9KHZ: US military in Korea\n"
"- KC6AA-KC6ZZ: Former US (Eastern and Western Caroline Islands), "
"now Federated States of Micronesia (V6) and Republic of Palau (T8)\n"
"- KX6AA-KX6ZZ: Former US (Marshall Islands), "
"now Republic of the Marshall Islands (V73)\n"
"- Any suffix SOS or QRA-QUZ\n"
"- Any 2x3 with X as the first suffix letter\n"
"- Any 2x3 with AF, KF, NF, or WF prefix and suffix EMA: FEMA\n"
"- Any 2x3 with AA-AL, NA-NZ, WC, WK, WM, WR, or WT prefix: \"Group X\"\n"
"- Any 2x1, 2x2, or 2x3 with KP, NP, WP prefix and 0, 6, 7, 8, 9 number\n"
"- Any 1x1 callsign: Special Event"))])
# format: country: (title, description, text)
options = {'us': (us_calls_title, us_calls_desc, us_calls)}
options = {"us": (us_calls_title, us_calls_desc, us_calls)}

View File

@ -7,8 +7,8 @@ This file is part of discord-qrmbot and is released under the terms of the GNU
General Public License, version 2.
"""
phonetics = {'a': 'alfa', 'b': 'bravo', 'c': 'charlie', 'd': 'delta', 'e': 'echo', 'f': 'foxtrot',
'g': 'golf', 'h': 'hotel', 'i': 'india', 'j': 'juliett', 'k': 'kilo', 'l': 'lima',
'm': 'mike', 'n': 'november', 'o': 'oscar', 'p': 'papa', 'q': 'quebec', 'r': 'romeo',
's': 'sierra', 't': 'tango', 'u': 'uniform', 'v': 'victor', 'w': 'whiskey', 'x': 'x-ray',
'y': 'yankee', 'z': 'zulu'}
phonetics = {"a": "alfa", "b": "bravo", "c": "charlie", "d": "delta", "e": "echo", "f": "foxtrot",
"g": "golf", "h": "hotel", "i": "india", "j": "juliett", "k": "kilo", "l": "lima",
"m": "mike", "n": "november", "o": "oscar", "p": "papa", "q": "quebec", "r": "romeo",
"s": "sierra", "t": "tango", "u": "uniform", "v": "victor", "w": "whiskey", "x": "x-ray",
"y": "yankee", "z": "zulu"}

View File

@ -7,43 +7,43 @@ This file is part of discord-qrmbot and is released under the terms of the GNU
General Public License, version 2.
"""
pool_names = {'us': {'technician': 'E2',
'tech': 'E2',
't': 'E2',
'general': 'E3',
'gen': 'E3',
'g': 'E3',
'extra': 'E4',
'e': 'E4'},
'ca': {'basic': 'CA_B',
'b': 'CA_B',
'advanced': 'CA_A',
'adv': 'CA_A',
'a': 'CA_A',
'basic_fr': 'CA_FB',
'b_fr': 'CA_FB',
'base': 'CA_FB',
'advanced_fr': 'CA_FS',
'adv_fr': 'CA_FS',
'a_fr': 'CA_FS',
'supérieure': 'CA_FS',
'superieure': 'CA_FS',
's': 'CA_FS'},
'us_c': {'c1': 'C1',
'comm1': 'C1',
'c3': 'C3',
'comm3': 'C3',
'c6': 'C6',
'comm6': 'C6',
'c7': 'C7',
'comm7': 'C7',
'c7r': 'C7R',
'comm7r': 'C7R',
'c8': 'C8',
'comm8': 'C8',
'c9': 'C9',
'comm9': 'C9'}}
pool_names = {"us": {"technician": "E2",
"tech": "E2",
"t": "E2",
"general": "E3",
"gen": "E3",
"g": "E3",
"extra": "E4",
"e": "E4"},
"ca": {"basic": "CA_B",
"b": "CA_B",
"advanced": "CA_A",
"adv": "CA_A",
"a": "CA_A",
"basic_fr": "CA_FB",
"b_fr": "CA_FB",
"base": "CA_FB",
"advanced_fr": "CA_FS",
"adv_fr": "CA_FS",
"a_fr": "CA_FS",
"supérieure": "CA_FS",
"superieure": "CA_FS",
"s": "CA_FS"},
"us_c": {"c1": "C1",
"comm1": "C1",
"c3": "C3",
"comm3": "C3",
"c6": "C6",
"comm6": "C6",
"c7": "C7",
"comm7": "C7",
"c7r": "C7R",
"comm7r": "C7R",
"c8": "C8",
"comm8": "C8",
"c9": "C9",
"comm9": "C9"}}
pool_emojis = {'us': '🇺🇸',
'ca': '🇨🇦',
'us_c': '🇺🇸 🏢'}
pool_emojis = {"us": "🇺🇸",
"ca": "🇨🇦",
"us_c": "🇺🇸 🏢"}

View File

@ -27,7 +27,7 @@ debug = False
owners_uids = (200102491231092736,)
# The extensions to load when running the bot.
exts = ['ae7q', 'base', 'fun', 'grid', 'ham', 'image', 'lookup', 'morse', 'qrz', 'study', 'weather']
exts = ["ae7q", "base", "fun", "grid", "ham", "image", "lookup", "morse", "qrz", "study", "weather"]
# Either "time", "random", or "fixed" (first item in statuses)
status_mode = "fixed"
@ -37,17 +37,17 @@ statuses = ["with lids on the air", "with fire"]
# Timezone for the status (string)
# See https://pythonhosted.org/pytz/ for more info
status_tz = 'US/Eastern'
status_tz = "US/Eastern"
# The text to put in the "playing" status, with start and stop times
time_statuses = [('with lids on 3.840', (00, 00), (6, 00)),
('with lids on 7.200', (6, 00), (10, 00)),
('with lids on 14.313', (10, 00), (18, 00)),
('with lids on 7.200', (18, 00), (20, 00)),
('with lids on 3.840', (20, 00), (23, 59))]
time_statuses = [("with lids on 3.840", (00, 00), (6, 00)),
("with lids on 7.200", (6, 00), (10, 00)),
("with lids on 14.313", (10, 00), (18, 00)),
("with lids on 7.200", (18, 00), (20, 00)),
("with lids on 3.840", (20, 00), (23, 59))]
# Emoji IDs and keywords for emoji reactions
# Use the format {emoji_id (int): ('tuple', 'of', 'lowercase', 'keywords')}
# Use the format {emoji_id (int): ("tuple", "of", "lowercase", "keywords")}
msg_reacts = {}
# A :pika: emote's ID, None for no emote :c