diff --git a/bot.py b/bot.py index ba60708..d362a97 100644 --- a/bot.py +++ b/bot.py @@ -6,21 +6,31 @@ import traceback import yaml from discord.ext import commands -from cogs.utils.settings import Settings +from discord.ext.commands.view import StringView +from cogs.utils.settingsmanager import Settings from cogs.utils.localizer import Localizer - - -initial_extensions = [ - 'cogs.cogs', - 'cogs.botsettings', - 'cogs.misc' -] +from cogs.utils.localizer import LocalizerWrapper +from cogs.utils.alias import Aliaser +from cogs.utils.context import Context with codecs.open("data/config.yaml", 'r', encoding='utf8') as f: conf = yaml.safe_load(f) +initial_extensions = [ + 'cogs.cogs', + 'cogs.settings', + 'cogs.misc' +] + + +on_ready_extensions = [ + 'cogs.music', + 'cogs.musicevents' +] + + def _get_prefix(bot, message): if not message.guild: return bot.settings.default_prefix @@ -35,6 +45,7 @@ class Bot(commands.Bot): self.settings = Settings(**conf['default server settings']) self.localizer = Localizer(conf.get('locale path', "./localization"), conf.get('locale', 'en_en')) + self.aliaser = Aliaser(conf.get('locale path', "./localization"), conf.get('locale', 'en_en')) self.debug = debug for extension in initial_extensions: @@ -61,9 +72,7 @@ class Bot(commands.Bot): pass elif isinstance(err, commands.NoPrivateMessage): - # Todo, fix, use default bot lang. await ctx.send('That command is not available in DMs') - #await ctx.send(self.localizer.format_str("{commands.not_available_dm}", self.settings.get(ctx.guild, 'locale', 'default_locale'))) elif isinstance(err, commands.CheckFailure): pass @@ -75,19 +84,43 @@ class Bot(commands.Bot): traceback.print_tb(tb) print(err) + async def on_message(self, message): + if message.author.bot: + return + await self.process_commands(message) + + async def process_commands(self, message): + ctx = await self.get_context(message, cls=Context) + + # Replace aliases with commands + ctx = self.aliaser.get_command(ctx) + if ctx.command and isinstance(ctx.command, commands.GroupMixin): + ctx = self.aliaser.get_subcommand(ctx, ctx.command, [str(ctx.command)]) + + # Add the localizer + if ctx.command and ctx.command.cog_name: + ctx.localizer = LocalizerWrapper(self.localizer, ctx.locale, ctx.command.cog_name.lower()) + else: + ctx.localizer = LocalizerWrapper(self.localizer, ctx.locale, None) + + await self.invoke(ctx) + async def on_ready(self): if not hasattr(self, 'uptime'): self.uptime = time.time() if self.debug: print('\n\nDebug mode') + for extension in on_ready_extensions: + try: + self.load_extension(extension) + except Exception as e: + print(e) + print(f'\nLogged in as: {self.user.name}' + f' in {len(self.guilds)} servers.') print(f'Version: {discord.__version__}\n') - self.load_extension('cogs.music') - self.load_extension('cogs.musicevents') - await self.change_presence(activity=discord.Game(type=0, name=conf["bot"]["playing status"]), status=discord.Status.online) diff --git a/cogs/misc.py b/cogs/misc.py index 4f24f25..22aa746 100644 --- a/cogs/misc.py +++ b/cogs/misc.py @@ -45,18 +45,17 @@ class Misc: """ Info om musikkspilleren """ - embed = discord.Embed(title='{info.music.title}', color=ctx.me.color) + embed = discord.Embed(title='{music.title}', color=ctx.me.color) lavalink = self.bot.lavalink listeners = 0 for guild, player in lavalink.players: listeners += len(player.listeners) - embed.add_field(name='{info.music.players}', value=f'{len(lavalink.players)}') - embed.add_field(name='{info.music.listeners}', value=f'{listeners}') - embed = self.bot.localizer.format_embed(embed, self.bot.settings.get(ctx.guild, 'locale', 'default_locale')) + embed.add_field(name='{music.players}', value=f'{len(lavalink.players)}') + embed.add_field(name='{music.listeners}', value=f'{listeners}') + embed = ctx.localizer.format_embed(embed) await ctx.send(embed=embed) - @commands.command(name="reloadlocale") @@ -65,6 +64,12 @@ class Misc: self.bot.localizer.load_localizations() await ctx.send("Localizations reloaded.") + @commands.command(name="reloadalias") + async def reload_alias(self, ctx): + self.bot.aliaser.index_localizations() + self.bot.aliaser.load_localizations() + await ctx.send("Aliases reloaded.") + @commands.command() async def info(self, ctx): """ @@ -93,19 +98,18 @@ class Misc: embed.set_author(name=self.bot.user.name, icon_url=avatar) embed.set_thumbnail(url=avatar) embed.set_image(url='https://cdn.discordapp.com/attachments/298524946454282250/368118192251469835/vintage1turntable.png') - embed.add_field(name="{info.bot.what}", - value='{info.bot.infotext}', inline=False) + embed.add_field(name="{bot.what}", + value='{bot.infotext}', inline=False) embed.set_footer(icon_url="https://cdn.discordapp.com/icons/532176350019321917/92f43a1f67308a99a30c169db4b671dd.png?size=64", - text="{info.bot.footer_text}") - embed.add_field(name="{info.bot.how}", - value='{info.bot.spectext}') - embed.add_field(name="{info.bot.how_many}", - value='{info.bot.stattext}') - embed.add_field(name="{info.bot.how_long}", + text="{bot.footer_text}") + embed.add_field(name="{bot.how}", + value='{bot.spectext}') + embed.add_field(name="{bot.how_many}", + value='{bot.stattext}') + embed.add_field(name="{bot.how_long}", value=uptimetext) - embed = self.bot.localizer.format_embed(embed, - self.bot.settings.get(ctx.guild, 'locale', 'default_locale'), + embed = ctx.localizer.format_embed(embed, _python_v=platform.python_version(), _discord_v=discord.__version__, _lavalink_v=LavalinkVersion, @@ -114,11 +118,6 @@ class Misc: ) await ctx.send(embed=embed) - @commands.command() - @checks.is_even() - async def only_me(self, ctx): - await ctx.send('Only you!') - def setup(bot): bot.add_cog(Misc(bot)) diff --git a/cogs/music.py b/cogs/music.py index ef98542..21a78f4 100644 --- a/cogs/music.py +++ b/cogs/music.py @@ -16,7 +16,7 @@ from typing import Optional from .utils import checks, RoxUtils, timeformatter from .utils.mixplayer import MixPlayer from .utils.embedscroller import QueueScroller -from .utils.localizer import LocalizerWrapper + time_rx = re.compile('[0-9]+') url_rx = re.compile('https?:\\/\\/(?:www\\.)?.+') @@ -35,48 +35,29 @@ class Music: bot.lavalink.add_node(**conf['lavalink nodes']['main']) bot.add_listener(bot.lavalink.voice_update_handler, 'on_socket_response') - def getLocalizer(self, guild): - lang = self.bot.settings.get(guild, 'locale', 'default_locale') - return LocalizerWrapper(self.bot.localizer, lang, "music.response") - - async def __local_check(self, ctx): - """ Ensures the commands are used in the correct channels """ + async def __before_invoke(self, ctx): + if not ctx.guild: + raise commands.NoPrivateMessage textchannels = self.bot.settings.get(ctx.guild, 'channels.text', []) if textchannels: if ctx.channel.id not in textchannels: - localizer = self.getLocalizer(ctx.guild) - response = localizer.format_str('{settings_check.textchannel}') + response = ctx.localizer.format_str('{settings_check.textchannel}') for channel_id in textchannels: response += f'<#{channel_id}>, ' - await ctx.send(response[:-2]) - return False + return await ctx.send(response[:-2]) + + await self.ensure_voice(ctx) return True - async def __before_invoke(self, ctx): - # TODO: rewrite this thing. Probably remove ensure_voice. - - guild_check = ctx.guild is not None - # This is essentially the same as `@commands.guild_only()` - # except it saves us repeating ourselves (and also a few lines). - - if guild_check: - await self.ensure_voice(ctx) - # Ensure that the bot and command author share a mutual voicechannel. - else: - raise commands.NoPrivateMessage - - return guild_check - async def connect_to(self, guild_id: int, channel_id: str): """ Connects to the given voicechannel ID. A channel_id of `None` means disconnect. """ ws = self.bot._connection._get_websocket(guild_id) await ws.voice_state(str(guild_id), channel_id) - @commands.command(name='play', aliases=['p','spill','s','spel']) + @commands.command(name='play') async def _play(self, ctx, *, query: str): """ Searches and plays a song from a given query. """ player = self.bot.lavalink.players.get(ctx.guild.id) - localizer = self.getLocalizer(ctx.guild) query = query.strip('<>') if not url_rx.match(query): @@ -85,7 +66,7 @@ class Music: results = await player.node.get_tracks(query) if not results or not results['tracks']: - return await ctx.send(localizer.format_str("{nothing_found}")) + return await ctx.send(ctx.localizer.format_str("{nothing_found}")) embed = discord.Embed(color=ctx.me.color) @@ -106,33 +87,32 @@ class Music: embed.title = '{playlist_enqued}' embed.description = f'{results["playlistInfo"]["name"]} - {numtracks} {{tracks}}' - embed = localizer.format_embed(embed) + embed = ctx.localizer.format_embed(embed) await ctx.send(embed=embed) else: track = results['tracks'][0] await self.enqueue(ctx, track, embed) - embed = localizer.format_embed(embed) + embed = ctx.localizer.format_embed(embed) await ctx.send(embed=embed) if not player.is_playing: await player.play() - @commands.command(name='seek', aliases=['spol']) + @commands.command(name='seek') @checks.DJ_or(alone=True) async def _seek(self, ctx, *, time: str): """ Seeks to a given position in a track. """ player = self.bot.lavalink.players.get(ctx.guild.id) - localizer = self.getLocalizer(ctx.guild) if not player.is_playing: - return await ctx.send(localizer.format_str("{not_playing}")) + return await ctx.send(ctx.localizer.format_str("{not_playing}")) if ctx.author not in player.listeners: - return await ctx.send(localizer.format_str("{have_to_listen}")) + return await ctx.send(ctx.localizer.format_str("{have_to_listen}")) seconds = time_rx.search(time) if not seconds: - return await ctx.send(localizer.format_str("{seek.missing_amount}")) + return await ctx.send(ctx.localizer.format_str("{seek.missing_amount}")) seconds = int(seconds.group()) * 1000 if time.startswith('-'): @@ -140,17 +120,16 @@ class Music: track_time = player.position + seconds await player.seek(track_time) - msg = localizer.format_str("{seek.track_moved}", _position=timeformatter.format(track_time)) + msg = ctx.localizer.format_str("{seek.track_moved}", _position=timeformatter.format(track_time)) await ctx.send(msg) - @commands.command(name='skip', aliases=['hopp']) + @commands.command(name='skip') async def _skip(self, ctx): """ Skips the current track. """ player = self.bot.lavalink.players.get(ctx.guild.id) - localizer = self.getLocalizer(ctx.guild) if not player.is_playing: - return await ctx.send(localizer.format_str("{not_playing}")) + return await ctx.send(ctx.localizer.format_str("{not_playing}")) player.add_skipper(ctx.author) total = len(player.listeners) @@ -159,56 +138,53 @@ class Music: if skips/total >= threshold/100 or player.current.requester == ctx.author.id: await player.skip() - await ctx.send(localizer.format_str("{skip.skipped}")) + await ctx.send(ctx.localizer.format_str("{skip.skipped}")) else: if skips != 0: needed = math.ceil(total*threshold/100) - msg = localizer.format_str("{skip.require_vote}", _skips=skips, _total=needed) + msg = ctx.localizer.format_str("{skip.require_vote}", _skips=skips, _total=needed) await ctx.send(msg) - @commands.command(name='skipto', aliases=['forceskip','hopptil','tvinghopp']) + @commands.command(name='skipto') @checks.DJ_or(alone=True) async def _skip_to(self, ctx, pos: int=1): """ Plays the queue from a specific point. Disregards tracks before the pos. """ player = self.bot.lavalink.players.get(ctx.guild.id) - localizer = self.getLocalizer(ctx.guild) if ctx.author not in player.listeners: - return await ctx.send(localizer.format_str("{have_to_listen}")) + return await ctx.send(ctx.localizer.format_str("{have_to_listen}")) if pos < 1: - return await ctx.send(localizer.format_str("{skip_to.invalid_pos}")) + return await ctx.send(ctx.localizer.format_str("{skip_to.invalid_pos}")) if len(player.queue) < pos: - return await ctx.send(localizer.format_str("{skip_to.exceeds_queue}")) + return await ctx.send(ctx.localizer.format_str("{skip_to.exceeds_queue}")) await player.skip(pos - 1) - msg = localizer.format_str("{skip_to.skipped_to}", _title=player.current.title, _pos=pos) + msg = ctx.localizer.format_str("{skip_to.skipped_to}", _title=player.current.title, _pos=pos) await ctx.send(msg) - @commands.command(name='stop', aliases=['stopp']) + @commands.command(name='stop') @checks.DJ_or(alone=True) async def _stop(self, ctx): """ Stops the player and clears its queue. """ player = self.bot.lavalink.players.get(ctx.guild.id) - localizer = self.getLocalizer(ctx.guild) if ctx.author not in player.listeners: - return await ctx.send(localizer.format_str("{have_to_listen}")) + return await ctx.send(ctx.localizer.format_str("{have_to_listen}")) if not player.is_playing: - return await ctx.send(localizer.format_str("{not_playing}")) + return await ctx.send(ctx.localizer.format_str("{not_playing}")) player.queue.clear() await player.stop() - await ctx.send(localizer.format_str("{stop}")) + await ctx.send(ctx.localizer.format_str("{stop}")) - @commands.command(name='now', aliases=['np','current', 'nå', 'nåspilles', 'gjeldende', 'spillernå','spelarno','nospelar']) + @commands.command(name='now') async def _now(self, ctx): """ Shows some stats about the currently playing song. """ player = self.bot.lavalink.players.get(ctx.guild.id) - localizer = self.getLocalizer(ctx.guild) if not player.current: - return await ctx.send(localizer.format_str("{not_playing}")) + return await ctx.send(ctx.localizer.format_str("{not_playing}")) position = timeformatter.format(player.position) if player.current.stream: @@ -219,7 +195,7 @@ class Music: member = ctx.guild.get_member(player.current.requester) embed = discord.Embed(color=ctx.me.color, description=song, title='{now}') - thumbnail_url = await RoxUtils.ThumbNailer.identify(self,player.current.identifier, player.current.uri) + thumbnail_url = await RoxUtils.ThumbNailer.identify(self, player.current.identifier, player.current.uri) if thumbnail_url: embed.set_thumbnail(url=thumbnail_url) @@ -229,100 +205,96 @@ class Music: member_identifier = member.name embed.set_footer(text=f'{{requested_by}} {member_identifier}', icon_url=member.avatar_url) - embed = localizer.format_embed(embed) + embed = ctx.localizer.format_embed(embed) await ctx.send(embed=embed) - @commands.command(name='queue', aliases=['q', 'kø']) + @commands.command(name='queue') async def _queue(self, ctx, user: discord.Member=None): """ Shows the player's queue. """ player = self.bot.lavalink.players.get(ctx.guild.id) - localizer = self.getLocalizer(ctx.guild) if player.queue.empty: - return await ctx.send(localizer.format_str("{queue.empty}")) + embed = discord.Embed(description='{queue.empty}', color=ctx.me.color) + embed = ctx.localizer.format_embed(embed) + return await ctx.send(embed=embed) if user is None: queue = player.global_queue() - scroller = QueueScroller(ctx, queue, localizer, lines_per_page=10) + scroller = QueueScroller(ctx, queue, ctx.localizer, lines_per_page=10) await scroller.start_scrolling() else: user_queue = player.user_queue(user.id, dual=True) if not user_queue: - return await ctx.send(localizer.format_str("{queue.empty}", _user=user.name)) + return await ctx.send(ctx.localizer.format_str("{queue.empty}", _user=user.name)) - scroller = QueueScroller(ctx, user_queue, localizer, lines_per_page=10, user_name=user.name) + scroller = QueueScroller(ctx, user_queue, ctx.localizer, lines_per_page=10, user_name=user.name) await scroller.start_scrolling() - @commands.command(name='myqueue', aliases=['mq','minkø','mk']) + @commands.command(name='myqueue') async def _myqueue(self, ctx): """ Shows your queue. """ player = self.bot.lavalink.players.get(ctx.guild.id) - localizer = self.getLocalizer(ctx.guild) user_queue = player.user_queue(ctx.author.id, dual=True) if not user_queue: - return await ctx.send(localizer.format_str("{my_queue}")) + return await ctx.send(ctx.localizer.format_str("{my_queue}")) - scroller = QueueScroller(ctx, user_queue, localizer, lines_per_page=10, user_name=ctx.author.name) + scroller = QueueScroller(ctx, user_queue, ctx.localizer, lines_per_page=10, user_name=ctx.author.name) await scroller.start_scrolling() - @commands.command(name='pause', aliases=['resume','gjenoppta']) + @commands.command(name='pause') @checks.DJ_or(alone=True) async def _pause(self, ctx): """ Pauses/Resumes the current track. """ player = self.bot.lavalink.players.get(ctx.guild.id) - localizer = self.getLocalizer(ctx.guild) if not player.is_playing: - return await ctx.send(localizer.format_str("{not_playing}")) + return await ctx.send(ctx.localizer.format_str("{not_playing}")) if player.paused: await player.set_pause(False) - await ctx.send(localizer.format_str("{resume.resumed}")) + await ctx.send(ctx.localizer.format_str("{resume.resumed}")) else: await player.set_pause(True) - await ctx.send(localizer.format_str("{resume.paused}")) + await ctx.send(ctx.localizer.format_str("{resume.paused}")) - @commands.command(name='shuffle', aliases=['stokk']) + @commands.command(name='shuffle') async def _shuffle(self, ctx): """ Shuffles your queue. """ player = self.bot.lavalink.players.get(ctx.guild.id) - localizer = self.getLocalizer(ctx.guild) user_queue = player.user_queue(ctx.author.id) if not user_queue: - return await ctx.send(localizer.format_str("{my_queue}")) + return await ctx.send(ctx.localizer.format_str("{my_queue}")) player.shuffle_user_queue(ctx.author.id) - await ctx.send(localizer.format_str("{shuffle}")) + await ctx.send(ctx.localizer.format_str("{shuffle}")) - @commands.command(name='move', aliases=['m', 'flytt','f']) + @commands.command(name='move') async def _move(self, ctx, from_pos: int, to_pos: int): """ Moves a song in your queue. """ player = self.bot.lavalink.players.get(ctx.guild.id) - localizer = self.getLocalizer(ctx.guild) user_queue = player.user_queue(ctx.author.id) if not user_queue: - return await ctx.send(localizer.format_str("{my_queue}")) + return await ctx.send(ctx.localizer.format_str("{my_queue}")) if not all(x in range(1,len(user_queue) + 1) for x in [from_pos, to_pos]): - return await ctx.send(localizer.format_str("{out_of_range}", _len=len(user_queue))) + return await ctx.send(ctx.localizer.format_str("{out_of_range}", _len=len(user_queue))) moved = player.move_user_track(ctx.author.id, from_pos - 1, to_pos - 1) if moved is None: - return await ctx.send(localizer.format_str("{move.not_in_queue}")) + return await ctx.send(ctx.localizer.format_str("{move.not_in_queue}")) - msg = localizer.format_str("{move.moved_to}", _title=moved.title, _from=from_pos, _to=to_pos) + msg = ctx.localizer.format_str("{move.moved_to}", _title=moved.title, _from=from_pos, _to=to_pos) await ctx.send(msg) - @commands.command(name='remove', aliases=['rem', 'fjern']) + @commands.command(name='remove') async def _remove(self, ctx, pos: int): """ Removes an item from the player's queue with the given pos. """ player = self.bot.lavalink.players.get(ctx.guild.id) - localizer = self.getLocalizer(ctx.guild) if player.queue.empty: return @@ -332,29 +304,28 @@ class Music: return if pos > len(user_queue) or pos < 1: - return await ctx.send(localizer.format_str("{out_of_range}", _len=len(user_queue))) + return await ctx.send(ctx.localizer.format_str("{out_of_range}", _len=len(user_queue))) removed = player.remove_user_track(ctx.author.id, pos - 1) - await ctx.send(localizer.format_str("{remove}", _title=removed.title)) + await ctx.send(ctx.localizer.format_str("{remove}", _title=removed.title)) - @commands.command(name="DJremove", aliases=['DJfjern','djfjern']) + @commands.command(name="DJremove") @checks.DJ_or() async def _djremove(self, ctx, pos: int, user: discord.Member=None): """ Remove a song from either the global queue or a users queue""" player = self.bot.lavalink.players.get(ctx.guild.id) - localizer = self.getLocalizer(ctx.guild) if user is None: if player.queue.empty: return if pos > len(player.queue) or pos < 1: - return await ctx.send(localizer.format_str("{out_of_range}", _len=len(player.queue))) + return await ctx.send(ctx.localizer.format_str("{out_of_range}", _len=len(player.queue))) removed = player.remove_global_track(pos - 1) requester = self.bot.get_user(removed.requester) - await ctx.send(localizer.format_str("{dj_removed}", _title=removed.title, _user=requester.name)) + await ctx.send(ctx.localizer.format_str("{dj_removed}", _title=removed.title, _user=requester.name)) else: if player.queue.empty: @@ -365,17 +336,16 @@ class Music: return if pos > len(user_queue) or pos < 1: - return await ctx.send(localizer.format_str("{out_of_range}", _len=len(user_queue))) + return await ctx.send(ctx.localizer.format_str("{out_of_range}", _len=len(user_queue))) removed = player.remove_user_track(user.id, pos - 1) - await ctx.send(localizer.format_str("{dj_removed}", _title=removed.title, _user=requester.name)) + await ctx.send(ctx.localizer.format_str("{dj_removed}", _title=removed.title, _user=requester.name)) - @commands.command(name='removeuser', aliases=['fjernbruker','fjernbrukar']) + @commands.command(name='removeuser') @checks.DJ_or(alone=True) async def _user_queue_remove(self, ctx, user: discord.Member): """ Remove a song from either the global queue or a users queue""" player = self.bot.lavalink.players.get(ctx.guild.id) - localizer = self.getLocalizer(ctx.guild) if player.queue.empty: return @@ -388,15 +358,14 @@ class Music: embed = discord.Embed(color=ctx.me.color) embed.description = "{dj_remove_user}" - embed = localizer.format_embed(embed, _id=user.id) + embed = ctx.localizer.format_embed(embed, _id=user.id) await ctx.send(embed=embed) - @commands.command(name='search', aliases=['søk','finn']) + @commands.command(name='search') async def _search(self, ctx, *, query): """ Lists the first 10 search results from a given query. """ player = self.bot.lavalink.players.get(ctx.guild.id) - localizer = self.getLocalizer(ctx.guild) if not query.startswith('ytsearch:') and not query.startswith('scsearch:'): query = 'ytsearch:' + query @@ -405,7 +374,7 @@ class Music: embed = discord.Embed(description='{nothing_found}', color=0x36393F) if not results or not results['tracks']: - embed = localizer.format_embed(embed) + embed = ctx.localizer.format_embed(embed) return await ctx.send(embed=embed) tracks = results['tracks'] @@ -447,7 +416,7 @@ class Music: return False embed.description = '{search}' - embed = localizer.format_embed(embed) + embed = ctx.localizer.format_embed(embed) result_msg = await ctx.send(embed=embed) for emoji, index in choices[:result_count]: @@ -458,7 +427,7 @@ class Music: embed.title = '{results}' embed.color = ctx.me.color - embed = localizer.format_embed(embed) + embed = ctx.localizer.format_embed(embed) await result_msg.edit(embed=embed) @@ -470,7 +439,7 @@ class Music: await result_msg.clear_reactions() embed.title = '' embed.description='{time_expired}' - embed = localizer.format_embed(embed) + embed = ctx.localizer.format_embed(embed) await result_msg.edit(embed=embed) await asyncio.sleep(5) await result_msg.delete() @@ -481,37 +450,35 @@ class Music: await result_msg.clear_reactions() track = tracks[choice - 1] await self.enqueue(ctx, track, embed) - embed = localizer.format_embed(embed) + embed = ctx.localizer.format_embed(embed) await result_msg.edit(embed=embed) if not player.is_playing: await player.play() - @commands.command(name='disconnect', aliases=['dc','kf','koblefra','koblefrå']) + @commands.command(name='disconnect') @checks.DJ_or(alone=True) async def _disconnect(self, ctx): """ Disconnects the player from the voice channel and clears its queue. """ player = self.bot.lavalink.players.get(ctx.guild.id) - localizer = self.getLocalizer(ctx.guild) if not player.is_connected: - return await ctx.send(localizer.format_str("{not_connected}")) + return await ctx.send(ctx.localizer.format_str("{not_connected}")) if not ctx.author.voice or (player.is_connected and ctx.author.voice.channel.id != int(player.channel_id)): - return await ctx.send(localizer.format_str("{disconnect.not_in_voice}")) + return await ctx.send(ctx.localizer.format_str("{disconnect.not_in_voice}")) player.queue.clear() await player.stop() await self.connect_to(ctx.guild.id, None) embed = discord.Embed(description='{disconnect.disconnected}', color=ctx.me.color) - embed = localizer.format_embed(embed) + embed = ctx.localizer.format_embed(embed) await ctx.send(embed=embed) - @commands.command(name='volume', aliases=['vol', 'volum', 'lydstyrke']) + @commands.command(name='volume') @checks.DJ_or(alone=True, current=True) async def _volume(self, ctx, volume: int = None): """ Changes the player's volume. Must be between 0 and 1000. Error Handling for that is done by Lavalink. """ player = self.bot.lavalink.players.get(ctx.guild.id) - localizer = self.getLocalizer(ctx.guild) if not volume: return await ctx.send(f'🔈 | {player.volume}%') @@ -520,36 +487,34 @@ class Music: is_admin = getattr(ctx.author.guild_permissions, 'administrator', None) == True if int(player.current.requester) == ctx.author.id and not is_dj and not is_admin: if not 50 <= volume <= 125: - return await ctx.send(localizer.format_str("{volume.out_of_range}")) + return await ctx.send(ctx.localizer.format_str("{volume.out_of_range}")) await player.set_volume(volume) embed = discord.Embed(color=ctx.me.color) embed.description = "{volume.set_to}" - embed = localizer.format_embed(embed, _volume=player.volume) + embed = ctx.localizer.format_embed(embed, _volume=player.volume) await ctx.send(embed=embed) - @commands.command(name='normalize', aliases=['normal','nl','normaliser']) + @commands.command(name='normalize') @checks.DJ_or(alone=True) async def _normalize(self, ctx): """ Reset the equalizer and """ player = self.bot.lavalink.players.get(ctx.guild.id) - localizer = self.getLocalizer(ctx.guild) await player.set_volume(100) await player.bassboost(False) embed = discord.Embed(color=ctx.me.color) embed.description = '{volume.reset}' - embed = localizer.format_embed(embed) + embed = ctx.localizer.format_embed(embed) await ctx.send(embed=embed) - @commands.command(name='boost', aliases=['boo','bassforsterker','bassforsterkar']) + @commands.command(name='boost') @checks.DJ_or(alone=True) async def _boost(self, ctx, boost: bool=None): """ Set the equalizer to bass boost the music """ player = self.bot.lavalink.players.get(ctx.guild.id) - localizer = self.getLocalizer(ctx.guild) if boost is not None: await player.bassboost(boost) @@ -561,43 +526,43 @@ class Music: else: embed.description = '{boost.off}' - embed = localizer.format_embed(embed) + embed = ctx.localizer.format_embed(embed) await ctx.send(embed=embed) - @commands.command(name='history', aliases=['h','hist','historie']) + @commands.command(name='history') async def _history(self, ctx): """ Show the last 10 songs played """ player = self.bot.lavalink.players.get(ctx.guild.id) - localizer = self.getLocalizer(ctx.guild) history = player.get_history() if not history: - return await ctx.send(localizer.format_str("{history.empty}")) + embed = discord.Embed(description='{history.empty}', color=ctx.me.color) + embed = ctx.localizer.format_embed(embed) + return await ctx.send(embed=embed) track = history[0] - description = localizer.format_str("{history.current}", _title=track.title, _uri=track.uri,_id=track.requester) + '\n\n' - description += localizer.format_str("{history.previous}", _len=len(history)-1) + '\n' + description = ctx.localizer.format_str("{history.current}", _title=track.title, _uri=track.uri,_id=track.requester) + '\n\n' + description += ctx.localizer.format_str("{history.previous}", _len=len(history)-1) + '\n' thumb_url = await RoxUtils.ThumbNailer.identify(self, track.identifier, track.uri) for index, track in enumerate(history[1:], start=1): - description += localizer.format_str("{history.track}",_index=-index, _title=track.title, _uri=track.uri, _id=track.requester) + '\n' + description += ctx.localizer.format_str("{history.track}",_index=-index, _title=track.title, _uri=track.uri, _id=track.requester) + '\n' - embed = discord.Embed(title=localizer.format_str('{history.title}'), color=ctx.me.color, description=description) + embed = discord.Embed(title=ctx.localizer.format_str('{history.title}'), color=ctx.me.color, description=description) if thumb_url: embed.set_thumbnail(url=thumb_url) await ctx.send(embed=embed) - @commands.command(name='scrub', aliases=['kontrol','konsoll']) + @commands.command(name='scrub') @checks.DJ_or(alone=True) async def _scrub(self, ctx): """ Lists the first 10 search results from a given query. """ player = self.bot.lavalink.players.get(ctx.guild.id) - localizer = self.getLocalizer(ctx.guild) controls = '{scrub.controls}' embed = discord.Embed(description='{nothing_playing}', color=ctx.me.color) - embed = localizer.format_embed(embed) + embed = ctx.localizer.format_embed(embed) if player.current is None: return await ctx.send(embed=embed) @@ -631,14 +596,14 @@ class Music: return False embed.description = '{scrub.add}' - embed = localizer.format_embed(embed) + embed = ctx.localizer.format_embed(embed) msg = await ctx.send(embed=embed) for (emoji, _, _) in scrubber: await msg.add_reaction(emoji) embed.description = controls - embed = localizer.format_embed(embed) + embed = ctx.localizer.format_embed(embed) await msg.edit(embed=embed) scrubbing = True while scrubbing: @@ -694,8 +659,7 @@ class Music: if voice_channels: if voice_channel.id not in voice_channels: - localizer = self.getLocalizer(ctx.guild) - response = localizer.format_str('{settings_check.voicechannel}') + response = ctx.localizer.format_str('{settings_check.voicechannel}') for channel_id in voice_channels: channel = ctx.guild.get_channel(channel_id) if channel is not None: @@ -713,11 +677,10 @@ class Music: async def enqueue(self, ctx, track, embed): player = self.bot.lavalink.players.get(ctx.guild.id) - localizer = self.getLocalizer(ctx.guild) maxlength = self.max_track_length(ctx.guild, player) if maxlength and track['info']['length'] > maxlength: - embed.description = localizer.format_str("{enqueue.toolong}", + embed.description = ctx.localizer.format_str("{enqueue.toolong}", _length=timeformatter.format(track['info']['length']), _max=timeformatter.format(maxlength)) return diff --git a/cogs/botsettings.py b/cogs/settings.py similarity index 89% rename from cogs/botsettings.py rename to cogs/settings.py index 9d8ff21..d9af0b9 100644 --- a/cogs/botsettings.py +++ b/cogs/settings.py @@ -5,7 +5,7 @@ import codecs from discord.ext import commands from cogs.utils import checks -class BotSettings: +class Settings: def __init__(self, bot): self.bot = bot self.settings = self.bot.settings @@ -19,7 +19,7 @@ class BotSettings: return formatted[:-2] @checks.is_admin() - @commands.group(name='set', hidden=True) + @commands.group(name='settings', hidden=True) async def _set(self, ctx): if ctx.invoked_subcommand is None: await self.current_settings.invoke(ctx) @@ -178,53 +178,54 @@ class BotSettings: @commands.guild_only() @_set.command(name='current') async def current_settings(self, ctx): - embed = discord.Embed(title='Settings', color=ctx.me.color) - embed.description = f'Current settings for {ctx.guild.name}' + embed = discord.Embed(title='{current.title}', color=ctx.me.color) + embed.description = '{current.description}' embed.set_thumbnail(url=ctx.guild.icon_url) prefixes = self.bot.settings.get(ctx.guild, 'prefixes', 'default_prefix') - embed.add_field(name='Prefix', value=self.format_prefixes(prefixes)) + embed.add_field(name='{current.prefix}', value=self.format_prefixes(prefixes)) locale = f"{self.bot.settings.get(ctx.guild, 'locale', 'default_locale')}" - embed.add_field(name='Locale', value=locale) + embed.add_field(name='{current.locale}', value=locale) threshold = self.bot.settings.get(ctx.guild, 'vote_threshold', 50) - embed.add_field(name='Vote threshold', value=f'{threshold}%') + embed.add_field(name='{current.threshold}', value=f'{threshold}%') is_dynamic = self.bot.settings.get(ctx.guild, 'duration.is_dynamic', 'default_is_dynamic') - embed.add_field(name='Dynamic max duration', value=is_dynamic) + embed.add_field(name='{current.dynamicmax}', value=is_dynamic) duration = self.bot.settings.get(ctx.guild, 'maxduration') if duration: - embed.add_field(name='Max track duration', value=f'{duration} minutes') + embed.add_field(name='{current.maxduration}', value=f'{duration} minutes') textchannels = self.bot.settings.get(ctx.guild, 'channels.text') if textchannels: channels = [ctx.guild.get_channel(channel) for channel in textchannels] mentioned = [channel.mention for channel in channels if channel is not None] - embed.add_field(name='Text channels', value='\n'.join(mentioned)) + embed.add_field(name='{current.textchannels}', value='\n'.join(mentioned)) voicechannels = self.bot.settings.get(ctx.guild, 'channels.music') if voicechannels: channels = [ctx.guild.get_channel(channel) for channel in voicechannels] mentioned = [channel.name for channel in channels if channel is not None] - embed.add_field(name='Music channels', value='\n'.join(mentioned)) + embed.add_field(name='{current.musicchannels}', value='\n'.join(mentioned)) listenchannels = self.bot.settings.get(ctx.guild, 'channels.listen_only') if listenchannels: channels = [ctx.guild.get_channel(channel) for channel in listenchannels] mentioned = [channel.name for channel in channels if channel is not None] - embed.add_field(name='Listen only channels', value='\n'.join(mentioned)) + embed.add_field(name='{current.listenchannels}', value='\n'.join(mentioned)) djroles = self.bot.settings.get(ctx.guild, 'roles.dj') if djroles: roles = [ctx.guild.get_role(role) for role in djroles] mentioned = [role.name for role in roles if role is not None] - embed.add_field(name='Dj roles', value=', '.join(mentioned)) + embed.add_field(name='{current.djroles}', value=', '.join(mentioned)) + embed = ctx.localizer.format_embed(embed, _guild=ctx.guild.name) msg = await ctx.send(embed=embed) return msg def setup(bot): - bot.add_cog(BotSettings(bot)) + bot.add_cog(Settings(bot)) diff --git a/cogs/utils/alias.py b/cogs/utils/alias.py new file mode 100644 index 0000000..8593d81 --- /dev/null +++ b/cogs/utils/alias.py @@ -0,0 +1,116 @@ +import discord +import yaml + +from glob import glob +from os import path + +from discord.ext import commands + +class Aliaser: + def __init__(self, localization_folder, default_lang): + self.localization_folder = path.realpath(localization_folder) + self.default_lang = default_lang + self.index_localizations() + self.load_localizations() + + def _gen_alias_dict(self, commands): + """ Reverses the direction of a dict of commans→aliases to be alias→command. """ + inverted = {} + subcommands = {} + for cmd, properties in commands.items(): + if isinstance(properties, list): + for alias in properties: + inverted[alias] = cmd + else: + for alias in properties['aliases']: + inverted[alias] = cmd + if properties.get('sub_commands', None): + subcommands[cmd] = self._gen_alias_dict(properties['sub_commands']) + inverted['sub_commands'] = subcommands + return inverted + + def index_localizations(self): + self.localization_table = {} + for folder in glob(path.join(self.localization_folder, "*/")): + if 'global' in folder: + continue + folder_base = path.basename(path.dirname(folder)) + self.localization_table[folder_base] = False + + def load_localizations(self): + for lang in self.localization_table.keys(): + with open(path.join(self.localization_folder, lang, "aliases.yaml"), "r", encoding='utf-8') as f: + data = yaml.load(f) + self.localization_table[lang] = {'aliases': data, 'commands': self._gen_alias_dict(data)} + + def convert_alias(self, locale, alias, default=None, parents=[]): + locale = self.localization_table[locale] + + # Traverse through the alias tree as dictated by the parents list + def traverse(alias_tree, parents, alias): + # Return the command when no more parents exist + if not parents: + return alias_tree.get(alias, None) + parent = parents.pop(0) + sub_commands = alias_tree.get('sub_commands', None) + if not sub_commands: + return None + alias_tree = sub_commands.get(parent, None) + if not alias_tree: + return None + return traverse(alias_tree, parents, alias) + + alias_tree = locale['commands'] + command = traverse(alias_tree, parents, default) + if command: + return command + return default + + def get_alias(self, locale, command): + locale = self.localization_table[locale] + return locale['aliases'].get(command, []) + + def get_command(self, ctx): + """ Get a top level command. """ + if not ctx.prefix: + ctx.command = None + return ctx + ctx.view.undo() + alias = ctx.view.get_word() + command = self.convert_alias(ctx.locale, alias, alias) + ctx.invoked_with = command + ctx.command = ctx.bot.all_commands.get(command) + return ctx + + def get_subcommand(self, ctx, group, parents=[]): + """ Recursicely replaces all subcommand aliases with subcommands.""" + view = ctx.view + prev = view.previous + idx = view.index + + # get the alias used + view.skip_ws() + alias = view.get_word() + + subcmd = self.convert_alias(ctx.locale, alias, alias, parents.copy()) + parents.append(subcmd) + + # Replace the alias in the command + view.buffer = view.buffer.replace(alias, subcmd) + + # Update the view to account for difference in length + strdiff = len(subcmd) - len(alias) + view.end += strdiff + view.index += strdiff + + # Get the subcommand + sub = group.all_commands.get(subcmd, None) + + # Translate any subsubcommands + if sub and isinstance(sub, commands.GroupMixin): + self.get_subcommand(ctx, sub, parents) + + # Reset the view indexes + view.index = idx + view.previous = prev + return ctx \ No newline at end of file diff --git a/cogs/utils/context.py b/cogs/utils/context.py new file mode 100644 index 0000000..632ba0c --- /dev/null +++ b/cogs/utils/context.py @@ -0,0 +1,9 @@ +from discord.ext import commands + +class Context(commands.Context): + def __init__(self, **kwargs): + super().__init__(**kwargs) + if self.message.guild: + self.locale = self.bot.settings.get(self.message.guild, 'locale', 'default_locale') + else: + self.locale = self.bot.settings.default_locale \ No newline at end of file diff --git a/cogs/utils/localizer.py b/cogs/utils/localizer.py index c4e27cc..578aa67 100644 --- a/cogs/utils/localizer.py +++ b/cogs/utils/localizer.py @@ -5,10 +5,9 @@ import re from cogs.utils.dict_utils import flatten, SafeDict -import json +import yaml import copy - from discord import Embed """ @@ -52,10 +51,12 @@ class Localizer: elif localization is False: self.localization_table[lang] = {} l_table = self.localization_table[lang] - for file in glob(path.join(self.localization_folder, lang, "*.json")): + for file in glob(path.join(self.localization_folder, lang, "*.yaml")): + if 'aliases' in file: + continue file_base = path.basename(file).split(".")[0] with open(file, "r", encoding='utf-8') as f: - data = json.load(f) + data = yaml.load(f) l_table[file_base] = data diff --git a/cogs/utils/settings.py b/cogs/utils/settingsmanager.py similarity index 100% rename from cogs/utils/settings.py rename to cogs/utils/settingsmanager.py diff --git a/localization/en_en/aliases.yaml b/localization/en_en/aliases.yaml new file mode 100644 index 0000000..08eeb6a --- /dev/null +++ b/localization/en_en/aliases.yaml @@ -0,0 +1,63 @@ +# Aliases for each of the commands in the the bot(except the forgotten onces.) +# Aliases without subcommands don't need to have "aliases:", specified. +# For commands with subcommands you need to have one subcategory called aliases and one called subcommands. + + +# Music +play: [p] +now: [np, nowplaying] +queue: [q] +myqueue: [mq] +seek: [] +skip: [] +skipto: [forceskip] +stop: [] +pause: [resume] +shuffle: [] +move: [m] +remove: [rem] +DJremove: [djremove] +removeuser: [] +search: [s] +disconnect: [dc] +volume: [v, vol] +normalize: [normal] +boost: [bassboost] +history: [h] +scrub: [] + +# Misc +info: [] +musicinfo: [] +uptime: [] +ping: [] +guilds: [] +reloadlocale: [] +reloadalias: [] + +# Settings +settings: + aliases: [set] + sub_commands: + current: [show] + serverlocale: [lang] + djroles: [] + dynamicmax: [] + musicchannels: [vc] + listenchannels: [] + textchannels: [tc] + maxduration: [] + modrole: [] + resetprefix: [] + serverprefix: [] + threshold: [] + +# Cogs +cogs: + aliases: [] + sub_commands: + load: [] + unload: [] + reload: [] + reloadall: [] + shutdown: [] \ No newline at end of file diff --git a/localization/en_en/commands.json b/localization/en_en/commands.json deleted file mode 100644 index 0f031d3..0000000 --- a/localization/en_en/commands.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "not_available_dm": "This command is not available in DMs" -} \ No newline at end of file diff --git a/localization/en_en/info.json b/localization/en_en/info.json deleted file mode 100644 index 401507f..0000000 --- a/localization/en_en/info.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "bot": { - "name": "What?", - "infotext": "Music bot written for use on /r/Norge's Discord Servers. It's open source! You can find it [HERE]({global.vars.repo})", - "spectext": "**Python:** [{_python_v}](https://www.python.org/)\n**Discord.py:** [{_discord_v}](https://github.com/Rapptz/discord.py/tree/rewrite)\n**Lavalink:** [{_lavalink_v}](https://github.com/Devoxin/Lavalink.py)", - "stattext": "**Guilds:** {_guilds}\n**Users:** {_members}", - "what": "What?", - "how": "How?", - "how_many": "How many?", - "how_long": "How long?", - "footer_text": "Made by /r/Norge, for /r/Norge" - }, - "music": { - "title": "Music info", - "players": "Players", - "listeners": "Listeners" - } -} \ No newline at end of file diff --git a/localization/en_en/misc.yaml b/localization/en_en/misc.yaml new file mode 100644 index 0000000..ac183c7 --- /dev/null +++ b/localization/en_en/misc.yaml @@ -0,0 +1,20 @@ +bot: + name: What? + infotext: Music bot written for use on /r/Norge's Discord Servers. It's open source! + You can find it [HERE]({global.vars.repo}) + spectext: |- + **Python:** [{_python_v}](https://www.python.org/) + **Discord.py:** [{_discord_v}](https://github.com/Rapptz/discord.py/tree/rewrite) + **Lavalink:** [{_lavalink_v}](https://github.com/Devoxin/Lavalink.py) + stattext: |- + **Guilds:** {_guilds} + **Users:** {_members} + what: What? + how: How? + how_many: How many? + how_long: How long? + footer_text: Made by /r/Norge +music: + title: Music info + players: Players + listeners: Listeners diff --git a/localization/en_en/music.json b/localization/en_en/music.json deleted file mode 100644 index 27b8748..0000000 --- a/localization/en_en/music.json +++ /dev/null @@ -1,90 +0,0 @@ -{ - "response": { - "nothing_found": "Nothing found!", - "not_playing": "Not playing.", - "nothing_playing": "Nothing playing", - "have_to_listen": "You have to be listening to the bot", - "playlist_enqued": "Playlist Enqueued!", - "requested_by": "Requested by", - "tracks": "tracks", - "seek": { - "missing_amount": "You need to specify the amount of seconds to skip!", - "track_moved": "Moved track to **{_position}**" - }, - "skip": { - "skipped": "Skipped.", - "require_vote": "{_skips} out of {_total} required haters have voted to skip." - }, - "skip_to": { - "invalid_pos": "Invalid specified position.", - "exceeds_queue": "The position exceeds the queue\\'s length.", - "skipped_to": "skipped to `{_title}` at position `{_pos}`" - }, - "stop": "⏹ | Stopped.", - "live": "🔴 LIVE", - "now": "Now Playing", - "queue": { - "empty": "The queue is empty", - "user_empty": "{_user}'s queue is empty", - "length": "**Queue** `{_length} songs`", - "userqueue": "**{_user}\\'s queue** `{_length}`", - "globaltrack":"`{_index}.` **[{_title}]({_uri})** _by <@{_user_id}>_\n", - "usertrack":"`{_index}({_globalindex}).` **[{_title}]({_uri})**\n", - "pageindicator":"Viewing page {_current}/{_total}" - }, - "my_queue": "Your queue is empty", - "resume": { - "resumed": "⏯ | Resumed", - "paused": "⏯ | Paused" - }, - "shuffle": "Your queue has been shuffled", - "out_of_range": "Positions must be between 1 and {_len}", - "move": { - "not_in_queue": "Check that the positions exist in your queue", - "moved_to": "{_title} moved from position {_from} to {_to} in your queue" - }, - "remove": "**{_title}** removed.", - "dj_removed": "**{_title}** queued by {_user} removed.", - "dj_remove_user": "Removed all songs queued by <@{_id}>.", - "search": "searching", - "results": "Results", - "time_expired": "Timer Expired", - "disconnect": { - "not_in_voice": "You\\'re not in my voicechannel!", - "disconnected": "Disconnected" - }, - "not_connected": "Not connected.", - "volume": { - "set_to": "Volume set to {_volume}", - "reset": "Volume and equalizer reset", - "out_of_range": "you can only set the volume between 50 and 125" - }, - "boost": { - "on": "Bass boost is on", - "off": "'Bass boost is off" - }, - "scrub": { - "add": "Adding controls", - "controls": "{music_controls}", - "_comment_controls": "scrub.controls read from music_controls.txt" - }, - "enqueue": { - "position": "position", - "playing_in": "Playing in", - "enqueued": "Song enqueued", - "estimated": "estimated", - "toolong": "Track too long. `{_length}>{_max}`" - }, - "history": { - "title": "Track history", - "current": "`Current` **[{_title}]({_uri})** _by <@{_id}>_", - "track": "`{_index}.` **[{_title}]({_uri})** _by <@{_id}>_", - "previous": "**{_len} Previous songs:**", - "empty": "There are no tracks in the history" - }, - "settings_check": { - "textchannel": "You can't use music commands in this channel.\nTry in: ", - "voicechannel": "I can only play music in: " - } - } -} \ No newline at end of file diff --git a/localization/en_en/music.yaml b/localization/en_en/music.yaml new file mode 100644 index 0000000..08aa8e8 --- /dev/null +++ b/localization/en_en/music.yaml @@ -0,0 +1,73 @@ +nothing_found: Nothing found! +not_playing: Not playing. +nothing_playing: Nothing playing +have_to_listen: You have to be listening to the bot +playlist_enqued: Playlist Enqueued! +requested_by: Requested by +tracks: tracks +seek: + missing_amount: You need to specify the amount of seconds to skip! + track_moved: Moved track to **{_position}** +skip: + skipped: Skipped. + require_vote: "{_skips} out of {_total} required haters have voted to skip." +skip_to: + invalid_pos: Invalid specified position. + exceeds_queue: The position exceeds the queue\'s length. + skipped_to: skipped to `{_title}` at position `{_pos}` +stop: "⏹ | Stopped." +live: "\U0001F534 LIVE" +now: Now Playing +queue: + empty: The queue is empty + user_empty: "{_user}'s queue is empty" + length: "**Queue** `{_length} songs`" + userqueue: "**{_user}\\'s queue** `{_length}`" + globaltrack: "`{_index}.` **[{_title}]({_uri})** _by <@{_user_id}>_\n" + usertrack: "`{_index}({_globalindex}).` **[{_title}]({_uri})**\n" + pageindicator: Viewing page {_current}/{_total} +my_queue: Your queue is empty +resume: + resumed: "⏯ | Resumed" + paused: "⏯ | Paused" +shuffle: Your queue has been shuffled +out_of_range: Positions must be between 1 and {_len} +move: + not_in_queue: Check that the positions exist in your queue + moved_to: "{_title} moved from position {_from} to {_to} in your queue" +remove: "**{_title}** removed." +dj_removed: "**{_title}** queued by {_user} removed." +dj_remove_user: Removed all songs queued by <@{_id}>. +search: searching +results: Results +time_expired: Timer Expired +disconnect: + not_in_voice: You\'re not in my voicechannel! + disconnected: Disconnected +not_connected: Not connected. +volume: + set_to: Volume set to {_volume} + reset: Volume and equalizer reset + out_of_range: you can only set the volume between 50 and 125 +boost: + 'on': Bass boost is on + 'off': Bass boost is off +scrub: + add: Adding controls + controls: "{music_controls}" + _comment_controls: scrub.controls read from music_controls.txt +enqueue: + position: position + playing_in: Playing in + enqueued: Song enqueued + estimated: estimated + toolong: Track too long. `{_length}>{_max}` +history: + title: Track history + current: "`Current` **[{_title}]({_uri})** _by <@{_id}>_" + track: "`{_index}.` **[{_title}]({_uri})** _by <@{_id}>_" + previous: "**{_len} Previous songs:**" + empty: There are no tracks in the history +settings_check: + textchannel: "You can't use music commands in this channel.\nTry in: " + voicechannel: 'I can only play music in: ' diff --git a/localization/en_en/settings.yaml b/localization/en_en/settings.yaml new file mode 100644 index 0000000..38a0d8e --- /dev/null +++ b/localization/en_en/settings.yaml @@ -0,0 +1,12 @@ +current: + title: Settings + description: Current settings for {_guild} + prefix: Prefix + locale: Locale + threshold: Vote threshold + dynamicmax: Dynamic max duration + maxduration: Max track duration + textchannels: Text channels + musicchannels: Music channels + listenchannels: Listen only channels + djroles: DJ roles diff --git a/localization/nb_no/aliases.yaml b/localization/nb_no/aliases.yaml new file mode 100644 index 0000000..994e3b9 --- /dev/null +++ b/localization/nb_no/aliases.yaml @@ -0,0 +1,43 @@ + +# Music aliases +play: [p, spill, s] +now: [np, nå] +queue: [q, kø, k] +myqueue: [mq, minkø, mk] +seek: [spol] +skip: [hopp] +skipto: [forceskip, hopptil, tvinghopp] +stop: [stopp] +pause: [resume, gjenoppta] +shuffle: [stokk] +move: [m, flytt, f] +remove: [rem, fjern] +DJremove: [DJfjern, djfjern] +removeuser: [fjernbruker] +search: [søk, finn] +disconnect: [dc, kf, koblefra] +volume: [vol, volum, lydstyrke, lyd] +normalize: [normal, nl, normaliser] +boost: [bassboost, bassforsterker] +history: [h, hist, historie] +scrub: [kontroller, konsoll] + +# Other +info: [informasjon] +musicinfo: [musikkinfo, musikkinformasjon] + +settings: + aliases: [innstillinger] + sub_commands: + serverlocale: [språk] + current: [gjeldende] + djroles: [djroller] + dynamicmax: [dynamiskmaks] + listenchannels: [] + maxduration: [maksvarighet] + modrole: [modrolle] + musicchannels: [musikkanaler] + resetprefix: [] + serverprefix: [serverprefiks] + textchannels: [tekstkanaler] + threshold: [grense] \ No newline at end of file diff --git a/localization/nb_no/commands.json b/localization/nb_no/commands.json deleted file mode 100644 index ee4ff4a..0000000 --- a/localization/nb_no/commands.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "not_available_dm": "Denne kommandoen er ikke tilgjengelig i DMs" -} \ No newline at end of file diff --git a/localization/nb_no/info.json b/localization/nb_no/info.json deleted file mode 100644 index 2b5d35b..0000000 --- a/localization/nb_no/info.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "bot": { - "name": "hva?", - "infotext": "Musikkbot skrevet for bruk på /r/Norge sine Discord Servere. Kildekoden er åpen! Den kan du finne [HER]({global.vars.repo})", - "spectext": "**Python:** [{_python_v}](https://www.python.org/)\n**Discord.py:** [{_discord_v}](https://github.com/Rapptz/discord.py/tree/rewrite)\n**Lavalink:** [{_lavalink_v}](https://github.com/Devoxin/Lavalink.py)", - "stattext": "**Tenarar:** {_guilds}\n**Brukere:** {_members}", - "what": "Hva?", - "how": "Hvordan?", - "how_many": "Hvor mange?", - "how_long": "Hvor lenge?", - "footer_text": "Laget av /r/Norge, for /r/Norge" - }, - "music": { - "title": "Musikkinfo", - "players": "Spillere", - "listeners": "Lyttere" - } -} \ No newline at end of file diff --git a/localization/nb_no/misc.yaml b/localization/nb_no/misc.yaml new file mode 100644 index 0000000..e17b8d4 --- /dev/null +++ b/localization/nb_no/misc.yaml @@ -0,0 +1,20 @@ +bot: + name: hva? + infotext: Musikkbot skrevet for bruk på /r/Norge sine Discord Servere. Kildekoden + er åpen! Den kan du finne [HER]({global.vars.repo}) + spectext: |- + **Python:** [{_python_v}](https://www.python.org/) + **Discord.py:** [{_discord_v}](https://github.com/Rapptz/discord.py/tree/rewrite) + **Lavalink:** [{_lavalink_v}](https://github.com/Devoxin/Lavalink.py) + stattext: |- + **Tenarar:** {_guilds} + **Brukere:** {_members} + what: Hva? + how: Hvordan? + how_many: Hvor mange? + how_long: Hvor lenge? + footer_text: Laget av /r/Norge, for /r/Norge +music: + title: Musikkinfo + players: Spillere + listeners: Lyttere diff --git a/localization/nb_no/music.json b/localization/nb_no/music.json deleted file mode 100644 index c0592c9..0000000 --- a/localization/nb_no/music.json +++ /dev/null @@ -1,90 +0,0 @@ -{ - "response": { - "nothing_found": "Kunne ikke finne sangen!", - "not_playing": "Spiller ikke av noe.", - "nothing_playing": "Ingenting spiller av", - "have_to_listen": "Du må høre på botten", - "playlist_enqued": "Spilleliste ble lagt til i køen!", - "requested_by": "Etterspurt av", - "tracks": "spor", - "seek": { - "missing_amount": "Du må spesifisere antall sekunder å spole!", - "track_moved": "Flyttet spor til **{_position}**" - }, - "skip": { - "skipped": "Hoppet over.", - "require_vote": "{_skips} av {_total} hatere må stemme for å hoppe over." - }, - "skip_to": { - "invalid_pos": "Ugyldig køposisjon.", - "exceeds_queue": "Posisjonen overgår køens lengde.", - "skipped_to": "hoppet til `{_title}` i posisjon `{_pos}`" - }, - "stop": "⏹ | Stoppet avspillingen.", - "live": "🔴 DIREKTE", - "now": "Spiller nå", - "queue": { - "empty": "Køen er tom", - "user_empty": "{_user} sin kø er tom", - "length": "**Kø** `{_length} sanger`", - "userqueue": "**{_user} sin kø** `{_length}` sanger", - "globaltrack":"`{_index}.` **[{_title}]({_uri})** _av <@{_user_id}>_\n", - "usertrack":"`{_index}({_globalindex}).` **[{_title}]({_uri})**\n", - "pageindicator":"Viser side {_current}/{_total}" - }, - "my_queue": "Din kø er tom", - "resume": { - "resumed": "⏯ | Avspilling gjenopptatt", - "paused": "⏯ | Pauset avspillingen" - }, - "shuffle": "Køen din har blitt omstokket", - "out_of_range": "Posisjonene må være mellom 1 og {_len}", - "move": { - "not_in_queue": "Sjekk at posisjonen eksisterer i køen din", - "moved_to": "`{_title}` flyttet fra plass `{_from}` til `{_to}` i køen din" - }, - "remove": "**{_title}** ble fjernet fra køen.", - "dj_removed": "**{_title}** som ble lagt til i køen av {_user}, ble fjernet.", - "dj_remove_user": "Fjernet alle sanger lagt til i køen av <@{_id}>.", - "search": "søker", - "results": "Resultater", - "time_expired": "Tid utgått", - "disconnect": { - "not_in_voice": "Du må være koblet til samme talekanal som meg!", - "disconnected": "Frakoblet" - }, - "not_connected": "Du er ikke koblet til en talekanal.", - "volume": { - "set_to": "Volum satt til {_volume}", - "reset": "Volum og equalizer resatt!", - "out_of_range": "Du må sette volumet til et tall mellom 50 og 125" - }, - "boost": { - "on": "Bassforsterkeren er på", - "off": "Bassforsterkeren er av" - }, - "scrub": { - "add": "Legger til kontrollere", - "controls": "{music_controls}", - "_comment_controls": "scrub.controls read from music_controls.txt" - }, - "enqueue": { - "position": "Køposisjon", - "playing_in": "Spiller av om", - "enqueued": "Ny sang lagt til i køen", - "estimated": "estimert", - "toolong": "Sang for lang. `{_length}>{_max}`" - }, - "history": { - "title": "Avspillingshistorikk", - "current": "`Spiller nå` **[{_title}]({_uri})** _av <@{_id}>_", - "track": "`{_index}.` **[{_title}]({_uri})** _av <@{_id}>_", - "previous": "**Tidligere sanger ({_len}):**", - "empty": "Ingen tidligere spilte sanger" - }, - "settings_check": { - "textchannel": "Du kan ikke bruke musikkommandoer i denne kanalen.\nPrøv i: ", - "voicechannel": "Jeg kan bare spille musikk i: " - } - } -} \ No newline at end of file diff --git a/localization/nb_no/music.yaml b/localization/nb_no/music.yaml new file mode 100644 index 0000000..0a7cce3 --- /dev/null +++ b/localization/nb_no/music.yaml @@ -0,0 +1,73 @@ +nothing_found: Kunne ikke finne sangen! +not_playing: Spiller ikke av noe. +nothing_playing: Ingenting spiller av +have_to_listen: Du må høre på botten +playlist_enqued: Spilleliste ble lagt til i køen! +requested_by: Etterspurt av +tracks: spor +seek: + missing_amount: Du må spesifisere antall sekunder å spole! + track_moved: Flyttet spor til **{_position}** +skip: + skipped: Hoppet over. + require_vote: "{_skips} av {_total} hatere må stemme for å hoppe over." +skip_to: + invalid_pos: Ugyldig køposisjon. + exceeds_queue: Posisjonen overgår køens lengde. + skipped_to: hoppet til `{_title}` i posisjon `{_pos}` +stop: "⏹ | Stoppet avspillingen." +live: "\U0001F534 DIREKTE" +now: Spiller nå +queue: + empty: Køen er tom + user_empty: "{_user} sin kø er tom" + length: "**Kø** `{_length} sanger`" + userqueue: "**{_user} sin kø** `{_length}` sanger" + globaltrack: "`{_index}.` **[{_title}]({_uri})** _av <@{_user_id}>_\n" + usertrack: "`{_index}({_globalindex}).` **[{_title}]({_uri})**\n" + pageindicator: Viser side {_current}/{_total} +my_queue: Din kø er tom +resume: + resumed: "⏯ | Avspilling gjenopptatt" + paused: "⏯ | Pauset avspillingen" +shuffle: Køen din har blitt omstokket +out_of_range: Posisjonene må være mellom 1 og {_len} +move: + not_in_queue: Sjekk at posisjonen eksisterer i køen din + moved_to: "`{_title}` flyttet fra plass `{_from}` til `{_to}` i køen din" +remove: "**{_title}** ble fjernet fra køen." +dj_removed: "**{_title}** som ble lagt til i køen av {_user}, ble fjernet." +dj_remove_user: Fjernet alle sanger lagt til i køen av <@{_id}>. +search: søker +results: Resultater +time_expired: Tid utgått +disconnect: + not_in_voice: Du må være koblet til samme talekanal som meg! + disconnected: Frakoblet +not_connected: Du er ikke koblet til en talekanal. +volume: + set_to: Volum satt til {_volume} + reset: Volum og equalizer resatt! + out_of_range: Du må sette volumet til et tall mellom 50 og 125 +boost: + 'on': Bassforsterkeren er på + 'off': Bassforsterkeren er av +scrub: + add: Legger til kontrollere + controls: "{music_controls}" + _comment_controls: scrub.controls read from music_controls.txt +enqueue: + position: Køposisjon + playing_in: Spiller av om + enqueued: Ny sang lagt til i køen + estimated: estimert + toolong: Sang for lang. `{_length}>{_max}` +history: + title: Avspillingshistorikk + current: "`Spiller nå` **[{_title}]({_uri})** _av <@{_id}>_" + track: "`{_index}.` **[{_title}]({_uri})** _av <@{_id}>_" + previous: "**Tidligere sanger ({_len}):**" + empty: Ingen tidligere spilte sanger +settings_check: + textchannel: "Du kan ikke bruke musikkommandoer i denne kanalen.\nPrøv i: " + voicechannel: 'Jeg kan bare spille musikk i: ' diff --git a/localization/nb_no/settings.yaml b/localization/nb_no/settings.yaml new file mode 100644 index 0000000..cd28f11 --- /dev/null +++ b/localization/nb_no/settings.yaml @@ -0,0 +1,12 @@ +current: + title: Innstillinger + description: Gjeldende innstillinger for {_guild} + prefix: Prefiks + locale: Språk + threshold: Stemmegrense + dynamicmax: Dynamisk maksvarighet + maxduration: Maks sanglengde + textchannels: Tekstkanaler + musicchannels: Musikkanaler + listenchannels: '"Kun lyd"-kanaler' + djroles: DJroller diff --git a/localization/nn_no/aliases.yaml b/localization/nn_no/aliases.yaml new file mode 100644 index 0000000..bf694eb --- /dev/null +++ b/localization/nn_no/aliases.yaml @@ -0,0 +1,27 @@ + +# Music aliases +play: [p, spel, s] +now: [np, 'no'] +queue: [q, kø, k] +myqueue: [mq, minkø, mk] +seek: [spol] +skip: [hopp] +skipto: [forceskip, hopptil, tvinghopp] +stop: [stopp] +pause: [resume, gjenoppta] +shuffle: [stokk] +move: [m, flytt, f] +remove: [rem, fjern] +DJremove: [DJfjern, djfjern] +removeuser: [fjernbruker] +search: [søk, finn] +disconnect: [dc, kf, koblefrå] +volume: [vol, volum, lydstyrke, lyd] +normalize: [normal, nl, normaliser] +boost: [bassboost, bassforsterkar] +history: [h, hist, historie] +scrub: [kontroller, konsoll] + +# Other +info: [informasjon] +musicinfo: [musikkinfo, musikkinformasjon] diff --git a/localization/nn_no/commands.json b/localization/nn_no/commands.json deleted file mode 100644 index 1a2c83c..0000000 --- a/localization/nn_no/commands.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "not_available_dm": "Denna kommandoa er ikkje tilgjengeleg i direkte meldingar DMs" -} \ No newline at end of file diff --git a/localization/nn_no/info.json b/localization/nn_no/info.json deleted file mode 100644 index a579c6e..0000000 --- a/localization/nn_no/info.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "bot": { - "name": "Kva?", - "infotext": "Musikkbot skreve til bruk på /r/Norge sine Discord Serverar. Kjeldekoda er ope! Dei kan du finna [HER]({global.vars.repo})", - "spectext": "**Python:** [{_python_v}](https://www.python.org/)\n**Discord.py:** [{_discord_v}](https://github.com/Rapptz/discord.py/tree/rewrite)\n**Lavalink:** [{_lavalink_v}](https://github.com/Devoxin/Lavalink.py)", - "stattext": "**Tenarar:** {_guilds}\n**Brukarar:** {_members}", - "what": "Kva?", - "how": "Korleis?", - "how_many": "Kor mange?", - "how_long": "Kor lengje?", - "footer_text": "Laga av /r/Norge, for /r/Norge" - }, - "music": { - "title": "Musikkinformasjon", - "players": "Spelarar", - "listeners": "Lyttarar" - } -} \ No newline at end of file diff --git a/localization/nn_no/misc.yaml b/localization/nn_no/misc.yaml new file mode 100644 index 0000000..8d2d0f6 --- /dev/null +++ b/localization/nn_no/misc.yaml @@ -0,0 +1,20 @@ +bot: + name: Kva? + infotext: Musikkbot skreve til bruk på /r/Norge sine Discord Serverar. Kjeldekoda + er ope! Dei kan du finna [HER]({global.vars.repo}) + spectext: |- + **Python:** [{_python_v}](https://www.python.org/) + **Discord.py:** [{_discord_v}](https://github.com/Rapptz/discord.py/tree/rewrite) + **Lavalink:** [{_lavalink_v}](https://github.com/Devoxin/Lavalink.py) + stattext: |- + **Tenarar:** {_guilds} + **Brukarar:** {_members} + what: Kva? + how: Korleis? + how_many: Kor mange? + how_long: Kor lengje? + footer_text: Laga av /r/Norge, for /r/Norge +music: + title: Musikkinformasjon + players: Spelarar + listeners: Lyttarar diff --git a/localization/nn_no/music.json b/localization/nn_no/music.json deleted file mode 100644 index 788c1cf..0000000 --- a/localization/nn_no/music.json +++ /dev/null @@ -1,90 +0,0 @@ -{ - "response": { - "nothing_found": "Orsak, kunne ikkje finna songen!", - "not_playing": "Spelar ikkje noko.", - "nothing_playing": "Ingenting speles av", - "have_to_listen": "Dykk må høyra på botten", - "playlist_enqued": "Spellista vart lagt til i køen!", - "requested_by": "Etterspurd av", - "tracks": "spor", - "seek": { - "missing_amount": "Dykk må klargjera antal sekundar å spola!", - "track_moved": "Flytta spor til **{_position}**" - }, - "skip": { - "skipped": "Hoppa over.", - "require_vote": "{_skips} av {_total} hatarar må stemma for å hoppa over." - }, - "skip_to": { - "invalid_pos": "Ugild køposisjon.", - "exceeds_queue": "Posisjona overgår køas lengde.", - "skipped_to": "hoppa til `{_title}` i posisjon `{_pos}`" - }, - "stop": "⏹ | Stoppa avspelinga.", - "live": "🔴 DIREKTE", - "now": "Spelar no", - "queue": { - "empty": "Køa er tom", - "user_empty": "{_user} sin kø er tom", - "length": "**Kø** `{_length} songar`", - "userqueue": "Køen til **{_user}** `{_length}` songar", - "globaltrack":"`{_index}.` **[{_title}]({_uri})** _av <@{_user_id}>_\n", - "usertrack":"`{_index}({_globalindex}).` **[{_title}]({_uri})**\n", - "pageindicator":"Viser side {_current}/{_total}" - }, - "my_queue": "Din kø er tom", - "resume": { - "resumed": "⏯ | Avspeling oppattatt", - "paused": "⏯ | Pauset avspeling" - }, - "shuffle": "Køa di har vorte omstokka", - "out_of_range": "Posisjonane må væra millom 1 og {_len}", - "move": { - "not_in_queue": "Kontrollar at posisjona eksisterar i køa di", - "moved_to": "`{_title}` flytta frå plass `{_from}` til `{_to}` i køa di" - }, - "remove": "**{_title}** vart fjerna frå køa.", - "dj_removed": "**{_title}** som vart lagt til i køa av {_user}, vart fjerna.", - "dj_remove_user": "Fjerna alle songar lagt til i køa av <@{_id}>.", - "search": "søkjar", - "results": "Resultatar", - "time_expired": "Tid utgått", - "disconnect": { - "not_in_voice": "Dykk må være tilkopla same talekanal som meg!", - "disconnected": "Fråkopla" - }, - "not_connected": "Dykk er ikkje kopla til ein talekanal.", - "volume": { - "set_to": "Volum satt til {_volume}", - "reset": "Volum og utjevnar igjensatt!", - "out_of_range": "Du må setja volumet til eit tal mellom femti og hundreogtjuefem" - }, - "boost": { - "on": "Bassforsterkaren er på", - "off": "Bassforsterkaren er av" - }, - "scrub": { - "add": "Legjar til kontrollera", - "controls": "{music_controls}", - "_comment_controls": "scrub.controls read from music_controls.txt" - }, - "enqueue": { - "position": "Køposisjon", - "playing_in": "Spelar av om", - "enqueued": "Ny song lagt til i køa", - "estimated": "estimert", - "toolong": "Song for lang. `{_length}>{_max}`" - }, - "history": { - "title": "Avspelingshistorikk", - "current": "`Spelar no` **[{_title}]({_uri})** _av <@{_id}>_", - "track": "`{_index}.` **[{_title}]({_uri})** _av <@{_id}>_", - "previous": "**Tidlegare songar ({_len}):**", - "empty": "Ingen tidlegare spelte songar" - }, - "settings_check": { - "textchannel": "Du kan ikkje bruke musikkommandoar i denne kanalen.\nPrøv i: ", - "voicechannel": "Eg kan berre spele musikk i: " - } - } -} \ No newline at end of file diff --git a/localization/nn_no/music.yaml b/localization/nn_no/music.yaml new file mode 100644 index 0000000..d30191a --- /dev/null +++ b/localization/nn_no/music.yaml @@ -0,0 +1,73 @@ +nothing_found: Orsak, kunne ikkje finna songen! +not_playing: Spelar ikkje noko. +nothing_playing: Ingenting speles av +have_to_listen: Dykk må høyra på botten +playlist_enqued: Spellista vart lagt til i køen! +requested_by: Etterspurd av +tracks: spor +seek: + missing_amount: Dykk må klargjera antal sekundar å spola! + track_moved: Flytta spor til **{_position}** +skip: + skipped: Hoppa over. + require_vote: "{_skips} av {_total} hatarar må stemma for å hoppa over." +skip_to: + invalid_pos: Ugild køposisjon. + exceeds_queue: Posisjona overgår køens lengde. + skipped_to: hoppa til `{_title}` i posisjon `{_pos}` +stop: "⏹ | Stoppa avspelinga." +live: "\U0001F534 DIREKTE" +now: Spelar no +queue: + empty: Køa er tom + user_empty: "{_user} sin kø er tom" + length: "**Kø** `{_length} songar`" + userqueue: Køen til **{_user}** `{_length}` songar + globaltrack: "`{_index}.` **[{_title}]({_uri})** _av <@{_user_id}>_\n" + usertrack: "`{_index}({_globalindex}).` **[{_title}]({_uri})**\n" + pageindicator: Viser side {_current}/{_total} +my_queue: Din kø er tom +resume: + resumed: "⏯ | Avspeling oppattatt" + paused: "⏯ | Pauset avspeling" +shuffle: Køa di har vorte omstokka +out_of_range: Posisjonane må væra millom 1 og {_len} +move: + not_in_queue: Kontrollar at posisjona eksisterar i køen din + moved_to: "`{_title}` flytta frå plass `{_from}` til `{_to}` i køen din" +remove: "**{_title}** vart fjerna frå køen." +dj_removed: "**{_title}** som vart lagt til i køen av {_user}, vart fjerna." +dj_remove_user: Fjerna alle songar lagt til i køen av <@{_id}>. +search: søkjar +results: Resultatar +time_expired: Tid utgått +disconnect: + not_in_voice: Dykk må være tilkopla same talekanal som meg! + disconnected: Fråkopla +not_connected: Dykk er ikkje kopla til ein talekanal. +volume: + set_to: Volum satt til {_volume} + reset: Volum og utjevnar igjensatt! + out_of_range: Du må setja volumet til eit tal mellom femti og hundreogtjuefem +boost: + 'on': Bassforsterkaren er på + 'off': Bassforsterkaren er av +scrub: + add: Legjar til kontrollera + controls: "{music_controls}" + _comment_controls: scrub.controls read from music_controls.txt +enqueue: + position: Køposisjon + playing_in: Spelar av om + enqueued: Ny song lagt til i køen + estimated: estimert + toolong: Song for lang. `{_length}>{_max}` +history: + title: Avspelingshistorikk + current: "`Spelar no` **[{_title}]({_uri})** _av <@{_id}>_" + track: "`{_index}.` **[{_title}]({_uri})** _av <@{_id}>_" + previous: "**Tidlegare songar ({_len}):**" + empty: Ingen tidlegare spelte songar +settings_check: + textchannel: "Du kan ikkje bruke musikkommandoar i denne kanalen.\nPrøv i: " + voicechannel: 'Eg kan berre spele musikk i: ' diff --git a/localization/nn_no/settings.yaml b/localization/nn_no/settings.yaml new file mode 100644 index 0000000..056364b --- /dev/null +++ b/localization/nn_no/settings.yaml @@ -0,0 +1,12 @@ +current: + title: Innstillingar + description: Gjeldande innstillingar for {_guild} + prefix: Prefiks + locale: Språk + threshold: Stemmegrense + dynamicmax: Dynamisk maksvarigskap + maxduration: Maks songlengde + textchannels: Tekstkanalar + musicchannels: Musikkanalar + listenchannels: '"Kun lyd"-kanalar' + djroles: DJroller