Code style upgrade

This commit is contained in:
Rafael Vargas
2022-07-23 09:52:20 -03:00
parent cd3eddb125
commit 3eab6176c3
36 changed files with 221 additions and 220 deletions

View File

@@ -0,0 +1,85 @@
from abc import ABC, abstractmethod
from typing import List
from discord.ext.commands import Context
from discord import Client, Guild, ClientUser, Member
from Config.Messages import Messages
from Handlers.PlayersController import PlayersController
from Music.Player import Player
from Handlers.HandlerResponse import HandlerResponse
from Config.Configs import Configs
from Config.Helper import Helper
from Views.Embeds import Embeds
class AbstractHandler(ABC):
def __init__(self, ctx: Context, bot: Client) -> None:
self.__bot: Client = bot
self.__controller = PlayersController(self.__bot)
self.__player: Player = self.__controller.get_player(ctx.guild)
self.__guild: Guild = ctx.guild
self.__ctx: Context = ctx
self.__bot_user: ClientUser = self.__bot.user
self.__id = self.__bot_user.id
self.__messages = Messages()
self.__config = Configs()
self.__helper = Helper()
self.__embeds = Embeds()
self.__bot_member: Member = self.__get_member()
@abstractmethod
async def run(self) -> HandlerResponse:
pass
@property
def id(self) -> int:
return self.__id
@property
def bot_member(self) -> Member:
return self.__bot_member
@property
def bot_user(self) -> ClientUser:
return self.__bot_user
@property
def guild(self) -> Guild:
return self.__guild
@property
def player(self) -> Player:
return self.__player
@property
def controller(self) -> PlayersController:
return self.__controller
@property
def bot(self) -> Client:
return self.__bot
@property
def config(self) -> Configs:
return self.__config
@property
def messages(self) -> Messages:
return self.__messages
@property
def helper(self) -> Helper:
return self.__helper
@property
def ctx(self) -> Context:
return self.__ctx
@property
def embeds(self) -> Embeds:
return self.__embeds
def __get_member(self) -> Member:
guild_members: List[Member] = self.__guild.members
for member in guild_members:
if member.id == self.__id:
return member

22
Handlers/ClearHandler.py Normal file
View File

@@ -0,0 +1,22 @@
from discord.ext.commands import Context
from discord import Client
from Handlers.AbstractHandler import AbstractHandler
from Handlers.HandlerResponse import HandlerResponse
from Parallelism.ProcessManager import ProcessManager
class ClearHandler(AbstractHandler):
def __init__(self, ctx: Context, bot: Client) -> None:
super().__init__(ctx, bot)
async def run(self) -> HandlerResponse:
# Get the current process of the guild
processManager = ProcessManager()
processContext = processManager.getRunningPlayerContext(self.guild)
if processContext:
# Clear the playlist
playlist = processContext.getPlaylist()
with processContext.getLock():
playlist.clear()
return HandlerResponse(self.ctx)

View File

@@ -0,0 +1,27 @@
from typing import Union
from discord.ext.commands import Context
from Config.Exceptions import VulkanError
from discord import Embed
class HandlerResponse:
def __init__(self, ctx: Context, embed: Embed = None, error: VulkanError = None) -> None:
self.__ctx: Context = ctx
self.__error: VulkanError = error
self.__embed: Embed = embed
self.__success = False if error else True
@property
def ctx(self) -> Context:
return self.__ctx
@property
def embed(self) -> Union[Embed, None]:
return self.__embed
def error(self) -> Union[VulkanError, None]:
return self.__error
@property
def success(self) -> bool:
return self.__success

View File

@@ -0,0 +1,24 @@
from discord.ext.commands import Context
from discord import Client
from Handlers.AbstractHandler import AbstractHandler
from Handlers.HandlerResponse import HandlerResponse
from Utils.Utils import Utils
class HistoryHandler(AbstractHandler):
def __init__(self, ctx: Context, bot: Client) -> None:
super().__init__(ctx, bot)
async def run(self) -> HandlerResponse:
history = self.player.playlist.getSongsHistory()
if len(history) == 0:
text = self.messages.HISTORY_EMPTY
else:
text = f'\n📜 History Length: {len(history)} | Max: {self.config.MAX_SONGS_HISTORY}\n'
for pos, song in enumerate(history, start=1):
text += f"**`{pos}` - ** {song.title} - `{Utils.format_time(song.duration)}`\n"
embed = self.embeds.HISTORY(text)
return HandlerResponse(self.ctx, embed)

