18 Commits

Author SHA1 Message Date
Rafael Vargas
b39e1f0216 Updating README 2022-01-12 13:27:09 -04:00
Rafael Vargas
37f5773c8e Merge pull request #16 from RafaelSolVargas/patch1.2
Patch1.2
2022-01-12 11:38:50 -04:00
Rafael Vargas
0d606361da Refactoring clean messages 2022-01-12 11:31:15 -04:00
Rafael Vargas
173b6a7b43 Stability Update 2022-01-12 10:41:31 -04:00
Rafael Vargas
5b4c5b49f1 Updating help command in Vulkan 2022-01-12 09:41:35 -04:00
Rafael Vargas
ea5245cf95 Removing some useless modules and spliting config file to help file too 2022-01-12 09:40:25 -04:00
Rafael Vargas
5063c45e87 Adding prev and history command 2022-01-11 21:46:09 -04:00
Rafael Vargas
c41a6a6c8a Adding new config variables to prev command 2022-01-11 21:40:20 -04:00
Rafael Vargas
615fd580bc Merge pull request #15 from RafaelSolVargas/stylish-update
Closing branch
2022-01-08 19:54:20 -04:00
Rafael Vargas
9b38ba4434 Updating README 2022-01-08 19:50:53 -04:00
Rafael Vargas
bbddcb555b Fixing my bot prefix 2022-01-08 19:16:35 -04:00
Rafael Vargas
3b1a7d5364 Fixing my bot prefix 2022-01-08 19:08:22 -04:00
Rafael Vargas
37acd1a61b Merge pull request #14 from RafaelSolVargas/stylish-update
Stylish update and Fixing Known Errors
2022-01-08 19:01:01 -04:00
Rafael Vargas
ccad2de97c Fixing Player trying to play a song before it got downloaded 2022-01-08 18:54:03 -04:00
Rafael Vargas
a1881cd46b Fixing aliases 2022-01-08 18:51:44 -04:00
Rafael Vargas
bb8d514525 Setting Spotify to search with artist ID 2022-01-08 18:03:56 -04:00
Rafael Vargas
ef2d7bb8fa Stylish update 2022-01-08 17:07:02 -04:00
Rafael Vargas
13bc51fae7 Updating requirements file with version used 2022-01-07 14:29:34 -04:00
16 changed files with 571 additions and 491 deletions

1
.gitignore vendored
View File

@@ -1,3 +1,4 @@
assets/
__pycache__
.env
.cache

View File

