From 9e5997e4ecafa4c2e06d8ccb0208c55713fa5ca2 Mon Sep 17 00:00:00 2001 From: Choco <94597336+ChocoMeow@users.noreply.github.com> Date: Sat, 18 Feb 2023 18:57:12 +0800 Subject: [PATCH] Support message command --- cogs/admin.py | 336 ++++++----- cogs/basic.py | 1119 +++++++++++++++++------------------ cogs/effect.py | 299 ++++------ cogs/playlist.py | 471 +++++++-------- function.py | 1 + main.py | 69 ++- settings Example.json | 11 + view/help.py | 4 +- voicelink/player.py | 10 +- voicelink/queue.py | 4 +- voicelink/spotify/client.py | 1 - 11 files changed, 1120 insertions(+), 1205 deletions(-) diff --git a/cogs/admin.py b/cogs/admin.py index c849922..a8d966d 100644 --- a/cogs/admin.py +++ b/cogs/admin.py @@ -4,188 +4,187 @@ import io import contextlib import textwrap import traceback +import function from discord import app_commands from discord.ext import commands from function import ( - langs, - lang_guilds, - update_settings, - get_settings, + langs, + lang_guilds, + update_settings, + get_settings, get_lang, embed_color, - time as ctime + time as ctime, + get_aliases, + cooldown_check ) from view import DebugModal -class Admin(commands.GroupCog, name="settings"): + +class Admin(commands.Cog, name="settings"): def __init__(self, bot) -> None: self.bot = bot self.description = "This category is only available to admin permissions on the server." - def get_settings(self, interaction: discord.Interaction) -> dict: - player: voicelink.Player = interaction.guild.voice_client + def get_settings(self, ctx: commands.Context) -> dict: + player: voicelink.Player = ctx.guild.voice_client if not player: - settings = get_settings(interaction.guild_id) + settings = get_settings(ctx.guild.id) else: settings = player.settings - - return player, settings - @app_commands.command( - name = "language", - description = "You can choose your preferred language, the bot message will change to the language you set." - ) - @app_commands.checks.has_permissions(manage_guild=True) - @app_commands.checks.cooldown(2, 60.0, key=lambda i: (i.guild_id)) - @app_commands.guild_only() - async def language(self, interaction: discord.Interaction, language: str): + return player, settings + + @commands.hybrid_group(name="settings", invoke_without_command=True) + async def settings(self, ctx: commands.Context): + return + + @settings.command(name="language", aliases=get_aliases("language")) + @commands.has_permissions(manage_guild=True) + @commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild) + async def language(self, ctx: commands.Context, language: str): + "You can choose your preferred language, the bot message will change to the language you set." language = language.upper() if language not in langs: - return await interaction.response.send_message(get_lang(interaction.guild_id, "languageNotFound")) + return await ctx.send(get_lang(ctx.guild.id, "languageNotFound")) - player, settings = self.get_settings(interaction) + player, settings = self.get_settings(ctx) if player: player.lang = language - lang_guilds[interaction.guild_id] = language - update_settings(interaction.guild_id, {'lang': language}) + lang_guilds[ctx.guild.id] = language + update_settings(ctx.guild.id, {'lang': language}) + + await ctx.send(get_lang(ctx.guild.id, 'changedLanguage').format(language)) - await interaction.response.send_message(get_lang(interaction.guild_id, 'changedLanguage').format(language)) - @language.autocomplete('language') - async def autocomplete_callback(self, interaction: discord.Interaction, current: str) -> list: + async def autocomplete_callback(self, ctx: commands.Context, current: str) -> list: if current: - return [ app_commands.Choice(name=lang, value=lang) for lang in langs.keys() if current.upper() in lang ] - return [ app_commands.Choice(name=lang, value=lang) for lang in langs.keys() ] + return [app_commands.Choice(name=lang, value=lang) for lang in langs.keys() if current.upper() in lang] + return [app_commands.Choice(name=lang, value=lang) for lang in langs.keys()] - @app_commands.command( - name = "dj", - description = "Set a DJ role or remove DJ role." - ) - @app_commands.checks.has_permissions(manage_guild=True) - @app_commands.guild_only() - async def dj(self, interaction: discord.Interaction, role: discord.Role = None): - player: voicelink.Player = interaction.guild.voice_client + @settings.command(name="dj", aliases=get_aliases("dj")) + @commands.has_permissions(manage_guild=True) + @commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild) + async def dj(self, ctx: commands.Context, role: discord.Role = None): + "Set a DJ role or remove DJ role." + player: voicelink.Player = ctx.guild.voice_client if not role: if player: player.settings.pop('dj', None) - update_settings(interaction.guild_id, {'dj':''}, mode="Unset") + update_settings(ctx.guild.id, {'dj': ''}, mode="Unset") else: if player: player.settings['dj'] = role.id - update_settings(interaction.guild_id, {'dj': role.id }) + update_settings(ctx.guild.id, {'dj': role.id}) - await interaction.response.send_message(get_lang(interaction.guild_id, 'setDJ').format(f"<@&{role.id}>" if role else "None"), allowed_mentions=discord.AllowedMentions.none()) + await ctx.send(get_lang(ctx.guild.id, 'setDJ').format(f"<@&{role.id}>" if role else "None"), allowed_mentions=discord.AllowedMentions.none()) - @app_commands.command( - name = "queue", - description = "Change to another type of queue mode." - ) - @app_commands.choices(mode= [ + @settings.command(name="queue", aliases=get_aliases("queue")) + @app_commands.choices(mode=[ app_commands.Choice(name="FairQueue", value="FairQueue"), app_commands.Choice(name="Queue", value="Queue") ]) - @app_commands.checks.has_permissions(manage_guild=True) - @app_commands.guild_only() - async def queue(self, interaction: discord.Interaction, mode: str): - player, settings = self.get_settings(interaction) + @commands.has_permissions(manage_guild=True) + @commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild) + async def queue(self, ctx: commands.Context, mode: str): + "Change to another type of queue mode." + player, settings = self.get_settings(ctx) + if mode.capitalize() not in ["FairQueue", "Queue"]: + mode = "Queue" settings["queueType"] = mode - update_settings(interaction.guild_id, {"queueType": mode}) - await interaction.response.send_message(get_lang(interaction.guild_id, "setqueue").format(mode)) + update_settings(ctx.guild.id, {"queueType": mode}) + await ctx.send(get_lang(ctx.guild.id, "setqueue").format(mode)) - @app_commands.command( - name = "247", - description = "Toggles 24/7 mode, which disables automatic inactivity-based disconnects." - ) - @app_commands.guild_only() - @app_commands.checks.has_permissions(manage_guild=True) - async def playforever(self, interaction: discord.Interaction): - player, settings = self.get_settings(interaction) + @settings.command(name="247", aliases=get_aliases("247")) + @commands.has_permissions(manage_guild=True) + @commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild) + async def playforever(self, ctx: commands.Context): + "Toggles 24/7 mode, which disables automatic inactivity-based disconnects." + player, settings = self.get_settings(ctx) toggle = settings.get('24/7', False) settings['24/7'] = not toggle - update_settings(interaction.guild_id, {'24/7':not toggle}) - toggle = get_lang(interaction.guild_id, "enabled" if not toggle else "disabled") - await interaction.response.send_message(get_lang(interaction.guild_id, '247').format(toggle)) + update_settings(ctx.guild.id, {'24/7': not toggle}) + toggle = get_lang(ctx.guild.id, "enabled" if not toggle else "disabled") + await ctx.send(get_lang(ctx.guild.id, '247').format(toggle)) - @app_commands.command( - name = "bypassvote", - description = "Toggles voting system.") - @app_commands.checks.has_permissions(manage_guild=True) - async def bypassvote(self, interaction: discord.Interaction): - player, settings = self.get_settings(interaction) + @settings.command(name="bypassvote", aliases=get_aliases("bypassvote")) + @commands.has_permissions(manage_guild=True) + @commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild) + async def bypassvote(self, ctx: commands.Context): + "Toggles voting system." + player, settings = self.get_settings(ctx) toggle = settings.get('votedisable', True) settings['votedisable'] = not toggle - update_settings(interaction.guild_id, {'votedisable': not toggle}) - toggle = get_lang(interaction.guild_id, "enabled" if not toggle else "disabled") - await interaction.response.send_message(get_lang(interaction.guild_id, 'bypassVote').format(toggle)) - - @app_commands.command( - name = "view", - description = "Show all the bot settings in your server." - ) - @app_commands.checks.has_permissions(manage_guild=True) - @app_commands.guild_only() - async def view(self, interaction: discord.Interaction): - player, settings = self.get_settings(interaction) - embed=discord.Embed(color=embed_color) - embed.set_author(name=get_lang(interaction.guild_id, 'settingsMenu').format(interaction.guild.name), icon_url=self.bot.user.display_avatar.url) - if interaction.guild.icon: - embed.set_thumbnail(url=interaction.guild.icon.url) + update_settings(ctx.guild.id, {'votedisable': not toggle}) + toggle = get_lang(ctx.guild.id, + "enabled" if not toggle else "disabled") + await ctx.send(get_lang(ctx.guild.id, 'bypassVote').format(toggle)) - embed.add_field(name=get_lang(interaction.guild_id, 'settingsTitle'), value=get_lang(interaction.guild_id, 'settingsValue').format( - settings.get('lang', 'EN'), - settings.get('controller', True), - f"<@&{settings['dj']}>" if 'dj' in settings else '`None`', - settings.get('votedisable', False), - settings.get('24/7', False), - settings.get('volume', 100), - ctime(settings.get('playTime', 0) * 60 * 1000), - inline=True) - ) - embed.add_field(name=get_lang(interaction.guild_id, 'settingsTitle2'), value=get_lang(interaction.guild_id, 'settingsValue2').format( - settings.get("queueType", "Queue"), - "200", - settings.get("duplicateTrack", True) - ) - ) + @settings.command(name="view", aliases=get_aliases("view")) + @commands.has_permissions(manage_guild=True) + @commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild) + async def view(self, ctx: commands.Context): + "Show all the bot settings in your server." + player, settings = self.get_settings(ctx) + embed = discord.Embed(color=embed_color) + embed.set_author(name=get_lang(ctx.guild.id, 'settingsMenu').format( + ctx.guild.name), icon_url=self.bot.user.display_avatar.url) + if ctx.guild.icon: + embed.set_thumbnail(url=ctx.guild.icon.url) - perms = interaction.guild.me.guild_permissions - embed.add_field(name=get_lang(interaction.guild_id, 'settingsPermTitle'), value=get_lang(interaction.guild_id, 'settingsPermValue').format( - '' if perms.administrator else '', - '' if perms.manage_guild else '', - '' if perms.manage_channels else '', - '' if perms.manage_messages else ''), inline=False - ) - await interaction.response.send_message(embed=embed) + embed.add_field(name=get_lang(ctx.guild.id, 'settingsTitle'), value=get_lang(ctx.guild.id, 'settingsValue').format( + settings.get('lang', 'EN'), + settings.get('controller', True), + f"<@&{settings['dj']}>" if 'dj' in settings else '`None`', + settings.get( + 'votedisable', False), + settings.get( + '24/7', False), + settings.get( + 'volume', 100), + ctime(settings.get( + 'playTime', 0) * 60 * 1000), + inline=True) + ) + embed.add_field(name=get_lang(ctx.guild.id, 'settingsTitle2'), value=get_lang(ctx.guild.id, 'settingsValue2').format( + settings.get("queueType", "Queue"), + function.max_queue, + settings.get("duplicateTrack", True) + ) + ) - @app_commands.command( - name = "volume", - description = "Set the player's volume." - ) - @app_commands.describe( - value = "Input a integer." - ) - @app_commands.checks.has_permissions(manage_guild=True) - @app_commands.guild_only() - async def volume(self, interaction: discord.Interaction, value: app_commands.Range[int, 1, 150]): - player: voicelink.Player = interaction.guild.voice_client + perms = ctx.guild.me.guild_permissions + embed.add_field(name=get_lang(ctx.guild.id, 'settingsPermTitle'), value=get_lang(ctx.guild.id, 'settingsPermValue').format( + '' if perms.administrator else '', + '' if perms.manage_guild else '', + '' if perms.manage_channels else '', + '' if perms.manage_messages else ''), inline=False + ) + await ctx.send(embed=embed) + + @settings.command(name="volume", aliases=get_aliases("volume")) + @app_commands.describe(value="Input a integer.") + @commands.has_permissions(manage_guild=True) + @commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild) + async def volume(self, ctx: commands.Context, value: commands.Range[int, 1, 150]): + "Set the player's volume." + player: voicelink.Player = ctx.guild.voice_client if player: player.settings['volume'] = value await player.set_volume(value) - - update_settings(interaction.guild_id, {'volume':value}) - await interaction.response.send_message(get_lang(interaction.guild_id, 'setVolume').format(value)) - @app_commands.command( - name = "togglecontroller", - description = "Toggles the music controller." - ) - @app_commands.checks.has_permissions(manage_guild=True) - @app_commands.guild_only() - async def togglecontroller(self, interaction: discord.Interaction): - player, settings = self.get_settings(interaction) + update_settings(ctx.guild.id, {'volume': value}) + await ctx.send(get_lang(ctx.guild.id, 'setVolume').format(value)) + + @settings.command(name="togglecontroller", aliases=get_aliases("togglecontroller")) + @commands.has_permissions(manage_guild=True) + @commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild) + async def togglecontroller(self, ctx: commands.Context): + "Toggles the music controller." + player, settings = self.get_settings(ctx) toggle = settings.get('controller', True) settings['controller'] = not toggle if player and settings['controller'] is False: @@ -195,49 +194,43 @@ class Admin(commands.GroupCog, name="settings"): except: discord.ui.View.from_message(player.controller).stop() - update_settings(interaction.guild_id, {'controller': not toggle}) - toggle = get_lang(interaction.guild_id, "enabled" if not toggle else "disabled") - await interaction.response.send_message(get_lang(interaction.guild_id, 'togglecontroller').format(toggle)) + update_settings(ctx.guild.id, {'controller': not toggle}) + toggle = get_lang(ctx.guild.id, "enabled" if not toggle else "disabled") + await ctx.send(get_lang(ctx.guild.id, 'togglecontroller').format(toggle)) - @app_commands.command( - name = "duplicatetrack", - description = "Toggle Vocard to prevent duplicate songs from queuing." - ) - @app_commands.choices(toggle = [ - app_commands.Choice(name="Enable", value="enabled"), - app_commands.Choice(name="Disable", value="disabled") - ]) - @app_commands.checks.has_permissions(manage_guild=True) - @app_commands.guild_only() - async def duplicatetrack(self, interaction: discord.Interaction, toggle: str): - player, settings = self.get_settings(interaction) + @settings.command(name="duplicatetrack", aliases=get_aliases("duplicatetrack")) + @commands.has_permissions(manage_guild=True) + @commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild) + async def duplicatetrack(self, ctx: commands.Context): + "Toggle Vocard to prevent duplicate songs from queuing." + player, settings = self.get_settings(ctx) + toggle = settings.get('duplicateTrack', False) if player: - player.queue._duplicateTrack = False if toggle == 'enabled' else True + player.queue._duplicateTrack = not toggle + + update_settings(ctx.guild.id, {'duplicateTrack': not toggle}) + toggle = get_lang(ctx.guild.id, "enabled" if not toggle else "disabled") + return await ctx.send(get_lang(ctx.guild.id, "toggleDuplicateTrack").format(toggle)) + + @settings.command(name="debug", aliases=get_aliases("debug")) + @commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild) + async def debug(self, ctx: commands.Context): + if ctx.author.id not in function.bot_access_user: + return await ctx.send("You are not able to use this command!") - update_settings(interaction.guild_id, {'duplicateTrack': False if toggle == 'enabled' else True}) - return await interaction.response.send_message(get_lang(interaction.guild_id, "toggleDuplicateTrack").format(get_lang(interaction.guild_id, toggle))) - - @app_commands.command( - name = "debug", - ) - @app_commands.guild_only() - async def debug(self, interaction: discord.Interaction): - if interaction.user.id not in [358819659581227011, 705783356767338648]: - return await interaction.response.send_message("You are not able to use this command!") - def clear_code(content): if content.startswith("```") and content.endswith("```"): return "\n".join(content.split("\n")[1:])[:-3] else: return content - + modal = DebugModal(title="Debug Panel") - await interaction.response.send_modal(modal) + await ctx.send(modal=modal) await modal.wait() if modal.values is None: return - + e = None local_variables = { @@ -245,30 +238,33 @@ class Admin(commands.GroupCog, name="settings"): "commands": commands, "voicelink": voicelink, "bot": self.bot, - "interaction": interaction, - "channel": interaction.channel, - "author": interaction.user, - "guild": interaction.guild, - "message": interaction.message, + "ctx": ctx, + "channel": ctx.channel, + "author": ctx.user, + "guild": ctx.guild, + "message": ctx.message, "input": None } code = clear_code(modal.values) - str_obj = io.StringIO() #Retrieves a stream of data + str_obj = io.StringIO() # Retrieves a stream of data try: with contextlib.redirect_stdout(str_obj): - exec(f"async def func():\n{textwrap.indent(code, ' ')}", local_variables) + exec( + f"async def func():\n{textwrap.indent(code, ' ')}", local_variables) obj = await local_variables["func"]() result = f"{str_obj.getvalue()}\n-- {obj}\n" except Exception as e: - errormsg = ''.join(traceback.format_exception(e, e, e.__traceback__)) - return await interaction.followup.send(f"```py\n{errormsg}```") + errormsg = ''.join( + traceback.format_exception(e, e, e.__traceback__)) + return await ctx.send(f"```py\n{errormsg}```") string = result.split("\n") text = "" for index, i in enumerate(string, start=1): text += f"{'%03d' % index} | {i}\n" - return await interaction.followup.send(f"```{text}```") + return await ctx.send(f"```{text}```") + async def setup(bot: commands.Bot) -> None: - await bot.add_cog(Admin(bot)) \ No newline at end of file + await bot.add_cog(Admin(bot)) diff --git a/cogs/basic.py b/cogs/basic.py index 1ab6839..ce6ad00 100644 --- a/cogs/basic.py +++ b/cogs/basic.py @@ -11,7 +11,9 @@ from function import ( youtube_api_key, requests_api, get_lang, - embed_color + embed_color, + cooldown_check, + get_aliases ) from addons import getLyrics @@ -19,39 +21,57 @@ from view import SearchView, ListView, LinkView, LyricsView, ChapterView, HelpVi from validators import url from random import shuffle -async def connect_channel(interaction: discord.Interaction, channel: discord.VoiceChannel = None) -> voicelink.Player: +searchPlatform = { + "youtube": "ytsearch", + "youtubemusic": "ytmsearch", + "soundcloud": "scsearch", + "apple": "amsearch", +} + + +async def connect_channel(ctx: commands.Context, channel: discord.VoiceChannel = None) -> voicelink.Player: try: - channel = channel or interaction.user.voice.channel + channel = channel or ctx.author.voice.channel except: - raise voicelink.VoicelinkException(get_lang(interaction.guild_id, 'noChannel')) + raise voicelink.VoicelinkException(get_lang(ctx.guild.id, 'noChannel')) - check = channel.permissions_for(interaction.guild.me) + check = channel.permissions_for(ctx.guild.me) if check.connect == False or check.speak == False: - raise voicelink.VoicelinkException(get_lang(interaction.guild_id, 'noPermission')) + raise voicelink.VoicelinkException( + get_lang(ctx.guild.id, 'noPermission')) - player: voicelink.Player = await channel.connect(cls=voicelink.Player(interaction.client, channel, interaction)) + player: voicelink.Player = await channel.connect(cls=voicelink.Player(ctx.bot, channel, ctx)) return player -async def nowplay(interaction: discord.Interaction, player: voicelink.Player): + +async def nowplay(ctx: commands.Context, player: voicelink.Player): track = player.current if not track: - return await interaction.response.send_message(player.get_msg('noTrackPlaying'), ephemeral=True) + return await ctx.send(player.get_msg('noTrackPlaying'), ephemeral=True) - upnext = "\n".join(f"`{index}.` `[{track.formatLength}]` [{track.title[:30]}]({track.uri})" for index, track in enumerate(player.queue.tracks()[:2], start=2)) - embed=discord.Embed(description=player.get_msg('nowplayingDesc').format(track.title), color=embed_color) - embed.set_author(name=track.requester if track.requester else interaction.client, icon_url=track.requester.display_avatar.url if track.requester else interaction.client.user.display_avatar.url) + upnext = "\n".join(f"`{index}.` `[{track.formatLength}]` [{track.title[:30]}]({track.uri})" for index, track in enumerate( + player.queue.tracks()[:2], start=2)) + embed = discord.Embed(description=player.get_msg( + 'nowplayingDesc').format(track.title), color=embed_color) + embed.set_author(name=track.requester if track.requester else ctx.bot, + icon_url=track.requester.display_avatar.url if track.requester else ctx.me.display_avatar.url) if upnext: - embed.add_field(name=player.get_msg('nowplayingField'), value=upnext) - pbar = "".join(":radio_button:" if i == round(player.position // round(track.length // 15)) else "▬" for i in range(15)) - icon = ":red_circle:" if track.is_stream else (":pause_button:" if player.is_paused else ":arrow_forward:") + embed.add_field(name=player.get_msg('nowplayingField'), value=upnext) + pbar = "".join(":radio_button:" if i == round( + player.position // round(track.length // 15)) else "▬" for i in range(15)) + icon = ":red_circle:" if track.is_stream else ( + ":pause_button:" if player.is_paused else ":arrow_forward:") - embed.add_field(name="\u2800", value=f"{icon} {pbar} **[{ctime(player.position)}/{track.formatLength}]**", inline=False) - - return await interaction.response.send_message(embed=embed, view=LinkView(player.get_msg('nowplayingLink').format(track.source), track.emoji, track.uri)) + embed.add_field( + name="\u2800", value=f"{icon} {pbar} **[{ctime(player.position)}/{track.formatLength}]**", inline=False) + + return await ctx.send(embed=embed, view=LinkView(player.get_msg('nowplayingLink').format(track.source), track.emoji, track.uri)) + + +async def help_autocomplete(ctx: commands.Context, current: str) -> list: + return [app_commands.Choice(name=c.capitalize(), value=c) for c in ctx.bot.cogs if c not in ["Nodes", "Task"] and current in c] -async def help_autocomplete(interaction: discord.Interaction, current: str) -> list: - return [ app_commands.Choice(name=c.capitalize(), value=c) for c in interaction.client.cogs if c not in ["Nodes", "Task"] and current in c ] class Basic(commands.Cog): def __init__(self, bot: commands.Bot) -> None: @@ -62,795 +82,720 @@ class Basic(commands.Cog): callback=self._play, ) self.bot.tree.add_command(self.ctx_menu) - + async def cog_unload(self) -> None: - self.bot.tree.remove_command(self.ctx_menu.name, type=self.ctx_menu.type) + self.bot.tree.remove_command( + self.ctx_menu.name, type=self.ctx_menu.type) - @app_commands.command( - name = "connect", - description = "Connect to a voice channel." - ) - @app_commands.describe( - channel="Provide a channel to connect." - ) - @app_commands.checks.cooldown(2, 30.0, key=lambda i: (i.guild_id)) - @app_commands.guild_only() - async def connect(self, interaction: discord.Interaction, channel: discord.VoiceChannel = None) -> None: + @commands.hybrid_command(name="connect", aliases=get_aliases("connect")) + @app_commands.describe(channel="Provide a channel to connect.") + @commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild) + async def connect(self, ctx: commands.Context, channel: discord.VoiceChannel = None) -> None: + "Connect to a voice channel." try: - player = await connect_channel(interaction, channel) + player = await connect_channel(ctx, channel) except discord.errors.ClientException: - return await interaction.response.send_message(get_lang(interaction.guild_id, "alreadyConnected")) + return await ctx.send(get_lang(ctx.guild.id, "alreadyConnected")) - await interaction.response.send_message(player.get_msg('connect').format(player.channel)) + await ctx.send(player.get_msg('connect').format(player.channel)) - @app_commands.command( - name = "play", - description = "Loads your input and added it to the queue." - ) - @app_commands.describe( - query="Input a query or a searchable link.", - ) - @app_commands.checks.cooldown(2, 15.0, key=lambda i: (i.guild_id)) - @app_commands.guild_only() - async def play(self, interaction: discord.Interaction, query: str) -> None: - player: voicelink.Player = interaction.guild.voice_client - if not player: - player = await connect_channel(interaction) + @commands.hybrid_command(name="play", aliases=get_aliases("play")) + @app_commands.describe(query="Input a query or a searchable link.") + @commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild) + async def play(self, ctx: commands.Context, *, query: str) -> None: + "Loads your input and added it to the queue." + player: voicelink.Player = ctx.guild.voice_client + if not player: + player = await connect_channel(ctx) - if interaction.user not in player.channel.members: - if not interaction.user.guild_permissions.manage_guild: - return await interaction.response.send_message(player.get_msg('notInChannel').format(interaction.user.mention, player.channel.mention), ephemeral=True) + if ctx.author not in player.channel.members: + if not ctx.author.guild_permissions.manage_guild: + return await ctx.send(player.get_msg('notInChannel').format(ctx.author.mention, player.channel.mention), ephemeral=True) - tracks = await player.get_tracks(query, requester=interaction.user) + tracks = await player.get_tracks(query, requester=ctx.author) if not tracks: - return await interaction.response.send_message(player.get_msg('noTrackFound')) + return await ctx.send(player.get_msg('noTrackFound')) try: if isinstance(tracks, voicelink.Playlist): for track in tracks.tracks: await player.queue.put(track) - await interaction.response.send_message(player.get_msg('playlistLoad').format(tracks.name, len(tracks.tracks))) + await ctx.send(player.get_msg('playlistLoad').format(tracks.name, len(tracks.tracks))) else: position = await player.queue.put(tracks[0]) - await interaction.response.send_message((f"`{player.get_msg('live')}`" if tracks[0].is_stream else "") + ( player.get_msg('trackLoad_pos').format(tracks[0].title, tracks[0].author, tracks[0].formatLength, position) if position >= 1 and player.is_playing else player.get_msg('trackLoad').format(tracks[0].title, tracks[0].author, tracks[0].formatLength))) + await ctx.send((f"`{player.get_msg('live')}`" if tracks[0].is_stream else "") + (player.get_msg('trackLoad_pos').format(tracks[0].title, tracks[0].author, tracks[0].formatLength, position) if position >= 1 and player.is_playing else player.get_msg('trackLoad').format(tracks[0].title, tracks[0].author, tracks[0].formatLength))) except voicelink.QueueFull as e: - await interaction.response.send_message(e) + await ctx.send(e) finally: if not player.is_playing: await player.do_next() - - @app_commands.checks.cooldown(2, 15.0, key=lambda i: (i.guild_id)) - async def _play(self, interaction: discord.Interaction, message: discord.Message): + + @commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild) + async def _play(self, ctx: commands.Context, message: discord.Message): query = "" if message.content: - url = re.findall("http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+", message.content) + url = re.findall( + "http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+", message.content) if url: query = url[0] elif message.attachments: query = message.attachments[0].url if not query: - return await interaction.response.send_message(get_lang(interaction.guild_id, key="noPlaySource"), ephemeral=True) + return await ctx.send(get_lang(ctx.guild.id, key="noPlaySource"), ephemeral=True) - player: voicelink.Player = interaction.guild.voice_client - if not player: - player = await connect_channel(interaction) - - if interaction.user not in player.channel.members: - if not interaction.user.guild_permissions.manage_guild: - return await interaction.response.send_message(player.get_msg('notInChannel').format(interaction.user.mention, player.channel.mention), ephemeral=True) + player: voicelink.Player = ctx.guild.voice_client + if not player: + player = await connect_channel(ctx) - tracks = await player.get_tracks(query, requester=interaction.user) + if ctx.author not in player.channel.members: + if not ctx.author.guild_permissions.manage_guild: + return await ctx.send(player.get_msg('notInChannel').format(ctx.author.mention, player.channel.mention), ephemeral=True) + + tracks = await player.get_tracks(query, requester=ctx.author) if not tracks: - return await interaction.response.send_message(player.get_msg('noTrackFound')) + return await ctx.send(player.get_msg('noTrackFound')) try: if isinstance(tracks, voicelink.Playlist): for track in tracks.tracks: await player.queue.put(track) - await interaction.response.send_message(player.get_msg('playlistLoad').format(tracks.name, len(tracks.tracks))) + await ctx.send(player.get_msg('playlistLoad').format(tracks.name, len(tracks.tracks))) else: await player.queue.put(tracks[0]) - await interaction.response.send_message((f"`{player.get_msg('live')}`" if tracks[0].is_stream else "") + ( player.get_msg('trackLoad_pos').format(tracks[0].title, tracks[0].author, tracks[0].formatLength, player.queue.count) if player.queue.count >= 1 and player.is_playing else player.get_msg('trackLoad').format(tracks[0].title, tracks[0].author, tracks[0].formatLength))) + await ctx.send((f"`{player.get_msg('live')}`" if tracks[0].is_stream else "") + (player.get_msg('trackLoad_pos').format(tracks[0].title, tracks[0].author, tracks[0].formatLength, player.queue.count) if player.queue.count >= 1 and player.is_playing else player.get_msg('trackLoad').format(tracks[0].title, tracks[0].author, tracks[0].formatLength))) except voicelink.QueueFull as e: - await interaction.response.send_message(e) + await ctx.send(e) finally: if not player.is_playing: await player.do_next() - @app_commands.command( - name = "search", - description = "Loads your input and added it to the queue." - ) + @commands.hybrid_command(name="search", aliases=get_aliases("search")) @app_commands.describe( query="Input the name of the song.", platform="Select the platform you want to search." ) - @app_commands.checks.cooldown(2, 15.0, key=lambda i: (i.guild_id)) - @app_commands.choices(platform = [ + @app_commands.choices(platform=[ app_commands.Choice(name="Youtube", value="Youtube"), - app_commands.Choice(name="Youtube Music", value="Youtube Music"), + app_commands.Choice(name="Youtube Music", value="YoutubeMusic"), app_commands.Choice(name="Spotify", value="Spotify"), app_commands.Choice(name="SoundCloud", value="SoundCloud"), app_commands.Choice(name="Apple Music", value="Apple") ]) - @app_commands.guild_only() - async def search(self, interaction: discord.Interaction, query: str, platform: str = "Youtube"): - player: voicelink.Player = interaction.guild.voice_client - if not player: - player = await connect_channel(interaction) + @commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild) + async def search(self, ctx: commands.Context, *, query: str, platform: str = "Youtube"): + "Loads your input and added it to the queue." + player: voicelink.Player = ctx.guild.voice_client + if not player: + player = await connect_channel(ctx) + + if ctx.author not in player.channel.members: + if not ctx.author.guild_permissions.manage_guild: + return await ctx.send(player.get_msg('notInChannel').format(ctx.author.mention, player.channel.mention), ephemeral=True) - if interaction.user not in player.channel.members: - if not interaction.user.guild_permissions.manage_guild: - return await interaction.response.send_message(player.get_msg('notInChannel').format(interaction.user.mention, player.channel.mention), ephemeral=True) - if url(query): - return await interaction.response.send_message(player.get_msg('noLinkSupport'), ephemeral=True) - if platform != 'Spotify': - query_platform = ("ytsearch" if platform == "Youtube" else "ytmsearch" if platform == "Youtube Music" else "scsearch" if platform == "SoundCloud" else "amsearch") + f":{query}" - tracks = await player.get_tracks(query=query_platform, requester=interaction.user) + return await ctx.send(player.get_msg('noLinkSupport'), ephemeral=True) + + if platform != 'spotify': + query_platform = searchPlatform.get(platform.lower(), 'ytsearch') + f":{query}" + tracks = await player.get_tracks(query=query_platform, requester=ctx.author) else: - tracks = await player.spotifySearch(query=query, requester=interaction.user) + tracks = await player.spotifySearch(query=query, requester=ctx.author) + if not tracks: - return await interaction.response.send_message(player.get_msg('noTrackFound')) - query_track = "\n".join(f"`{index}.` `[{track.formatLength}]` **{track.title[:35]}**" for index, track in enumerate(tracks[0:10], start=1)) - embed=discord.Embed(title=player.get_msg('searchTitle').format(query), description=player.get_msg('searchDesc').format(emoji_source(platform.lower()), platform, len(tracks[0:10]), query_track), color=embed_color) + return await ctx.send(player.get_msg('noTrackFound')) + + query_track = "\n".join( + f"`{index}.` `[{track.formatLength}]` **{track.title[:35]}**" for index, track in enumerate(tracks[0:10], start=1)) + embed = discord.Embed(title=player.get_msg('searchTitle').format(query), description=player.get_msg( + 'searchDesc').format(emoji_source(platform.lower()), platform, len(tracks[0:10]), query_track), color=embed_color) view = SearchView(tracks=tracks[0:10], lang=player.lang) - await interaction.response.send_message(embed=embed, view=view, ephemeral=True) - view.response = await interaction.original_response() + message = await ctx.send(embed=embed, view=view, ephemeral=True) + view.response = message await view.wait() if view.values is not None: msg = "" for value in view.values: track = tracks[int(value.split(". ")[0]) - 1] await player.queue.put(track) - msg += ((f"`{player.get_msg('live')}`" if track.is_stream else "") + ( player.get_msg('trackLoad_pos').format(track.title, track.author, track.formatLength, player.queue.count) if player.queue.count >= 1 else player.get_msg('trackLoad').format(track.title, track.author, track.formatLength)) ) - await interaction.followup.send(msg) + msg += ((f"`{player.get_msg('live')}`" if track.is_stream else "") + (player.get_msg('trackLoad_pos').format(track.title, track.author, track.formatLength, + player.queue.count) if player.queue.count >= 1 else player.get_msg('trackLoad').format(track.title, track.author, track.formatLength))) + await ctx.send(msg) if not player.is_playing: await player.do_next() - @app_commands.command( - name = "pause", - description = "Pause the music." - ) - @app_commands.checks.cooldown(2, 15.0, key=lambda i: (i.guild_id)) - @app_commands.guild_only() - async def pause(self, interaction: discord.Interaction): - player: voicelink.Player = interaction.guild.voice_client + @commands.hybrid_command(name="pause", aliases=get_aliases("pause")) + @commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild) + async def pause(self, ctx: commands.Context): + "Pause the music." + player: voicelink.Player = ctx.guild.voice_client if not player: - return await interaction.response.send_message(get_lang(interaction.guild_id, 'noPlayer'), ephemeral=True) - - if interaction.user not in player.channel.members: - if not interaction.user.guild_permissions.manage_guild: - return await interaction.response.send_message(player.get_msg('notInChannel').format(interaction.user.mention, player.channel.mention), ephemeral=True) - - if player.is_paused: - return await interaction.response.send_message(player.get_msg('pauseError')) + return await ctx.send(get_lang(ctx.guild.id, 'noPlayer'), ephemeral=True) - if not await player.is_privileged(interaction.user): - if interaction.user in player.pause_votes: - return await interaction.response.send_message(player.get_msg('voted'), ephemeral=True) + if ctx.author not in player.channel.members: + if not ctx.author.guild_permissions.manage_guild: + return await ctx.send(player.get_msg('notInChannel').format(ctx.author.mention, player.channel.mention), ephemeral=True) + + if player.is_paused: + return await ctx.send(player.get_msg('pauseError')) + + if not await player.is_privileged(ctx.author): + if ctx.author in player.pause_votes: + return await ctx.send(player.get_msg('voted'), ephemeral=True) else: - player.pause_votes.add(interaction.user) + player.pause_votes.add(ctx.author) if len(player.pause_votes) >= (required := player.required()): pass else: - return await interaction.response.send_message(player.get_msg('pauseVote').format(interaction.user, len(player.pause_votes), required)) + return await ctx.send(player.get_msg('pauseVote').format(ctx.author, len(player.pause_votes), required)) await player.set_pause(True) player.pause_votes.clear() - await interaction.response.send_message(player.get_msg('paused').format(interaction.user)) - - @app_commands.command( - name = "resume", - description = "Resume the music." - ) - @app_commands.checks.cooldown(2, 15.0, key=lambda i: (i.guild_id)) - @app_commands.guild_only() - async def resume(self, interaction: discord.Interaction): - player: voicelink.Player = interaction.guild.voice_client + await ctx.send(player.get_msg('paused').format(ctx.author)) + + @commands.hybrid_command(name="resume", aliases=get_aliases("resume")) + @commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild) + async def resume(self, ctx: commands.Context): + "Resume the music." + player: voicelink.Player = ctx.guild.voice_client if not player: - return await interaction.response.send_message(get_lang(interaction.guild_id, 'noPlayer'), ephemeral=True) - - if interaction.user not in player.channel.members: - if not interaction.user.guild_permissions.manage_guild: - return await interaction.response.send_message(player.get_msg('notInChannel').format(interaction.user.mention, player.channel.mention), ephemeral=True) - + return await ctx.send(get_lang(ctx.guild.id, 'noPlayer'), ephemeral=True) + + if ctx.author not in player.channel.members: + if not ctx.author.guild_permissions.manage_guild: + return await ctx.send(player.get_msg('notInChannel').format(ctx.author.mention, player.channel.mention), ephemeral=True) + if not player.is_paused: - return await interaction.response.send_message(player.get_msg('resumeError')) - - if not await player.is_privileged(interaction.user): - if interaction.user in player.resume_votes: - return await interaction.response.send_message(player.get_msg('voted'), ephemeral=True) + return await ctx.send(player.get_msg('resumeError')) + + if not await player.is_privileged(ctx.author): + if ctx.author in player.resume_votes: + return await ctx.send(player.get_msg('voted'), ephemeral=True) else: - player.resume_votes.add(interaction.user) + player.resume_votes.add(ctx.author) if len(player.resume_votes) >= (required := player.required()): pass else: - return await interaction.response.send_message(player.get_msg('resumeVote').format(interaction.user, len(player.resume_votes), required)) + return await ctx.send(player.get_msg('resumeVote').format(ctx.author, len(player.resume_votes), required)) await player.set_pause(False) player.resume_votes.clear() - await interaction.response.send_message(player.get_msg('resumed').format(interaction.user)) + await ctx.send(player.get_msg('resumed').format(ctx.author)) - @app_commands.command( - name = "skip", - description = "Skips to the next song or skips to the specified song." - ) - @app_commands.describe( - index="Enter a index that you want to skip to." - ) - @app_commands.checks.cooldown(3, 20.0, key=lambda i: (i.guild_id)) - @app_commands.guild_only() - async def skip(self, interaction: discord.Interaction, index: int = 0): - player: voicelink.Player = interaction.guild.voice_client + @commands.hybrid_command(name="skip", aliases=get_aliases("skip")) + @app_commands.describe(index="Enter a index that you want to skip to.") + @commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild) + async def skip(self, ctx: commands.Context, index: int = 0): + "Skips to the next song or skips to the specified song." + player: voicelink.Player = ctx.guild.voice_client if not player: - return await interaction.response.send_message(get_lang(interaction.guild_id, 'noPlayer'), ephemeral=True) - - if interaction.user not in player.channel.members: - if not interaction.user.guild_permissions.manage_guild: - return await interaction.response.send_message(player.get_msg('notInChannel').format(interaction.user.mention, player.channel.mention), ephemeral=True) - + return await ctx.send(get_lang(ctx.guild.id, 'noPlayer'), ephemeral=True) + + if ctx.author not in player.channel.members: + if not ctx.author.guild_permissions.manage_guild: + return await ctx.send(player.get_msg('notInChannel').format(ctx.author.mention, player.channel.mention), ephemeral=True) + if not player.is_playing: - return await interaction.response.send_message(player.get_msg('skipError'), ephemeral=True) - - if not await player.is_privileged(interaction.user): - if interaction.user == player.current.requester: + return await ctx.send(player.get_msg('skipError'), ephemeral=True) + + if not await player.is_privileged(ctx.author): + if ctx.author == player.current.requester: pass - elif interaction.user in player.skip_votes: - return await interaction.response.send_message(player.get_msg('voted'), ephemeral=True) + elif ctx.author in player.skip_votes: + return await ctx.send(player.get_msg('voted'), ephemeral=True) else: - player.skip_votes.add(interaction.user) + player.skip_votes.add(ctx.author) if len(player.skip_votes) >= (required := player.required()): pass else: - return await interaction.response.send_message(player.get_msg('skipVote').format(interaction.user, len(player.skip_votes), required)) + return await ctx.send(player.get_msg('skipVote').format(ctx.author, len(player.skip_votes), required)) if not player.node._available: - return await interaction.response.send_message(player.get_msg('nodeReconnect')) + return await ctx.send(player.get_msg('nodeReconnect')) if index: await player.queue.skipto(index) - - await interaction.response.send_message(player.get_msg('skipped').format(interaction.user)) + + await ctx.send(player.get_msg('skipped').format(ctx.author)) if player.queue.repeat == "Track": player.queue.set_repeat("Off") await player.stop() - @app_commands.command( - name = "back", - description = "Skips back to the previous song or skips to the specified previous song." - ) - @app_commands.describe( - index="Enter a index that you want to skip back to." - ) - @app_commands.checks.cooldown(3, 20.0, key=lambda i: (i.guild_id)) - @app_commands.guild_only() - async def back(self, interaction: discord.Interaction, index: int = 1): - player: voicelink.Player = interaction.guild.voice_client + @commands.hybrid_command(name="back", aliases=get_aliases("back")) + @app_commands.describe(index="Enter a index that you want to skip back to.") + @commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild) + async def back(self, ctx: commands.Context, index: int = 1): + "Skips back to the previous song or skips to the specified previous song." + player: voicelink.Player = ctx.guild.voice_client if not player: - return await interaction.response.send_message(get_lang(interaction.guild_id, 'noPlayer'), ephemeral=True) - - if interaction.user not in player.channel.members: - if not interaction.user.guild_permissions.manage_guild: - return await interaction.response.send_message(player.get_msg('notInChannel').format(interaction.user.mention, player.channel.mention), ephemeral=True) - - if not await player.is_privileged(interaction.user): - if interaction.user in player.previous_votes: - return await interaction.response.send_message(player.get_msg('voted'), ephemeral=True) + return await ctx.send(get_lang(ctx.guild.id, 'noPlayer'), ephemeral=True) + + if ctx.author not in player.channel.members: + if not ctx.author.guild_permissions.manage_guild: + return await ctx.send(player.get_msg('notInChannel').format(ctx.author.mention, player.channel.mention), ephemeral=True) + + if not await player.is_privileged(ctx.author): + if ctx.author in player.previous_votes: + return await ctx.send(player.get_msg('voted'), ephemeral=True) else: - player.previous_votes.add(interaction.user) + player.previous_votes.add(ctx.author) if len(player.previous_votes) >= (required := player.required()): pass else: - return await interaction.response.send_message(player.get_msg('backVote').format(interaction.user, len(player.previous_votes), required)) + return await ctx.send(player.get_msg('backVote').format(ctx.author, len(player.previous_votes), required)) if not player.node._available: - return await interaction.response.send_message(player.get_msg('nodeReconnect')) - + return await ctx.send(player.get_msg('nodeReconnect')) + if not player.is_playing: player.queue.backto(index) - await player.do_next() + await player.do_next() else: player.queue.backto(index + 1) await player.stop() - - await interaction.response.send_message(player.get_msg('backed').format(interaction.user)) + + await ctx.send(player.get_msg('backed').format(ctx.author)) if player.queue.repeat == "Track": player.queue.set_repeat("Off") - @app_commands.command( - name = "seek", - description = "Change the player position." - ) - @app_commands.describe( - position="Input position. Exmaple: 1:20." - ) - @app_commands.checks.cooldown(3, 20.0, key=lambda i: (i.guild_id)) - @app_commands.guild_only() - async def seek(self, interaction: discord.Interaction, position: str): - player: voicelink.Player = interaction.guild.voice_client + @commands.hybrid_command(name="seek", aliases=get_aliases("seek")) + @app_commands.describe(position="Input position. Exmaple: 1:20.") + @commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild) + async def seek(self, ctx: commands.Context, position: str): + "Change the player position." + player: voicelink.Player = ctx.guild.voice_client if not player: - return await interaction.response.send_message(get_lang(interaction.guild_id, 'noPlayer'), ephemeral=True) - - if interaction.user not in player.channel.members: - if not interaction.user.guild_permissions.manage_guild: - return await interaction.response.send_message(player.get_msg('notInChannel').format(interaction.user.mention, player.channel.mention), ephemeral=True) - + return await ctx.send(get_lang(ctx.guild.id, 'noPlayer'), ephemeral=True) + + if ctx.author not in player.channel.members: + if not ctx.author.guild_permissions.manage_guild: + return await ctx.send(player.get_msg('notInChannel').format(ctx.author.mention, player.channel.mention), ephemeral=True) + if not player.current: - return await interaction.response.send_message(player.get_msg('noTrackPlaying'), ephemeral=True) - if not await player.is_privileged(interaction.user): - return await interaction.response.send_message(player.get_msg('missingPerms_pos'), ephemeral=True) + return await ctx.send(player.get_msg('noTrackPlaying'), ephemeral=True) + if not await player.is_privileged(ctx.author): + return await ctx.send(player.get_msg('missingPerms_pos'), ephemeral=True) if player.position == 0: - return await interaction.response.send_message(player.get_msg('noTrackPlaying'), ephemeral=True) - + return await ctx.send(player.get_msg('noTrackPlaying'), ephemeral=True) + num = formatTime(position) if num is None: - return await interaction.response.send_message(player.get_msg('timeFormatError'), ephemeral=True) - + return await ctx.send(player.get_msg('timeFormatError'), ephemeral=True) + await player.seek(num) - await interaction.response.send_message(player.get_msg('seek').format(position)) - - @app_commands.command( - name = "queue", - description = "Display the players queue songs in your queue." - ) - @app_commands.checks.cooldown(2, 40.0, key=lambda i: (i.guild_id)) - @app_commands.guild_only() - async def queue(self, interaction: discord.Interaction): - player: voicelink.Player = interaction.guild.voice_client + await ctx.send(player.get_msg('seek').format(position)) + + @commands.hybrid_command(name="queue", aliases=get_aliases("queue")) + @commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild) + async def queue(self, ctx: commands.Context): + "Display the players queue songs in your queue." + player: voicelink.Player = ctx.guild.voice_client if not player: - return await interaction.response.send_message(get_lang(interaction.guild_id, 'noPlayer'), ephemeral=True) - - if interaction.user not in player.channel.members: - if not interaction.user.guild_permissions.manage_guild: - return await interaction.response.send_message(player.get_msg('notInChannel').format(interaction.user.mention, player.channel.mention), ephemeral=True) - + return await ctx.send(get_lang(ctx.guild.id, 'noPlayer'), ephemeral=True) + + if ctx.author not in player.channel.members: + if not ctx.author.guild_permissions.manage_guild: + return await ctx.send(player.get_msg('notInChannel').format(ctx.author.mention, player.channel.mention), ephemeral=True) + if player.queue.is_empty: - return await nowplay(interaction, player) - view = ListView(player=player, author=interaction.user) - await interaction.response.send_message(embed=view.build_embed(), view=view) - view.response = await interaction.original_response() - - @app_commands.command( - name = "history", - description = "Display the players queue songs in your history queue." - ) - @app_commands.checks.cooldown(2, 40.0, key=lambda i: (i.guild_id)) - @app_commands.guild_only() - async def history(self, interaction: discord.Interaction): - player: voicelink.Player = interaction.guild.voice_client + return await nowplay(ctx, player) + view = ListView(player=player, author=ctx.author) + message = await ctx.send(embed=view.build_embed(), view=view) + view.response = message + + @commands.hybrid_command(name="history", aliases=get_aliases("history")) + @commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild) + async def history(self, ctx: commands.Context): + "Display the players queue songs in your history queue." + player: voicelink.Player = ctx.guild.voice_client if not player: - return await interaction.response.send_message(get_lang(interaction.guild_id, 'noPlayer'), ephemeral=True) - - if interaction.user not in player.channel.members: - if not interaction.user.guild_permissions.manage_guild: - return await interaction.response.send_message(player.get_msg('notInChannel').format(interaction.user.mention, player.channel.mention), ephemeral=True) - + return await ctx.send(get_lang(ctx.guild.id, 'noPlayer'), ephemeral=True) + + if ctx.author not in player.channel.members: + if not ctx.author.guild_permissions.manage_guild: + return await ctx.send(player.get_msg('notInChannel').format(ctx.author.mention, player.channel.mention), ephemeral=True) + if not player.queue.history(): - return await nowplay(interaction, player) + return await nowplay(ctx, player) - view = ListView(player=player, author=interaction.user, isQueue=False) - await interaction.response.send_message(embed=view.build_embed(), view=view) - view.response = await interaction.original_response() + view = ListView(player=player, author=ctx.author, isQueue=False) + message = await ctx.send(embed=view.build_embed(), view=view) + view.response = message - @app_commands.command( - name = "leave", - description = "Disconnects the bot from your voice channel and chears the queue." - ) - @app_commands.guild_only() - async def leave(self, interaction: discord.Interaction): - player: voicelink.Player = interaction.guild.voice_client + @commands.hybrid_command(name="leave", aliases=get_aliases("leave")) + @commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild) + async def leave(self, ctx: commands.Context): + "Disconnects the bot from your voice channel and chears the queue." + player: voicelink.Player = ctx.guild.voice_client if not player: - return await interaction.response.send_message(get_lang(interaction.guild_id, 'noPlayer'), ephemeral=True) - - if interaction.user not in player.channel.members: - if not interaction.user.guild_permissions.manage_guild: - return await interaction.response.send_message(player.get_msg('notInChannel').format(interaction.user.mention, player.channel.mention), ephemeral=True) - - if not await player.is_privileged(interaction.user): - if interaction.user in player.stop_votes: - return await interaction.response.send_message(player.get_msg('voted'), ephemeral=True) + return await ctx.send(get_lang(ctx.guild.id, 'noPlayer'), ephemeral=True) + + if ctx.author not in player.channel.members: + if not ctx.author.guild_permissions.manage_guild: + return await ctx.send(player.get_msg('notInChannel').format(ctx.author.mention, player.channel.mention), ephemeral=True) + + if not await player.is_privileged(ctx.author): + if ctx.author in player.stop_votes: + return await ctx.send(player.get_msg('voted'), ephemeral=True) else: - player.stop_votes.add(interaction.user) + player.stop_votes.add(ctx.author) if len(player.stop_votes) >= (required := player.required(leave=True)): pass else: - return await interaction.response.send_message(player.get_msg('leaveVote').format(interaction.user, len(player.stop_votes), required)) - - await interaction.response.send_message(player.get_msg('left').format(interaction.user)) + return await ctx.send(player.get_msg('leaveVote').format(ctx.author, len(player.stop_votes), required)) + + await ctx.send(player.get_msg('left').format(ctx.author)) await player.teardown() - - @app_commands.command( - name = "nowplaying", - description = "Shows details of the current track." - ) - @app_commands.checks.cooldown(2, 10.0, key=lambda i: (i.guild_id)) - @app_commands.guild_only() - async def nowplaying(self, interaction: discord.Interaction): - player: voicelink.Player = interaction.guild.voice_client - if not player: - return await interaction.response.send_message(get_lang(interaction.guild_id, 'noPlayer'), ephemeral=True) - - if interaction.user not in player.channel.members: - if not interaction.user.guild_permissions.manage_guild: - return await interaction.response.send_message(player.get_msg('notInChannel').format(interaction.user.mention, player.channel.mention), ephemeral=True) - - await nowplay(interaction, player) - @app_commands.command( - name = "loop", - description = "Changes Loop mode." - ) - @app_commands.describe( - mode = "Choose a looping mode." - ) - @app_commands.choices( mode = [ - app_commands.Choice(name='Off', value='Off'), - app_commands.Choice(name='Track', value='Track'), - app_commands.Choice(name='Queue', value='Queue') + @commands.hybrid_command(name="nowplaying", aliases=get_aliases("nowplaying")) + @commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild) + async def nowplaying(self, ctx: commands.Context): + "Shows details of the current track." + player: voicelink.Player = ctx.guild.voice_client + if not player: + return await ctx.send(get_lang(ctx.guild.id, 'noPlayer'), ephemeral=True) + + if ctx.author not in player.channel.members: + if not ctx.author.guild_permissions.manage_guild: + return await ctx.send(player.get_msg('notInChannel').format(ctx.author.mention, player.channel.mention), ephemeral=True) + + await nowplay(ctx, player) + + @commands.hybrid_command(name="loop", aliases=get_aliases("loop")) + @app_commands.describe(mode="Choose a looping mode.") + @app_commands.choices(mode=[ + app_commands.Choice(name='Off', value='off'), + app_commands.Choice(name='Track', value='track'), + app_commands.Choice(name='Queue', value='queue') ]) - @app_commands.guild_only() - async def loop(self, interaction: discord.Interaction, mode: str): - player: voicelink.Player = interaction.guild.voice_client + @commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild) + async def loop(self, ctx: commands.Context, mode: str): + "Changes Loop mode." + player: voicelink.Player = ctx.guild.voice_client if not player: - return await interaction.response.send_message(get_lang(interaction.guild_id, 'noPlayer'), ephemeral=True) - - if interaction.user not in player.channel.members: - if not interaction.user.guild_permissions.manage_guild: - return await interaction.response.send_message(player.get_msg('notInChannel').format(interaction.user.mention, player.channel.mention), ephemeral=True) - - if not await player.is_privileged(interaction.user): - return await interaction.response.send_message(player.get_msg('missingPerms_mode'), ephemeral=True) + return await ctx.send(get_lang(ctx.guild.id, 'noPlayer'), ephemeral=True) + if ctx.author not in player.channel.members: + if not ctx.author.guild_permissions.manage_guild: + return await ctx.send(player.get_msg('notInChannel').format(ctx.author.mention, player.channel.mention), ephemeral=True) + + if not await player.is_privileged(ctx.author): + return await ctx.send(player.get_msg('missingPerms_mode'), ephemeral=True) + + if mode.lower() not in ['off', 'track', 'queue']: + mode = "off" player.queue.set_repeat(mode) - await interaction.response.send_message(player.get_msg('repeat').format(mode)) + await ctx.send(player.get_msg('repeat').format(mode.capitalize())) - @app_commands.command( - name = "clear", - description = "Remove all the tracks in your queue or history queue." - ) - @app_commands.describe( - queue = "Choose a queue that you want to clear." - ) - @app_commands.choices( queue = [ - app_commands.Choice(name='Queue', value='Queue'), - app_commands.Choice(name='History', value='History') + @commands.hybrid_command(name="clear", aliases=get_aliases("clear")) + @app_commands.describe(queue="Choose a queue that you want to clear.") + @app_commands.choices(queue=[ + app_commands.Choice(name='Queue', value='queue'), + app_commands.Choice(name='History', value='history') ]) - @app_commands.guild_only() - async def clear(self, interaction: discord.Interaction, queue: str): - player: voicelink.Player = interaction.guild.voice_client + @commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild) + async def clear(self, ctx: commands.Context, queue: str = "queue"): + "Remove all the tracks in your queue or history queue." + player: voicelink.Player = ctx.guild.voice_client if not player: - return await interaction.response.send_message(get_lang(interaction.guild_id, 'noPlayer'), ephemeral=True) - - if interaction.user not in player.channel.members: - if not interaction.user.guild_permissions.manage_guild: - return await interaction.response.send_message(player.get_msg('notInChannel').format(interaction.user.mention, player.channel.mention), ephemeral=True) - - if not await player.is_privileged(interaction.user): - return await interaction.response.send_message(player.get_msg('missingPerms_queue'), ephemeral=True) + return await ctx.send(get_lang(ctx.guild.id, 'noPlayer'), ephemeral=True) - if queue == 'Queue': - player.queue.clear() - elif queue == 'History': + if ctx.author not in player.channel.members: + if not ctx.author.guild_permissions.manage_guild: + return await ctx.send(player.get_msg('notInChannel').format(ctx.author.mention, player.channel.mention), ephemeral=True) + + if not await player.is_privileged(ctx.author): + return await ctx.send(player.get_msg('missingPerms_queue'), ephemeral=True) + + queue = queue.lower() + if queue == 'history': player.queue.history_clear(player.is_playing) - - await interaction.response.send_message(player.get_msg('cleared').format(queue)) - - @app_commands.command( - name = "remove", - description = "Removes specified track or a range of tracks from the queue." - ) - @app_commands.describe( - position = "Input a position from the queue to be removed.", - position2 = "Set the range of the queue to be removed.", - member = "Remove tracks requested by a specific member." - ) - @app_commands.guild_only() - async def remove(self, interaction: discord.Interaction, position: int, position2: int = None, member: discord.Member = None): - player: voicelink.Player = interaction.guild.voice_client - if not player: - return await interaction.response.send_message(get_lang(interaction.guild_id, 'noPlayer'), ephemeral=True) - - if interaction.user not in player.channel.members: - if not interaction.user.guild_permissions.manage_guild: - return await interaction.response.send_message(player.get_msg('notInChannel').format(interaction.user.mention, player.channel.mention), ephemeral=True) - - if not await player.is_privileged(interaction.user): - return await interaction.response.send_message(player.get_msg('missingPerms_queue'), ephemeral=True) + else: + queue = "queue" + player.queue.clear() - removedTrack = player.queue.remove(position, position2, member) - await interaction.response.send_message(player.get_msg('removed').format(removedTrack)) + await ctx.send(player.get_msg('cleared').format(queue.capitalize())) - @app_commands.command( - name = "forward", - description = "Forwards by a certain amount of time in the current track. The default is 10 seconds." - ) + @commands.hybrid_command(name="remove", aliases=get_aliases("remove")) @app_commands.describe( - position = "Input a amount that you to forward to. Exmaple: 1:20" + position1="Input a position from the queue to be removed.", + position2="Set the range of the queue to be removed.", + member="Remove tracks requested by a specific member." ) - @app_commands.checks.cooldown(2, 20.0, key=lambda i: (i.guild_id)) - @app_commands.guild_only() - async def forward(self, interaction: discord.Interaction, position: str = "10"): - player: voicelink.Player = interaction.guild.voice_client + @commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild) + async def remove(self, ctx: commands.Context, position1: int, position2: int = None, member: discord.Member = None): + "Removes specified track or a range of tracks from the queue." + player: voicelink.Player = ctx.guild.voice_client if not player: - return await interaction.response.send_message(get_lang(interaction.guild_id, 'noPlayer'), ephemeral=True) - - if interaction.user not in player.channel.members: - if not interaction.user.guild_permissions.manage_guild: - return await interaction.response.send_message(player.get_msg('notInChannel').format(interaction.user.mention, player.channel.mention), ephemeral=True) - + return await ctx.send(get_lang(ctx.guild.id, 'noPlayer'), ephemeral=True) + + if ctx.author not in player.channel.members: + if not ctx.author.guild_permissions.manage_guild: + return await ctx.send(player.get_msg('notInChannel').format(ctx.author.mention, player.channel.mention), ephemeral=True) + + if not await player.is_privileged(ctx.author): + return await ctx.send(player.get_msg('missingPerms_queue'), ephemeral=True) + + removedTrack = player.queue.remove(position1, position2, member) + await ctx.send(player.get_msg('removed').format(removedTrack)) + + @commands.hybrid_command(name="forward", aliases=get_aliases("forward")) + @app_commands.describe(position="Input a amount that you to forward to. Exmaple: 1:20") + @commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild) + async def forward(self, ctx: commands.Context, position: str = "10"): + "Forwards by a certain amount of time in the current track. The default is 10 seconds." + player: voicelink.Player = ctx.guild.voice_client + if not player: + return await ctx.send(get_lang(ctx.guild.id, 'noPlayer'), ephemeral=True) + + if ctx.author not in player.channel.members: + if not ctx.author.guild_permissions.manage_guild: + return await ctx.send(player.get_msg('notInChannel').format(ctx.author.mention, player.channel.mention), ephemeral=True) + if not player.current: - return await interaction.response.send_message(player.get_msg('noTrackPlaying'), ephemeral=True) - if not await player.is_privileged(interaction.user): - return await interaction.response.send_message(player.get_msg('missingPerms_pos'), ephemeral=True) + return await ctx.send(player.get_msg('noTrackPlaying'), ephemeral=True) + if not await player.is_privileged(ctx.author): + return await ctx.send(player.get_msg('missingPerms_pos'), ephemeral=True) num = formatTime(position) if num is None: - return await interaction.response.send_message(player.get_msg('timeFormatError'), ephemeral=True) + return await ctx.send(player.get_msg('timeFormatError'), ephemeral=True) await player.seek(player.position + num) - await interaction.response.send_message(player.get_msg('forward').format(ctime(player.position + num))) - - @app_commands.command( - name = "rewind", - description = "Rewind by a certain amount of time in the current track. The default is 10 seconds." - ) - @app_commands.describe( - position = "Input a amount that you to rewind to. Exmaple: 1:20" - ) - @app_commands.checks.cooldown(2, 20.0, key=lambda i: (i.guild_id)) - @app_commands.guild_only() - async def rewind(self, interaction: discord.Interaction, position: str = "10"): - player: voicelink.Player = interaction.guild.voice_client - if not player: - return await interaction.response.send_message(get_lang(interaction.guild_id, 'noPlayer'), ephemeral=True) - - if interaction.user not in player.channel.members: - if not interaction.user.guild_permissions.manage_guild: - return await interaction.response.send_message(player.get_msg('notInChannel').format(interaction.user.mention, player.channel.mention), ephemeral=True) - - if not player.current: - return await interaction.response.send_message(player.get_msg('noTrackPlaying'), ephemeral=True) + await ctx.send(player.get_msg('forward').format(ctime(player.position + num))) - if not await player.is_privileged(interaction.user): - return await interaction.response.send_message(player.get_msg('missingPerms_pos'), ephemeral=True) + @commands.hybrid_command(name="rewind", aliases=get_aliases("rewind")) + @app_commands.describe(position="Input a amount that you to rewind to. Exmaple: 1:20") + @commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild) + async def rewind(self, ctx: commands.Context, position: str = "10"): + "Rewind by a certain amount of time in the current track. The default is 10 seconds." + player: voicelink.Player = ctx.guild.voice_client + if not player: + return await ctx.send(get_lang(ctx.guild.id, 'noPlayer'), ephemeral=True) + + if ctx.author not in player.channel.members: + if not ctx.author.guild_permissions.manage_guild: + return await ctx.send(player.get_msg('notInChannel').format(ctx.author.mention, player.channel.mention), ephemeral=True) + + if not player.current: + return await ctx.send(player.get_msg('noTrackPlaying'), ephemeral=True) + + if not await player.is_privileged(ctx.author): + return await ctx.send(player.get_msg('missingPerms_pos'), ephemeral=True) num = formatTime(position) if num is None: - return await interaction.response.send_message(player.get_msg('timeFormatError'), ephemeral=True) + return await ctx.send(player.get_msg('timeFormatError'), ephemeral=True) await player.seek(player.position - num) - await interaction.response.send_message(player.get_msg('rewind').format(ctime(player.position - num))) + await ctx.send(player.get_msg('rewind').format(ctime(player.position - num))) - @app_commands.command( - name = "replay", - description = "Reset the progress of the current song." - ) - @app_commands.checks.cooldown(2, 20.0, key=lambda i: (i.guild_id)) - @app_commands.guild_only() - async def replay(self, interaction: discord.Interaction): - player: voicelink.Player = interaction.guild.voice_client + @commands.hybrid_command(name="replay", aliases=get_aliases("replay")) + @commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild) + async def replay(self, ctx: commands.Context): + "Reset the progress of the current song." + player: voicelink.Player = ctx.guild.voice_client if not player: - return await interaction.response.send_message(get_lang(interaction.guild_id, 'noPlayer'), ephemeral=True) - - if interaction.user not in player.channel.members: - if not interaction.user.guild_permissions.manage_guild: - return await interaction.response.send_message(player.get_msg('notInChannel').format(interaction.user.mention, player.channel.mention), ephemeral=True) - - if not player.current: - return await interaction.response.send_message(player.get_msg('noTrackPlaying'), ephemeral=True) + return await ctx.send(get_lang(ctx.guild.id, 'noPlayer'), ephemeral=True) - if not await player.is_privileged(interaction.user): - return await interaction.response.send_message(player.get_msg('missingPerms_pos'), ephemeral=True) + if ctx.author not in player.channel.members: + if not ctx.author.guild_permissions.manage_guild: + return await ctx.send(player.get_msg('notInChannel').format(ctx.author.mention, player.channel.mention), ephemeral=True) + + if not player.current: + return await ctx.send(player.get_msg('noTrackPlaying'), ephemeral=True) + + if not await player.is_privileged(ctx.author): + return await ctx.send(player.get_msg('missingPerms_pos'), ephemeral=True) await player.seek(0) - await interaction.response.send_message(player.get_msg('replay')) - - @app_commands.command( - name = "shuffle", - description = "Randomizes the tracks in the queue." - ) - @app_commands.checks.cooldown(1, 15.0, key=lambda i: (i.guild_id)) - @app_commands.guild_only() - async def shuffle(self, interaction: discord.Interaction): - player: voicelink.Player = interaction.guild.voice_client + await ctx.send(player.get_msg('replay')) + + @commands.hybrid_command(name="shuffle", aliases=get_aliases("shuffle")) + @commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild) + async def shuffle(self, ctx: commands.Context): + "Randomizes the tracks in the queue." + player: voicelink.Player = ctx.guild.voice_client if not player: - return await interaction.response.send_message(get_lang(interaction.guild_id, 'noPlayer'), ephemeral=True) - - if interaction.user not in player.channel.members: - if not interaction.user.guild_permissions.manage_guild: - return await interaction.response.send_message(player.get_msg('notInChannel').format(interaction.user.mention, player.channel.mention), ephemeral=True) - - if not await player.is_privileged(interaction.user): - if interaction.user in player.shuffle_votes: - return await interaction.response.send_message(player.get_msg('voted'), ephemeral=True) + return await ctx.send(get_lang(ctx.guild.id, 'noPlayer'), ephemeral=True) + + if ctx.author not in player.channel.members: + if not ctx.author.guild_permissions.manage_guild: + return await ctx.send(player.get_msg('notInChannel').format(ctx.author.mention, player.channel.mention), ephemeral=True) + + if not await player.is_privileged(ctx.author): + if ctx.author in player.shuffle_votes: + return await ctx.send(player.get_msg('voted'), ephemeral=True) else: - player.shuffle_votes.add(interaction.user) + player.shuffle_votes.add(ctx.author) if len(player.shuffle_votes) >= (required := player.required()): pass else: - return await interaction.response.send_message(player.get_msg('shuffleVote').format(interaction.user, len(player.skip_votes), required)) + return await ctx.send(player.get_msg('shuffleVote').format(ctx.author, len(player.skip_votes), required)) replacement = player.queue.tracks() if len(replacement) < 3: - return await interaction.response.send_message(player.get_msg('shuffleError')) + return await ctx.send(player.get_msg('shuffleError')) shuffle(replacement) player.queue.replace("Queue", replacement) player.shuffle_votes.clear() - await interaction.response.send_message(player.get_msg('shuffled')) + await ctx.send(player.get_msg('shuffled')) - @app_commands.command( - name = "move", - description = "Moves the specified song to the specified position." - ) + @commands.hybrid_command(name="move", aliases=get_aliases("move")) @app_commands.describe( - track = "The track to move. Example: 2", - position = "The new position to move the track to. Exmaple: 1" + position1="The track to move. Example: 2", + position2="The new position to move the track to. Exmaple: 1" ) - @app_commands.checks.cooldown(2, 15.0, key=lambda i: (i.guild_id)) - @app_commands.guild_only() - async def move(self, interaction: discord.Interaction, track: int, position: int ): - player: voicelink.Player = interaction.guild.voice_client + @commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild) + async def move(self, ctx: commands.Context, position1: int, position2: int): + "Moves the specified song to the specified position." + player: voicelink.Player = ctx.guild.voice_client if not player: - return await interaction.response.send_message(get_lang(interaction.guild_id, 'noPlayer'), ephemeral=True) - - if interaction.user not in player.channel.members: - if not interaction.user.guild_permissions.manage_guild: - return await interaction.response.send_message(player.get_msg('notInChannel').format(interaction.user.mention, player.channel.mention), ephemeral=True) - - if not await player.is_privileged(interaction.user): - return await interaction.response.send_message(player.get_msg('missingPerms_pos'), ephemeral=True) + return await ctx.send(get_lang(ctx.guild.id, 'noPlayer'), ephemeral=True) + + if ctx.author not in player.channel.members: + if not ctx.author.guild_permissions.manage_guild: + return await ctx.send(player.get_msg('notInChannel').format(ctx.author.mention, player.channel.mention), ephemeral=True) + + if not await player.is_privileged(ctx.author): + return await ctx.send(player.get_msg('missingPerms_pos'), ephemeral=True) + + player.queue.swap(position1, position2) + await ctx.send(player.get_msg('moved').format(position1, position2)) + + @commands.hybrid_command(name="lyrics", aliases=get_aliases("lyrics")) + @app_commands.describe(name="Searches for your query and displays the reutned lyrics.") + @commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild) + async def lyrics(self, ctx: commands.Context, name: str = None): + "Displays lyrics for the playing track." + player: voicelink.Player = ctx.guild.voice_client - player.queue.swap(track, position) - await interaction.response.send_message(player.get_msg('moved').format(track, position)) - - @app_commands.command( - name = "lyrics", - description = "Displays lyrics for the playing track." - ) - @app_commands.describe( - name = "Searches for your query and displays the reutned lyrics.", - ) - @app_commands.checks.cooldown(2, 60.0, key=lambda i: (i.guild_id)) - @app_commands.guild_only() - async def lyrics(self, interaction: discord.Interaction, name: str = None): - player: voicelink.Player = interaction.guild.voice_client - if not name: if not player or not player.is_playing: - return await interaction.response.send_message(get_lang(interaction.guild_id, 'noTrackPlaying'), ephemeral=True) + return await ctx.send(get_lang(ctx.guild.id, 'noTrackPlaying'), ephemeral=True) name = player.current.title + " " + player.current.author - await interaction.response.defer() + await ctx.defer() song = await getLyrics(name) if not song: - return await interaction.followup.send(get_lang(interaction.guild_id, 'lyricsNotFound'), ephemeral=True) + return await ctx.send(get_lang(ctx.guild.id, 'lyricsNotFound'), ephemeral=True) - view = LyricsView(name=name, source={_: re.findall(r'.*\n(?:.*\n){,22}', v) for _, v in song.items()}, author=interaction.user) - await interaction.followup.send(embed=view.build_embed(), view=view) - view.response = await interaction.original_response() + view = LyricsView(name=name, source={_: re.findall( + r'.*\n(?:.*\n){,22}', v) for _, v in song.items()}, author=ctx.author) + message = await ctx.send(embed=view.build_embed(), view=view) + view.response = message - @app_commands.command( - name = "swapdj", - description = "Transfer dj to another." - ) - @app_commands.describe( - member = "Choose a member to transfer the dj role." - ) - @app_commands.guild_only() - async def swapdj(self, interaction: discord.Interaction, member: discord.Member): - player: voicelink.Player = interaction.guild.voice_client + @commands.hybrid_command(name="swapdj", aliases=get_aliases("swapdj")) + @app_commands.describe(member="Choose a member to transfer the dj role.") + @commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild) + async def swapdj(self, ctx: commands.Context, member: discord.Member): + "Transfer dj to another." + player: voicelink.Player = ctx.guild.voice_client if not player: - return await interaction.response.send_message(get_lang(interaction.guild_id, 'noPlayer'), ephemeral=True) - - if interaction.user not in player.channel.members: - if not interaction.user.guild_permissions.manage_guild: - return await interaction.response.send_message(player.get_msg('notInChannel').format(interaction.user.mention, player.channel.mention), ephemeral=True) - - if player.dj.id != interaction.user.id or player.settings.get('dj', False): - return await interaction.response.send_message(player.get_msg('notdj').format(f"<@&{player.settings['dj']}>" if player.settings.get('dj') else player.dj.mention), ephemeral=True) - + return await ctx.send(get_lang(ctx.guild.id, 'noPlayer'), ephemeral=True) + + if ctx.author not in player.channel.members: + if not ctx.author.guild_permissions.manage_guild: + return await ctx.send(player.get_msg('notInChannel').format(ctx.author.mention, player.channel.mention), ephemeral=True) + + if player.dj.id != ctx.author.id or player.settings.get('dj', False): + return await ctx.send(player.get_msg('notdj').format(f"<@&{player.settings['dj']}>" if player.settings.get('dj') else player.dj.mention), ephemeral=True) + if player.dj.id == member.id or member.bot: - return await interaction.response.send_message(player.get_msg('djToMe'), ephemeral=True) - + return await ctx.send(player.get_msg('djToMe'), ephemeral=True) + if member not in player.channel.members: - return await interaction.response.send_message(player.get_msg('djnotinchannel').format(member), ephemeral=True) - + return await ctx.send(player.get_msg('djnotinchannel').format(member), ephemeral=True) + player.dj = member - await interaction.response.send_message(player.get_msg('djswap').format(member)) + await ctx.send(player.get_msg('djswap').format(member)) - - @app_commands.command( - name = "chapters", - description = "Lists all chapters of the currently playing song (if any)." - ) - @app_commands.checks.cooldown(2, 30.0, key=lambda i: (i.guild_id)) - @app_commands.guild_only() - async def chapters(self, interaction: discord.Interaction): - player: voicelink.Player = interaction.guild.voice_client + @commands.hybrid_command(name="chapters", aliases=get_aliases("chapters")) + @commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild) + async def chapters(self, ctx: commands.Context): + "Lists all chapters of the currently playing song (if any)." + player: voicelink.Player = ctx.guild.voice_client if not player: - return await interaction.response.send_message(get_lang(interaction.guild_id, 'noPlayer'), ephemeral=True) - - if interaction.user not in player.channel.members: - if not interaction.user.guild_permissions.manage_guild: - return await interaction.response.send_message(player.get_msg('notInChannel').format(interaction.user.mention, player.channel.mention), ephemeral=True) - - if not (track := player.current): - return await interaction.response.send_message(player.get_msg('noTrackPlaying'), ephemeral=True) - if not await player.is_privileged(interaction.user): - return await interaction.response.send_message(player.get_msg('missingPerms_pos'), ephemeral=True) - - if track.source != 'youtube': - return await interaction.response.send_message(player.get_msg('chatpersNotSupport'), ephemeral=True) + return await ctx.send(get_lang(ctx.guild.id, 'noPlayer'), ephemeral=True) - request_uri = "https://youtube.googleapis.com/youtube/v3/videos?part=snippet&id={videoId}&key={key}".format(videoId=track.identifier, key=youtube_api_key) + if ctx.author not in player.channel.members: + if not ctx.author.guild_permissions.manage_guild: + return await ctx.send(player.get_msg('notInChannel').format(ctx.author.mention, player.channel.mention), ephemeral=True) + + if not (track := player.current): + return await ctx.send(player.get_msg('noTrackPlaying'), ephemeral=True) + if not await player.is_privileged(ctx.author): + return await ctx.send(player.get_msg('missingPerms_pos'), ephemeral=True) + + if track.source != 'youtube': + return await ctx.send(player.get_msg('chatpersNotSupport'), ephemeral=True) + + request_uri = "https://youtube.googleapis.com/youtube/v3/videos?part=snippet&id={videoId}&key={key}".format( + videoId=track.identifier, key=youtube_api_key) data = await requests_api(request_uri) if not data: - return await interaction.response.send_message(player.get_msg('noChaptersFound'), ephemeral=True) + return await ctx.send(player.get_msg('noChaptersFound'), ephemeral=True) try: desc = data['items'][0]['snippet']['description'] except KeyError: - return await interaction.response.send_message(player.get_msg('noChaptersFound'), ephemeral=True) - - chapters = re.findall(r"(?P\d+:\d+|\d+:\d+:\d+) (?P.+)", desc) + return await ctx.send(player.get_msg('noChaptersFound'), ephemeral=True) + + chapters = re.findall( + r"(?P\d+:\d+|\d+:\d+:\d+) (?P.+)", desc) if not chapters: - return await interaction.response.send_message(player.get_msg('noChaptersFound'), ephemeral=True) + return await ctx.send(player.get_msg('noChaptersFound'), ephemeral=True) - view = ChapterView(player, chapters, author=interaction.user) - await interaction.response.send_message(view=view) - view.response = await interaction.original_response() + view = ChapterView(player, chapters, author=ctx.author) + message = await ctx.send(view=view) + view.response = message - @app_commands.command( - name = "autoplay", - description = "Toggles autoplay mode, it will automatically queue the best songs to play." - ) - @app_commands.guild_only() - async def autoplay(self, interaction: discord.Interaction): - player: voicelink.Player = interaction.guild.voice_client + @commands.hybrid_command(name="autoplay", aliases=get_aliases("autoplay")) + @commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild) + async def autoplay(self, ctx: commands.Context): + "Toggles autoplay mode, it will automatically queue the best songs to play." + player: voicelink.Player = ctx.guild.voice_client if not player: - return await interaction.response.send_message(get_lang(interaction.guild_id, 'noPlayer'), ephemeral=True) - - if not await player.is_privileged(interaction.user): - return await interaction.response.send_message(player.get_msg('missingPerms_autoplay'), ephemeral=True) + return await ctx.send(get_lang(ctx.guild.id, 'noPlayer'), ephemeral=True) + + if not await player.is_privileged(ctx.author): + return await ctx.send(player.get_msg('missingPerms_autoplay'), ephemeral=True) check = not player.settings.get("autoplay", False) player.settings['autoplay'] = check - await interaction.response.send_message(player.get_msg('autoplay').format(player.get_msg('enabled') if check else player.get_msg('disabled'))) + await ctx.send(player.get_msg('autoplay').format(player.get_msg('enabled') if check else player.get_msg('disabled'))) if not player.is_playing: await player.do_next() - @app_commands.command( - name = "help", - description = "Lists all the commands in Vocard." - ) + @commands.hybrid_command(name="help", aliases=get_aliases("help")) @app_commands.autocomplete(category=help_autocomplete) - @app_commands.guild_only() - async def help(self, interaction: discord.Interaction, category: str = "News") -> None: + @commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild) + async def help(self, ctx: commands.Context, category: str = "News") -> None: + "Lists all the commands in Vocard." if category not in self.bot.cogs: category = "News" - view = HelpView(self.bot, interaction.user) + view = HelpView(self.bot, ctx.author) embed = view.build_embed(category) - await interaction.response.send_message(embed=embed, view=view) - view.response = await interaction.original_response() + message = await ctx.send(embed=embed, view=view) + view.response = message - @app_commands.command( - name = "ping", - description = "Test if the bot is alive, and see the delay between your commands and my response." - ) - @app_commands.guild_only() - async def ping(self, interaction: discord.Interaction): - player: voicelink.Player = interaction.guild.voice_client + @commands.hybrid_command(name="ping", aliases=get_aliases("ping")) + @commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild) + async def ping(self, ctx: commands.Context): + "Test if the bot is alive, and see the delay between your commands and my response." + player: voicelink.Player = ctx.guild.voice_client embed = discord.Embed(color=embed_color) - embed.add_field(name=get_lang(interaction.guild_id, 'pingTitle1'), value=get_lang(interaction.guild_id, 'pingfield1').format("0", "0", self.bot.latency, '😭' if self.bot.latency > 5 else ('😨' if self.bot.latency > 1 else '👌'), "St Louis, MO, United States")) + embed.add_field(name=get_lang(ctx.guild.id, 'pingTitle1'), value=get_lang(ctx.guild.id, 'pingfield1').format( + "0", "0", self.bot.latency, '😭' if self.bot.latency > 5 else ('😨' if self.bot.latency > 1 else '👌'), "St Louis, MO, United States")) if player: - embed.add_field(name=get_lang(interaction.guild_id, 'pingTitle2'), value=get_lang(interaction.guild_id, 'pingfield2').format(player.node._identifier, player.ping, player.node.player_count, player.channel.rtc_region), inline=False) + embed.add_field(name=get_lang(ctx.guild.id, 'pingTitle2'), value=get_lang(ctx.guild.id, 'pingfield2').format( + player.node._identifier, player.ping, player.node.player_count, player.channel.rtc_region), inline=False) + + await ctx.send(embed=embed) + - await interaction.response.send_message(embed=embed) - async def setup(bot: commands.Bot) -> None: - await bot.add_cog(Basic(bot)) \ No newline at end of file + await bot.add_cog(Basic(bot)) diff --git a/cogs/effect.py b/cogs/effect.py index e4dd7a3..8d1306e 100644 --- a/cogs/effect.py +++ b/cogs/effect.py @@ -3,262 +3,191 @@ import voicelink from function import ( get_lang, + get_aliases, + cooldown_check ) from discord import app_commands from discord.ext import commands -async def check_access(interaction: discord.Interaction): - player: voicelink.Player = interaction.guild.voice_client + +async def check_access(ctx: commands.Context): + player: voicelink.Player = ctx.guild.voice_client if not player: - raise voicelink.VoicelinkException(get_lang(interaction.guild_id, 'noPlayer')) - + raise voicelink.VoicelinkException(get_lang(ctx.guild.id, 'noPlayer')) + + if ctx.author not in player.channel.members: + if not ctx.author.guild_permissions.manage_guild: + return await ctx.send(player.get_msg('notInChannel').format(ctx.author.mention, player.channel.mention), ephemeral=True) + return player + class Effect(commands.Cog): def __init__(self, bot: commands.Bot) -> None: self.bot = bot self.description = "This category is only available to DJ on this server. (You can setdj on your server by /settings setdj )" - - async def effect_autocomplete(self, interaction: discord.Interaction, current: str) -> list: - player: voicelink.Player = interaction.guild.voice_client + + async def effect_autocomplete(self, ctx: commands.Context, current: str) -> list: + player: voicelink.Player = ctx.guild.voice_client if not player: return [] if current: - return [ app_commands.Choice(name=effect.tag, value=effect.tag) for effect in player.filters.get_filters() if current in effect.tag] - return [ app_commands.Choice(name=effect.tag, value=effect.tag) for effect in player.filters.get_filters() ] + return [app_commands.Choice(name=effect.tag, value=effect.tag) for effect in player.filters.get_filters() if current in effect.tag] + return [app_commands.Choice(name=effect.tag, value=effect.tag) for effect in player.filters.get_filters()] - @app_commands.command( - name = "speed", - description= "Sets the player's playback speed." - ) - @app_commands.describe( - value = "The value to set the speed to. Default is `1.0`" - ) - @app_commands.guild_only() - async def speed(self, interaction: discord.Interaction, value: app_commands.Range[float, 0, 2]): - player = await check_access(interaction) + @commands.hybrid_command(name="speed", aliases=get_aliases("speed")) + @app_commands.describe(value="The value to set the speed to. Default is `1.0`") + @commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild) + async def speed(self, ctx: commands.Context, value: commands.Range[float, 0, 2]): + "Sets the player's playback speed" + player = await check_access(ctx) - if interaction.user not in player.channel.members: - if not interaction.user.guild_permissions.manage_guild: - return await interaction.response.send_message(player.get_msg('notInChannel').format(interaction.user.mention, player.channel.mention), ephemeral=True) - if player.filters.has_filter(filter_tag="speed"): player.filters.remove_filter(filter_tag="speed") - await player.add_filter(voicelink.Timescale(tag="speed", speed= value)) - await interaction.response.send_message(f"You set the speed to **{value}**.") + await player.add_filter(voicelink.Timescale(tag="speed", speed=value)) + await ctx.send(f"You set the speed to **{value}**.") - @app_commands.command( - name = "karaoke", - description= "Uses equalization to eliminate part of a band, usually targeting vocals." - ) + @commands.hybrid_command(name="karaoke", aliases=get_aliases("karaoke")) @app_commands.describe( - level = "The level of the karaoke. Default is `1.0`", - monolevel = "The monolevel of the karaoke. Default is `1.0`", - filterband = "The filter band of the karaoke. Default is `220.0`", - filterwidth = "The filter band of the karaoke. Default is `100.0`" + level="The level of the karaoke. Default is `1.0`", + monolevel="The monolevel of the karaoke. Default is `1.0`", + filterband="The filter band of the karaoke. Default is `220.0`", + filterwidth="The filter band of the karaoke. Default is `100.0`" ) - @app_commands.guild_only() - async def karaoke(self, interaction: discord.Interaction, level: app_commands.Range[float, 0, 2] = 1.0, monolevel: app_commands.Range[float, 0, 2] = 1.0, filterband: app_commands.Range[float, 100, 300] = 220.0, filterwidth: app_commands.Range[float, 50, 150] = 100.0) -> None: - player = await check_access(interaction) + @commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild) + async def karaoke(self, ctx: commands.Context, level: commands.Range[float, 0, 2] = 1.0, monolevel: commands.Range[float, 0, 2] = 1.0, filterband: commands.Range[float, 100, 300] = 220.0, filterwidth: commands.Range[float, 50, 150] = 100.0) -> None: + "Uses equalization to eliminate part of a band, usually targeting vocals." + player = await check_access(ctx) - if interaction.user not in player.channel.members: - if not interaction.user.guild_permissions.manage_guild: - return await interaction.response.send_message(player.get_msg('notInChannel').format(interaction.user.mention, player.channel.mention), ephemeral=True) - if player.filters.has_filter(filter_tag="karaoke"): player.filters.remove_filter(filter_tag="karaoke") await player.add_filter(voicelink.Karaoke(tag="karaoke", level=level, mono_level=monolevel, filter_band=filterband, filter_width=filterwidth)) - await interaction.response.send_message(player.get_msg('karaoke').format(level, monolevel, filterband, filterwidth)) - - @app_commands.command( - name = "tremolo", - description= "Uses amplification to create a shuddering effect, where the volume quickly oscillates." - ) + await ctx.send(player.get_msg('karaoke').format(level, monolevel, filterband, filterwidth)) + + @commands.hybrid_command(name="tremolo", aliases=get_aliases("tremolo")) @app_commands.describe( - frequency = "The frequency of the tremolo. Default is `2.0`", - depth = "The depth of the tremolo. Default is `0.5`" + frequency="The frequency of the tremolo. Default is `2.0`", + depth="The depth of the tremolo. Default is `0.5`" ) - @app_commands.guild_only() - async def tremolo(self, interaction: discord.Interaction, frequency: app_commands.Range[float, 0, 10] = 2.0, depth: app_commands.Range[float, 0, 1] = 0.5) -> None: - player = await check_access(interaction) + @commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild) + async def tremolo(self, ctx: commands.Context, frequency: commands.Range[float, 0, 10] = 2.0, depth: commands.Range[float, 0, 1] = 0.5) -> None: + "Uses amplification to create a shuddering effect, where the volume quickly oscillates." + player = await check_access(ctx) - if interaction.user not in player.channel.members: - if not interaction.user.guild_permissions.manage_guild: - return await interaction.response.send_message(player.get_msg('notInChannel').format(interaction.user.mention, player.channel.mention), ephemeral=True) - if player.filters.has_filter(filter_tag="tremolo"): player.filters.remove_filter(filter_tag="tremolo") await player.add_filter(voicelink.Tremolo(tag="tremolo", frequency=frequency, depth=depth)) - await interaction.response.send_message(player.get_msg('tremolo&vibrato').format(frequency, depth)) + await ctx.send(player.get_msg('tremolo&vibrato').format(frequency, depth)) - @app_commands.command( - name = "vibrato", - description= "Similar to tremolo. While tremolo oscillates the volume, vibrato oscillates the pitch." - ) + @commands.hybrid_command(name="vibrato", aliases=get_aliases("vibrato")) @app_commands.describe( - frequency = "The frequency of the vibrato. Default is `2.0`", - depth = "The Depth of the vibrato. Default is `0.5`" + frequency="The frequency of the vibrato. Default is `2.0`", + depth="The Depth of the vibrato. Default is `0.5`" ) - @app_commands.guild_only() - async def vibrato(self, interaction: discord.Interaction, frequency: app_commands.Range[float, 0, 14] = 2.0, depth: app_commands.Range[float, 0, 1] = 0.5) -> None: - player = await check_access(interaction) + @commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild) + async def vibrato(self, ctx: commands.Context, frequency: commands.Range[float, 0, 14] = 2.0, depth: commands.Range[float, 0, 1] = 0.5) -> None: + "Similar to tremolo. While tremolo oscillates the volume, vibrato oscillates the pitch." + player = await check_access(ctx) - if interaction.user not in player.channel.members: - if not interaction.user.guild_permissions.manage_guild: - return await interaction.response.send_message(player.get_msg('notInChannel').format(interaction.user.mention, player.channel.mention), ephemeral=True) - if player.filters.has_filter(filter_tag="vibrato"): player.filters.remove_filter(filter_tag="vibrato") await player.add_filter(voicelink.Vibrato(tag="vibrato", frequency=frequency, depth=depth)) - await interaction.response.send_message(player.get_msg('tremolo&vibrato').format(frequency, depth)) + await ctx.send(player.get_msg('tremolo&vibrato').format(frequency, depth)) - @app_commands.command( - name = "rotation", - description= "Rotates the sound around the stereo channels/user headphones aka Audio Panning." - ) - @app_commands.describe( - hertz = "The hertz of the rotation. Default is `0.2`" - ) - @app_commands.guild_only() - async def rotation(self, interaction: discord.Interaction, hertz: app_commands.Range[float, 0, 2] = 0.2) -> None: - player = await check_access(interaction) + @commands.hybrid_command(name="rotation", aliases=get_aliases("rotation")) + @app_commands.describe(hertz="The hertz of the rotation. Default is `0.2`") + @commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild) + async def rotation(self, ctx: commands.Context, hertz: commands.Range[float, 0, 2] = 0.2) -> None: + "Rotates the sound around the stereo channels/user headphones aka Audio Panning." + player = await check_access(ctx) - if interaction.user not in player.channel.members: - if not interaction.user.guild_permissions.manage_guild: - return await interaction.response.send_message(player.get_msg('notInChannel').format(interaction.user.mention, player.channel.mention), ephemeral=True) - if player.filters.has_filter(filter_tag="rotation"): player.filters.remove_filter(filter_tag="rotation") await player.add_filter(voicelink.Rotation(tag="rotation", rotation_hertz=hertz)) - await interaction.response.send_message(player.get_msg('rotation').format(hertz)) + await ctx.send(player.get_msg('rotation').format(hertz)) - @app_commands.command( - name = "distortion", - description= "Distortion effect. It can generate some pretty unique audio effects." - ) - @app_commands.guild_only() - async def distortion(self, interaction: discord.Interaction) -> None: - player = await check_access(interaction) + @commands.hybrid_command(name="distortion", aliases=get_aliases("distortion")) + @commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild) + async def distortion(self, ctx: commands.Context) -> None: + "Distortion effect. It can generate some pretty unique audio effects." + player = await check_access(ctx) - if interaction.user not in player.channel.members: - if not interaction.user.guild_permissions.manage_guild: - return await interaction.response.send_message(player.get_msg('notInChannel').format(interaction.user.mention, player.channel.mention), ephemeral=True) - if player.filters.has_filter(filter_tag="distortion"): player.filters.remove_filter(filter_tag="distortion") - await player.add_filter(voicelink.Distortion(tag="distortion", sin_offset= 0.0, sin_scale= 1.0, cos_offset= 0.0, cos_scale=1.0, tan_offset= 0.0, tan_scale=1.0, offset=0.0, scale=1.0)) - await interaction.response.send_message(player.get_msg('distortion')) + await player.add_filter(voicelink.Distortion(tag="distortion", sin_offset=0.0, sin_scale=1.0, cos_offset=0.0, cos_scale=1.0, tan_offset=0.0, tan_scale=1.0, offset=0.0, scale=1.0)) + await ctx.send(player.get_msg('distortion')) - @app_commands.command( - name = "lowpass", - description= "Filter which supresses higher frequencies and allows lower frequencies to pass." - ) - @app_commands.describe( - smoothing = "The level of the lowPass. Default is `20.0`" - ) - @app_commands.guild_only() - async def lowpass(self, interaction: discord.Interaction, smoothing: app_commands.Range[float, 10, 30] = 20.0) -> None: - player = await check_access(interaction) + @commands.hybrid_command(name="lowpass", aliases=get_aliases("lowpass")) + @app_commands.describe(smoothing="The level of the lowPass. Default is `20.0`") + @commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild) + async def lowpass(self, ctx: commands.Context, smoothing: commands.Range[float, 10, 30] = 20.0) -> None: + "Filter which supresses higher frequencies and allows lower frequencies to pass." + player = await check_access(ctx) - if interaction.user not in player.channel.members: - if not interaction.user.guild_permissions.manage_guild: - return await interaction.response.send_message(player.get_msg('notInChannel').format(interaction.user.mention, player.channel.mention), ephemeral=True) - if player.filters.has_filter(filter_tag="lowpass"): player.filters.remove_filter(filter_tag="lowpass") - await player.add_filter(voicelink.LowPass(tag="lowpass", smoothing= smoothing)) - await interaction.response.send_message(player.get_msg('lowpass').format(smoothing)) + await player.add_filter(voicelink.LowPass(tag="lowpass", smoothing=smoothing)) + await ctx.send(player.get_msg('lowpass').format(smoothing)) - @app_commands.command( - name = "channelmix", - description= "Filter which manually adjusts the panning of the audio." - ) + @commands.hybrid_command(name="channelmix", aliases=get_aliases("channelmix")) @app_commands.describe( - left_to_left = "Sounds from left to left. Default is `1.0`", - right_to_right = "Sounds from right to right. Default is `1.0`", - left_to_right = "Sounds from left to right. Default is `0.0`", - right_to_left = "Sounds from right to left. Default is `0.0`" + left_to_left="Sounds from left to left. Default is `1.0`", + right_to_right="Sounds from right to right. Default is `1.0`", + left_to_right="Sounds from left to right. Default is `0.0`", + right_to_left="Sounds from right to left. Default is `0.0`" ) - @app_commands.guild_only() - async def channelmix(self, interaction: discord.Interaction, left_to_left: app_commands.Range[float, 0, 1] = 1.0, right_to_right: app_commands.Range[float, 0, 1] = 1.0, left_to_right: app_commands.Range[float, 0, 1] = 0.0, right_to_left: app_commands.Range[float, 0, 1] = 0.0) -> None: - player = await check_access(interaction) - - if interaction.user not in player.channel.members: - if not interaction.user.guild_permissions.manage_guild: - return await interaction.response.send_message(player.get_msg('notInChannel').format(interaction.user.mention, player.channel.mention), ephemeral=True) - + @commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild) + async def channelmix(self, ctx: commands.Context, left_to_left: commands.Range[float, 0, 1] = 1.0, right_to_right: commands.Range[float, 0, 1] = 1.0, left_to_right: commands.Range[float, 0, 1] = 0.0, right_to_left: commands.Range[float, 0, 1] = 0.0) -> None: + "Filter which manually adjusts the panning of the audio." + player = await check_access(ctx) + if player.filters.has_filter(filter_tag="channelmix"): player.filters.remove_filter(filter_tag="channelmix") await player.add_filter(voicelink.ChannelMix(tag="channelmix", left_to_left=left_to_left, right_to_right=right_to_right, left_to_right=left_to_right, right_to_left=right_to_left)) - await interaction.response.send_message(player.get_msg('channelmix').format(left_to_left, right_to_right, left_to_right, right_to_left)) + await ctx.send(player.get_msg('channelmix').format(left_to_left, right_to_right, left_to_right, right_to_left)) - @app_commands.command( - name = "nightcore", - description= "Add nightcore filter into your player." - ) - @app_commands.guild_only() - async def nightcore(self, interaction: discord.Interaction) -> None: - player = await check_access(interaction) + @commands.hybrid_command(name="nightcore", aliases=get_aliases("nightcore")) + @commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild) + async def nightcore(self, ctx: commands.Context) -> None: + "Add nightcore filter into your player." + player = await check_access(ctx) - if interaction.user not in player.channel.members: - if not interaction.user.guild_permissions.manage_guild: - return await interaction.response.send_message(player.get_msg('notInChannel').format(interaction.user.mention, player.channel.mention), ephemeral=True) - await player.add_filter(voicelink.Timescale.nightcore()) - await interaction.response.send_message(player.get_msg('nightcore')) + await ctx.send(player.get_msg('nightcore')) - @app_commands.command( - name = "8d", - description= "Add 8D filter into your player." - ) - @app_commands.guild_only() - async def eightD(self, interaction: discord.Interaction) -> None: - player = await check_access(interaction) + @commands.hybrid_command(name="8d", aliases=get_aliases("8d")) + @commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild) + async def eightD(self, ctx: commands.Context) -> None: + "Add 8D filter into your player." + player = await check_access(ctx) - if interaction.user not in player.channel.members: - if not interaction.user.guild_permissions.manage_guild: - return await interaction.response.send_message(player.get_msg('notInChannel').format(interaction.user.mention, player.channel.mention), ephemeral=True) - await player.add_filter(voicelink.Rotation.nightD()) - await interaction.response.send_message(player.get_msg('8d')) + await ctx.send(player.get_msg('8d')) + + @commands.hybrid_command(name="vaporwave", aliases=get_aliases("vaporwave")) + @commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild) + async def vaporwave(self, ctx: commands.Context) -> None: + "Add vaporwave filter into your player." + player = await check_access(ctx) - @app_commands.command( - name = "vaporwave", - description= "Add vaporwave filter into your player." - ) - @app_commands.guild_only() - async def vaporwave(self, interaction: discord.Interaction) -> None: - player = await check_access(interaction) - - if interaction.user not in player.channel.members: - if not interaction.user.guild_permissions.manage_guild: - return await interaction.response.send_message(player.get_msg('notInChannel').format(interaction.user.mention, player.channel.mention), ephemeral=True) - await player.add_filter(voicelink.Timescale.vaporwave()) - await interaction.response.send_message(player.get_msg('vaporwave')) + await ctx.send(player.get_msg('vaporwave')) - @app_commands.command( - name = "cleareffect", - description= "Clear all or specific sound effects." - ) - @app_commands.describe( - effect = "Remove a specific sound effects." - ) - @app_commands.guild_only() + @commands.hybrid_command(name="cleareffect", aliases=get_aliases("cleareffect")) + @app_commands.describe(effect="Remove a specific sound effects.") @app_commands.autocomplete(effect=effect_autocomplete) - async def cleareffect(self, interaction: discord.Interaction, effect: str = None) -> None: - player: voicelink.Player = interaction.guild.voice_client - if not player: - return await interaction.response.send_message(get_lang(interaction.guild_id, "noPlayer"), ephemeral=True) + @commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild) + async def cleareffect(self, ctx: commands.Context, effect: str = None) -> None: + "Clear all or specific sound effects." + player = await check_access(ctx) - if interaction.user not in player.channel.members: - if not interaction.user.guild_permissions.manage_guild: - return await interaction.response.send_message(player.get_msg('notInChannel').format(interaction.user.mention, player.channel.mention), ephemeral=True) if effect: await player.remove_filter(effect) else: await player.reset_filter() - await interaction.response.send_message(player.get_msg('cleareffect')) + + await ctx.send(player.get_msg('cleareffect')) + async def setup(bot: commands.Bot) -> None: - await bot.add_cog(Effect(bot)) \ No newline at end of file + await bot.add_cog(Effect(bot)) diff --git a/cogs/playlist.py b/cogs/playlist.py index 77b5454..0aa0d77 100644 --- a/cogs/playlist.py +++ b/cogs/playlist.py @@ -1,6 +1,5 @@ import discord import voicelink -import re from discord import app_commands from discord.ext import commands @@ -14,43 +13,49 @@ from function import ( update_inbox, get_lang, playlist_name, - embed_color + embed_color, + get_aliases, + cooldown_check ) from datetime import datetime from view import PlaylistView, InboxView -def assign_playlistId(existed:list) -> str: + +def assign_playlistId(existed: list) -> str: for i in range(200, 210): if str(i) not in existed: return str(i) -async def check_playlist_perms(userid:int, authorid:int, dId:str) -> dict: + +async def check_playlist_perms(userid: int, authorid: int, dId: str) -> dict: playlist = await get_playlist(authorid, 'playlist', dId) if not playlist or userid not in playlist['perms']['read']: return {} return playlist -async def check_playlist(interaction: discord.Interaction, name:str = None, full:bool = False, share:bool = True) -> dict: - user = await get_playlist(interaction.user.id, 'playlist') + +async def check_playlist(ctx: commands.Context, name: str = None, full: bool = False, share: bool = True) -> dict: + user = await get_playlist(ctx.author.id, 'playlist') if not user: return None - await interaction.response.defer() + await ctx.defer() if full: return user if not name: return {'playlist': user['200'], 'position': 1, 'id': "200"} - + for index, data in enumerate(user, start=1): playlist = user[data] if playlist['name'].lower() == name: if playlist['type'] == 'share' and share: - playlist = await check_playlist_perms(interaction.user.id, playlist['user'], playlist['referId']) - if not playlist or interaction.user.id not in playlist['perms']['read']: + playlist = await check_playlist_perms(ctx.author.id, playlist['user'], playlist['referId']) + if not playlist or ctx.author.id not in playlist['perms']['read']: return {'playlist': None, 'position': index, 'id': data} return {'playlist': playlist, 'position': index, 'id': data} return {'playlist': None, 'position': None, 'id': None} + async def search_playlist(url: str, requester: discord.Member, timeNeed=True): try: tracks = await voicelink.NodePool.get_node().get_tracks(url, requester=requester) @@ -63,92 +68,98 @@ async def search_playlist(url: str, requester: discord.Member, timeNeed=True): return None return tracks | ({'time': ctime(time)} if timeNeed else {}) -async def connect_channel(interaction: discord.Interaction, channel: discord.VoiceChannel = None) -> voicelink.Player: + +async def connect_channel(ctx: commands.Context, channel: discord.VoiceChannel = None) -> voicelink.Player: try: - channel = channel or interaction.user.voice.channel + channel = channel or ctx.author.voice.channel except: - raise voicelink.VoicelinkException(get_lang(interaction.guild_id, 'noChannel')) + raise voicelink.VoicelinkException( + get_lang(ctx.guild.id, 'noChannel')) - check = channel.permissions_for(interaction.guild.me) + check = channel.permissions_for(ctx.guild.me) if check.connect == False or check.speak == False: - raise voicelink.VoicelinkException(get_lang(interaction.guild_id, 'noPermission')) + raise voicelink.VoicelinkException( + get_lang(ctx.guild.id, 'noPermission')) - player: voicelink.Player = await channel.connect(cls=voicelink.Player(interaction.client, channel, interaction)) + player: voicelink.Player = await channel.connect(cls=voicelink.Player(ctx.box, channel, ctx)) return player -class Playlist(commands.GroupCog, name="playlist"): + +class Playlists(commands.Cog, name="playlist"): def __init__(self, bot: commands.Bot) -> None: self.bot = bot self.description = "This is the Vocard playlist system. You can save your favorites and use Vocard to play on any server." - - async def playlist_autocomplete(self, interaction: discord.Interaction, current: str) -> list: - playlists = playlist_name.get(str(interaction.user.id), None) - if not playlists: - playlists_raw = await get_playlist(interaction.user.id, 'playlist') - playlists = playlist_name[str(interaction.user.id)] = [value['name'] for value in playlists_raw.values()] if playlists_raw else [] - if current: - return [ app_commands.Choice(name=p, value=p) for p in playlists if current in p ] - return [ app_commands.Choice(name=p, value=p) for p in playlists ] - @app_commands.command( - name = "play", - description = "Play all songs from your favorite playlist." - ) + async def playlist_autocomplete(self, ctx: commands.Context, current: str) -> list: + playlists = playlist_name.get(str(ctx.author.id), None) + if not playlists: + playlists_raw = await get_playlist(ctx.author.id, 'playlist') + playlists = playlist_name[str(ctx.author.id)] = [ + value['name'] for value in playlists_raw.values()] if playlists_raw else [] + if current: + return [app_commands.Choice(name=p, value=p) for p in playlists if current in p] + return [app_commands.Choice(name=p, value=p) for p in playlists] + + @commands.hybrid_group(name="playlist", invoke_without_command=True) + async def playlist(self, ctx: commands.Context): + return + + @playlist.command(name="play", aliases=get_aliases("play")) @app_commands.describe( - name = "Input the name of your custom playlist", - value = "Play the specific track from your custom playlist." + name="Input the name of your custom playlist", + value="Play the specific track from your custom playlist." ) @app_commands.autocomplete(name=playlist_autocomplete) - @app_commands.guild_only() - async def play(self, interaction: discord.Interaction, name: str = None, value: int = None) -> None: - result = await check_playlist(interaction, name.lower() if name else None) + @commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild) + async def play(self, ctx: commands.Context, name: str = None, value: int = None) -> None: + "Play all songs from your favorite playlist." + result = await check_playlist(ctx, name.lower() if name else None) if not result: - return await create_account(interaction) + return await create_account(ctx) if not result['playlist']: - return await interaction.followup.send(get_lang(interaction.guild_id, 'playlistNotFound').format(name), ephemeral=True) - rank, max_p, max_t = await checkroles(interaction.user.id) + return await ctx.send(get_lang(ctx.guild.id, 'playlistNotFound').format(name), ephemeral=True) + rank, max_p, max_t = await checkroles(ctx.author.id) if result['position'] > max_p: - return await interaction.followup.send(get_lang(interaction.guild_id, 'playlistNotAccess'), ephemeral=True) + return await ctx.send(get_lang(ctx.guild.id, 'playlistNotAccess'), ephemeral=True) - player: voicelink.Player = interaction.guild.voice_client + player: voicelink.Player = ctx.guild.voice_client if not player: - player = await connect_channel(interaction) + player = await connect_channel(ctx) if result['playlist']['type'] == 'link': - tracks = await search_playlist(result['playlist']['uri'], interaction.user, timeNeed=False) + tracks = await search_playlist(result['playlist']['uri'], ctx.author, timeNeed=False) else: if not result['playlist']['tracks']: - return await interaction.followup.send(get_lang(interaction.guild_id, 'playlistNoTrack').format(result['playlist']['name']), ephemeral=True) + return await ctx.send(get_lang(ctx.guild.id, 'playlistNoTrack').format(result['playlist']['name']), ephemeral=True) playtrack = [] for track in result['playlist']['tracks'][:max_t]: track['info']['length'] *= 1000 - playtrack.append(voicelink.Track(track_id=track['id'], info=track['info'], requester=interaction.user, spotify= True if extract(track['info']['uri']).domain == 'spotify' else False)) + playtrack.append(voicelink.Track(track_id=track['id'], info=track['info'], requester=ctx.author, spotify=True if extract( + track['info']['uri']).domain == 'spotify' else False)) tracks = {"name": result['playlist']['name'], "tracks": playtrack} - + if not tracks: - return await interaction.followup.send(get_lang(interaction.guild_id, 'playlistNoTrack').format(result['playlist']['name']), ephemeral=True) - + return await ctx.send(get_lang(ctx.guild.id, 'playlistNoTrack').format(result['playlist']['name']), ephemeral=True) + if value and 0 < value <= (len(tracks['tracks'])): tracks['tracks'] = [tracks['tracks'][value - 1]] for track in tracks['tracks']: await player.queue.put(track) - - await interaction.followup.send(get_lang(interaction.guild_id, 'playlistPlay').format(result['playlist']['name'], len(tracks['tracks'][:max_t]))) - + + await ctx.send(get_lang(ctx.guild.id, 'playlistPlay').format(result['playlist']['name'], len(tracks['tracks'][:max_t]))) + if not player.is_playing: await player.do_next() - @app_commands.command( - name = "view", - description = "List all your playlist and all songs in your favourite playlist." - ) - @app_commands.guild_only() - async def view(self, interaction: discord.Interaction) -> None: - user = await check_playlist(interaction, full=True) + @playlist.command(name="view", aliases=get_aliases("view")) + @commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild) + async def view(self, ctx: commands.Context) -> None: + "List all your playlist and all songs in your favourite playlist." + user = await check_playlist(ctx, full=True) if not user: - return await create_account(interaction) - rank, max_p, max_t = await checkroles(interaction.user.id) + return await create_account(ctx) + rank, max_p, max_t = await checkroles(ctx.author.id) results = [] for index, data in enumerate(user, start=1): @@ -156,275 +167,267 @@ class Playlist(commands.GroupCog, name="playlist"): time = 0 try: if playlist['type'] == 'link': - tracks = await search_playlist(playlist['uri'], requester=interaction.user) - results.append({'emoji':('🔒' if max_p < index else '🔗'), 'id': data, 'time': tracks['time'], 'name':playlist['name'], 'tracks': tracks['tracks'], 'perms': playlist['perms'], 'type': playlist['type']}) + tracks = await search_playlist(playlist['uri'], requester=ctx.author) + results.append({'emoji': ('🔒' if max_p < index else '🔗'), 'id': data, 'time': tracks['time'], 'name': playlist[ + 'name'], 'tracks': tracks['tracks'], 'perms': playlist['perms'], 'type': playlist['type']}) else: if share := playlist['type'] == 'share': - playlist = await check_playlist_perms(interaction.user.id, playlist['user'], playlist['referId']) + playlist = await check_playlist_perms(ctx.author.id, playlist['user'], playlist['referId']) if not playlist: - await update_playlist(interaction.user.id, {f"playlist.{data}":1}, mode=False) + await update_playlist(ctx.author.id, {f"playlist.{data}": 1}, mode=False) continue if playlist['type'] == 'link': - tracks = await search_playlist(playlist['uri'], requester=interaction.user) - results.append({'emoji':('🔒' if max_p < index else '🤝'), 'id': data, 'time': tracks['time'], 'name':user[data]['name'], 'tracks': tracks['tracks'], 'perms': playlist['perms'], 'owner': user[data]['user'], 'type': 'share'}) + tracks = await search_playlist(playlist['uri'], requester=ctx.author) + results.append({'emoji': ('🔒' if max_p < index else '🤝'), 'id': data, 'time': tracks['time'], 'name': user[data][ + 'name'], 'tracks': tracks['tracks'], 'perms': playlist['perms'], 'owner': user[data]['user'], 'type': 'share'}) continue for track in playlist['tracks']: time += track['info']['length'] * 1000 - results.append({'emoji':('🔒' if max_p < index else ('🤝' if share else '❤️')), 'id': data, 'time': ctime(time), 'name':user[data]['name'], 'tracks': playlist['tracks'], 'perms': playlist['perms'], 'owner': user[data].get('user', None), 'type': user[data]['type']}) + results.append({'emoji': ('🔒' if max_p < index else ('🤝' if share else '❤️')), 'id': data, 'time': ctime( + time), 'name': user[data]['name'], 'tracks': playlist['tracks'], 'perms': playlist['perms'], 'owner': user[data].get('user', None), 'type': user[data]['type']}) except: - results.append({'emoji': '⛔', 'id': data, 'time': '00:00', 'name': 'Error', 'tracks': [], 'type': 'error'}) + results.append({'emoji': '⛔', 'id': data, 'time': '00:00', + 'name': 'Error', 'tracks': [], 'type': 'error'}) - embed=discord.Embed(title=get_lang(interaction.guild_id, 'playlistViewTitle').format(interaction.user.name), - description='```%0s %4s %10s %10s %10s\n' % tuple(get_lang(interaction.guild_id, 'playlistViewHeaders')) + '\n'.join('%0s %3s. %10s %10s %10s'% (info['emoji'], info['id'], f"[{info['time']}]", info['name'], len(info['tracks'])) for info in results) + '```', - color=embed_color) - embed.add_field(name=get_lang(interaction.guild_id, 'playlistMaxP'), value=f"➥ {len(user)}/{max_p}", inline=True) - embed.add_field(name=get_lang(interaction.guild_id, 'playlistMaxT'), value=f"➥ {max_t}", inline=True) - embed.set_footer(text=get_lang(interaction.guild_id, 'playlistFooter')) - - view = PlaylistView(embed, results, interaction.user) - await interaction.followup.send(embed=embed, view=view, ephemeral=True) - view.response = await interaction.original_response() + embed = discord.Embed(title=get_lang(ctx.guild.id, 'playlistViewTitle').format(ctx.author.name), + description='```%0s %4s %10s %10s %10s\n' % tuple(get_lang(ctx.guild.id, 'playlistViewHeaders')) + '\n'.join( + '%0s %3s. %10s %10s %10s' % (info['emoji'], info['id'], f"[{info['time']}]", info['name'], len(info['tracks'])) for info in results) + '```', + color=embed_color) + embed.add_field(name=get_lang(ctx.guild.id, 'playlistMaxP'), + value=f"➥ {len(user)}/{max_p}", inline=True) + embed.add_field(name=get_lang(ctx.guild.id, + 'playlistMaxT'), value=f"➥ {max_t}", inline=True) + embed.set_footer(text=get_lang(ctx.guild.id, 'playlistFooter')) - @app_commands.command( - name = "create", - description = "Create your custom playlist." - ) + view = PlaylistView(embed, results, ctx.author) + messsage = await ctx.send(embed=embed, view=view, ephemeral=True) + view.response = messsage + + @playlist.command(name="create", aliases=get_aliases("create")) @app_commands.describe( - name = "Give a name to your playlist.", - link = "Provide a playlist link if you are creating link playlist." + name="Give a name to your playlist.", + link="Provide a playlist link if you are creating link playlist." ) - @app_commands.guild_only() - async def create(self, interaction: discord.Interaction, name: str, link: str = None): + @commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild) + async def create(self, ctx: commands.Context, name: str, link: str = None): + "Create your custom playlist." if len(name) > 10: - return await interaction.response.send_message(get_lang(interaction.guild_id, 'playlistOverText'), ephemeral=True) + return await ctx.response.send_message(get_lang(ctx.guild.id, 'playlistOverText'), ephemeral=True) isLinkType = True if link else False - rank, max_p, max_t = await checkroles(interaction.user.id) + rank, max_p, max_t = await checkroles(ctx.author.id) if isLinkType and rank != "Gold": - return await interaction.response.send_message(get_lang(interaction.guild_id, 'playlistCreateError'), ephemeral=True) - user = await check_playlist(interaction, full=True) + return await ctx.response.send_message(get_lang(ctx.guild.id, 'playlistCreateError'), ephemeral=True) + user = await check_playlist(ctx, full=True) if not user: - return await create_account(interaction) + return await create_account(ctx) if len(user) >= max_p: - return await interaction.followup.send(get_lang(interaction.guild_id, 'overPlaylistCreation').format(max_p), ephemeral=True) + return await ctx.send(get_lang(ctx.guild.id, 'overPlaylistCreation').format(max_p), ephemeral=True) for data in user: if user[data]['name'].lower() == name.lower(): - return await interaction.followup.send(get_lang(interaction.guild_id, 'playlistExists'), ephemeral=True) + return await ctx.send(get_lang(ctx.guild.id, 'playlistExists'), ephemeral=True) if isLinkType: - tracks = await voicelink.NodePool.get_node().get_tracks(link, requester=interaction.user) + tracks = await voicelink.NodePool.get_node().get_tracks(link, requester=ctx.author) if not isinstance(tracks, voicelink.Playlist): - return await interaction.followup.send(get_lang(interaction.guild_id, 'playlistNotInvaildUrl'), ephemeral=True) - - playlist_name.pop(str(interaction.user.id), None) - data = {'uri': link, 'perms': {'read': []}, 'name': name, 'type': 'link'} if isLinkType else {'tracks': [], 'perms': {'read': [], 'write': [], 'remove': []}, 'name': name, 'type': 'playlist'} - await update_playlist(interaction.user.id, { f"playlist.{assign_playlistId([data for data in user])}": data }) - await interaction.followup.send(get_lang(interaction.guild_id, 'playlistCreated')) - - @app_commands.command( - name = "delete", - description = "Delete your custom playlist." - ) - @app_commands.describe( - name = "The name of the playlist." - ) - @app_commands.guild_only() + return await ctx.send(get_lang(ctx.guild.id, 'playlistNotInvaildUrl'), ephemeral=True) + + playlist_name.pop(str(ctx.author.id), None) + data = {'uri': link, 'perms': {'read': []}, 'name': name, 'type': 'link'} if isLinkType else { + 'tracks': [], 'perms': {'read': [], 'write': [], 'remove': []}, 'name': name, 'type': 'playlist'} + await update_playlist(ctx.author.id, {f"playlist.{assign_playlistId([data for data in user])}": data}) + await ctx.send(get_lang(ctx.guild.id, 'playlistCreated')) + + @playlist.command(name="delete", aliases=get_aliases("delete")) + @app_commands.describe(name="The name of the playlist.") @app_commands.autocomplete(name=playlist_autocomplete) - async def delete(self, interaction: discord.Interaction, name: str): - result = await check_playlist(interaction, name.lower(), share=False) + @commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild) + async def delete(self, ctx: commands.Context, name: str): + "Delete your custom playlist." + result = await check_playlist(ctx, name.lower(), share=False) if not result: - return await create_account(interaction) + return await create_account(ctx) if not result['playlist']: - return await interaction.followup.send(get_lang(interaction.guild_id, 'playlistNotFound').format(name), ephemeral=True) + return await ctx.send(get_lang(ctx.guild.id, 'playlistNotFound').format(name), ephemeral=True) if result['id'] == "200": - return await interaction.followup.send(get_lang(interaction.guild_id, 'playlistDeleteError'), ephemeral=True) + return await ctx.send(get_lang(ctx.guild.id, 'playlistDeleteError'), ephemeral=True) if result['playlist']['type'] == 'share': - await update_playlist(result['playlist']['user'], {f"playlist.{result['playlist']['referId']}.perms.read":interaction.user.id}, pull=True, mode=False) - - playlist_name.pop(str(interaction.user.id), None) - await update_playlist(interaction.user.id, {f"playlist.{result['id']}":1}, mode=False) - return await interaction.followup.send(get_lang(interaction.guild_id, 'playlistRemove').format(result['playlist']['name'])) + await update_playlist(result['playlist']['user'], {f"playlist.{result['playlist']['referId']}.perms.read": ctx.author.id}, pull=True, mode=False) - @app_commands.command( - name = "share", - description = "Share your custom playlist with your friends." - ) + playlist_name.pop(str(ctx.author.id), None) + await update_playlist(ctx.author.id, {f"playlist.{result['id']}": 1}, mode=False) + return await ctx.send(get_lang(ctx.guild.id, 'playlistRemove').format(result['playlist']['name'])) + + @playlist.command(name="share", aliases=get_aliases("share")) @app_commands.describe( - member = "The user id of your friend.", - name = "The name of the playlist that you want to share." + member="The user id of your friend.", + name="The name of the playlist that you want to share." ) - @app_commands.guild_only() @app_commands.autocomplete(name=playlist_autocomplete) - async def share(self, interaction: discord.Interaction, member: discord.Member, name: str): - if member.id == interaction.user.id: - return await interaction.response.send_message(get_lang(interaction.guild_id, 'playlistSendErrorPlayer'), ephemeral=True) + @commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild) + async def share(self, ctx: commands.Context, member: discord.Member, name: str): + "Share your custom playlist with your friends." + if member.id == ctx.author.id: + return await ctx.response.send_message(get_lang(ctx.guild.id, 'playlistSendErrorPlayer'), ephemeral=True) if member.bot: - return await interaction.response.send_message(get_lang(interaction.guild_id, 'playlistSendErrorBot'), ephemeral=True) - result = await check_playlist(interaction, name.lower(), share=False) + return await ctx.response.send_message(get_lang(ctx.guild.id, 'playlistSendErrorBot'), ephemeral=True) + result = await check_playlist(ctx, name.lower(), share=False) if not result: - return await create_account(interaction) + return await create_account(ctx) if not result['playlist']: - return await interaction.followup.send(get_lang(interaction.guild_id, 'playlistNotFound').format(name), ephemeral=True) - + return await ctx.send(get_lang(ctx.guild.id, 'playlistNotFound').format(name), ephemeral=True) + if result['playlist']['type'] == 'share': - return await interaction.followup.send(get_lang(interaction.guild_id, 'playlistBelongs').format(result['playlist']['user']), ephemeral=True) + return await ctx.send(get_lang(ctx.guild.id, 'playlistBelongs').format(result['playlist']['user']), ephemeral=True) if member.id in result['playlist']['perms']['read']: - return await interaction.followup.send(get_lang(interaction.guild_id, 'playlistShare').format(member), ephemeral=True) + return await ctx.send(get_lang(ctx.guild.id, 'playlistShare').format(member), ephemeral=True) receiver = await get_playlist(member.id) if not receiver: - return await interaction.followup.send(get_lang(interaction.guild_id, 'noPlaylistAcc').format(member)) + return await ctx.send(get_lang(ctx.guild.id, 'noPlaylistAcc').format(member)) for mail in receiver['inbox']: - if mail['sender'] == interaction.user.id and mail['referId'] == result['id']: - return await interaction.followup.send(get_lang(interaction.guild_id, 'playlistSent'), ephemeral=True) + if mail['sender'] == ctx.author.id and mail['referId'] == result['id']: + return await ctx.send(get_lang(ctx.guild.id, 'playlistSent'), ephemeral=True) if len(receiver['inbox']) >= 10: - return await interaction.followup.send(get_lang(interaction.guild_id, 'inboxFull').format(member), ephemeral=True) + return await ctx.send(get_lang(ctx.guild.id, 'inboxFull').format(member), ephemeral=True) - await update_inbox(member.id, {'sender': interaction.user.id, 'referId': result['id'], 'time': datetime.now(),'title': f'Playlist invitation from {interaction.user}', 'description': f"You are invited to use this playlist.\nPlaylist Name: {result['playlist']['name']}\nPlaylist type: {result['playlist']['type']}", 'type': 'invite'}) - return await interaction.followup.send(get_lang(interaction.guild_id, 'invitationSent').format(member)) + await update_inbox(member.id, {'sender': ctx.author.id, 'referId': result['id'], 'time': datetime.now(), 'title': f'Playlist invitation from {ctx.author}', 'description': f"You are invited to use this playlist.\nPlaylist Name: {result['playlist']['name']}\nPlaylist type: {result['playlist']['type']}", 'type': 'invite'}) + return await ctx.send(get_lang(ctx.guild.id, 'invitationSent').format(member)) - @app_commands.command( - name = "rename", - description = "Rename your custom playlist." - ) + @playlist.command(name="rename", aliases=get_aliases("rename")) @app_commands.describe( - name = "The name of your playlist.", - newname = "The new name of your playlist." + name="The name of your playlist.", + newname="The new name of your playlist." ) - @app_commands.guild_only() @app_commands.autocomplete(name=playlist_autocomplete) - async def rename(self, interaction: discord.Interaction, name: str, newname: str) -> None: + @commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild) + async def rename(self, ctx: commands.Context, name: str, newname: str) -> None: + "Rename your custom playlist." if len(newname) > 10: - return await interaction.response.send_message(get_lang(interaction.guild_id, 'playlistOverText'), ephemeral=True) + return await ctx.response.send_message(get_lang(ctx.guild.id, 'playlistOverText'), ephemeral=True) if name.lower() == newname.lower(): - return await interaction.response.send_message(get_lang(interaction.guild_id, 'playlistSameName'), ephemeral=True) - user = await check_playlist(interaction, full=True) + return await ctx.response.send_message(get_lang(ctx.guild.id, 'playlistSameName'), ephemeral=True) + user = await check_playlist(ctx, full=True) if not user: - return await create_account(interaction) + return await create_account(ctx) found, id = False, 0 for data in user: if user[data]['name'].lower() == name.lower(): found, id = True, data if user[data]['name'].lower() == newname.lower(): - return await interaction.followup.send(get_lang(interaction.guild_id, 'playlistExists'), ephemeral=True) + return await ctx.send(get_lang(ctx.guild.id, 'playlistExists'), ephemeral=True) if not found: - return await interaction.followup.send(get_lang(interaction.guild_id, 'playlistNotFound').format(name), ephemeral=True) - - playlist_name.pop(str(interaction.user.id), None) - await update_playlist(interaction.user.id, {f'playlist.{id}.name': newname}) - await interaction.followup.send(get_lang(interaction.guild_id, 'playlistRenamed').format(name, newname)) + return await ctx.send(get_lang(ctx.guild.id, 'playlistNotFound').format(name), ephemeral=True) - @app_commands.command( - name = "inbox", - description = "Show your playlist invitation." - ) - @app_commands.guild_only() - async def inbox(self, interaction: discord.Interaction) -> None: - user = await get_playlist(interaction.user.id) + playlist_name.pop(str(ctx.author.id), None) + await update_playlist(ctx.author.id, {f'playlist.{id}.name': newname}) + await ctx.send(get_lang(ctx.guild.id, 'playlistRenamed').format(name, newname)) + + @playlist.command(name="inbox", aliases=get_aliases("inbox")) + @commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild) + async def inbox(self, ctx: commands.Context) -> None: + "Show your playlist invitation." + user = await get_playlist(ctx.author.id) if user is None: - return await create_account(interaction) + return await create_account(ctx) if not user['inbox']: - return await interaction.response.send_message(get_lang(interaction.guild_id, 'inboxNoMsg'), ephemeral=True) - + return await ctx.response.send_message(get_lang(ctx.guild.id, 'inboxNoMsg'), ephemeral=True) + inbox = user['inbox'].copy() - view = InboxView(interaction.user.name, user['inbox']) - await interaction.response.send_message(embed=view.build_embed(), view=view, ephemeral=True) - view.response = await interaction.original_response() + view = InboxView(ctx.author.name, user['inbox']) + message = await ctx.response.send_message(embed=view.build_embed(), view=view, ephemeral=True) + view.response = message await view.wait() if inbox == user['inbox']: - return + return updateData, dId = {}, {dId for dId in user["playlist"]} for data in view.newplaylist[:(5 - len(user['playlist']))]: addId = assign_playlistId(dId) - await update_playlist(data['sender'], {f"playlist.{data['referId']}.perms.read": interaction.user.id}, push=True) - updateData[f'playlist.{addId}'] = {'user':data['sender'], 'referId': data['referId'], 'name': f"Share{data['time'].strftime('%M%S')}", 'type': 'share'} + await update_playlist(data['sender'], {f"playlist.{data['referId']}.perms.read": ctx.author.id}, push=True) + updateData[f'playlist.{addId}'] = {'user': data['sender'], 'referId': data['referId'], + 'name': f"Share{data['time'].strftime('%M%S')}", 'type': 'share'} dId.add(addId) - playlist_name.pop(str(interaction.user.id), None) - await update_playlist(interaction.user.id, updateData | {'inbox':view.inbox}) + playlist_name.pop(str(ctx.author.id), None) + await update_playlist(ctx.author.id, updateData | {'inbox': view.inbox}) - @app_commands.command( - name = "add", - description = "Add tracks in to your custom playlist." - ) + @playlist.command(name="add", aliases=get_aliases("add")) + @commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild) @app_commands.describe( - name = "The name of the playlist.", - query = "Input a query or a searchable link." + name="The name of the playlist.", + query="Input a query or a searchable link." ) - @app_commands.guild_only() @app_commands.autocomplete(name=playlist_autocomplete) - async def add(self, interaction: discord.Interaction, name: str, query: str) -> None: - result = await check_playlist(interaction, name.lower(), share=False) + async def add(self, ctx: commands.Context, name: str, query: str) -> None: + "Add tracks in to your custom playlist." + result = await check_playlist(ctx, name.lower(), share=False) if not result: - return await create_account(interaction) + return await create_account(ctx) if not result['playlist']: - return await interaction.followup.send(get_lang(interaction.guild_id, 'playlistNotFound').format(name), ephemeral=True) + return await ctx.send(get_lang(ctx.guild.id, 'playlistNotFound').format(name), ephemeral=True) if result['playlist']['type'] in ['share', 'link']: - return await interaction.followup.send(get_lang(interaction.guild_id, 'playlistNotAllow'), ephemeral=True) - rank, max_p, max_t = await checkroles(interaction.user.id) + return await ctx.send(get_lang(ctx.guild.id, 'playlistNotAllow'), ephemeral=True) + rank, max_p, max_t = await checkroles(ctx.author.id) if len(result['playlist']['tracks']) >= max_t: - return await interaction.followup.send(get_lang(interaction.guild_id, 'playlistLimitTrack').format(max_t), ephemeral=True) + return await ctx.send(get_lang(ctx.guild.id, 'playlistLimitTrack').format(max_t), ephemeral=True) - results = await voicelink.NodePool.get_node().get_tracks(query, requester = interaction.user) + results = await voicelink.NodePool.get_node().get_tracks(query, requester=ctx.author) if not results: - return await interaction.followup.send(get_lang(interaction.guild_id, 'noTrackFound')) + return await ctx.send(get_lang(ctx.guild.id, 'noTrackFound')) if isinstance(results, voicelink.Playlist): - return await interaction.followup.send(get_lang(interaction.guild_id, 'playlistPlaylistLink'), ephemeral=True) + return await ctx.send(get_lang(ctx.guild.id, 'playlistPlaylistLink'), ephemeral=True) if results[0].is_stream: - return await interaction.followup.send(get_lang(interaction.guild_id, 'playlistStream'), ephemeral=True) + return await ctx.send(get_lang(ctx.guild.id, 'playlistStream'), ephemeral=True) - await update_playlist(interaction.user.id, {f'playlist.{result["id"]}.tracks':{'id': results[0].track_id, 'info': {'identifier': results[0].identifier, - 'author': results[0].author, - 'length': results[0].length / 1000, - 'title': results[0].title, - 'uri': results[0].uri}}}, push=True) - await interaction.followup.send(get_lang(interaction.guild_id, 'playlistAdded').format(results[0].title, interaction.user, result['playlist']['name'])) - - @app_commands.command( - name = "remove", - description = "Remove song from your favorite playlist." - ) + await update_playlist(ctx.author.id, {f'playlist.{result["id"]}.tracks': {'id': results[0].track_id, 'info': {'identifier': results[0].identifier, + 'author': results[0].author, + 'length': results[0].length / 1000, + 'title': results[0].title, + 'uri': results[0].uri}}}, push=True) + await ctx.send(get_lang(ctx.guild.id, 'playlistAdded').format(results[0].title, ctx.author, result['playlist']['name'])) + + @playlist.command(name="remove", aliases=get_aliases("remove")) + @commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild) @app_commands.describe( - name = "The name of the playlist.", - position = "Input a position from the playlist to be removed." + name="The name of the playlist.", + position="Input a position from the playlist to be removed." ) - @app_commands.guild_only() @app_commands.autocomplete(name=playlist_autocomplete) - async def remove(self, interaction: discord.Interaction, name: str, position: int): - result = await check_playlist(interaction, name.lower(), share=False) + async def remove(self, ctx: commands.Context, name: str, position: int): + "Remove song from your favorite playlist." + result = await check_playlist(ctx, name.lower(), share=False) if not result: - return await create_account(interaction) + return await create_account(ctx) if not result['playlist']: - return await interaction.followup.send(get_lang(interaction.guild_id, 'playlistNotFound').format(name), ephemeral=True) + return await ctx.send(get_lang(ctx.guild.id, 'playlistNotFound').format(name), ephemeral=True) if result['playlist']['type'] in ['link', 'share']: - return await interaction.followup.send(get_lang(interaction.guild_id, 'playlistNotAllow'), ephemeral=True) + return await ctx.send(get_lang(ctx.guild.id, 'playlistNotAllow'), ephemeral=True) if not 0 < position <= len(result['playlist']['tracks']): - return await interaction.followup.send(get_lang(interaction.guild_id, 'playlistPositionNotFound').format(position, name)) - - await update_playlist(interaction.user.id, {f'playlist.{result["id"]}.tracks': result['playlist']['tracks'][position - 1]}, pull=True, mode=False) - await interaction.followup.send(get_lang(interaction.guild_id, 'playlistRemoved').format(result['playlist']['tracks'][position - 1]['info']['title'], interaction.user, name)) - - @app_commands.command( - name = "clear", - description = "Remove all songs from your favorite playlist." - ) - @app_commands.guild_only() + return await ctx.send(get_lang(ctx.guild.id, 'playlistPositionNotFound').format(position, name)) + + await update_playlist(ctx.author.id, {f'playlist.{result["id"]}.tracks': result['playlist']['tracks'][position - 1]}, pull=True, mode=False) + await ctx.send(get_lang(ctx.guild.id, 'playlistRemoved').format(result['playlist']['tracks'][position - 1]['info']['title'], ctx.author, name)) + + @playlist.command(name="clear", aliases=get_aliases("clear")) + @commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild) @app_commands.autocomplete(name=playlist_autocomplete) - async def clear(self, interaction: discord.Interaction, name: str) -> None: - result = await check_playlist(interaction, name.lower(), share=False) + async def clear(self, ctx: commands.Context, name: str) -> None: + "Remove all songs from your favorite playlist." + result = await check_playlist(ctx, name.lower(), share=False) if not result: - return await create_account(interaction) + return await create_account(ctx) if not result['playlist']: - return await interaction.followup.send(get_lang(interaction.guild_id, 'playlistNotFound').format(name), ephemeral=True) - + return await ctx.send(get_lang(ctx.guild.id, 'playlistNotFound').format(name), ephemeral=True) + if result['playlist']['type'] in ['link', 'share']: - return await interaction.followup.send(get_lang(interaction.guild_id, 'playlistNotAllow'), ephemeral=True) - - await update_playlist(interaction.user.id, {f'playlist.{result["id"]}.tracks': []}) - await interaction.followup.send(get_lang(interaction.guild_id, 'playlistClear').format(name)) + return await ctx.send(get_lang(ctx.guild.id, 'playlistNotAllow'), ephemeral=True) + + await update_playlist(ctx.author.id, {f'playlist.{result["id"]}.tracks': []}) + await ctx.send(get_lang(ctx.guild.id, 'playlistClear').format(name)) + async def setup(bot: commands.Bot) -> None: - await bot.add_cog(Playlist(bot)) \ No newline at end of file + await bot.add_cog(Playlists(bot)) diff --git a/function.py b/function.py index 73e7e5c..c71281d 100644 --- a/function.py +++ b/function.py @@ -37,6 +37,7 @@ lang_guilds = {} #Cache guild language local_langs = {} #Stores all the localization languages in ./local_langs playlist_name = {} #Cache the user's playlist name +bot_prefix = "?" #The default bot prefix #----------------- Nodes ----------------- nodes = {} diff --git a/main.py b/main.py index 2c3b95a..520cf5a 100644 --- a/main.py +++ b/main.py @@ -1,21 +1,15 @@ import discord import sys, os, traceback, aiohttp import update +import function -from function import ( - langs_setup, - settings_setup, - local_langs, - get_lang, - invite_link, - error_log -) from discord.ext import commands from dotenv import load_dotenv from datetime import datetime from voicelink import VoicelinkException load_dotenv() +function.settings_setup() class Translator(discord.app_commands.Translator): async def load(self): @@ -31,12 +25,17 @@ class Translator(discord.app_commands.Translator): class Vocard(commands.Bot): - async def on_message(self, message): - pass - + async def on_message(self, message: discord.Message, /) -> None: + if message.author.bot or not message.guild: + return False + + if self.user.mentioned_in(message) and not message.mention_everyone: + await message.channel.send(f"My prefix is `{self.command_prefix}`") + + await self.process_commands(message) + async def setup_hook(self): - langs_setup() - settings_setup() + function.langs_setup() for module in os.listdir('./cogs'): if module.endswith('.py'): try: @@ -57,18 +56,50 @@ class Vocard(commands.Bot): print(f"Python Version: {sys.version}") print("------------------") + + elif isinstance(error, (commands.CommandOnCooldown, commands.MissingPermissions, commands.RangeError, commands.BadArgument)): + pass + + elif isinstance(error, commands.MissingRequiredArgument): + command = f" Correct Usage: {ctx.prefix}" + (f"{ctx.command.parent.qualified_name} " if ctx.command.parent else "") + f"{ctx.command.name} {ctx.command.signature}" + position = command.find(f"<{ctx.current_parameter.name}>") + 1 + error = f"```css\n[You are missing argument!]\n{command}\n" + " " * position + "^" * len(ctx.current_parameter.name) + "```" + + elif not issubclass(error.__class__, VoicelinkException): + error = function.get_lang(ctx.guild.id, "unknownException") + function.invite_link + if (guildId := ctx.guild.id) not in function.error_log: + function.error_log[guildId] = {} + function.error_log[guildId][round(datetime.timestamp(datetime.now()))] = str(traceback.format_exc()) + + try: + return await ctx.reply(error, ephemeral=True) + except: + pass + +class CommandCheck(discord.app_commands.CommandTree): + + async def interaction_check(self, interaction: discord.Interaction, /) -> bool: + if not interaction.guild: + await interaction.response.send_message("This command can only be used in guilds!") + return False + + return await super().interaction_check(interaction) + intents = discord.Intents.default() intents.members = True +intents.message_content = True if function.bot_prefix else False member_cache = discord.MemberCacheFlags( voice=True, joined=False ) -bot = Vocard(command_prefix="?", +bot = Vocard(command_prefix=function.bot_prefix, help_command=None, + tree_cls=CommandCheck, chunk_guilds_at_startup = False, member_cache_flags=member_cache, - activity=discord.Activity(type=discord.ActivityType.listening,name="/help"), + activity=discord.Activity(type=discord.ActivityType.listening,name="/help"), + case_insensitive = True, intents=intents) @bot.tree.error @@ -79,10 +110,10 @@ async def app_command_error(interaction: discord.Interaction, error): elif isinstance(error, (discord.app_commands.CommandOnCooldown, discord.app_commands.errors.MissingPermissions)): pass elif not issubclass(error.__class__, VoicelinkException): - error = get_lang(interaction.guild_id, "unknownException") + invite_link - if (guildId := interaction.guild_id) not in error_log: - error_log[guildId] = {} - error_log[guildId][round(datetime.timestamp(datetime.now()))] = str(traceback.format_exc()) + error = function.get_lang(interaction.guild_id, "unknownException") + function.invite_link + if (guildId := interaction.guild_id) not in function.error_log: + function.error_log[guildId] = {} + function.error_log[guildId][round(datetime.timestamp(datetime.now()))] = str(traceback.format_exc()) try: if interaction.response.is_done(): return await interaction.followup.send(error, ephemeral=True) diff --git a/settings Example.json b/settings Example.json index 94acc31..8d6b580 100644 --- a/settings Example.json +++ b/settings Example.json @@ -7,6 +7,7 @@ "identifier": "DEFAULT" } }, + "prefix": "?", "bot_access_user": [], "color_code":"0xb3b3b3", "emoji_source_raw": { @@ -20,5 +21,15 @@ "apple": "<:applemusic:994844332374884413>", "reddit": "<:reddit:996007566863773717>", "tiktok": "<:tiktok:996007689798811698>" + }, + "cooldowns": { + "connect": [2, 30], + "playlist view": [1, 30] + }, + "aliases": { + "connect": ["join"], + "leave": ["stop", "bye"], + "play": ["p"], + "view": ["v"] } } \ No newline at end of file diff --git a/view/help.py b/view/help.py index 64e0faf..31fad05 100644 --- a/view/help.py +++ b/view/help.py @@ -109,9 +109,9 @@ class HelpView(discord.ui.View): else: cog = [c for _, c in self.bot.cogs.items() if _.capitalize() == category][0] - commands = [command for command in cog.walk_app_commands()] + commands = [command for command in cog.walk_commands()] embed.description = cog.description embed.add_field(name=f"{category} Commands: [{len(commands)}]", - value="```{}```".format("".join(f"/{command.qualified_name}\n" for command in commands))) + value="```{}```".format("".join(f"/{command.qualified_name}\n" for command in commands if not command.qualified_name == cog.qualified_name))) return embed \ No newline at end of file diff --git a/voicelink/player.py b/voicelink/player.py index a614f0d..635e968 100644 --- a/voicelink/player.py +++ b/voicelink/player.py @@ -41,10 +41,10 @@ from discord import ( StageChannel, Member, Embed, - ui, - Interaction + ui ) +from discord.ext import commands from . import events from .enums import SearchType from .events import VoicelinkEvent, TrackEndEvent, TrackStartEvent @@ -72,12 +72,12 @@ class Player(VoiceProtocol): self, client: Optional[Client] = None, channel: Optional[VoiceChannel] = None, - ctx: Interaction = None, + ctx: commands.Context = None, ): self.client = client self._bot = client self.context = ctx - self.dj: Member = ctx.user + self.dj: Member = ctx.author self.channel = channel self._guild = channel.guild if channel else None @@ -335,7 +335,7 @@ class Player(VoiceProtocol): embed.set_author(name=self.get_msg("playerAuthor").format(self.channel.name), icon_url=self.client.user.avatar.url) embed.description = self.get_msg("playerDesc").format(track.title, track.uri, (track.requester.mention if track.requester else "<@605618911471468554>"), (f"<@&{self.settings['dj']}>" if self.settings.get('dj') else f"{self.dj.mention}")) embed.set_image(url=track.thumbnail if track.thumbnail else "https://cdn.discordapp.com/attachments/674788144931012638/823086668445384704/eq-dribbble.gif") - embed.set_footer(text=self.get_msg("playerFooter").format(self.queue.count, (self.get_msg("live") if track.is_stream else function.time(track.length)), self.volume, self.get_msg("playerFooter2").format(self.queue.repeat) if self.queue.repeat != "Off" else "")) + embed.set_footer(text=self.get_msg("playerFooter").format(self.queue.count, (self.get_msg("live") if track.is_stream else function.time(track.length)), self.volume, self.get_msg("playerFooter2").format(self.queue.repeat.capitalize()) if self.queue._repeat else "")) except: embed = Embed(description=self.get_msg("missingTrackInfo"), color=function.embed_color) return embed diff --git a/voicelink/queue.py b/voicelink/queue.py index 222265d..a14ea8e 100644 --- a/voicelink/queue.py +++ b/voicelink/queue.py @@ -96,9 +96,9 @@ class Queue: self._position -= index def set_repeat(self, mode:str): - if mode == 'Track': + if mode == 'track': self._repeat = 1 - elif mode == 'Queue': + elif mode == 'queue': self._repeat = 2 self._repeat_position = self._position - 1 else: diff --git a/voicelink/spotify/client.py b/voicelink/spotify/client.py index 70815b6..5f8758e 100644 --- a/voicelink/spotify/client.py +++ b/voicelink/spotify/client.py @@ -137,7 +137,6 @@ class Client: elif isArtist: return Artist(data) else: - tracks = [ Track(track["track"]) for track in data["tracks"]["items"] if track["track"] is not None