39
Handlers/LoopHandler.py Normal file
View File

@@ -0,0 +1,39 @@
from discord.ext.commands import Context
from discord import Client
from Handlers.AbstractHandler import AbstractHandler
from Handlers.HandlerResponse import HandlerResponse
from Config.Exceptions import BadCommandUsage
class LoopHandler(AbstractHandler):
def __init__(self, ctx: Context, bot: Client) -> None:
super().__init__(ctx, bot)
async def run(self, args: str) -> HandlerResponse:
if args == '' or args is None:
self.player.playlist.loop_all()
embed = self.embeds.LOOP_ALL_ACTIVATED()
return HandlerResponse(self.ctx, embed)
args = args.lower()
if self.player.playlist.getCurrentSong() is None:
embed = self.embeds.NOT_PLAYING()
error = BadCommandUsage()
return HandlerResponse(self.ctx, embed, error)
if args == 'one':
self.player.playlist.loop_one()
embed = self.embeds.LOOP_ONE_ACTIVATED()
return HandlerResponse(self.ctx, embed)
elif args == 'all':
self.player.playlist.loop_all()
embed = self.embeds.LOOP_ALL_ACTIVATED()
return HandlerResponse(self.ctx, embed)
elif args == 'off':
self.player.playlist.loop_off()
embed = self.embeds.LOOP_DISABLE()
return HandlerResponse(self.ctx, embed)
else:
error = BadCommandUsage()
embed = self.embeds.BAD_LOOP_USE()
return HandlerResponse(self.ctx, embed, error)

63
Handlers/MoveHandler.py Normal file
View File

@@ -0,0 +1,63 @@
from typing import Union
from discord.ext.commands import Context
from discord import Client
from Handlers.AbstractHandler import AbstractHandler
from Handlers.HandlerResponse import HandlerResponse
from Config.Exceptions import BadCommandUsage, VulkanError, InvalidInput, NumberRequired, UnknownError
from Music.Playlist import Playlist
from Parallelism.ProcessManager import ProcessManager
class MoveHandler(AbstractHandler):
def __init__(self, ctx: Context, bot: Client) -> None:
super().__init__(ctx, bot)
async def run(self, pos1: str, pos2: str) -> HandlerResponse:
processManager = ProcessManager()
processContext = processManager.getRunningPlayerContext(self.guild)
if not processContext:
embed = self.embeds.NOT_PLAYING()
error = BadCommandUsage()
return HandlerResponse(self.ctx, embed, error)
with processContext.getLock():
error = self.__validateInput(pos1, pos2)
if error:
embed = self.embeds.ERROR_EMBED(error.message)
return HandlerResponse(self.ctx, embed, error)
playlist = processContext.getPlaylist()
pos1, pos2 = self.__sanitizeInput(playlist, pos1, pos2)
if not playlist.validate_position(pos1) or not playlist.validate_position(pos2):
error = InvalidInput()
embed = self.embeds.PLAYLIST_RANGE_ERROR()
return HandlerResponse(self.ctx, embed, error)
try:
song = playlist.move_songs(pos1, pos2)
song_name = song.title if song.title else song.identifier
embed = self.embeds.SONG_MOVED(song_name, pos1, pos2)
return HandlerResponse(self.ctx, embed)
except:
embed = self.embeds.ERROR_MOVING()
error = UnknownError()
return HandlerResponse(self.ctx, embed, error)
def __validateInput(self, pos1: str, pos2: str) -> Union[VulkanError, None]:
try:
pos1 = int(pos1)
pos2 = int(pos2)
except:
return NumberRequired(self.messages.ERROR_NUMBER)
def __sanitizeInput(self, playlist: Playlist, pos1: int, pos2: int) -> tuple:
pos1 = int(pos1)
pos2 = int(pos2)
if pos1 == -1:
pos1 = len(playlist.getSongs())
if pos2 == -1:
pos2 = len(playlist.getSongs())
return pos1, pos2

View File

