Merge pull request #177 from classabbyamp/hamstudy-refactor

Hamstudy refactor
This commit is contained in:
Abigail Gold 2020-01-21 22:26:49 -05:00 committed by GitHub
commit 9113638b67
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 186 additions and 62 deletions

View File

@ -12,6 +12,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
### Changed ### Changed
- Changelog command to accept a version as argument. - Changelog command to accept a version as argument.
- The qrz command can now link to a QRZ page instead of embedding the data with the `--link` flag. - The qrz command can now link to a QRZ page instead of embedding the data with the `--link` flag.
- All currently-available pools can now be accessed by the `hamstudy` command.
- The `hamstudy` command now uses the syntax `?hamstudy <country> <pool>`.
- Replaced `hamstudyanswer` command with answering by reaction.
### Fixed ### Fixed
- Fixed ditto marks (") appearing in the ae7q call command. - Fixed ditto marks (") appearing in the ae7q call command.
- Fixed issue where incorrect table was parsed in ae7q call command. - Fixed issue where incorrect table was parsed in ae7q call command.

View File

@ -44,7 +44,11 @@ emojis = SimpleNamespace(check_mark='✅',
warning='⚠️', warning='⚠️',
question='', question='',
no_entry='', no_entry='',
bangbang='‼️') bangbang='‼️',
a='🇦',
b='🇧',
c='🇨',
d='🇩')
paths = SimpleNamespace(data=Path("./data/"), paths = SimpleNamespace(data=Path("./data/"),
resources=Path("./resources/"), resources=Path("./resources/"),

View File

@ -9,15 +9,20 @@ General Public License, version 2.
import random import random
import json import json
from datetime import datetime
import asyncio
import aiohttp import aiohttp
import discord.ext.commands as commands import discord.ext.commands as commands
import common as cmn import common as cmn
from resources import study
class StudyCog(commands.Cog): class StudyCog(commands.Cog):
choices = {cmn.emojis.a: 'A', cmn.emojis.b: 'B', cmn.emojis.c: 'C', cmn.emojis.d: 'D'}
def __init__(self, bot: commands.Bot): def __init__(self, bot: commands.Bot):
self.bot = bot self.bot = bot
self.lastq = dict() self.lastq = dict()
@ -25,41 +30,85 @@ class StudyCog(commands.Cog):
self.session = aiohttp.ClientSession(connector=bot.qrm.connector) 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, country: str = '', level: str = ''):
'''Gets a random question from the Technician, General, and/or Extra question pools.''' '''Gets a random question from [HamStudy's](https://hamstudy.org) question pools.'''
tech_pool = 'E2_2018'
gen_pool = 'E3_2019'
extra_pool = 'E4_2016'
embed = cmn.embed_factory(ctx)
with ctx.typing(): with ctx.typing():
selected_pool = None embed = cmn.embed_factory(ctx)
try:
level = level.lower()
except AttributeError: # no level given (it's None)
pass
if level in ['t', 'technician', 'tech']: country = country.lower()
selected_pool = tech_pool level = level.lower()
if level in ['g', 'gen', 'general']: if country in study.pool_names.keys():
selected_pool = gen_pool if level in study.pool_names[country].keys():
pool_name = study.pool_names[country][level]
if level in ['e', 'ae', 'extra']: elif level in ("random", "r"):
selected_pool = extra_pool # select a random level in that country
pool_name = random.choice(list(study.pool_names[country].values()))
if (level is None) or (level == 'all'): # no pool given or user wants all, so pick a random pool else:
selected_pool = random.choice([tech_pool, gen_pool, extra_pool]) # show list of possible pools
if (level is not None) and (selected_pool is None): # unrecognized pool given by user embed.title = "Pool Not Found!"
embed.title = 'Error in HamStudy command' embed.description = "Possible arguments are:"
embed.description = ('The question pool you gave was unrecognized. ' embed.colour = cmn.colours.bad
'There are many ways to call up certain question pools - try ?rq t, g, or e. ' for cty in study.pool_names:
'\n\nNote that currently only the US question pools are available.') levels = '`, `'.join(study.pool_names[cty].keys())
embed.add_field(name=f"**Country: `{cty}` {study.pool_emojis[cty]}**",
value=f"Levels: `{levels}`", inline=False)
embed.add_field(name="**Random**", value="To select a random pool or country, use `random` or `r`")
await ctx.send(embed=embed)
return
elif country in ("random", "r"):
# select a random country and level
country = random.choice(list(study.pool_names.keys()))
pool_name = random.choice(list(study.pool_names[country].values()))
else:
# show list of possible pools
embed.title = "Pool Not Found!"
embed.description = "Possible arguments are:"
embed.colour = cmn.colours.bad embed.colour = cmn.colours.bad
for cty in study.pool_names:
levels = '`, `'.join(study.pool_names[cty].keys())
embed.add_field(name=f"**Country: `{cty}` {study.pool_emojis[cty]}**",
value=f"Levels: `{levels}`", inline=False)
embed.add_field(name="**Random**", value="To select a random pool or country, use `random` or `r`")
await ctx.send(embed=embed) await ctx.send(embed=embed)
return return
async with self.session.get(f'https://hamstudy.org/pools/{selected_pool}') as resp: pools = await self.hamstudy_get_pools()
pool_matches = [p for p in pools.keys() if "_".join(p.split("_")[:-1]) == pool_name]
if len(pool_matches) > 0:
if len(pool_matches) == 1:
pool = pool_matches[0]
else:
# look at valid_from and expires dates to find the correct one
for p in pool_matches:
valid_from = datetime.fromisoformat(pools[p]["valid_from"][:-1] + "+00:00")
expires = datetime.fromisoformat(pools[p]["expires"][:-1] + "+00:00")
if valid_from < datetime.utcnow() < expires:
pool = p
break
else:
# show list of possible pools
embed.title = "Pool Not Found!"
embed.description = "Possible arguments are:"
embed.colour = cmn.colours.bad
for cty in study.pool_names:
levels = '`, `'.join(study.pool_names[cty].keys())
embed.add_field(name=f"**Country: `{cty}` {study.pool_emojis[cty]}**",
value=f"Levels: `{levels}`", inline=False)
embed.add_field(name="**Random**", value="To select a random pool or country, use `random` or `r`")
await ctx.send(embed=embed)
return
pool_meta = pools[pool]
async with self.session.get(f'https://hamstudy.org/pools/{pool}') as resp:
if resp.status != 200: if resp.status != 200:
embed.title = 'Error in HamStudy command' embed.title = 'Error in HamStudy command'
embed.description = 'Could not load questions' embed.description = 'Could not load questions'
@ -73,47 +122,66 @@ class StudyCog(commands.Cog):
pool_questions = random.choice(pool_section)['questions'] pool_questions = random.choice(pool_section)['questions']
question = random.choice(pool_questions) question = random.choice(pool_questions)
embed.title = question['id'] embed.title = f"{study.pool_emojis[country]} {pool_meta['class']} {question['id']}"
embed.description = self.source embed.description = self.source
embed.colour = cmn.colours.good
embed.add_field(name='Question:', value=question['text'], inline=False) embed.add_field(name='Question:', value=question['text'], inline=False)
embed.add_field(name='Answers:', value='**A:** ' + question['answers']['A'] embed.add_field(name='Answers:',
+ '\n**B:** ' + question['answers']['B'] value=(f"**{cmn.emojis.a}** {question['answers']['A']}"
+ '\n**C:** ' + question['answers']['C'] f"\n**{cmn.emojis.b}** {question['answers']['B']}"
+ '\n**D:** ' + question['answers']['D'], inline=False) f"\n**{cmn.emojis.c}** {question['answers']['C']}"
embed.add_field(name='Answer:', value='Type _?rqa_ for answer', inline=False) f"\n**{cmn.emojis.d}** {question['answers']['D']}"),
inline=False)
embed.add_field(name='To Answer:',
value=('Answer with reactions below. If not answered within 10 minutes,'
' the answer will be revealed.'),
inline=False)
if 'image' in question: if 'image' in question:
image_url = f'https://hamstudy.org/_1330011/images/{selected_pool.split("_",1)[1]}/{question["image"]}' image_url = f'https://hamstudy.org/_1330011/images/{pool.split("_",1)[1]}/{question["image"]}'
embed.set_image(url=image_url) embed.set_image(url=image_url)
self.lastq[ctx.message.channel.id] = (question['id'], question['answer'])
await ctx.send(embed=embed)
@commands.command(name="hamstudyanswer", aliases=['rqa', 'randomquestionanswer', 'randomqa', 'hamstudya'], q_msg = await ctx.send(embed=embed)
category=cmn.cat.study)
async def _q_answer(self, ctx: commands.Context, answer: str = None): await cmn.add_react(q_msg, cmn.emojis.a)
'''Returns the answer to question last asked (Optional argument: your answer).''' await cmn.add_react(q_msg, cmn.emojis.b)
with ctx.typing(): await cmn.add_react(q_msg, cmn.emojis.c)
correct_ans = self.lastq[ctx.message.channel.id][1] await cmn.add_react(q_msg, cmn.emojis.d)
q_num = self.lastq[ctx.message.channel.id][0]
embed = cmn.embed_factory(ctx) def check(reaction, user):
if answer is not None: return (user.id != self.bot.user.id
answer = answer.upper() and reaction.message.id == q_msg.id
if answer == correct_ans: and str(reaction.emoji) in self.choices.keys())
result = f'Correct! The answer to {q_num} was **{correct_ans}**.'
embed.title = f'{q_num} Answer' try:
embed.description = f'{self.source}\n\n{result}' reaction, user = await self.bot.wait_for('reaction_add', timeout=600.0, check=check)
embed.colour = cmn.colours.good except asyncio.TimeoutError:
else: embed.remove_field(2)
result = f'Incorrect. The answer to {q_num} was **{correct_ans}**, not **{answer}**.' embed.add_field(name="Answer:", value=f"Timed out! The correct answer was **{question['answer']}**.")
embed.title = f'{q_num} Answer' await q_msg.edit(embed=embed)
embed.description = f'{self.source}\n\n{result}' else:
embed.colour = cmn.colours.bad 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']}**.")
embed.colour = cmn.colours.good
await q_msg.edit(embed=embed)
else: else:
result = f'The correct answer to {q_num} was **{correct_ans}**.' embed.remove_field(2)
embed.title = f'{q_num} Answer' embed.add_field(name="Answer:", value=f"Incorrect! The correct answer was **{question['answer']}**.")
embed.description = f'{self.source}\n\n{result}' embed.colour = cmn.colours.bad
embed.colour = cmn.colours.neutral await q_msg.edit(embed=embed)
await ctx.send(embed=embed)
async def hamstudy_get_pools(self):
async with self.session.get('https://hamstudy.org/pools/') as resp:
if resp.status != 200:
raise ConnectionError
else:
pools_dict = json.loads(await resp.read())
pools = dict()
for ls in pools_dict.values():
for pool in ls:
pools[pool["id"]] = pool
return pools
def setup(bot: commands.Bot): def setup(bot: commands.Bot):

49
resources/study.py Normal file
View File

@ -0,0 +1,49 @@
"""
A listing of hamstudy command resources
---
Copyright (C) 2019-2020 Abigail Gold, 0x5c
This file is part of discord-qrmbot and is released under the terms of the GNU
General Public License, version 2.
"""
pool_names = {'us': {'technician': 'E2',
'tech': 'E2',
't': 'E2',
'general': 'E3',
'gen': 'E3',
'g': 'E3',
'extra': 'E4',
'e': 'E4'},
'ca': {'basic': 'CA_B',
'b': 'CA_B',
'advanced': 'CA_A',
'adv': 'CA_A',
'a': 'CA_A',
'basic_fr': 'CA_FB',
'b_fr': 'CA_FB',
'base': 'CA_FB',
'advanced_fr': 'CA_FS',
'adv_fr': 'CA_FS',
'a_fr': 'CA_FS',
'supérieure': 'CA_FS',
'superieure': 'CA_FS',
's': 'CA_FS'},
'us_c': {'c1': 'C1',
'comm1': 'C1',
'c3': 'C3',
'comm3': 'C3',
'c6': 'C6',
'comm6': 'C6',
'c7': 'C7',
'comm7': 'C7',
'c7r': 'C7R',
'comm7r': 'C7R',
'c8': 'C8',
'comm8': 'C8',
'c9': 'C9',
'comm9': 'C9'}}
pool_emojis = {'us': '🇺🇸',
'ca': '🇨🇦',
'us_c': '🇺🇸 🏢'}