Adding new controllers

This commit is contained in:
Rafael Vargas 2022-03-23 13:48:01 -04:00
parent a828350201
commit fd1e58211b
18 changed files with 328 additions and 133 deletions

View File

@ -1,12 +1,22 @@
from msilib.schema import ControlEvent
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 Configs
from Config.Helper import Helper
from Controllers.ClearController import ClearController
from Controllers.NowPlayingController import NowPlayingController
from Controllers.PlayerController import PlayersController
from Music.Player import Player
from Music.utils import is_connected
from Utils.Utils import is_connected
from Controllers.SkipController import SkipController
from Controllers.PauseController import PauseController
from Controllers.StopController import StopController
from Controllers.ResumeController import ResumeController
from Controllers.HistoryController import HistoryController
from Controllers.QueueController import QueueController
from Controllers.LoopController import LoopController
from Views.EmoteView import EmoteView
from Views.EmbedView import EmbedView
@ -19,16 +29,11 @@ class Music(commands.Cog):
self.__guilds: Dict[Guild, Player] = {}
self.__bot: Client = bot
self.__config = Configs()
self.__controller = PlayersController(self.__bot)
@commands.Cog.listener()
async def on_ready(self) -> None:
"""Load a player for each guild that the Bot are"""
for guild in self.__bot.guilds:
player = Player(self.__bot, guild)
await player.force_stop()
self.__guilds[guild] = player
print(f'Player for guild {guild.name} created')
self.__controller = PlayersController(self.__bot)
@commands.Cog.listener()
async def on_guild_join(self, guild: Guild) -> None:
@ -84,31 +89,35 @@ class Music(commands.Cog):
@commands.command(name='stop', help=helper.HELP_STOP, description=helper.HELP_STOP_LONG, aliases=['parar'])
async def stop(self, ctx: Context) -> None:
player = self.__get_player(ctx)
if player is None:
return
controller = StopController(ctx, self.__bot)
response = await controller.run()
if response.success:
view = EmoteView(response)
else:
await player.stop()
view = EmbedView(response)
await view.run()
@commands.command(name='pause', help=helper.HELP_PAUSE, description=helper.HELP_PAUSE_LONG, aliases=['pausar'])
async def pause(self, ctx: Context) -> None:
player = self.__get_player(ctx)
if player is None:
return
else:
success = await player.pause()
if success:
await self.__send_embed(ctx, self.__config.SONG_PLAYER, self.__config.SONG_PAUSED, 'blue')
controller = PauseController(ctx, self.__bot)
response = await controller.run()
view1 = EmoteView(response)
view2 = EmbedView(response)
await view1.run()
await view2.run()
@commands.command(name='resume', help=helper.HELP_RESUME, description=helper.HELP_RESUME_LONG, aliases=['soltar'])
async def resume(self, ctx: Context) -> None:
player = self.__get_player(ctx)
if player is None:
return
else:
success = await player.resume()
if success:
await self.__send_embed(ctx, self.__config.SONG_PLAYER, self.__config.SONG_RESUMED, 'blue')
controller = ResumeController(ctx, self.__bot)
response = await controller.run()
view1 = EmoteView(response)
view2 = EmbedView(response)
await view1.run()
await view2.run()
@commands.command(name='prev', help=helper.HELP_PREV, description=helper.HELP_PREV_LONG, aliases=['anterior'])
async def prev(self, ctx: Context) -> None:
@ -126,39 +135,39 @@ class Music(commands.Cog):
@commands.command(name='history', help=helper.HELP_HISTORY, description=helper.HELP_HISTORY_LONG, aliases=['historico'])
async def history(self, ctx: Context) -> None:
player = self.__get_player(ctx)
if player is None:
return
else:
embed = player.history()
await ctx.send(embed=embed)
controller = HistoryController(ctx, self.__bot)
response = await controller.run()
view1 = EmbedView(response)
view2 = EmoteView(response)
await view1.run()
await view2.run()
@commands.command(name='loop', help=helper.HELP_LOOP, description=helper.HELP_LOOP_LONG, aliases=['l', 'repeat'])
async def loop(self, ctx: Context, args: str) -> None:
player = self.__get_player(ctx)
if player is None:
return
else:
description = await player.loop(args)
await self.__send_embed(ctx, self.__config.SONG_PLAYER, description, 'blue')
async def loop(self, ctx: Context, args='') -> None:
controller = LoopController(ctx, self.__bot)
response = await controller.run(args)
view1 = EmoteView(response)
view2 = EmbedView(response)
await view1.run()
await view2.run()
@commands.command(name='clear', help=helper.HELP_CLEAR, description=helper.HELP_CLEAR_LONG, aliases=['c', 'limpar'])
async def clear(self, ctx: Context) -> None:
player = self.__get_player(ctx)
if player is None:
return
else:
await player.clear()
controller = ClearController(ctx, self.__bot)
response = await controller.run()
view = EmoteView(response)
await view.run()
@commands.command(name='np', help=helper.HELP_NP, description=helper.HELP_NP_LONG, aliases=['playing', 'now'])
async def now_playing(self, ctx: Context) -> None:
player = self.__get_player(ctx)
if player is None:
return
else:
embed = await player.now_playing()
await self.__clean_messages(ctx)
await ctx.send(embed=embed)
controller = NowPlayingController(ctx, self.__bot)
response = await controller.run()
view = EmbedView(response)
await view.run()
@commands.command(name='shuffle', help=helper.HELP_SHUFFLE, description=helper.HELP_SHUFFLE_LONG, aliases=['aleatorio'])
async def shuffle(self, ctx: Context) -> None:
@ -232,10 +241,7 @@ class Music(commands.Cog):
continue
def __get_player(self, ctx: Context) -> Player:
try:
return self.__guilds[ctx.guild]
except:
return None
return self.__controller.get_player(ctx.guild)
def setup(bot):