@@ -0,0 +1,26 @@
from discord.ext.commands import Context
from discord import Client
from Handlers.AbstractHandler import AbstractHandler
from Handlers.HandlerResponse import HandlerResponse
from Utils.Cleaner import Cleaner
class NowPlayingHandler(AbstractHandler):
def __init__(self, ctx: Context, bot: Client) -> None:
super().__init__(ctx, bot)
self.__cleaner = Cleaner()
async def run(self) -> HandlerResponse:
if not self.player.playing:
embed = self.embeds.NOT_PLAYING()
return HandlerResponse(self.ctx, embed)
if self.player.playlist.isLoopingOne():
title = self.messages.ONE_SONG_LOOPING
else:
title = self.messages.SONG_PLAYING
await self.__cleaner.clean_messages(self.ctx, self.config.CLEANER_MESSAGES_QUANT)
info = self.player.playlist.getCurrentSong().info
embed = self.embeds.SONG_INFO(info, title)
return HandlerResponse(self.ctx, embed)

22
Handlers/PauseHandler.py Normal file
View File

@@ -0,0 +1,22 @@
from discord.ext.commands import Context
from discord import Client
from Handlers.AbstractHandler import AbstractHandler
from Handlers.HandlerResponse import HandlerResponse
from Parallelism.ProcessManager import ProcessManager
from Parallelism.Commands import VCommands, VCommandsType
class PauseHandler(AbstractHandler):
def __init__(self, ctx: Context, bot: Client) -> None:
super().__init__(ctx, bot)
async def run(self) -> HandlerResponse:
processManager = ProcessManager()
processContext = processManager.getRunningPlayerContext(self.guild)
if processContext:
# Send Pause command to be execute by player process
command = VCommands(VCommandsType.PAUSE, None)
queue = processContext.getQueue()
queue.put(command)
return HandlerResponse(self.ctx)

96
Handlers/PlayHandler.py Normal file
View File

@@ -0,0 +1,96 @@
from Config.Exceptions import DownloadingError, InvalidInput, VulkanError
from discord.ext.commands import Context
from discord import Client
from Handlers.AbstractHandler import AbstractHandler
from Config.Exceptions import ImpossibleMove, UnknownError
from Handlers.HandlerResponse import HandlerResponse
from Music.Downloader import Downloader
from Music.Searcher import Searcher
from Music.Song import Song
from Parallelism.ProcessManager import ProcessManager
from Parallelism.Commands import VCommands, VCommandsType
class PlayHandler(AbstractHandler):
def __init__(self, ctx: Context, bot: Client) -> None:
super().__init__(ctx, bot)
self.__searcher = Searcher()
self.__down = Downloader()
async def run(self, args: str) -> HandlerResponse:
track = " ".join(args)
requester = self.ctx.author.name
if not self.__isUserConnected():
error = ImpossibleMove()
embed = self.embeds.NO_CHANNEL()
return HandlerResponse(self.ctx, embed, error)
try:
musics = await self.__searcher.search(track)
if musics is None or len(musics) == 0:
raise InvalidInput(self.messages.INVALID_INPUT, self.messages.ERROR_TITLE)
for music in musics:
song = Song(music, self.player.playlist, requester)
self.player.playlist.add_song(song)
quant = len(musics)
songs_preload = self.player.playlist.getSongsToPreload()
await self.__down.preload(songs_preload)
if quant == 1:
pos = len(self.player.playlist)
song = self.__down.finish_one_song(song)
if song.problematic:
embed = self.embeds.SONG_PROBLEMATIC()
error = DownloadingError()
response = HandlerResponse(self.ctx, embed, error)
elif not self.player.playing:
embed = self.embeds.SONG_ADDED(song.title)
response = HandlerResponse(self.ctx, embed)
else:
embed = self.embeds.SONG_ADDED_TWO(song.info, pos)
response = HandlerResponse(self.ctx, embed)
else:
embed = self.embeds.SONGS_ADDED(quant)
response = HandlerResponse(self.ctx, embed)
# Get the process context for the current guild
manager = ProcessManager(self.bot)
processContext = manager.getPlayerContext(self.guild, self.ctx)
# Add the downloaded song to the process playlist
# All access to shared memory should be protect by acquire the Lock
with processContext.getLock():
processContext.getPlaylist().add_song(song)
# If process already started send a command to the player process by queue
process = processContext.getProcess()
queue = processContext.getQueue()
if process.is_alive():
command = VCommands(VCommandsType.PLAY)
queue.put(command)
else:
# Start the process
process.start()
return response
except Exception as err:
if isinstance(err, VulkanError): # If error was already processed
print(f'DEVELOPER NOTE -> PlayController Error: {err.message}')
error = err
embed = self.embeds.CUSTOM_ERROR(error)
else:
print(f'DEVELOPER NOTE -> PlayController Error: {err}')
error = UnknownError()
embed = self.embeds.UNKNOWN_ERROR()
return HandlerResponse(self.ctx, embed, error)
def __isUserConnected(self) -> bool:
if self.ctx.author.voice:
return True
else:
return False

