diff --git a/Config/Embeds.py b/Config/Embeds.py index c367853..21b716b 100644 --- a/Config/Embeds.py +++ b/Config/Embeds.py @@ -277,6 +277,41 @@ class VEmbeds: ) return embed + def PLAYER_RESUMED(self) -> Embed: + embed = Embed( + title=self.__messages.SONG_RESUMED, + colour=self.__colors.BLUE + ) + return embed + + def SKIPPING_SONG(self) -> Embed: + embed = Embed( + title=self.__messages.SONG_SKIPPED, + colour=self.__colors.BLUE + ) + return embed + + def STOPPING_PLAYER(self) -> Embed: + embed = Embed( + title=self.__messages.STOPPING, + colour=self.__colors.BLUE + ) + return embed + + def RETURNING_SONG(self) -> Embed: + embed = Embed( + title=self.__messages.RETURNING_SONG, + colour=self.__colors.BLUE + ) + return embed + + def PLAYER_PAUSED(self) -> Embed: + embed = Embed( + title=self.__messages.SONG_PAUSED, + colour=self.__colors.BLUE + ) + return embed + def NOT_PREVIOUS_SONG(self) -> Embed: embed = Embed( title=self.__messages.SONG_PLAYER, diff --git a/Config/Messages.py b/Config/Messages.py index cf6ca05..b243ce5 100644 --- a/Config/Messages.py +++ b/Config/Messages.py @@ -26,6 +26,9 @@ class Messages(Singleton): self.ALL_SONGS_LOOPING = f'{self.__emojis.MUSIC} Looping All Songs' self.SONG_PAUSED = f'{self.__emojis.PAUSE} Song paused' self.SONG_RESUMED = f'{self.__emojis.PLAY} Song playing' + self.SONG_SKIPPED = f'{self.__emojis.SKIP} Song skipped' + self.RETURNING_SONG = f'{self.__emojis.BACK} Playing previous song' + self.STOPPING = f'{self.__emojis.STOP} Player Stopped' self.EMPTY_QUEUE = f'{self.__emojis.QUEUE} Song queue is empty, use {configs.BOT_PREFIX}play to add new songs' self.SONG_DOWNLOADING = f'{self.__emojis.DOWNLOADING} Downloading...' diff --git a/DiscordCogs/MusicCog.py b/DiscordCogs/MusicCog.py index b5409d9..b5e6252 100644 --- a/DiscordCogs/MusicCog.py +++ b/DiscordCogs/MusicCog.py @@ -17,8 +17,9 @@ from Handlers.ResumeHandler import ResumeHandler from Handlers.HistoryHandler import HistoryHandler from Handlers.QueueHandler import QueueHandler from Handlers.LoopHandler import LoopHandler -from UI.Responses.EmoteCogResponse import EmoteCommandResponse -from UI.Responses.EmbedCogResponse import EmbedCommandResponse +from Messages.MessagesCategory import MessagesCategory +from Messages.Responses.EmoteCogResponse import EmoteCommandResponse +from Messages.Responses.EmbedCogResponse import EmbedCommandResponse from Music.VulkanBot import VulkanBot from Config.Configs import VConfigs from Config.Embeds import VEmbeds @@ -46,10 +47,10 @@ class MusicCog(Cog): response = await controller.run(args) if response is not None: - view1 = EmbedCommandResponse(response) - view2 = EmoteCommandResponse(response) - await view1.run() - await view2.run() + cogResponser1 = EmbedCommandResponse(response, MessagesCategory.PLAYER) + cogResponser2 = EmoteCommandResponse(response, MessagesCategory.PLAYER) + await cogResponser1.run() + await cogResponser2.run() except Exception as e: print(f'[ERROR IN COG] -> {e}') @@ -58,7 +59,7 @@ class MusicCog(Cog): try: pageNumber = " ".join(args) - controller = QueueHandler(ctx, self.__bot,) + controller = QueueHandler(ctx, self.__bot) if pageNumber == "": response = await controller.run() @@ -67,14 +68,16 @@ class MusicCog(Cog): pageNumber -= 1 # Change index 1 to 0 response = await controller.run(pageNumber) - view = EmbedCommandResponse(response) - await view.run() + cogResponser = EmbedCommandResponse(response, MessagesCategory.QUEUE) + await cogResponser.run() except ValueError as e: + # Draft a Handler Response to pass to cogResponser error = InvalidInput() embed = self.__embeds.INVALID_ARGUMENTS() response = HandlerResponse(ctx, embed, error) - view = EmbedCommandResponse(response) - await view.run() + + cogResponser = EmbedCommandResponse(response, MessagesCategory.QUEUE) + await cogResponser.run(deleteLast=False) except Exception as e: print(f'[ERROR IN COG] -> {e}') @@ -84,12 +87,10 @@ class MusicCog(Cog): controller = SkipHandler(ctx, self.__bot) response = await controller.run() - if response.success: - view = EmoteCommandResponse(response) - else: - view = EmbedCommandResponse(response) - - await view.run() + cogResponser1 = EmbedCommandResponse(response, MessagesCategory.PLAYER) + cogResponser2 = EmoteCommandResponse(response, MessagesCategory.PLAYER) + await cogResponser1.run() + await cogResponser2.run() except Exception as e: print(f'[ERROR IN COG] -> {e}') @@ -99,12 +100,10 @@ class MusicCog(Cog): controller = StopHandler(ctx, self.__bot) response = await controller.run() - if response.success: - view = EmoteCommandResponse(response) - else: - view = EmbedCommandResponse(response) - - await view.run() + cogResponser1 = EmbedCommandResponse(response, MessagesCategory.PLAYER) + cogResponser2 = EmoteCommandResponse(response, MessagesCategory.PLAYER) + await cogResponser1.run() + await cogResponser2.run() except Exception as e: print(f'[ERROR IN COG] -> {e}') @@ -114,10 +113,10 @@ class MusicCog(Cog): controller = PauseHandler(ctx, self.__bot) response = await controller.run() - view1 = EmoteCommandResponse(response) - view2 = EmbedCommandResponse(response) - await view1.run() - await view2.run() + cogResponser1 = EmoteCommandResponse(response, MessagesCategory.PLAYER) + cogResponser2 = EmbedCommandResponse(response, MessagesCategory.PLAYER) + await cogResponser1.run() + await cogResponser2.run() except Exception as e: print(f'[ERROR IN COG] -> {e}') @@ -127,10 +126,10 @@ class MusicCog(Cog): controller = ResumeHandler(ctx, self.__bot) response = await controller.run() - view1 = EmoteCommandResponse(response) - view2 = EmbedCommandResponse(response) - await view1.run() - await view2.run() + cogResponser1 = EmoteCommandResponse(response, MessagesCategory.PLAYER) + cogResponser2 = EmbedCommandResponse(response, MessagesCategory.PLAYER) + await cogResponser1.run() + await cogResponser2.run() except Exception as e: print(f'[ERROR IN COG] -> {e}') @@ -141,10 +140,10 @@ class MusicCog(Cog): response = await controller.run() if response is not None: - view1 = EmbedCommandResponse(response) - view2 = EmoteCommandResponse(response) - await view1.run() - await view2.run() + cogResponser1 = EmbedCommandResponse(response, MessagesCategory.PLAYER) + cogResponser2 = EmoteCommandResponse(response, MessagesCategory.PLAYER) + await cogResponser1.run() + await cogResponser2.run() except Exception as e: print(f'[ERROR IN COG] -> {e}') @@ -154,10 +153,10 @@ class MusicCog(Cog): controller = HistoryHandler(ctx, self.__bot) response = await controller.run() - view1 = EmbedCommandResponse(response) - view2 = EmoteCommandResponse(response) - await view1.run() - await view2.run() + cogResponser1 = EmbedCommandResponse(response, MessagesCategory.HISTORY) + cogResponser2 = EmoteCommandResponse(response, MessagesCategory.HISTORY) + await cogResponser1.run() + await cogResponser2.run() except Exception as e: print(f'[ERROR IN COG] -> {e}') @@ -167,10 +166,10 @@ class MusicCog(Cog): controller = LoopHandler(ctx, self.__bot) response = await controller.run(args) - view1 = EmoteCommandResponse(response) - view2 = EmbedCommandResponse(response) - await view1.run() - await view2.run() + cogResponser1 = EmoteCommandResponse(response, MessagesCategory.LOOP) + cogResponser2 = EmbedCommandResponse(response, MessagesCategory.LOOP) + await cogResponser1.run() + await cogResponser2.run() except Exception as e: print(f'[ERROR IN COG] -> {e}') @@ -180,8 +179,10 @@ class MusicCog(Cog): controller = ClearHandler(ctx, self.__bot) response = await controller.run() - view = EmoteCommandResponse(response) - await view.run() + cogResponser1 = EmbedCommandResponse(response, MessagesCategory.PLAYER) + cogResponser2 = EmoteCommandResponse(response, MessagesCategory.PLAYER) + await cogResponser1.run() + await cogResponser2.run() except Exception as e: print(f'[ERROR IN COG] -> {e}') @@ -191,10 +192,10 @@ class MusicCog(Cog): controller = NowPlayingHandler(ctx, self.__bot) response = await controller.run() - view1 = EmbedCommandResponse(response) - view2 = EmoteCommandResponse(response) - await view1.run() - await view2.run() + cogResponser1 = EmbedCommandResponse(response, MessagesCategory.NOW_PLAYING) + cogResponser2 = EmoteCommandResponse(response, MessagesCategory.NOW_PLAYING) + await cogResponser1.run() + await cogResponser2.run() except Exception as e: print(f'[ERROR IN COG] -> {e}') @@ -204,10 +205,10 @@ class MusicCog(Cog): controller = ShuffleHandler(ctx, self.__bot) response = await controller.run() - view1 = EmbedCommandResponse(response) - view2 = EmoteCommandResponse(response) - await view1.run() - await view2.run() + cogResponser1 = EmbedCommandResponse(response, MessagesCategory.PLAYER) + cogResponser2 = EmoteCommandResponse(response, MessagesCategory.PLAYER) + await cogResponser1.run() + await cogResponser2.run() except Exception as e: print(f'[ERROR IN COG] -> {e}') @@ -217,10 +218,10 @@ class MusicCog(Cog): controller = MoveHandler(ctx, self.__bot) response = await controller.run(pos1, pos2) - view1 = EmbedCommandResponse(response) - view2 = EmoteCommandResponse(response) - await view1.run() - await view2.run() + cogResponser1 = EmbedCommandResponse(response, MessagesCategory.MANAGING_QUEUE) + cogResponser2 = EmoteCommandResponse(response, MessagesCategory.MANAGING_QUEUE) + await cogResponser1.run() + await cogResponser2.run() except Exception as e: print(f'[ERROR IN COG] -> {e}') @@ -230,10 +231,10 @@ class MusicCog(Cog): controller = RemoveHandler(ctx, self.__bot) response = await controller.run(position) - view1 = EmbedCommandResponse(response) - view2 = EmoteCommandResponse(response) - await view1.run() - await view2.run() + cogResponser1 = EmbedCommandResponse(response, MessagesCategory.MANAGING_QUEUE) + cogResponser2 = EmoteCommandResponse(response, MessagesCategory.MANAGING_QUEUE) + await cogResponser1.run() + await cogResponser2.run() except Exception as e: print(f'[ERROR IN COG] -> {e}') @@ -243,10 +244,10 @@ class MusicCog(Cog): controller = ResetHandler(ctx, self.__bot) response = await controller.run() - view1 = EmbedCommandResponse(response) - view2 = EmoteCommandResponse(response) - await view1.run() - await view2.run() + cogResponser1 = EmbedCommandResponse(response, MessagesCategory.PLAYER) + cogResponser2 = EmoteCommandResponse(response, MessagesCategory.PLAYER) + await cogResponser1.run() + await cogResponser2.run() except Exception as e: print(f'[ERROR IN COG] -> {e}') diff --git a/Handlers/PauseHandler.py b/Handlers/PauseHandler.py index cba457e..a76b947 100644 --- a/Handlers/PauseHandler.py +++ b/Handlers/PauseHandler.py @@ -25,7 +25,8 @@ class PauseHandler(AbstractHandler): queue = processInfo.getQueueToPlayer() queue.put(command) - return HandlerResponse(self.ctx) + embed = self.embeds.PLAYER_PAUSED() + return HandlerResponse(self.ctx, embed) else: embed = self.embeds.NOT_PLAYING() return HandlerResponse(self.ctx, embed) diff --git a/Handlers/PrevHandler.py b/Handlers/PrevHandler.py index b1d63ef..dee9ebd 100644 --- a/Handlers/PrevHandler.py +++ b/Handlers/PrevHandler.py @@ -45,7 +45,9 @@ class PrevHandler(AbstractHandler): prevCommand = VCommands(VCommandsType.PREV, self.author.voice.channel.id) queue = processInfo.getQueueToPlayer() queue.put(prevCommand) - return HandlerResponse(self.ctx) + + embed = self.embeds.RETURNING_SONG() + return HandlerResponse(self.ctx, embed) def __user_connected(self) -> bool: if self.author.voice: diff --git a/Handlers/QueueHandler.py b/Handlers/QueueHandler.py index e2ca767..e84c0e7 100644 --- a/Handlers/QueueHandler.py +++ b/Handlers/QueueHandler.py @@ -2,14 +2,15 @@ from discord.ext.commands import Context from Config.Exceptions import InvalidIndex from Handlers.AbstractHandler import AbstractHandler from Handlers.HandlerResponse import HandlerResponse -from UI.Views.EmptyView import EmptyView +from Messages.MessagesCategory import MessagesCategory +from UI.Views.BasicView import BasicView from Utils.Utils import Utils from Music.VulkanBot import VulkanBot from Music.Song import Song from Music.Playlist import Playlist from typing import List, Union from discord import Button, Interaction -from UI.Buttons.EmptyButton import EmptyButton +from UI.Buttons.EmptyButton import CallbackButton from Config.Emojis import VEmojis @@ -54,7 +55,7 @@ class QueueHandler(AbstractHandler): songs = songsPages[pageNumber] # Create view for this embed buttons = self.__createViewButtons(songsPages, pageNumber) - queueView = EmptyView(self.bot, buttons, self.config.QUEUE_VIEW_TIMEOUT) + queueView = BasicView(self.bot, buttons, self.config.QUEUE_VIEW_TIMEOUT) if playlist.isLoopingAll(): title = self.messages.ALL_SONGS_LOOPING @@ -86,12 +87,12 @@ class QueueHandler(AbstractHandler): buttons = [] if pageNumber > 0: prevPageNumber = pageNumber - 1 - buttons.append(EmptyButton(self.bot, self.run, VEmojis().BACK, - "Prev Page", pageNumber=prevPageNumber)) + buttons.append(CallbackButton(self.bot, self.run, VEmojis().BACK, self.ctx.channel, + self.guild.id, MessagesCategory.QUEUE, "Prev Page", pageNumber=prevPageNumber)) if pageNumber < len(songsPages) - 1: nextPageNumber = pageNumber + 1 - buttons.append(EmptyButton(self.bot, self.run, VEmojis().SKIP, - "Next Page", pageNumber=nextPageNumber)) + buttons.append(CallbackButton(self.bot, self.run, VEmojis().SKIP, self.ctx.channel, + self.guild.id, MessagesCategory.QUEUE, "Next Page", pageNumber=nextPageNumber)) return buttons diff --git a/Handlers/ResumeHandler.py b/Handlers/ResumeHandler.py index 209a55a..a9988e7 100644 --- a/Handlers/ResumeHandler.py +++ b/Handlers/ResumeHandler.py @@ -25,7 +25,8 @@ class ResumeHandler(AbstractHandler): queue = processInfo.getQueueToPlayer() queue.put(command) - return HandlerResponse(self.ctx) + embed = self.embeds.PLAYER_RESUMED() + return HandlerResponse(self.ctx, embed) else: embed = self.embeds.NOT_PLAYING() return HandlerResponse(self.ctx, embed) diff --git a/Handlers/SkipHandler.py b/Handlers/SkipHandler.py index ff43ef3..ae11295 100644 --- a/Handlers/SkipHandler.py +++ b/Handlers/SkipHandler.py @@ -37,7 +37,8 @@ class SkipHandler(AbstractHandler): queue = processInfo.getQueueToPlayer() queue.put(command) - return HandlerResponse(self.ctx) + embed = self.embeds.SKIPPING_SONG() + return HandlerResponse(self.ctx, embed) else: embed = self.embeds.NOT_PLAYING() return HandlerResponse(self.ctx, embed) diff --git a/Handlers/StopHandler.py b/Handlers/StopHandler.py index 91a486e..e921e5a 100644 --- a/Handlers/StopHandler.py +++ b/Handlers/StopHandler.py @@ -25,7 +25,8 @@ class StopHandler(AbstractHandler): queue = processInfo.getQueueToPlayer() queue.put(command) - return HandlerResponse(self.ctx) + embed = self.embeds.STOPPING_PLAYER() + return HandlerResponse(self.ctx, embed) else: embed = self.embeds.NOT_PLAYING() return HandlerResponse(self.ctx, embed) diff --git a/Messages/MessagesCategory.py b/Messages/MessagesCategory.py new file mode 100644 index 0000000..0cb1f31 --- /dev/null +++ b/Messages/MessagesCategory.py @@ -0,0 +1,11 @@ +from enum import Enum + + +class MessagesCategory(Enum): + QUEUE = 1 + HISTORY = 2 + LOOP = 3 + NOW_PLAYING = 4 + PLAYER = 5 + MANAGING_QUEUE = 6 + OTHERS = 7 diff --git a/Messages/MessagesManager.py b/Messages/MessagesManager.py new file mode 100644 index 0000000..585852a --- /dev/null +++ b/Messages/MessagesManager.py @@ -0,0 +1,65 @@ +from typing import Dict, List +from discord import Message +from Config.Singleton import Singleton +from Messages.MessagesCategory import MessagesCategory + + +class MessagesManager(Singleton): + def __init__(self) -> None: + if not super().created: + # For each guild, and for each category, there will be a list of messages + self.__guildsMessages: Dict[int, Dict[MessagesCategory, List[Message]]] = {} + + def addMessage(self, guildID: int, category: MessagesCategory, message: Message) -> None: + if message is None: + return + + # If guild not exists create Dict + if guildID not in self.__guildsMessages.keys(): + self.__guildsMessages[guildID] = {} + # If category not in guild yet, add + if category not in self.__guildsMessages[guildID].keys(): + self.__guildsMessages[guildID][category] = [] + + sendedMessages = self.__guildsMessages[guildID][category] + sendedMessages.append(message) + + async def addMessageAndClearPrevious(self, guildID: int, category: MessagesCategory, message: Message) -> None: + if message is None: + return + + # If guild not exists create Dict + if guildID not in self.__guildsMessages.keys(): + self.__guildsMessages[guildID] = {} + # If category not in guild yet, add + if category not in self.__guildsMessages[guildID].keys(): + self.__guildsMessages[guildID][category] = [] + + sendedMessages = self.__guildsMessages[guildID][category] + + # Delete sended all messages of this category + for previousMessage in sendedMessages: + await self.__deleteMessage(previousMessage) + + # Create a new list with only the new message + self.__guildsMessages[guildID][category] = [message] + + async def clearMessagesOfCategory(self, guildID: int, category: MessagesCategory) -> None: + sendedMessages = self.__guildsMessages[guildID][category] + + for message in sendedMessages: + self.__deleteMessage(message) + + async def clearMessagesOfGuild(self, guildID: int) -> None: + categoriesMessages = self.__guildsMessages[guildID] + + for category in categoriesMessages.keys(): + for message in categoriesMessages[category]: + self.__deleteMessage(message) + + async def __deleteMessage(self, message: Message) -> None: + try: + await message.delete() + except Exception as e: + print(f'[ERROR DELETING MESSAGE] -> {e}') + pass diff --git a/UI/Responses/AbstractCogResponse.py b/Messages/Responses/AbstractCogResponse.py similarity index 59% rename from UI/Responses/AbstractCogResponse.py rename to Messages/Responses/AbstractCogResponse.py index 1cee764..3bfa044 100644 --- a/UI/Responses/AbstractCogResponse.py +++ b/Messages/Responses/AbstractCogResponse.py @@ -2,13 +2,16 @@ from abc import ABC, abstractmethod from Handlers.HandlerResponse import HandlerResponse from discord.ext.commands import Context from discord import Message -from discord.ui import View +from Messages.MessagesCategory import MessagesCategory +from Messages.MessagesManager import MessagesManager from Music.VulkanBot import VulkanBot class AbstractCommandResponse(ABC): - def __init__(self, response: HandlerResponse) -> None: + def __init__(self, response: HandlerResponse, category: MessagesCategory) -> None: + self.__messagesManager = MessagesManager() self.__response: HandlerResponse = response + self.__category: MessagesCategory = category self.__context: Context = response.ctx self.__message: Message = response.ctx.message self.__bot: VulkanBot = response.ctx.bot @@ -17,6 +20,10 @@ class AbstractCommandResponse(ABC): def response(self) -> HandlerResponse: return self.__response + @property + def category(self) -> MessagesCategory: + return self.__category + @property def bot(self) -> VulkanBot: return self.__bot @@ -29,6 +36,10 @@ class AbstractCommandResponse(ABC): def context(self) -> Context: return self.__context + @property + def manager(self) -> MessagesManager: + return self.__messagesManager + @abstractmethod - async def run(self) -> None: + async def run(self, deleteLast: bool = True) -> None: pass diff --git a/Messages/Responses/EmbedCogResponse.py b/Messages/Responses/EmbedCogResponse.py new file mode 100644 index 0000000..2e391d6 --- /dev/null +++ b/Messages/Responses/EmbedCogResponse.py @@ -0,0 +1,20 @@ +from Messages.Responses.AbstractCogResponse import AbstractCommandResponse +from Handlers.HandlerResponse import HandlerResponse +from Messages.MessagesCategory import MessagesCategory + + +class EmbedCommandResponse(AbstractCommandResponse): + def __init__(self, response: HandlerResponse, category: MessagesCategory) -> None: + super().__init__(response, category) + + async def run(self, deleteLast: bool = True) -> None: + if self.response.embed and self.response.view: + message = await self.context.send(embed=self.response.embed, view=self.response.view) + elif self.response.embed: + message = await self.context.send(embed=self.response.embed) + + # Only delete the previous message if this is not error and not forbidden by method caller + if deleteLast and self.response.success: + await self.manager.addMessageAndClearPrevious(self.context.guild.id, self.category, message) + else: + self.manager.addMessage(self.context.guild.id, self.category, message) diff --git a/UI/Responses/EmoteCogResponse.py b/Messages/Responses/EmoteCogResponse.py similarity index 52% rename from UI/Responses/EmoteCogResponse.py rename to Messages/Responses/EmoteCogResponse.py index 09294cf..c1d62bc 100644 --- a/UI/Responses/EmoteCogResponse.py +++ b/Messages/Responses/EmoteCogResponse.py @@ -1,15 +1,16 @@ from Config.Emojis import VEmojis -from UI.Responses.AbstractCogResponse import AbstractCommandResponse +from Messages.Responses.AbstractCogResponse import AbstractCommandResponse from Handlers.HandlerResponse import HandlerResponse +from Messages.MessagesCategory import MessagesCategory class EmoteCommandResponse(AbstractCommandResponse): - def __init__(self, response: HandlerResponse) -> None: - super().__init__(response) + def __init__(self, response: HandlerResponse, category: MessagesCategory) -> None: + super().__init__(response, category) self.__emojis = VEmojis() - async def run(self) -> None: + async def run(self, deleteLast: bool = True) -> None: if self.response.success: await self.message.add_reaction(self.__emojis.SUCCESS) else: diff --git a/Music/MessagesController.py b/Music/MessagesController.py deleted file mode 100644 index 0e8567a..0000000 --- a/Music/MessagesController.py +++ /dev/null @@ -1,65 +0,0 @@ -from typing import List -from discord import Embed, Message, TextChannel -from Music.VulkanBot import VulkanBot -from Parallelism.ProcessInfo import ProcessInfo -from Config.Configs import VConfigs -from Config.Messages import Messages -from Music.Song import Song -from Config.Embeds import VEmbeds -from UI.Views.PlayerView import PlayerView - - -class MessagesController: - def __init__(self, bot: VulkanBot) -> None: - self.__bot = bot - self.__previousMessages = [] - self.__configs = VConfigs() - self.__messages = Messages() - self.__embeds = VEmbeds() - - async def sendNowPlaying(self, processInfo: ProcessInfo, song: Song) -> None: - # Get the lock of the playlist - playlist = processInfo.getPlaylist() - if playlist.isLoopingOne(): - title = self.__messages.ONE_SONG_LOOPING - else: - title = self.__messages.SONG_PLAYING - - # Create View and Embed - embed = self.__embeds.SONG_INFO(song.info, title) - view = PlayerView(self.__bot) - channel = processInfo.getTextChannel() - # Delete the previous and send the message - await self.__deletePreviousNPMessages() - await channel.send(embed=embed, view=view) - - # Get the sended message - sendedMessage = await self.__getSendedMessage(channel) - # Set the message witch contains the view - view.set_message(message=sendedMessage) - self.__previousMessages.append(sendedMessage) - - async def __deletePreviousNPMessages(self) -> None: - for message in self.__previousMessages: - try: - await message.delete() - except: - pass - self.__previousMessages.clear() - - async def __getSendedMessage(self, channel: TextChannel) -> Message: - stringToIdentify = 'Uploader:' - last_messages: List[Message] = await channel.history(limit=5).flatten() - - for message in last_messages: - try: - if message.author == self.__bot.user: - if len(message.embeds) > 0: - embed: Embed = message.embeds[0] - if len(embed.fields) > 0: - if embed.fields[0].name == stringToIdentify: - return message - - except Exception as e: - print(f'DEVELOPER NOTE -> Error cleaning messages {e}') - continue diff --git a/Parallelism/ProcessExecutor.py b/Parallelism/ProcessExecutor.py new file mode 100644 index 0000000..1509e18 --- /dev/null +++ b/Parallelism/ProcessExecutor.py @@ -0,0 +1,79 @@ +from typing import List +from discord import Button, TextChannel +from discord.ui import View +from Config.Emojis import VEmojis +from Messages.MessagesCategory import MessagesCategory +from Music.VulkanBot import VulkanBot +from Parallelism.ProcessInfo import ProcessInfo +from Config.Messages import Messages +from Music.Song import Song +from Config.Embeds import VEmbeds +from UI.Buttons.HandlerButton import HandlerButton +from UI.Views.BasicView import BasicView +from Messages.MessagesManager import MessagesManager +from Handlers.PrevHandler import PrevHandler +from Handlers.PauseHandler import PauseHandler +from Handlers.SkipHandler import SkipHandler +from Handlers.StopHandler import StopHandler +from Handlers.ResumeHandler import ResumeHandler +from Handlers.LoopHandler import LoopHandler +from Handlers.QueueHandler import QueueHandler + + +class ProcessCommandsExecutor: + def __init__(self, bot: VulkanBot, guildID: int) -> None: + self.__bot = bot + self.__guildID = guildID + self.__messagesManager = MessagesManager() + self.__messages = Messages() + self.__embeds = VEmbeds() + self.__emojis = VEmojis() + + async def sendNowPlaying(self, processInfo: ProcessInfo, song: Song) -> None: + # Get the lock of the playlist + playlist = processInfo.getPlaylist() + if playlist.isLoopingOne(): + title = self.__messages.ONE_SONG_LOOPING + else: + title = self.__messages.SONG_PLAYING + + # Create View and Embed + embed = self.__embeds.SONG_INFO(song.info, title) + channel = processInfo.getTextChannel() + view = self.__getPlayerView(channel) + # Send Message and add to the MessagesManager + message = await channel.send(embed=embed, view=view) + await self.__messagesManager.addMessageAndClearPrevious(self.__guildID, MessagesCategory.NOW_PLAYING, message) + + # Set in the view the message witch contains the view + view.set_message(message=message) + + def __getPlayerView(self, channel: TextChannel) -> View: + buttons = self.__getPlayerButtons(channel) + view = BasicView(self.__bot, buttons) + return view + + def __getPlayerButtons(self, textChannel: TextChannel) -> List[Button]: + """Create the Buttons to be inserted in the Player View""" + buttons: List[Button] = [] + + buttons.append(HandlerButton(self.__bot, PrevHandler, self.__emojis.BACK, + textChannel, self.__guildID, MessagesCategory.PLAYER, "Back")) + buttons.append(HandlerButton(self.__bot, PauseHandler, self.__emojis.PAUSE, + textChannel, self.__guildID, MessagesCategory.PLAYER, "Pause")) + buttons.append(HandlerButton(self.__bot, ResumeHandler, self.__emojis.PLAY, + textChannel, self.__guildID, MessagesCategory.PLAYER, "Play")) + buttons.append(HandlerButton(self.__bot, StopHandler, self.__emojis.STOP, + textChannel, self.__guildID, MessagesCategory.PLAYER, "Stop")) + buttons.append(HandlerButton(self.__bot, SkipHandler, self.__emojis.SKIP, + textChannel, self.__guildID, MessagesCategory.PLAYER, "Skip")) + buttons.append(HandlerButton(self.__bot, QueueHandler, self.__emojis.QUEUE, + textChannel, self.__guildID, MessagesCategory.QUEUE, "Songs")) + buttons.append(HandlerButton(self.__bot, LoopHandler, self.__emojis.LOOP_ONE, + textChannel, self.__guildID, MessagesCategory.LOOP, "Loop One", 'One')) + buttons.append(HandlerButton(self.__bot, LoopHandler, self.__emojis.LOOP_OFF, + textChannel, self.__guildID, MessagesCategory.LOOP, "Loop Off", 'Off')) + buttons.append(HandlerButton(self.__bot, LoopHandler, self.__emojis.LOOP_ALL, + textChannel, self.__guildID, MessagesCategory.LOOP, "Loop All", 'All')) + + return buttons diff --git a/Parallelism/ProcessManager.py b/Parallelism/ProcessManager.py index 7c0bf20..ccfc667 100644 --- a/Parallelism/ProcessManager.py +++ b/Parallelism/ProcessManager.py @@ -7,7 +7,7 @@ from typing import Dict, Tuple, Union from Config.Singleton import Singleton from discord import Guild, Interaction from discord.ext.commands import Context -from Music.MessagesController import MessagesController +from Parallelism.ProcessExecutor import ProcessCommandsExecutor from Music.Song import Song from Parallelism.PlayerProcess import PlayerProcess from Music.Playlist import Playlist @@ -30,7 +30,7 @@ class ProcessManager(Singleton): self.__manager.start() self.__playersProcess: Dict[Guild, ProcessInfo] = {} self.__playersListeners: Dict[Guild, Tuple[Thread, bool]] = {} - self.__playersMessages: Dict[Guild, MessagesController] = {} + self.__playersCommandsExecutor: Dict[Guild, ProcessCommandsExecutor] = {} def setPlayerInfo(self, guild: Guild, info: ProcessInfo): self.__playersProcess[guild.id] = info @@ -91,7 +91,7 @@ class ProcessManager(Singleton): thread.start() # Create a Message Controller for this player - self.__playersMessages[guildID] = MessagesController(self.__bot) + self.__playersCommandsExecutor[guildID] = ProcessCommandsExecutor(self.__bot, guildID) return processInfo @@ -157,7 +157,7 @@ class ProcessManager(Singleton): def __terminateProcess(self, guildID: int) -> None: # Delete all structures associated with the Player del self.__playersProcess[guildID] - del self.__playersMessages[guildID] + del self.__playersCommandsExecutor[guildID] threadListening = self.__playersListeners[guildID] threadListening._stop() del self.__playersListeners[guildID] @@ -174,9 +174,9 @@ class ProcessManager(Singleton): self.__playersProcess[guildID].setStatus(ProcessStatus.SLEEPING) async def showNowPlaying(self, guildID: int, song: Song) -> None: - messagesController = self.__playersMessages[guildID] + commandExecutor = self.__playersCommandsExecutor[guildID] processInfo = self.__playersProcess[guildID] - await messagesController.sendNowPlaying(processInfo, song) + await commandExecutor.sendNowPlaying(processInfo, song) class VManager(BaseManager): diff --git a/UI/Buttons/EmptyButton.py b/UI/Buttons/EmptyButton.py index 1f12fd4..3125864 100644 --- a/UI/Buttons/EmptyButton.py +++ b/UI/Buttons/EmptyButton.py @@ -1,13 +1,22 @@ from typing import Awaitable -from discord import ButtonStyle, Interaction +from Config.Emojis import VEmojis +from discord import ButtonStyle, Interaction, Message, TextChannel from discord.ui import Button from Handlers.HandlerResponse import HandlerResponse +from Messages.MessagesCategory import MessagesCategory +from Messages.MessagesManager import MessagesManager from Music.VulkanBot import VulkanBot -class EmptyButton(Button): - def __init__(self, bot: VulkanBot, cb: Awaitable, emoji, label=None, *args, **kwargs): +class CallbackButton(Button): + """When clicked execute an callback passing the args and kwargs""" + + def __init__(self, bot: VulkanBot, cb: Awaitable, emoji: VEmojis, textChannel: TextChannel, guildID: int, category: MessagesCategory, label=None, *args, **kwargs): super().__init__(label=label, style=ButtonStyle.secondary, emoji=emoji) + self.__channel = textChannel + self.__guildID = guildID + self.__category = category + self.__messagesManager = MessagesManager() self.__bot = bot self.__args = args self.__kwargs = kwargs @@ -21,6 +30,9 @@ class EmptyButton(Button): response: HandlerResponse = await self.__callback(*self.__args, **self.__kwargs) if response and response.view is not None: - await interaction.followup.send(embed=response.embed, view=response.view) - elif response: - await interaction.followup.send(embed=response.embed) + message: Message = await self.__channel.send(embed=response.embed, view=response.view) + else: + message: Message = await self.__channel.send(embed=response.embed) + + # Clear the last sended message in this category and add the new one + await self.__messagesManager.addMessageAndClearPrevious(self.__guildID, self.__category, message) diff --git a/UI/Buttons/HandlerButton.py b/UI/Buttons/HandlerButton.py new file mode 100644 index 0000000..3b08bbb --- /dev/null +++ b/UI/Buttons/HandlerButton.py @@ -0,0 +1,40 @@ +from Config.Emojis import VEmojis +from discord import ButtonStyle, Interaction, Message, TextChannel +from discord.ui import Button +from Handlers.HandlerResponse import HandlerResponse +from Messages.MessagesCategory import MessagesCategory +from Music.VulkanBot import VulkanBot +from Handlers.AbstractHandler import AbstractHandler +from Messages.MessagesManager import MessagesManager + + +class HandlerButton(Button): + """Button that will create and execute a Handler Object when clicked""" + + def __init__(self, bot: VulkanBot, handler: type[AbstractHandler], emoji: VEmojis, textChannel: TextChannel, guildID: int, category: MessagesCategory, label=None, *args, **kwargs): + super().__init__(label=label, style=ButtonStyle.secondary, emoji=emoji) + self.__messagesManager = MessagesManager() + self.__category = category + self.__guildID = guildID + self.__channel = textChannel + self.__bot = bot + self.__args = args + self.__kwargs = kwargs + self.__handlerClass = handler + + async def callback(self, interaction: Interaction) -> None: + """Callback to when Button is clicked""" + # Return to Discord that this command is being processed + await interaction.response.defer() + + # Create the handler object + handler = self.__handlerClass(interaction, self.__bot) + response: HandlerResponse = await handler.run(*self.__args, **self.__kwargs) + + if response and response.view is not None: + message: Message = await self.__channel.send(embed=response.embed, view=response.view) + else: + message: Message = await self.__channel.send(embed=response.embed) + + # Clear the last category sended message and add the new one + await self.__messagesManager.addMessageAndClearPrevious(self.__guildID, self.__category, message) diff --git a/UI/Responses/EmbedCogResponse.py b/UI/Responses/EmbedCogResponse.py deleted file mode 100644 index f86490b..0000000 --- a/UI/Responses/EmbedCogResponse.py +++ /dev/null @@ -1,11 +0,0 @@ -from UI.Responses.AbstractCogResponse import AbstractCommandResponse -from Handlers.HandlerResponse import HandlerResponse - - -class EmbedCommandResponse(AbstractCommandResponse): - def __init__(self, response: HandlerResponse) -> None: - super().__init__(response) - - async def run(self) -> None: - if self.response.embed: - await self.context.send(embed=self.response.embed, view=self.response.view) diff --git a/UI/Views/EmptyView.py b/UI/Views/BasicView.py similarity index 90% rename from UI/Views/EmptyView.py rename to UI/Views/BasicView.py index aee9da1..743af7d 100644 --- a/UI/Views/EmptyView.py +++ b/UI/Views/BasicView.py @@ -7,7 +7,9 @@ from Music.VulkanBot import VulkanBot emojis = VEmojis() -class EmptyView(View): +class BasicView(View): + """View that receives buttons to hold, in timeout disable buttons""" + def __init__(self, bot: VulkanBot, buttons: List[Button], timeout: float = 6000): super().__init__(timeout=timeout) self.__bot = bot