Merge branch 'master' into ae7q-refactor

This commit is contained in:
Abigail Gold 2020-01-08 16:33:07 -05:00
commit f5a9b0b780
No known key found for this signature in database
GPG Key ID: CF88335E873C3FB4
21 changed files with 107 additions and 45 deletions

View File

@ -28,7 +28,7 @@ $ run.sh
## Copyright ## Copyright
Copyright (C) 2019 Abigail Gold, 0x5c Copyright (C) 2019-2020 Abigail Gold, 0x5c
This program is released under the terms of the GNU General Public License, This program is released under the terms of the GNU General Public License,
version 2. See `COPYING` for full license text. version 2. See `COPYING` for full license text.

View File

@ -1,23 +1,19 @@
""" """
Common tools for the bot. Common tools for the bot.
--- ---
Copyright (C) 2019 Abigail Gold, 0x5c Copyright (C) 2019-2020 Abigail Gold, 0x5c
This file is part of discord-qrm2 and is released under the terms of the GNU This file is part of discord-qrm2 and is released under the terms of the GNU
General Public License, version 2. General Public License, version 2.
---
`colours`: Colours used by embeds.
`cat`: Category names for the HelpCommand.
""" """
import collections import collections
import json import json
import re
import traceback import traceback
from pathlib import Path
from datetime import datetime from datetime import datetime
from pathlib import Path
from types import SimpleNamespace from types import SimpleNamespace
import discord import discord
@ -26,7 +22,8 @@ import discord.ext.commands as commands
import data.options as opt import data.options as opt
__all__ = ["colours", "cat", "emojis", "embed_factory", "error_embed_factory", "add_react", "check_if_owner"] __all__ = ["colours", "cat", "emojis", "paths", "ImageMetadata", "ImagesGroup",
"embed_factory", "error_embed_factory", "add_react", "check_if_owner"]
# --- Common values --- # --- Common values ---
@ -94,6 +91,29 @@ class ImagesGroup(collections.abc.Mapping):
return str(self._images) return str(self._images)
# --- Converters ---
class GlobalChannelConverter(commands.IDConverter):
"""Converter to get any bot-acessible channel by ID/mention (global), or name (in current guild only)."""
async def convert(self, ctx: commands.Context, argument: str):
bot = ctx.bot
guild = ctx.guild
match = self._get_id_match(argument) or re.match(r'<#([0-9]+)>$', argument)
result = None
if match is None:
# not a mention/ID
if guild:
result = discord.utils.get(guild.text_channels, name=argument)
else:
raise commands.BadArgument(f"""Channel named "{argument}" not found in this guild.""")
else:
channel_id = int(match.group(1))
result = bot.get_channel(channel_id)
if not isinstance(result, (discord.TextChannel, discord.abc.PrivateChannel)):
raise commands.BadArgument(f"""Channel "{argument}" not found.""")
return result
# --- Helper functions --- # --- Helper functions ---
def embed_factory(ctx: commands.Context) -> discord.Embed: def embed_factory(ctx: commands.Context) -> discord.Embed:

View File

@ -1,7 +1,7 @@
""" """
ae7q extension for qrm ae7q extension for qrm
--- ---
Copyright (C) 2019 Abigail Gold, 0x5c Copyright (C) 2019-2020 Abigail Gold, 0x5c
This file is part of discord-qrm2 and is released under the terms of the GNU This file is part of discord-qrm2 and is released under the terms of the GNU
General Public License, version 2. General Public License, version 2.
@ -16,6 +16,7 @@ KC4USA: reserved, no call history, *but* has application history
import discord.ext.commands as commands import discord.ext.commands as commands
import aiohttp
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
import common as cmn import common as cmn
@ -24,7 +25,7 @@ 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.session = bot.qrm.session self.session = aiohttp.ClientSession(connector=bot.qrm.connector)
@commands.group(name="ae7q", aliases=["ae"], category=cmn.cat.lookup) @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):

View File