View File

@@ -0,0 +1,56 @@
from typing import Dict, List, Union
from Config.Singleton import Singleton
from discord import Guild, Client, VoiceClient, Member
from Music.Player import Player
class PlayersController(Singleton):
def __init__(self, bot: Client = None) -> None:
if not super().created:
self.__bot: Client = bot
if bot is not None:
self.__players: Dict[Guild, Player] = self.__create_players()
def set_bot(self, bot: Client) -> None:
self.__bot: Client = bot
self.__players: Dict[Guild, Player] = self.__create_players()
def get_player(self, guild: Guild) -> Player:
if guild not in self.__players.keys():
player = Player(self.__bot, guild)
self.__players[guild] = player
return self.__players[guild]
def reset_player(self, guild: Guild) -> None:
if isinstance(guild, Guild):
player = Player(self.__bot, guild)
self.__players[guild] == player
def get_guild_voice(self, guild: Guild) -> Union[VoiceClient, None]:
if guild.voice_client is None:
return None
else:
return guild.voice_client
def create_player(self, guild: Guild) -> None:
player = Player(self.__bot, guild)
self.__players[guild] = player
print(f'Player for guild {guild.name} created')
def __create_players(self) -> Dict[Guild, Player]:
list_guilds: List[Guild] = self.__bot.guilds
players: Dict[Guild, Player] = {}
for guild in list_guilds:
player = Player(self.__bot, guild)
players[guild] = player
print(f'Player for guild {guild.name} created')
return players
def __get_guild_bot_member(self, guild: Guild) -> Member:
members: List[Member] = guild.members
for member in members:
if member.id == self.__bot.user.id:
return member

60
Handlers/PrevHandler.py Normal file
View File

@@ -0,0 +1,60 @@
from discord.ext.commands import Context
from discord import Client
from Handlers.AbstractHandler import AbstractHandler
from Config.Exceptions import BadCommandUsage, ImpossibleMove, UnknownError
from Handlers.HandlerResponse import HandlerResponse
class PrevHandler(AbstractHandler):
def __init__(self, ctx: Context, bot: Client) -> None:
super().__init__(ctx, bot)
async def run(self) -> HandlerResponse:
if len(self.player.playlist.history()) == 0:
error = ImpossibleMove()
embed = self.embeds.NOT_PREVIOUS_SONG()
return HandlerResponse(self.ctx, embed, error)
if not self.__user_connected():
error = ImpossibleMove()
embed = self.embeds.NO_CHANNEL()
return HandlerResponse(self.ctx, embed, error)
if not self.__is_connected():
success = await self.__connect()
if not success:
error = UnknownError()
embed = self.embeds.UNKNOWN_ERROR()
return HandlerResponse(self.ctx, embed, error)
if self.player.playlist.isLoopingAll() or self.player.playlist.isLoopingOne():
error = BadCommandUsage()
embed = self.embeds.FAIL_DUE_TO_LOOP_ON()
return HandlerResponse(self.ctx, embed, error)
await self.player.play_prev(self.ctx)
def __user_connected(self) -> bool:
if self.ctx.author.voice:
return True
else:
return False
def __is_connected(self) -> bool:
try:
voice_channel = self.guild.voice_client.channel
if not self.guild.voice_client.is_connected():
return False
else:
return True
except:
return False
async def __connect(self) -> bool:
# if self.guild.voice_client is None:
try:
await self.ctx.author.voice.channel.connect(reconnect=True, timeout=None)
return True
except:
return False

56
Handlers/QueueHandler.py Normal file
View File