View File

@ -0,0 +1,13 @@
from discord.ext.commands import Context
from discord import Client
from Controllers.AbstractController import AbstractController
from Controllers.ControllerResponse import ControllerResponse
class ClearController(AbstractController):
def __init__(self, ctx: Context, bot: Client) -> None:
super().__init__(ctx, bot)
async def run(self) -> ControllerResponse:
self.player.playlist.clear()
return ControllerResponse(self.ctx)

View File

@ -0,0 +1,24 @@
from discord.ext.commands import Context
from discord import Client
from Controllers.AbstractController import AbstractController
from Controllers.ControllerResponse import ControllerResponse
from Utils.Utils import format_time
class HistoryController(AbstractController):
def __init__(self, ctx: Context, bot: Client) -> None:
super().__init__(ctx, bot)
async def run(self) -> ControllerResponse:
history = self.player.playlist.songs_history
if len(history) == 0:
text = self.config.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} - `{format_time(song.duration)}`\n"
embed = self.embeds.HISTORY(text)
return ControllerResponse(self.ctx, embed)

View File

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

View File

@ -0,0 +1,23 @@
from discord.ext.commands import Context
from discord import Client
from Controllers.AbstractController import AbstractController
from Controllers.ControllerResponse import ControllerResponse
class NowPlayingController(AbstractController):
def __init__(self, ctx: Context, bot: Client) -> None:
super().__init__(ctx, bot)
async def run(self) -> ControllerResponse:
if not self.player.playing:
embed = self.embeds.NOT_PLAYING()
return ControllerResponse(self.ctx, embed)
if self.player.playlist.looping_one:
title = self.config.ONE_SONG_LOOPING
else:
title = self.config.SONG_PLAYING
info = self.player.playlist.current.info
embed = self.embeds.SONG_INFO(info, title)
return ControllerResponse(self.ctx, embed)

View File

@ -0,0 +1,16 @@
from discord.ext.commands import Context
from discord import Client
from Controllers.AbstractController import AbstractController
from Controllers.ControllerResponse import ControllerResponse
class PauseController(AbstractController):
def __init__(self, ctx: Context, bot: Client) -> None:
super().__init__(ctx, bot)
async def run(self) -> ControllerResponse:
if self.guild.voice_client is not None:
if self.guild.voice_client.is_playing():
self.guild.voice_client.pause()
return ControllerResponse(self.ctx)

View File

@ -40,5 +40,6 @@ class PlayersController(Singleton):
for guild in list_guilds:
player = Player(self.__bot, guild)
players[guild] = player
print(f'Player for guild {guild.name} created')
return players

View File

@ -0,0 +1,44 @@
from discord.ext.commands import Context
from discord import Client
from Controllers.AbstractController import AbstractController
from Controllers.ControllerResponse import ControllerResponse
from Music.Downloader import Downloader
from Utils.Utils import format_time
class QueueController(AbstractController):
def __init__(self, ctx: Context, bot: Client) -> None:
super().__init__(ctx, bot)
self.__down = Downloader()
async def run(self) -> ControllerResponse:
if self.player.playlist.looping_one:
info = self.player.playlist.current
embed = self.embeds.ONE_SONG_LOOPING(info)
return ControllerResponse(self.ctx, embed)
songs_preload = self.player.playlist.songs_to_preload
if len(songs_preload) == 0:
embed = self.embeds.EMPTY_QUEUE
return ControllerResponse(self.ctx, embed)
await self.__down.preload(songs_preload)
if self.player.playlist.looping_all:
title = self.__config.ALL_SONGS_LOOPING
else:
title = self.__config.QUEUE_TITLE
title = self.config.QUEUE_TITLE
total_time = format_time(sum([int(song.duration if song.duration else 0)
for song in songs_preload]))
total_songs = len(self.player.playlist)
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.config.SONG_DOWNLOADING
text += f"**`{pos}` - ** {song_name} - `{format_time(song.duration)}`\n"
embed = self.embeds.QUEUE(title, text)
return ControllerResponse(self.ctx, embed)