@ -1,23 +1,25 @@
""" """
Base extension for qrm Base extension for qrm
--- ---
Copyright (C) 2019 Abigail Gold, 0x5c Copyright (C) 2019-2020 Abigail Gold, 0x5c
This file is part of discord-qrm2 and is released under the terms of the GNU This file is part of discord-qrm2 and is released under the terms of the GNU
General Public License, version 2. General Public License, version 2.
""" """
import random
import re import re
from collections import OrderedDict from collections import OrderedDict
import random from typing import Union
import discord import discord
import discord.ext.commands as commands import discord.ext.commands as commands
import info import info
import common as cmn
import data.options as opt import data.options as opt
import common as cmn
class QrmHelpCommand(commands.HelpCommand): class QrmHelpCommand(commands.HelpCommand):
@ -171,8 +173,13 @@ class BaseCog(commands.Cog):
@commands.command(name="echo", aliases=["e"], hidden=True) @commands.command(name="echo", aliases=["e"], hidden=True)
@commands.check(cmn.check_if_owner) @commands.check(cmn.check_if_owner)
async def _echo(self, ctx: commands.Context, channel: commands.TextChannelConverter, *, msg: str): async def _echo(self, ctx: commands.Context,
"""Send a message in a channel as qrm. Only works within a server or DM to server, not between servers.""" channel: Union[cmn.GlobalChannelConverter, commands.UserConverter], *, msg: str):
"""Send a message in a channel as qrm. Accepts channel/user IDs/mentions.
Channel names are current-guild only.
Does not work with the ID of the bot user."""
if isinstance(channel, discord.ClientUser):
raise commands.BadArgument("Can't send to the bot user!")
await channel.send(msg) await channel.send(msg)

View File

@ -1,7 +1,7 @@
""" """
Fun extension for qrm Fun extension for qrm
--- ---
Copyright (C) 2019 Abigail Gold, 0x5c Copyright (C) 2019-2020 Abigail Gold, 0x5c
This file is part of discord-qrm2 and is released under the terms of the GNU This file is part of discord-qrm2 and is released under the terms of the GNU
General Public License, version 2. General Public License, version 2.

View File

@ -1,7 +1,7 @@
""" """
Grid extension for qrm Grid extension for qrm
--- ---
Copyright (C) 2019 Abigail Gold, 0x5c Copyright (C) 2019-2020 Abigail Gold, 0x5c
This file is part of discord-qrm2 and is released under the terms of the GNU This file is part of discord-qrm2 and is released under the terms of the GNU
General Public License, version 2. General Public License, version 2.

View File

@ -1,7 +1,7 @@
""" """
Ham extension for qrm Ham extension for qrm
--- ---
Copyright (C) 2019 Abigail Gold, 0x5c Copyright (C) 2019-2020 Abigail Gold, 0x5c
This file is part of discord-qrm2 and is released under the terms of the GNU This file is part of discord-qrm2 and is released under the terms of the GNU
General Public License, version 2. General Public License, version 2.

View File

@ -1,7 +1,7 @@
""" """
Image extension for qrm Image extension for qrm
--- ---
Copyright (C) 2019 Abigail Gold, 0x5c Copyright (C) 2019-2020 Abigail Gold, 0x5c
This file is part of discord-qrm2 and is released under the terms of the GNU This file is part of discord-qrm2 and is released under the terms of the GNU
General Public License, version 2. General Public License, version 2.
@ -9,6 +9,8 @@ General Public License, version 2.
import io import io
import aiohttp
import discord import discord
import discord.ext.commands as commands import discord.ext.commands as commands
@ -20,7 +22,7 @@ class ImageCog(commands.Cog):
self.bot = bot self.bot = bot
self.bandcharts = cmn.ImagesGroup(cmn.paths.bandcharts / "meta.json") self.bandcharts = cmn.ImagesGroup(cmn.paths.bandcharts / "meta.json")
self.maps = cmn.ImagesGroup(cmn.paths.maps / "meta.json") self.maps = cmn.ImagesGroup(cmn.paths.maps / "meta.json")
self.session = bot.qrm.session self.session = aiohttp.ClientSession(connector=bot.qrm.connector)
@commands.command(name="bandplan", aliases=['plan', 'bands'], category=cmn.cat.ref) @commands.command(name="bandplan", aliases=['plan', 'bands'], category=cmn.cat.ref)
async def _bandplan(self, ctx: commands.Context, region: str = ''): async def _bandplan(self, ctx: commands.Context, region: str = ''):