@@ -0,0 +1,56 @@
import asyncio
from discord.ext.commands import Context
from discord import Client
from Handlers.AbstractHandler import AbstractHandler
from Handlers.HandlerResponse import HandlerResponse
from Music.Downloader import Downloader
from Utils.Utils import Utils
from Parallelism.ProcessManager import ProcessManager
class QueueHandler(AbstractHandler):
def __init__(self, ctx: Context, bot: Client) -> None:
super().__init__(ctx, bot)
self.__down = Downloader()
async def run(self) -> HandlerResponse:
# Retrieve the process of the guild
process = ProcessManager()
processContext = process.getRunningPlayerContext(self.guild)
if not processContext: # If no process return empty list
embed = self.embeds.EMPTY_QUEUE()
return HandlerResponse(self.ctx, embed)
# Acquire the Lock to manipulate the playlist
with processContext.getLock():
playlist = processContext.getPlaylist()
if playlist.isLoopingOne():
song = playlist.getCurrentSong()
embed = self.embeds.ONE_SONG_LOOPING(song.info)
return HandlerResponse(self.ctx, embed)
songs_preload = playlist.getSongsToPreload()
if len(songs_preload) == 0:
embed = self.embeds.EMPTY_QUEUE()
return HandlerResponse(self.ctx, embed)
asyncio.create_task(self.__down.preload(songs_preload))
if playlist.isLoopingAll():
title = self.messages.ALL_SONGS_LOOPING
else:
title = self.messages.QUEUE_TITLE
total_time = Utils.format_time(sum([int(song.duration if song.duration else 0)
for song in songs_preload]))
total_songs = len(playlist.getSongs())
text = f'📜 Queue length: {total_songs} | ⌛ Duration: `{total_time}` downloaded \n\n'
for pos, song in enumerate(songs_preload, start=1):
song_name = song.title if song.title else self.messages.SONG_DOWNLOADING
text += f"**`{pos}` - ** {song_name} - `{Utils.format_time(song.duration)}`\n"
embed = self.embeds.QUEUE(title, text)
return HandlerResponse(self.ctx, embed)

52
Handlers/RemoveHandler.py Normal file
View File

@@ -0,0 +1,52 @@
from typing import Union
from discord.ext.commands import Context
from discord import Client
from Handlers.AbstractHandler import AbstractHandler
from Handlers.HandlerResponse import HandlerResponse
from Config.Exceptions import BadCommandUsage, VulkanError, ErrorRemoving, InvalidInput, NumberRequired
class RemoveHandler(AbstractHandler):
def __init__(self, ctx: Context, bot: Client) -> None:
super().__init__(ctx, bot)
async def run(self, position: str) -> HandlerResponse:
if not self.player.playlist:
embed = self.embeds.NOT_PLAYING()
error = BadCommandUsage()
return HandlerResponse(self.ctx, embed, error)
error = self.__validate_input(position)
if error:
embed = self.embeds.ERROR_EMBED(error.message)
return HandlerResponse(self.ctx, embed, error)
position = self.__sanitize_input(position)
if not self.player.playlist.validate_position(position):
error = InvalidInput()
embed = self.embeds.PLAYLIST_RANGE_ERROR()
return HandlerResponse(self.ctx, embed, error)
try:
song = self.player.playlist.remove_song(position)
name = song.title if song.title else song.identifier
embed = self.embeds.SONG_REMOVED(name)
return HandlerResponse(self.ctx, embed)
except:
error = ErrorRemoving()
embed = self.embeds.ERROR_REMOVING()
return HandlerResponse(self.ctx, embed, error)
def __validate_input(self, position: str) -> Union[VulkanError, None]:
try:
position = int(position)
except:
return NumberRequired(self.messages.ERROR_NUMBER)
def __sanitize_input(self, position: str) -> int:
position = int(position)
if position == -1:
position = len(self.player.playlist)
return position

20
Handlers/ResetHandler.py Normal file
View File

