Changing folders names and adding more concrete classes of controllers and viewers

This commit is contained in:
Rafael Vargas 2022-03-22 17:39:09 -04:00
parent 14705569c1
commit 2240c7535a
27 changed files with 488 additions and 188 deletions

34
config/Colors.py Normal file
View File

@ -0,0 +1,34 @@
from Config.Singleton import Singleton
class Colors(Singleton):
def __init__(self) -> None:
self.__red = 0xDC143C
self.__green = 0x1F8B4C
self.__grey = 0x708090
self.__blue = 0x206694
self.__black = 0x23272A
@property
def RED(self) -> str:
return self.__red
@property
def GREEN(self) -> str:
return self.__green
@property
def GREY(self) -> str:
return self.__grey
@property
def BLUE(self) -> str:
return self.__blue
@property
def BLACK(self) -> str:
return self.__black
@property
def RED(self) -> str:
return self.__red

View File

@ -1,5 +1,5 @@
from config.Singleton import Singleton
from config.Config import Config
from Config.Singleton import Singleton
from Config.Config import Config
class Helper(Singleton):

View File

@ -1,5 +1,5 @@
from decouple import config
from config.Singleton import Singleton
from Config.Singleton import Singleton
class Config(Singleton):
@ -77,11 +77,13 @@ class Config(Singleton):
self.MY_ERROR_BAD_COMMAND = 'This string serves to verify if some error was raised by myself on purpose'
self.BAD_COMMAND_TITLE = 'Misuse of command'
self.BAD_COMMAND = f'❌ Bad usage of this command, type {self.BOT_PREFIX}help "command" to understand the command better'
self.INVITE_URL = 'https://discordapp.com/oauth2/authorize?client_id={}&scope=bot>'
self.VIDEO_UNAVAILABLE = '❌ Sorry. This video is unavailable for download.'
self.COLOURS = {
'red': 0xDC143C,
'green': 0x1F8B4C,
'grey': 0x708090,
'blue': 0x206694,
'black': 0x23272A
}
self.COLOURS = {
'red': 0xDC143C,
'green': 0x1F8B4C,
'grey': 0x708090,
'blue': 0x206694,
'black': 0x23272A
}

View File

@ -0,0 +1,39 @@
from config.Config import Config
class Error(Exception):
def __init__(self, message='', title='', *args: object) -> None:
self.__message = message
self.__title = title
super().__init__(*args)
@property
def message(self) -> str:
return self.__message
@property
def title(self) -> str:
return self.__title
class ImpossibleMove(Error):
def __init__(self, message='', title='', *args: object) -> None:
config = Config()
if title == '':
title = config.IMPOSSIBLE_MOVE
super().__init__(message, title, *args)
class MusicUnavailable(Error):
def __init__(self, message='', title='', *args: object) -> None:
super().__init__(message, title, *args)
class BadCommandUsage(Error):
def __init__(self, message='', title='', *args: object) -> None:
super().__init__(message, title, *args)
class UnknownError(Error):
def __init__(self, message='', title='', *args: object) -> None:
super().__init__(message, title, *args)

View File

@ -2,8 +2,8 @@ import discord
from discord import Client
from discord.ext.commands.errors import CommandNotFound, MissingRequiredArgument, UserInputError
from discord.ext import commands
from config.Config import Config
from config.Helper import Helper
from Config.Config import Config
from Config.Helper import Helper
helper = Helper()

View File

@ -2,11 +2,14 @@ from typing import Dict
from discord import Guild, Client, Embed
from discord.ext import commands
from discord.ext.commands import Context
from config.Config import Config
from config.Helper import Helper
from vulkan.music.Player import Player
from vulkan.music.utils import is_connected
from vulkan.controllers.SkipController import SkipHandler
from Config.Config import Config
from Config.Helper import Helper
from Vulkan.Music.Player import Player
from Vulkan.Music.utils import is_connected
from Vulkan.Controllers.SkipController import SkipController
from Vulkan.Views.EmoteView import EmoteView
from Vulkan.Views.EmbedView import EmbedView
helper = Helper()
@ -69,8 +72,15 @@ class Music(commands.Cog):
@commands.command(name="skip", help=helper.HELP_SKIP, description=helper.HELP_SKIP_LONG, aliases=['s', 'pular'])
async def skip(self, ctx: Context) -> None:
handler = SkipHandler(ctx, self.__bot)
await handler.run()
controller = SkipController(ctx, self.__bot)
response = await controller.run()
if response.success:
view = EmoteView(response)
else:
view = EmbedView(response)
await view.run()
@commands.command(name='stop', help=helper.HELP_STOP, description=helper.HELP_STOP_LONG, aliases=['parar'])
async def stop(self, ctx: Context) -> None:

