From 43a24d614b41507e0c9bd946bfd9e8ee5ca3ec3d Mon Sep 17 00:00:00 2001
From: classabbyamp <5366828+classabbyamp@users.noreply.github.com>
Date: Wed, 20 Jan 2021 02:48:38 -0500
Subject: [PATCH 1/9] add METAR and TAF commands (#340)
fixes #171
---
CHANGELOG.md | 1 +
exts/weather.py | 75 ++++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 75 insertions(+), 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 71a4f04..3562965 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [Unreleased]
### Added
- MUF and foF2 maps from [prop.kc2g.com](https://prop.kc2g.com/).
+- Commands to show METAR (`?metar`) and TAF (`?taf`) (aeronautical weather conditions).
### Changed
- New colour theme for `?greyline`.
- Moved great circle distance and bearing calculation from `?ungrid` to `?griddistance`.
diff --git a/exts/weather.py b/exts/weather.py
index f899fe7..b4871e6 100644
--- a/exts/weather.py
+++ b/exts/weather.py
@@ -9,7 +9,11 @@ the GNU General Public License, version 2.
import re
+from typing import List
+import aiohttp
+
+from discord import Embed
import discord.ext.commands as commands
import common as cmn
@@ -20,8 +24,10 @@ class WeatherCog(commands.Cog):
def __init__(self, bot: commands.Bot):
self.bot = bot
+ self.session = aiohttp.ClientSession(connector=bot.qrm.connector)
- @commands.command(aliases=["solar", "bandconditions", "cond", "condx", "conditions"], category=cmn.cat.weather)
+ @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)
@@ -103,6 +109,73 @@ 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)
+ 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.
+
+ Airports should be given as an \
+ [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)
+ 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.
+
+ Airports should be given as an \
+ [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, 0, True))
+
+ async def gen_metar_taf_embed(self, ctx: commands.Context, airport: str, hours: int, taf: bool) -> Embed:
+ embed = cmn.embed_factory(ctx)
+ airport = airport.upper()
+
+ if re.fullmatch(r"\w(\w|\d){2,3}", airport):
+ metar = await self.get_metar_taf_data(airport, hours, taf)
+
+ if taf:
+ embed.title = f"Current TAF for {airport}"
+ elif hours > 0:
+ embed.title = f"METAR for {airport} for the last {hours} hour{'s' if hours > 1 else ''}"
+ else:
+ embed.title = f"Current METAR for {airport}"
+
+ embed.description = "Data from [aviationweather.gov](https://www.aviationweather.gov/metar/data)."
+ embed.colour = cmn.colours.good
+
+ data = "\n".join(metar)
+ embed.description += f"\n\n```\n{data}\n```"
+ else:
+ embed.title = "Invalid airport given!"
+ embed.colour = cmn.colours.bad
+ return embed
+
+ async def get_metar_taf_data(self, airport: str, hours: int, taf: bool) -> List[str]:
+ url = (f"https://www.aviationweather.gov/metar/data?ids={airport}&format=raw&hours={hours}"
+ f"&taf={'on' if taf else 'off'}&layout=off")
+ async with self.session.get(url) as r:
+ if r.status != 200:
+ raise cmn.BotHTTPError(r)
+ page = await r.text()
+
+ # pare down to just the data
+ page = page.split("")[1].split("")[0].strip()
+ # split at
s
+ data = re.split(r"", page, maxsplit=len(airport))
+
+ parsed = []
+ for sec in data:
+ if sec.strip():
+ for line in sec.split("\n"):
+ line = line.strip()
+ # remove HTML stuff
+ line = line.replace("", "").replace("
", "")
+ line = line.replace("", "").replace("", "")
+ line = line.replace("
", "\n").replace(" ", " ")
+ line = line.strip("\n")
+ parsed.append(line)
+ return parsed
+
def setup(bot: commands.Bot):
bot.add_cog(WeatherCog(bot))
From b091b22c172587d5dbb862f42c883eed322076b2 Mon Sep 17 00:00:00 2001
From: classabbyamp <5366828+classabbyamp@users.noreply.github.com>
Date: Mon, 25 Jan 2021 17:24:13 -0500
Subject: [PATCH 2/9] add resources contrib info to ?info, ?issue, and readme
(#343)
fixes #307
---
README.md | 2 ++
exts/base.py | 7 +++++--
info.py | 5 ++++-
3 files changed, 11 insertions(+), 3 deletions(-)
diff --git a/README.md b/README.md
index 2a849ad..b125ea1 100644
--- a/README.md
+++ b/README.md
@@ -32,6 +32,8 @@ $ run.sh
Check out the [contribution guidelines](/CONTRIBUTING.md) for more information about how to contribute to this project.
+All issues and requests related to resources (including maps, band charts, data) should be added in [miaowware/qrm-resources](https://github.com/miaowware/qrm-resources).
+
## Copyright
Copyright (C) 2019-2020 Abigail Gold, 0x5c
diff --git a/exts/base.py b/exts/base.py
index 970373b..bae2853 100644
--- a/exts/base.py
+++ b/exts/base.py
@@ -170,8 +170,11 @@ class BaseCog(commands.Cog):
"""Shows how to create a bug report or feature request about the bot."""
embed = cmn.embed_factory(ctx)
embed.title = "Found a bug? Have a feature request?"
- embed.description = ("Submit an issue on the [issue tracker]"
- "(https://github.com/miaowware/qrm2/issues)!")
+ embed.description = """Submit an issue on the [issue tracker](https://github.com/miaowware/qrm2/issues)!
+
+ All issues and requests related to resources (including maps, band charts, data) \
+ should be added in \
+ [miaowware/qrm-resources](https://github.com/miaowware/qrm-resources/issues)."""
await ctx.send(embed=embed)
@commands.command(name="echo", aliases=["e"], category=cmn.cat.admin)
diff --git a/info.py b/info.py
index 04b38c1..e673c23 100644
--- a/info.py
+++ b/info.py
@@ -11,6 +11,9 @@ the GNU 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/miaowware/qrm2"
+contributing = """Check out the [source on GitHub](https://github.com/miaowware/qrm2). Contributions are welcome!
+
+ All issues and requests related to resources (including maps, band charts, data) should be added \
+ in [miaowware/qrm-resources](https://github.com/miaowware/qrm-resources)."""
release = "2.5.1"
bot_server = "https://discord.gg/Ntbg3J4"
From 6dfc05217fe4ccb44b91371d0cfa3d9edf59567c Mon Sep 17 00:00:00 2001
From: classabbyamp <5366828+classabbyamp@users.noreply.github.com>
Date: Mon, 25 Jan 2021 17:24:45 -0500
Subject: [PATCH 3/9] add the ability to select an element to hamstudy (#344)
fixes #208
---
CHANGELOG.md | 1 +
exts/study.py | 17 +++++++++++++++--
2 files changed, 16 insertions(+), 2 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3562965..dd32f06 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
### Added
- MUF and foF2 maps from [prop.kc2g.com](https://prop.kc2g.com/).
- Commands to show METAR (`?metar`) and TAF (`?taf`) (aeronautical weather conditions).
+- The ability to select an element of a pool in `?hamstudy`.
### Changed
- New colour theme for `?greyline`.
- Moved great circle distance and bearing calculation from `?ungrid` to `?griddistance`.
diff --git a/exts/study.py b/exts/study.py
index f55dd9a..d4fc5dc 100644
--- a/exts/study.py
+++ b/exts/study.py
@@ -31,13 +31,14 @@ class StudyCog(commands.Cog):
self.session = aiohttp.ClientSession(connector=bot.qrm.connector)
@commands.command(name="hamstudy", aliases=["rq", "randomquestion", "randomq"], category=cmn.cat.study)
- async def _random_question(self, ctx: commands.Context, country: str = "", level: str = ""):
+ 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():
embed = cmn.embed_factory(ctx)
country = country.lower()
level = level.lower()
+ element = element.upper()
if country in study.pool_names.keys():
if level in study.pool_names[country].keys():
@@ -115,7 +116,19 @@ class StudyCog(commands.Cog):
pool = json.loads(await resp.read())["pool"]
# Select a question
- pool_section = random.choice(pool)["sections"]
+ if element:
+ els = [el["id"] for el in pool]
+ if element in els:
+ pool_section = pool[els.index(element)]["sections"]
+ else:
+ embed.title = "Element Not Found!"
+ embed.description = f"Possible Elements for Country `{country}` and Level `{level}` are:"
+ embed.colour = cmn.colours.bad
+ embed.description += "\n\n" + "`" + "`, `".join(els) + "`"
+ await ctx.send(embed=embed)
+ return
+ else:
+ pool_section = random.choice(pool)["sections"]
pool_questions = random.choice(pool_section)["questions"]
question = random.choice(pool_questions)
From eb98e295d2fe10a4c4155649f6a91f68c64f3d9a Mon Sep 17 00:00:00 2001
From: classabbyamp <5366828+classabbyamp@users.noreply.github.com>
Date: Mon, 25 Jan 2021 17:25:26 -0500
Subject: [PATCH 4/9] add git commit to info output (#342)
fixes #341
---
exts/base.py | 19 ++++++++++++++++++-
1 file changed, 18 insertions(+), 1 deletion(-)
diff --git a/exts/base.py b/exts/base.py
index bae2853..aee4a03 100644
--- a/exts/base.py
+++ b/exts/base.py
@@ -11,6 +11,7 @@ the GNU General Public License, version 2.
import random
import re
from typing import Union
+import pathlib
import discord
import discord.ext.commands as commands
@@ -101,6 +102,22 @@ class BaseCog(commands.Cog):
def __init__(self, bot: commands.Bot):
self.bot = bot
self.changelog = parse_changelog()
+ commit_file = pathlib.Path("git_commit")
+ dot_git = pathlib.Path(".git")
+ if commit_file.is_file():
+ with commit_file.open() as f:
+ self.commit = f.readline().strip()[:7]
+ elif dot_git.is_dir():
+ head_file = pathlib.Path(dot_git, "HEAD")
+ if head_file.is_file():
+ with head_file.open() as hf:
+ head = hf.readline().split(": ")[1].strip()
+ branch_file = pathlib.Path(dot_git, head)
+ if branch_file.is_file():
+ with branch_file.open() as bf:
+ self.commit = bf.readline().strip()[:7]
+ else:
+ self.commit = ""
@commands.command(name="info", aliases=["about"])
async def _info(self, ctx: commands.Context):
@@ -110,7 +127,7 @@ class BaseCog(commands.Cog):
embed.description = info.description
embed.add_field(name="Authors", value=", ".join(info.authors))
embed.add_field(name="License", value=info.license)
- embed.add_field(name="Version", value=f"v{info.release}")
+ embed.add_field(name="Version", value=f"v{info.release} {'(`' + self.commit + '`)' if self.commit else ''}")
embed.add_field(name="Contributing", value=info.contributing, inline=False)
embed.add_field(name="Official Server", value=info.bot_server, inline=False)
embed.set_thumbnail(url=str(self.bot.user.avatar_url))
From 4ab4748b9f242bab6cbe54e248ad808d292c2a48 Mon Sep 17 00:00:00 2001
From: Fran Rogers
Date: Fri, 29 Jan 2021 22:55:26 -0500
Subject: [PATCH 5/9] Validate inputs for ?ae7q and ?qrz commands
---
exts/ae7q.py | 42 ++++++++++++++++++++++++++++++++++++++++++
exts/qrz.py | 9 +++++++++
2 files changed, 51 insertions(+)
diff --git a/exts/ae7q.py b/exts/ae7q.py
index b11aeeb..eacad7e 100644
--- a/exts/ae7q.py
+++ b/exts/ae7q.py
@@ -16,6 +16,8 @@ the GNU General Public License, version 2.
# KC4USA: reserved, no call history, *but* has application history
+import re
+
import aiohttp
from bs4 import BeautifulSoup
@@ -44,6 +46,14 @@ class AE7QCog(commands.Cog):
base_url = "http://ae7q.com/query/data/CallHistory.php?CALL="
embed = cmn.embed_factory(ctx)
+ if not re.match('[A-Z0-9]+$', callsign):
+ embed = cmn.embed_factory(ctx)
+ embed.title = "AE7Q History for Callsign"
+ embed.colour = cmn.colours.bad
+ embed.description = "Not a valid callsign!"
+ await ctx.send(embed=embed)
+ return
+
async with self.session.get(base_url + callsign) as resp:
if resp.status != 200:
raise cmn.BotHTTPError(resp)
@@ -110,6 +120,14 @@ class AE7QCog(commands.Cog):
base_url = "http://ae7q.com/query/data/CallHistory.php?CALL="
embed = cmn.embed_factory(ctx)
+ if not re.match('[A-Z0-9]+$', callsign):
+ embed = cmn.embed_factory(ctx)
+ embed.title = "AE7Q Trustee History for Callsign"
+ embed.colour = cmn.colours.bad
+ embed.description = "Not a valid callsign!"
+ await ctx.send(embed=embed)
+ return
+
async with self.session.get(base_url + callsign) as resp:
if resp.status != 200:
raise cmn.BotHTTPError(resp)
@@ -178,6 +196,14 @@ class AE7QCog(commands.Cog):
base_url = "http://ae7q.com/query/data/CallHistory.php?CALL="
embed = cmn.embed_factory(ctx)
+ if not re.match('[A-Z0-9]+$', callsign):
+ embed = cmn.embed_factory(ctx)
+ embed.title = "AE7Q Application History for Callsign"
+ embed.colour = cmn.colours.bad
+ embed.description = "Not a valid callsign!"
+ await ctx.send(embed=embed)
+ return
+
async with self.session.get(base_url + callsign) as resp:
if resp.status != 200:
raise cmn.BotHTTPError(resp)
@@ -250,6 +276,14 @@ class AE7QCog(commands.Cog):
base_url = "http://ae7q.com/query/data/FrnHistory.php?FRN="
embed = cmn.embed_factory(ctx)
+ if not re.match('[0-9]+$', frn):
+ embed = cmn.embed_factory(ctx)
+ embed.title = "AE7Q History for FRN"
+ embed.colour = cmn.colours.bad
+ embed.description = "Not a valid FRN!"
+ await ctx.send(embed=embed)
+ return
+
async with self.session.get(base_url + frn) as resp:
if resp.status != 200:
raise cmn.BotHTTPError(resp)
@@ -313,6 +347,14 @@ class AE7QCog(commands.Cog):
base_url = "http://ae7q.com/query/data/LicenseeIdHistory.php?ID="
embed = cmn.embed_factory(ctx)
+ if not re.match('[A-Z][0-9]+$', licensee_id, re.IGNORECASE):
+ embed = cmn.embed_factory(ctx)
+ embed.title = "AE7Q History for Licensee"
+ embed.colour = cmn.colours.bad
+ embed.description = "Not a valid licensee ID!"
+ await ctx.send(embed=embed)
+ return
+
async with self.session.get(base_url + licensee_id) as resp:
if resp.status != 200:
raise cmn.BotHTTPError(resp)
diff --git a/exts/qrz.py b/exts/qrz.py
index 0e79e4b..776e0fa 100644
--- a/exts/qrz.py
+++ b/exts/qrz.py
@@ -9,6 +9,7 @@ the GNU General Public License, version 2.
from io import BytesIO
+import re
import aiohttp
from lxml import etree
@@ -31,6 +32,14 @@ class QRZCog(commands.Cog):
"""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]
+ if not re.match('[A-Z0-9]+$', callsign, re.IGNORECASE):
+ embed = cmn.embed_factory(ctx)
+ embed.title = "QRZ Data for Callsign"
+ embed.colour = cmn.colours.bad
+ embed.description = "Not a valid callsign!"
+ await ctx.send(embed=embed)
+ return
+
if keys.qrz_user == "" or keys.qrz_pass == "" or "--link" in flags:
await ctx.send(f"http://qrz.com/db/{callsign}")
return
From 483a0bad195e3379f54d1f0b8d6e747194eefe78 Mon Sep 17 00:00:00 2001
From: Fran Rogers
Date: Fri, 29 Jan 2021 23:43:30 -0500
Subject: [PATCH 6/9] Use str.isalnum and str.isdecimal instead of re.match for
?ae7q and ?qrz input validation
---
exts/ae7q.py | 12 +++++-------
exts/qrz.py | 3 +--
2 files changed, 6 insertions(+), 9 deletions(-)
diff --git a/exts/ae7q.py b/exts/ae7q.py
index eacad7e..8cf76c7 100644
--- a/exts/ae7q.py
+++ b/exts/ae7q.py
@@ -16,8 +16,6 @@ the GNU General Public License, version 2.
# KC4USA: reserved, no call history, *but* has application history
-import re
-
import aiohttp
from bs4 import BeautifulSoup
@@ -46,7 +44,7 @@ class AE7QCog(commands.Cog):
base_url = "http://ae7q.com/query/data/CallHistory.php?CALL="
embed = cmn.embed_factory(ctx)
- if not re.match('[A-Z0-9]+$', callsign):
+ if not callsign.isalnum():
embed = cmn.embed_factory(ctx)
embed.title = "AE7Q History for Callsign"
embed.colour = cmn.colours.bad
@@ -120,7 +118,7 @@ class AE7QCog(commands.Cog):
base_url = "http://ae7q.com/query/data/CallHistory.php?CALL="
embed = cmn.embed_factory(ctx)
- if not re.match('[A-Z0-9]+$', callsign):
+ if not callsign.isalnum():
embed = cmn.embed_factory(ctx)
embed.title = "AE7Q Trustee History for Callsign"
embed.colour = cmn.colours.bad
@@ -196,7 +194,7 @@ class AE7QCog(commands.Cog):
base_url = "http://ae7q.com/query/data/CallHistory.php?CALL="
embed = cmn.embed_factory(ctx)
- if not re.match('[A-Z0-9]+$', callsign):
+ if not callsign.isalnum():
embed = cmn.embed_factory(ctx)
embed.title = "AE7Q Application History for Callsign"
embed.colour = cmn.colours.bad
@@ -276,7 +274,7 @@ class AE7QCog(commands.Cog):
base_url = "http://ae7q.com/query/data/FrnHistory.php?FRN="
embed = cmn.embed_factory(ctx)
- if not re.match('[0-9]+$', frn):
+ if not frn.isdecimal():
embed = cmn.embed_factory(ctx)
embed.title = "AE7Q History for FRN"
embed.colour = cmn.colours.bad
@@ -347,7 +345,7 @@ class AE7QCog(commands.Cog):
base_url = "http://ae7q.com/query/data/LicenseeIdHistory.php?ID="
embed = cmn.embed_factory(ctx)
- if not re.match('[A-Z][0-9]+$', licensee_id, re.IGNORECASE):
+ if not licensee_id.isalnum():
embed = cmn.embed_factory(ctx)
embed.title = "AE7Q History for Licensee"
embed.colour = cmn.colours.bad
diff --git a/exts/qrz.py b/exts/qrz.py
index 776e0fa..169635e 100644
--- a/exts/qrz.py
+++ b/exts/qrz.py
@@ -9,7 +9,6 @@ the GNU General Public License, version 2.
from io import BytesIO
-import re
import aiohttp
from lxml import etree
@@ -32,7 +31,7 @@ class QRZCog(commands.Cog):
"""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]
- if not re.match('[A-Z0-9]+$', callsign, re.IGNORECASE):
+ if not callsign.isalnum():
embed = cmn.embed_factory(ctx)
embed.title = "QRZ Data for Callsign"
embed.colour = cmn.colours.bad
From d7e544edcd1321d5bc2883856b527c4ab0f444c8 Mon Sep 17 00:00:00 2001
From: Abigail G
Date: Sun, 28 Feb 2021 01:51:19 -0500
Subject: [PATCH 7/9] hamstudy improvements - added orange colour for timeouts
- added emojis in the last field - made the correct answer bold after
answering/timeout - minor code optimisations
---
common.py | 3 +++
exts/study.py | 50 +++++++++++++++++++++++++++++---------------------
2 files changed, 32 insertions(+), 21 deletions(-)
diff --git a/common.py b/common.py
index af0e9f9..47caf11 100644
--- a/common.py
+++ b/common.py
@@ -36,6 +36,7 @@ colours = SimpleNamespace(
good=0x43B581,
neutral=0x7289DA,
bad=0xF04747,
+ timeout=0xF26522,
)
# meow
@@ -56,10 +57,12 @@ emojis = SimpleNamespace(
question="❓",
no_entry="⛔",
bangbang="‼️",
+ clock="⏱",
a="🇦",
b="🇧",
c="🇨",
d="🇩",
+ e="🇪",
)
paths = SimpleNamespace(
diff --git a/exts/study.py b/exts/study.py
index d4fc5dc..1739267 100644
--- a/exts/study.py
+++ b/exts/study.py
@@ -22,7 +22,7 @@ from resources import study
class StudyCog(commands.Cog):
- choices = {cmn.emojis.a: "A", cmn.emojis.b: "B", cmn.emojis.c: "C", cmn.emojis.d: "D"}
+ choices = {"A": cmn.emojis.a, "B": cmn.emojis.b, "C": cmn.emojis.c, "D": cmn.emojis.d, "E": cmn.emojis.e}
def __init__(self, bot: commands.Bot):
self.bot = bot
@@ -131,18 +131,22 @@ class StudyCog(commands.Cog):
pool_section = random.choice(pool)["sections"]
pool_questions = random.choice(pool_section)["questions"]
question = random.choice(pool_questions)
+ answers = question['answers']
+ answers_str = ""
+ answers_str_bolded = ""
+ for letter, ans in answers.items():
+ answers_str += f"{self.choices[letter]} {ans}\n"
+ if letter == question["answer"]:
+ answers_str_bolded += f"{self.choices[letter]} **{ans}**\n"
+ else:
+ answers_str_bolded += f"{self.choices[letter]} {ans}\n"
embed.title = f"{study.pool_emojis[country]} {pool_meta['class']} {question['id']}"
embed.description = self.source
embed.add_field(name="Question:", value=question["text"], inline=False)
- embed.add_field(name="Answers:",
- value=(f"**{cmn.emojis.a}** {question['answers']['A']}"
- f"\n**{cmn.emojis.b}** {question['answers']['B']}"
- f"\n**{cmn.emojis.c}** {question['answers']['C']}"
- f"\n**{cmn.emojis.d}** {question['answers']['D']}"),
- inline=False)
+ embed.add_field(name="Answers:", value=answers_str, inline=False)
embed.add_field(name="To Answer:",
- value=("Answer with reactions below. If not answered within 10 minutes,"
+ value=("Answer with reactions below. If not answered within 5 minutes,"
" the answer will be revealed."),
inline=False)
if "image" in question:
@@ -151,31 +155,35 @@ class StudyCog(commands.Cog):
q_msg = await ctx.send(embed=embed)
- await cmn.add_react(q_msg, cmn.emojis.a)
- await cmn.add_react(q_msg, cmn.emojis.b)
- await cmn.add_react(q_msg, cmn.emojis.c)
- await cmn.add_react(q_msg, cmn.emojis.d)
+ for i in range(len(answers)):
+ await cmn.add_react(q_msg, list(self.choices.values())[i])
def check(reaction, user):
return (user.id != self.bot.user.id
and reaction.message.id == q_msg.id
- and str(reaction.emoji) in self.choices.keys())
+ and str(reaction.emoji) in self.choices.values())
try:
- reaction, user = await self.bot.wait_for("reaction_add", timeout=600.0, check=check)
+ reaction, _ = await self.bot.wait_for("reaction_add", timeout=300.0, check=check)
except asyncio.TimeoutError:
- embed.remove_field(2)
- embed.add_field(name="Answer:", value=f"Timed out! The correct answer was **{question['answer']}**.")
+ embed.set_field_at(1, name="Answers:", value=answers_str_bolded, inline=False)
+ embed.set_field_at(2, name="Answer:",
+ value=(f"{cmn.emojis.clock} "
+ f"**Timed out!** The correct answer was {self.choices[question['answer']]}"))
+ embed.colour = cmn.colours.timeout
await q_msg.edit(embed=embed)
else:
- if self.choices[str(reaction.emoji)] == question["answer"]:
- embed.remove_field(2)
- embed.add_field(name="Answer:", value=f"Correct! The answer was **{question['answer']}**.")
+ if self.choices[question["answer"]] == str(reaction.emoji):
+ embed.set_field_at(1, name="Answers:", value=answers_str_bolded, inline=False)
+ embed.set_field_at(2, name="Answer:", value=(f"{cmn.emojis.check_mark} "
+ f"**Correct!** The answer was {reaction.emoji}"))
embed.colour = cmn.colours.good
await q_msg.edit(embed=embed)
else:
- embed.remove_field(2)
- embed.add_field(name="Answer:", value=f"Incorrect! The correct answer was **{question['answer']}**.")
+ embed.set_field_at(1, name="Answers:", value=answers_str_bolded, inline=False)
+ embed.set_field_at(2, name="Answer:",
+ value=(f"{cmn.emojis.x} **Incorrect!** The correct answer was "
+ f"{self.choices[question['answer']]}, not {reaction.emoji}"))
embed.colour = cmn.colours.bad
await q_msg.edit(embed=embed)
From 4d64d22ec6f1a3f37671a13fac9c3d8247ca8e55 Mon Sep 17 00:00:00 2001
From: Abigail G
Date: Sat, 6 Mar 2021 21:16:00 -0500
Subject: [PATCH 8/9] add emojis, question mark, and changelog
fixes #347
---
CHANGELOG.md | 2 ++
exts/study.py | 61 ++++++++++++++++++++++++++++++++++++---------------
2 files changed, 45 insertions(+), 18 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index dd32f06..b40efdf 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,11 +9,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- MUF and foF2 maps from [prop.kc2g.com](https://prop.kc2g.com/).
- Commands to show METAR (`?metar`) and TAF (`?taf`) (aeronautical weather conditions).
- The ability to select an element of a pool in `?hamstudy`.
+- The ability to answer ❓ to a HamStudy question to get the answer.
### Changed
- New colour theme for `?greyline`.
- Moved great circle distance and bearing calculation from `?ungrid` to `?griddistance`.
- `?ungrid` to `?latlong`.
- Renamed `?cond` to `?solar`.
+- Reduced `?hamstudy` timeout to 5 minutes.
### Fixed
- Weird image caching situation for `?greyline` on Discord's side.
### Deprecated
diff --git a/exts/study.py b/exts/study.py
index 1739267..530d6db 100644
--- a/exts/study.py
+++ b/exts/study.py
@@ -23,6 +23,7 @@ from resources import study
class StudyCog(commands.Cog):
choices = {"A": cmn.emojis.a, "B": cmn.emojis.b, "C": cmn.emojis.c, "D": cmn.emojis.d, "E": cmn.emojis.e}
+ choices_inv = {y: x for x, y in choices.items()}
def __init__(self, bot: commands.Bot):
self.bot = bot
@@ -143,9 +144,9 @@ class StudyCog(commands.Cog):
embed.title = f"{study.pool_emojis[country]} {pool_meta['class']} {question['id']}"
embed.description = self.source
- embed.add_field(name="Question:", value=question["text"], inline=False)
- embed.add_field(name="Answers:", value=answers_str, inline=False)
- embed.add_field(name="To Answer:",
+ embed.add_field(name="Question", value=question["text"], inline=False)
+ embed.add_field(name="Answers", value=answers_str, inline=False)
+ embed.add_field(name="To Answer",
value=("Answer with reactions below. If not answered within 5 minutes,"
" the answer will be revealed."),
inline=False)
@@ -157,35 +158,59 @@ class StudyCog(commands.Cog):
for i in range(len(answers)):
await cmn.add_react(q_msg, list(self.choices.values())[i])
+ await cmn.add_react(q_msg, cmn.emojis.question)
def check(reaction, user):
return (user.id != self.bot.user.id
and reaction.message.id == q_msg.id
- and str(reaction.emoji) in self.choices.values())
+ and (str(reaction.emoji) in self.choices.values() or str(reaction.emoji) == cmn.emojis.question))
try:
- reaction, _ = await self.bot.wait_for("reaction_add", timeout=300.0, check=check)
+ reaction, user = await self.bot.wait_for("reaction_add", timeout=300.0, check=check)
except asyncio.TimeoutError:
- embed.set_field_at(1, name="Answers:", value=answers_str_bolded, inline=False)
- embed.set_field_at(2, name="Answer:",
+ embed.set_field_at(1, name="Answers", value=answers_str_bolded, inline=False)
+ embed.set_field_at(2, name="Answer",
value=(f"{cmn.emojis.clock} "
f"**Timed out!** The correct answer was {self.choices[question['answer']]}"))
embed.colour = cmn.colours.timeout
await q_msg.edit(embed=embed)
else:
- if self.choices[question["answer"]] == str(reaction.emoji):
- embed.set_field_at(1, name="Answers:", value=answers_str_bolded, inline=False)
- embed.set_field_at(2, name="Answer:", value=(f"{cmn.emojis.check_mark} "
- f"**Correct!** The answer was {reaction.emoji}"))
- embed.colour = cmn.colours.good
+ if str(reaction.emoji) == cmn.emojis.question:
+ embed.set_field_at(1, name="Answers", value=answers_str_bolded, inline=False)
+ embed.set_field_at(2, name="Answer",
+ value=f"The correct answer was {self.choices[question['answer']]}", inline=False)
+ embed.add_field(name="Answer Requested By", value=str(user), inline=False)
+ embed.colour = cmn.colours.timeout
await q_msg.edit(embed=embed)
else:
- embed.set_field_at(1, name="Answers:", value=answers_str_bolded, inline=False)
- embed.set_field_at(2, name="Answer:",
- value=(f"{cmn.emojis.x} **Incorrect!** The correct answer was "
- f"{self.choices[question['answer']]}, not {reaction.emoji}"))
- embed.colour = cmn.colours.bad
- await q_msg.edit(embed=embed)
+ answers_str_checked = ""
+ chosen_ans = self.choices_inv[str(reaction.emoji)]
+ for letter, ans in answers.items():
+ answers_str_checked += f"{self.choices[letter]}"
+ if letter == question["answer"] == chosen_ans:
+ answers_str_checked += f"{cmn.emojis.check_mark} **{ans}**\n"
+ elif letter == question["answer"]:
+ answers_str_checked += f" **{ans}**\n"
+ elif letter == chosen_ans:
+ answers_str_checked += f"{cmn.emojis.x} {ans}\n"
+ else:
+ answers_str_checked += f" {ans}\n"
+
+ if self.choices[question["answer"]] == str(reaction.emoji):
+ embed.set_field_at(1, name="Answers", value=answers_str_checked, inline=False)
+ embed.set_field_at(2, name="Answer", value=(f"{cmn.emojis.check_mark} "
+ f"**Correct!** The answer was {reaction.emoji}"))
+ embed.add_field(name="Answered By", value=str(user), inline=False)
+ embed.colour = cmn.colours.good
+ await q_msg.edit(embed=embed)
+ else:
+ embed.set_field_at(1, name="Answers", value=answers_str_checked, inline=False)
+ embed.set_field_at(2, name="Answer",
+ value=(f"{cmn.emojis.x} **Incorrect!** The correct answer was "
+ f"{self.choices[question['answer']]}, not {reaction.emoji}"))
+ embed.add_field(name="Answered By", value=str(user), inline=False)
+ embed.colour = cmn.colours.bad
+ await q_msg.edit(embed=embed)
async def hamstudy_get_pools(self):
async with self.session.get("https://hamstudy.org/pools/") as resp:
From be6c78f4deb98780c4b39f44241a15e13d39067a Mon Sep 17 00:00:00 2001
From: Abigail G
Date: Sat, 6 Mar 2021 23:55:38 -0500
Subject: [PATCH 9/9] fix issue with pushing docker images to ghcr
---
.github/workflows/docker.yml | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index 6ee0915..53ff245 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -57,7 +57,9 @@ jobs:
[[ "$VERSION" != "dev" ]] && docker tag $IMAGE_NAME $IMAGE_ID:latest || true
- name: Push images to registry
- run: docker push ${{ steps.tag_image.outputs.image_id }}
+ run: |
+ [[ "${{ steps.tag_image.outputs.version }}" != "dev" ]] && docker push ${{ steps.tag_image.outputs.image_id }}:latest || true
+ docker push ${{ steps.tag_image.outputs.image_id }}:${{ steps.tag_image.outputs.version }}
- name: Deploy official images
id: deploy_images