@@ -0,0 +1,20 @@
from discord.ext.commands import Context
from discord import Client
from Handlers.AbstractHandler import AbstractHandler
from Handlers.HandlerResponse import HandlerResponse
from Handlers.PlayersController import PlayersController
class ResetHandler(AbstractHandler):
def __init__(self, ctx: Context, bot: Client) -> None:
super().__init__(ctx, bot)
self.__controller = PlayersController(self.bot)
async def run(self) -> HandlerResponse:
try:
await self.player.force_stop()
await self.bot_member.move_to(None)
self.__controller.reset_player(self.guild)
return HandlerResponse(self.ctx)
except Exception as e:
print(f'DEVELOPER NOTE -> Reset Error: {e}')

22
Handlers/ResumeHandler.py Normal file
View File

@@ -0,0 +1,22 @@
from discord.ext.commands import Context
from discord import Client
from Handlers.AbstractHandler import AbstractHandler
from Handlers.HandlerResponse import HandlerResponse
from Parallelism.ProcessManager import ProcessManager
from Parallelism.Commands import VCommands, VCommandsType
class ResumeHandler(AbstractHandler):
def __init__(self, ctx: Context, bot: Client) -> None:
super().__init__(ctx, bot)
async def run(self) -> HandlerResponse:
processManager = ProcessManager()
processContext = processManager.getRunningPlayerContext(self.guild)
if processContext:
# Send Resume command to be execute by player process
command = VCommands(VCommandsType.RESUME, None)
queue = processContext.getQueue()
queue.put(command)
return HandlerResponse(self.ctx)

View File

@@ -0,0 +1,27 @@
import asyncio
from discord.ext.commands import Context
from discord import Client
from Handlers.AbstractHandler import AbstractHandler
from Handlers.HandlerResponse import HandlerResponse
from Config.Exceptions import UnknownError
from Music.Downloader import Downloader
class ShuffleHandler(AbstractHandler):
def __init__(self, ctx: Context, bot: Client) -> None:
super().__init__(ctx, bot)
self.__down = Downloader()
async def run(self) -> HandlerResponse:
try:
self.player.playlist.shuffle()
songs = self.player.playlist.getSongsToPreload()
asyncio.create_task(self.__down.preload(songs))
embed = self.embeds.SONGS_SHUFFLED()
return HandlerResponse(self.ctx, embed)
except Exception as e:
print(f'DEVELOPER NOTE -> Error Shuffling: {e}')
error = UnknownError()
embed = self.embeds.ERROR_SHUFFLING()
return HandlerResponse(self.ctx, embed, error)

28
Handlers/SkipHandler.py Normal file
View File

@@ -0,0 +1,28 @@
from discord.ext.commands import Context
from discord import Client
from Handlers.AbstractHandler import AbstractHandler
from Config.Exceptions import BadCommandUsage
from Handlers.HandlerResponse import HandlerResponse
from Parallelism.ProcessManager import ProcessManager
from Parallelism.Commands import VCommands, VCommandsType
class SkipHandler(AbstractHandler):
def __init__(self, ctx: Context, bot: Client) -> None:
super().__init__(ctx, bot)
async def run(self) -> HandlerResponse:
processManager = ProcessManager()
processContext = processManager.getRunningPlayerContext(self.guild)
if processContext: # Verify if there is a running process
playlist = processContext.getPlaylist()
if playlist.isLoopingOne():
embed = self.embeds.ERROR_DUE_LOOP_ONE_ON()
error = BadCommandUsage()
return HandlerResponse(self.ctx, embed, error)
# Send a command to the player process to skip the music
command = VCommands(VCommandsType.SKIP, None)
queue = processContext.getQueue()
queue.put(command)
return HandlerResponse(self.ctx)

22
Handlers/StopHandler.py Normal file
View File

@@ -0,0 +1,22 @@
from discord.ext.commands import Context
from discord import Client
from Handlers.AbstractHandler import AbstractHandler
from Handlers.HandlerResponse import HandlerResponse
from Parallelism.ProcessManager import ProcessManager
from Parallelism.Commands import VCommands, VCommandsType
class StopHandler(AbstractHandler):
def __init__(self, ctx: Context, bot: Client) -> None:
super().__init__(ctx, bot)
async def run(self) -> HandlerResponse:
processManager = ProcessManager()
processContext = processManager.getRunningPlayerContext(self.guild)
if processContext:
# Send command to player process stop
command = VCommands(VCommandsType.STOP, None)
queue = processContext.getQueue()
queue.put(command)
return HandlerResponse(self.ctx)