View File

@ -1,8 +1,8 @@
from random import randint, random
import discord
from discord.ext import commands
from config.Config import Config
from config.Helper import Helper
from Config.Config import Config
from Config.Helper import Helper
helper = Helper()

View File

@ -0,0 +1,57 @@
from abc import ABC, abstractmethod
from discord.ext.commands import Context
from discord import Client, Guild
from Vulkan.Controllers.PlayerController import PlayersController
from Vulkan.Music.Player import Player
from Vulkan.Controllers.ControllerResponse import ControllerResponse
from Config.Config import Config
from Config.Helper import Helper
from Vulkan.Views.Embeds.Embeds import Embeds
class AbstractController(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.__config = Config()
self.__helper = Helper()
self.__embeds = Embeds()
@abstractmethod
async def run(self) -> ControllerResponse:
pass
@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) -> Config:
return self.__config
@property
def helper(self) -> Helper:
return self.__helper
@property
def ctx(self) -> Context:
return self.__ctx
@property
def embeds(self) -> Embeds:
return self.__embeds

View File

@ -1,34 +0,0 @@
from abc import ABC, abstractmethod
from discord.ext.commands import Context
from discord import Client, Guild
from vulkan.controllers.PlayerController import PlayersController
from vulkan.music.Player import Player
from vulkan.results.AbstractResult import AbstractResult
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
@abstractmethod
async def run(self) -> AbstractResult:
pass
@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

View File

@ -0,0 +1,27 @@
from typing import Union
from discord.ext.commands import Context
from Vulkan.Exceptions.Exceptions import Error
from discord import Embed
class ControllerResponse:
def __init__(self, ctx: Context, embed: Embed = None, error: Error = None) -> None:
self.__ctx: Context = ctx
self.__error: Error = 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[Error, None]:
return self.__error
@property
def success(self) -> bool:
return self.__success

View File

@ -1,26 +0,0 @@
from abc import ABC, abstractmethod
class Error(Exception, ABC):
@abstractmethod
def message():
pass
@abstractmethod
def title():
pass
class MusicUnavailable(Error):
def __init__(self, message: str) -> None:
self.__message = message
super().__init__(message)
def message():
pass
def title():
pass
def __str__(self) -> str:
return self.__message

View File

@ -1,7 +1,7 @@
from typing import Dict, List, Union
from config.Singleton import Singleton
from Config.Singleton import Singleton
from discord import Guild, Client, VoiceClient
from vulkan.music.Player import Player
from Vulkan.Music.Player import Player
class PlayersController(Singleton):

View File

@ -1,26 +1,26 @@
from discord.ext.commands import Context
from discord import Client
from vulkan.controllers.AbstractHandler import AbstractHandler
from Vulkan.Controllers.AbstractController import AbstractController
from Vulkan.Exceptions.Exceptions import BadCommandUsage
from Vulkan.Controllers.ControllerResponse import ControllerResponse
class SkipHandler(AbstractHandler):
class SkipController(AbstractController):
def __init__(self, ctx: Context, bot: Client) -> None:
super().__init__(ctx, bot)
async def run(self) -> None:
async def run(self) -> ControllerResponse:
if self.player.playlist.looping_one:
""" embed = Embed(
title=config.SONG_PLAYER,
description=config.LOOP_ON,
colour=config.COLOURS['blue']
)
await ctx.send(embed=embed)
return False
"""
return None
embed = self.__embeds.FAIL_DUE_TO_LOOP_ON
error = BadCommandUsage('', '')
response = ControllerResponse(self.ctx, embed, error)
return response
voice = self.controller.get_guild_voice(self.guild)
if voice is None:
return None
response = ControllerResponse(self.ctx)
return response
else:
voice.stop()
response = ControllerResponse(self.ctx)
return response

View File

@ -1,10 +1,10 @@
import asyncio
from typing import List
from config.Config import Config
from Config.Config import Config
from yt_dlp import YoutubeDL
from concurrent.futures import ThreadPoolExecutor
from vulkan.music.Song import Song
from vulkan.music.utils import is_url, run_async
from Vulkan.Music.Song import Song
from Vulkan.Music.utils import is_url, run_async
class Downloader():

