Merge pull request #149 from bobloy/audiotrivia_update

Audiotrivia updates from lessons learned attempting core
decrypt_initial
bobloy 4 years ago committed by GitHub
commit 54be5addb5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -2,9 +2,8 @@
import asyncio import asyncio
import logging import logging
import lavalink
from lavalink.enums import LoadType
from redbot.cogs.trivia import TriviaSession from redbot.cogs.trivia import TriviaSession
from redbot.cogs.trivia.session import _parse_answers
from redbot.core.utils.chat_formatting import bold from redbot.core.utils.chat_formatting import bold
log = logging.getLogger("red.fox_v3.audiotrivia.audiosession") log = logging.getLogger("red.fox_v3.audiotrivia.audiosession")
@ -13,14 +12,14 @@ log = logging.getLogger("red.fox_v3.audiotrivia.audiosession")
class AudioSession(TriviaSession): class AudioSession(TriviaSession):
"""Class to run a session of audio trivia""" """Class to run a session of audio trivia"""
def __init__(self, ctx, question_list: dict, settings: dict, player: lavalink.Player): def __init__(self, ctx, question_list: dict, settings: dict, audio=None):
super().__init__(ctx, question_list, settings) super().__init__(ctx, question_list, settings)
self.player = player self.audio = audio
@classmethod @classmethod
def start(cls, ctx, question_list, settings, player: lavalink.Player = None): def start(cls, ctx, question_list, settings, audio=None):
session = cls(ctx, question_list, settings, player) session = cls(ctx, question_list, settings, audio)
loop = ctx.bot.loop loop = ctx.bot.loop
session._task = loop.create_task(session.run()) session._task = loop.create_task(session.run())
return session return session
@ -34,57 +33,89 @@ class AudioSession(TriviaSession):
await self._send_startup_msg() await self._send_startup_msg()
max_score = self.settings["max_score"] max_score = self.settings["max_score"]
delay = self.settings["delay"] delay = self.settings["delay"]
audio_delay = self.settings["audio_delay"]
timeout = self.settings["timeout"] timeout = self.settings["timeout"]
for question, answers in self._iter_questions(): if self.audio is not None:
import lavalink
player = lavalink.get_player(self.ctx.guild.id)
player.store("channel", self.ctx.channel.id) # What's this for? I dunno
await self.audio.set_player_settings(self.ctx)
else:
lavalink = None
player = False
for question, answers, audio_url in self._iter_questions():
async with self.ctx.typing(): async with self.ctx.typing():
await asyncio.sleep(3) await asyncio.sleep(3)
self.count += 1 self.count += 1
await self.player.stop() msg = bold(f"Question number {self.count}!") + f"\n\n{question}"
if player:
msg = bold(f"Question number {self.count}!") + "\n\nName this audio!" await player.stop()
if audio_url:
if not player:
log.debug("Got an audio question in a non-audio trivia session")
continue
load_result = await player.load_tracks(audio_url)
if (
load_result.has_error
or load_result.load_type != lavalink.enums.LoadType.TRACK_LOADED
):
await self.ctx.maybe_send_embed(
"Audio Track has an error, skipping. See logs for details"
)
log.info(f"Track has error: {load_result.exception_message}")
continue
tracks = load_result.tracks
track = tracks[0]
seconds = track.length / 1000
track.uri = "" # Hide the info from `now`
if self.settings["repeat"] and seconds < audio_delay:
# Append it until it's longer than the delay
tot_length = seconds + 0
while tot_length < audio_delay:
player.add(self.ctx.author, track)
tot_length += seconds
else:
player.add(self.ctx.author, track)
if not player.current:
await player.play()
await self.ctx.maybe_send_embed(msg) await self.ctx.maybe_send_embed(msg)
log.debug(f"Audio question: {question}") log.debug(f"Audio question: {question}")
# print("Audio question: {}".format(question))
# await self.ctx.invoke(self.audio.play(ctx=self.ctx, query=question))
# ctx_copy = copy(self.ctx)
# await self.ctx.invoke(self.player.play, query=question)
query = question.strip("<>")
load_result = await self.player.load_tracks(query)
log.debug(f"{load_result.load_type=}")
if load_result.has_error or load_result.load_type != LoadType.TRACK_LOADED:
await self.ctx.maybe_send_embed(f"Track has error, skipping. See logs for details")
log.info(f"Track has error: {load_result.exception_message}")
continue # Skip tracks with error
tracks = load_result.tracks
track = tracks[0]
seconds = track.length / 1000
if self.settings["repeat"] and seconds < delay:
# Append it until it's longer than the delay
tot_length = seconds + 0
while tot_length < delay:
self.player.add(self.ctx.author, track)
tot_length += seconds
else:
self.player.add(self.ctx.author, track)
if not self.player.current:
log.debug("Pressing play")
await self.player.play()
continue_ = await self.wait_for_answer(answers, delay, timeout) continue_ = await self.wait_for_answer(
answers, audio_delay if audio_url else delay, timeout
)
if continue_ is False: if continue_ is False:
break break
if any(score >= max_score for score in self.scores.values()): if any(score >= max_score for score in self.scores.values()):
await self.end_game() await self.end_game()
break break
else: else:
await self.ctx.send("There are no more questions!") await self.ctx.maybe_send_embed("There are no more questions!")
await self.end_game() await self.end_game()
async def end_game(self): async def end_game(self):
await super().end_game() await super().end_game()
await self.player.disconnect() if self.audio is not None:
await self.ctx.invoke(self.audio.command_disconnect)
def _iter_questions(self):
"""Iterate over questions and answers for this session.
Yields
------
`tuple`
A tuple containing the question (`str`) and the answers (`tuple` of
`str`).
"""
for question, q_data in self.question_list:
answers = _parse_answers(q_data["answers"])
_audio = q_data["audio"]
if _audio:
yield _audio, answers, question.strip("<>")
else:
yield question, answers, _audio

