mirror of
https://github.com/miaowware/qrm2.git
synced 2025-09-06 22:57:47 -04:00
Merge branch 'master' into lookupcog
This commit is contained in:
commit
25ea169bf1
29
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
29
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal 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.
|
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal 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.
|
@ -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
|
||||
|
@ -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"
|
||||
```
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Discord QRM Bot
|
||||
# qrm, a Bot for Discord
|
||||
|
||||
A discord bot with ham radio functionalities.
|
||||
|
||||
|
52
common.py
52
common.py
@ -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
|
||||
|
@ -2,6 +2,6 @@ version: '3'
|
||||
services:
|
||||
bot:
|
||||
build: .
|
||||
container_name: "qrmbot"
|
||||
container_name: "discord-qrm2"
|
||||
volumes:
|
||||
- "./data:/app/data:rw"
|
||||
|
@ -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:
|
@ -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,
|
@ -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.
|
||||
"""
|
||||
|
@ -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.
|
||||
"""
|
||||
|
@ -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))
|
@ -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.
|
||||
"""
|
||||
|
@ -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.
|
||||
"""
|
||||
|
@ -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})')
|
@ -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.
|
||||
"""
|
||||
|
@ -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):
|
4
info.py
4
info.py
@ -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
95
main.py
@ -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()
|
||||
|
||||
|
@ -8,5 +8,5 @@
|
||||
"?": "..--..", "'": ".----.", "!": "-.-.--", "/": "-..-.", "(": "-.--.", ")": "-.--.-",
|
||||
"&": ".-...", ":": "---...", ";": "-.-.-.", "=": "-...-", "+": ".-.-.", "-": "-....-",
|
||||
"\"": ".-..-.", "@": ".--.-.", "Ä": ".-.-", "Å": ".-.-", "Ą": ".-.-", "Æ": ".-.-",
|
||||
"É": "..-..", "Ñ": "--.--", "Ö": "---.", "Ü": "..--", "Š": "----"
|
||||
"É": "..-..", "Ñ": "--.--", "Ö": "---.", "Ü": "..--", "Š": "----", " ": "/"
|
||||
}
|
||||
|
@ -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'
|
||||
|
Loading…
x
Reference in New Issue
Block a user