Custom Help Command (#55)

* subclasses HelpCommand to implement a custom help command.

add cog names for display in the help command.

Globalsettings is no longer a cog. HelpCommand uses the custom attribute
category for grouping commands.

* PEP8 fixes

* improve variable names for help command clarity. Fixes #70

* improve help command formatting

add aliases for weather commands
add help text for ae7q call and qrz

* move global_settings to common

* rename import alias

* fix loading/unloading of help command

* fix info command

* fix options import

* changed canonical command names to be more descriptive

* remove cog names and revert map/bandplan error listing

* add link to hamstudy references
This commit is contained in:
Abigail Gold 2019-12-06 01:19:42 -05:00 committed by GitHub
parent f821a2e2f2
commit ad2c559a0f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 257 additions and 140 deletions

View File

@ -21,20 +21,22 @@ import discord.ext.commands as commands
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
import aiohttp import aiohttp
import common as cmn
class AE7QCog(commands.Cog): class AE7QCog(commands.Cog):
def __init__(self, bot: commands.Bot): def __init__(self, bot: commands.Bot):
self.bot = bot self.bot = bot
self.gs = bot.get_cog("GlobalSettings")
@commands.group(name="ae7q", aliases=["ae"]) @commands.group(name="ae7q", aliases=["ae"], category=cmn.cat.lookup)
async def _ae7q_lookup(self, ctx: commands.Context): async def _ae7q_lookup(self, ctx: commands.Context):
'''Look up a callsign, FRN, or Licensee ID on ae7q.com''' '''Look up a callsign, FRN, or Licensee ID on [ae7q.com](http://ae7q.com/).'''
if ctx.invoked_subcommand is None: if ctx.invoked_subcommand is None:
await ctx.send_help(ctx.command) await ctx.send_help(ctx.command)
@_ae7q_lookup.command(name="call") @_ae7q_lookup.command(name="call", category=cmn.cat.lookup)
async def _ae7q_call(self, ctx: commands.Context, callsign: str): async def _ae7q_call(self, ctx: commands.Context, callsign: str):
'''Look up the history for a callsign on [ae7q.com](http://ae7q.com/).'''
callsign = callsign.upper() callsign = callsign.upper()
desc = '' desc = ''
base_url = "http://ae7q.com/query/data/CallHistory.php?CALL=" base_url = "http://ae7q.com/query/data/CallHistory.php?CALL="
@ -61,7 +63,7 @@ class AE7QCog(commands.Cog):
if rows is None: if rows is None:
embed = discord.Embed(title=f"AE7Q History for {callsign}", embed = discord.Embed(title=f"AE7Q History for {callsign}",
colour=self.gs.colours.bad, colour=cmn.colours.bad,
url=f"{base_url}{callsign}", url=f"{base_url}{callsign}",
timestamp=datetime.utcnow()) timestamp=datetime.utcnow())
embed.set_footer(text=ctx.author.name, embed.set_footer(text=ctx.author.name,
@ -91,7 +93,7 @@ class AE7QCog(commands.Cog):
table_contents += [row_cells] table_contents += [row_cells]
embed = discord.Embed(title=f"AE7Q Records for {callsign}", embed = discord.Embed(title=f"AE7Q Records for {callsign}",
colour=self.gs.colours.good, colour=cmn.colours.good,
url=f"{base_url}{callsign}", url=f"{base_url}{callsign}",
timestamp=datetime.utcnow()) timestamp=datetime.utcnow())
@ -118,20 +120,20 @@ class AE7QCog(commands.Cog):
# TODO: write commands for other AE7Q response types? # TODO: write commands for other AE7Q response types?
# @_ae7q_lookup.command(name="trustee") # @_ae7q_lookup.command(name="trustee")
# async def _ae7q_trustee(self, ctx, callsign: str): # async def _ae7q_trustee(self, ctx: commands.Context, callsign: str):
# pass # pass
# @_ae7q_lookup.command(name="applications", aliases=['apps']) # @_ae7q_lookup.command(name="applications", aliases=['apps'])
# async def _ae7q_applications(self, ctx, callsign: str): # async def _ae7q_applications(self, ctx: commands.Context, callsign: str):
# pass # pass
# @_ae7q_lookup.command(name="frn") # @_ae7q_lookup.command(name="frn")
# async def _ae7q_frn(self, ctx, frn: str): # async def _ae7q_frn(self, ctx: commands.Context, frn: str):
# base_url = "http://ae7q.com/query/data/FrnHistory.php?FRN=" # base_url = "http://ae7q.com/query/data/FrnHistory.php?FRN="
# pass # pass
# @_ae7q_lookup.command(name="licensee", aliases=["lic"]) # @_ae7q_lookup.command(name="licensee", aliases=["lic"])
# async def _ae7q_licensee(self, ctx, frn: str): # async def _ae7q_licensee(self, ctx: commands.Context, frn: str):
# base_url = "http://ae7q.com/query/data/LicenseeIdHistory.php?ID=" # base_url = "http://ae7q.com/query/data/LicenseeIdHistory.php?ID="
# pass # pass

View File

@ -15,26 +15,114 @@ import random
import discord import discord
import discord.ext.commands as commands import discord.ext.commands as commands
import info
from data import options as opt
import common as cmn
class QrmHelpCommand(commands.HelpCommand):
def __init__(self):
super().__init__(command_attrs={'help': 'Shows help about qrm or a command',
'aliases': ['h']})
def get_bot_mapping(self):
bot = self.context.bot
mapping = {}
for cmd in bot.commands:
cat = cmd.__original_kwargs__.get('category', None)
if cat in mapping:
mapping[cat].append(cmd)
else:
mapping[cat] = [cmd]
return mapping
def get_command_signature(self, command):
parent = command.full_parent_name
if command.aliases != []:
aliases = ', '.join(command.aliases)
fmt = command.name
if parent:
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}'
async def send_error_message(self, error):
embed = discord.Embed(title='qrm Help Error',
description=error,
colour=cmn.colours.bad,
timestamp=datetime.utcnow()
)
embed.set_footer(text=self.context.author.name,
icon_url=str(self.context.author.avatar_url))
await self.context.send(embed=embed)
async def send_bot_help(self, mapping):
embed = discord.Embed(title='qrm Help',
description=(f'For command-specific help and usage, use `{opt.prefix}help [command name]`'
'. Many commands have shorter aliases.'),
colour=cmn.colours.neutral,
timestamp=datetime.utcnow()
)
embed.set_footer(text=self.context.author.name,
icon_url=str(self.context.author.avatar_url))
for cat, cmds in mapping.items():
cmds = list(filter(lambda x: not x.hidden, cmds))
if cmds == []:
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)
else:
embed.add_field(name='Other', value=', '.join(names), inline=False)
await self.context.send(embed=embed)
async def send_command_help(self, command):
embed = discord.Embed(title=self.get_command_signature(command),
description=command.help,
colour=cmn.colours.neutral,
timestamp=datetime.utcnow()
)
embed.set_footer(text=self.context.author.name,
icon_url=str(self.context.author.avatar_url))
await self.context.send(embed=embed)
async def send_group_help(self, group):
embed = discord.Embed(title=self.get_command_signature(group),
description=group.help,
colour=cmn.colours.neutral,
timestamp=datetime.utcnow()
)
embed.set_footer(text=self.context.author.name,
icon_url=str(self.context.author.avatar_url))
for cmd in group.commands:
embed.add_field(name=self.get_command_signature(cmd), value=cmd.help, inline=False)
await self.context.send(embed=embed)
class BaseCog(commands.Cog): class BaseCog(commands.Cog):
def __init__(self, bot: commands.Bot): def __init__(self, bot: commands.Bot):
self.bot = bot self.bot = bot
self.gs = bot.get_cog("GlobalSettings")
self.changelog = parse_changelog() self.changelog = parse_changelog()
@commands.command(name="info", aliases=["about"]) @commands.command(name="info", aliases=["about"])
async def _info(self, ctx): async def _info(self, ctx: commands.Context):
"""Shows info about qrm.""" """Shows info about qrm."""
embed = discord.Embed(title="About qrm", embed = discord.Embed(title="About qrm",
description=self.gs.info.description, description=info.description,
colour=self.gs.colours.neutral, colour=cmn.colours.neutral,
timestamp=datetime.utcnow()) timestamp=datetime.utcnow())
embed.set_footer(text=ctx.author.name, embed.set_footer(text=ctx.author.name,
icon_url=str(ctx.author.avatar_url)) icon_url=str(ctx.author.avatar_url))
embed = embed.add_field(name="Authors", value=", ".join(info.authors))
embed = embed.add_field(name="License", value=info.license)
embed = embed.add_field(name="Version", value=f'v{info.release}')
embed = embed.add_field(name="Contributing", value=info.contributing, inline=False)
embed.set_thumbnail(url=str(self.bot.user.avatar_url)) embed.set_thumbnail(url=str(self.bot.user.avatar_url))
embed = embed.add_field(name="Authors", value=", ".join(self.gs.info.authors))
embed = embed.add_field(name="Contributing", value=self.gs.info.contributing)
embed = embed.add_field(name="License", value=self.gs.info.license)
await ctx.send(embed=embed) await ctx.send(embed=embed)
@commands.command(name="ping") @commands.command(name="ping")
@ -43,7 +131,7 @@ class BaseCog(commands.Cog):
content = ctx.message.author.mention if random.random() < 0.05 else '' content = ctx.message.author.mention if random.random() < 0.05 else ''
embed = discord.Embed(title="**Pong!**", embed = discord.Embed(title="**Pong!**",
description=f'Current ping is {self.bot.latency*1000:.1f} ms', description=f'Current ping is {self.bot.latency*1000:.1f} ms',
colour=self.gs.colours.neutral, colour=cmn.colours.neutral,
timestamp=datetime.utcnow()) timestamp=datetime.utcnow())
embed.set_footer(text=ctx.author.name, embed.set_footer(text=ctx.author.name,
icon_url=str(ctx.author.avatar_url)) icon_url=str(ctx.author.avatar_url))
@ -55,7 +143,7 @@ class BaseCog(commands.Cog):
embed = discord.Embed(title="qrm Changelog", embed = discord.Embed(title="qrm Changelog",
description=("For a full listing, visit [Github](https://" description=("For a full listing, visit [Github](https://"
"github.com/classabbyamp/discord-qrm-bot/blob/master/CHANGELOG.md)."), "github.com/classabbyamp/discord-qrm-bot/blob/master/CHANGELOG.md)."),
colour=self.gs.colours.neutral, colour=cmn.colours.neutral,
timestamp=datetime.utcnow()) timestamp=datetime.utcnow())
embed.set_footer(text=ctx.author.name, embed.set_footer(text=ctx.author.name,
icon_url=str(ctx.author.avatar_url)) icon_url=str(ctx.author.avatar_url))
@ -111,3 +199,9 @@ async def format_changelog(log: dict):
def setup(bot: commands.Bot): def setup(bot: commands.Bot):
bot.add_cog(BaseCog(bot)) bot.add_cog(BaseCog(bot))
bot._original_help_command = bot.help_command
bot.help_command = QrmHelpCommand()
def teardown(bot: commands.Bot):
bot.help_command = bot._original_help_command

View File

@ -9,23 +9,24 @@ General Public License, version 2.
import discord.ext.commands as commands import discord.ext.commands as commands
import common as cmn
class FunCog(commands.Cog): class FunCog(commands.Cog):
def __init__(self, bot: commands.Bot): def __init__(self, bot: commands.Bot):
self.bot = bot self.bot = bot
self.gs = bot.get_cog("GlobalSettings")
@commands.command(name="xkcd", aliases=['x']) @commands.command(name="xkcd", aliases=['x'], category=cmn.cat.fun)
async def _xkcd(self, ctx: commands.Context, num: str): async def _xkcd(self, ctx: commands.Context, number: str):
'''Look up an xkcd by number.''' '''Look up an xkcd by number.'''
await ctx.send('http://xkcd.com/' + num) await ctx.send('http://xkcd.com/' + number)
@commands.command(name="tar") @commands.command(name="tar", category=cmn.cat.fun)
async def _tar(self, ctx: commands.Context): async def _tar(self, ctx: commands.Context):
'''Returns an xkcd about tar.''' '''Returns an xkcd about tar.'''
await ctx.send('http://xkcd.com/1168') await ctx.send('http://xkcd.com/1168')
@commands.command(name="xd") @commands.command(name="xd", hidden=True, category=cmn.cat.fun)
async def _xd(self, ctx: commands.Context): async def _xd(self, ctx: commands.Context):
'''ecks dee''' '''ecks dee'''
await ctx.send('ECKS DEE :smirk:') await ctx.send('ECKS DEE :smirk:')

View File

@ -13,17 +13,17 @@ from datetime import datetime
import discord import discord
import discord.ext.commands as commands import discord.ext.commands as commands
import common as cmn
class GridCog(commands.Cog): class GridCog(commands.Cog):
def __init__(self, bot: commands.Bot): def __init__(self, bot: commands.Bot):
self.bot = bot self.bot = bot
self.gs = bot.get_cog("GlobalSettings")
@commands.command(name="grid") @commands.command(name="grid", category=cmn.cat.maps)
async def _grid_sq_lookup(self, ctx: commands.Context, lat: str, lon: str): async def _grid_sq_lookup(self, ctx: commands.Context, lat: str, lon: str):
'''Calculates the grid square for latitude and longitude coordinates. '''Calculates the grid square for latitude and longitude coordinates,
Usage: `?grid <lat> <lon>` with negative being latitude South and longitude West.'''
`lat` and `lon` are decimal coordinates, with negative being latitude South and longitude West.'''
with ctx.typing(): with ctx.typing():
grid = "**" grid = "**"
try: try:
@ -39,7 +39,7 @@ class GridCog(commands.Cog):
grid += "**" grid += "**"
embed = discord.Embed(title=f'Maidenhead Grid Locator for {float(lat):.6f}, {float(lon):.6f}', embed = discord.Embed(title=f'Maidenhead Grid Locator for {float(lat):.6f}, {float(lon):.6f}',
description=grid, description=grid,
colour=self.gs.colours.good, colour=cmn.colours.good,
timestamp=datetime.utcnow()) timestamp=datetime.utcnow())
embed.set_footer(text=ctx.author.name, embed.set_footer(text=ctx.author.name,
icon_url=str(ctx.author.avatar_url)) icon_url=str(ctx.author.avatar_url))
@ -48,16 +48,16 @@ class GridCog(commands.Cog):
except ValueError as err: except ValueError as err:
msg = f'Error generating grid square for {lat}, {lon}.' msg = f'Error generating grid square for {lat}, {lon}.'
embed = discord.Embed(title=msg, description=str(err), embed = discord.Embed(title=msg, description=str(err),
colour=self.gs.colours.bad, colour=cmn.colours.bad,
timestamp=datetime.utcnow()) timestamp=datetime.utcnow())
embed.set_footer(text=ctx.author.name, embed.set_footer(text=ctx.author.name,
icon_url=str(ctx.author.avatar_url)) icon_url=str(ctx.author.avatar_url))
await ctx.send(embed=embed) await ctx.send(embed=embed)
@commands.command(name="ungrid", aliases=['loc']) @commands.command(name="ungrid", aliases=['loc'], category=cmn.cat.maps)
async def _location_lookup(self, ctx: commands.Context, grid: str, grid2: str = None): 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. '''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.''' If two grid squares are given, the distance and azimuth between them is calculated.'''
with ctx.typing(): with ctx.typing():
if grid2 is None or grid2 == '': if grid2 is None or grid2 == '':
try: try:
@ -67,14 +67,14 @@ class GridCog(commands.Cog):
if len(grid) >= 6: if len(grid) >= 6:
embed = discord.Embed(title=f'Latitude and Longitude for {grid}', embed = discord.Embed(title=f'Latitude and Longitude for {grid}',
description=f'**{loc[0]:.5f}, {loc[1]:.5f}**', description=f'**{loc[0]:.5f}, {loc[1]:.5f}**',
colour=self.gs.colours.good, colour=cmn.colours.good,
url=f'https://www.openstreetmap.org/#map=13/{loc[0]:.5f}/{loc[1]:.5f}', url=f'https://www.openstreetmap.org/#map=13/{loc[0]:.5f}/{loc[1]:.5f}',
timestamp=datetime.utcnow()) timestamp=datetime.utcnow())
else: else:
embed = discord.Embed(title=f'Latitude and Longitude for {grid}', embed = discord.Embed(title=f'Latitude and Longitude for {grid}',
description=f'**{loc[0]:.1f}, {loc[1]:.1f}**', description=f'**{loc[0]:.1f}, {loc[1]:.1f}**',
colour=self.gs.colours.good, colour=cmn.colours.good,
url=f'https://www.openstreetmap.org/#map=10/{loc[0]:.1f}/{loc[1]:.1f}', url=f'https://www.openstreetmap.org/#map=10/{loc[0]:.1f}/{loc[1]:.1f}',
timestamp=datetime.utcnow()) timestamp=datetime.utcnow())
embed.set_footer(text=ctx.author.name, embed.set_footer(text=ctx.author.name,
@ -82,7 +82,7 @@ class GridCog(commands.Cog):
except Exception as e: except Exception as e:
msg = f'Error generating latitude and longitude for grid {grid}.' msg = f'Error generating latitude and longitude for grid {grid}.'
embed = discord.Embed(title=msg, description=str(e), embed = discord.Embed(title=msg, description=str(e),
colour=self.gs.colours.bad, colour=cmn.colours.bad,
timestamp=datetime.utcnow()) timestamp=datetime.utcnow())
embed.set_footer(text=ctx.author.name, embed.set_footer(text=ctx.author.name,
icon_url=str(ctx.author.avatar_url)) icon_url=str(ctx.author.avatar_url))
@ -113,14 +113,14 @@ class GridCog(commands.Cog):
des = f'**Distance:** {d:.1f} km ({d_mi:.1f} mi)\n**Bearing:** {bearing:.1f}°' des = f'**Distance:** {d:.1f} km ({d_mi:.1f} mi)\n**Bearing:** {bearing:.1f}°'
embed = discord.Embed(title=f'Great Circle Distance and Bearing from {grid} to {grid2}', embed = discord.Embed(title=f'Great Circle Distance and Bearing from {grid} to {grid2}',
description=des, description=des,
colour=self.gs.colours.good, colour=cmn.colours.good,
timestamp=datetime.utcnow()) timestamp=datetime.utcnow())
embed.set_footer(text=ctx.author.name, embed.set_footer(text=ctx.author.name,
icon_url=str(ctx.author.avatar_url)) icon_url=str(ctx.author.avatar_url))
except Exception as e: except Exception as e:
msg = f'Error generating great circle distance and bearing from {grid} and {grid2}.' msg = f'Error generating great circle distance and bearing from {grid} and {grid2}.'
embed = discord.Embed(title=msg, description=str(e), embed = discord.Embed(title=msg, description=str(e),
colour=self.gs.colours.bad, colour=cmn.colours.bad,
timestamp=datetime.utcnow()) timestamp=datetime.utcnow())
embed.set_footer(text=ctx.author.name, embed.set_footer(text=ctx.author.name,
icon_url=str(ctx.author.avatar_url)) icon_url=str(ctx.author.avatar_url))

View File

@ -13,36 +13,36 @@ from datetime import datetime
import discord import discord
import discord.ext.commands as commands import discord.ext.commands as commands
import common as cmn
from resources import callsign_info from resources import callsign_info
class HamCog(commands.Cog): class HamCog(commands.Cog):
def __init__(self, bot: commands.Bot): def __init__(self, bot: commands.Bot):
self.bot = bot self.bot = bot
self.gs = bot.get_cog("GlobalSettings")
with open('resources/qcodes.json') as qcode_file: with open('resources/qcodes.json') as qcode_file:
self.qcodes = json.load(qcode_file) self.qcodes = json.load(qcode_file)
with open('resources/words') as words_file: with open('resources/words') as words_file:
self.words = words_file.read().lower().splitlines() self.words = words_file.read().lower().splitlines()
@commands.command(name="qcode", aliases=['q']) @commands.command(name="qcode", aliases=['q'], category=cmn.cat.ref)
async def _qcode_lookup(self, ctx: commands.Context, qcode: str): async def _qcode_lookup(self, ctx: commands.Context, qcode: str):
'''Look up a Q Code.''' '''Look up a Q Code.'''
with ctx.typing(): with ctx.typing():
qcode = qcode.upper() qcode = qcode.upper()
if qcode in self.qcodes: if qcode in self.qcodes:
embed = discord.Embed(title=qcode, description=self.qcodes[qcode], embed = discord.Embed(title=qcode, description=self.qcodes[qcode],
colour=self.gs.colours.good, colour=cmn.colours.good,
timestamp=datetime.utcnow()) timestamp=datetime.utcnow())
else: else:
embed = discord.Embed(title=f'Q Code {qcode} not found', embed = discord.Embed(title=f'Q Code {qcode} not found',
colour=self.gs.colours.bad, colour=cmn.colours.bad,
timestamp=datetime.utcnow()) timestamp=datetime.utcnow())
embed.set_footer(text=ctx.author.name, embed.set_footer(text=ctx.author.name,
icon_url=str(ctx.author.avatar_url)) icon_url=str(ctx.author.avatar_url))
await ctx.send(embed=embed) await ctx.send(embed=embed)
@commands.command(name="phonetics", aliases=['ph', 'phoneticize', 'phoneticise', 'phone']) @commands.command(name="phonetics", aliases=['ph', 'phoneticize', 'phoneticise', 'phone'], category=cmn.cat.fun)
async def _phonetics_lookup(self, ctx: commands.Context, *, msg: str): 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(): with ctx.typing():
@ -55,13 +55,13 @@ class HamCog(commands.Cog):
result += ' ' result += ' '
embed = discord.Embed(title=f'Phonetics for {msg}', embed = discord.Embed(title=f'Phonetics for {msg}',
description=result.title(), description=result.title(),
colour=self.gs.colours.good, colour=cmn.colours.good,
timestamp=datetime.utcnow()) timestamp=datetime.utcnow())
embed.set_footer(text=ctx.author.name, embed.set_footer(text=ctx.author.name,
icon_url=str(ctx.author.avatar_url)) icon_url=str(ctx.author.avatar_url))
await ctx.send(embed=embed) await ctx.send(embed=embed)
@commands.command(name="utc", aliases=['z']) @commands.command(name="utc", aliases=['z'], category=cmn.cat.ref)
async def _utc_lookup(self, ctx: commands.Context): async def _utc_lookup(self, ctx: commands.Context):
'''Gets the current time in UTC.''' '''Gets the current time in UTC.'''
with ctx.typing(): with ctx.typing():
@ -69,13 +69,13 @@ class HamCog(commands.Cog):
result = '**' + now.strftime('%Y-%m-%d %H:%M') + 'Z**' result = '**' + now.strftime('%Y-%m-%d %H:%M') + 'Z**'
embed = discord.Embed(title='The current time is:', embed = discord.Embed(title='The current time is:',
description=result, description=result,
colour=self.gs.colours.good, colour=cmn.colours.good,
timestamp=datetime.utcnow()) timestamp=datetime.utcnow())
embed.set_footer(text=ctx.author.name, embed.set_footer(text=ctx.author.name,
icon_url=str(ctx.author.avatar_url)) icon_url=str(ctx.author.avatar_url))
await ctx.send(embed=embed) await ctx.send(embed=embed)
@commands.command(name="vanities", aliases=["vanity", "pfx", "prefixes", "prefix"]) @commands.command(name="prefixes", aliases=["vanity", "pfx", "vanities", "prefix"])
async def _vanity_prefixes(self, ctx: commands.Context, country: str = None): 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: if country is None:

View File

@ -15,22 +15,22 @@ import discord.ext.commands as commands
import aiohttp import aiohttp
import common as cmn
class ImageCog(commands.Cog): class ImageCog(commands.Cog):
def __init__(self, bot: commands.Bot): def __init__(self, bot: commands.Bot):
self.bot = bot self.bot = bot
self.gs = bot.get_cog("GlobalSettings")
@commands.command(name="plan", aliases=['bands']) @commands.command(name="bandplan", aliases=['plan', 'bands'], category=cmn.cat.ref)
async def _bandplan(self, ctx: commands.Context, msg: str = ''): async def _bandplan(self, ctx: commands.Context, region: str = ''):
'''Posts an image of Frequency Allocations. '''Posts an image of Frequency Allocations.'''
Optional argument: `cn`, `ca`, `nl`, `us`, `mx`.'''
name = {'cn': 'Chinese', name = {'cn': 'Chinese',
'ca': 'Canadian', 'ca': 'Canadian',
'nl': 'Dutch', 'nl': 'Dutch',
'us': 'US', 'us': 'US',
'mx': 'Mexican'} 'mx': 'Mexican'}
arg = msg.lower() arg = region.lower()
with ctx.typing(): with ctx.typing():
if arg not in name: if arg not in name:
@ -39,7 +39,7 @@ class ImageCog(commands.Cog):
desc += f'`{abbrev}`: {title}\n' desc += f'`{abbrev}`: {title}\n'
embed = discord.Embed(title=f'Bandplan Not Found!', embed = discord.Embed(title=f'Bandplan Not Found!',
description=desc, description=desc,
colour=self.gs.colours.bad, colour=cmn.colours.bad,
timestamp=datetime.utcnow()) timestamp=datetime.utcnow())
embed.set_footer(text=ctx.author.name, embed.set_footer(text=ctx.author.name,
icon_url=str(ctx.author.avatar_url)) icon_url=str(ctx.author.avatar_url))
@ -48,7 +48,7 @@ class ImageCog(commands.Cog):
img = discord.File(f"resources/images/bandchart/{arg}bandchart.png", img = discord.File(f"resources/images/bandchart/{arg}bandchart.png",
filename=f'{arg}bandchart.png') filename=f'{arg}bandchart.png')
embed = discord.Embed(title=f'{name[arg]} Amateur Radio Bands', embed = discord.Embed(title=f'{name[arg]} Amateur Radio Bands',
colour=self.gs.colours.good, colour=cmn.colours.good,
timestamp=datetime.utcnow()) timestamp=datetime.utcnow())
embed.set_image(url=f'attachment://{arg}bandchart.png') embed.set_image(url=f'attachment://{arg}bandchart.png')
embed.set_footer(text=ctx.author.name, embed.set_footer(text=ctx.author.name,
@ -56,20 +56,20 @@ class ImageCog(commands.Cog):
await ctx.send(embed=embed, file=img) await ctx.send(embed=embed, file=img)
@commands.command(name="grayline", aliases=['greyline', 'grey', 'gray', 'gl']) @commands.command(name="grayline", aliases=['greyline', 'grey', 'gray', 'gl'], category=cmn.cat.maps)
async def _grayline(self, ctx: commands.Context): 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.'''
gl_url = ('http://www.fourmilab.ch/cgi-bin/uncgi/Earth?img=NOAAtopo.evif' 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=') '&imgsize=320&dynimg=y&opt=-p&lat=&lon=&alt=&tle=&date=0&utc=&jd=')
with ctx.typing(): with ctx.typing():
embed = discord.Embed(title='Current Greyline Conditions', embed = discord.Embed(title='Current Greyline Conditions',
colour=self.gs.colours.good, colour=cmn.colours.good,
timestamp=datetime.utcnow()) timestamp=datetime.utcnow())
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
async with session.get(gl_url) as resp: async with session.get(gl_url) as resp:
if resp.status != 200: if resp.status != 200:
embed.description = 'Could not download file...' embed.description = 'Could not download file...'
embed.colour = self.gs.colours.bad embed.colour = cmn.colours.bad
else: else:
data = io.BytesIO(await resp.read()) data = io.BytesIO(await resp.read())
embed.set_image(url=f'attachment://greyline.jpg') embed.set_image(url=f'attachment://greyline.jpg')
@ -77,11 +77,9 @@ class ImageCog(commands.Cog):
icon_url=str(ctx.author.avatar_url)) icon_url=str(ctx.author.avatar_url))
await ctx.send(embed=embed, file=discord.File(data, 'greyline.jpg')) await ctx.send(embed=embed, file=discord.File(data, 'greyline.jpg'))
@commands.command(name="map") @commands.command(name="map", category=cmn.cat.maps)
async def _map(self, ctx: commands.Context, msg: str = ''): async def _map(self, ctx: commands.Context, map_id: str = ''):
'''Posts an image of Frequency Allocations. '''Posts an image of a ham-relevant map.'''
Optional argument:`cq` = CQ Zones, `itu` = ITU Zones, `arrl` or `rac` =
ARRL/RAC sections, `cn` = Chinese Callsign Areas, `us` = US Callsign Areas.'''
map_titles = {"cq": 'Worldwide CQ Zones Map', map_titles = {"cq": 'Worldwide CQ Zones Map',
"itu": 'Worldwide ITU Zones Map', "itu": 'Worldwide ITU Zones Map',
"arrl": 'ARRL/RAC Section Map', "arrl": 'ARRL/RAC Section Map',
@ -89,7 +87,7 @@ class ImageCog(commands.Cog):
"cn": 'Chinese Callsign Areas', "cn": 'Chinese Callsign Areas',
"us": 'US Callsign Areas'} "us": 'US Callsign Areas'}
arg = msg.lower() arg = map_id.lower()
with ctx.typing(): with ctx.typing():
if arg not in map_titles: if arg not in map_titles:
desc = 'Possible arguments are:\n' desc = 'Possible arguments are:\n'
@ -97,7 +95,7 @@ class ImageCog(commands.Cog):
desc += f'`{abbrev}`: {title}\n' desc += f'`{abbrev}`: {title}\n'
embed = discord.Embed(title=f'Map Not Found!', embed = discord.Embed(title=f'Map Not Found!',
description=desc, description=desc,
colour=self.gs.colours.bad, colour=cmn.colours.bad,
timestamp=datetime.utcnow()) timestamp=datetime.utcnow())
embed.set_footer(text=ctx.author.name, embed.set_footer(text=ctx.author.name,
icon_url=str(ctx.author.avatar_url)) icon_url=str(ctx.author.avatar_url))
@ -106,7 +104,7 @@ class ImageCog(commands.Cog):
img = discord.File(f"resources/images/map/{arg}map.png", img = discord.File(f"resources/images/map/{arg}map.png",
filename=f'{arg}map.png') filename=f'{arg}map.png')
embed = discord.Embed(title=f'{map_titles[arg]} Map', embed = discord.Embed(title=f'{map_titles[arg]} Map',
colour=self.gs.colours.good, colour=cmn.colours.good,
timestamp=datetime.utcnow()) timestamp=datetime.utcnow())
embed.set_image(url=f'attachment://{arg}map.png') embed.set_image(url=f'attachment://{arg}map.png')
embed.set_footer(text=ctx.author.name, embed.set_footer(text=ctx.author.name,

View File

@ -13,16 +13,17 @@ from datetime import datetime
import discord import discord
import discord.ext.commands as commands import discord.ext.commands as commands
import common as cmn
class MorseCog(commands.Cog): class MorseCog(commands.Cog):
def __init__(self, bot: commands.Bot): def __init__(self, bot: commands.Bot):
self.bot = bot self.bot = bot
self.gs = bot.get_cog("GlobalSettings")
with open('resources/morse.json') as morse_file: with open('resources/morse.json') as morse_file:
self.ascii2morse = json.load(morse_file) self.ascii2morse = json.load(morse_file)
self.morse2ascii = {v: k for k, v in self.ascii2morse.items()} self.morse2ascii = {v: k for k, v in self.ascii2morse.items()}
@commands.command(name="morse", aliases=['cw']) @commands.command(name="morse", aliases=['cw'], category=cmn.cat.ref)
async def _morse(self, ctx: commands.Context, *, msg: str): async def _morse(self, ctx: commands.Context, *, msg: str):
"""Converts ASCII to international morse code.""" """Converts ASCII to international morse code."""
with ctx.typing(): with ctx.typing():
@ -35,13 +36,13 @@ class MorseCog(commands.Cog):
result += ' ' result += ' '
embed = discord.Embed(title=f'Morse Code for {msg}', embed = discord.Embed(title=f'Morse Code for {msg}',
description=result, description=result,
colour=self.gs.colours.good, colour=cmn.colours.good,
timestamp=datetime.utcnow()) timestamp=datetime.utcnow())
embed.set_footer(text=ctx.author.name, embed.set_footer(text=ctx.author.name,
icon_url=str(ctx.author.avatar_url)) icon_url=str(ctx.author.avatar_url))
await ctx.send(embed=embed) await ctx.send(embed=embed)
@commands.command(name="unmorse", aliases=['demorse', 'uncw', 'decw']) @commands.command(name="unmorse", aliases=['demorse', 'uncw', 'decw'], category=cmn.cat.ref)
async def _unmorse(self, ctx: commands.Context, *, msg: str): async def _unmorse(self, ctx: commands.Context, *, msg: str):
'''Converts international morse code to ASCII.''' '''Converts international morse code to ASCII.'''
with ctx.typing(): with ctx.typing():
@ -58,15 +59,15 @@ class MorseCog(commands.Cog):
result += ' ' result += ' '
embed = discord.Embed(title=f'ASCII for {msg0}', embed = discord.Embed(title=f'ASCII for {msg0}',
description=result, description=result,
colour=self.gs.colours.good, colour=cmn.colours.good,
timestamp=datetime.utcnow()) timestamp=datetime.utcnow())
embed.set_footer(text=ctx.author.name, embed.set_footer(text=ctx.author.name,
icon_url=str(ctx.author.avatar_url)) icon_url=str(ctx.author.avatar_url))
await ctx.send(embed=embed) await ctx.send(embed=embed)
@commands.command(name="weight", aliases=["cwweight", 'cww']) @commands.command(name="cwweight", aliases=["weight", 'cww'], category=cmn.cat.ref)
async def _weight(self, ctx: commands.Context, msg: str): async def _weight(self, ctx: commands.Context, *, msg: str):
'''Calculates the CW Weight of a callsign.''' '''Calculates the CW Weight of a callsign or message.'''
with ctx.typing(): with ctx.typing():
msg = msg.upper() msg = msg.upper()
weight = 0 weight = 0
@ -81,7 +82,7 @@ class MorseCog(commands.Cog):
res = f'The CW weight is **{weight}**' res = f'The CW weight is **{weight}**'
embed = discord.Embed(title=f'CW Weight of {msg}', embed = discord.Embed(title=f'CW Weight of {msg}',
description=res, description=res,
colour=self.gs.colours.good, colour=cmn.colours.good,
timestamp=datetime.utcnow()) timestamp=datetime.utcnow())
embed.set_footer(text=ctx.author.name, embed.set_footer(text=ctx.author.name,
icon_url=str(ctx.author.avatar_url)) icon_url=str(ctx.author.avatar_url))

View File

@ -16,18 +16,21 @@ from discord.ext import commands, tasks
import aiohttp import aiohttp
from lxml import etree from lxml import etree
import common as cmn
import keys
class QRZCog(commands.Cog): class QRZCog(commands.Cog):
def __init__(self, bot: commands.Bot): def __init__(self, bot: commands.Bot):
self.bot = bot self.bot = bot
self.gs = bot.get_cog("GlobalSettings")
self.session = aiohttp.ClientSession() self.session = aiohttp.ClientSession()
self._qrz_session_init.start() self._qrz_session_init.start()
@commands.command(name="qrz", aliases=["call"]) @commands.command(name="call", aliases=["qrz"], category=cmn.cat.lookup)
async def _qrz_lookup(self, ctx: commands.Context, call: str): async def _qrz_lookup(self, ctx: commands.Context, callsign: str):
if self.gs.keys.qrz_user == '' or self.gs.keys.qrz_pass == '': '''Look up a callsign on [QRZ.com](https://www.qrz.com/).'''
await ctx.send(f'http://qrz.com/db/{call}') if keys.qrz_user == '' or keys.qrz_pass == '':
await ctx.send(f'http://qrz.com/db/{callsign}')
return return
try: try:
@ -35,7 +38,7 @@ class QRZCog(commands.Cog):
except ConnectionError: except ConnectionError:
await self.get_session() await self.get_session()
url = f'http://xmldata.qrz.com/xml/current/?s={self.key};callsign={call}' url = f'http://xmldata.qrz.com/xml/current/?s={self.key};callsign={callsign}'
async with self.session.get(url) as resp: async with self.session.get(url) as resp:
if resp.status != 200: 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})')
@ -47,11 +50,11 @@ class QRZCog(commands.Cog):
if 'Error' in resp_session: if 'Error' in resp_session:
if 'Session Timeout' in resp_session['Error']: if 'Session Timeout' in resp_session['Error']:
await self.get_session() await self.get_session()
await self._qrz_lookup(ctx, call) await self._qrz_lookup(ctx, callsign)
return return
if 'Not found' in resp_session['Error']: if 'Not found' in resp_session['Error']:
embed = discord.Embed(title=f"QRZ Data for {call.upper()}", embed = discord.Embed(title=f"QRZ Data for {callsign.upper()}",
colour=self.gs.colours.bad, colour=cmn.colours.bad,
description='No data found!', description='No data found!',
timestamp=datetime.utcnow()) timestamp=datetime.utcnow())
embed.set_footer(text=ctx.author.name, embed.set_footer(text=ctx.author.name,
@ -65,7 +68,7 @@ class QRZCog(commands.Cog):
resp_data = {el.tag.split('}')[1]: el.text for el in resp_xml_data[0].getiterator()} resp_data = {el.tag.split('}')[1]: el.text for el in resp_xml_data[0].getiterator()}
embed = discord.Embed(title=f"QRZ Data for {resp_data['call']}", embed = discord.Embed(title=f"QRZ Data for {resp_data['call']}",
colour=self.gs.colours.good, colour=cmn.colours.good,
url=f'http://www.qrz.com/db/{resp_data["call"]}', url=f'http://www.qrz.com/db/{resp_data["call"]}',
timestamp=datetime.utcnow()) timestamp=datetime.utcnow())
embed.set_footer(text=ctx.author.name, embed.set_footer(text=ctx.author.name,
@ -82,7 +85,7 @@ class QRZCog(commands.Cog):
async def get_session(self): async def get_session(self):
"""Session creation and caching.""" """Session creation and caching."""
self.key = await qrz_login(self.gs.keys.qrz_user, self.gs.keys.qrz_pass, self.session) 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) qrz_file.write(self.key)

View File

@ -16,14 +16,16 @@ import discord.ext.commands as commands
import aiohttp import aiohttp
import common as cmn
class StudyCog(commands.Cog): class StudyCog(commands.Cog):
def __init__(self, bot: commands.Bot): def __init__(self, bot: commands.Bot):
self.bot = bot self.bot = bot
self.gs = bot.get_cog("GlobalSettings")
self.lastq = dict() self.lastq = dict()
self.source = 'Data courtesy of [HamStudy.org](https://hamstudy.org/)'
@commands.command(name="rq", aliases=['randomq']) @commands.command(name="hamstudy", aliases=['rq', 'randomquestion', 'randomq'], category=cmn.cat.study)
async def _random_question(self, ctx: commands.Context, level: str = None): async def _random_question(self, ctx: commands.Context, level: str = None):
'''Gets a random question from the Technician, General, and/or Extra question pools.''' '''Gets a random question from the Technician, General, and/or Extra question pools.'''
tech_pool = 'E2_2018' tech_pool = 'E2_2018'
@ -66,7 +68,8 @@ class StudyCog(commands.Cog):
question = random.choice(pool_questions) question = random.choice(pool_questions)
embed = discord.Embed(title=question['id'], embed = discord.Embed(title=question['id'],
colour=self.gs.colours.good, description=self.source,
colour=cmn.colours.good,
timestamp=datetime.utcnow()) timestamp=datetime.utcnow())
embed.set_footer(text=ctx.author.name, embed.set_footer(text=ctx.author.name,
icon_url=str(ctx.author.avatar_url)) icon_url=str(ctx.author.avatar_url))
@ -83,31 +86,32 @@ class StudyCog(commands.Cog):
self.lastq[ctx.message.channel.id] = (question['id'], question['answer']) self.lastq[ctx.message.channel.id] = (question['id'], question['answer'])
await ctx.send(embed=embed) await ctx.send(embed=embed)
@commands.command(name="rqa") @commands.command(name="hamstudyanswer", aliases=['rqa', 'randomquestionanswer', 'randomqa', 'hamstudya'],
async def _q_answer(self, ctx: commands.Context, ans: str = None): category=cmn.cat.study)
async def _q_answer(self, ctx: commands.Context, answer: str = None):
'''Returns the answer to question last asked (Optional argument: your answer).''' '''Returns the answer to question last asked (Optional argument: your answer).'''
with ctx.typing(): with ctx.typing():
correct_ans = self.lastq[ctx.message.channel.id][1] correct_ans = self.lastq[ctx.message.channel.id][1]
q_num = self.lastq[ctx.message.channel.id][0] q_num = self.lastq[ctx.message.channel.id][0]
if ans is not None: if answer is not None:
ans = ans.upper() answer = answer.upper()
if ans == correct_ans: if answer == correct_ans:
result = f'Correct! The answer to {q_num} was **{correct_ans}**.' result = f'Correct! The answer to {q_num} was **{correct_ans}**.'
embed = discord.Embed(title=f'{q_num} Answer', embed = discord.Embed(title=f'{q_num} Answer',
description=result, description=f'{self.source}\n\n{result}',
colour=self.gs.colours.good, colour=cmn.colours.good,
timestamp=datetime.utcnow()) timestamp=datetime.utcnow())
else: else:
result = f'Incorrect. The answer to {q_num} was **{correct_ans}**, not **{ans}**.' result = f'Incorrect. The answer to {q_num} was **{correct_ans}**, not **{answer}**.'
embed = discord.Embed(title=f'{q_num} Answer', embed = discord.Embed(title=f'{q_num} Answer',
description=result, description=f'{self.source}\n\n{result}',
colour=self.gs.colours.bad, colour=cmn.colours.bad,
timestamp=datetime.utcnow()) timestamp=datetime.utcnow())
else: else:
result = f'The correct answer to {q_num} was **{correct_ans}**.' result = f'The correct answer to {q_num} was **{correct_ans}**.'
embed = discord.Embed(title=f'{q_num} Answer', embed = discord.Embed(title=f'{q_num} Answer',
description=result, description=f'{self.source}\n\n{result}',
colour=self.gs.colours.neutral, colour=cmn.colours.neutral,
timestamp=datetime.utcnow()) timestamp=datetime.utcnow())
embed.set_footer(text=ctx.author.name, embed.set_footer(text=ctx.author.name,

View File

@ -16,26 +16,27 @@ import discord.ext.commands as commands
import aiohttp import aiohttp
import common as cmn
class WeatherCog(commands.Cog): class WeatherCog(commands.Cog):
wttr_units_regex = re.compile(r"\B-([cCfF])\b") wttr_units_regex = re.compile(r"\B-([cCfF])\b")
def __init__(self, bot: commands.Bot): def __init__(self, bot: commands.Bot):
self.bot = bot self.bot = bot
self.gs = bot.get_cog("GlobalSettings")
@commands.command(name="cond", aliases=['condx']) @commands.command(name="bandconditions", aliases=['cond', 'condx', 'conditions'], category=cmn.cat.weather)
async def _band_conditions(self, ctx: commands.Context): async def _band_conditions(self, ctx: commands.Context):
'''Posts an image of HF Band Conditions.''' '''Posts an image of HF Band Conditions.'''
with ctx.typing(): with ctx.typing():
embed = discord.Embed(title='Current Solar Conditions', embed = discord.Embed(title='Current Solar Conditions',
colour=self.gs.colours.good, colour=cmn.colours.good,
timestamp=datetime.utcnow()) timestamp=datetime.utcnow())
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
async with session.get('http://www.hamqsl.com/solarsun.php') as resp: async with session.get('http://www.hamqsl.com/solarsun.php') as resp:
if resp.status != 200: if resp.status != 200:
embed.description = 'Could not download file...' embed.description = 'Could not download file...'
embed.colour = self.gs.colours.bad embed.colour = cmn.colours.bad
else: else:
data = io.BytesIO(await resp.read()) data = io.BytesIO(await resp.read())
embed.set_image(url=f'attachment://condx.png') embed.set_image(url=f'attachment://condx.png')
@ -43,7 +44,7 @@ class WeatherCog(commands.Cog):
icon_url=str(ctx.author.avatar_url)) icon_url=str(ctx.author.avatar_url))
await ctx.send(embed=embed, file=discord.File(data, 'condx.png')) await ctx.send(embed=embed, file=discord.File(data, 'condx.png'))
@commands.group(name="weather", aliases=['wttr']) @commands.group(name="weather", aliases=['wttr'], category=cmn.cat.weather)
async def _weather_conditions(self, ctx: commands.Context): 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/).
@ -59,13 +60,13 @@ class WeatherCog(commands.Cog):
if ctx.invoked_subcommand is None: if ctx.invoked_subcommand is None:
await ctx.send_help(ctx.command) await ctx.send_help(ctx.command)
@_weather_conditions.command(name='forecast') @_weather_conditions.command(name='forecast', aliases=['fc', 'future'], category=cmn.cat.weather)
async def _weather_conditions_forecast(self, ctx: commands.Context, *, args: str): 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/). '''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.''' See help for weather command for possible location types. Add a `-c` or `-f` to use Celcius or Fahrenheit.'''
with ctx.typing(): with ctx.typing():
try: try:
units_arg = re.search(self.wttr_units_regex, args).group(1) units_arg = re.search(self.wttr_units_regex, location).group(1)
except AttributeError: except AttributeError:
units_arg = '' units_arg = ''
if units_arg.lower() == 'f': if units_arg.lower() == 'f':
@ -75,18 +76,18 @@ See help for weather command for possible location types. Add a `-c` or `-f` to
else: else:
units = '' units = ''
loc = self.wttr_units_regex.sub('', args).strip() loc = self.wttr_units_regex.sub('', location).strip()
embed = discord.Embed(title=f'Weather Forecast for {loc}', embed = discord.Embed(title=f'Weather Forecast for {loc}',
description='Data from [wttr.in](http://wttr.in/).', description='Data from [wttr.in](http://wttr.in/).',
colour=self.gs.colours.good, colour=cmn.colours.good,
timestamp=datetime.utcnow()) timestamp=datetime.utcnow())
loc = loc.replace(' ', '+') loc = loc.replace(' ', '+')
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
async with session.get(f'http://wttr.in/{loc}_{units}pnFQ.png') as resp: async with session.get(f'http://wttr.in/{loc}_{units}pnFQ.png') as resp:
if resp.status != 200: if resp.status != 200:
embed.description = 'Could not download file...' embed.description = 'Could not download file...'
embed.colour = self.gs.colours.bad embed.colour = cmn.colours.bad
else: else:
data = io.BytesIO(await resp.read()) data = io.BytesIO(await resp.read())
loc = loc.replace('+', '') loc = loc.replace('+', '')
@ -97,13 +98,13 @@ See help for weather command for possible location types. Add a `-c` or `-f` to
icon_url=str(ctx.author.avatar_url)) icon_url=str(ctx.author.avatar_url))
await ctx.send(embed=embed, file=discord.File(data, f'{loc}_{units}pnFQ.png')) await ctx.send(embed=embed, file=discord.File(data, f'{loc}_{units}pnFQ.png'))
@_weather_conditions.command(name='now') @_weather_conditions.command(name='now', aliases=['n'], category=cmn.cat.weather)
async def _weather_conditions_now(self, ctx: commands.Context, *, args: str): 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/). '''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.''' See help for weather command for possible location types. Add a `-c` or `-f` to use Celcius or Fahrenheit.'''
with ctx.typing(): with ctx.typing():
try: try:
units_arg = re.search(self.wttr_units_regex, args).group(1) units_arg = re.search(self.wttr_units_regex, location).group(1)
except AttributeError: except AttributeError:
units_arg = '' units_arg = ''
if units_arg.lower() == 'f': if units_arg.lower() == 'f':
@ -113,18 +114,18 @@ See help for weather command for possible location types. Add a `-c` or `-f` to
else: else:
units = '' units = ''
loc = self.wttr_units_regex.sub('', args).strip() loc = self.wttr_units_regex.sub('', location).strip()
embed = discord.Embed(title=f'Current Weather for {loc}', embed = discord.Embed(title=f'Current Weather for {loc}',
description='Data from [wttr.in](http://wttr.in/).', description='Data from [wttr.in](http://wttr.in/).',
colour=self.gs.colours.good, colour=cmn.colours.good,
timestamp=datetime.utcnow()) timestamp=datetime.utcnow())
loc = loc.replace(' ', '+') loc = loc.replace(' ', '+')
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
async with session.get(f'http://wttr.in/{loc}_0{units}pnFQ.png') as resp: async with session.get(f'http://wttr.in/{loc}_0{units}pnFQ.png') as resp:
if resp.status != 200: if resp.status != 200:
embed.description = 'Could not download file...' embed.description = 'Could not download file...'
embed.colour = self.gs.colours.bad embed.colour = cmn.colours.bad
else: else:
data = io.BytesIO(await resp.read()) data = io.BytesIO(await resp.read())
loc = loc.replace('+', '') loc = loc.replace('+', '')

27
common.py Normal file
View File

@ -0,0 +1,27 @@
"""
Common tools for the bot.
---
Copyright (C) 2019 Abigail Gold, 0x5c
This file is part of discord-qrmbot and is released under the terms of the GNU
General Public License, version 2.
---
`colours`: Colours used by embeds.
`cat`: Category names for the HelpCommand.
"""
from types import SimpleNamespace
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')

View File

@ -15,11 +15,13 @@ General Public License, version 2.
`contrubuting`: Info on how to contribute to the bot. `contrubuting`: Info on how to contribute to the bot.
`release`: Current bot version.
`release_timestamp`: When the bot was last released. `release_timestamp`: When the bot was last released.
""" """
authors = ("@ClassAbbyAmplifier#2229", "@0x5c") authors = ("@ClassAbbyAmplifier#2229", "@0x5c#0639")
description = """A bot with various useful ham radio-related functions, written in Python.""" description = """A bot with various useful ham radio-related functions, written in Python."""
license = "Released under the GNU General Public License v2" license = "Released under the GNU General Public License v2"
contributing = "Check out the source on GitHub, contributions welcome: https://github.com/classabbyamp/discord-qrm-bot" contributing = "Check out the source on GitHub, contributions welcome: https://github.com/classabbyamp/discord-qrm-bot"
release_timestamp = "not yet :P" release = '1.0.0'

22
main.py
View File

@ -8,8 +8,6 @@ This file is part of discord-qrmbot and is released under the terms of the GNU
General Public License, version 2. General Public License, version 2.
""" """
from types import SimpleNamespace
import discord import discord
from discord.ext import commands, tasks from discord.ext import commands, tasks
@ -26,20 +24,6 @@ exit_code = 1 # The default exit code. ?shutdown and ?restart will change it ac
debug_mode = opt.debug # Separate assignement in-case we define an override (ternary operator goes here) debug_mode = opt.debug # Separate assignement in-case we define an override (ternary operator goes here)
class GlobalSettings(commands.Cog):
def __init__(self, bot: commands.Bot):
self.bot = bot
self.opt = opt
self.keys = keys
self.info = info
self.colours = SimpleNamespace(good=0x43B581,
neutral=0x7289DA,
bad=0xF04747)
self.debug = debug_mode
# --- Bot setup --- # --- Bot setup ---
bot = commands.Bot(command_prefix=opt.prefix, bot = commands.Bot(command_prefix=opt.prefix,
@ -67,7 +51,7 @@ async def check_if_owner(ctx: commands.Context):
# --- Commands --- # --- Commands ---
@bot.command(name="restart") @bot.command(name="restart", hidden=True)
@commands.check(check_if_owner) @commands.check(check_if_owner)
async def _restart_bot(ctx: commands.Context): async def _restart_bot(ctx: commands.Context):
"""Restarts the bot.""" """Restarts the bot."""
@ -77,7 +61,7 @@ async def _restart_bot(ctx: commands.Context):
await bot.logout() await bot.logout()
@bot.command(name="shutdown") @bot.command(name="shutdown", hidden=True)
@commands.check(check_if_owner) @commands.check(check_if_owner)
async def _shutdown_bot(ctx: commands.Context): async def _shutdown_bot(ctx: commands.Context):
"""Shuts down the bot.""" """Shuts down the bot."""
@ -109,7 +93,7 @@ async def _before_ensure_activity():
# --- Run --- # --- Run ---
bot.add_cog(GlobalSettings(bot)) # bot.add_cog(GlobalSettings(bot))
for cog in opt.cogs: for cog in opt.cogs:
bot.load_extension(f"cogs.{cog}") bot.load_extension(f"cogs.{cog}")