@ -1,17 +1,17 @@
import datetime import datetime
import logging import logging
import pathlib import pathlib
from typing import List from typing import List, Optional
import discord
import lavalink import lavalink
import yaml import yaml
from redbot.cogs.audio import Audio from redbot.cogs.audio import Audio
from redbot.cogs.trivia import LOG from redbot.cogs.trivia.trivia import InvalidListError, Trivia, get_core_lists
from redbot.cogs.trivia.trivia import InvalidListError, Trivia
from redbot.core import Config, checks, commands from redbot.core import Config, checks, commands
from redbot.core.bot import Red from redbot.core.bot import Red
from redbot.core.data_manager import cog_data_path from redbot.core.data_manager import cog_data_path
from redbot.core.utils.chat_formatting import box from redbot.core.utils.chat_formatting import bold, box
from .audiosession import AudioSession from .audiosession import AudioSession
@ -28,12 +28,11 @@ class AudioTrivia(Trivia):
def __init__(self, bot: Red): def __init__(self, bot: Red):
super().__init__() super().__init__()
self.bot = bot self.bot = bot
self.audio = None
self.audioconf = Config.get_conf( self.audioconf = Config.get_conf(
self, identifier=651171001051118411410511810597, force_registration=True self, identifier=651171001051118411410511810597, force_registration=True
) )
self.audioconf.register_guild(delay=30.0, repeat=True) self.audioconf.register_guild(audio_delay=30.0, repeat=True)
@commands.group() @commands.group()
@commands.guild_only() @commands.guild_only()
@ -44,49 +43,42 @@ class AudioTrivia(Trivia):
settings_dict = await audioset.all() settings_dict = await audioset.all()
msg = box( msg = box(
"**Audio settings**\n" "**Audio settings**\n"
"Answer time limit: {delay} seconds\n" "Answer time limit: {audio_delay} seconds\n"
"Repeat Short Audio: {repeat}" "Repeat Short Audio: {repeat}"
"".format(**settings_dict), "".format(**settings_dict),
lang="py", lang="py",
) )
await ctx.send(msg) await ctx.send(msg)
@atriviaset.command(name="delay") @atriviaset.command(name="timelimit")
async def atriviaset_delay(self, ctx: commands.Context, seconds: float): async def atriviaset_timelimit(self, ctx: commands.Context, seconds: float):
"""Set the maximum seconds permitted to answer a question.""" """Set the maximum seconds permitted to answer a question."""
if seconds < 4.0: if seconds < 4.0:
await ctx.send("Must be at least 4 seconds.") await ctx.send("Must be at least 4 seconds.")
return return
settings = self.audioconf.guild(ctx.guild) settings = self.audioconf.guild(ctx.guild)
await settings.delay.set(seconds) await settings.audo_delay.set(seconds)
await ctx.send("Done. Maximum seconds to answer set to {}.".format(seconds)) await ctx.maybe_send_embed(f"Done. Maximum seconds to answer set to {seconds}.")
@atriviaset.command(name="repeat") @atriviaset.command(name="repeat")
async def atriviaset_repeat(self, ctx: commands.Context, true_or_false: bool): async def atriviaset_repeat(self, ctx: commands.Context, true_or_false: bool):
"""Set whether or not short audio will be repeated""" """Set whether or not short audio will be repeated"""
settings = self.audioconf.guild(ctx.guild) settings = self.audioconf.guild(ctx.guild)
await settings.repeat.set(true_or_false) await settings.repeat.set(true_or_false)
await ctx.send("Done. Repeating short audio is now set to {}.".format(true_or_false)) await ctx.maybe_send_embed(f"Done. Repeating short audio is now set to {true_or_false}.")
@commands.group(invoke_without_command=True) @commands.group(invoke_without_command=True)
@commands.guild_only() @commands.guild_only()
async def audiotrivia(self, ctx: commands.Context, *categories: str): async def audiotrivia(self, ctx: commands.Context, *categories: str):
"""Start trivia session on the specified category. """Start trivia session on the specified category or categories.
Includes Audio categories.
You may list multiple categories, in which case the trivia will involve You may list multiple categories, in which case the trivia will involve
questions from all of them. questions from all of them.
""" """
if not categories and ctx.invoked_subcommand is None: if not categories and ctx.invoked_subcommand is None:
await ctx.send_help() await ctx.send_help()
return return
if self.audio is None:
self.audio: Audio = self.bot.get_cog("Audio")
if self.audio is None:
await ctx.maybe_send_embed("Audio is not loaded. Load it and try again")
return
categories = [c.lower() for c in categories] categories = [c.lower() for c in categories]
session = self._get_trivia_session(ctx.channel) session = self._get_trivia_session(ctx.channel)
if session is not None: if session is not None:
@ -94,45 +86,9 @@ class AudioTrivia(Trivia):
"There is already an ongoing trivia session in this channel." "There is already an ongoing trivia session in this channel."
) )
return return
status = await self.audio.config.status()
notify = await self.audio.config.guild(ctx.guild).notify()
if status:
await ctx.maybe_send_embed(
f"It is recommended to disable audio status with `{ctx.prefix}audioset status`"
)
if notify:
await ctx.maybe_send_embed(
f"It is recommended to disable audio notify with `{ctx.prefix}audioset notify`"
)
if not self.audio._player_check(ctx):
try:
if not ctx.author.voice.channel.permissions_for(
ctx.me
).connect or self.audio.is_vc_full(ctx.author.voice.channel):
return await ctx.maybe_send_embed(
"I don't have permission to connect to your channel."
)
await lavalink.connect(ctx.author.voice.channel)
lavaplayer = lavalink.get_player(ctx.guild.id)
lavaplayer.store("connect", datetime.datetime.utcnow())
except AttributeError:
return await ctx.maybe_send_embed("Connect to a voice channel first.")
lavaplayer = lavalink.get_player(ctx.guild.id)
lavaplayer.store("channel", ctx.channel.id) # What's this for? I dunno
await self.audio.set_player_settings(ctx)
if not ctx.author.voice or ctx.author.voice.channel != lavaplayer.channel:
return await ctx.maybe_send_embed(
"You must be in the voice channel to use the audiotrivia command."
)
trivia_dict = {} trivia_dict = {}
authors = [] authors = []
any_audio = False
for category in reversed(categories): for category in reversed(categories):
# We reverse the categories so that the first list's config takes # We reverse the categories so that the first list's config takes
# priority over the others. # priority over the others.
@ -140,19 +96,22 @@ class AudioTrivia(Trivia):
dict_ = self.get_audio_list(category) dict_ = self.get_audio_list(category)
except FileNotFoundError: except FileNotFoundError:
await ctx.maybe_send_embed( await ctx.maybe_send_embed(
"Invalid category `{0}`. See `{1}audiotrivia list`" f"Invalid category `{category}`. See `{ctx.prefix}audiotrivia list`"
" for a list of trivia categories." " for a list of trivia categories."
"".format(category, ctx.prefix)
) )
except InvalidListError: except InvalidListError:
await ctx.maybe_send_embed( await ctx.maybe_send_embed(
"There was an error parsing the trivia list for" "There was an error parsing the trivia list for"
" the `{}` category. It may be formatted" f" the `{category}` category. It may be formatted"
" incorrectly.".format(category) " incorrectly."
) )
else: else:
trivia_dict.update(dict_) is_audio = dict_.pop("AUDIO", False)
authors.append(trivia_dict.pop("AUTHOR", None)) authors.append(dict_.pop("AUTHOR", None))
trivia_dict.update(
{_q: {"audio": is_audio, "answers": _a} for _q, _a in dict_.items()}
)
any_audio = any_audio or is_audio
continue continue
return return
if not trivia_dict: if not trivia_dict:
@ -161,9 +120,35 @@ class AudioTrivia(Trivia):
) )
return return
if not any_audio:
audio = None
else:
audio: Optional["Audio"] = self.bot.get_cog("Audio")
if audio is None:
await ctx.send("Audio lists were parsed but Audio is not loaded!")
return
status = await audio.config.status()
notify = await audio.config.guild(ctx.guild).notify()
if status:
await ctx.maybe_send_embed(
f"It is recommended to disable audio status with `{ctx.prefix}audioset status`"
)
if notify:
await ctx.maybe_send_embed(
f"It is recommended to disable audio notify with `{ctx.prefix}audioset notify`"
)
failed = await ctx.invoke(audio.command_summon)
if failed:
return
lavaplayer = lavalink.get_player(ctx.guild.id)
lavaplayer.store("channel", ctx.channel.id) # What's this for? I dunno
settings = await self.config.guild(ctx.guild).all() settings = await self.config.guild(ctx.guild).all()
audiosettings = await self.audioconf.guild(ctx.guild).all() audiosettings = await self.audioconf.guild(ctx.guild).all()
config = trivia_dict.pop("CONFIG", None) config = trivia_dict.pop("CONFIG", {"answer": None})["answer"]
if config and settings["allow_override"]: if config and settings["allow_override"]:
settings.update(config) settings.update(config)
settings["lists"] = dict(zip(categories, reversed(authors))) settings["lists"] = dict(zip(categories, reversed(authors)))
@ -171,25 +156,33 @@ class AudioTrivia(Trivia):
# Delay in audiosettings overwrites delay in settings # Delay in audiosettings overwrites delay in settings
combined_settings = {**settings, **audiosettings} combined_settings = {**settings, **audiosettings}
session = AudioSession.start( session = AudioSession.start(
ctx=ctx, ctx,
question_list=trivia_dict, trivia_dict,
settings=combined_settings, combined_settings,
player=lavaplayer, audio,
) )
self.trivia_sessions.append(session) self.trivia_sessions.append(session)
LOG.debug("New audio trivia session; #%s in %d", ctx.channel, ctx.guild.id) log.debug("New audio trivia session; #%s in %d", ctx.channel, ctx.guild.id)
@audiotrivia.command(name="list") @audiotrivia.command(name="list")
@commands.guild_only() @commands.guild_only()
async def audiotrivia_list(self, ctx: commands.Context): async def audiotrivia_list(self, ctx: commands.Context):
"""List available trivia categories.""" """List available trivia including audio categories."""
lists = set(p.stem for p in self._audio_lists()) lists = set(p.stem for p in self._all_audio_lists())
if await ctx.embed_requested():
msg = box("**Available trivia lists**\n\n{}".format(", ".join(sorted(lists)))) await ctx.send(
if len(msg) > 1000: embed=discord.Embed(
await ctx.author.send(msg) title="Available trivia lists",
return colour=await ctx.embed_colour(),
await ctx.send(msg) description=", ".join(sorted(lists)),
)
)
else:
msg = box(bold("Available trivia lists") + "\n\n" + ", ".join(sorted(lists)))
if len(msg) > 1000:
await ctx.author.send(msg)
else:
await ctx.send(msg)
def get_audio_list(self, category: str) -> dict: def get_audio_list(self, category: str) -> dict:
"""Get the audiotrivia list corresponding to the given category. """Get the audiotrivia list corresponding to the given category.
@ -206,7 +199,7 @@ class AudioTrivia(Trivia):
""" """
try: try:
path = next(p for p in self._audio_lists() if p.stem == category) path = next(p for p in self._all_audio_lists() if p.stem == category)
except StopIteration: except StopIteration:
raise FileNotFoundError("Could not find the `{}` category.".format(category)) raise FileNotFoundError("Could not find the `{}` category.".format(category))
@ -218,13 +211,15 @@ class AudioTrivia(Trivia):
else: else:
return dict_ return dict_
def _audio_lists(self) -> List[pathlib.Path]: def _all_audio_lists(self) -> List[pathlib.Path]:
# Custom trivia lists uploaded with audiotrivia. Not necessarily audio lists
personal_lists = [p.resolve() for p in cog_data_path(self).glob("*.yaml")] personal_lists = [p.resolve() for p in cog_data_path(self).glob("*.yaml")]
return personal_lists + get_core_lists() # Add to that custom lists uploaded with trivia and core lists
return personal_lists + get_core_audio_lists() + self._all_lists()
def get_core_lists() -> List[pathlib.Path]: def get_core_audio_lists() -> List[pathlib.Path]:
"""Return a list of paths for all trivia lists packaged with the bot.""" """Return a list of paths for all trivia lists packaged with the bot."""
core_lists_path = pathlib.Path(__file__).parent.resolve() / "data/lists" core_lists_path = pathlib.Path(__file__).parent.resolve() / "data/lists"
return list(core_lists_path.glob("*.yaml")) return list(core_lists_path.glob("*.yaml"))