View File

@ -1,7 +1,7 @@
""" """
Lookup extension for qrm Lookup extension for qrm
--- ---
Copyright (C) 2019 Abigail Gold, 0x5c Copyright (C) 2019-2020 Abigail Gold, 0x5c
This file is part of discord-qrm2 and is released under the terms of the GNU This file is part of discord-qrm2 and is released under the terms of the GNU
General Public License, version 2. General Public License, version 2.

View File

@ -1,7 +1,7 @@
""" """
Morse Code extension for qrm Morse Code extension for qrm
--- ---
Copyright (C) 2019 Abigail Gold, 0x5c Copyright (C) 2019-2020 Abigail Gold, 0x5c
This file is part of discord-qrm2 and is released under the terms of the GNU This file is part of discord-qrm2 and is released under the terms of the GNU
General Public License, version 2. General Public License, version 2.

View File

@ -1,7 +1,7 @@
""" """
QRZ extension for qrm QRZ extension for qrm
--- ---
Copyright (C) 2019 Abigail Gold, 0x5c Copyright (C) 2019-2020 Abigail Gold, 0x5c
This file is part of discord-qrm2 and is released under the terms of the GNU This file is part of discord-qrm2 and is released under the terms of the GNU
General Public License, version 2. General Public License, version 2.
@ -21,7 +21,7 @@ import data.keys as 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.session = bot.qrm.session self.session = aiohttp.ClientSession(connector=bot.qrm.connector)
self._qrz_session_init.start() self._qrz_session_init.start()
@commands.command(name="call", aliases=["qrz"], category=cmn.cat.lookup) @commands.command(name="call", aliases=["qrz"], category=cmn.cat.lookup)

View File

@ -1,7 +1,7 @@
""" """
Study extension for qrm Study extension for qrm
--- ---
Copyright (C) 2019 Abigail Gold, 0x5c Copyright (C) 2019-2020 Abigail Gold, 0x5c
This file is part of discord-qrm2 and is released under the terms of the GNU This file is part of discord-qrm2 and is released under the terms of the GNU
General Public License, version 2. General Public License, version 2.
@ -10,6 +10,8 @@ General Public License, version 2.
import random import random
import json import json
import aiohttp
import discord.ext.commands as commands import discord.ext.commands as commands
import common as cmn import common as cmn
@ -20,7 +22,7 @@ class StudyCog(commands.Cog):
self.bot = bot self.bot = bot
self.lastq = dict() self.lastq = dict()
self.source = 'Data courtesy of [HamStudy.org](https://hamstudy.org/)' self.source = 'Data courtesy of [HamStudy.org](https://hamstudy.org/)'
self.session = bot.qrm.session 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.cat.study)
async def _random_question(self, ctx: commands.Context, level: str = None): async def _random_question(self, ctx: commands.Context, level: str = None):

View File

@ -1,7 +1,7 @@
""" """
Weather extension for qrm Weather extension for qrm
--- ---
Copyright (C) 2019 Abigail Gold, 0x5c Copyright (C) 2019-2020 Abigail Gold, 0x5c
This file is part of discord-qrm2 and is released under the terms of the GNU This file is part of discord-qrm2 and is released under the terms of the GNU
General Public License, version 2. General Public License, version 2.
@ -10,6 +10,8 @@ General Public License, version 2.
import io import io
import re import re
import aiohttp
import discord import discord
import discord.ext.commands as commands import discord.ext.commands as commands
@ -21,7 +23,7 @@ class WeatherCog(commands.Cog):
def __init__(self, bot: commands.Bot): def __init__(self, bot: commands.Bot):
self.bot = bot self.bot = bot
self.session = bot.qrm.session self.session = aiohttp.ClientSession(connector=bot.qrm.connector)
@commands.command(name="bandconditions", aliases=['cond', 'condx', 'conditions'], category=cmn.cat.weather) @commands.command(name="bandconditions", aliases=['cond', 'condx', 'conditions'], category=cmn.cat.weather)
async def _band_conditions(self, ctx: commands.Context): async def _band_conditions(self, ctx: commands.Context):

View File

