diff --git a/royalpack/commands/__init__.py b/royalpack/commands/__init__.py index a0caa62f..cfec8e83 100644 --- a/royalpack/commands/__init__.py +++ b/royalpack/commands/__init__.py @@ -10,10 +10,10 @@ from .smecds import SmecdsCommand from .videochannel import VideochannelCommand # from .trivia import TriviaCommand # from .matchmaking import MatchmakingCommand -# from .pause import PauseCommand +from .pause import PauseCommand from .play import PlayCommand # from .playmode import PlaymodeCommand -# from .queue import QueueCommand +from .queue import QueueCommand from .skip import SkipCommand from .summon import SummonCommand from .youtube import YoutubeCommand @@ -40,10 +40,10 @@ available_commands = [ VideochannelCommand, # TriviaCommand, # MatchmakingCommand, - # PauseCommand, + PauseCommand, PlayCommand, # PlaymodeCommand, - # QueueCommand, + QueueCommand, SkipCommand, SummonCommand, YoutubeCommand, diff --git a/royalpack/commands/googlevideo.py b/royalpack/commands/googlevideo.py index f61bad56..45c5e30d 100644 --- a/royalpack/commands/googlevideo.py +++ b/royalpack/commands/googlevideo.py @@ -11,3 +11,5 @@ class GooglevideoCommand(PlayCommand): syntax = "{ricerca}" _URL_FORMAT = "gvsearch:{url}" + + # Too bad gvsearch: always finds nothing. diff --git a/royalpack/commands/pause.py b/royalpack/commands/pause.py index 14147e0e..d4f6fc6a 100644 --- a/royalpack/commands/pause.py +++ b/royalpack/commands/pause.py @@ -1,53 +1,27 @@ -import typing import discord +from typing import * from royalnet.commands import * -from royalnet.bots import DiscordBot class PauseCommand(Command): name: str = "pause" - description: str = "Mette in pausa o riprende la riproduzione della canzone attuale." + aliases = ["resume"] - syntax = "[ [guild] ]" - - @staticmethod - async def _legacy_pause_handler(bot: DiscordBot, guild_name: typing.Optional[str]): - # Find the matching guild - if guild_name: - guilds: typing.List[discord.Guild] = bot.client.find_guild_by_name(guild_name) - else: - guilds = bot.client.guilds - if len(guilds) == 0: - raise CommandError("No guilds with the specified name found.") - if len(guilds) > 1: - raise CommandError("Multiple guilds with the specified name found.") - guild = list(bot.client.guilds)[0] - # Set the currently playing source as ended - voice_client: discord.VoiceClient = bot.client.find_voice_client_by_guild(guild) - if not (voice_client.is_playing() or voice_client.is_paused()): - raise CommandError("There is nothing to pause.") - # Toggle pause - resume = voice_client._player.is_paused() - if resume: - voice_client._player.resume() - else: - voice_client._player.pause() - return {"resumed": resume} - - _event_name = "_legacy_pause" - - def __init__(self, interface: CommandInterface): - super().__init__(interface) - if interface.name == "discord": - interface.register_herald_action(self._event_name, self._legacy_pause_handler) + description: str = "Metti in pausa o riprendi la riproduzione di un file." async def run(self, args: CommandArgs, data: CommandData) -> None: - guild_name, = args.match(r"(?:\[(.+)])?") - response = await self.interface.call_herald_action("discord", self._event_name, { - "guild_name": guild_name - }) - if response["resumed"]: - await data.reply(f"▶️ Riproduzione ripresa.") + if self.interface.name == "discord": + message: discord.Message = data.message + guild: discord.Guild = message.guild + guild_id: Optional[int] = guild.id else: - await data.reply(f"⏸ Riproduzione messa in pausa.") + guild_id = None + response: Dict[str, Any] = await self.interface.call_herald_event("discord", "discord_pause", + guild_id=guild_id) + + if response["action"] == "paused": + await data.reply("⏸ Riproduzione messa in pausa.") + + elif response["action"] == "resumed": + await data.reply("▶️ Riproduzione ripresa!") diff --git a/royalpack/commands/queue.py b/royalpack/commands/queue.py index a242d83f..2af55c66 100644 --- a/royalpack/commands/queue.py +++ b/royalpack/commands/queue.py @@ -1,9 +1,9 @@ -import typing import pickle +import base64 import discord +from typing import * from royalnet.commands import * -from royalnet.utils import numberemojiformat -from royalnet.bots import DiscordBot +from royalnet.utils import * class QueueCommand(Command): @@ -11,83 +11,52 @@ class QueueCommand(Command): aliases = ["q"] - description: str = "Visualizza la coda di riproduzione attuale." - - syntax = "[ [guild] ]" - - @staticmethod - async def _legacy_queue_handler(bot: "DiscordBot", guild_name: typing.Optional[str]): - # Find the matching guild - if guild_name: - guilds: typing.List[discord.Guild] = bot.client.find_guild_by_name(guild_name) - else: - guilds = bot.client.guilds - if len(guilds) == 0: - raise CommandError("No guilds with the specified name found.") - if len(guilds) > 1: - raise CommandError("Multiple guilds with the specified name found.") - guild = list(bot.client.guilds)[0] - # Check if the guild has a PlayMode - playmode = bot.music_data.get(guild) - if not playmode: - return { - "type": None - } - try: - queue = playmode.queue_preview() - except NotImplementedError: - return { - "type": playmode.__class__.__name__ - } - return { - "type": playmode.__class__.__name__, - "queue": - { - "strings": [str(dfile.info) for dfile in queue], - "pickled_embeds": str(pickle.dumps([dfile.info.to_discord_embed() for dfile in queue])) - } - } - - _event_name = "_legacy_queue" - - def __init__(self, interface: CommandInterface): - super().__init__(interface) - if interface.name == "discord": - interface.register_herald_action(self._event_name, self._legacy_queue_handler) + description: str = "Visualizza la coda di riproduzione attuale.." async def run(self, args: CommandArgs, data: CommandData) -> None: - guild_name, = args.match(r"(?:\[(.+)])?") - response = await self.interface.call_herald_action("discord", self._event_name, {"guild_name": guild_name}) - if response["type"] is None: - await data.reply("ℹ️ Non c'è nessuna coda di riproduzione attiva al momento.") - return - elif "queue" not in response: - await data.reply(f"ℹ️ La coda di riproduzione attuale ([c]{response['type']}[/c]) non permette l'anteprima.") - return - if response["type"] == "Playlist": - if len(response["queue"]["strings"]) == 0: - message = f"ℹ️ Questa [c]Playlist[/c] è vuota." - else: - message = f"ℹ️ Questa [c]Playlist[/c] contiene {len(response['queue']['strings'])} elementi, e i prossimi saranno:\n" - elif response["type"] == "Pool": - if len(response["queue"]["strings"]) == 0: - message = f"ℹ️ Questo [c]Pool[/c] è vuoto." - else: - message = f"ℹ️ Questo [c]Pool[/c] contiene {len(response['queue']['strings'])} elementi, tra cui:\n" - elif response["type"] == "Layers": - if len(response["queue"]["strings"]) == 0: - message = f"ℹ️ Nessun elemento è attualmente in riproduzione, pertanto non ci sono [c]Layers[/c]:" - else: - message = f"ℹ️ I [c]Layers[/c] dell'elemento attualmente in riproduzione sono {len(response['queue']['strings'])}, tra cui:\n" - else: - if len(response["queue"]["strings"]) == 0: - message = f"ℹ️ Il PlayMode attuale, [c]{response['type']}[/c], è vuoto.\n" - else: - message = f"ℹ️ Il PlayMode attuale, [c]{response['type']}[/c], contiene {len(response['queue']['strings'])} elementi:\n" if self.interface.name == "discord": - await data.reply(message) - for embed in pickle.loads(eval(response["queue"]["pickled_embeds"]))[:5]: - await data.message.channel.send(embed=embed) + message: discord.Message = data.message + guild: discord.Guild = message.guild + guild_id: Optional[int] = guild.id else: - message += numberemojiformat(response["queue"]["strings"][:10]) - await data.reply(message) + guild_id = None + response: Dict[str, Any] = await self.interface.call_herald_event("discord", "discord_queue", + guild_id=guild_id) + + queue_type = response["type"] + if queue_type == "PlayableYTDQueue": + next_up = response["next_up"] + now_playing = response["now_playing"] + await data.reply(f"ℹ️ La coda contiene {len(next_up)} file.\n\n") + + if now_playing is not None: + reply = f"Attualmente, sta venendo riprodotto:\n" + if self.interface.name == "discord": + await data.reply(reply) + embed = pickle.loads(base64.b64decode(bytes(now_playing["stringified_base64_pickled_discord_embed"], + encoding="ascii"))) + # noinspection PyUnboundLocalVariable + await message.channel.send(embed=embed) + else: + reply += f"▶️ {now_playing['title']}\n\n" + await data.reply(reply) + else: + await data.reply("⏹ Attualmente, non sta venendo riprodotto nulla.") + + reply = "" + if len(next_up) >= 1: + reply += "I prossimi file in coda sono:\n" + if self.interface.name == "discord": + await data.reply(reply) + for item in next_up[:5]: + embed = pickle.loads(base64.b64decode(bytes(item["stringified_base64_pickled_discord_embed"], + encoding="ascii"))) + # noinspection PyUnboundLocalVariable + await message.channel.send(embed=embed) + else: + reply += numberemojiformat([a["title"] for a in next_up[:5]]) + await data.reply(reply) + else: + await data.reply("ℹ️ Non ci sono altri file in coda.") + else: + raise CommandError(f"Non so come visualizzare il contenuto di un [c]{queue_type}[/c].") diff --git a/royalpack/commands/yahoovideo.py b/royalpack/commands/yahoovideo.py index 9536b06a..7e61c115 100644 --- a/royalpack/commands/yahoovideo.py +++ b/royalpack/commands/yahoovideo.py @@ -11,3 +11,5 @@ class YahoovideoCommand(PlayCommand): syntax = "{ricerca}" _URL_FORMAT = "yvsearch:{url}" + + # Too bad yvsearch: always finds nothing. diff --git a/royalpack/events/__init__.py b/royalpack/events/__init__.py index 83593c84..64834fb5 100644 --- a/royalpack/events/__init__.py +++ b/royalpack/events/__init__.py @@ -3,6 +3,8 @@ from .discord_cv import DiscordCvEvent from .discord_summon import DiscordSummonEvent from .discord_play import DiscordPlayEvent from .discord_skip import DiscordSkipEvent +from .discord_queue import DiscordQueueEvent +from .discord_pause import DiscordPauseEvent # Enter the commands of your Pack here! available_events = [ @@ -10,6 +12,8 @@ available_events = [ DiscordSummonEvent, DiscordPlayEvent, DiscordSkipEvent, + DiscordQueueEvent, + DiscordPauseEvent, ] # Don't change this, it should automatically generate __all__ diff --git a/royalpack/events/discord_pause.py b/royalpack/events/discord_pause.py new file mode 100644 index 00000000..f7733f5c --- /dev/null +++ b/royalpack/events/discord_pause.py @@ -0,0 +1,36 @@ +import discord +from typing import * +from royalnet.commands import * +from royalnet.serf.discord import * + + +class DiscordPauseEvent(Event): + name = "discord_pause" + + async def run(self, + guild_id: Optional[int] = None, + **kwargs) -> dict: + if not isinstance(self.serf, DiscordSerf): + raise UnsupportedError() + client: discord.Client = self.serf.client + if len(self.serf.voice_players) == 1: + voice_player: VoicePlayer = self.serf.voice_players[0] + else: + if guild_id is None: + # TODO: trovare un modo per riprodurre canzoni su più server da Telegram + raise InvalidInputError("Non so in che Server riprodurre questo file...\n" + "Invia il comando su Discord, per favore!") + guild: discord.Guild = client.get_guild(guild_id) + if guild is None: + raise InvalidInputError("Impossibile trovare il Server specificato.") + voice_player: VoicePlayer = self.serf.find_voice_player(guild) + if voice_player is None: + raise UserError("Il bot non è in nessun canale vocale.\n" + "Evocalo prima con [c]summon[/c]!") + + if voice_player.voice_client.is_paused(): + voice_player.voice_client.resume() + return {"action": "resumed"} + else: + voice_player.voice_client.pause() + return {"action": "paused"} diff --git a/royalpack/events/discord_queue.py b/royalpack/events/discord_queue.py new file mode 100644 index 00000000..396ab887 --- /dev/null +++ b/royalpack/events/discord_queue.py @@ -0,0 +1,49 @@ +import discord +import pickle +import base64 +from typing import * +from royalnet.commands import * +from royalnet.serf.discord import * + + +class DiscordQueueEvent(Event): + name = "discord_queue" + + async def run(self, + guild_id: Optional[int] = None, + **kwargs) -> dict: + if not isinstance(self.serf, DiscordSerf): + raise UnsupportedError() + client: discord.Client = self.serf.client + if len(self.serf.voice_players) == 1: + voice_player: VoicePlayer = self.serf.voice_players[0] + else: + if guild_id is None: + # TODO: trovare un modo per riprodurre canzoni su più server da Telegram + raise InvalidInputError("Non so in che Server riprodurre questo file...\n" + "Invia il comando su Discord, per favore!") + guild: discord.Guild = client.get_guild(guild_id) + if guild is None: + raise InvalidInputError("Impossibile trovare il Server specificato.") + voice_player: VoicePlayer = self.serf.find_voice_player(guild) + if voice_player is None: + raise UserError("Il bot non è in nessun canale vocale.\n" + "Evocalo prima con [c]summon[/c]!") + if isinstance(voice_player.playing, PlayableYTDQueue): + now_playing = voice_player.playing.now_playing + return { + "type": f"{voice_player.playing.__class__.__qualname__}", + "now_playing": { + "title": now_playing.info.title, + "stringified_base64_pickled_discord_embed": str(base64.b64encode(pickle.dumps(now_playing.embed())), + encoding="ascii") + } if now_playing is not None else None, + "next_up": [{ + "title": ytd.info.title, + "stringified_base64_pickled_discord_embed": str(base64.b64encode(pickle.dumps(ytd.embed())), + encoding="ascii") + } for ytd in voice_player.playing.contents] + } + else: + raise CommandError(f"Non so come visualizzare il contenuto di un " + f"[c]{voice_player.playing.__class__.__qualname__}[/c].")