View File

@ -0,0 +1,16 @@
from discord.ext.commands import Context
from discord import Client
from Controllers.AbstractController import AbstractController
from Controllers.ControllerResponse import ControllerResponse
class ResumeController(AbstractController):
def __init__(self, ctx: Context, bot: Client) -> None:
super().__init__(ctx, bot)
async def run(self) -> ControllerResponse:
if self.guild.voice_client is not None:
if self.guild.voice_client.is_paused():
self.guild.voice_client.resume()
return ControllerResponse(self.ctx)

View File

@ -11,16 +11,13 @@ class SkipController(AbstractController):
async def run(self) -> ControllerResponse:
if self.player.playlist.looping_one:
embed = self.__embeds.FAIL_DUE_TO_LOOP_ON
error = BadCommandUsage('', '')
response = ControllerResponse(self.ctx, embed, error)
return response
embed = self.embeds.ERROR_DUE_LOOP_ONE_ON()
error = BadCommandUsage()
return ControllerResponse(self.ctx, embed, error)
voice = self.controller.get_guild_voice(self.guild)
if voice is None:
response = ControllerResponse(self.ctx)
return response
return ControllerResponse(self.ctx)
else:
voice.stop()
response = ControllerResponse(self.ctx)
return response
return ControllerResponse(self.ctx)

View File

@ -0,0 +1,23 @@
from discord.ext.commands import Context
from discord import Client
from Controllers.AbstractController import AbstractController
from Controllers.ControllerResponse import ControllerResponse
class StopController(AbstractController):
def __init__(self, ctx: Context, bot: Client) -> None:
super().__init__(ctx, bot)
async def run(self) -> ControllerResponse:
if self.guild.voice_client is None:
return ControllerResponse(self.ctx)
if self.guild.voice_client.is_connected():
self.player.playlist.clear()
self.player.playlist.loop_off()
self.guild.voice_client.stop()
await self.guild.voice_client.disconnect()
return ControllerResponse(self.ctx)

View File

@ -4,7 +4,7 @@ from Config.Config import Configs
from yt_dlp import YoutubeDL
from concurrent.futures import ThreadPoolExecutor
from Music.Song import Song
from Music.utils import is_url, run_async
from Utils.Utils import is_url, run_async
class Downloader():

View File

@ -8,7 +8,7 @@ from Music.Playlist import Playlist
from Music.Searcher import Searcher
from Music.Song import Song
from Music.Types import Provider
from Music.utils import *
from Utils.Utils import *
class Player(commands.Cog):
@ -28,6 +28,10 @@ class Player(commands.Cog):
self.FFMPEG_OPTIONS = {'before_options': '-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5',
'options': '-vn'}
@property
def playing(self) -> bool:
return self.__playing
@property
def playlist(self) -> Playlist:
return self.__playlist
@ -212,6 +216,7 @@ class Player(commands.Cog):
def history(self) -> Embed:
history = self.__playlist.songs_history
print(f'Player -> {history}')
if len(history) == 0:
text = self.__config.HISTORY_EMPTY

View File