@@ -1,12 +1,53 @@
# **Vulkan**
A multi-purpose Discord bot, including music, written in Python and supports Youtube and Spotify sources for playing. Vulkan was designed so that everyone could fork this project and use it, including a 24/7 host on Heroku.
A Music Discord bot, written in Python, that supports Youtube and Spotify sources for playing. Vulkan was designed so that anyone can fork this project, follow the instructions and use it in their own way, Vulkan can also be configured in Heroku to work 24/7.
Vulkan is also able to get data from Warframe and send it to users, get random phrases and handle random operations to help users decide what to do.
# **Music**
- Play musics from Youtube and Spotify Playlists
- Control loop of one or all musics
- Allow moving and removing musics in the queue
- Play musics in queue randomly
- Store played songs and allow bidirectional flow
### Commands
```!play [title, spotify_url, youtube_url]``` - Start playing song
```!resume``` - Resume the song player
```!pause``` - Pause the song player
```!skip``` - Skip the currently playing song
```!prev``` - Return to play the previous song
```!stop``` - Stop the playing of musics
```!queue``` - Show the musics list in queue
```!history``` - Show the played songs list
```!loop [one, all, off]``` - Control the loop of songs
```!shuffle``` - Shuffle the songs in queue
```!remove [x]``` - Remove the song in position x
```!move [x, y]``` - Change the musics in position x and y in Queue
```!np``` - Show information of the currently song
```!clear``` - Clear the songs in queue, doesn't stop the player
```!reset``` - Reset the player, recommended if any error happen
```!invite``` - Send the URL to invite Vulkan to your server
```!help [command]``` - Show more info about the command selected
## **Prerequisites:**
# **Usage:**
### **API Keys**
* Your Discord Application - [Discord](https://discord.com/developers)
@@ -26,17 +67,11 @@ pip install -r requirements.txt
*FFMPEG must be configured in the PATH for Windows users. Check this [YoutubeVideo](https://www.youtube.com/watch?v=r1AtmY-RMyQ&t=114s&ab_channel=TroubleChute).*
### **.Env File Example**
This is an example of how your .env file (located in root) should look like, those API url could be the same.
This is an example of how your .env file (located in root) should look like.
```
CETUS_API=https://api.warframestat.us/pc/cetusCycle
CAMBION_API=https://api.warframestat.us/pc/cambionCycle
FISSURES_API=https://api.warframestat.us/pc/fissures
PHRASES_API='http://api.forismatic.com/api/1.0/?method=getQuote&key=457653&format=json&lang=en'
BOT_TOKEN=Your_Own_Bot_Token
SPOTIFY_ID=Your_Own_Spotify_ID
SPOTIFY_SECRET=Your_Own_Spotify_Secret
SECRET_MESSAGE=Your_Own_Secret_Message
```
@@ -57,40 +92,6 @@ To run your Bot in Heroku 24/7, you will need the Procfile located in root, then
- https://github.com/xrisk/heroku-opus.git
# **Music**
- Play musics from Youtube and Spotify Playlists
- Controll loop of one or all musics
- Allow realocating musics in the queue
- Play musics in queue randomly
### Commands
```!play [title, spotify_url, youtube_url]``` - Start playing song in Discord
```!resume``` - Resume the song player
```!pause``` - Pause the song player
```!move [x, y]``` - Change the musics in position x and y in Queue
```!skip``` - Skip the currently playing song
```!stop``` - Stop the playing of musics
```!queue``` - Show the musics list in queue
```!shuffle``` - Shuffle the songs in queue
```!remove [x]``` - Remove the song in position x
```!reset``` - Reset the player, recommended if any error happen
```!loop [one, all, off]``` - Control the loop of songs
```!np``` - Show information of the currently song
```!clear``` - Clear the songs in queue, doesn't stop the player
## License
- This program is free software: you can redistribute it and/or modify it under the terms of the [MIT License](https://github.com/RafaelSolVargas/Vulkan/blob/master/LICENSE).
@@ -100,4 +101,4 @@ To run your Bot in Heroku 24/7, you will need the Procfile located in root, then
## Acknowledgment
- See the DingoLingo [project](https://github.com/Raptor123471/DingoLingo) from Raptor123471, it helped me a lot building Vulkan
- See the DingoLingo [project](https://github.com/Raptor123471/DingoLingo) from Raptor123471, it helped me a lot to build Vulkan.

View File

@@ -1,13 +1,8 @@
from decouple import config
CETUS_API = config('CETUS_API')
CAMBION_API = config('CAMBION_API')
FISSURES_API = config('FISSURES_API')
BOT_TOKEN = config('BOT_TOKEN')
SPOTIFY_ID = config('SPOTIFY_ID')
SPOTIFY_SECRET = config('SPOTIFY_SECRET')
SECRET_MESSAGE = config('SECRET_MESSAGE')
PHRASES_API = config('PHRASES_API')
BOT_PREFIX = '!'
VC_TIMEOUT = 600
@@ -16,47 +11,62 @@ STARTUP_MESSAGE = 'Starting Vulkan...'
STARTUP_COMPLETE_MESSAGE = 'Vulkan is now operating.'
MAX_PLAYLIST_LENGTH = 50
MAX_API_PHRASES_TRIES = 10
MAX_API_CETUS_TRIES = 10
MAX_API_CAMBION_TRIES = 10
MAX_API_FISSURES_TRIES = 10
MAX_PRELOAD_SONGS = 10
MAX_SONGS_HISTORY = 15
INVITE_MESSAGE = 'To invite Vulkan to your own server, click [here]({})'
SONGINFO_UPLOADER = "Uploader: "
SONGINFO_DURATION = "Duration: "
SONGINFO_REQUESTER = 'Requester: '
HELP_SKIP = 'Skip the current playing song'
HELP_RESUME = 'Resumes the song player'
HELP_CLEAR = 'Clear the queue'
HELP_STOP = 'Stop the song player, removing Vulkan from voice channel'
HELP_LOOP = '(one/all/off) - Control the loop of songs'
HELP_NP = 'Show the info of the current song'
HELP_QUEUE = f'Show the first {MAX_PRELOAD_SONGS} songs in queue'
HELP_PAUSE = 'Pauses the song player'
HELP_SHUFFLE = 'Shuffle the songs playing'
HELP_PLAY = '(title/youtube/spotify) - Plays a song'
HELP_MOVE = '(x, y) - Moves a song from position x to y in queue'
HELP_REMOVE = '(x, -1) - Remove a song in the position x or -1 for the last song'
HELP_RESET = 'Reset the Player of a guild'
HELP_WARFRAME = f'({BOT_PREFIX}warframe help for more)'
HELP_RANDOM = '(x) - Return a random number between 1 and x'
HELP_ESCOLHA = '(x, y, z...) - Choose randomly one item passed'
HELP_CARA = 'Return cara or coroa'
HELP_DROP = '(user_name) - Try to remove the user from the current voice channel'
HELP_FRASE = "Send a randomly phrase, perhaps you get the braba"
HELP_HELP = 'This command :)'
SONGS_ADDED = 'You added {} songs to the queue'
SONG_ADDED = 'You added the song {} to the queue'
SONG_QUEUE_TITLE = 'Songs Queue'
SONG_ADDED = 'You added the song `{}` to the queue'
SONG_ADDED_TWO = '🎧 Song added to the queue'
SONG_PLAYING = '🎧 Song playing now'
SONG_PLAYER = '🎧 Song Player'
QUEUE_TITLE = '🎧 Songs in Queue'
ONE_SONG_LOOPING = '🎧 Looping One Song'
ALL_SONGS_LOOPING = '🎧 Looping All Songs'
SONG_PAUSED = '⏸️ Song paused'
SONG_RESUMED = '▶️ Song playing'
EMPTY_QUEUE = f'📜 Song queue is empty, use {BOT_PREFIX}play to add new songs'
SONG_DOWNLOADING = '📥 Downloading...'
ERROR_TITLE = 'Error :/'
HISTORY_TITLE = '🎧 Played Songs'
HISTORY_EMPTY = '📜 There is no musics in history'
SONG_MOVED_SUCCESSFULLY = 'Song `{}` in position `{}` moved with `{}` in position `{}` successfully'
SONG_REMOVED_SUCCESSFULLY = 'Song `{}` removed successfully'
LOOP_ALL_ON = f'❌ Vulkan is looping all songs, use {BOT_PREFIX}loop off to disable this loop first'
LOOP_ONE_ON = f'❌ Vulkan is looping one song, use {BOT_PREFIX}loop off to disable this loop first'
LOOP_ALL_ALREADY_ON = '🔁 Vulkan is already looping all songs'
LOOP_ONE_ALREADY_ON = '🔂 Vulkan is already looping the current song'
LOOP_ALL_ACTIVATE = '🔁 Looping all songs'
LOOP_ONE_ACTIVATE = '🔂 Looping the current song'
LOOP_DISABLE = '➡️ Loop disabled'
LOOP_ALREADY_DISABLE = '❌ Loop is already disabled'
LOOP_ON = f'❌ This command cannot be invoked with any loop activated. Use {BOT_PREFIX}loop off to disable loop'
SONGS_SHUFFLED = '🔀 Songs shuffled successfully'
ERROR_SHUFFLING = '❌ Error while shuffling the songs'
ERROR_MOVING = '❌ Error while moving the songs'
LENGTH_ERROR = '❌ Numbers must be between 1 and queue length, use -1 for the last song'
ERROR_NUMBER = '❌ This command require a number'
ERROR_PLAYING = '❌ Error while playing songs'
COMMAND_NOT_FOUND = f'❌ Command not found, type {BOT_PREFIX}help to see all commands'
UNKNOWN_ERROR = f'❌ Unknown Error, if needed, use {BOT_PREFIX}reset to reset the player of your server'
ERROR_MISSING_ARGUMENTS = f'❌ Missing arguments in this function. Type {BOT_PREFIX}help to see all commands'
NOT_PREVIOUS = '❌ There is none previous song to play'
PLAYER_NOT_PLAYING = f'❌ No song playing. Use {BOT_PREFIX}play to start the player'
IMPOSSIBLE_MOVE = 'That is impossible :('
ERROR_TITLE = 'Error :-('
NO_CHANNEL = 'To play some music, connect to any voice channel first.'
NO_GUILD = 'This guild are not connected to Vulkan'
INVALID_INPUT = 'This type of input was too strange, try something better'
DOWNLOADING_ERROR = 'An error occurred while downloading'
EXTRACTING_ERROR = 'An error ocurred while searching for the songs'
NO_GUILD = f'This server does not has a Player, try {BOT_PREFIX}reset'
INVALID_INPUT = f'This type of input was too strange, try something better or type {BOT_PREFIX}help play'
DOWNLOADING_ERROR = 'An error occurred while downloading'
EXTRACTING_ERROR = 'An error ocurred while searching for the songs'
COLOURS = {
'red': 0xDC143C,

43
config/help.py Normal file
View File

@@ -0,0 +1,43 @@
from config.config import *
HELP_SKIP = 'Skip the current playing song.'
HELP_SKIP_LONG = 'Skip the playing of the current song, does not work if loop one is activated. \n\nArguments: None.'
HELP_RESUME = 'Resumes the song player.'
HELP_RESUME_LONG = 'If the player if paused, return the playing. \n\nArguments: None.'
HELP_CLEAR = 'Clear the queue and songs history.'
HELP_CLEAR_LONG = 'Clear the songs queue and songs history. \n\nArguments: None.'
HELP_STOP = 'Stop the song player.'
HELP_STOP_LONG = 'Stop the song player, clear queue and history and remove Vulkan from voice channel.\n\nArguments: None.'
HELP_LOOP = 'Control the loop of songs.'
HELP_LOOP_LONG = 'Controll the loop of songs.\n\n Require: A song being played.\nArguments:\nOne - Start looping the current song. \
\nAll - Start looping all songs in queue.\nOff - Disable loop.'
HELP_NP = 'Show the info of the current song.'
HELP_NP_LONG = 'Show the information of the song being played.\n\nRequire: A song being played.\nArguments: None.'
HELP_QUEUE = f'Show the first {MAX_PRELOAD_SONGS} songs in queue.'
HELP_QUEUE_LONG = f'Show the first {MAX_PRELOAD_SONGS} song in the queue.\n\nArguments: None.'
HELP_PAUSE = 'Pauses the song player.'
HELP_PAUSE_LONG = 'If playing, pauses the song player.\n\nArguments: None'
HELP_PREV = 'Play the previous song.'
HELP_PREV_LONG = 'Play the previous song. If playing, the current song will return to queue.\n\nRequire: Loop to be disable.\nArguments: None.'
HELP_SHUFFLE = 'Shuffle the songs playing.'
HELP_SHUFFLE_LONG = 'Randomly shuffle the songs in the queue.\n\nArguments: None.'
HELP_PLAY = 'Plays a song.'
HELP_PLAY_LONG = 'Play a song in discord. \n\nRequire: You to be connected to a voice channel.\nArguments: Youtube or Spotify song/playlist link or the title of the song to be searched in Youtube.'
HELP_HISTORY = f'Show the history of played songs.'
HELP_HISTORY_LONG = f'Show the last {MAX_SONGS_HISTORY} played songs'
HELP_MOVE = 'Moves a song from position x to y in queue.'
HELP_MOVE_LONG = 'Moves a song from position x to position y in queue.\n\nRequire: Positions to be both valid numbers.\nArguments: 1º Number => Initial position, 2º Number => Destination position. Both numbers could be -1 to refer to the last song in queue.\nDefault: By default, if the second number is not passed, it will be 1, moving the selected song to 1º position.'
HELP_REMOVE = 'Remove a song in position x.'
HELP_REMOVE_LONG = 'Remove a song from queue in the position passed.\n\nRequire: Position to be a valid number.\nArguments: 1º Number => Position in queue of the song.'
HELP_RESET = 'Reset the Player of the server.'
HELP_RESET_LONG = 'Reset the Player of the server. Recommended if you find any type of error.\n\nArguments: None'
HELP_HELP = f'Use {BOT_PREFIX}help "command" for more info.'
HELP_HELP_LONG = f'Use {BOT_PREFIX}help command for more info about the command selected.'
HELP_INVITE = 'Send the invite URL to call Vulkan to your server.'
HELP_INVITE_LONG = 'Send an message in text channel with a URL to be used to invite Vulkan to your own server.\n\nArguments: None.'
HELP_RANDOM = 'Return a random number between 1 and x.'
HELP_RANDOM_LONG = 'Send a randomly selected number between 1 and the number you pass.\n\nRequired: Number to be a valid number.\nArguments: 1º Any number to be used as range.'
HELP_CHOOSE = 'Choose randomly one item passed.'
HELP_CHOOSE_LONG = 'Choose randomly one item passed in this command.\n\nRequire: Itens to be separated by comma.\nArguments: As much as you want.'
HELP_CARA = 'Return cara or coroa.'
HELP_CARA_LONG = 'Return cara or coroa.'

View File

@@ -1,5 +1,5 @@
discord.py
discord.py==1.7.3
discord.py[voice]
yt_dlp
spotipy
python-decouple
python-decouple==3.5
spotipy==2.19.0
yt-dlp==2021.12.1

View File

@@ -3,6 +3,7 @@ from discord import Client
from discord.ext.commands.errors import CommandNotFound, MissingRequiredArgument
from discord.ext import commands
from config import config
from config import help
class Control(commands.Cog):
@@ -11,11 +12,12 @@ class Control(commands.Cog):
def __init__(self, bot: Client):
self.__bot = bot
self.__comandos = {
'MUSIC': ['resume', 'pause', 'loop', 'stop', 'skip', 'play', 'queue', 'clear', 'np', 'shuffle', 'move', 'remove', 'reset'],
'WARFRAME': ['warframe'],
'RANDOM': ['escolha', 'cara', 'random'],
'HELP': ['help'],
'OTHERS': ['frase', 'drop']
'MUSIC': ['resume', 'pause', 'loop', 'stop',
'skip', 'play', 'queue', 'clear',
'np', 'shuffle', 'move', 'remove',
'reset', 'prev', 'history'],
'RANDOM': ['choose', 'cara', 'random']
}
@commands.Cog.listener()
@@ -27,44 +29,93 @@ class Control(commands.Cog):
@commands.Cog.listener()
async def on_command_error(self, ctx, error):
if isinstance(error, MissingRequiredArgument):
await ctx.channel.send(f'Falta argumentos. Digite {config.BOT_PREFIX}help para ver todos os comandos\n\nOu tente {config.BOT_PREFIX}command help para mais informações')
embed = discord.Embed(
title=config.ERROR_TITLE,
description=config.ERROR_MISSING_ARGUMENTS,
colour=config.COLOURS['red']
)
await ctx.send(embed=embed)
elif isinstance(error, CommandNotFound):
await ctx.channel.send(f'O comando não existe')
embed = discord.Embed(
title=config.ERROR_TITLE,
description=config.COMMAND_NOT_FOUND,
colour=config.COLOURS['red']
)
await ctx.send(embed=embed)
else:
await ctx.channel.send(f'Teve um erro aí bicho')
raise error
print(error)
embed = discord.Embed(
title=config.ERROR_TITLE,
description=config.UNKNOWN_ERROR,
colour=config.COLOURS['red']
)
await ctx.send(embed=embed)
@commands.command(name="help", alisases=['ajuda'], help=config.HELP_HELP)
async def help_msg(self, ctx):
helptxt = ''
help_music = '-- MUSIC\n'
help_random = '-- RANDOM\n'
help_warframe = '-- WARFRAME\n'
help_help = '-- HELP\n'
help_others = '-- OTHERS\n'
@commands.command(name="help", help=help.HELP_HELP, description=help.HELP_HELP_LONG, aliases=['h', 'ajuda'])
async def help_msg(self, ctx, command_help=''):
if command_help != '':
for command in self.__bot.commands:
if command.name == command_help:
txt = command.description if command.description else command.help
for command in self.__bot.commands:
if command.name in self.__comandos['MUSIC']:
help_music += f'**{command}** - {command.help}\n'
elif command.name in self.__comandos['HELP']:
help_help += f'**{command}** - {command.help}\n'
elif command.name in self.__comandos['OTHERS']:
help_others += f'**{command}** - {command.help}\n'
elif command.name in self.__comandos['WARFRAME']:
help_warframe += f'**{command}** - {command.help}\n'
else:
help_random += f'**{command}** - {command.help}\n'
embedhelp = discord.Embed(
title=f'**Description of {command_help}** command',
description=txt,
colour=config.COLOURS['blue']
)
helptxt = f'{help_music}\n{help_warframe}\n{help_random}\n{help_others}\n{help_help}'
await ctx.send(embed=embedhelp)
return
embedhelp = discord.Embed(
colour=config.COLOURS['grey'],
title=f'Comandos do {self.__bot.user.name}',
description=helptxt
embedhelp = discord.Embed(
title='Command Help',
description=f'Command {command_help} Not Found',
colour=config.COLOURS['red']
)
await ctx.send(embed=embedhelp)
else:
helptxt = ''
help_music = '🎧 `MUSIC`\n'
help_random = '🎲 `RANDOM`\n'
help_help = '👾 `HELP`\n'
for command in self.__bot.commands:
if command.name in self.__comandos['MUSIC']:
help_music += f'**{command}** - {command.help}\n'
elif command.name in self.__comandos['RANDOM']:
help_random += f'**{command}** - {command.help}\n'
else:
help_help += f'**{command}** - {command.help}\n'
helptxt = f'\n{help_music}\n{help_help}\n{help_random}'
embedhelp = discord.Embed(
title=f'**Available Commands of {self.__bot.user.name}**',
description=helptxt,
colour=config.COLOURS['blue']
)
embedhelp.set_thumbnail(url=self.__bot.user.avatar_url)
await ctx.send(embed=embedhelp)
@commands.command(name='invite', help=help.HELP_INVITE, description=help.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 = config.INVITE_MESSAGE.format(invite_url)
embed = discord.Embed(
title="Invite Vulkan",
description=txt,
colour=config.COLOURS['blue']
)
embedhelp.set_thumbnail(url=self.__bot.user.avatar_url)
await ctx.send(embed=embedhelp)
await ctx.send(embed=embed)
def setup(bot):

View File

@@ -2,40 +2,56 @@ import discord
from discord.ext import commands
from config import config
from config import help
from vulkan.music.Player import Player
from vulkan.music.utils import *
class Music(commands.Cog):
def __init__(self, bot):
def __init__(self, bot) -> None:
self.__guilds = {}
self.__bot: discord.Client = bot
@commands.Cog.listener()
async def on_ready(self):
async def on_ready(self) -> None:
"""Load a player for each guild that the Bot are"""
for guild in self.__bot.guilds:
self.__guilds[guild] = Player(self.__bot, guild)
print(f'Player for guild {guild.name} created')
@commands.command(name="play", help=config.HELP_PLAY, aliases=['p', 'tocar'])
async def play(self, ctx, *args):
user_input = " ".join(args)
@commands.Cog.listener()
async def on_guild_join(self, guild) -> None:
"""Load a player when joining a guild"""
self.__guilds[guild] = Player(self.__bot, guild)
print(f'Player for guild {guild.name} created')
@commands.Cog.listener()
async def on_guild_remove(self, guild) -> None:
"""Removes the player of the guild if banned"""
if guild in self.__guilds.keys():
self.__guilds.pop(guild, None)
print(f'Player for guild {guild.name} destroyed')
@commands.command(name="play", help=help.HELP_PLAY, description=help.HELP_PLAY_LONG, aliases=['p', 'tocar'])
async def play(self, ctx, *args) -> None:
track = " ".join(args)
requester = ctx.author.name
player = self.__get_player(ctx)
if player == None:
await self.__send_embed(ctx, description=config.NO_GUILD, colour_name='red')
await self.__send_embed(ctx, config.ERROR_TITLE, config.NO_GUILD, 'red')
return
if is_connected(ctx) == None:
result = await player.connect(ctx)
if result['success'] == False:
await self.__send_embed(ctx, description=result['reason'], colour_name='red')
success = await player.connect(ctx)
if success == False:
await self.__send_embed(ctx, config.IMPOSSIBLE_MOVE, config.NO_CHANNEL, 'red')
return
await player.play(ctx, user_input, requester)
await player.play(ctx, track, requester)
@commands.command(name="queue", help=config.HELP_QUEUE, aliases=['q', 'fila'])
async def queue(self, ctx):
@commands.command(name="queue", help=help.HELP_QUEUE, description=help.HELP_QUEUE_LONG, aliases=['q', 'fila'])
async def queue(self, ctx) -> None:
player = self.__get_player(ctx)
if player == None:
return
@@ -43,62 +59,84 @@ class Music(commands.Cog):
embed = await player.queue()
await ctx.send(embed=embed)
@commands.command(name="skip", help=config.HELP_SKIP, aliases=['pular'])
async def skip(self, ctx):
@commands.command(name="skip", help=help.HELP_SKIP, description=help.HELP_SKIP_LONG, aliases=['s', 'pular'])
async def skip(self, ctx) -> None:
player = self.__get_player(ctx)
if player == None:
return
else:
await player.skip()
await player.skip(ctx)
@commands.command(name='stop', help=config.HELP_STOP, aliases=['parar'])
async def stop(self, ctx):
@commands.command(name='stop', help=help.HELP_STOP, description=help.HELP_STOP_LONG, aliases=['parar'])
async def stop(self, ctx) -> None:
player = self.__get_player(ctx)
if player == None:
return
else:
await player.stop()
@commands.command(name='pause', help=config.HELP_PAUSE, aliases=['pausar'])
async def pause(self, ctx):
@commands.command(name='pause', help=help.HELP_PAUSE, description=help.HELP_PAUSE_LONG, aliases=['pausar'])
async def pause(self, ctx) -> None:
player = self.__get_player(ctx)
if player == None:
print('No player')
return
else:
success = await player.pause()
if success:
await self.__send_embed(ctx, description='Song paused', colour_name='blue')
await self.__send_embed(ctx, config.SONG_PLAYER, config.SONG_PAUSED, 'blue')
@commands.command(name='resume', help=config.HELP_RESUME, aliases=['soltar'])
async def resume(self, ctx):
@commands.command(name='resume', help=help.HELP_RESUME, description=help.HELP_RESUME_LONG, aliases=['soltar'])
async def resume(self, ctx) -> None:
player = self.__get_player(ctx)
if player == None:
return
else:
success = await player.resume()
if success:
await self.__send_embed(ctx, description='Song Playing', colour_name='blue')
await self.__send_embed(ctx, config.SONG_PLAYER, config.SONG_RESUMED, 'blue')
@commands.command(name='loop', help=config.HELP_LOOP, aliases=['repeat'])
async def loop(self, ctx, args: str):
@commands.command(name='prev', help=help.HELP_PREV, description=help.HELP_PREV_LONG, aliases=['anterior'])
async def prev(self, ctx) -> None:
player = self.__get_player(ctx)
if player == None:
return
if is_connected(ctx) == None:
success = await player.connect(ctx)
if success == False:
await self.__send_embed(ctx, config.IMPOSSIBLE_MOVE, config.NO_CHANNEL, 'red')
return
await player.play_prev(ctx)
@commands.command(name='history', help=help.HELP_HISTORY, description=help.HELP_HISTORY_LONG, aliases=['historico'])
async def history(self, ctx) -> None:
player = self.__get_player(ctx)
if player == None:
return
else:
result = await player.loop(args)
await self.__send_embed(ctx, description=result, colour_name='blue')
embed = player.history()
await ctx.send(embed=embed)
@commands.command(name='clear', help=config.HELP_CLEAR, aliases=['limpar'])
async def clear(self, ctx):
@commands.command(name='loop', help=help.HELP_LOOP, description=help.HELP_LOOP_LONG, aliases=['l', 'repeat'])
async def loop(self, ctx, args: str) -> None:
player = self.__get_player(ctx)
if player == None:
return
else:
description = await player.loop(args)
await self.__send_embed(ctx, config.SONG_PLAYER, description, 'blue')
@commands.command(name='clear', help=help.HELP_CLEAR, description=help.HELP_CLEAR_LONG, aliases=['c', 'limpar'])
async def clear(self, ctx) -> None:
player = self.__get_player(ctx)
if player == None:
return
else:
await player.clear()
@commands.command(name='np', help=config.HELP_NP, aliases=['playing', 'now'])
async def now_playing(self, ctx):
@commands.command(name='np', help=help.HELP_NP, description=help.HELP_NP_LONG, aliases=['playing', 'now'])
async def now_playing(self, ctx) -> None:
player = self.__get_player(ctx)
if player == None:
return
@@ -107,46 +145,45 @@ class Music(commands.Cog):
await self.__clean_messages(ctx)
await ctx.send(embed=embed)
@commands.command(name='shuffle', help=config.HELP_SHUFFLE, aliases=['aleatorio'])
async def shuffle(self, ctx):
@commands.command(name='shuffle', help=help.HELP_SHUFFLE, description=help.HELP_SHUFFLE_LONG, aliases=['aleatorio'])
async def shuffle(self, ctx) -> None:
player = self.__get_player(ctx)
if player == None:
return
else:
result = await player.shuffle()
await self.__send_embed(ctx, description=result, colour_name='blue')
description = await player.shuffle()
await self.__send_embed(ctx, config.SONG_PLAYER, description, 'blue')
@commands.command(name='move', help=config.HELP_MOVE, aliases=['mover'])
async def move(self, ctx, pos1, pos2='1'):
@commands.command(name='move', help=help.HELP_MOVE, description=help.HELP_MOVE_LONG, aliases=['m', 'mover'])
async def move(self, ctx, pos1, pos2='1') -> None:
player = self.__get_player(ctx)
if player == None:
return
else:
result = await player.move(pos1, pos2)
await self.__send_embed(ctx, description=result, colour_name='blue')
description = await player.move(pos1, pos2)
await self.__send_embed(ctx, config.SONG_PLAYER, description, 'blue')
@commands.command(name='remove', help=config.HELP_REMOVE, aliases=['remover'])
async def remove(self, ctx, position):
"""Remove a song from the queue in the position"""
@commands.command(name='remove', help=help.HELP_REMOVE, description=help.HELP_REMOVE_LONG, aliases=['remover'])
async def remove(self, ctx, position) -> None:
player = self.__get_player(ctx)
if player == None:
return
else:
result = await player.remove(position)
await self.__send_embed(ctx, description=result, colour_name='blue')
description = await player.remove(position)
await self.__send_embed(ctx, config.SONG_PLAYER, description, 'blue')
@commands.command(name='reset', help=config.HELP_RESET, aliases=['resetar'])
async def reset(self, ctx):
@commands.command(name='reset', help=help.HELP_RESET, description=help.HELP_RESET_LONG, aliases=['resetar'])
async def reset(self, ctx) -> None:
player = self.__get_player(ctx)
if player != None:
await player.stop()
self.__guilds[ctx.guild] = Player(self.__bot, ctx.guild)
async def __send_embed(self, ctx, title='', description='', colour_name='grey'):
async def __send_embed(self, ctx, title='', description='', colour='grey') -> None:
try:
colour = config.COLOURS[colour_name]
except Exception as e:
colour = config.COLOURS[colour]
except:
colour = config.COLOURS['grey']
embedvc = discord.Embed(
@@ -156,8 +193,7 @@ class Music(commands.Cog):
)
await ctx.send(embed=embedvc)
async def __clean_messages(self, ctx):
"""Clear Bot messages if send recently"""
async def __clean_messages(self, ctx) -> None:
last_messages = await ctx.channel.history(limit=5).flatten()
for message in last_messages:
@@ -165,12 +201,14 @@ class Music(commands.Cog):
if message.author == self.__bot.user:
if len(message.embeds) > 0:
embed = message.embeds[0]
if embed.title == 'Song Playing Now' or embed.title == 'Song Looping Now':
await message.delete()
if len(embed.fields) > 0:
if embed.fields[0].name == 'Uploader:':
await message.delete()
except:
continue
def __get_player(self, ctx):
def __get_player(self, ctx) -> Player:
try:
return self.__guilds[ctx.guild]
except:

View File

@@ -1,59 +0,0 @@
from discord.client import Client
import requests
import json
from config import config
from discord.ext import commands
from random import random as rand
class Phrases(commands.Cog):
"""Deal with the generation of motivational phrases"""
def __init__(self, bot: Client):
self.__bot = bot
@commands.command(name='frase', help=config.HELP_FRASE)
async def phrase(self, ctx):
"""Send some phrase to the requester"""
secret = await self.__calculate_rgn()
if secret != None:
await ctx.send(secret)
else:
phrase = await self.__get_phrase()
await ctx.send(phrase)
async def __calculate_rgn(self):
"""Calculate the chance from the phrase function return a secret custom message"""
x = rand()
if x < 0.15:
return config.SECRET_MESSAGE
else:
return None
async def __get_phrase(self):
"""Get the phrase from the server"""
tries = 0
while True:
tries += 1
if tries > config.MAX_API_PHRASES_TRIES:
return 'O banco de dados dos cara tá off, bando de vagabundo, tenta depois aí bicho'
try:
response = requests.get(config.PHRASES_API)
data = json.loads(response.content)
phrase = data['quoteText']
author = data['quoteAuthor']
if phrase == '' or author == '':
continue
text = f'{phrase} \nBy: {author}'
return text
except Exception as e:
continue
def setup(bot):
bot.add_cog(Phrases(bot))

View File

@@ -2,6 +2,7 @@ from random import randint, random
import discord
from discord.ext import commands
from config import config
from config import help
class Random(commands.Cog):
@@ -10,14 +11,14 @@ class Random(commands.Cog):
def __init__(self, bot):
self.__bot = bot
@commands.command(name='random', help=config.HELP_RANDOM)
async def random(self, ctx, arg: str):
@commands.command(name='random', help=help.HELP_RANDOM, description=help.HELP_RANDOM_LONG)
async def random(self, ctx, arg: str) -> None:
try:
arg = int(arg)
except Exception as e:
except:
embed = discord.Embed(
description='Manda um número aí ow animal',
description=config.ERROR_NUMBER,
colour=config.COLOURS['red']
)
await ctx.send(embed=embed)
@@ -32,14 +33,14 @@ class Random(commands.Cog):
x = randint(a, b)
embed = discord.Embed(
title=f'Número Aleatório entre {a, b}',
title=f'Random number between [{a, b}]',
description=x,
colour=config.COLOURS['green']
)
await ctx.send(embed=embed)
@commands.command(name='cara', help=config.HELP_CARA)
async def cara(self, ctx):
@commands.command(name='cara', help=help.HELP_CARA, description=help.HELP_CARA_LONG)
async def cara(self, ctx) -> None:
x = random()
if x < 0.5:
result = 'cara'
@@ -48,13 +49,13 @@ class Random(commands.Cog):
embed = discord.Embed(
title='Cara Cora',
description=f'Resultado: {result}',
description=f'Result: {result}',
colour=config.COLOURS['green']
)
await ctx.send(embed=embed)
@commands.command(name='escolha', help=config.HELP_ESCOLHA)
async def escolher(self, ctx, *args: str):
@commands.command(name='choose', help=help.HELP_CHOOSE, description=help.HELP_CHOOSE_LONG)
async def choose(self, ctx, *args: str) -> None:
try:
user_input = " ".join(args)
itens = user_input.split(sep=',')
@@ -62,16 +63,16 @@ class Random(commands.Cog):
index = randint(0, len(itens)-1)
embed = discord.Embed(
title='Escolha de algo',
title='Choose something',
description=itens[index],
colour=config.COLOURS['green']
)
await ctx.send(embed=embed)
except Exception as e:
except:
embed = discord.Embed(
title='Escolha de algo',
description='Erro: Envie várias coisas separadas por vírgula',
colour=config.COLOURS['green']
title='Choose something.',
description=f'Error: Use {config.BOT_PREFIX}help choose to understand this command.',
colour=config.COLOURS['red']
)
await ctx.send(embed=embed)

View File

@@ -1,118 +0,0 @@
import requests
import json
import discord
from discord.ext import commands
from config import config
from discord import Embed
class Warframe(commands.Cog):
"""Deal with the generation of warframe data"""
def __init__(self, bot: discord.Client):
self.__bot = bot
self.__open_functions = ['cetus', 'cambion', 'fissures']
@commands.command(name='warframe', help=config.HELP_WARFRAME)
async def warframe(self, ctx, arg) -> Embed:
if arg in self.__open_functions:
function = getattr(Warframe, f'_Warframe__{arg}')
embed = await function(self)
await ctx.send(embed=embed)
else:
info = f'Warframe commands: {self.__open_functions}'
embed = Embed(
title='Invalid Command',
description=info,
colour=config.COLOURS['blue']
)
await ctx.send(embed=embed)
async def __cetus(self) -> Embed:
description = await self.__get_cetus()
embed = discord.Embed(
title='Warframe Cetus Timing',
description=description,
colour=config.COLOURS['blue']
)
return embed
async def __get_cetus(self) -> str:
"""Return the information of the Warframe API"""
tries = 0
while True:
tries += 1
if tries > config.MAX_API_CETUS_TRIES:
return 'Os DE baiano não tão com o banco de dados ligado'
try:
response = requests.get(config.CETUS_API)
data = json.loads(response.content)
short = data['shortString']
return short
except Exception as e:
continue
async def __cambion(self) -> Embed:
description = await self.__get_cambion()
embed = discord.Embed(
title='Warframe Cambion Timing',
description=description,
colour=config.COLOURS['blue']
)
return embed
async def __get_cambion(self) -> str:
"""Return the information of the Warframe API"""
tries = 0
while True:
tries += 1
if tries > config.MAX_API_CAMBION_TRIES:
return 'Os DE baiano não tão com o banco de dados ligado'
try:
response = requests.get(config.CAMBION_API)
data = json.loads(response.content)
info = f'**Active:** {data["active"]}\n**Time Left:** {data["timeLeft"]}'
return info
except:
continue
async def __fissures(self) -> Embed:
description = await self.__get_fissures()
embed = discord.Embed(
title='Warframe Fissures Status',
description=description,
colour=config.COLOURS['blue']
)
return embed
async def __get_fissures(self) -> str:
"""Return the information of the Warframe API"""
tries = 0
while True:
tries += 1
if tries > config.MAX_API_FISSURES_TRIES:
return 'Os DE baiano não tão com o banco de dados ligado'
try:
response = requests.get(config.FISSURES_API)
data = json.loads(response.content)
info = ''
for pos, fissure in enumerate(data, start=1):
info += f'`{pos}` - **Mission:** {fissure["missionType"]} | **Type:** {fissure["tier"]} | **Timing:** {fissure["eta"]} | **Storm:** {fissure["isStorm"]}\n'
return info
except Exception as e:
continue
def setup(bot):
bot.add_cog(Warframe(bot))

View File

@@ -64,7 +64,6 @@ class Downloader():
except (ExtractorError, DownloadError) as e:
return None
else:
print('Invalid type of playlist URL')
return None
async def preload(self, songs: list) -> None:

View File

@@ -24,10 +24,6 @@ class IPlaylist(ABC):
def next_song(self):
pass
@abstractmethod
def prev_song(self):
pass
@abstractmethod
def add_song(self, identifier: str) -> None:
pass

View File

@@ -6,6 +6,7 @@ import datetime
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 *
@@ -21,45 +22,60 @@ class Player(commands.Cog):
self.__timer = Timer(self.__timeout_handler)
self.__playing = False
# Flag to control if the player should stop totally the playing
self.__force_stop = False
self.YDL_OPTIONS = {'format': 'bestaudio', 'noplaylist': 'True'}
self.FFMPEG_OPTIONS = {'before_options': '-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5',
'options': '-vn'}
async def connect(self, ctx):
async def connect(self, ctx) -> bool:
if not ctx.author.voice:
return {'success': False, 'reason': config.NO_CHANNEL}
return False
if self.__guild.voice_client == None:
await ctx.author.voice.channel.connect(reconnect=True, timeout=None)
return {'success': True, 'reason': ''}
return True
def __play_next(self, error, ctx) -> None:
if self.__force_stop: # If it's forced to stop player
self.__force_stop = False
return
def __play_next(self, error, ctx):
song = self.__playlist.next_song()
if song != None: # If there is not a song for the song
if song != None:
coro = self.__play_music(ctx, song)
self.__bot.loop.create_task(coro)
else:
self.__playing = False
async def __play_music(self, ctx, song):
self.__playing = True
async def __play_music(self, ctx, song: Song) -> None:
try:
source = self.__ensure_source(song)
if source == None:
self.__play_next(None, ctx)
player = discord.FFmpegPCMAudio(song.source, **self.FFMPEG_OPTIONS)
self.__guild.voice_client.play(
player, after=lambda e: self.__play_next(e, ctx))
self.__playing = True
self.__timer.cancel()
self.__timer = Timer(self.__timeout_handler)
player = discord.FFmpegPCMAudio(song.source, **self.FFMPEG_OPTIONS)
self.__guild.voice_client.play(
player, after=lambda e: self.__play_next(e, ctx))
await ctx.invoke(self.__bot.get_command('np'))
self.__timer.cancel()
self.__timer = Timer(self.__timeout_handler)
songs = self.__playlist.songs_to_preload
await self.__down.preload(songs)
await ctx.invoke(self.__bot.get_command('np'))
async def play(self, ctx, track, requester) -> str:
songs = self.__playlist.songs_to_preload
await self.__down.preload(songs)
except:
self.__play_next(None, ctx)
async def play(self, ctx, track=str, requester=str) -> str:
try:
songs_names, provider = self.__searcher.search(track)
if provider == Provider.Unknown:
if provider == Provider.Unknown or songs_names == None:
embed = discord.Embed(
title=config.ERROR_TITLE,
description=config.INVALID_INPUT,
@@ -98,67 +114,81 @@ class Player(commands.Cog):
return
elif not self.__playing:
embed = discord.Embed(
title=config.SONG_QUEUE_TITLE,
title=config.SONG_PLAYER,
description=config.SONG_ADDED.format(song.title),
colour=config.COLOURS['blue'])
await ctx.send(embed=embed)
else:
embed = self.__format_embed(song.info, config.SONG_ADDED)
embed = self.__format_embed(song.info, config.SONG_ADDED_TWO)
await ctx.send(embed=embed)
else:
embed = discord.Embed(
title=config.SONG_QUEUE_TITLE,
title=config.SONG_PLAYER,
description=config.SONGS_ADDED.format(songs_quant),
colour=config.COLOURS['blue'])
await ctx.send(embed=embed)
if not self.__playing:
try_another = True
first_song = self.__playlist.next_song()
await self.__play_music(ctx, first_song)
while try_another: # This will ensure the first song source to be ready
first_song = self.__playlist.next_song()
if first_song == None:
embed = discord.Embed(
title=config.ERROR_TITLE,
description=config.DOWNLOADING_ERROR,
colour=config.COLOURS['blue'])
await ctx.send(embed=embed)
break
async def play_prev(self, ctx) -> None:
"""Stop the currently playing cycle, load the previous song and play"""
if self.__playlist.looping_one or self.__playlist.looping_all: # Do not allow play if loop
embed = discord.Embed(
title=config.SONG_PLAYER,
description=config.LOOP_ON,
colour=config.COLOURS['blue']
)
await ctx.send(embed=embed)
return
while True:
if first_song.source != None: # If song got downloaded
try_another = False
break
song = self.__playlist.prev_song() # Prepare the prev song to play again
if song == None:
embed = discord.Embed(
title=config.SONG_PLAYER,
description=config.NOT_PREVIOUS,
colour=config.COLOURS['blue']
)
await ctx.send(embed=embed)
else:
if self.__guild.voice_client.is_playing() or self.__guild.voice_client.is_paused():
# Will forbidden next_song to execute after stopping current player
self.__force_stop = True
self.__guild.voice_client.stop()
self.__playing = False
if first_song.problematic: # If song got any error, try another one
break
if first_song != None:
await self.__play_music(ctx, first_song)
await self.__play_music(ctx, song)
async def queue(self) -> discord.Embed:
if self.__playlist.looping_one:
info = self.__playlist.current.info
title = 'Song Looping Now'
title = config.ONE_SONG_LOOPING
return self.__format_embed(info, title)
songs_preload = self.__playlist.songs_to_preload
await self.__down.preload(songs_preload)
total_time = format_time(sum([int(song.duration if song.duration else 0)
for song in songs_preload])) # Sum the duration
total_songs = len(self.__playlist)
text = f'Total musics: {total_songs} | Duration: `{total_time}` downloaded \n\n'
for pos, song in enumerate(songs_preload, start=1):
title = song.title if song.title else 'Downloading...'
text += f"**`{pos}` - ** {title} - `{format_time(song.duration)}`\n"
if len(songs_preload) == 0:
title = config.SONG_PLAYER
text = config.EMPTY_QUEUE
title = 'Songs in Queue'
if len(songs_preload) > 0:
if self.__playlist.looping_all:
title = 'Repeating All'
else:
text = 'There is no musics in queue'
if self.__playlist.looping_all:
title = config.ALL_SONGS_LOOPING
else:
title = config.QUEUE_TITLE
await self.__down.preload(songs_preload)
total_time = format_time(sum([int(song.duration if song.duration else 0)
for song in songs_preload]))
total_songs = len(self.__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 config.SONG_DOWNLOADING
text += f"**`{pos}` - ** {song_name} - `{format_time(song.duration)}`\n"
embed = discord.Embed(
title=title,
@@ -168,13 +198,40 @@ class Player(commands.Cog):
return embed
async def skip(self) -> bool:
async def skip(self, ctx) -> bool:
if self.__playlist.looping_one:
embed = discord.Embed(
title=config.SONG_PLAYER,
description=config.LOOP_ON,
colour=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) -> discord.Embed:
history = self.__playlist.songs_history
if len(history) == 0:
text = config.HISTORY_EMPTY
else:
text = f'\n📜 History Length: {len(history)} | Max: {config.MAX_SONGS_HISTORY}\n'
for pos, song in enumerate(history, start=1):
text += f"**`{pos}` - ** {song.title} - `{format_time(song.duration)}`\n"
embed = discord.Embed(
title=config.HISTORY_TITLE,
description=text,
colour=config.COLOURS['blue']
)
return embed
async def stop(self) -> bool:
if self.__guild.voice_client == None:
return False
@@ -202,8 +259,11 @@ class Player(commands.Cog):
self.__guild.voice_client.resume()
return True
async def loop(self, args: str):
async def loop(self, args: str) -> str:
args = args.lower()
if self.__playlist.current == None:
return config.PLAYER_NOT_PLAYING
if args == 'one':
description = self.__playlist.loop_one()
elif args == 'all':
@@ -211,7 +271,7 @@ class Player(commands.Cog):
elif args == 'off':
description = self.__playlist.loop_off()
else:
description = 'Comando Loop\nOne - Repete a música atual\nAll - Repete as músicas atuais\nOff - Desativa o loop'
description = help.HELP_LONG_LOOP
return description
@@ -219,10 +279,18 @@ class Player(commands.Cog):
self.__playlist.clear()
async def now_playing(self) -> discord.Embed:
if not self.__playing:
embed = discord.Embed(
title=config.SONG_PLAYER,
description=config.PLAYER_NOT_PLAYING,
colour=config.COLOURS['blue']
)
return embed
if self.__playlist.looping_one:
title = 'Song Looping Now'
title = config.ONE_SONG_LOOPING
else:
title = 'Song Playing Now'
title = config.SONG_PLAYING
current_song = self.__playlist.current
embed = self.__format_embed(current_song.info, title)
@@ -235,17 +303,20 @@ class Player(commands.Cog):
songs = self.__playlist.songs_to_preload
await self.__down.preload(songs)
return 'Musics shuffled successfully'
return config.SONGS_SHUFFLED
except:
return 'An error ocurred :/'
return config.ERROR_SHUFFLING
async def move(self, pos1, pos2='1') -> str:
if not self.__playing:
return config.PLAYER_NOT_PLAYING
try:
pos1 = int(pos1)
pos2 = int(pos2)
except:
return 'This command require a number'
return config.ERROR_NUMBER
result = self.__playlist.move_songs(pos1, pos2)
@@ -255,16 +326,19 @@ class Player(commands.Cog):
async def remove(self, position) -> str:
"""Remove a song from the queue in the position"""
if not self.__playing:
return config.PLAYER_NOT_PLAYING
try:
position = int(position)
except:
return 'This command require a number'
return config.ERROR_NUMBER
result = self.__playlist.remove_song(position)
return result
def __format_embed(self, info, title='') -> discord.Embed:
def __format_embed(self, info=dict, title='') -> discord.Embed:
"""Configure the embed to show the song information"""
embedvc = discord.Embed(
title=title,
@@ -306,3 +380,11 @@ class Player(commands.Cog):
self.__playlist.clear()
self.__playlist.loop_off()
await self.__guild.voice_client.disconnect()
def __ensure_source(self, song: Song) -> str:
while True:
if song.source != None: # If song got downloaded
return song.source
if song.problematic: # If song got any error
return None

View File

@@ -12,13 +12,16 @@ class Playlist(IPlaylist):
def __init__(self) -> None:
self.__queue = deque() # Store the musics to play
self.__songs_history = deque() # Store the musics played
self.__name_history = deque() # Store the name of musics played
self.__looping_one = False
self.__looping_all = False
self.__current: Song = None
@property
def songs_history(self) -> deque:
return self.__songs_history
@property
def looping_one(self) -> bool:
return self.__looping_one
@@ -39,43 +42,46 @@ class Playlist(IPlaylist):
return len(self.__queue)
def next_song(self) -> Song:
"""Return the next song to play"""
"""Return the next song to play in a normal playlist flow"""
if self.__current == None and len(self.__queue) == 0:
# If not playing and nothing to play
return None
# If playing
played_song = self.__current
# Check if need to repeat the played song
if self.__looping_one: # Insert the current song to play again
self.__queue.appendleft(played_song)
# Att played song info
if played_song != None:
if not self.__looping_one and not self.__looping_all:
if played_song.problematic == False:
self.__songs_history.appendleft(played_song)
if self.__looping_all: # Insert the current song in the end of queue
self.__queue.append(played_song)
if len(self.__songs_history) > config.MAX_SONGS_HISTORY:
self.__songs_history.pop() # Remove the older
while True: # Try to get the source of next song
if len(self.__queue) == 0: # If no more song to play, return None
return None
elif self.__looping_one: # Insert the current song to play again
self.__queue.appendleft(played_song)
# Att the current with the first one
self.__current = self.__queue[0]
self.__queue.popleft() # Remove the current from queue
self.__name_history.append(
self.__current.identifier) # Add to name history
self.__songs_history.append(self.__current) # Add to song history
elif self.__looping_all: # Insert the current song in the end of queue
self.__queue.append(played_song)
return self.__current
# Get the new song
if len(self.__queue) == 0:
return None
self.__current = self.__queue.popleft()
return self.__current
def prev_song(self) -> Song:
"""Return the source of the last song played
Return None or the source of the prev song
"""
"""If playing return it to queue and return the previous song to play"""
if len(self.__songs_history) == 0:
return None
else:
return self.__songs_history[0].source
if self.__current != None:
self.__queue.appendleft(self.__current)
last_song = self.__songs_history.popleft() # Get the last song
self.__current = last_song
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"""
@@ -94,7 +100,6 @@ class Playlist(IPlaylist):
def clear(self) -> None:
"""Clear the songs to play song history"""
self.__queue.clear()
self.__songs_history.clear()
def loop_one(self) -> str:
"""Try to start the loop of the current song
@@ -102,12 +107,13 @@ class Playlist(IPlaylist):
Return: Embed descrition to show to user
"""
if self.__looping_all == True:
return 'Vulkan already looping one music, disable loop first'
return config.LOOP_ALL_ON
elif self.__looping_one == True:
return "I'm already doing this, you dumb ass"
return config.LOOP_ONE_ALREADY_ON
else:
self.__looping_one = True
return 'Repeating the current song'
return config.LOOP_ONE_ACTIVATE
def loop_all(self) -> str:
"""Try to start the loop of all songs
@@ -115,21 +121,23 @@ class Playlist(IPlaylist):
Return: Embed descrition to show to user
"""
if self.__looping_one == True:
return 'Vulkan already looping one music, disable loop first'
return config.LOOP_ONE_ON
elif self.__looping_all == True:
return "I'm already doing this, you dumb ass"
return config.LOOP_ALL_ALREADY_ON
else:
self.__looping_all = True
return 'Repeating all songs in queue'
return config.LOOP_ALL_ACTIVATE
def loop_off(self) -> str:
"""Disable both types of loop"""
if self.__looping_all == False and self.__looping_one == False:
return "The loop is already off, you fucking dick head"
return config.LOOP_ALREADY_DISABLE
self.__looping_all = False
self.__looping_one = False
return 'Loop disable'
return config.LOOP_DISABLE
def destroy_song(self, song_destroy: Song) -> None:
"""Destroy a song object from the queue"""
@@ -150,7 +158,7 @@ class Playlist(IPlaylist):
pos2 = len(self.__queue)
if pos2 not in range(1, len(self.__queue) + 1) or pos1 not in range(1, len(self.__queue) + 1):
return 'Numbers must be between 1 and queue length, or -1 for the last song'
return config.LENGTH_ERROR
try:
song1 = self.__queue[pos1-1]
@@ -162,18 +170,26 @@ class Playlist(IPlaylist):
song1_name = song1.title if song1.title else song1.identifier
song2_name = song2.title if song2.title else song2.identifier
return f'Song `{song1_name}` in position `{pos1}` moved with `{song2_name}` in position `{pos2}` successfully'
except Exception as e:
print(e)
return 'There was a problem with the moving of songs'
return config.SONG_MOVED_SUCCESSFULLY.format(song1_name, pos1, song2_name, pos2)
except:
return config.ERROR_MOVING
def remove_song(self, position) -> tuple:
def remove_song(self, position) -> str:
if position not in range(1, len(self.__queue) + 1) and position != -1:
return 'Numbers must be between 1 and queue length, or -1 for the last song'
return config.LENGTH_ERROR
else:
song = self.__queue[position-1]
self.__queue.remove(song)
song_name = song.title if song.title else song.identifier
return f'Song `{song_name}` removed successfully'
return 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'
titles.append(title)
return titles

View File

@@ -24,7 +24,7 @@ class SpotifySearch():
except:
return False
def search(self, music) -> list:
def search(self, music=str) -> list:
"""Search and return the title of musics on Spotify"""
type = music.split('/')[3].split('?')[0]
code = music.split('/')[4].split('?')[0]
@@ -34,19 +34,20 @@ class SpotifySearch():
musics = self.__get_playlist(code)
elif type == 'track':
musics = self.__get_track(code)
elif type == 'artist':
musics = self.__get_artist(code)
else:
return None
return musics
def __get_album(self, code) -> list:
"""Get the externals urls of a album
def __get_album(self, code=str) -> list:
"""Convert a album ID to list of songs names
ARG: Spotify Code of the Album
ARG: Spotify Code of the Album
"""
if self.__connected == True:
try:
# Load all music objects
results = self.__api.album_tracks(code)
musics = results['items']
@@ -66,10 +67,10 @@ class SpotifySearch():
except Exception as e:
raise e
def __get_playlist(self, code) -> list:
"""Get the externals urls of a playlist
def __get_playlist(self, code=str) -> list:
"""Convert a playlist ID to list of songs names
Arg: Spotify Code of the Playlist
Arg: Spotify Code of the Playlist
"""
try:
results = self.__api.playlist_items(code)
@@ -96,10 +97,10 @@ class SpotifySearch():
except Exception as e:
raise e
def __get_track(self, code) -> list:
"""Convert a external_url track to the title of the music
def __get_track(self, code=str) -> list:
"""Convert a track ID to the title of the music
ARG: Spotify Code of the Music
ARG: Spotify Code of the Track
"""
results = self.__api.track(code)
name = results['name']
@@ -109,10 +110,24 @@ class SpotifySearch():
return [f'{name} {artists}']
def __get_artist(self, code=str) -> list:
"""Convert a external_url track to the title of the music
ARG: Spotify Code of the Music
"""
results = self.__api.artist_top_tracks(code, country='BR')
musics_titles = []
for music in results['tracks']:
title = self.__extract_title(music)
musics_titles.append(title)
return musics_titles
def __extract_title(self, music: dict) -> str:
"""Receive a spotify music object and return his title
ARG: music dict returned by Spotify
ARG: music dict returned by Spotify
"""
title = f'{music["name"]} '
for artist in music['artists']:

View File

@@ -6,12 +6,16 @@ from config import config
def is_connected(ctx):
try:
voice_channel = ctx.guild.voice_client.channel
return voice_channel
if not ctx.guild.voice_client.is_connected():
return None
else:
return voice_channel
except:
return None
def format_time(duration):
def format_time(duration) -> str:
if not duration:
return "00:00"