diff --git a/CHANGELOG.md b/CHANGELOG.md index f675d65..51359cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,8 +10,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Configuration option to use another rTeX instance for `?tex`. ### Changed - Main name and aliases of `?bandplan`. +- Recategorized the commands. ### Fixed - Lack of input sanitisation in `?xkcd`. +- Incorrect capitalisation of the categories in the `?help` command. ## [2.6.0] - 2021-03-18 @@ -84,7 +86,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [2.3.1] - 2020-04-02 ### Fixed -- Wordlist containing innappropriate words. +- Wordlist containing inappropriate words. ## [2.3.0] - 2020-03-30 diff --git a/common.py b/common.py index a2aa46c..cd36d1b 100644 --- a/common.py +++ b/common.py @@ -9,6 +9,7 @@ the GNU General Public License, version 2. import collections +import enum import json import re import traceback @@ -26,7 +27,7 @@ from discord import Emoji, Reaction, PartialEmoji import data.options as opt -__all__ = ["colours", "cat", "emojis", "paths", "ImageMetadata", "ImagesGroup", +__all__ = ["colours", "BoltCats", "Cats", "emojis", "paths", "ImageMetadata", "ImagesGroup", "embed_factory", "error_embed_factory", "add_react", "check_if_owner"] @@ -39,16 +40,24 @@ colours = SimpleNamespace( timeout=0xF26522, ) + +class BoltCats(enum.Enum): + ADMIN = "Bot Control" + INFO = "Bot Information" + + # meow -cat = SimpleNamespace( - lookup="Information Lookup", - fun="Fun", - maps="Mapping", - ref="Reference", - study="Exam Study", - weather="Land and Space Weather", - admin="Bot Control", -) +class Cats(enum.Enum): + CALC = "Calculators" + CODES = "Code References and Tools" + FUN = "Fun" + LOOKUP = "Information Lookup" + REF = "Reference" + STUDY = "Exam Study" + TIME = "Time and Time Zones" + UTILS = "Utilities" + WEATHER = "Land and Space Weather" + emojis = SimpleNamespace( check_mark="✅", diff --git a/exts/ae7q.py b/exts/ae7q.py index 8cf76c7..1d6f76d 100644 --- a/exts/ae7q.py +++ b/exts/ae7q.py @@ -29,13 +29,13 @@ class AE7QCog(commands.Cog): self.bot = bot self.session = aiohttp.ClientSession(connector=bot.qrm.connector) - @commands.group(name="ae7q", aliases=["ae"], case_insensitive=True, category=cmn.cat.lookup) + @commands.group(name="ae7q", aliases=["ae"], case_insensitive=True, category=cmn.Cats.LOOKUP) async def _ae7q_lookup(self, ctx: commands.Context): """Looks up a callsign, FRN, or Licensee ID on [ae7q.com](http://ae7q.com/).""" if ctx.invoked_subcommand is None: await ctx.send_help(ctx.command) - @_ae7q_lookup.command(name="call", aliases=["c"], category=cmn.cat.lookup) + @_ae7q_lookup.command(name="call", aliases=["c"], category=cmn.Cats.LOOKUP) async def _ae7q_call(self, ctx: commands.Context, callsign: str): """Looks up the history of a callsign on [ae7q.com](http://ae7q.com/).""" with ctx.typing(): @@ -109,7 +109,7 @@ class AE7QCog(commands.Cog): await ctx.send(embed=embed) - @_ae7q_lookup.command(name="trustee", aliases=["t"], category=cmn.cat.lookup) + @_ae7q_lookup.command(name="trustee", aliases=["t"], category=cmn.Cats.LOOKUP) async def _ae7q_trustee(self, ctx: commands.Context, callsign: str): """Looks up the licenses for which a licensee is trustee on [ae7q.com](http://ae7q.com/).""" with ctx.typing(): @@ -184,7 +184,7 @@ class AE7QCog(commands.Cog): await ctx.send(embed=embed) - @_ae7q_lookup.command(name="applications", aliases=["a"], category=cmn.cat.lookup) + @_ae7q_lookup.command(name="applications", aliases=["a"], category=cmn.Cats.LOOKUP) async def _ae7q_applications(self, ctx: commands.Context, callsign: str): """Looks up the application history for a callsign on [ae7q.com](http://ae7q.com/).""" """ @@ -262,7 +262,7 @@ class AE7QCog(commands.Cog): raise NotImplementedError("Application history lookup not yet supported. " "Check back in a later version of the bot.") - @_ae7q_lookup.command(name="frn", aliases=["f"], category=cmn.cat.lookup) + @_ae7q_lookup.command(name="frn", aliases=["f"], category=cmn.Cats.LOOKUP) async def _ae7q_frn(self, ctx: commands.Context, frn: str): """Looks up the history of an FRN on [ae7q.com](http://ae7q.com/).""" """ @@ -337,7 +337,7 @@ class AE7QCog(commands.Cog): await ctx.send(embed=embed) - @_ae7q_lookup.command(name="licensee", aliases=["l"], category=cmn.cat.lookup) + @_ae7q_lookup.command(name="licensee", aliases=["l"], category=cmn.Cats.LOOKUP) async def _ae7q_licensee(self, ctx: commands.Context, licensee_id: str): """Looks up the history of a licensee ID on [ae7q.com](http://ae7q.com/).""" with ctx.typing(): diff --git a/exts/base.py b/exts/base.py index 3a24a71..ab5309f 100644 --- a/exts/base.py +++ b/exts/base.py @@ -23,7 +23,11 @@ from data import options as opt class QrmHelpCommand(commands.HelpCommand): def __init__(self): - super().__init__(command_attrs={"help": "Shows help about qrm or a command", "aliases": ["h"]}) + super().__init__(command_attrs={ + "help": "Shows help about qrm or a command", + "aliases": ["h"], + "category": cmn.BoltCats.INFO + }) self.verify_checks = True self.context: commands.Context @@ -73,7 +77,7 @@ class QrmHelpCommand(commands.HelpCommand): continue names = sorted([cmd.name for cmd in cmds]) if cat is not None: - embed.add_field(name=cat.title(), value=", ".join(names), inline=False) + embed.add_field(name=cat.value, value=", ".join(names), inline=False) else: embed.add_field(name="Other", value=", ".join(names), inline=False) await self.context.send(embed=embed) @@ -136,7 +140,7 @@ class BaseCog(commands.Cog): self.bot_invite = (f"https://discordapp.com/oauth2/authorize?client_id={self.bot.user.id}" f"&scope=bot&permissions={opt.invite_perms}") - @commands.command(name="info", aliases=["about"]) + @commands.command(name="info", aliases=["about"], category=cmn.BoltCats.INFO) async def _info(self, ctx: commands.Context): """Shows info about qrm.""" embed = cmn.embed_factory(ctx) @@ -154,7 +158,7 @@ class BaseCog(commands.Cog): embed.set_thumbnail(url=str(self.bot.user.avatar_url)) await ctx.send(embed=embed) - @commands.command(name="ping", aliases=["beep"]) + @commands.command(name="ping", aliases=["beep"], category=cmn.BoltCats.INFO) async def _ping(self, ctx: commands.Context): """Shows the current latency to the discord endpoint.""" embed = cmn.embed_factory(ctx) @@ -167,7 +171,7 @@ class BaseCog(commands.Cog): embed.description = f"Current ping is {self.bot.latency*1000:.1f} ms" await ctx.send(content, embed=embed) - @commands.command(name="changelog", aliases=["clog"]) + @commands.command(name="changelog", aliases=["clog"], category=cmn.BoltCats.INFO) async def _changelog(self, ctx: commands.Context, version: str = "latest"): """Shows what has changed in a bot version. Defaults to the latest version.""" embed = cmn.embed_factory(ctx) @@ -203,7 +207,7 @@ class BaseCog(commands.Cog): await ctx.send(embed=embed) - @commands.command(name="issue") + @commands.command(name="issue", category=cmn.BoltCats.INFO) async def _issue(self, ctx: commands.Context): """Shows how to create a bug report or feature request about the bot.""" embed = cmn.embed_factory(ctx) @@ -215,7 +219,7 @@ class BaseCog(commands.Cog): [miaowware/qrm-resources](https://github.com/miaowware/qrm-resources/issues).""" await ctx.send(embed=embed) - @commands.command(name="donate", aliases=["tip"]) + @commands.command(name="donate", aliases=["tip"], category=cmn.BoltCats.INFO) async def _donate(self, ctx: commands.Context): """Shows ways to help support development of the bot via donations.""" embed = cmn.embed_factory(ctx) @@ -226,7 +230,7 @@ class BaseCog(commands.Cog): embed.add_field(name=title, value=url, inline=False) await ctx.send(embed=embed) - @commands.command(name="invite", enabled=opt.enable_invite_cmd) + @commands.command(name="invite", enabled=opt.enable_invite_cmd, category=cmn.BoltCats.INFO) async def _invite(self, ctx: commands.Context): """Generates a link to invite the bot to a server.""" if not (await self.bot.application_info()).bot_public: @@ -236,7 +240,7 @@ class BaseCog(commands.Cog): embed.description = self.bot_invite await ctx.send(embed=embed) - @commands.command(name="echo", aliases=["e"], category=cmn.cat.admin) + @commands.command(name="echo", aliases=["e"], category=cmn.BoltCats.ADMIN) @commands.check(cmn.check_if_owner) async def _echo(self, ctx: commands.Context, channel: Union[cmn.GlobalChannelConverter, commands.UserConverter], *, msg: str): diff --git a/exts/qrz.py b/exts/callsign.py similarity index 97% rename from exts/qrz.py rename to exts/callsign.py index 135729a..f49d8de 100644 --- a/exts/qrz.py +++ b/exts/callsign.py @@ -1,7 +1,8 @@ """ -QRZ extension for qrm +Callsign Lookup extension for qrm --- -Copyright (C) 2019-2020 Abigail Gold, 0x5c +Copyright (C) 2019-2020 Abigail Gold, 0x5c (as qrz.py) +Copyright (C) 2021 Abigail Gold, 0x5c This file is part of qrm2 and is released under the terms of the GNU General Public License, version 2. @@ -40,7 +41,7 @@ class QRZCog(commands.Cog): except AttributeError: pass - @commands.command(name="call", aliases=["qrz"], category=cmn.cat.lookup) + @commands.command(name="call", aliases=["qrz"], category=cmn.Cats.LOOKUP) async def _qrz_lookup(self, ctx: commands.Context, callsign: str, *flags): """Looks up a callsign on [QRZ.com](https://www.qrz.com/). Add `--link` to only link the QRZ page.""" flags = [f.lower() for f in flags] diff --git a/exts/ham.py b/exts/codes.py similarity index 53% rename from exts/ham.py rename to exts/codes.py index 739c15f..d42648e 100644 --- a/exts/ham.py +++ b/exts/codes.py @@ -1,7 +1,8 @@ """ -Ham extension for qrm +Codes extension for qrm --- -Copyright (C) 2019-2021 Abigail Gold, 0x5c +Copyright (C) 2019-2021 Abigail Gold, 0x5c (as ham.py) +Copyright (C) 2021 Abigail Gold, 0x5c This file is part of qrm2 and is released under the terms of the GNU General Public License, version 2. @@ -9,18 +10,15 @@ the GNU General Public License, version 2. import json -from datetime import datetime import discord.ext.commands as commands import common as cmn -from resources import callsign_info class HamCog(commands.Cog): def __init__(self, bot: commands.Bot): self.bot = bot - self.pfxs = callsign_info.options with open(cmn.paths.resources / "phonetics.1.json") as file: d = json.load(file) self.phonetics: dict[str, str] = d["phonetics"] @@ -28,7 +26,7 @@ class HamCog(commands.Cog): with open(cmn.paths.resources / "qcodes.1.json") as file: self.qcodes: dict = json.load(file) - @commands.command(name="qcode", aliases=["q"], category=cmn.cat.ref) + @commands.command(name="qcode", aliases=["q"], category=cmn.Cats.CODES) async def _qcode_lookup(self, ctx: commands.Context, qcode: str): """Looks up the meaning of a Q Code.""" qcode = qcode.upper() @@ -42,7 +40,7 @@ class HamCog(commands.Cog): embed.colour = cmn.colours.bad await ctx.send(embed=embed) - @commands.command(name="phonetics", aliases=["ph", "phoneticize", "phoneticise", "phone"], category=cmn.cat.ref) + @commands.command(name="phonetics", aliases=["ph", "phoneticize", "phoneticise", "phone"], category=cmn.Cats.CODES) async def _phonetics_lookup(self, ctx: commands.Context, *, msg: str): """Returns NATO phonetics for a word or phrase.""" result = "" @@ -58,51 +56,7 @@ class HamCog(commands.Cog): embed.colour = cmn.colours.good await ctx.send(embed=embed) - @commands.command(name="utc", aliases=["z"], category=cmn.cat.ref) - async def _utc_lookup(self, ctx: commands.Context): - """Returns the current time in UTC.""" - now = datetime.utcnow() - result = "**" + now.strftime("%Y-%m-%d %H:%M") + "Z**" - embed = cmn.embed_factory(ctx) - embed.title = "The current time is:" - embed.description = result - embed.colour = cmn.colours.good - await ctx.send(embed=embed) - - @commands.command(name="prefixes", aliases=["vanity", "pfx", "vanities", "prefix"], category=cmn.cat.ref) - async def _vanity_prefixes(self, ctx: commands.Context, country: str = ""): - """Lists valid callsign prefixes for different countries.""" - country = country.lower() - embed = cmn.embed_factory(ctx) - if country not in self.pfxs: - desc = "Possible arguments are:\n" - for key, val in self.pfxs.items(): - desc += f"`{key}`: {val.title}{(' ' + val.emoji if val.emoji else '')}\n" - embed.title = f"{country} Not Found!" - embed.description = desc - embed.colour = cmn.colours.bad - await ctx.send(embed=embed) - return - else: - data = self.pfxs[country] - embed.title = data.title + (" " + data.emoji if data.emoji else "") - embed.description = data.desc - embed.colour = cmn.colours.good - - for name, val in data.calls.items(): - embed.add_field(name=name, value=val, inline=False) - await ctx.send(embed=embed) - - @commands.command(name="contests", aliases=["cc", "tests"], category=cmn.cat.ref) - async def _contests(self, ctx: commands.Context): - embed = cmn.embed_factory(ctx) - embed.title = "Contest Calendar" - embed.description = ("*We are currently rewriting the old, Chrome-based `contests` command. In the meantime, " - "use [the website](https://www.contestcalendar.com/weeklycont.php).*") - embed.colour = cmn.colours.good - await ctx.send(embed=embed) - - @commands.command(name="phoneticweight", aliases=["pw"], category=cmn.cat.ref) + @commands.command(name="phoneticweight", aliases=["pw"], category=cmn.Cats.CODES) async def _weight(self, ctx: commands.Context, *, msg: str): """Calculates the phonetic weight of a callsign or message.""" embed = cmn.embed_factory(ctx) diff --git a/exts/contests.py b/exts/contests.py new file mode 100644 index 0000000..15313f0 --- /dev/null +++ b/exts/contests.py @@ -0,0 +1,28 @@ +""" +Contest Calendar extension for qrm +--- +Copyright (C) 2021 Abigail Gold, 0x5c + +This file is part of qrm2 and is released under the terms of +the GNU General Public License, version 2. +""" + + +import discord.ext.commands as commands + +import common as cmn + + +class ContestCalendarCog(commands.Cog): + @commands.command(name="contests", aliases=["cc", "tests"], category=cmn.Cats.LOOKUP) + async def _contests(self, ctx: commands.Context): + embed = cmn.embed_factory(ctx) + embed.title = "Contest Calendar" + embed.description = ("*We are currently rewriting the old, Chrome-based `contests` command. In the meantime, " + "use [the website](https://www.contestcalendar.com/weeklycont.php).*") + embed.colour = cmn.colours.good + await ctx.send(embed=embed) + + +def setup(bot: commands.Bot): + bot.add_cog(ContestCalendarCog(bot)) diff --git a/exts/dbconv.py b/exts/dbconv.py index d402adb..807223e 100644 --- a/exts/dbconv.py +++ b/exts/dbconv.py @@ -67,7 +67,7 @@ class DbConvCog(commands.Cog): def __init__(self, bot: commands.Bot): self.bot = bot - @commands.command(name="dbconv", aliases=["dbc"], category=cmn.cat.ref) + @commands.command(name="dbconv", aliases=["dbc"], category=cmn.Cats.CALC) async def _db_conv(self, ctx: commands.Context, value: Optional[float] = None, unit_from: Optional[UnitConverter] = None, diff --git a/exts/lookup.py b/exts/dxcc.py similarity index 68% rename from exts/lookup.py rename to exts/dxcc.py index ee1b18e..f4a851b 100644 --- a/exts/lookup.py +++ b/exts/dxcc.py @@ -1,7 +1,8 @@ """ -Lookup extension for qrm +DXCC Prefix Lookup extension for qrm --- -Copyright (C) 2019-2020 Abigail Gold, 0x5c +Copyright (C) 2019-2020 Abigail Gold, 0x5c (as lookup.py) +Copyright (C) 2021 Abigail Gold, 0x5c This file is part of qrm2 and is released under the terms of the GNU General Public License, version 2. @@ -21,7 +22,7 @@ import common as cmn cty_path = Path("./data/cty.json") -class LookupCog(commands.Cog): +class DXCCCog(commands.Cog): def __init__(self, bot): self.bot = bot try: @@ -29,19 +30,7 @@ class LookupCog(commands.Cog): except OSError: self.cty = BigCty() - # TODO: See #107 - # @commands.command(name="sat", category=cmn.cat.lookup) - # async def _sat_lookup(self, ctx: commands.Context, sat_name: str, grid1: str, grid2: str = None): - # """Links to info about satellite passes on satmatch.com.""" - # now = datetime.utcnow().strftime("%Y-%m-%d%%20%H:%M") - # if grid2 is None or grid2 == "": - # await ctx.send(f"http://www.satmatch.com/satellite/{sat_name}/obs1/{grid1}" - # f"?search_start_time={now}&duration_hrs=24") - # else: - # await ctx.send(f"http://www.satmatch.com/satellite/{sat_name}/obs1/{grid1}" - # f"/obs2/{grid2}?search_start_time={now}&duration_hrs=24") - - @commands.command(name="dxcc", aliases=["dx"], category=cmn.cat.lookup) + @commands.command(name="dxcc", aliases=["dx"], category=cmn.Cats.LOOKUP) async def _dxcc_lookup(self, ctx: commands.Context, query: str): """Gets DXCC info about a callsign prefix.""" query = query.upper() @@ -82,6 +71,6 @@ def run_update(cty_obj, dump_loc): def setup(bot: commands.Bot): - lookupcog = LookupCog(bot) - bot.add_cog(lookupcog) - lookupcog._update_cty.start() + dxcccog = DXCCCog(bot) + bot.add_cog(dxcccog) + dxcccog._update_cty.start() diff --git a/exts/fun.py b/exts/fun.py index c8939d7..aee803f 100644 --- a/exts/fun.py +++ b/exts/fun.py @@ -26,22 +26,22 @@ class FunCog(commands.Cog): with open(cmn.paths.resources / "words.1.txt") as words_file: self.words = words_file.read().lower().splitlines() - @commands.command(name="xkcd", aliases=["x"], category=cmn.cat.fun) + @commands.command(name="xkcd", aliases=["x"], category=cmn.Cats.FUN) async def _xkcd(self, ctx: commands.Context, number: int): """Looks up an xkcd comic by number.""" await ctx.send("http://xkcd.com/" + str(number)) - @commands.command(name="tar", category=cmn.cat.fun) + @commands.command(name="tar", category=cmn.Cats.FUN) async def _tar(self, ctx: commands.Context): """Returns xkcd: tar.""" await ctx.send("http://xkcd.com/1168") - @commands.command(name="standards", category=cmn.cat.fun) + @commands.command(name="standards", category=cmn.Cats.FUN) async def _standards(self, ctx: commands.Context): """Returns xkcd: Standards.""" await ctx.send("http://xkcd.com/927") - @commands.command(name="worksplit", aliases=["split", "ft8"], category=cmn.cat.fun) + @commands.command(name="worksplit", aliases=["split", "ft8"], category=cmn.Cats.FUN) async def _worksplit(self, ctx: commands.Context): """Posts "Work split you lids".""" embed = cmn.embed_factory(ctx) @@ -49,12 +49,12 @@ class FunCog(commands.Cog): embed.set_image(url=opt.resources_url + self.imgs["worksplit"]) await ctx.send(embed=embed) - @commands.command(name="xd", hidden=True, category=cmn.cat.fun) + @commands.command(name="xd", hidden=True, category=cmn.Cats.FUN) async def _xd(self, ctx: commands.Context): """ecks dee""" await ctx.send("ECKS DEE :smirk:") - @commands.command(name="funetics", aliases=["fun"], category=cmn.cat.fun) + @commands.command(name="funetics", aliases=["fun"], category=cmn.Cats.FUN) async def _funetics_lookup(self, ctx: commands.Context, *, msg: str): """Generates fun/wacky phonetics for a word or phrase.""" result = "" diff --git a/exts/grid.py b/exts/grid.py index f83b9d3..f9f90ce 100644 --- a/exts/grid.py +++ b/exts/grid.py @@ -19,7 +19,7 @@ class GridCog(commands.Cog): def __init__(self, bot: commands.Bot): self.bot = bot - @commands.command(name="grid", category=cmn.cat.maps) + @commands.command(name="grid", category=cmn.Cats.CALC) async def _grid_sq_lookup(self, ctx: commands.Context, lat: float, lon: float): ("""Calculates the grid square for latitude and longitude coordinates.""" """\n\nCoordinates should be in decimal format, with negative being latitude South and longitude West.""" @@ -33,7 +33,7 @@ class GridCog(commands.Cog): embed.colour = cmn.colours.good await ctx.send(embed=embed) - @commands.command(name="latlong", aliases=["latlon", "loc", "ungrid"], category=cmn.cat.maps) + @commands.command(name="latlong", aliases=["latlon", "loc", "ungrid"], category=cmn.Cats.CALC) async def _location_lookup(self, ctx: commands.Context, grid: str): ("""Calculates the latitude and longitude for the center of a grid locator.""" """\n\nTo calculate the grid locator from a latitude and longitude, use `grid`""" @@ -49,7 +49,7 @@ class GridCog(commands.Cog): "latlong` to see other names for this command.*")) await ctx.send(embed=embed) - @commands.command(name="griddistance", aliases=["griddist", "distance", "dist"], category=cmn.cat.maps) + @commands.command(name="griddistance", aliases=["griddist", "distance", "dist"], category=cmn.Cats.CALC) async def _dist_lookup(self, ctx: commands.Context, grid1: str, grid2: str): """Calculates the great circle distance and azimuthal bearing between two grid locators.""" g1 = gridtools.Grid(grid1) diff --git a/exts/image.py b/exts/image.py index c7941fb..5f674d7 100644 --- a/exts/image.py +++ b/exts/image.py @@ -9,7 +9,6 @@ the GNU General Public License, version 2. import aiohttp -from datetime import datetime import discord.ext.commands as commands @@ -19,34 +18,22 @@ import data.options as opt class ImageCog(commands.Cog): - gl_baseurl = "https://www.fourmilab.ch/cgi-bin/uncgi/Earth?img=ETOPO1_day-m.evif&dynimg=y&opt=-p" - def __init__(self, bot: commands.Bot): self.bot = bot self.bandcharts = cmn.ImagesGroup(cmn.paths.resources / "bandcharts.1.json") self.maps = cmn.ImagesGroup(cmn.paths.resources / "maps.1.json") self.session = aiohttp.ClientSession(connector=bot.qrm.connector) - @commands.command(name="bandchart", aliases=["bandplan", "plan", "bands"], category=cmn.cat.ref) + @commands.command(name="bandchart", aliases=["bandplan", "plan", "bands"], category=cmn.Cats.REF) async def _bandcharts(self, ctx: commands.Context, chart_id: str = ""): """Gets the frequency allocations chart for a given country.""" await ctx.send(embed=create_embed(ctx, "Bandchart", self.bandcharts, chart_id)) - @commands.command(name="map", category=cmn.cat.maps) + @commands.command(name="map", category=cmn.Cats.REF) async def _map(self, ctx: commands.Context, map_id: str = ""): """Posts a ham-relevant map.""" await ctx.send(embed=create_embed(ctx, "Map", self.maps, map_id)) - @commands.command(name="grayline", aliases=["greyline", "grey", "gray", "gl"], category=cmn.cat.maps) - async def _grayline(self, ctx: commands.Context): - """Gets a map of the current greyline, where HF propagation is the best.""" - embed = cmn.embed_factory(ctx) - embed.title = "Current Greyline Conditions" - embed.colour = cmn.colours.good - date_params = f"&date=1&utc={datetime.utcnow():%Y-%m-%d+%H:%M:%S}" - embed.set_image(url=self.gl_baseurl + date_params) - await ctx.send(embed=embed) - def create_embed(ctx: commands.Context, not_found_name: str, db: cmn.ImagesGroup, img_id: str): """Creates an embed for the image and its metadata, or list available images in the group.""" diff --git a/exts/weather.py b/exts/land_weather.py similarity index 85% rename from exts/weather.py rename to exts/land_weather.py index b4871e6..890a3c4 100644 --- a/exts/weather.py +++ b/exts/land_weather.py @@ -1,7 +1,8 @@ """ -Weather extension for qrm +Land Weather extension for qrm --- -Copyright (C) 2019-2020 Abigail Gold, 0x5c +Copyright (C) 2019-2020 Abigail Gold, 0x5c (as weather.py) +Copyright (C) 2021 Abigail Gold, 0x5c This file is part of qrm2 and is released under the terms of the GNU General Public License, version 2. @@ -26,21 +27,7 @@ class WeatherCog(commands.Cog): self.bot = bot self.session = aiohttp.ClientSession(connector=bot.qrm.connector) - @commands.command(name="solarweather", aliases=["solar", "bandconditions", "cond", "condx", "conditions"], - category=cmn.cat.weather) - async def solarweather(self, ctx: commands.Context): - """Gets a solar weather report.""" - embed = cmn.embed_factory(ctx) - embed.title = "☀️ Current Solar Weather" - if ctx.invoked_with in ["bandconditions", "cond", "condx", "conditions"]: - embed.add_field(name="⚠️ Deprecated Command Alias", - value=(f"This command has been renamed to `{ctx.prefix}solar`!\n" - "The alias you used will be removed in the next version.")) - embed.colour = cmn.colours.good - embed.set_image(url="http://www.hamqsl.com/solarsun.php") - await ctx.send(embed=embed) - - @commands.group(name="weather", aliases=["wttr"], case_insensitive=True, category=cmn.cat.weather) + @commands.group(name="weather", aliases=["wttr"], case_insensitive=True, category=cmn.Cats.WEATHER) async def _weather_conditions(self, ctx: commands.Context): """Gets local weather conditions from [wttr.in](http://wttr.in/). @@ -57,7 +44,7 @@ class WeatherCog(commands.Cog): if ctx.invoked_subcommand is None: await ctx.send_help(ctx.command) - @_weather_conditions.command(name="forecast", aliases=["fc", "future"], category=cmn.cat.weather) + @_weather_conditions.command(name="forecast", aliases=["fc", "future"], category=cmn.Cats.WEATHER) async def _weather_conditions_forecast(self, ctx: commands.Context, *, location: str): """Gets local weather forecast for the next three days from [wttr.in](http://wttr.in/). See help of the `weather` command for possible location types and options.""" @@ -83,7 +70,7 @@ class WeatherCog(commands.Cog): embed.set_image(url=f"http://wttr.in/{loc}_{units}pnFQ.png") await ctx.send(embed=embed) - @_weather_conditions.command(name="now", aliases=["n"], category=cmn.cat.weather) + @_weather_conditions.command(name="now", aliases=["n"], category=cmn.Cats.WEATHER) async def _weather_conditions_now(self, ctx: commands.Context, *, location: str): """Gets current local weather conditions from [wttr.in](http://wttr.in/). See help of the `weather` command for possible location types and options.""" @@ -109,7 +96,7 @@ class WeatherCog(commands.Cog): embed.set_image(url=f"http://wttr.in/{loc}_0{units}pnFQ.png") await ctx.send(embed=embed) - @commands.command(name="metar", category=cmn.cat.weather) + @commands.command(name="metar", category=cmn.Cats.WEATHER) async def metar(self, ctx: commands.Context, airport: str, hours: int = 0): """Gets current raw METAR (Meteorological Terminal Aviation Routine Weather Report) for an airport. \ Optionally, a number of hours can be given to show a number of hours of historical METAR data. @@ -118,7 +105,7 @@ class WeatherCog(commands.Cog): [ICAO code](https://en.wikipedia.org/wiki/List_of_airports_by_IATA_and_ICAO_code).""" await ctx.send(embed=await self.gen_metar_taf_embed(ctx, airport, hours, False)) - @commands.command(name="taf", category=cmn.cat.weather) + @commands.command(name="taf", category=cmn.Cats.WEATHER) async def taf(self, ctx: commands.Context, airport: str): """Gets forecasted raw TAF (Terminal Aerodrome Forecast) data for an airport. Includes the latest METAR data. diff --git a/exts/morse.py b/exts/morse.py index e52db9b..5a73a90 100644 --- a/exts/morse.py +++ b/exts/morse.py @@ -23,7 +23,7 @@ class MorseCog(commands.Cog): self.morse: dict[str, str] = d["morse"] self.ascii: dict[str, int] = d["ascii"] - @commands.command(name="morse", aliases=["cw"], category=cmn.cat.ref) + @commands.command(name="morse", aliases=["cw"], category=cmn.Cats.CODES) async def _morse(self, ctx: commands.Context, *, msg: str): """Converts ASCII to international morse code.""" result = "" @@ -39,7 +39,7 @@ class MorseCog(commands.Cog): embed.colour = cmn.colours.good await ctx.send(embed=embed) - @commands.command(name="unmorse", aliases=["demorse", "uncw", "decw"], category=cmn.cat.ref) + @commands.command(name="unmorse", aliases=["demorse", "uncw", "decw"], category=cmn.Cats.CODES) async def _unmorse(self, ctx: commands.Context, *, msg: str): """Converts international morse code to ASCII.""" result = "" @@ -59,7 +59,7 @@ class MorseCog(commands.Cog): embed.colour = cmn.colours.good await ctx.send(embed=embed) - @commands.command(name="cwweight", aliases=["weight", "cww"], category=cmn.cat.ref) + @commands.command(name="cwweight", aliases=["weight", "cww"], category=cmn.Cats.CODES) async def _weight(self, ctx: commands.Context, *, msg: str): """Calculates the CW weight of a callsign or message.""" embed = cmn.embed_factory(ctx) diff --git a/exts/prefixes.py b/exts/prefixes.py new file mode 100644 index 0000000..b9f3081 --- /dev/null +++ b/exts/prefixes.py @@ -0,0 +1,48 @@ +""" +Prefixes Lookup extension for qrm +--- +Copyright (C) 2021 Abigail Gold, 0x5c + +This file is part of qrm2 and is released under the terms of +the GNU General Public License, version 2. +""" + + +import discord.ext.commands as commands + +import common as cmn +from resources import callsign_info + + +class PrefixesCog(commands.Cog): + def __init__(self, bot: commands.Bot): + self.bot = bot + self.pfxs = callsign_info.options + + @commands.command(name="prefixes", aliases=["vanity", "pfx", "vanities", "prefix"], category=cmn.Cats.REF) + async def _vanity_prefixes(self, ctx: commands.Context, country: str = ""): + """Lists valid callsign prefixes for different countries.""" + country = country.lower() + embed = cmn.embed_factory(ctx) + if country not in self.pfxs: + desc = "Possible arguments are:\n" + for key, val in self.pfxs.items(): + desc += f"`{key}`: {val.title}{(' ' + val.emoji if val.emoji else '')}\n" + embed.title = f"{country} Not Found!" + embed.description = desc + embed.colour = cmn.colours.bad + await ctx.send(embed=embed) + return + else: + data = self.pfxs[country] + embed.title = data.title + (" " + data.emoji if data.emoji else "") + embed.description = data.desc + embed.colour = cmn.colours.good + + for name, val in data.calls.items(): + embed.add_field(name=name, value=val, inline=False) + await ctx.send(embed=embed) + + +def setup(bot: commands.Bot): + bot.add_cog(PrefixesCog(bot)) diff --git a/exts/propagation.py b/exts/propagation.py index 43efd64..4984767 100644 --- a/exts/propagation.py +++ b/exts/propagation.py @@ -12,6 +12,7 @@ from io import BytesIO import aiohttp import cairosvg +from datetime import datetime import discord import discord.ext.commands as commands @@ -22,12 +23,14 @@ import common as cmn class PropagationCog(commands.Cog): muf_url = "https://prop.kc2g.com/renders/current/mufd-normal-now.svg" fof2_url = "https://prop.kc2g.com/renders/current/fof2-normal-now.svg" + gl_baseurl = "https://www.fourmilab.ch/cgi-bin/uncgi/Earth?img=ETOPO1_day-m.evif&dynimg=y&opt=-p" + n0nbh_sun_url = "http://www.hamqsl.com/solarsun.php" def __init__(self, bot): self.bot = bot self.session = aiohttp.ClientSession(connector=bot.qrm.connector) - @commands.command(name="mufmap", aliases=["muf"], category=cmn.cat.weather) + @commands.command(name="mufmap", aliases=["muf"], category=cmn.Cats.WEATHER) async def mufmap(self, ctx: commands.Context): """Shows a world map of the Maximum Usable Frequency (MUF).""" async with ctx.typing(): @@ -41,7 +44,7 @@ class PropagationCog(commands.Cog): embed.set_image(url="attachment://muf_map.png") await ctx.send(file=file, embed=embed) - @commands.command(name="fof2map", aliases=["fof2", "critfreq"], category=cmn.cat.weather) + @commands.command(name="fof2map", aliases=["fof2", "critfreq"], category=cmn.Cats.WEATHER) async def fof2map(self, ctx: commands.Context): """Shows a world map of the Critical Frequency (foF2).""" async with ctx.typing(): @@ -55,6 +58,30 @@ class PropagationCog(commands.Cog): embed.set_image(url="attachment://fof2_map.png") await ctx.send(file=file, embed=embed) + @commands.command(name="grayline", aliases=["greyline", "grey", "gray", "gl"], category=cmn.Cats.WEATHER) + async def grayline(self, ctx: commands.Context): + """Gets a map of the current greyline, where HF propagation is the best.""" + embed = cmn.embed_factory(ctx) + embed.title = "Current Greyline Conditions" + embed.colour = cmn.colours.good + date_params = f"&date=1&utc={datetime.utcnow():%Y-%m-%d+%H:%M:%S}" + embed.set_image(url=self.gl_baseurl + date_params) + await ctx.send(embed=embed) + + @commands.command(name="solarweather", aliases=["solar", "bandconditions", "cond", "condx", "conditions"], + category=cmn.Cats.WEATHER) + async def solarweather(self, ctx: commands.Context): + """Gets a solar weather report.""" + embed = cmn.embed_factory(ctx) + embed.title = "☀️ Current Solar Weather" + if ctx.invoked_with in ["bandconditions", "cond", "condx", "conditions"]: + embed.add_field(name="⚠️ Deprecated Command Alias", + value=(f"This command has been renamed to `{ctx.prefix}solar`!\n" + "The alias you used will be removed in the next version.")) + embed.colour = cmn.colours.good + embed.set_image(url=self.n0nbh_sun_url) + await ctx.send(embed=embed) + def setup(bot: commands.Bot): bot.add_cog(PropagationCog(bot)) diff --git a/exts/study.py b/exts/study.py index 530d6db..423aff6 100644 --- a/exts/study.py +++ b/exts/study.py @@ -31,7 +31,7 @@ class StudyCog(commands.Cog): self.source = "Data courtesy of [HamStudy.org](https://hamstudy.org/)" self.session = aiohttp.ClientSession(connector=bot.qrm.connector) - @commands.command(name="hamstudy", aliases=["rq", "randomquestion", "randomq"], category=cmn.cat.study) + @commands.command(name="hamstudy", aliases=["rq", "randomquestion", "randomq"], category=cmn.Cats.STUDY) async def _random_question(self, ctx: commands.Context, country: str = "", level: str = "", element: str = ""): """Gets a random question from [HamStudy's](https://hamstudy.org) question pools.""" with ctx.typing(): diff --git a/exts/tex.py b/exts/tex.py index 6acf246..bc70bff 100644 --- a/exts/tex.py +++ b/exts/tex.py @@ -26,7 +26,7 @@ class TexCog(commands.Cog): with open(cmn.paths.resources / "template.1.tex") as latex_template: self.template = latex_template.read() - @commands.command(name="tex", aliases=["latex"], category=cmn.cat.fun) + @commands.command(name="tex", aliases=["latex"], category=cmn.Cats.UTILS) async def tex(self, ctx: commands.Context, *, expr: str): """Renders a LaTeX expression.""" payload = { diff --git a/exts/time.py b/exts/time.py new file mode 100644 index 0000000..4310ca7 --- /dev/null +++ b/exts/time.py @@ -0,0 +1,35 @@ +""" +Time extension for qrm +--- +Copyright (C) 2021 classabbyamp, 0x5c + +This file is part of qrm2 and is released under the terms of +the GNU General Public License, version 2. +""" + + +from datetime import datetime + +import discord.ext.commands as commands + +import common as cmn + + +class TimeCog(commands.Cog): + def __init__(self, bot): + self.bot = bot + + @commands.command(name="utc", aliases=["z"], category=cmn.Cats.TIME) + async def _utc_lookup(self, ctx: commands.Context): + """Returns the current time in UTC.""" + now = datetime.utcnow() + result = "**" + now.strftime("%Y-%m-%d %H:%M") + "Z**" + embed = cmn.embed_factory(ctx) + embed.title = "The current time is:" + embed.description = result + embed.colour = cmn.colours.good + await ctx.send(embed=embed) + + +def setup(bot: commands.Bot): + bot.add_cog(TimeCog(bot)) diff --git a/main.py b/main.py index 274e5c1..2b7db03 100644 --- a/main.py +++ b/main.py @@ -74,7 +74,7 @@ bot.qrm.debug_mode = debug_mode # --- Commands --- -@bot.command(name="restart", aliases=["rs"], category=cmn.cat.admin) +@bot.command(name="restart", aliases=["rs"], category=cmn.BoltCats.ADMIN) @commands.check(cmn.check_if_owner) async def _restart_bot(ctx: commands.Context): """Restarts the bot.""" @@ -85,7 +85,7 @@ async def _restart_bot(ctx: commands.Context): await bot.logout() -@bot.command(name="shutdown", aliases=["shut"], category=cmn.cat.admin) +@bot.command(name="shutdown", aliases=["shut"], category=cmn.BoltCats.ADMIN) @commands.check(cmn.check_if_owner) async def _shutdown_bot(ctx: commands.Context): """Shuts down the bot.""" @@ -96,7 +96,7 @@ async def _shutdown_bot(ctx: commands.Context): await bot.logout() -@bot.group(name="extctl", aliases=["ex"], case_insensitive=True, category=cmn.cat.admin) +@bot.group(name="extctl", aliases=["ex"], case_insensitive=True, category=cmn.BoltCats.ADMIN) @commands.check(cmn.check_if_owner) async def _extctl(ctx: commands.Context): """Extension control commands. diff --git a/templates/data/options.py b/templates/data/options.py index c88b204..b8db98f 100644 --- a/templates/data/options.py +++ b/templates/data/options.py @@ -31,20 +31,23 @@ owners_uids = (200102491231092736, 564766093051166729) # The extensions to load when running the bot. exts = [ - "ae7q", "base", + "ae7q", + "callsign", + "codes", + "contests", + "dbconv", + "dxcc", "fun", "grid", - "ham", "image", - "lookup", + "land_weather", "morse", - "qrz", + "prefixes", + "propagation", "study", "tex", - "weather", - "dbconv", - "propagation", + "time", ] # URL to the resources