@ -7,7 +7,6 @@ import random
class Playlist(IPlaylist):
"""Class to manage and control the songs to play and played"""
def __init__(self) -> None:
self.__config = Configs()
@ -43,7 +42,6 @@ class Playlist(IPlaylist):
return len(self.__queue)
def next_song(self) -> Song:
"""Return the next song to play in a normal playlist flow"""
if self.__current == None and len(self.__queue) == 0:
return None
@ -72,7 +70,6 @@ class Playlist(IPlaylist):
return self.__current
def prev_song(self) -> Song:
"""If playing return it to queue and return the previous song to play"""
if len(self.__songs_history) == 0:
return None
else:
@ -84,74 +81,38 @@ class Playlist(IPlaylist):
return self.__current # return the song
def add_song(self, identifier: str, requester: str) -> Song:
"""Create a song object, add to queue and return it"""
song = Song(identifier=identifier, playlist=self, requester=requester)
self.__queue.append(song)
return song
def shuffle(self) -> None:
"""Shuffle the order of the songs to play"""
random.shuffle(self.__queue)
def revert(self) -> None:
"""Revert the order of the songs to play"""
self.__queue.reverse()
def clear(self) -> None:
"""Clear the songs to play song history"""
self.__queue.clear()
def loop_one(self) -> str:
"""Try to start the loop of the current song
Return: Embed descrition to show to user
"""
if self.__looping_all == True:
return self.__config.LOOP_ALL_ON
elif self.__looping_one == True:
return self.__config.LOOP_ONE_ALREADY_ON
else:
def loop_one(self) -> None:
self.__looping_one = True
return self.__config.LOOP_ONE_ACTIVATE
self.__looping_all = False
def loop_all(self) -> str:
"""Try to start the loop of all songs
Return: Embed descrition to show to user
"""
if self.__looping_one == True:
return self.__config.LOOP_ONE_ON
elif self.__looping_all == True:
return self.__config.LOOP_ALL_ALREADY_ON
else:
def loop_all(self) -> None:
self.__looping_all = True
return self.__config.LOOP_ALL_ACTIVATE
self.__looping_one = False
def loop_off(self) -> str:
"""Disable both types of loop"""
if self.__looping_all == False and self.__looping_one == False:
return self.__config.LOOP_ALREADY_DISABLE
self.__looping_all = False
self.__looping_one = False
return self.__config.LOOP_DISABLE
def destroy_song(self, song_destroy: Song) -> None:
"""Destroy a song object from the queue"""
for song in self.__queue:
if song == song_destroy:
self.__queue.remove(song)
break
def move_songs(self, pos1, pos2) -> str:
"""Try to move the song in pos1 to pos2, -1 is the last
Positions: First music is 1
Return: String with the status of the function, to show to user
"""
if pos1 == -1:
pos1 = len(self.__queue)
if pos2 == -1:
@ -186,7 +147,6 @@ class Playlist(IPlaylist):
return self.__config.SONG_REMOVED_SUCCESSFULLY.format(song_name)
def history(self) -> list:
"""Return a list with the song title of all played songs"""
titles = []
for song in self.__songs_history:
title = song.title if song.title else 'Unknown'

View File

@ -1,6 +1,6 @@
from Music.Types import Provider
from Music.Spotify import SpotifySearch
from Music.utils import is_url
from Utils.Utils import is_url
class Searcher():

View File

