mirror of
https://github.com/weyne85/discord_music_bot.git
synced 2025-10-29 16:58:27 +00:00
Adding types and changing some messages from class
This commit is contained in:
parent
f9b46e13ff
commit
b4159c7e86
@ -1,9 +1,11 @@
|
||||
import discord
|
||||
from discord import Client
|
||||
from discord import Client, Game, Status, Embed
|
||||
from discord.ext.commands.errors import CommandNotFound, MissingRequiredArgument, UserInputError
|
||||
from discord.ext import commands
|
||||
from Config.Config import Configs
|
||||
from Config.Helper import Helper
|
||||
from Config.Messages import Messages
|
||||
from Config.Colors import Colors
|
||||
from Views.Embeds import Embeds
|
||||
|
||||
helper = Helper()
|
||||
|
||||
@ -13,6 +15,9 @@ class Control(commands.Cog):
|
||||
def __init__(self, bot: Client):
|
||||
self.__bot = bot
|
||||
self.__config = Configs()
|
||||
self.__messages = Messages()
|
||||
self.__colors = Colors()
|
||||
self.__embeds = Embeds()
|
||||
self.__comandos = {
|
||||
'MUSIC': ['resume', 'pause', 'loop', 'stop',
|
||||
'skip', 'play', 'queue', 'clear',
|
||||
@ -24,49 +29,23 @@ class Control(commands.Cog):
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_ready(self):
|
||||
print(self.__config.STARTUP_MESSAGE)
|
||||
await self.__bot.change_presence(status=discord.Status.online, activity=discord.Game(name=f"Vulkan | {self.__config.BOT_PREFIX}help"))
|
||||
print(self.__config.STARTUP_COMPLETE_MESSAGE)
|
||||
print(self.__messages.STARTUP_MESSAGE)
|
||||
await self.__bot.change_presence(status=Status.online, activity=Game(name=f"Vulkan | {self.__config.BOT_PREFIX}help"))
|
||||
print(self.__messages.STARTUP_COMPLETE_MESSAGE)
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_command_error(self, ctx, error):
|
||||
if isinstance(error, MissingRequiredArgument):
|
||||
embed = discord.Embed(
|
||||
title=self.__config.ERROR_TITLE,
|
||||
description=self.__config.ERROR_MISSING_ARGUMENTS,
|
||||
colour=self.__config.COLOURS['black']
|
||||
)
|
||||
embed = self.__embeds.MISSING_ARGUMENTS()
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
elif isinstance(error, CommandNotFound):
|
||||
embed = discord.Embed(
|
||||
title=self.__config.ERROR_TITLE,
|
||||
description=self.__config.COMMAND_NOT_FOUND,
|
||||
colour=self.__config.COLOURS['black']
|
||||
)
|
||||
embed = self.__embeds.COMMAND_NOT_FOUND()
|
||||
await ctx.send(embed=embed)
|
||||
elif isinstance(error, UserInputError):
|
||||
my_error = False
|
||||
if len(error.args) > 0:
|
||||
for arg in error.args:
|
||||
if arg == self.__config.MY_ERROR_BAD_COMMAND:
|
||||
embed = discord.Embed(
|
||||
title=self.__config.BAD_COMMAND_TITLE,
|
||||
description=self.__config.BAD_COMMAND,
|
||||
colour=self.__config.COLOURS['black']
|
||||
)
|
||||
await ctx.send(embed=embed)
|
||||
my_error = True
|
||||
break
|
||||
if not my_error:
|
||||
raise error
|
||||
|
||||
else:
|
||||
print(f'DEVELOPER NOTE -> Comand Error: {error}')
|
||||
embed = discord.Embed(
|
||||
title=self.__config.ERROR_TITLE,
|
||||
description=self.__config.UNKNOWN_ERROR,
|
||||
colour=self.__config.COLOURS['red']
|
||||
)
|
||||
embed = self.__embeds.UNKNOWN_ERROR()
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
@commands.command(name="help", help=helper.HELP_HELP, description=helper.HELP_HELP_LONG, aliases=['h', 'ajuda'])
|
||||
@ -76,19 +55,19 @@ class Control(commands.Cog):
|
||||
if command.name == command_help:
|
||||
txt = command.description if command.description else command.help
|
||||
|
||||
embedhelp = discord.Embed(
|
||||
embedhelp = Embed(
|
||||
title=f'**Description of {command_help}** command',
|
||||
description=txt,
|
||||
colour=self.__config.COLOURS['blue']
|
||||
colour=self.__colors.BLUE
|
||||
)
|
||||
|
||||
await ctx.send(embed=embedhelp)
|
||||
return
|
||||
|
||||
embedhelp = discord.Embed(
|
||||
embedhelp = Embed(
|
||||
title='Command Help',
|
||||
description=f'Command {command_help} Not Found',
|
||||
colour=self.__config.COLOURS['red']
|
||||
colour=self.__colors.RED
|
||||
)
|
||||
|
||||
await ctx.send(embed=embedhelp)
|
||||
@ -111,10 +90,10 @@ class Control(commands.Cog):
|
||||
|
||||
helptxt = f'\n{help_music}\n{help_help}\n{help_random}'
|
||||
helptxt += f'\n\nType {self.__config.BOT_PREFIX}help "command" for more information about the command chosen'
|
||||
embedhelp = discord.Embed(
|
||||
embedhelp = Embed(
|
||||
title=f'**Available Commands of {self.__bot.user.name}**',
|
||||
description=helptxt,
|
||||
colour=self.__config.COLOURS['blue']
|
||||
colour=self.__colors.BLUE
|
||||
)
|
||||
|
||||
embedhelp.set_thumbnail(url=self.__bot.user.avatar_url)
|
||||
@ -122,14 +101,13 @@ class Control(commands.Cog):
|
||||
|
||||
@commands.command(name='invite', help=helper.HELP_INVITE, description=helper.HELP_INVITE_LONG)
|
||||
async def invite_bot(self, ctx):
|
||||
invite_url = 'https://discordapp.com/oauth2/authorize?client_id={}&scope=bot>'.format(
|
||||
self.__bot.user.id)
|
||||
txt = self.__config.INVITE_MESSAGE.format(invite_url)
|
||||
invite_url = self.__config.INVITE_URL.format(self.__bot.user.id)
|
||||
txt = self.__config.INVITE_MESSAGE.format(invite_url, invite_url)
|
||||
|
||||
embed = discord.Embed(
|
||||
embed = Embed(
|
||||
title="Invite Vulkan",
|
||||
description=txt,
|
||||
colour=self.__config.COLOURS['blue']
|
||||
colour=self.__colors.BLUE
|
||||
)
|
||||
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
@ -1,30 +1,30 @@
|
||||
from random import randint, random
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from discord import Client
|
||||
from discord.ext.commands import Context, command, Cog
|
||||
from Config.Colors import Colors
|
||||
from Config.Config import Configs
|
||||
from Config.Helper import Helper
|
||||
from Views.Embeds import Embeds
|
||||
|
||||
helper = Helper()
|
||||
|
||||
|
||||
class Random(commands.Cog):
|
||||
class Random(Cog):
|
||||
|
||||
def __init__(self, bot):
|
||||
self.__bot = bot
|
||||
def __init__(self, bot: Client):
|
||||
self.__config = Configs()
|
||||
self.__colors = Colors()
|
||||
self.__embeds = Embeds()
|
||||
|
||||
@commands.command(name='random', help=helper.HELP_RANDOM, description=helper.HELP_RANDOM_LONG)
|
||||
async def random(self, ctx, arg: str) -> None:
|
||||
@command(name='random', help=helper.HELP_RANDOM, description=helper.HELP_RANDOM_LONG)
|
||||
async def random(self, ctx: Context, arg: str) -> None:
|
||||
try:
|
||||
arg = int(arg)
|
||||
|
||||
except:
|
||||
embed = discord.Embed(
|
||||
description=self.__config.ERROR_NUMBER,
|
||||
colour=self.__config.COLOURS['red']
|
||||
)
|
||||
embed = self.__embeds.ERROR_NUMBER()
|
||||
await ctx.send(embed=embed)
|
||||
return
|
||||
return None
|
||||
|
||||
if arg < 1:
|
||||
a = arg
|
||||
@ -34,29 +34,21 @@ class Random(commands.Cog):
|
||||
b = arg
|
||||
|
||||
x = randint(a, b)
|
||||
embed = discord.Embed(
|
||||
title=f'Random number between [{a, b}]',
|
||||
description=x,
|
||||
colour=self.__config.COLOURS['green']
|
||||
)
|
||||
embed = self.__embeds.RANDOM_NUMBER(a, b, x)
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
@commands.command(name='cara', help=helper.HELP_CARA, description=helper.HELP_CARA_LONG)
|
||||
async def cara(self, ctx) -> None:
|
||||
@command(name='cara', help=helper.HELP_CARA, description=helper.HELP_CARA_LONG)
|
||||
async def cara(self, ctx: Context) -> None:
|
||||
x = random()
|
||||
if x < 0.5:
|
||||
result = 'cara'
|
||||
else:
|
||||
result = 'coroa'
|
||||
|
||||
embed = discord.Embed(
|
||||
title='Cara Cora',
|
||||
description=f'Result: {result}',
|
||||
colour=self.__config.COLOURS['green']
|
||||
)
|
||||
embed = self.__embeds.CARA_COROA(result)
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
@commands.command(name='choose', help=helper.HELP_CHOOSE, description=helper.HELP_CHOOSE_LONG)
|
||||
@command(name='choose', help=helper.HELP_CHOOSE, description=helper.HELP_CHOOSE_LONG)
|
||||
async def choose(self, ctx, *args: str) -> None:
|
||||
try:
|
||||
user_input = " ".join(args)
|
||||
@ -64,18 +56,10 @@ class Random(commands.Cog):
|
||||
|
||||
index = randint(0, len(itens)-1)
|
||||
|
||||
embed = discord.Embed(
|
||||
title='Choose something',
|
||||
description=itens[index],
|
||||
colour=self.__config.COLOURS['green']
|
||||
)
|
||||
embed = self.__embeds.CHOSEN_THING(itens[index])
|
||||
await ctx.send(embed=embed)
|
||||
except:
|
||||
embed = discord.Embed(
|
||||
title='Choose something.',
|
||||
description=f'Error: Use {self.__config.BOT_PREFIX}help choose to understand this command.',
|
||||
colour=self.__config.COLOURS['red']
|
||||
)
|
||||
embed = self.__embeds.BAD_CHOOSE_USE()
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
|
||||
|
||||
@ -2,6 +2,7 @@ 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 Controllers.PlayerController import PlayersController
|
||||
from Music.Player import Player
|
||||
from Controllers.ControllerResponse import ControllerResponse
|
||||
@ -19,6 +20,7 @@ class AbstractController(ABC):
|
||||
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()
|
||||
@ -60,6 +62,10 @@ class AbstractController(ABC):
|
||||
def config(self) -> Configs:
|
||||
return self.__config
|
||||
|
||||
@property
|
||||
def messages(self) -> Messages:
|
||||
return self.__messages
|
||||
|
||||
@property
|
||||
def helper(self) -> Helper:
|
||||
return self.__helper
|
||||
|
||||
@ -13,7 +13,7 @@ class HistoryController(AbstractController):
|
||||
history = self.player.playlist.songs_history
|
||||
|
||||
if len(history) == 0:
|
||||
text = self.config.HISTORY_EMPTY
|
||||
text = self.messages.HISTORY_EMPTY
|
||||
|
||||
else:
|
||||
text = f'\n📜 History Length: {len(history)} | Max: {self.config.MAX_SONGS_HISTORY}\n'
|
||||
|
||||
@ -10,7 +10,7 @@ class LoopController(AbstractController):
|
||||
super().__init__(ctx, bot)
|
||||
|
||||
async def run(self, args: str) -> ControllerResponse:
|
||||
if args == '' or args == None:
|
||||
if args == '' or args is None:
|
||||
self.player.playlist.loop_all()
|
||||
embed = self.embeds.LOOP_ALL_ACTIVATED()
|
||||
return ControllerResponse(self.ctx, embed)
|
||||
|
||||
@ -49,7 +49,7 @@ class MoveController(AbstractController):
|
||||
pos1 = int(pos1)
|
||||
pos2 = int(pos2)
|
||||
except:
|
||||
return NumberRequired(self.config.ERROR_NUMBER)
|
||||
return NumberRequired(self.messages.ERROR_NUMBER)
|
||||
|
||||
def __sanitize_input(self, pos1: int, pos2: int) -> tuple:
|
||||
pos1 = int(pos1)
|
||||
|
||||
@ -2,11 +2,13 @@ from discord.ext.commands import Context
|
||||
from discord import Client
|
||||
from Controllers.AbstractController import AbstractController
|
||||
from Controllers.ControllerResponse import ControllerResponse
|
||||
from Utils.Cleaner import Cleaner
|
||||
|
||||
|
||||
class NowPlayingController(AbstractController):
|
||||
def __init__(self, ctx: Context, bot: Client) -> None:
|
||||
super().__init__(ctx, bot)
|
||||
self.__cleaner = Cleaner()
|
||||
|
||||
async def run(self) -> ControllerResponse:
|
||||
if not self.player.playing:
|
||||
@ -14,9 +16,10 @@ class NowPlayingController(AbstractController):
|
||||
return ControllerResponse(self.ctx, embed)
|
||||
|
||||
if self.player.playlist.looping_one:
|
||||
title = self.config.ONE_SONG_LOOPING
|
||||
title = self.messages.ONE_SONG_LOOPING
|
||||
else:
|
||||
title = self.config.SONG_PLAYING
|
||||
title = self.messages.SONG_PLAYING
|
||||
await self.__cleaner.clean_messages(self.ctx, self.config.CLEANER_MESSAGES_QUANT)
|
||||
|
||||
info = self.player.playlist.current.info
|
||||
embed = self.embeds.SONG_INFO(info, title)
|
||||
|
||||
@ -25,9 +25,9 @@ class QueueController(AbstractController):
|
||||
await self.__down.preload(songs_preload)
|
||||
|
||||
if self.player.playlist.looping_all:
|
||||
title = self.config.ALL_SONGS_LOOPING
|
||||
title = self.messages.ALL_SONGS_LOOPING
|
||||
else:
|
||||
title = self.config.QUEUE_TITLE
|
||||
title = self.messages.QUEUE_TITLE
|
||||
|
||||
total_time = Utils.format_time(sum([int(song.duration if song.duration else 0)
|
||||
for song in songs_preload]))
|
||||
@ -36,7 +36,7 @@ class QueueController(AbstractController):
|
||||
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
|
||||
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)
|
||||
|
||||
@ -42,7 +42,7 @@ class RemoveController(AbstractController):
|
||||
try:
|
||||
position = int(position)
|
||||
except:
|
||||
return NumberRequired(self.config.ERROR_NUMBER)
|
||||
return NumberRequired(self.messages.ERROR_NUMBER)
|
||||
|
||||
def __sanitize_input(self, position: str) -> int:
|
||||
position = int(position)
|
||||
|
||||
@ -1,81 +0,0 @@
|
||||
from abc import ABC, abstractproperty, abstractmethod
|
||||
|
||||
|
||||
class IPlaylist(ABC):
|
||||
@abstractproperty
|
||||
def looping_one(self):
|
||||
pass
|
||||
|
||||
@abstractproperty
|
||||
def looping_all(self):
|
||||
pass
|
||||
|
||||
@abstractproperty
|
||||
def songs_to_preload(self) -> list:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def __len__(self):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def next_song(self):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def add_song(self, identifier: str) -> None:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def shuffle(self) -> None:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def revert(self) -> None:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def clear(self) -> None:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def loop_one(self) -> str:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def loop_all(self) -> str:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def loop_off(self) -> str:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def destroy_song(self, song_destroy) -> None:
|
||||
pass
|
||||
|
||||
|
||||
class ISong(ABC):
|
||||
@abstractmethod
|
||||
def finish_down(self, info: dict) -> None:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def source(self) -> str:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def title(self) -> str:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def duration(self) -> str:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def identifier(self) -> str:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def destroy(self) -> None:
|
||||
pass
|
||||
@ -1,3 +1,4 @@
|
||||
import asyncio
|
||||
from discord.ext import commands
|
||||
from Config.Config import Configs
|
||||
from discord import Client, Guild, FFmpegPCMAudio, Embed
|
||||
@ -18,7 +19,6 @@ class Player(commands.Cog):
|
||||
|
||||
self.__timer = Timer(self.__timeout_handler)
|
||||
self.__playing = False
|
||||
self.__config = Configs()
|
||||
|
||||
# Flag to control if the player should stop totally the playing
|
||||
self.__force_stop = False
|
||||
@ -68,7 +68,7 @@ class Player(commands.Cog):
|
||||
|
||||
song = self.__playlist.next_song()
|
||||
|
||||
if song != None:
|
||||
if song is not None:
|
||||
coro = self.__play_music(ctx, song)
|
||||
self.__bot.loop.create_task(coro)
|
||||
else:
|
||||
@ -76,15 +76,15 @@ class Player(commands.Cog):
|
||||
|
||||
async def __play_music(self, ctx: Context, song: Song) -> None:
|
||||
try:
|
||||
source = self.__ensure_source(song)
|
||||
if source == None:
|
||||
source = await self.__ensure_source(song)
|
||||
if source is None:
|
||||
self.__play_next(None, ctx)
|
||||
|
||||
self.__playing = True
|
||||
|
||||
player = FFmpegPCMAudio(song.source, **self.FFMPEG_OPTIONS)
|
||||
self.__guild.voice_client.play(
|
||||
player, after=lambda e: self.__play_next(e, ctx))
|
||||
voice = self.__guild.voice_client
|
||||
voice.play(player, after=lambda e: self.__play_next(e, ctx))
|
||||
|
||||
self.__timer.cancel()
|
||||
self.__timer = Timer(self.__timeout_handler)
|
||||
@ -92,46 +92,12 @@ class Player(commands.Cog):
|
||||
await ctx.invoke(self.__bot.get_command('np'))
|
||||
|
||||
songs = self.__playlist.songs_to_preload
|
||||
await self.__down.preload(songs)
|
||||
asyncio.create_task(self.__down.preload(songs))
|
||||
except:
|
||||
self.__play_next(None, ctx)
|
||||
|
||||
def __format_embed(self, info: dict, title='', 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
|
||||
|
||||
async def __timeout_handler(self) -> None:
|
||||
if self.__guild.voice_client == None:
|
||||
if self.__guild.voice_client is None:
|
||||
return
|
||||
|
||||
if self.__guild.voice_client.is_playing() or self.__guild.voice_client.is_paused():
|
||||
@ -142,9 +108,10 @@ class Player(commands.Cog):
|
||||
self.__playlist.loop_off()
|
||||
await self.__guild.voice_client.disconnect()
|
||||
|
||||
def __ensure_source(self, song: Song) -> str:
|
||||
async def __ensure_source(self, song: Song) -> str:
|
||||
while True:
|
||||
if song.source != None: # If song got downloaded
|
||||
await asyncio.sleep(0.1)
|
||||
if song.source is not None: # If song got downloaded
|
||||
return song.source
|
||||
|
||||
if song.problematic: # If song got any error
|
||||
|
||||
@ -1,12 +1,11 @@
|
||||
from collections import deque
|
||||
from typing import List
|
||||
from Config.Config import Configs
|
||||
from Music.Interfaces import IPlaylist
|
||||
from Music.Song import Song
|
||||
import random
|
||||
|
||||
|
||||
class Playlist(IPlaylist):
|
||||
class Playlist:
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.__config = Configs()
|
||||
@ -54,7 +53,7 @@ class Playlist(IPlaylist):
|
||||
return len(self.__queue)
|
||||
|
||||
def next_song(self) -> Song:
|
||||
if self.__current == None and len(self.__queue) == 0:
|
||||
if self.__current is None and len(self.__queue) == 0:
|
||||
return None
|
||||
|
||||
played_song = self.__current
|
||||
|
||||
@ -1,13 +1,10 @@
|
||||
from Music.Interfaces import ISong, IPlaylist
|
||||
class Song:
|
||||
|
||||
|
||||
class Song(ISong):
|
||||
|
||||
def __init__(self, identifier: str, playlist: IPlaylist, requester: str) -> None:
|
||||
def __init__(self, identifier: str, playlist, requester: str) -> None:
|
||||
self.__identifier = identifier
|
||||
self.__info = {'requester': requester}
|
||||
self.__problematic = False
|
||||
self.__playlist: IPlaylist = playlist
|
||||
self.__playlist = playlist
|
||||
|
||||
def finish_down(self, info: dict) -> None:
|
||||
if info is None:
|
||||
|
||||
134
Views/Embeds.py
134
Views/Embeds.py
@ -1,3 +1,4 @@
|
||||
from Config.Messages import Messages
|
||||
from Exceptions.Exceptions import Error
|
||||
from discord import Embed
|
||||
from Config.Config import Configs
|
||||
@ -8,15 +9,16 @@ from datetime import timedelta
|
||||
class Embeds:
|
||||
def __init__(self) -> None:
|
||||
self.__config = Configs()
|
||||
self.__messages = Messages()
|
||||
self.__colors = Colors()
|
||||
|
||||
def ONE_SONG_LOOPING(self, info: dict) -> Embed:
|
||||
title = self.__config.ONE_SONG_LOOPING
|
||||
title = self.__messages.ONE_SONG_LOOPING
|
||||
return self.SONG_INFO(info, title)
|
||||
|
||||
def EMPTY_QUEUE(self) -> Embed:
|
||||
title = self.__config.SONG_PLAYER
|
||||
text = self.__config.EMPTY_QUEUE
|
||||
title = self.__messages.SONG_PLAYER
|
||||
text = self.__messages.EMPTY_QUEUE
|
||||
embed = Embed(
|
||||
title=title,
|
||||
description=text,
|
||||
@ -24,42 +26,50 @@ class Embeds:
|
||||
)
|
||||
return embed
|
||||
|
||||
def MISSING_ARGUMENTS(self) -> Embed:
|
||||
embed = Embed(
|
||||
title=self.__messages.ERROR_TITLE,
|
||||
description=self.__messages.ERROR_MISSING_ARGUMENTS,
|
||||
colour=self.__colors.BLACK
|
||||
)
|
||||
return embed
|
||||
|
||||
def SONG_ADDED_TWO(self, info: dict, pos: int) -> Embed:
|
||||
embed = self.SONG_INFO(info, self.__config.SONG_ADDED_TWO, pos)
|
||||
embed = self.SONG_INFO(info, self.__messages.SONG_ADDED_TWO, pos)
|
||||
return embed
|
||||
|
||||
def INVALID_INPUT(self) -> Embed:
|
||||
embed = Embed(
|
||||
title=self.__config.ERROR_TITLE,
|
||||
description=self.__config.INVALID_INPUT,
|
||||
title=self.__messages.ERROR_TITLE,
|
||||
description=self.__messages.INVALID_INPUT,
|
||||
colour=self.__colors.BLACK)
|
||||
return embed
|
||||
|
||||
def UNAVAILABLE_VIDEO(self) -> Embed:
|
||||
embed = Embed(
|
||||
title=self.__config.ERROR_TITLE,
|
||||
description=self.__config.VIDEO_UNAVAILABLE,
|
||||
title=self.__messages.ERROR_TITLE,
|
||||
description=self.__messages.VIDEO_UNAVAILABLE,
|
||||
colour=self.__colors.BLACK)
|
||||
return embed
|
||||
|
||||
def DOWNLOADING_ERROR(self) -> Embed:
|
||||
embed = Embed(
|
||||
title=self.__config.ERROR_TITLE,
|
||||
description=self.__config.DOWNLOADING_ERROR,
|
||||
title=self.__messages.ERROR_TITLE,
|
||||
description=self.__messages.DOWNLOADING_ERROR,
|
||||
colour=self.__colors.BLACK)
|
||||
return embed
|
||||
|
||||
def SONG_ADDED(self, title: str) -> Embed:
|
||||
embed = Embed(
|
||||
title=self.__config.SONG_PLAYER,
|
||||
description=self.__config.SONG_ADDED.format(title),
|
||||
title=self.__messages.SONG_PLAYER,
|
||||
description=self.__messages.SONG_ADDED.format(title),
|
||||
colour=self.__colors.BLUE)
|
||||
return embed
|
||||
|
||||
def SONGS_ADDED(self, quant: int) -> Embed:
|
||||
embed = Embed(
|
||||
title=self.__config.SONG_PLAYER,
|
||||
description=self.__config.SONGS_ADDED.format(quant),
|
||||
title=self.__messages.SONG_PLAYER,
|
||||
description=self.__messages.SONGS_ADDED.format(quant),
|
||||
colour=self.__colors.BLUE)
|
||||
return embed
|
||||
|
||||
@ -70,11 +80,11 @@ class Embeds:
|
||||
colour=self.__colors.BLUE
|
||||
)
|
||||
|
||||
embedvc.add_field(name=self.__config.SONGINFO_UPLOADER,
|
||||
embedvc.add_field(name=self.__messages.SONGINFO_UPLOADER,
|
||||
value=info['uploader'],
|
||||
inline=False)
|
||||
|
||||
embedvc.add_field(name=self.__config.SONGINFO_REQUESTER,
|
||||
embedvc.add_field(name=self.__messages.SONGINFO_REQUESTER,
|
||||
value=info['requester'],
|
||||
inline=True)
|
||||
|
||||
@ -83,15 +93,15 @@ class Embeds:
|
||||
|
||||
if 'duration' in info.keys():
|
||||
duration = str(timedelta(seconds=info['duration']))
|
||||
embedvc.add_field(name=self.__config.SONGINFO_DURATION,
|
||||
embedvc.add_field(name=self.__messages.SONGINFO_DURATION,
|
||||
value=f"{duration}",
|
||||
inline=True)
|
||||
else:
|
||||
embedvc.add_field(name=self.__config.SONGINFO_DURATION,
|
||||
value=self.__config.SONGINFO_UNKNOWN_DURATION,
|
||||
embedvc.add_field(name=self.__messages.SONGINFO_DURATION,
|
||||
value=self.__messages.SONGINFO_UNKNOWN_DURATION,
|
||||
inline=True)
|
||||
|
||||
embedvc.add_field(name=self.__config.SONGINFO_POSITION,
|
||||
embedvc.add_field(name=self.__messages.SONGINFO_POSITION,
|
||||
value=position,
|
||||
inline=True)
|
||||
|
||||
@ -99,16 +109,16 @@ class Embeds:
|
||||
|
||||
def SONG_MOVED(self, song_name: str, pos1: int, pos2: int) -> Embed:
|
||||
embed = Embed(
|
||||
title=self.__config.SONG_PLAYER,
|
||||
description=self.__config.SONG_MOVED_SUCCESSFULLY.format(song_name, pos1, pos2),
|
||||
title=self.__messages.SONG_PLAYER,
|
||||
description=self.__messages.SONG_MOVED_SUCCESSFULLY.format(song_name, pos1, pos2),
|
||||
colour=self.__colors.BLUE
|
||||
)
|
||||
return embed
|
||||
|
||||
def ERROR_MOVING(self) -> Embed:
|
||||
embed = Embed(
|
||||
title=self.__config.UNKNOWN_ERROR,
|
||||
description=self.__config.ERROR_MOVING,
|
||||
title=self.__messages.UNKNOWN_ERROR,
|
||||
description=self.__messages.ERROR_MOVING,
|
||||
colour=self.__colors.BLACK
|
||||
)
|
||||
return embed
|
||||
@ -130,139 +140,139 @@ class Embeds:
|
||||
|
||||
def WRONG_LENGTH_INPUT(self) -> Embed:
|
||||
embed = Embed(
|
||||
title=self.__config.BAD_COMMAND_TITLE,
|
||||
description=self.__config.LENGTH_ERROR,
|
||||
title=self.__messages.BAD_COMMAND_TITLE,
|
||||
description=self.__messages.LENGTH_ERROR,
|
||||
colour=self.__colors.BLACK
|
||||
)
|
||||
return embed
|
||||
|
||||
def BAD_LOOP_USE(self) -> Embed:
|
||||
embed = Embed(
|
||||
title=self.__config.BAD_COMMAND_TITLE,
|
||||
description=self.__config.BAD_USE_OF_LOOP,
|
||||
title=self.__messages.BAD_COMMAND_TITLE,
|
||||
description=self.__messages.BAD_USE_OF_LOOP,
|
||||
colour=self.__colors.BLACK
|
||||
)
|
||||
return embed
|
||||
|
||||
def COMMAND_ERROR(self):
|
||||
embed = Embed(
|
||||
title=self.__config.ERROR_TITLE,
|
||||
description=self.__config.ERROR_MISSING_ARGUMENTS,
|
||||
title=self.__messages.ERROR_TITLE,
|
||||
description=self.__messages.ERROR_MISSING_ARGUMENTS,
|
||||
colour=self.__colors.BLACK
|
||||
)
|
||||
return embed
|
||||
|
||||
def COMMAND_NOT_FOUND(self) -> Embed:
|
||||
embed = Embed(
|
||||
title=self.__config.ERROR_TITLE,
|
||||
description=self.__config.COMMAND_NOT_FOUND,
|
||||
title=self.__messages.ERROR_TITLE,
|
||||
description=self.__messages.COMMAND_NOT_FOUND,
|
||||
colour=self.__colors.BLACK
|
||||
)
|
||||
return embed
|
||||
|
||||
def MY_ERROR_BAD_COMMAND(self) -> Embed:
|
||||
embed = Embed(
|
||||
title=self.__config.BAD_COMMAND_TITLE,
|
||||
description=self.__config.BAD_COMMAND,
|
||||
title=self.__messages.BAD_COMMAND_TITLE,
|
||||
description=self.__messages.BAD_COMMAND,
|
||||
colour=self.__colors.BLACK
|
||||
)
|
||||
return embed
|
||||
|
||||
def UNKNOWN_ERROR(self) -> Embed:
|
||||
embed = Embed(
|
||||
title=self.__config.ERROR_TITLE,
|
||||
description=self.__config.UNKNOWN_ERROR,
|
||||
title=self.__messages.ERROR_TITLE,
|
||||
description=self.__messages.UNKNOWN_ERROR,
|
||||
colour=self.__colors.RED
|
||||
)
|
||||
return embed
|
||||
|
||||
def FAIL_DUE_TO_LOOP_ON(self) -> Embed:
|
||||
embed = Embed(
|
||||
title=self.__config.SONG_PLAYER,
|
||||
description=self.__config.LOOP_ON,
|
||||
title=self.__messages.SONG_PLAYER,
|
||||
description=self.__messages.LOOP_ON,
|
||||
colour=self.__colors.BLACK
|
||||
)
|
||||
return embed
|
||||
|
||||
def ERROR_SHUFFLING(self) -> Embed:
|
||||
embed = Embed(
|
||||
title=self.__config.SONG_PLAYER,
|
||||
description=self.__config.ERROR_SHUFFLING,
|
||||
title=self.__messages.SONG_PLAYER,
|
||||
description=self.__messages.ERROR_SHUFFLING,
|
||||
colour=self.__colors.BLACK
|
||||
)
|
||||
return embed
|
||||
|
||||
def SONGS_SHUFFLED(self) -> Embed:
|
||||
embed = Embed(
|
||||
title=self.__config.SONG_PLAYER,
|
||||
description=self.__config.SONGS_SHUFFLED,
|
||||
title=self.__messages.SONG_PLAYER,
|
||||
description=self.__messages.SONGS_SHUFFLED,
|
||||
colour=self.__colors.BLUE
|
||||
)
|
||||
return embed
|
||||
|
||||
def LOOP_ONE_ACTIVATED(self) -> Embed:
|
||||
embed = Embed(
|
||||
title=self.__config.LOOP_ONE_ACTIVATE,
|
||||
title=self.__messages.LOOP_ONE_ACTIVATE,
|
||||
colour=self.__colors.BLUE
|
||||
)
|
||||
return embed
|
||||
|
||||
def LOOP_ALL_ACTIVATED(self) -> Embed:
|
||||
embed = Embed(
|
||||
title=self.__config.LOOP_ALL_ACTIVATE,
|
||||
title=self.__messages.LOOP_ALL_ACTIVATE,
|
||||
colour=self.__colors.BLUE
|
||||
)
|
||||
return embed
|
||||
|
||||
def SONG_PROBLEMATIC(self) -> Embed:
|
||||
embed = Embed(
|
||||
title=self.__config.ERROR_TITLE,
|
||||
description=self.__config.DOWNLOADING_ERROR,
|
||||
title=self.__messages.ERROR_TITLE,
|
||||
description=self.__messages.DOWNLOADING_ERROR,
|
||||
colour=self.__colors.BLACK)
|
||||
return embed
|
||||
|
||||
def NO_CHANNEL(self) -> Embed:
|
||||
embed = Embed(
|
||||
title=self.__config.IMPOSSIBLE_MOVE,
|
||||
description=self.__config.NO_CHANNEL,
|
||||
title=self.__messages.IMPOSSIBLE_MOVE,
|
||||
description=self.__messages.NO_CHANNEL,
|
||||
colour=self.__colors.BLACK
|
||||
)
|
||||
return embed
|
||||
|
||||
def ERROR_DUE_LOOP_ONE_ON(self) -> Embed:
|
||||
embed = Embed(
|
||||
title=self.__config.BAD_COMMAND_TITLE,
|
||||
description=self.__config.ERROR_DUE_LOOP_ONE_ON,
|
||||
title=self.__messages.BAD_COMMAND_TITLE,
|
||||
description=self.__messages.ERROR_DUE_LOOP_ONE_ON,
|
||||
colour=self.__colors.BLACK
|
||||
)
|
||||
return embed
|
||||
|
||||
def LOOP_DISABLE(self) -> Embed:
|
||||
embed = Embed(
|
||||
title=self.__config.LOOP_DISABLE,
|
||||
title=self.__messages.LOOP_DISABLE,
|
||||
colour=self.__colors.BLUE
|
||||
)
|
||||
return embed
|
||||
|
||||
def NOT_PREVIOUS_SONG(self) -> Embed:
|
||||
embed = Embed(
|
||||
title=self.__config.SONG_PLAYER,
|
||||
description=self.__config.NOT_PREVIOUS,
|
||||
title=self.__messages.SONG_PLAYER,
|
||||
description=self.__messages.NOT_PREVIOUS,
|
||||
colour=self.__colors.BLUE
|
||||
)
|
||||
return embed
|
||||
|
||||
def HISTORY(self, description: str) -> Embed:
|
||||
embed = Embed(
|
||||
title=self.__config.HISTORY_TITLE,
|
||||
title=self.__messages.HISTORY_TITLE,
|
||||
description=description,
|
||||
colour=self.__colors.BLUE)
|
||||
return embed
|
||||
|
||||
def NOT_PLAYING(self) -> Embed:
|
||||
embed = Embed(
|
||||
title=self.__config.SONG_PLAYER,
|
||||
description=self.__config.PLAYER_NOT_PLAYING,
|
||||
title=self.__messages.SONG_PLAYER,
|
||||
description=self.__messages.PLAYER_NOT_PLAYING,
|
||||
colour=self.__colors.BLUE)
|
||||
return embed
|
||||
|
||||
@ -275,9 +285,9 @@ class Embeds:
|
||||
return embed
|
||||
|
||||
def INVITE(self, bot_id: str) -> Embed:
|
||||
link = self.__config.INVITE_URL
|
||||
link = self.__messages.INVITE_URL
|
||||
link.format(bot_id)
|
||||
text = self.__config.INVITE_MESSAGE.format(link, link)
|
||||
text = self.__messages.INVITE_MESSAGE.format(link, link)
|
||||
|
||||
embed = Embed(
|
||||
title="Invite Vulkan",
|
||||
@ -288,14 +298,14 @@ class Embeds:
|
||||
|
||||
def ERROR_NUMBER(self) -> Embed:
|
||||
embed = Embed(
|
||||
description=self.__config.ERROR_NUMBER,
|
||||
description=self.__messages.ERROR_NUMBER,
|
||||
colour=self.__colors.BLACK
|
||||
)
|
||||
return embed
|
||||
|
||||
def RANDOM_NUMBER(self, a: int, b: int, x: int) -> Embed:
|
||||
embed = Embed(
|
||||
title=f'Random number between [{a, b}]',
|
||||
title=f'Random number between [{a}, {b}]',
|
||||
description=x,
|
||||
colour=self.__colors.GREEN
|
||||
)
|
||||
@ -303,14 +313,14 @@ class Embeds:
|
||||
|
||||
def SONG_REMOVED(self, song_name: str) -> Embed:
|
||||
embed = Embed(
|
||||
description=self.__config.SONG_REMOVED_SUCCESSFULLY.format(song_name),
|
||||
description=self.__messages.SONG_REMOVED_SUCCESSFULLY.format(song_name),
|
||||
colour=self.__colors.BLUE
|
||||
)
|
||||
return embed
|
||||
|
||||
def PLAYLIST_RANGE_ERROR(self) -> Embed:
|
||||
embed = Embed(
|
||||
description=self.__config.LENGTH_ERROR,
|
||||
description=self.__messages.LENGTH_ERROR,
|
||||
colour=self.__colors.BLACK
|
||||
)
|
||||
return embed
|
||||
|
||||
@ -9,8 +9,6 @@ class Messages(Singleton):
|
||||
self.STARTUP_MESSAGE = 'Starting Vulkan...'
|
||||
self.STARTUP_COMPLETE_MESSAGE = 'Vulkan is now operating.'
|
||||
|
||||
self.INVITE_MESSAGE = 'To invite Vulkan to your own server, click [here]({})'
|
||||
|
||||
self.SONGINFO_UPLOADER = "Uploader: "
|
||||
self.SONGINFO_DURATION = "Duration: "
|
||||
self.SONGINFO_REQUESTER = 'Requester: '
|
||||
@ -67,8 +65,8 @@ class Messages(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 {configs.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.ERROR_DUE_LOOP_ONE_ON = f'❌ This command cannot be executed with loop one activated. Use {self.BOT_PREFIX}loop off to disable loop.'
|
||||
|
||||
|
||||
class SearchMessages(Singleton):
|
||||
|
||||
@ -9,85 +9,19 @@ class Configs(Singleton):
|
||||
self.BOT_TOKEN = config('BOT_TOKEN')
|
||||
self.SPOTIFY_ID = config('SPOTIFY_ID')
|
||||
self.SPOTIFY_SECRET = config('SPOTIFY_SECRET')
|
||||
self.CLEANER_MESSAGES_QUANT = 5
|
||||
|
||||
self.BOT_PREFIX = '$'
|
||||
self.VC_TIMEOUT = 600
|
||||
|
||||
self.STARTUP_MESSAGE = 'Starting Vulkan...'
|
||||
self.STARTUP_COMPLETE_MESSAGE = 'Vulkan is now operating.'
|
||||
|
||||
self.MAX_PLAYLIST_LENGTH = 50
|
||||
self.MAX_PLAYLIST_FORCED_LENGTH = 5
|
||||
self.MAX_PRELOAD_SONGS = 10
|
||||
self.MAX_SONGS_HISTORY = 15
|
||||
|
||||
self.INVITE_MESSAGE = 'To invite Vulkan to your own server, click [here]({})'
|
||||
|
||||
self.SONGINFO_UPLOADER = "Uploader: "
|
||||
self.SONGINFO_DURATION = "Duration: "
|
||||
self.SONGINFO_REQUESTER = 'Requester: '
|
||||
self.SONGINFO_POSITION = 'Position: '
|
||||
|
||||
self.SONGS_ADDED = 'You added {} songs to the queue'
|
||||
self.SONG_ADDED = 'You added the song `{}` to the queue'
|
||||
self.SONG_ADDED_TWO = '🎧 Song added to the queue'
|
||||
self.SONG_PLAYING = '🎧 Song playing now'
|
||||
self.SONG_PLAYER = '🎧 Song Player'
|
||||
self.QUEUE_TITLE = '🎧 Songs in Queue'
|
||||
self.ONE_SONG_LOOPING = '🎧 Looping One Song'
|
||||
self.ALL_SONGS_LOOPING = '🎧 Looping All Songs'
|
||||
self.SONG_PAUSED = '⏸️ Song paused'
|
||||
self.SONG_RESUMED = '▶️ Song playing'
|
||||
self.EMPTY_QUEUE = f'📜 Song queue is empty, use {self.BOT_PREFIX}play to add new songs'
|
||||
self.SONG_DOWNLOADING = '📥 Downloading...'
|
||||
|
||||
self.HISTORY_TITLE = '🎧 Played Songs'
|
||||
self.HISTORY_EMPTY = '📜 There is no musics in history'
|
||||
|
||||
self.SONG_MOVED_SUCCESSFULLY = 'Song `{}` in position `{}` moved to the position `{}` successfully'
|
||||
self.SONG_REMOVED_SUCCESSFULLY = 'Song `{}` removed successfully'
|
||||
|
||||
self.LOOP_ALL_ON = f'❌ Vulkan is looping all songs, use {self.BOT_PREFIX}loop off to disable this loop first'
|
||||
self.LOOP_ONE_ON = f'❌ Vulkan is looping one song, use {self.BOT_PREFIX}loop off to disable this loop first'
|
||||
self.LOOP_ALL_ALREADY_ON = '🔁 Vulkan is already looping all songs'
|
||||
self.LOOP_ONE_ALREADY_ON = '🔂 Vulkan is already looping the current song'
|
||||
self.LOOP_ALL_ACTIVATE = '🔁 Looping all songs'
|
||||
self.LOOP_ONE_ACTIVATE = '🔂 Looping the current song'
|
||||
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'
|
||||
self.LENGTH_ERROR = '❌ Numbers must be between 1 and queue length, use -1 for the last song'
|
||||
self.ERROR_NUMBER = '❌ This command require a number'
|
||||
self.ERROR_PLAYING = '❌ Error while playing songs'
|
||||
self.COMMAND_NOT_FOUND = f'❌ Command not found, type {self.BOT_PREFIX}help to see all commands'
|
||||
self.UNKNOWN_ERROR = f'❌ Unknown Error, if needed, use {self.BOT_PREFIX}reset to reset the player of your server'
|
||||
self.ERROR_MISSING_ARGUMENTS = f'❌ Missing arguments in this command. Type {self.BOT_PREFIX}help "command" to see more info about this command'
|
||||
self.NOT_PREVIOUS = '❌ There is none previous song to play'
|
||||
self.PLAYER_NOT_PLAYING = f'❌ No song playing. Use {self.BOT_PREFIX}play to start the player'
|
||||
self.IMPOSSIBLE_MOVE = 'That is impossible :('
|
||||
self.ERROR_TITLE = 'Error :-('
|
||||
self.NO_CHANNEL = 'To play some music, connect to any voice channel first.'
|
||||
self.NO_GUILD = f'This server does not has a Player, try {self.BOT_PREFIX}reset'
|
||||
self.INVALID_INPUT = f'This type of input was too strange, try something better or type {self.BOT_PREFIX}help play'
|
||||
self.DOWNLOADING_ERROR = '❌ An error occurred while downloading'
|
||||
self.EXTRACTING_ERROR = '❌ An error ocurred while searching for the songs'
|
||||
self.INVITE_MESSAGE = """To invite Vulkan to your own server, click [here]({}).
|
||||
|
||||
Or use this direct URL: ({})"""
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user