@ -1,4 +1,5 @@
AUTHOR: Plab AUTHOR: Plab
AUDIO: "[Audio] Identify this Anime!"
https://www.youtube.com/watch?v=2uq34TeWEdQ: https://www.youtube.com/watch?v=2uq34TeWEdQ:
- 'Hagane no Renkinjutsushi (2009)' - 'Hagane no Renkinjutsushi (2009)'
- '(2009) الخيميائي المعدني الكامل' - '(2009) الخيميائي المعدني الكامل'

@ -1,4 +1,5 @@
AUTHOR: Lazar AUTHOR: Lazar
AUDIO: "[Audio] Identify this NHL Team by their goal horn"
https://youtu.be/6OejNXrGkK0: https://youtu.be/6OejNXrGkK0:
- Anaheim Ducks - Anaheim Ducks
- Anaheim - Anaheim

@ -1,4 +1,5 @@
AUTHOR: Bobloy AUTHOR: Bobloy
AUDIO: "[Audio] Identify this video game"
https://www.youtube.com/watch?v=GBPbJyxqHV0: https://www.youtube.com/watch?v=GBPbJyxqHV0:
- Super Mario 64 - Super Mario 64
https://www.youtube.com/watch?v=0jXTBAGv9ZQ: https://www.youtube.com/watch?v=0jXTBAGv9ZQ:
Loading…
Cancel
Save