View File

@ -1,14 +1,14 @@
from discord.ext import commands
from config.Config import Config
from Config.Config import Config
from discord import Client, Guild, FFmpegPCMAudio, Embed
from discord.ext.commands import Context
from datetime import timedelta
from vulkan.music.Downloader import Downloader
from vulkan.music.Playlist import Playlist
from vulkan.music.Searcher import Searcher
from vulkan.music.Song import Song
from vulkan.music.Types import Provider
from vulkan.music.utils import *
from Vulkan.Music.Downloader import Downloader
from Vulkan.Music.Playlist import Playlist
from Vulkan.Music.Searcher import Searcher
from Vulkan.Music.Song import Song
from Vulkan.Music.Types import Provider
from Vulkan.Music.utils import *
class Player(commands.Cog):
@ -21,7 +21,7 @@ class Player(commands.Cog):
self.__timer = Timer(self.__timeout_handler)
self.__playing = False
self.__config = Config.Config()
self.__config = Config()
# Flag to control if the player should stop totally the playing
self.__force_stop = False
@ -210,22 +210,6 @@ class Player(commands.Cog):
return embed
async def skip(self, ctx: Context) -> bool:
if self.__playlist.looping_one:
embed = Embed(
title=self.__config.SONG_PLAYER,
description=self.__config.LOOP_ON,
colour=self.__config.COLOURS['blue']
)
await ctx.send(embed=embed)
return False
if self.__guild.voice_client != None:
self.__guild.voice_client.stop()
return True
else:
return False
def history(self) -> Embed:
history = self.__playlist.songs_history

View File

@ -1,11 +1,10 @@
from collections import deque
from typing import List
from config.Config import Config
from Config.Config import Config
from Vulkan.Music.Interfaces import IPlaylist
from Vulkan.Music.Song import Song
import random
from vulkan.music.Interfaces import IPlaylist
from vulkan.music.Song import Song
class Playlist(IPlaylist):
"""Class to manage and control the songs to play and played"""

View File