@ -9,7 +9,20 @@ class Embeds:
self.__config = Configs()
self.__colors = Colors()
@property
def ONE_SONG_LOOPING(self, info: dict) -> Embed:
title = self.__config.ONE_SONG_LOOPING
return self.SONG_INFO(info, title)
def EMPTY_QUEUE(self) -> Embed:
title = self.__config.SONG_PLAYER
text = self.__config.EMPTY_QUEUE
embed = Embed(
title=title,
description=text,
colour=self.__colors.BLUE
)
return embed
def INVALID_INPUT(self) -> Embed:
embed = Embed(
title=self.__config.ERROR_TITLE,
@ -17,7 +30,6 @@ class Embeds:
colours=self.__colors.BLUE)
return embed
@property
def UNAVAILABLE_VIDEO(self) -> Embed:
embed = Embed(
title=self.__config.ERROR_TITLE,
@ -25,7 +37,6 @@ class Embeds:
colours=self.__colors.BLUE)
return embed
@property
def DOWNLOADING_ERROR(self) -> Embed:
embed = Embed(
title=self.__config.ERROR_TITLE,
@ -33,7 +44,6 @@ class Embeds:
colours=self.__colors.BLUE)
return embed
@property
def SONG_ADDED(self, title: str) -> Embed:
embed = Embed(
title=self.__config.SONG_PLAYER,
@ -41,7 +51,6 @@ class Embeds:
colours=self.__colors.BLUE)
return embed
@property
def SONGS_ADDED(self, quant: int) -> Embed:
embed = Embed(
title=self.__config.SONG_PLAYER,
@ -49,7 +58,6 @@ class Embeds:
colour=self.__colors.BLUE)
return embed
@property
def SONG_INFO(self, info: dict, title: str, position='Playing Now') -> Embed:
embedvc = Embed(
title=title,
@ -84,7 +92,14 @@ class Embeds:
return embedvc
@property
def BAD_LOOP_USE(self) -> Embed:
embed = Embed(
title=self.__config.BAD_COMMAND_TITLE,
description=self.__config.BAD_USE_OF_LOOP,
colour=self.__colors.BLACK
)
return embed
def COMMAND_ERROR(self):
embed = Embed(
title=self.__config.ERROR_TITLE,
@ -93,7 +108,6 @@ class Embeds:
)
return embed
@property
def COMMAND_NOT_FOUND(self) -> Embed:
embed = Embed(
title=self.__config.ERROR_TITLE,
@ -102,7 +116,6 @@ class Embeds:
)
return embed
@property
def MY_ERROR_BAD_COMMAND(self) -> Embed:
embed = Embed(
title=self.__config.BAD_COMMAND_TITLE,
@ -111,7 +124,6 @@ class Embeds:
)
return embed
@property
def UNKNOWN_ERROR(self) -> Embed:
embed = Embed(
title=self.__config.ERROR_TITLE,
@ -120,7 +132,6 @@ class Embeds:
)
return embed
@property
def FAIL_DUE_TO_LOOP_ON(self) -> Embed:
embed = Embed(
title=self.__config.SONG_PLAYER,
@ -129,7 +140,30 @@ class Embeds:
)
return embed
@property
def LOOP_ONE_ACTIVATED(self) -> Embed:
embed = Embed(
title=self.__config.SONG_PLAYER,
description=self.__config.LOOP_ONE_ACTIVATE,
colour=self.__colors.BLUE
)
return embed
def LOOP_ALL_ACTIVATED(self) -> Embed:
embed = Embed(
title=self.__config.SONG_PLAYER,
description=self.__config.LOOP_ALL_ACTIVATE,
colour=self.__colors.BLUE
)
return embed
def LOOP_DISABLE(self) -> Embed:
embed = Embed(
title=self.__config.SONG_PLAYER,
description=self.__config.LOOP_DISABLE,
colour=self.__colors.BLUE
)
return embed
def NOT_PREVIOUS_SONG(self) -> Embed:
embed = Embed(
title=self.__config.SONG_PLAYER,
@ -138,7 +172,6 @@ class Embeds:
)
return embed
@property
def HISTORY(self, description: str) -> Embed:
embed = Embed(
title=self.__config.HISTORY_TITLE,
@ -146,7 +179,6 @@ class Embeds:
colour=self.__colors.BLUE)
return embed
@property
def NOT_PLAYING(self) -> Embed:
embed = Embed(
title=self.__config.SONG_PLAYER,
@ -154,7 +186,6 @@ class Embeds:
colour=self.__colors.BLUE)
return embed
@property
def QUEUE(self, title: str, description: str) -> Embed:
embed = Embed(
title=title,
@ -163,7 +194,6 @@ class Embeds:
)
return embed
@property
def INVITE(self, bot_id: str) -> Embed:
link = self.__config.INVITE_URL
link.format(bot_id)
@ -176,7 +206,6 @@ class Embeds:
)
return embed
@property
def ERROR_NUMBER(self) -> Embed:
embed = Embed(
description=self.__config.ERROR_NUMBER,
@ -184,7 +213,6 @@ class Embeds:
)
return embed
@property
def RANDOM_NUMBER(self, a: int, b: int, x: int) -> Embed:
embed = Embed(
title=f'Random number between [{a, b}]',
@ -193,7 +221,6 @@ class Embeds:
)
return embed
@property
def CARA_COROA(self, result: str) -> Embed:
embed = Embed(
title='Cara Cora',
@ -202,7 +229,6 @@ class Embeds:
)
return embed
@property
def CHOSEN_THING(self, thing: str) -> Embed:
embed = Embed(
title='Choose something',
@ -211,7 +237,6 @@ class Embeds:
)
return embed
@property
def BAD_CHOOSE_USE(self) -> Embed:
embed = Embed(
title='Choose something',

View File

@ -54,7 +54,9 @@ class Configs(Singleton):
self.LOOP_DISABLE = '➡️ Loop disabled'
self.LOOP_ALREADY_DISABLE = '❌ Loop is already disabled'
self.LOOP_ON = f'❌ This command cannot be invoked with any loop activated. Use {self.BOT_PREFIX}loop off to disable loop'
self.ERROR_DUE_LOOP_ONE_ON = f'❌ This command cannot be executed with loop one activated. Use {self.BOT_PREFIX}loop off to disable loop.'
self.BAD_USE_OF_LOOP = '❌ The available subcommands of loop are: [one], [all], [off], use them to control the loop of songs'
self.SONGS_SHUFFLED = '🔀 Songs shuffled successfully'
self.ERROR_SHUFFLING = '❌ Error while shuffling the songs'
self.ERROR_MOVING = '❌ Error while moving the songs'