mirror of
https://github.com/miaowware/qrm2.git
synced 2025-05-30 13:12:29 -04:00
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:
parent
f821a2e2f2
commit
ad2c559a0f
@ -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
|
||||||
|
|
||||||
|
112
cogs/basecog.py
112
cogs/basecog.py
@ -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
|
||||||
|
@ -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:')
|
||||||
|
@ -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))
|
||||||
|
@ -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:
|
||||||
|
@ -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,
|
||||||
|
@ -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))
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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,
|
||||||
|
@ -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
27
common.py
Normal 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')
|
6
info.py
6
info.py
@ -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
22
main.py
@ -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}")
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user