@ -1,6 +1,6 @@
from vulkan.music.Types import Provider
from vulkan.music.Spotify import SpotifySearch
from vulkan.music.utils import is_url
from Vulkan.Music.Types import Provider
from Vulkan.Music.Spotify import SpotifySearch
from Vulkan.Music.utils import is_url
class Searcher():
@ -16,6 +16,7 @@ class Searcher():
Return -> A list of musics names and Provider Type
"""
provider = self.__identify_source(music)
print(provider)
if provider == Provider.YouTube:
return [music], Provider.YouTube
@ -25,6 +26,7 @@ class Searcher():
musics = self.__Spotify.search(music)
return musics, Provider.Name
else:
print('Spotify Not Connected')
return [], Provider.Unknown
elif provider == Provider.Name:

View File

@ -1,4 +1,4 @@
from vulkan.music.Interfaces import ISong, IPlaylist
from Vulkan.Music.Interfaces import ISong, IPlaylist
class Song(ISong):

View File

@ -1,15 +1,15 @@
import spotipy
from spotipy.oauth2 import SpotifyClientCredentials
from config.Config import Config
from Config.Config import Config
class SpotifySearch():
"""Search a Spotify music or playlist and return the musics names"""
def __init__(self) -> None:
self.__config = Config()
self.__connected = False
self.__connect()
self.__config = Config()
@property
def connected(self):
@ -19,10 +19,11 @@ class SpotifySearch():
try:
# Initialize the connection with Spotify API
self.__api = spotipy.Spotify(auth_manager=SpotifyClientCredentials(
client_id=Config.SPOTIFY_ID, client_secret=self.__config.SPOTIFY_SECRET))
client_id=self.__config.SPOTIFY_ID, client_secret=self.__config.SPOTIFY_SECRET))
self.__connected = True
return True
except:
except Exception as e:
print(f'DEVELOPER NOTE -> Spotify Connection {e}')
return False
def search(self, music=str) -> list:

View File

@ -1,6 +1,6 @@
import re
import asyncio
from config.Config import Config
from Config.Config import Config
from functools import wraps, partial
config = Config()

View File

@ -1,20 +0,0 @@
from typing import Union
from discord.ext.commands import Context
from vulkan.controllers.Exceptions import Error
class AbstractResult:
def __init__(self, ctx: Context, success: bool) -> None:
self.__success: bool = success
self.__ctx: Context = ctx
@property
def ctx(self) -> Context:
return self.__ctx
@property
def success(self) -> bool:
return self.__success
def error(self) -> Union[Error, None]:
pass

View File

@ -1,9 +0,0 @@
from vulkan.results import AbstractResult
from typing import Union
from discord.ext.commands import Context
from vulkan.controllers.Exceptions import Error
class SuccessResult(AbstractResult):
def __init__(self) -> None:
super().__init__()

View File

@ -1,25 +1,23 @@
from abc import ABC, abstractmethod
from numpy import result_type
from vulkan.results.AbstractResult import AbstractResult
from Vulkan.Controllers.ControllerResponse import ControllerResponse
from discord.ext.commands import Context
from discord import Client, Message
class AbstractView(ABC):
def __init__(self, result: AbstractResult) -> None:
self.__result: AbstractResult = result
self.__context: Context = result.ctx
self.__message: Message = result.ctx.message
self.__bot: Client = result.ctx.bot
def __init__(self, response: ControllerResponse) -> None:
self.__response: ControllerResponse = response
self.__context: Context = response.ctx
self.__message: Message = response.ctx.message
self.__bot: Client = response.ctx.bot
@property
def result(self) -> AbstractResult:
return self.__result
def response(self) -> ControllerResponse:
return self.__response
@property
def bot(self) -> Client:
return self.__result.ctx.bot
return self.__bot
@property
def message(self) -> Message:
@ -30,5 +28,5 @@ class AbstractView(ABC):
return self.__context
@abstractmethod
def run(self) -> None:
async def run(self) -> None:
pass

11
vulkan/views/EmbedView.py Normal file
View File

@ -0,0 +1,11 @@
from Vulkan.Views.AbstractView import AbstractView
from Vulkan.Controllers.ControllerResponse import ControllerResponse
class EmbedView(AbstractView):
def __init__(self, response: ControllerResponse) -> None:
super().__init__(response)
async def run(self) -> None:
if self.response.embed:
await self.context.send(embed=self.response.embed)

View File

@ -0,0 +1,221 @@
from discord import Embed
from Config.Config import Config
from Config.Colors import Colors
from datetime import timedelta
class Embeds:
def __init__(self) -> None:
self.__config = Config()
self.__colors = Colors()
@property
def INVALID_INPUT(self) -> Embed:
embed = Embed(
title=self.__config.ERROR_TITLE,
description=self.__config.INVALID_INPUT,
colours=self.__colors.BLUE)
return embed
@property
def UNAVAILABLE_VIDEO(self) -> Embed:
embed = Embed(
title=self.__config.ERROR_TITLE,
description=self.__config.VIDEO_UNAVAILABLE,
colours=self.__colors.BLUE)
return embed
@property
def DOWNLOADING_ERROR(self) -> Embed:
embed = Embed(
title=self.__config.ERROR_TITLE,
description=self.__config.DOWNLOADING_ERROR,
colours=self.__colors.BLUE)
return embed
@property
def SONG_ADDED(self, title: str) -> Embed:
embed = Embed(
title=self.__config.SONG_PLAYER,
description=self.__config.SONG_ADDED.format(title),
colours=self.__colors.BLUE)
return embed
@property
def SONGS_ADDED(self, quant: int) -> Embed:
embed = Embed(
title=self.__config.SONG_PLAYER,
description=self.__config.SONGS_ADDED.format(quant),
colour=self.__colors.BLUE)
return embed
@property
def SONG_INFO(self, info: dict, title: str, position='Playing Now') -> Embed:
embedvc = Embed(
title=title,
description=f"[{info['title']}]({info['original_url']})",
color=self.__config.COLOURS['blue']
)
embedvc.add_field(name=self.__config.SONGINFO_UPLOADER,
value=info['uploader'],
inline=False)
embedvc.add_field(name=self.__config.SONGINFO_REQUESTER,
value=info['requester'],
inline=True)
if 'thumbnail' in info.keys():
embedvc.set_thumbnail(url=info['thumbnail'])
if 'duration' in info.keys():
duration = str(timedelta(seconds=info['duration']))
embedvc.add_field(name=self.__config.SONGINFO_DURATION,
value=f"{duration}",
inline=True)
else:
embedvc.add_field(name=self.__config.SONGINFO_DURATION,
value=self.__config.SONGINFO_UNKNOWN_DURATION,
inline=True)
embedvc.add_field(name=self.__config.SONGINFO_POSITION,
value=position,
inline=True)
return embedvc
@property
def COMMAND_ERROR(self):
embed = Embed(
title=self.__config.ERROR_TITLE,
description=self.__config.ERROR_MISSING_ARGUMENTS,
colour=self.__colors.BLACK
)
return embed
@property
def COMMAND_NOT_FOUND(self) -> Embed:
embed = Embed(
title=self.__config.ERROR_TITLE,
description=self.__config.COMMAND_NOT_FOUND,
colour=self.__colors.BLACK
)
return embed
@property
def MY_ERROR_BAD_COMMAND(self) -> Embed:
embed = Embed(
title=self.__config.BAD_COMMAND_TITLE,
description=self.__config.BAD_COMMAND,
colour=self.__colors.BLACK
)
return embed
@property
def UNKNOWN_ERROR(self) -> Embed:
embed = Embed(
title=self.__config.ERROR_TITLE,
description=self.__config.UNKNOWN_ERROR,
colour=self.__colors.RED
)
return embed
@property
def FAIL_DUE_TO_LOOP_ON(self) -> Embed:
embed = Embed(
title=self.__config.SONG_PLAYER,
description=self.__config.LOOP_ON,
colour=self.__colors.BLUE
)
return embed
@property
def NOT_PREVIOUS_SONG(self) -> Embed:
embed = Embed(
title=self.__config.SONG_PLAYER,
description=self.__config.NOT_PREVIOUS,
colour=self.__colors.BLUE
)
return embed
@property
def HISTORY(self, description: str) -> Embed:
embed = Embed(
title=self.__config.HISTORY_TITLE,
description=description,
colour=self.__colors.BLUE)
return embed
@property
def NOT_PLAYING(self) -> Embed:
embed = Embed(
title=self.__config.SONG_PLAYER,
description=self.__config.PLAYER_NOT_PLAYING,
colour=self.__colors.BLUE)
return embed
@property
def QUEUE(self, title: str, description: str) -> Embed:
embed = Embed(
title=title,
description=description,
colour=self.__colors.BLUE
)
return embed
@property
def INVITE(self, bot_id: str) -> Embed:
link = self.__config.INVITE_URL
link.format(bot_id)
text = self.__config.INVITE_MESSAGE.format(link, link)
embed = Embed(
title="Invite Vulkan",
description=text,
colour=self.__colors.BLUE
)
return embed
@property
def ERROR_NUMBER(self) -> Embed:
embed = Embed(
description=self.__config.ERROR_NUMBER,
colour=self.__colors.RED
)
return embed
@property
def RANDOM_NUMBER(self, a: int, b: int, x: int) -> Embed:
embed = Embed(
title=f'Random number between [{a, b}]',
description=x,
colour=self.__colors.GREEN
)
return embed
@property
def CARA_COROA(self, result: str) -> Embed:
embed = Embed(
title='Cara Cora',
description=f'Result: {result}',
colour=self.__colors.GREEN
)
return embed
@property
def CHOSEN_THING(self, thing: str) -> Embed:
embed = Embed(
title='Choose something',
description=f'Chosen: {thing}',
colour=self.__config.COLOURS['green']
)
return embed
@property
def BAD_CHOOSE_USE(self) -> Embed:
embed = Embed(
title='Choose something',
description=f'Error: Use {self.__config.BOT_PREFIX}help choose to understand this command.',
colour=self.__colors.RED
)
return embed

View File

@ -1,10 +1,14 @@
from vulkan.views.AbstractView import AbstractView
from vulkan.results import AbstractResult
from Vulkan.Views.AbstractView import AbstractView
from Vulkan.Controllers.ControllerResponse import ControllerResponse
class EmoteView(AbstractView):
def __init__(self, result: AbstractResult) -> None:
super().__init__(result)
def run(self) -> None:
return super().run()
def __init__(self, response: ControllerResponse) -> None:
super().__init__(response)
async def run(self) -> None:
if self.response.success:
await self.message.add_reaction('')
else:
await self.message.add_reaction('')

View File

@ -1,10 +1,10 @@
from vulkan.views.AbstractView import AbstractView
from vulkan.results import AbstractResult
from Vulkan.Views.AbstractView import AbstractView
from Vulkan.Controllers.ControllerResponse import ControllerResponse
class MessageView(AbstractView):
def __init__(self, result: AbstractResult) -> None:
super().__init__(result)
def __init__(self, response: ControllerResponse) -> None:
super().__init__(response)
def run(self) -> None:
async def run(self) -> None:
return super().run()