Merge branch 'master' into lookupcog

This commit is contained in:
Abigail Gold 2019-12-10 21:34:57 -05:00
commit 25ea169bf1
No known key found for this signature in database
GPG Key ID: CF88335E873C3FB4
22 changed files with 207 additions and 75 deletions

29
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@ -0,0 +1,29 @@
---
name: Bug report
about: Report a bug to help us improve qrm
title: ''
labels: bug
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Run command '...' with input '...'
2. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**System (include if related to running the bot):**
- OS: [e.g. Linux, Docker]
- Version: [e.g. 22]
**Additional context**
Add any other context about the problem here.

View File

@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for qrm
title: ''
labels: enhancement
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View File

@ -10,6 +10,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Rich lookup for QRZ, if a QRZ subscription is present
- Timestamp and requester username and avatar are now shown on embeds
- Current and 3-Day Forecast terrestrial weather conditions lookup commands
- Extension control commands.
- Changelog command
### Changed
- Rewrote code to take advantage of discord.py's cogs

View File

@ -4,8 +4,8 @@ A sample `docker-compose.yml` file:
version: '3'
services:
bot:
image: "classabbyamp/discord-qrm-bot:latest"
container_name: "qrmbot"
image: "classabbyamp/discord-qrm2:latest"
container_name: "discord-qrm2"
volumes:
- "./data:/app/data:rw"
```

View File

@ -1,4 +1,4 @@
# Discord QRM Bot
# qrm, a Bot for Discord
A discord bot with ham radio functionalities.

View File

@ -3,7 +3,7 @@ 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
This file is part of discord-qrm2 and is released under the terms of the GNU
General Public License, version 2.
---
@ -12,8 +12,22 @@ General Public License, version 2.
`cat`: Category names for the HelpCommand.
"""
import traceback
from datetime import datetime
from types import SimpleNamespace
import discord
import discord.ext.commands as commands
import data.options as opt
__all__ = ["colours", "cat", "emojis", "error_embed_factory", "add_react", "check_if_owner"]
# --- Common values ---
colours = SimpleNamespace(good=0x43B581,
neutral=0x7289DA,
@ -25,3 +39,39 @@ cat = SimpleNamespace(lookup='Information Lookup',
ref='Reference',
study='Exam Study',
weather='Land and Space Weather')
emojis = SimpleNamespace(good='',
bad='')
# --- Helper functions ---
def error_embed_factory(ctx: commands.Context, exception: Exception, debug_mode: bool) -> discord.Embed:
"""Creates an Error embed."""
if debug_mode:
fmtd_ex = traceback.format_exception(exception.__class__, exception, exception.__traceback__)
else:
fmtd_ex = traceback.format_exception_only(exception.__class__, exception)
embed = discord.Embed(title="Error",
timestamp=datetime.utcnow(),
colour=colours.bad)
embed.set_footer(text=ctx.author,
icon_url=str(ctx.author.avatar_url))
embed.description = "```\n" + '\n'.join(fmtd_ex) + "```"
return embed
async def add_react(msg: discord.Message, react: str):
try:
await msg.add_reaction(react)
except discord.Forbidden:
print(f"[!!] Missing permissions to add reaction in '{msg.guild.id}/{msg.channel.id}'!")
# --- Checks ---
async def check_if_owner(ctx: commands.Context):
if ctx.author.id in opt.owners_uids:
return True
await add_react(ctx.message, emojis.bad)
return False

View File

@ -2,6 +2,6 @@ version: '3'
services:
bot:
build: .
container_name: "qrmbot"
container_name: "discord-qrm2"
volumes:
- "./data:/app/data:rw"

View File

@ -1,9 +1,9 @@
"""
ae7q cog for qrm
ae7q extension for qrm
---
Copyright (C) 2019 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-qrm2 and is released under the terms of the GNU
General Public License, version 2.
---
Test callsigns:

View File

@ -1,9 +1,9 @@
"""
Base cog for qrm
Base extension for qrm
---
Copyright (C) 2019 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-qrm2 and is released under the terms of the GNU
General Public License, version 2.
"""
@ -17,7 +17,7 @@ import discord.ext.commands as commands
import info
from data import options as opt
import data.options as opt
import common as cmn
@ -142,7 +142,7 @@ class BaseCog(commands.Cog):
"""Show what has changed in recent bot versions."""
embed = discord.Embed(title="qrm Changelog",
description=("For a full listing, visit [Github](https://"
"github.com/classabbyamp/discord-qrm-bot/blob/master/CHANGELOG.md)."),
"github.com/classabbyamp/discord-qrm2/blob/master/CHANGELOG.md)."),
colour=cmn.colours.neutral,
timestamp=datetime.utcnow())
embed.set_footer(text=ctx.author.name,

View File

@ -1,9 +1,9 @@
"""
Fun cog for qrm
Fun extension for qrm
---
Copyright (C) 2019 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-qrm2 and is released under the terms of the GNU
General Public License, version 2.
"""

View File

@ -1,9 +1,9 @@
"""
Grid cog for qrm
Grid extension for qrm
---
Copyright (C) 2019 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-qrm2 and is released under the terms of the GNU
General Public License, version 2.
"""

View File

@ -1,9 +1,9 @@
"""
Ham cog for qrm
Ham extension for qrm
---
Copyright (C) 2019 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-qrm2 and is released under the terms of the GNU
General Public License, version 2.
"""
import json
@ -84,7 +84,7 @@ class HamCog(commands.Cog):
if country.lower() not in callsign_info.options:
embed = discord.Embed(title=f'{country} not found!',
description=f'Valid countries: {", ".join(callsign_info.options.keys())}',
colour=self.gs.colours.bad,
colour=cmn.colours.bad,
timestamp=datetime.utcnow())
embed.set_footer(text=ctx.author.name,
icon_url=str(ctx.author.avatar_url))
@ -92,7 +92,7 @@ class HamCog(commands.Cog):
return
embed = discord.Embed(title=callsign_info.options[country.lower()][0],
description=callsign_info.options[country.lower()][1],
colour=self.gs.colours.good,
colour=cmn.colours.good,
timestamp=datetime.utcnow())
embed.set_footer(text=ctx.author.name,
icon_url=str(ctx.author.avatar_url))

View File

@ -1,9 +1,9 @@
"""
Image cog for qrm
Image extension for qrm
---
Copyright (C) 2019 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-qrm2 and is released under the terms of the GNU
General Public License, version 2.
"""

View File

@ -1,9 +1,9 @@
"""
Morse Code cog for qrm
Morse Code extension for qrm
---
Copyright (C) 2019 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-qrm2 and is released under the terms of the GNU
General Public License, version 2.
"""

View File

@ -1,9 +1,9 @@
"""
QRZ cog for qrm
QRZ extension for qrm
---
Copyright (C) 2019 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-qrm2 and is released under the terms of the GNU
General Public License, version 2.
"""
from collections import OrderedDict
@ -17,7 +17,7 @@ import aiohttp
from lxml import etree
import common as cmn
import keys
import data.keys as keys
class QRZCog(commands.Cog):
@ -101,7 +101,7 @@ class QRZCog(commands.Cog):
async def qrz_login(user: str, passwd: str, session: aiohttp.ClientSession):
url = f'http://xmldata.qrz.com/xml/current/?username={user};password={passwd};agent=qrmbot'
url = f'http://xmldata.qrz.com/xml/current/?username={user};password={passwd};agent=discord-qrm2'
async with session.get(url) as resp:
if resp.status != 200:
raise ConnectionError(f'Unable to connect to QRZ (HTTP Error {resp.status})')

View File

@ -1,9 +1,9 @@
"""
Study cog for qrm
Study extension for qrm
---
Copyright (C) 2019 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-qrm2 and is released under the terms of the GNU
General Public License, version 2.
"""

View File

@ -1,9 +1,9 @@
"""
Weather cog for qrm
Weather extension for qrm
---
Copyright (C) 2019 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-qrm2 and is released under the terms of the GNU
General Public License, version 2.
"""
@ -90,13 +90,10 @@ See help for weather command for possible location types. Add a `-c` or `-f` to
embed.colour = cmn.colours.bad
else:
data = io.BytesIO(await resp.read())
loc = loc.replace('+', '')
loc = loc.replace('@', '')
loc = loc.replace('~', '')
embed.set_image(url=f'attachment://{loc}_{units}pnFQ.png')
embed.set_image(url=f'attachment://wttr_forecast.png')
embed.set_footer(text=ctx.author.name,
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'wttr_forecast.png'))
@_weather_conditions.command(name='now', aliases=['n'], category=cmn.cat.weather)
async def _weather_conditions_now(self, ctx: commands.Context, *, location: str):
@ -128,13 +125,10 @@ See help for weather command for possible location types. Add a `-c` or `-f` to
embed.colour = cmn.colours.bad
else:
data = io.BytesIO(await resp.read())
loc = loc.replace('+', '')
loc = loc.replace('@', '')
loc = loc.replace('~', '')
embed.set_image(url=f'attachment://{loc}_0{units}pnFQ.png')
embed.set_image(url=f'attachment://wttr_now.png')
embed.set_footer(text=ctx.author.name,
icon_url=str(ctx.author.avatar_url))
await ctx.send(embed=embed, file=discord.File(data, f'{loc}_0{units}pnFQ.png'))
await ctx.send(embed=embed, file=discord.File(data, 'wttr_now.png'))
def setup(bot: commands.Bot):

View File

@ -3,7 +3,7 @@ Static info about the bot.
---
Copyright (C) 2019 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-qrm2 and is released under the terms of the GNU
General Public License, version 2.
---
@ -23,5 +23,5 @@ General Public License, version 2.
authors = ("@ClassAbbyAmplifier#2229", "@0x5c#0639")
description = """A bot with various useful ham radio-related functions, written in Python."""
license = "Released under the GNU General Public License v2"
contributing = "Check out the source on GitHub, contributions welcome: https://github.com/classabbyamp/discord-qrm-bot"
contributing = "Check out the source on GitHub, contributions welcome: https://github.com/classabbyamp/discord-qrm2"
release = '1.0.0'

95
main.py
View File

@ -4,23 +4,29 @@ qrm, a bot for Discord
---
Copyright (C) 2019 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-qrm2 and is released under the terms of the GNU
General Public License, version 2.
"""
from datetime import datetime
import discord
from discord.ext import commands, tasks
import common as cmn
import info
from data import options as opt
from data import keys
import data.options as opt
import data.keys as keys
# --- Settings ---
exit_code = 1 # The default exit code. ?shutdown and ?restart will change it accordingly (fail-safe)
ext_dir = "exts" # The name of the directory where extensions are located.
debug_mode = opt.debug # Separate assignement in-case we define an override (ternary operator goes here)
@ -31,46 +37,80 @@ bot = commands.Bot(command_prefix=opt.prefix,
help_command=commands.MinimalHelpCommand())
# --- Helper functions ---
async def add_react(msg: discord.Message, react: str):
try:
await msg.add_reaction(react)
except discord.Forbidden:
print(f"!! Missing permissions to add reaction in '{msg.guild.id}/{msg.channel.id}'!")
# --- Checks ---
async def check_if_owner(ctx: commands.Context):
if ctx.author.id in opt.owners_uids:
return True
await add_react(ctx.message, "")
return False
# --- Commands ---
@bot.command(name="restart", hidden=True)
@commands.check(check_if_owner)
@commands.check(cmn.check_if_owner)
async def _restart_bot(ctx: commands.Context):
"""Restarts the bot."""
global exit_code
await add_react(ctx.message, "")
await cmn.add_react(ctx.message, cmn.emojis.good)
print(f"[**] Restarting! Requested by {ctx.author}.")
exit_code = 42 # Signals to the wrapper script that the bot needs to be restarted.
await bot.logout()
@bot.command(name="shutdown", hidden=True)
@commands.check(check_if_owner)
@commands.check(cmn.check_if_owner)
async def _shutdown_bot(ctx: commands.Context):
"""Shuts down the bot."""
global exit_code
await add_react(ctx.message, "")
await cmn.add_react(ctx.message, cmn.emojis.good)
print(f"[**] Shutting down! Requested by {ctx.author}.")
exit_code = 0 # Signals to the wrapper script that the bot should not be restarted.
await bot.logout()
@bot.group(name="extctl", hidden=True)
@commands.check(cmn.check_if_owner)
async def _extctl(ctx: commands.Context):
"""Extension control commands.
Defaults to `list` if no subcommand specified"""
if ctx.invoked_subcommand is None:
cmd = bot.get_command("extctl list")
await ctx.invoke(cmd)
@_extctl.command(name="list")
async def _extctl_list(ctx: commands.Context):
"""Lists Extensions."""
embed = discord.Embed(title="Loaded Extensions",
colour=cmn.colours.neutral,
timestamp=datetime.utcnow())
embed.description = "\n".join(["" + x.split(".")[1] for x in bot.extensions.keys()])
await ctx.send(embed=embed)
@_extctl.command(name="load")
async def _extctl_load(ctx: commands.Context, extension: str):
try:
bot.load_extension(ext_dir + "." + extension)
await cmn.add_react(ctx.message, cmn.emojis.good)
except commands.ExtensionError as ex:
embed = cmn.error_embed_factory(ctx, ex, debug_mode)
await ctx.send(embed=embed)
@_extctl.command(name="reload")
async def _extctl_reload(ctx: commands.Context, extension: str):
try:
bot.reload_extension(ext_dir + "." + extension)
await cmn.add_react(ctx.message, cmn.emojis.good)
except commands.ExtensionError as ex:
embed = cmn.error_embed_factory(ctx, ex, debug_mode)
await ctx.send(embed=embed)
@_extctl.command(name="unload")
async def _extctl_unload(ctx: commands.Context, extension: str):
try:
bot.unload_extension(ext_dir + "." + extension)
await cmn.add_react(ctx.message, cmn.emojis.good)
except commands.ExtensionError as ex:
embed = cmn.error_embed_factory(ctx, ex, debug_mode)
await ctx.send(embed=embed)
# --- Events ---
@bot.event
@ -93,9 +133,8 @@ async def _before_ensure_activity():
# --- Run ---
# bot.add_cog(GlobalSettings(bot))
for cog in opt.cogs:
bot.load_extension(f"cogs.{cog}")
for ext in opt.exts:
bot.load_extension(ext_dir + '.' + ext)
_ensure_activity.start()

View File

@ -8,5 +8,5 @@
"?": "..--..", "'": ".----.", "!": "-.-.--", "/": "-..-.", "(": "-.--.", ")": "-.--.-",
"&": ".-...", ":": "---...", ";": "-.-.-.", "=": "-...-", "+": ".-.-.", "-": "-....-",
"\"": ".-..-.", "@": ".--.-.", "Ä": ".-.-", "Å": ".-.-", "Ą": ".-.-", "Æ": ".-.-",
"É": "..-..", "Ñ": "--.--", "Ö": "---.", "Ü": "..--", "Š": "----"
"É": "..-..", "Ñ": "--.--", "Ö": "---.", "Ü": "..--", "Š": "----", " ": "/"
}

View File

@ -26,9 +26,8 @@ debug = False
# ! This MUST be a tuple of integers. Single element tuple: `(123,)`
owners_uids = (200102491231092736,)
# The cogs to load when running the bot.
cogs = ['basecog', 'morsecog', 'funcog', 'gridcog', 'hamcog', 'imagecog',
'studycog', 'ae7qcog', 'qrzcog', 'weathercog']
# The extensions to load when running the bot.
exts = ['ae7q', 'base', 'fun', 'grid', 'ham', 'image', 'morse', 'qrz', 'study', 'weather']
# The text to put in the "playing" status.
game = 'with lids on 7.200'