@ -1,7 +1,7 @@
""" """
Static info about the bot. Static info about the bot.
--- ---
Copyright (C) 2019 Abigail Gold, 0x5c Copyright (C) 2019-2020 Abigail Gold, 0x5c
This file is part of discord-qrm2 and is released under the terms of the GNU This file is part of discord-qrm2 and is released under the terms of the GNU
General Public License, version 2. General Public License, version 2.

31
main.py
View File

@ -2,7 +2,7 @@
""" """
qrm, a bot for Discord qrm, a bot for Discord
--- ---
Copyright (C) 2019 Abigail Gold, 0x5c Copyright (C) 2019-2020 Abigail Gold, 0x5c
This file is part of discord-qrm2 and is released under the terms of the GNU This file is part of discord-qrm2 and is released under the terms of the GNU
General Public License, version 2. General Public License, version 2.
@ -11,17 +11,19 @@ General Public License, version 2.
import sys import sys
import traceback import traceback
import asyncio
from datetime import time, datetime from datetime import time, datetime
import random import random
from types import SimpleNamespace from types import SimpleNamespace
import pytz import pytz
import aiohttp
import discord import discord
from discord.ext import commands, tasks from discord.ext import commands, tasks
import utils.connector as conn
import common as cmn import common as cmn
import info import info
import data.options as opt import data.options as opt
import data.keys as keys import data.keys as keys
@ -38,13 +40,21 @@ debug_mode = opt.debug # Separate assignement in-case we define an override (te
# --- Bot setup --- # --- Bot setup ---
# Loop/aiohttp stuff
loop = asyncio.get_event_loop()
connector = loop.run_until_complete(conn.new_connector())
bot = commands.Bot(command_prefix=opt.prefix, bot = commands.Bot(command_prefix=opt.prefix,
description=info.description, description=info.description,
help_command=commands.MinimalHelpCommand()) help_command=commands.MinimalHelpCommand(),
loop=loop,
connector=connector)
# Simple way to access bot-wide stuff in extensions.
bot.qrm = SimpleNamespace() bot.qrm = SimpleNamespace()
bot.qrm.session = aiohttp.ClientSession(headers={'User-Agent': f'discord-qrm2/{info.release}'})
# Let's store stuff here.
bot.qrm.connector = connector
bot.qrm.debug_mode = debug_mode bot.qrm.debug_mode = debug_mode
@ -54,7 +64,6 @@ bot.qrm.debug_mode = debug_mode
@commands.check(cmn.check_if_owner) @commands.check(cmn.check_if_owner)
async def _restart_bot(ctx: commands.Context): async def _restart_bot(ctx: commands.Context):
"""Restarts the bot.""" """Restarts the bot."""
await bot.qrm.session.close()
global exit_code global exit_code
await cmn.add_react(ctx.message, cmn.emojis.check_mark) await cmn.add_react(ctx.message, cmn.emojis.check_mark)
print(f"[**] Restarting! Requested by {ctx.author}.") print(f"[**] Restarting! Requested by {ctx.author}.")
@ -66,7 +75,6 @@ async def _restart_bot(ctx: commands.Context):
@commands.check(cmn.check_if_owner) @commands.check(cmn.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."""
await bot.qrm.session.close()
global exit_code global exit_code
await cmn.add_react(ctx.message, cmn.emojis.check_mark) await cmn.add_react(ctx.message, cmn.emojis.check_mark)
print(f"[**] Shutting down! Requested by {ctx.author}.") print(f"[**] Shutting down! Requested by {ctx.author}.")
@ -74,7 +82,7 @@ async def _shutdown_bot(ctx: commands.Context):
await bot.logout() await bot.logout()
@bot.group(name="extctl", hidden=True) @bot.group(name="extctl", aliases=["ex"], hidden=True)
@commands.check(cmn.check_if_owner) @commands.check(cmn.check_if_owner)
async def _extctl(ctx: commands.Context): async def _extctl(ctx: commands.Context):
"""Extension control commands. """Extension control commands.
@ -84,7 +92,7 @@ async def _extctl(ctx: commands.Context):
await ctx.invoke(cmd) await ctx.invoke(cmd)
@_extctl.command(name="list") @_extctl.command(name="list", aliases=["ls"])
async def _extctl_list(ctx: commands.Context): async def _extctl_list(ctx: commands.Context):
"""Lists Extensions.""" """Lists Extensions."""
embed = cmn.embed_factory(ctx) embed = cmn.embed_factory(ctx)
@ -93,7 +101,7 @@ async def _extctl_list(ctx: commands.Context):
await ctx.send(embed=embed) await ctx.send(embed=embed)
@_extctl.command(name="load") @_extctl.command(name="load", aliases=["ld"])
async def _extctl_load(ctx: commands.Context, extension: str): async def _extctl_load(ctx: commands.Context, extension: str):
try: try:
bot.load_extension(ext_dir + "." + extension) bot.load_extension(ext_dir + "." + extension)
@ -103,7 +111,7 @@ async def _extctl_load(ctx: commands.Context, extension: str):
await ctx.send(embed=embed) await ctx.send(embed=embed)
@_extctl.command(name="reload", aliases=["relaod"]) @_extctl.command(name="reload", aliases=["rl", "r", "relaod"])
async def _extctl_reload(ctx: commands.Context, extension: str): async def _extctl_reload(ctx: commands.Context, extension: str):
if ctx.invoked_with == "relaod": if ctx.invoked_with == "relaod":
pika = bot.get_emoji(opt.pika) pika = bot.get_emoji(opt.pika)
@ -117,7 +125,7 @@ async def _extctl_reload(ctx: commands.Context, extension: str):
await ctx.send(embed=embed) await ctx.send(embed=embed)
@_extctl.command(name="unload") @_extctl.command(name="unload", aliases=["ul"])
async def _extctl_unload(ctx: commands.Context, extension: str): async def _extctl_unload(ctx: commands.Context, extension: str):
try: try:
bot.unload_extension(ext_dir + "." + extension) bot.unload_extension(ext_dir + "." + extension)
@ -246,6 +254,7 @@ except ConnectionResetError as ex:
raise raise
raise SystemExit("ConnectionResetError: {}".format(ex)) raise SystemExit("ConnectionResetError: {}".format(ex))
# --- Exit --- # --- Exit ---
# Codes for the wrapper shell script: # Codes for the wrapper shell script:
# 0 - Clean exit, don't restart # 0 - Clean exit, don't restart

View File

@ -1,7 +1,7 @@
""" """
Information about callsigns for the vanity prefixes command in hamcog. Information about callsigns for the vanity prefixes command in hamcog.
--- ---
Copyright (C) 2019 Abigail Gold, 0x5c Copyright (C) 2019-2020 Abigail Gold, 0x5c
This file is part of discord-qrmbot and is released under the terms of the GNU 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.

View File

@ -1,7 +1,7 @@
""" """
A listing of morse code symbols A listing of morse code symbols
--- ---
Copyright (C) 2019 Abigail Gold, 0x5c Copyright (C) 2019-2020 Abigail Gold, 0x5c
This file is part of discord-qrmbot and is released under the terms of the GNU 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.

View File

@ -1,7 +1,7 @@
""" """
A listing of NATO Phonetics A listing of NATO Phonetics
--- ---
Copyright (C) 2019 Abigail Gold, 0x5c Copyright (C) 2019-2020 Abigail Gold, 0x5c
This file is part of discord-qrmbot and is released under the terms of the GNU 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.

View File

@ -1,7 +1,7 @@
""" """
A listing of Q Codes A listing of Q Codes
--- ---
Copyright (C) 2019 Abigail Gold, 0x5c Copyright (C) 2019-2020 Abigail Gold, 0x5c
This file is part of discord-qrmbot and is released under the terms of the GNU 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.

3
utils/__init__.py Normal file
View File

@ -0,0 +1,3 @@
"""
Various utilities for the bot.
"""

16
utils/connector.py Normal file
View File

@ -0,0 +1,16 @@
"""
Wrapper to handle aiohttp connector creation.
---
Copyright (C) 2020 Abigail Gold, 0x5c
This file is part of discord-qrm2 and is released under the terms of the GNU
General Public License, version 2.
"""
import aiohttp
async def new_connector(*args, **kwargs) -> aiohttp.TCPConnector:
"""*Yes, it's just a coro to instantiate a class.*"""
return aiohttp.TCPConnector(*args, **kwargs)