diff --git a/.vscode/settings.json b/.vscode/settings.json index 2b7e46d..de288e1 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,3 @@ { - "python.formatting.provider": "yapf" + "python.formatting.provider": "black" } \ No newline at end of file diff --git a/Pipfile b/Pipfile deleted file mode 100644 index 1abb85e..0000000 --- a/Pipfile +++ /dev/null @@ -1,17 +0,0 @@ -[[source]] -name = "pypi" -url = "https://pypi.org/simple" -verify_ssl = true - -[dev-packages] -yapf = "*" - -[packages] -humanize = "*" -"discord.py" = {extras = ["voice"], version = "*"} -sqlalchemy = "*" -aiomysql = "*" -lavalink = "*" - -[requires] -python_version = "3.8" diff --git a/README.md b/README.md index 7c14672..883d978 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,2 @@ -# CloudKid -CloudKid Music Bot +# ChristmasBot +Christmas Music Bot diff --git a/bot.py b/bot.py index 7b0478e..309505d 100644 --- a/bot.py +++ b/bot.py @@ -1,68 +1,55 @@ import discord +from discord.colour import Color from discord.ext import commands import sys import json import asyncio -import sqlalchemy as sa -from aiomysql.sa import Engine -from tables import get_tables -from typing import Sequence -from aiomysql.sa import create_engine -from discord import ActivityType -from discord import Status -from typing import Mapping +from typing import Any, Dict, Sequence +from discord import Message -class Database(): - def __init__(self, engine: Engine): +class Database: + def __init__(self): self.loop = asyncio.get_event_loop() - self.metadata = sa.MetaData() - self.tables = get_tables(self.metadata) - self.engine = engine - - @staticmethod - async def init(dbconf: dict): - engine = await create_engine(**dbconf, autocommit=True) - return Database(engine) -class CloudKid(commands.Bot): +class ChristmasBot(commands.Bot): INITIAL_EXTENSIONS = [ - 'cogs.owner', 'cogs.settings', 'cogs.information', 'cogs.music' + "cogs.owner", + "cogs.settings", + "cogs.information", + "cogs.music", ] - def __init__(self, config: dict): + def __init__(self, config: Dict[Any, Any]): intents: discord.Intents = discord.Intents.none() intents.voice_states = True intents.guild_messages = True + intents.guilds = True + intents.messages = True self.config = config - self.colors = self.process_colors(config["colors"]) + self.colors: Dict[str, Color] = self.process_colours(config.get("colors", [])) - super().__init__(command_prefix=self.prefix_callable, - description='CloudKid Radio', - case_insensitive=True, - fetch_offline_members=False, - intents=intents) + super().__init__( + command_prefix=self.prefix_callable, + description=self.config["info"].get("description"), + case_insensitive=False, + fetch_offline_members=False, + intents=intents, + slash_command_guilds=[227431704426446848], + ) - self.database = None - self.loop.create_task(self.async_init()) - - async def async_init(self): - dbconf = self.config["database"] - self.database = await Database.init(dbconf) - - async def prefix_callable(self, _, msg): + async def prefix_callable(self, _, msg: Message): return commands.when_mentioned_or(*self.config["prefixes"])(self, msg) async def load_cogs(self, cog_names: Sequence[str]): for cog in cog_names: try: self.load_extension(cog) - print(f'Succesfully loaded extension {cog}.') + print(f"Succesfully loaded extension {cog}.") except Exception as e: - print(f'Failed to load extension {cog}.\n\t{e}', - file=sys.stderr) + print(f"Failed to load extension {cog}.\n\t{e}", file=sys.stderr) async def on_ready(self): print(f"Logged in as: {self.user}") @@ -74,14 +61,15 @@ class CloudKid(commands.Bot): await self.change_presence(activity=discord.Game(text)) await self.load_cogs(self.INITIAL_EXTENSIONS) - def process_colors( - self, colors: Mapping[str, str]) -> Mapping[str, discord.Color]: + def process_colours(self, colors: Dict[str, str]) -> Dict[str, Color]: + colour_dict: Dict[str, Color] = {} for name, color in colors.items(): - colors[name] = discord.Color(int(color, 16)) - return colors + colour_dict[name] = Color(int(color, 16)) + return colour_dict if __name__ == "__main__": - config = json.load(open('config.json', 'r', encoding='utf-8')) + config = json.load(open("config.json", "r", encoding="utf-8")) token = config.pop("token") - CloudKid(config).run(token, reconnect=True) + bot = ChristmasBot(config) + bot.run(token, reconnect=True) diff --git a/cogs/information.py b/cogs/information.py index abd1b83..1d45160 100644 --- a/cogs/information.py +++ b/cogs/information.py @@ -2,12 +2,12 @@ import discord from discord.ext import tasks, commands import time +from discord.ext.commands import Context import humanize import datetime import lavalink -from utils import metadata +from utils.EmbedGenerator import EmbedGenerator from utils.paginator import HelpPaginator -from discord import Status class InformationCog(commands.Cog, name="Information"): @@ -21,49 +21,49 @@ class InformationCog(commands.Cog, name="Information"): await self.bot.wait_until_ready() title = "ck!connect | ck!help" if self.is_help_msg: - title = (await metadata.fetch_metadata())["name"] + title = "Tfoe broer" self.is_help_msg = not self.is_help_msg await self.bot.change_presence(activity=discord.Game(title)) @commands.cooldown(rate=1, per=5, type=commands.BucketType.user) - @commands.command(description='PONG!', aliases=['pong']) - async def ping(self, ctx): + @commands.command(description="PONG!", aliases=["pong"]) + async def ping(self, ctx: Context): """Test the latency""" - avatar = ctx.author.avatar_url_as(static_format='jpeg') - emoji = discord.utils.get(ctx.bot.emojis, name='loading') + avatar = ctx.author.avatar.with_static_format("jpeg") + emoji = discord.utils.get(ctx.bot.emojis, name="loading") start = time.monotonic() - msg = await ctx.send(embed=discord.Embed( - description=f'{emoji} Calculating ping')) + msg = await ctx.send( + embed=discord.Embed(description=f"{emoji} Calculating ping") + ) millis = (time.monotonic() - start) * 1000 heartbeat = ctx.bot.latency * 1000 embed = discord.Embed(color=discord.Color.blue()) - embed.add_field(name=':heartbeat: Heartbeat', - value=f'`{heartbeat:,.2f}ms`', - inline=True) - embed.add_field(name=':file_cabinet: ACK', - value=f'`{millis:,.2f}ms`', - inline=True) - embed.set_footer(text=f"Requested by: {ctx.author}", - icon_url=f"{avatar}") + embed.add_field( + name=":heartbeat: Heartbeat", value=f"`{heartbeat:,.2f}ms`", inline=True + ) + embed.add_field( + name=":file_cabinet: ACK", value=f"`{millis:,.2f}ms`", inline=True + ) + embed.set_footer(text=f"Requested by: {ctx.author}", icon_url=f"{avatar}") await msg.edit(embed=embed) - @commands.command(name='invite') + @commands.command( + name="invite", description="Gets the invite link!", slash_commands=True + ) @commands.cooldown(rate=1, per=5, type=commands.BucketType.user) - async def invite(self, ctx): + async def invite(self, ctx: Context): """Gets the invite link!""" - color = self.bot.colors["embed"] - avatar = ctx.author.avatar_url_as(static_format='jpeg') - client_id = ctx.bot.user.id - link = f"https://discord.com/oauth2/authorize?scope=bot&client_id={client_id}&permissions=70642768" - embed = discord.Embed(color=color) - embed.add_field(name='Add our bot to your server:', value=link) - embed.set_footer(text=f"Requested by: {ctx.author}", - icon_url=f"{avatar}") + link = f"https://discord.com/oauth2/authorize?client_id=643555373814382593&permissions=3230720&scope=bot%20applications.commands" + embed = await EmbedGenerator.Message(ctx, "Add our bot to your server:", link) await ctx.send(embed=embed) + @commands.command( + name="wlinfo", + description="Retrieve various Node/Server/Player information.", + slash_commands=True, + ) @commands.cooldown(rate=1, per=5, type=commands.BucketType.user) - @commands.command() - async def wlinfo(self, ctx): + async def wlinfo(self, ctx: Context): """Retrieve various Node/Server/Player information.""" player = self.bot.lavalink.player_manager.get(ctx.guild.id) node = player.node @@ -73,28 +73,29 @@ class InformationCog(commands.Cog, name="Information"): free = humanize.naturalsize(node.stats.memory_free) cpu = node.stats.cpu_cores - fmt = f'**WaveLink:** `{lavalink.__version__}`\n\n' \ - f'Connected to `{len(self.bot.lavalink.nodes)}` nodes.\n' \ - f'Best available Node `{self.bot.lavalink.get_best_node().__repr__()}`\n' \ - f'`{len(self.bot.lavalink.players)}` players are distributed on nodes.\n' \ - f'`{node.stats.players}` players are distributed on server.\n' \ - f'`{node.stats.playing_players}` players are playing on server.\n\n' \ - f'Server Memory: `{used}/{total}` | `({free} free)`\n' \ - f'Server CPU: `{cpu}`\n\n' \ - f'Server Uptime: `{datetime.timedelta(milliseconds=node.stats.uptime)}`' + fmt = ( + f"**WaveLink:** `{lavalink.__version__}`\n\n" + f"Connected to `{len(self.bot.lavalink.nodes)}` nodes.\n" + f"Best available Node `{self.bot.lavalink.get_best_node().__repr__()}`\n" + f"`{len(self.bot.lavalink.players)}` players are distributed on nodes.\n" + f"`{node.stats.players}` players are distributed on server.\n" + f"`{node.stats.playing_players}` players are playing on server.\n\n" + f"Server Memory: `{used}/{total}` | `({free} free)`\n" + f"Server CPU: `{cpu}`\n\n" + f"Server Uptime: `{datetime.timedelta(milliseconds=node.stats.uptime)}`" + ) await ctx.send(fmt) - @commands.command(name="help", aliases=["about", "info"]) + @commands.command(name="help", aliases=["about", "info"], slash_command=True) @commands.cooldown(1, 1, commands.BucketType.user) - async def about(self, ctx, command: str = None): + async def about(self, ctx: Context): """Exobot command list""" - if command: + if None: entity = self.bot.get_cog(command) or self.bot.get_command(command) if entity is None: - clean = command.replace('@', '@\u200b') - return await ctx.send( - f'Command or category "{clean}" not found.') + clean = command.replace("@", "@\u200b") + return await ctx.send(f'Command or category "{clean}" not found.') elif isinstance(entity, discord.ext.commands.Command): p = await HelpPaginator.from_command(ctx, entity) else: @@ -111,7 +112,7 @@ class InformationCog(commands.Cog, name="Information"): display_cogs = { "Information": ":information_source:", "Music": ":musical_note:", - "Settings": ":gear:" + "Settings": ":gear:", } for cog_name, cog_icon in display_cogs.items(): @@ -119,13 +120,11 @@ class InformationCog(commands.Cog, name="Information"): if not cog: continue cogname_str = f"{cog_icon} {cog_name}" - commands = [ - f"`{cmd.name}`" for cmd in cog.get_commands() if not cmd.hidden - ] + commands = [f"`{cmd.name}`" for cmd in cog.get_commands() if not cmd.hidden] commands_str = ", ".join(commands) embed.add_field(name=cogname_str, value=commands_str, inline=False) - avatar = ctx.author.avatar_url_as(static_format='jpeg') + avatar = ctx.author.avatar.with_static_format("jpeg") embed.set_footer(text=f"Requested by: {ctx.author}", icon_url=avatar) await ctx.send(embed=embed) diff --git a/cogs/music.py b/cogs/music.py index dbe7a3e..855a346 100644 --- a/cogs/music.py +++ b/cogs/music.py @@ -1,13 +1,10 @@ import discord +from discord import channel from discord.ext import commands -import time import lavalink -import re -import random import asyncio -from utils.database import AutoJoin -from utils import metadata +from bot import ChristmasBot from utils.EmbedGenerator import EmbedGenerator @@ -16,12 +13,12 @@ class MusicCog(commands.Cog, name="Music"): self.bot = bot self.stream = "https://azuracast.exobot.site/radio/8000/radio.opus" - if not hasattr(bot, 'lavalink'): + if not hasattr(bot, "lavalink"): bot.lavalink = lavalink.Client(bot.user.id) - bot.lavalink.add_node('de-1.rivalmc.net', 2333, '12345', 'eu', - 'poggers') - bot.add_listener(self.bot.lavalink.voice_update_handler, - 'on_socket_response') + bot.lavalink.add_node("de-1.rivalmc.net", 2333, "12345", "eu", "poggers") + bot.add_listener( + self.bot.lavalink.voice_update_handler, "on_socket_response" + ) lavalink.add_event_hook(self.track_hook) @@ -29,58 +26,60 @@ class MusicCog(commands.Cog, name="Music"): async def async_init(self): await self.bot.wait_until_ready() - channels = await AutoJoin.get_channels(self.bot) + + channels = [] # We startup to fast #NOTPOGGERS await asyncio.sleep(5) - for x in channels: - guild = self.bot.get_guild(x[0]) - player = self.bot.lavalink.player_manager.create(x[0], - endpoint=str( - guild.region)) + for channel in channels: + guild = self.bot.get_guild(channel[0]) + player = self.bot.lavalink.player_manager.create( + channel[0], endpoint=str(guild.region) + ) track = await player.node.get_tracks(self.stream) if not player.is_playing: await player.play(track["tracks"][0]) - await self.connect_to(x[0], x[1]) + await self.connect_to(channel[0], channel[1]) def cog_unload(self): - """ Cog unload handler. This removes any event hooks that were registered. """ + """Cog unload handler. This removes any event hooks that were registered.""" self.bot.lavalink._event_hooks.clear() async def cog_before_invoke(self, ctx): - """ Command before-invoke handler. """ + """Command before-invoke handler.""" guild_check = ctx.guild is not None if guild_check: await self.ensure_voice(ctx) return guild_check async def ensure_voice(self, ctx): - """ This check ensures that the bot and command author are in the same voicechannel. """ - player = self.bot.lavalink.player_manager.create(ctx.guild.id, - endpoint=str( - ctx.guild.region)) - should_connect = ctx.command.name in ('connect', ) + """This check ensures that the bot and command author are in the same voicechannel.""" + player = self.bot.lavalink.player_manager.create( + ctx.guild.id, endpoint=str(ctx.guild.region) + ) + should_connect = ctx.command.name in ("connect",) if not ctx.author.voice or not ctx.author.voice.channel: - raise commands.CommandInvokeError('Join a voicechannel first.') + raise commands.CommandInvokeError("Join a voicechannel first.") if not player.is_connected: if not should_connect: - raise commands.CommandInvokeError('Not connected.') + raise commands.CommandInvokeError("Not connected.") permissions = ctx.author.voice.channel.permissions_for(ctx.me) - if not permissions.connect or not permissions.speak: # Check user limit too? + if ( + not permissions.connect or not permissions.speak + ): # Check user limit too? raise commands.CommandInvokeError( - 'I need the `CONNECT` and `SPEAK` permissions.') + "I need the `CONNECT` and `SPEAK` permissions." + ) - player.store('channel', ctx.channel.id) - await self.connect_to(ctx.guild.id, - str(ctx.author.voice.channel.id)) + player.store("channel", ctx.channel.id) + await self.connect_to(ctx.guild.id, str(ctx.author.voice.channel.id)) else: if int(player.channel_id) != ctx.author.voice.channel.id: - raise commands.CommandInvokeError( - 'You need to be in my voicechannel.') + raise commands.CommandInvokeError("You need to be in my voicechannel.") async def track_hook(self, event): if isinstance(event, lavalink.events.QueueEndEvent): @@ -88,60 +87,58 @@ class MusicCog(commands.Cog, name="Music"): await self.connect_to(guild_id, None) async def connect_to(self, guild_id: int, channel_id: str): - """ Connects to the given voicechannel ID. A channel_id of `None` means disconnect. """ + """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='connect') + @commands.command(name="connect") async def connect(self, ctx): """Starts vibing.""" player = self.bot.lavalink.player_manager.get(ctx.guild.id) results = await player.node.get_tracks(self.stream) - if not results or not results['tracks']: - return await ctx.send('Nothing found!') + if not results or not results["tracks"]: + return await ctx.send("Nothing found!") - if results['loadType'] == 'PLAYLIST_LOADED': - tracks = results['tracks'] + if results["loadType"] == "PLAYLIST_LOADED": + tracks = results["tracks"] for track in tracks: player.add(requester=ctx.author.id, track=track) else: - track = results['tracks'][0] - track = lavalink.models.AudioTrack(track, - ctx.author.id, - recommended=True) + track = results["tracks"][0] + track = lavalink.models.AudioTrack(track, ctx.author.id, recommended=True) player.add(requester=ctx.author.id, track=track) if not player.is_playing: await player.play() - @commands.command(aliases=['dc']) + @commands.command(aliases=["dc"]) async def disconnect(self, ctx): - """ Disconnects the player from the voice channel and clears its queue. """ + """Disconnects the player from the voice channel and clears its queue.""" player = self.bot.lavalink.player_manager.get(ctx.guild.id) if not player.is_connected: - return await ctx.send('Not connected.') + return await ctx.send("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('You\'re not in my voicechannel!') + player.is_connected + and ctx.author.voice.channel.id != int(player.channel_id) + ): + return await ctx.send("You're not in my voicechannel!") player.queue.clear() await player.stop() await self.connect_to(ctx.guild.id, None) - @commands.command(name='now', aliases=['playing']) + @commands.command(name="now", aliases=["playing"]) async def now_playing(self, ctx): """Stop and disconnect the player and controller.""" - np = await metadata.fetch_metadata() em = discord.Embed(color=self.bot.colors["embed"]) - em.set_thumbnail(url=np["thumbnail"]) - em.add_field(name="Currently playing:", value=np["name"]) + # em.set_thumbnail(url=np["thumbnail"]) + em.add_field(name="Currently playing:", value="Some song") await EmbedGenerator.SendWithFooter(ctx, em) -def setup(bot): +def setup(bot: ChristmasBot): bot.add_cog(MusicCog(bot)) diff --git a/cogs/owner.py b/cogs/owner.py index e7b0322..9b20fda 100644 --- a/cogs/owner.py +++ b/cogs/owner.py @@ -11,32 +11,36 @@ from io import BytesIO from platform import python_version from contextlib import redirect_stdout +from discord.ext.commands.context import Context + +from bot import ChristmasBot + class OwnerCog(commands.Cog): - def __init__(self, bot): + def __init__(self, bot: ChristmasBot): self.bot = bot self._last_result = None @staticmethod def cleanup_code(content): - if content.startswith('```') and content.endswith('```'): - return '\n'.join(content.split('\n')[1:-1]) - return content.strip('` \n') + if content.startswith("```") and content.endswith("```"): + return "\n".join(content.split("\n")[1:-1]) + return content.strip("` \n") # Hidden means it won't show up on the default help. - @commands.command(name='load', hidden=True) + @commands.command(name="load", hidden=True) @commands.is_owner() - async def _cog_load(self, ctx, *, cog: str): + async def _cog_load(self, ctx: Context, *, cog: str): """Command which Loads a Module.""" try: self.bot.load_extension(cog) except Exception as e: - await ctx.send(f'**`ERROR:`** {type(e).__name__} - {e}') + await ctx.send(f"**`ERROR:`** {type(e).__name__} - {e}") else: - await ctx.send('**`SUCCESS`**') + await ctx.send("**`SUCCESS`**") - @commands.command(name='unload', hidden=True) + @commands.command(name="unload", hidden=True) @commands.is_owner() async def _cog_unload(self, ctx, *, cog: str): """Command which Unloads a Module.""" @@ -44,11 +48,11 @@ class OwnerCog(commands.Cog): try: self.bot.unload_extension(cog) except Exception as e: - await ctx.send(f'**`ERROR:`** {type(e).__name__} - {e}') + await ctx.send(f"**`ERROR:`** {type(e).__name__} - {e}") else: - await ctx.send('**`SUCCESS`**') + await ctx.send("**`SUCCESS`**") - @commands.command(name='reload', hidden=True) + @commands.command(name="reload", hidden=True) @commands.is_owner() async def _cog_reload(self, ctx, *, cog: str): """Command which Reloads a Module.""" @@ -57,35 +61,29 @@ class OwnerCog(commands.Cog): self.bot.unload_extension(cog) self.bot.load_extension(cog) except Exception as e: - await ctx.send(f'**`ERROR:`** {type(e).__name__} - {e}') + await ctx.send(f"**`ERROR:`** {type(e).__name__} - {e}") else: - await ctx.send('**`SUCCESS`**') + await ctx.send("**`SUCCESS`**") - @commands.command(name='shutdown', hidden=True) + @commands.command(name="shutdown", hidden=True) @commands.is_owner() async def shutdown(self, ctx): """Command which shutdowns the bot.""" await ctx.bot.logout() @commands.is_owner() - @commands.command(pass_context=True, - hidden=True, - name='eval', - aliases=['evaluate']) + @commands.command(pass_context=True, hidden=True, name="eval", aliases=["evaluate"]) async def _eval(self, ctx, *, body: str): env = { - 'bot': self.bot, - 'ctx': ctx, - 'channel': ctx.channel, - 'author': ctx.author, - 'guild': ctx.guild, - 'message': ctx.message, - '_': self._last_result + "bot": self.bot, + "ctx": ctx, + "channel": ctx.channel, + "author": ctx.author, + "guild": ctx.guild, + "message": ctx.message, + "_": self._last_result, } - if "import os" in body and ctx.author.id == 190875175460405249: - return await ctx.send("Ah ah ah, you didn't say the magic word.") - env.update(globals()) body = self.cleanup_code(body) @@ -98,26 +96,34 @@ class OwnerCog(commands.Cog): exec(to_compile, env) except Exception as e: # await ctx.message.add_reaction('naokoerror:447495055603662849') - fooem = discord.Embed(color=0xff0000) - fooem.add_field(name="Code evaluation was not successful.", - value=f'```\n{e.__class__.__name__}: {e}\n```') - fooem.set_footer(text=f"Evaluated using Python {python_version()}", - icon_url="http://i.imgur.com/9EftiVK.png") + fooem = discord.Embed(color=0xFF0000) + fooem.add_field( + name="Code evaluation was not successful.", + value=f"```\n{e.__class__.__name__}: {e}\n```", + ) + fooem.set_footer( + text=f"Evaluated using Python {python_version()}", + icon_url="http://i.imgur.com/9EftiVK.png", + ) await ctx.send(embed=fooem) # await ctx.message.remove_reaction('a:loading:452489773396000778', member=ctx.me) - func = env['func'] + func = env["func"] try: with redirect_stdout(stdout): ret = await func() except Exception as e: value = stdout.getvalue() # await ctx.message.add_reaction('naokoerror:447495055603662849') - fooem = discord.Embed(color=0xff0000) - fooem.add_field(name="Code evaluation was not successful.", - value=f'```\n{value}{traceback.format_exc()}\n```') - fooem.set_footer(text=f"Evaluated using Python {python_version()}", - icon_url="http://i.imgur.com/9EftiVK.png") + fooem = discord.Embed(color=0xFF0000) + fooem.add_field( + name="Code evaluation was not successful.", + value=f"```\n{value}{traceback.format_exc()}\n```", + ) + fooem.set_footer( + text=f"Evaluated using Python {python_version()}", + icon_url="http://i.imgur.com/9EftiVK.png", + ) await ctx.send(embed=fooem) try: # await ctx.message.remove_reaction('a:loading:452489773396000778', member=ctx.me) @@ -129,7 +135,8 @@ class OwnerCog(commands.Cog): value = stdout.getvalue() try: await ctx.message.remove_reaction( - 'a:loading:452489773396000778', member=ctx.me) + "a:loading:452489773396000778", member=ctx.me + ) # await ctx.message.add_reaction('naokotick:447494238872141827') except Exception: pass @@ -137,37 +144,42 @@ class OwnerCog(commands.Cog): if ret is None: if value: sfooem = discord.Embed(color=0x170041) - sfooem.add_field(name="Code evaluation was successful!", - value=f'```\n{value}\n```') + sfooem.add_field( + name="Code evaluation was successful!", + value=f"```\n{value}\n```", + ) sfooem.set_footer( text=f"Evaluated using Python {python_version()}", - icon_url="http://i.imgur.com/9EftiVK.png") + icon_url="http://i.imgur.com/9EftiVK.png", + ) await ctx.send(embed=sfooem) else: self._last_result = ret ssfooem = discord.Embed(color=0x170041) - ssfooem.add_field(name="Code evaluation was successful!", - value=f'```\n{value}{ret}\n```') + ssfooem.add_field( + name="Code evaluation was successful!", + value=f"```\n{value}{ret}\n```", + ) ssfooem.set_footer( text=f"Evaluated using Python {python_version()}", - icon_url="http://i.imgur.com/9EftiVK.png") + icon_url="http://i.imgur.com/9EftiVK.png", + ) await ctx.send(embed=ssfooem) @commands.is_owner() - @commands.command(hidden=True, aliases=['exec']) + @commands.command(hidden=True, aliases=["exec"]) async def execute(self, ctx, *, text: str): - """ Do a shell command. """ + """Do a shell command.""" message = await ctx.send(f"Loading...") - proc = await asyncio.create_subprocess_shell(text, - stdin=None, - stderr=PIPE, - stdout=PIPE) - out = (await proc.stdout.read()).decode('utf-8').strip() - err = (await proc.stderr.read()).decode('utf-8').strip() + proc = await asyncio.create_subprocess_shell( + text, stdin=None, stderr=PIPE, stdout=PIPE + ) + out = (await proc.stdout.read()).decode("utf-8").strip() + err = (await proc.stderr.read()).decode("utf-8").strip() if not out and not err: await message.delete() - return await ctx.message.add_reaction('👌') + return await ctx.message.add_reaction("👌") content = "" @@ -178,12 +190,12 @@ class OwnerCog(commands.Cog): if len(content) > 1500: try: - data = BytesIO(content.encode('utf-8')) + data = BytesIO(content.encode("utf-8")) await message.delete() - await ctx.send(content=f"The result was a bit too long..", - file=discord.File( - data, - filename=f"result_{int(time.time())}.txt")) + await ctx.send( + content=f"The result was a bit too long..", + file=discord.File(data, filename=f"result_{int(time.time())}.txt"), + ) except asyncio.TimeoutError as e: await message.delete() return await ctx.send(e) @@ -191,5 +203,5 @@ class OwnerCog(commands.Cog): await message.edit(content=f"```fix\n{content}\n```") -def setup(bot): +def setup(bot: ChristmasBot): bot.add_cog(OwnerCog(bot)) diff --git a/cogs/settings.py b/cogs/settings.py index c1cfec1..eae8520 100644 --- a/cogs/settings.py +++ b/cogs/settings.py @@ -1,33 +1,26 @@ from discord.ext import commands -from utils.database import AutoJoin -import discord from utils.EmbedGenerator import EmbedGenerator +from bot import ChristmasBot from discord.ext.commands import Context class SettingsCog(commands.Cog, name="Settings"): - def __init__(self, bot): + def __init__(self, bot: ChristmasBot): self.bot = bot - @commands.command(name='placeholder', hidden=True) - @commands.is_owner() - async def placeholder(self, ctx): - """Placeholder Command""" - - await ctx.send("Pong!") - @commands.group(aliases=["aj"], invoke_without_command=True) @commands.cooldown(rate=1, per=5, type=commands.BucketType.user) - async def autojoin(self, ctx): + async def autojoin(self, ctx: Context): await EmbedGenerator.Message( - ctx, "Autojoin", - f"Usage:\n\n`{ctx.prefix}autojoin set`\n`{ctx.prefix}autojoin unset`" + ctx, + "Autojoin", + f"Usage:\n\n`{ctx.prefix}autojoin set`\n`{ctx.prefix}autojoin unset`", ) @autojoin.command(name="set") @commands.has_permissions(manage_channels=True) @commands.cooldown(rate=1, per=5, type=commands.BucketType.user) - async def autojoin_set(self, ctx): + async def autojoin_set(self, ctx: Context): vc = ctx.author.voice.channel await AutoJoin.update_channel(self.bot, ctx.guild.id, vc.id) await EmbedGenerator.Message(ctx, "Autojoin", "`enabled`") @@ -35,11 +28,11 @@ class SettingsCog(commands.Cog, name="Settings"): @autojoin.command(name="unset") @commands.has_permissions(manage_channels=True) @commands.cooldown(rate=1, per=5, type=commands.BucketType.user) - async def autojoin_del(self, ctx): + async def autojoin_del(self, ctx: Context): vc = ctx.author.voice.channel await AutoJoin.del_channel(self.bot, ctx.guild.id) await EmbedGenerator.Message(ctx, "Autojoin", "`disabled`") -def setup(bot): +def setup(bot: ChristmasBot): bot.add_cog(SettingsCog(bot)) diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..3557484 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,535 @@ +[[package]] +name = "aiohttp" +version = "3.6.3" +description = "Async http client/server framework (asyncio)" +category = "main" +optional = false +python-versions = ">=3.5.3" + +[package.dependencies] +async-timeout = ">=3.0,<4.0" +attrs = ">=17.3.0" +chardet = ">=2.0,<4.0" +multidict = ">=4.5,<5.0" +yarl = ">=1.0,<1.6.0" + +[package.extras] +speedups = ["aiodns", "brotlipy", "cchardet"] + +[[package]] +name = "async-timeout" +version = "3.0.1" +description = "Timeout context manager for asyncio programs" +category = "main" +optional = false +python-versions = ">=3.5.3" + +[[package]] +name = "attrs" +version = "21.2.0" +description = "Classes Without Boilerplate" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[package.extras] +dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit"] +docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] +tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface"] +tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins"] + +[[package]] +name = "black" +version = "21.9b0" +description = "The uncompromising code formatter." +category = "dev" +optional = false +python-versions = ">=3.6.2" + +[package.dependencies] +click = ">=7.1.2" +mypy-extensions = ">=0.4.3" +pathspec = ">=0.9.0,<1" +platformdirs = ">=2" +regex = ">=2020.1.8" +tomli = ">=0.2.6,<2.0.0" +typing-extensions = [ + {version = ">=3.10.0.0", markers = "python_version < \"3.10\""}, + {version = "!=3.10.0.1", markers = "python_version >= \"3.10\""}, +] + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.6.0)", "aiohttp-cors (>=0.4.0)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +python2 = ["typed-ast (>=1.4.2)"] +uvloop = ["uvloop (>=0.15.2)"] + +[[package]] +name = "cffi" +version = "1.15.0" +description = "Foreign Function Interface for Python calling C code." +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +pycparser = "*" + +[[package]] +name = "chardet" +version = "3.0.4" +description = "Universal encoding detector for Python 2 and 3" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "click" +version = "8.0.3" +description = "Composable command line interface toolkit" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.4" +description = "Cross-platform colored terminal text." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "discord.py" +version = "2.0.0a3661+g96153bb1" +description = "A Python wrapper for the Discord API" +category = "main" +optional = false +python-versions = ">=3.8.0" +develop = false + +[package.dependencies] +aiohttp = ">=3.6.0,<3.8.0" +orjson = {version = ">=3.5.4", optional = true, markers = "extra == \"speed\""} +PyNaCl = {version = ">=1.3.0,<1.5", optional = true, markers = "extra == \"voice\""} + +[package.extras] +docs = ["sphinx (==4.0.2)", "sphinxcontrib-trio (==1.1.2)", "sphinxcontrib-websupport"] +speed = ["orjson (>=3.5.4)"] +voice = ["PyNaCl (>=1.3.0,<1.5)"] + +[package.source] +type = "git" +url = "https://github.com/iDevision/enhanced-discord.py" +reference = "2.0" +resolved_reference = "96153bb177d5a9e0be81f8c05e57d8777f4cd926" + +[[package]] +name = "humanize" +version = "3.12.0" +description = "Python humanize utilities" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.extras] +tests = ["freezegun", "pytest", "pytest-cov"] + +[[package]] +name = "idna" +version = "3.3" +description = "Internationalized Domain Names in Applications (IDNA)" +category = "main" +optional = false +python-versions = ">=3.5" + +[[package]] +name = "lavalink" +version = "3.1.4" +description = "A lavalink interface built for discord.py" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +aiohttp = ">=3.6.0,<3.7.0" + +[package.extras] +development = ["pylint", "flake8"] +docs = ["sphinx", "pygments", "guzzle-sphinx-theme"] + +[[package]] +name = "multidict" +version = "4.7.6" +description = "multidict implementation" +category = "main" +optional = false +python-versions = ">=3.5" + +[[package]] +name = "mypy-extensions" +version = "0.4.3" +description = "Experimental type system extensions for programs checked with the mypy typechecker." +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "orjson" +version = "3.6.4" +description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" +category = "main" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "pathspec" +version = "0.9.0" +description = "Utility library for gitignore style pattern matching of file paths." +category = "dev" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" + +[[package]] +name = "platformdirs" +version = "2.4.0" +description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.extras] +docs = ["Sphinx (>=4)", "furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)"] +test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"] + +[[package]] +name = "pycparser" +version = "2.20" +description = "C parser in Python" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "pynacl" +version = "1.4.0" +description = "Python binding to the Networking and Cryptography (NaCl) library" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[package.dependencies] +cffi = ">=1.4.1" +six = "*" + +[package.extras] +docs = ["sphinx (>=1.6.5)", "sphinx-rtd-theme"] +tests = ["pytest (>=3.2.1,!=3.3.0)", "hypothesis (>=3.27.0)"] + +[[package]] +name = "regex" +version = "2021.10.23" +description = "Alternative regular expression module, to replace re." +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" + +[[package]] +name = "tomli" +version = "1.2.2" +description = "A lil' TOML parser" +category = "dev" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "typing-extensions" +version = "3.10.0.2" +description = "Backported and Experimental Type Hints for Python 3.5+" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "yarl" +version = "1.5.1" +description = "Yet another URL library" +category = "main" +optional = false +python-versions = ">=3.5" + +[package.dependencies] +idna = ">=2.0" +multidict = ">=4.0" + +[metadata] +lock-version = "1.1" +python-versions = "^3.8" +content-hash = "fc38f4742a2189475a175fe2ec93ca9fe6509ddec3d64bc6f5dfbd53ef9816c7" + +[metadata.files] +aiohttp = [ + {file = "aiohttp-3.6.3-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:1a4160579ffbc1b69e88cb6ca8bb0fbd4947dfcbf9fb1e2a4fc4c7a4a986c1fe"}, + {file = "aiohttp-3.6.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:fb83326d8295e8840e4ba774edf346e87eca78ba8a89c55d2690352842c15ba5"}, + {file = "aiohttp-3.6.3-cp35-cp35m-win32.whl", hash = "sha256:470e4c90da36b601676fe50c49a60d34eb8c6593780930b1aa4eea6f508dfa37"}, + {file = "aiohttp-3.6.3-cp35-cp35m-win_amd64.whl", hash = "sha256:a885432d3cabc1287bcf88ea94e1826d3aec57fd5da4a586afae4591b061d40d"}, + {file = "aiohttp-3.6.3-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:c506853ba52e516b264b106321c424d03f3ddef2813246432fa9d1cefd361c81"}, + {file = "aiohttp-3.6.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:797456399ffeef73172945708810f3277f794965eb6ec9bd3a0c007c0476be98"}, + {file = "aiohttp-3.6.3-cp36-cp36m-win32.whl", hash = "sha256:60f4caa3b7f7a477f66ccdd158e06901e1d235d572283906276e3803f6b098f5"}, + {file = "aiohttp-3.6.3-cp36-cp36m-win_amd64.whl", hash = "sha256:2ad493de47a8f926386fa6d256832de3095ba285f325db917c7deae0b54a9fc8"}, + {file = "aiohttp-3.6.3-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:319b490a5e2beaf06891f6711856ea10591cfe84fe9f3e71a721aa8f20a0872a"}, + {file = "aiohttp-3.6.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:66d64486172b032db19ea8522328b19cfb78a3e1e5b62ab6a0567f93f073dea0"}, + {file = "aiohttp-3.6.3-cp37-cp37m-win32.whl", hash = "sha256:206c0ccfcea46e1bddc91162449c20c72f308aebdcef4977420ef329c8fcc599"}, + {file = "aiohttp-3.6.3-cp37-cp37m-win_amd64.whl", hash = "sha256:687461cd974722110d1763b45c5db4d2cdee8d50f57b00c43c7590d1dd77fc5c"}, + {file = "aiohttp-3.6.3.tar.gz", hash = "sha256:698cd7bc3c7d1b82bb728bae835724a486a8c376647aec336aa21a60113c3645"}, +] +async-timeout = [ + {file = "async-timeout-3.0.1.tar.gz", hash = "sha256:0c3c816a028d47f659d6ff5c745cb2acf1f966da1fe5c19c77a70282b25f4c5f"}, + {file = "async_timeout-3.0.1-py3-none-any.whl", hash = "sha256:4291ca197d287d274d0b6cb5d6f8f8f82d434ed288f962539ff18cc9012f9ea3"}, +] +attrs = [ + {file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"}, + {file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"}, +] +black = [ + {file = "black-21.9b0-py3-none-any.whl", hash = "sha256:380f1b5da05e5a1429225676655dddb96f5ae8c75bdf91e53d798871b902a115"}, + {file = "black-21.9b0.tar.gz", hash = "sha256:7de4cfc7eb6b710de325712d40125689101d21d25283eed7e9998722cf10eb91"}, +] +cffi = [ + {file = "cffi-1.15.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:c2502a1a03b6312837279c8c1bd3ebedf6c12c4228ddbad40912d671ccc8a962"}, + {file = "cffi-1.15.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:23cfe892bd5dd8941608f93348c0737e369e51c100d03718f108bf1add7bd6d0"}, + {file = "cffi-1.15.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:41d45de54cd277a7878919867c0f08b0cf817605e4eb94093e7516505d3c8d14"}, + {file = "cffi-1.15.0-cp27-cp27m-win32.whl", hash = "sha256:4a306fa632e8f0928956a41fa8e1d6243c71e7eb59ffbd165fc0b41e316b2474"}, + {file = "cffi-1.15.0-cp27-cp27m-win_amd64.whl", hash = "sha256:e7022a66d9b55e93e1a845d8c9eba2a1bebd4966cd8bfc25d9cd07d515b33fa6"}, + {file = "cffi-1.15.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:14cd121ea63ecdae71efa69c15c5543a4b5fbcd0bbe2aad864baca0063cecf27"}, + {file = "cffi-1.15.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:d4d692a89c5cf08a8557fdeb329b82e7bf609aadfaed6c0d79f5a449a3c7c023"}, + {file = "cffi-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0104fb5ae2391d46a4cb082abdd5c69ea4eab79d8d44eaaf79f1b1fd806ee4c2"}, + {file = "cffi-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:91ec59c33514b7c7559a6acda53bbfe1b283949c34fe7440bcf917f96ac0723e"}, + {file = "cffi-1.15.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f5c7150ad32ba43a07c4479f40241756145a1f03b43480e058cfd862bf5041c7"}, + {file = "cffi-1.15.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:00c878c90cb53ccfaae6b8bc18ad05d2036553e6d9d1d9dbcf323bbe83854ca3"}, + {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abb9a20a72ac4e0fdb50dae135ba5e77880518e742077ced47eb1499e29a443c"}, + {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a5263e363c27b653a90078143adb3d076c1a748ec9ecc78ea2fb916f9b861962"}, + {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f54a64f8b0c8ff0b64d18aa76675262e1700f3995182267998c31ae974fbc382"}, + {file = "cffi-1.15.0-cp310-cp310-win32.whl", hash = "sha256:c21c9e3896c23007803a875460fb786118f0cdd4434359577ea25eb556e34c55"}, + {file = "cffi-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:5e069f72d497312b24fcc02073d70cb989045d1c91cbd53979366077959933e0"}, + {file = "cffi-1.15.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:64d4ec9f448dfe041705426000cc13e34e6e5bb13736e9fd62e34a0b0c41566e"}, + {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2756c88cbb94231c7a147402476be2c4df2f6078099a6f4a480d239a8817ae39"}, + {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b96a311ac60a3f6be21d2572e46ce67f09abcf4d09344c49274eb9e0bf345fc"}, + {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75e4024375654472cc27e91cbe9eaa08567f7fbdf822638be2814ce059f58032"}, + {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:59888172256cac5629e60e72e86598027aca6bf01fa2465bdb676d37636573e8"}, + {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:27c219baf94952ae9d50ec19651a687b826792055353d07648a5695413e0c605"}, + {file = "cffi-1.15.0-cp36-cp36m-win32.whl", hash = "sha256:4958391dbd6249d7ad855b9ca88fae690783a6be9e86df65865058ed81fc860e"}, + {file = "cffi-1.15.0-cp36-cp36m-win_amd64.whl", hash = "sha256:f6f824dc3bce0edab5f427efcfb1d63ee75b6fcb7282900ccaf925be84efb0fc"}, + {file = "cffi-1.15.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:06c48159c1abed75c2e721b1715c379fa3200c7784271b3c46df01383b593636"}, + {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c2051981a968d7de9dd2d7b87bcb9c939c74a34626a6e2f8181455dd49ed69e4"}, + {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fd8a250edc26254fe5b33be00402e6d287f562b6a5b2152dec302fa15bb3e997"}, + {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91d77d2a782be4274da750752bb1650a97bfd8f291022b379bb8e01c66b4e96b"}, + {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:45db3a33139e9c8f7c09234b5784a5e33d31fd6907800b316decad50af323ff2"}, + {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:263cc3d821c4ab2213cbe8cd8b355a7f72a8324577dc865ef98487c1aeee2bc7"}, + {file = "cffi-1.15.0-cp37-cp37m-win32.whl", hash = "sha256:17771976e82e9f94976180f76468546834d22a7cc404b17c22df2a2c81db0c66"}, + {file = "cffi-1.15.0-cp37-cp37m-win_amd64.whl", hash = "sha256:3415c89f9204ee60cd09b235810be700e993e343a408693e80ce7f6a40108029"}, + {file = "cffi-1.15.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4238e6dab5d6a8ba812de994bbb0a79bddbdf80994e4ce802b6f6f3142fcc880"}, + {file = "cffi-1.15.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0808014eb713677ec1292301ea4c81ad277b6cdf2fdd90fd540af98c0b101d20"}, + {file = "cffi-1.15.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:57e9ac9ccc3101fac9d6014fba037473e4358ef4e89f8e181f8951a2c0162024"}, + {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b6c2ea03845c9f501ed1313e78de148cd3f6cad741a75d43a29b43da27f2e1e"}, + {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:10dffb601ccfb65262a27233ac273d552ddc4d8ae1bf93b21c94b8511bffe728"}, + {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:786902fb9ba7433aae840e0ed609f45c7bcd4e225ebb9c753aa39725bb3e6ad6"}, + {file = "cffi-1.15.0-cp38-cp38-win32.whl", hash = "sha256:da5db4e883f1ce37f55c667e5c0de439df76ac4cb55964655906306918e7363c"}, + {file = "cffi-1.15.0-cp38-cp38-win_amd64.whl", hash = "sha256:181dee03b1170ff1969489acf1c26533710231c58f95534e3edac87fff06c443"}, + {file = "cffi-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:45e8636704eacc432a206ac7345a5d3d2c62d95a507ec70d62f23cd91770482a"}, + {file = "cffi-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:31fb708d9d7c3f49a60f04cf5b119aeefe5644daba1cd2a0fe389b674fd1de37"}, + {file = "cffi-1.15.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6dc2737a3674b3e344847c8686cf29e500584ccad76204efea14f451d4cc669a"}, + {file = "cffi-1.15.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:74fdfdbfdc48d3f47148976f49fab3251e550a8720bebc99bf1483f5bfb5db3e"}, + {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffaa5c925128e29efbde7301d8ecaf35c8c60ffbcd6a1ffd3a552177c8e5e796"}, + {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f7d084648d77af029acb79a0ff49a0ad7e9d09057a9bf46596dac9514dc07df"}, + {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ef1f279350da2c586a69d32fc8733092fd32cc8ac95139a00377841f59a3f8d8"}, + {file = "cffi-1.15.0-cp39-cp39-win32.whl", hash = "sha256:2a23af14f408d53d5e6cd4e3d9a24ff9e05906ad574822a10563efcef137979a"}, + {file = "cffi-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:3773c4d81e6e818df2efbc7dd77325ca0dcb688116050fb2b3011218eda36139"}, + {file = "cffi-1.15.0.tar.gz", hash = "sha256:920f0d66a896c2d99f0adbb391f990a84091179542c205fa53ce5787aff87954"}, +] +chardet = [ + {file = "chardet-3.0.4-py2.py3-none-any.whl", hash = "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"}, + {file = "chardet-3.0.4.tar.gz", hash = "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae"}, +] +click = [ + {file = "click-8.0.3-py3-none-any.whl", hash = "sha256:353f466495adaeb40b6b5f592f9f91cb22372351c84caeb068132442a4518ef3"}, + {file = "click-8.0.3.tar.gz", hash = "sha256:410e932b050f5eed773c4cda94de75971c89cdb3155a72a0831139a79e5ecb5b"}, +] +colorama = [ + {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, + {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, +] +"discord.py" = [] +humanize = [ + {file = "humanize-3.12.0-py3-none-any.whl", hash = "sha256:4c71c4381f0209715cd993058e717c1b74d58ae2f8c6da7bdb59ab66473b9ab0"}, + {file = "humanize-3.12.0.tar.gz", hash = "sha256:5ec1a66e230a3e31fb3f184aab9436ea13d4e37c168e0ffc345ae5bb57e58be6"}, +] +idna = [ + {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"}, + {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"}, +] +lavalink = [ + {file = "lavalink-3.1.4.tar.gz", hash = "sha256:c030488391e27cdc1e3ee3093817c38848ebd0d1c7bcf0d6cd0f40b9b00a4e0c"}, +] +multidict = [ + {file = "multidict-4.7.6-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:275ca32383bc5d1894b6975bb4ca6a7ff16ab76fa622967625baeebcf8079000"}, + {file = "multidict-4.7.6-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:1ece5a3369835c20ed57adadc663400b5525904e53bae59ec854a5d36b39b21a"}, + {file = "multidict-4.7.6-cp35-cp35m-win32.whl", hash = "sha256:5141c13374e6b25fe6bf092052ab55c0c03d21bd66c94a0e3ae371d3e4d865a5"}, + {file = "multidict-4.7.6-cp35-cp35m-win_amd64.whl", hash = "sha256:9456e90649005ad40558f4cf51dbb842e32807df75146c6d940b6f5abb4a78f3"}, + {file = "multidict-4.7.6-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:e0d072ae0f2a179c375f67e3da300b47e1a83293c554450b29c900e50afaae87"}, + {file = "multidict-4.7.6-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:3750f2205b800aac4bb03b5ae48025a64e474d2c6cc79547988ba1d4122a09e2"}, + {file = "multidict-4.7.6-cp36-cp36m-win32.whl", hash = "sha256:f07acae137b71af3bb548bd8da720956a3bc9f9a0b87733e0899226a2317aeb7"}, + {file = "multidict-4.7.6-cp36-cp36m-win_amd64.whl", hash = "sha256:6513728873f4326999429a8b00fc7ceddb2509b01d5fd3f3be7881a257b8d463"}, + {file = "multidict-4.7.6-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:feed85993dbdb1dbc29102f50bca65bdc68f2c0c8d352468c25b54874f23c39d"}, + {file = "multidict-4.7.6-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:fcfbb44c59af3f8ea984de67ec7c306f618a3ec771c2843804069917a8f2e255"}, + {file = "multidict-4.7.6-cp37-cp37m-win32.whl", hash = "sha256:4538273208e7294b2659b1602490f4ed3ab1c8cf9dbdd817e0e9db8e64be2507"}, + {file = "multidict-4.7.6-cp37-cp37m-win_amd64.whl", hash = "sha256:d14842362ed4cf63751648e7672f7174c9818459d169231d03c56e84daf90b7c"}, + {file = "multidict-4.7.6-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:c026fe9a05130e44157b98fea3ab12969e5b60691a276150db9eda71710cd10b"}, + {file = "multidict-4.7.6-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:51a4d210404ac61d32dada00a50ea7ba412e6ea945bbe992e4d7a595276d2ec7"}, + {file = "multidict-4.7.6-cp38-cp38-win32.whl", hash = "sha256:5cf311a0f5ef80fe73e4f4c0f0998ec08f954a6ec72b746f3c179e37de1d210d"}, + {file = "multidict-4.7.6-cp38-cp38-win_amd64.whl", hash = "sha256:7388d2ef3c55a8ba80da62ecfafa06a1c097c18032a501ffd4cabbc52d7f2b19"}, + {file = "multidict-4.7.6.tar.gz", hash = "sha256:fbb77a75e529021e7c4a8d4e823d88ef4d23674a202be4f5addffc72cbb91430"}, +] +mypy-extensions = [ + {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, + {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, +] +orjson = [ + {file = "orjson-3.6.4-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:fc01a15f3101628fd619158daec79b30d7461149735e73542ca8c13be6b835be"}, + {file = "orjson-3.6.4-cp310-cp310-manylinux_2_24_aarch64.whl", hash = "sha256:48a69fed90f551bf9e9bb7a63e363fed4f67fc7c6e6bfb057054dc78f6721e9e"}, + {file = "orjson-3.6.4-cp310-cp310-manylinux_2_24_x86_64.whl", hash = "sha256:3722f02f50861d5e2a6be9d50bfe8da27a5155bb60043118a4e1ceb8c7040cf7"}, + {file = "orjson-3.6.4-cp310-none-win_amd64.whl", hash = "sha256:231a99a728322d0271e970b149c57deb67315e6837e6cd4166cf51d30161700c"}, + {file = "orjson-3.6.4-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:6cd300421b41f7e84e388b1792a18c3fc4c440ae3039434b9320956be05f0102"}, + {file = "orjson-3.6.4-cp37-cp37m-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:e55ef66ee1d35b1c43db275aff3a1ba7e0408b31e624912a612bd799df14e73e"}, + {file = "orjson-3.6.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eef8d332af8e6f7d6d2c1f3b5384c8d239800c1405b136da5f1710e802918d57"}, + {file = "orjson-3.6.4-cp37-cp37m-manylinux_2_24_aarch64.whl", hash = "sha256:8896e242a92733e454378e22711bd43a55fda4e80604fcefcc064ca977623673"}, + {file = "orjson-3.6.4-cp37-cp37m-manylinux_2_24_x86_64.whl", hash = "sha256:bdfa6f29f7b6aad70ce14591b99fba651008afa6bc3759f158887bcdc568b452"}, + {file = "orjson-3.6.4-cp37-none-win_amd64.whl", hash = "sha256:7c16c44872d33da0b97050a9ea8f7bc04e930c56e8185657bc200e1875a671da"}, + {file = "orjson-3.6.4-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:b467551f3be1dd08aff70c261cc883b63483eb0e31861ffe2cd8dac4fec7cfa9"}, + {file = "orjson-3.6.4-cp38-cp38-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:7bf61afef12f6416db3ea377f3491ca8ac677d3cac6db1ebffb7a5fe92cce3ca"}, + {file = "orjson-3.6.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:014ea74d4a5dd6a7e98540768072d5bd8c2fedbcbbedcbbaecbb614e66080e81"}, + {file = "orjson-3.6.4-cp38-cp38-manylinux_2_24_aarch64.whl", hash = "sha256:705cb90c536b4b9336c06b4a62c3c62e50354ddf20a2e48eb62bf34fb93d5b1f"}, + {file = "orjson-3.6.4-cp38-cp38-manylinux_2_24_x86_64.whl", hash = "sha256:159e2240fc36720a5cb51a1cbc9905dcb8758aad50b3e7f14f6178ce2e842004"}, + {file = "orjson-3.6.4-cp38-none-win_amd64.whl", hash = "sha256:d2ae087866a1050de83c2a28490850badb41aeeb8a4605c84dd6004d4e58b5a4"}, + {file = "orjson-3.6.4-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:b4a7efe039b1154b23e5df8787ac01e4621213aed303b6304a5f8ad89c01455d"}, + {file = "orjson-3.6.4-cp39-cp39-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:7b24f97ed76005f447e152b0e493abce8c60f010131998295175446312a71caf"}, + {file = "orjson-3.6.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1121187e2a721864b52e5dbb3cf8dd4a4546519a5fef1e13fa777347fb8884a2"}, + {file = "orjson-3.6.4-cp39-cp39-manylinux_2_24_aarch64.whl", hash = "sha256:4edffd9e2298ff4f4f939aa67248eba043dc65c9e7d940c28a62c5502c6f2aa8"}, + {file = "orjson-3.6.4-cp39-cp39-manylinux_2_24_x86_64.whl", hash = "sha256:e236fe94d8a77532f0065870fe265bd53e229012f39af99f79f5f1d4a8b0067c"}, + {file = "orjson-3.6.4-cp39-none-win_amd64.whl", hash = "sha256:5448cc1edd4c4bafc968404f92f0e9a582b4326ca442346bd1d1179a6faf52d9"}, + {file = "orjson-3.6.4.tar.gz", hash = "sha256:f8dbc428fc6d7420f231a7133d8dff4c882e64acb585dcf2fda74bdcfe1a6d9d"}, +] +pathspec = [ + {file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"}, + {file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"}, +] +platformdirs = [ + {file = "platformdirs-2.4.0-py3-none-any.whl", hash = "sha256:8868bbe3c3c80d42f20156f22e7131d2fb321f5bc86a2a345375c6481a67021d"}, + {file = "platformdirs-2.4.0.tar.gz", hash = "sha256:367a5e80b3d04d2428ffa76d33f124cf11e8fff2acdaa9b43d545f5c7d661ef2"}, +] +pycparser = [ + {file = "pycparser-2.20-py2.py3-none-any.whl", hash = "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"}, + {file = "pycparser-2.20.tar.gz", hash = "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0"}, +] +pynacl = [ + {file = "PyNaCl-1.4.0-cp27-cp27m-macosx_10_10_x86_64.whl", hash = "sha256:ea6841bc3a76fa4942ce00f3bda7d436fda21e2d91602b9e21b7ca9ecab8f3ff"}, + {file = "PyNaCl-1.4.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:d452a6746f0a7e11121e64625109bc4468fc3100452817001dbe018bb8b08514"}, + {file = "PyNaCl-1.4.0-cp27-cp27m-win32.whl", hash = "sha256:2fe0fc5a2480361dcaf4e6e7cea00e078fcda07ba45f811b167e3f99e8cff574"}, + {file = "PyNaCl-1.4.0-cp27-cp27m-win_amd64.whl", hash = "sha256:f8851ab9041756003119368c1e6cd0b9c631f46d686b3904b18c0139f4419f80"}, + {file = "PyNaCl-1.4.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:7757ae33dae81c300487591c68790dfb5145c7d03324000433d9a2c141f82af7"}, + {file = "PyNaCl-1.4.0-cp35-abi3-macosx_10_10_x86_64.whl", hash = "sha256:757250ddb3bff1eecd7e41e65f7f833a8405fede0194319f87899690624f2122"}, + {file = "PyNaCl-1.4.0-cp35-abi3-manylinux1_x86_64.whl", hash = "sha256:30f9b96db44e09b3304f9ea95079b1b7316b2b4f3744fe3aaecccd95d547063d"}, + {file = "PyNaCl-1.4.0-cp35-abi3-win32.whl", hash = "sha256:4e10569f8cbed81cb7526ae137049759d2a8d57726d52c1a000a3ce366779634"}, + {file = "PyNaCl-1.4.0-cp35-abi3-win_amd64.whl", hash = "sha256:c914f78da4953b33d4685e3cdc7ce63401247a21425c16a39760e282075ac4a6"}, + {file = "PyNaCl-1.4.0-cp35-cp35m-win32.whl", hash = "sha256:06cbb4d9b2c4bd3c8dc0d267416aaed79906e7b33f114ddbf0911969794b1cc4"}, + {file = "PyNaCl-1.4.0-cp35-cp35m-win_amd64.whl", hash = "sha256:511d269ee845037b95c9781aa702f90ccc36036f95d0f31373a6a79bd8242e25"}, + {file = "PyNaCl-1.4.0-cp36-cp36m-win32.whl", hash = "sha256:11335f09060af52c97137d4ac54285bcb7df0cef29014a1a4efe64ac065434c4"}, + {file = "PyNaCl-1.4.0-cp36-cp36m-win_amd64.whl", hash = "sha256:cd401ccbc2a249a47a3a1724c2918fcd04be1f7b54eb2a5a71ff915db0ac51c6"}, + {file = "PyNaCl-1.4.0-cp37-cp37m-win32.whl", hash = "sha256:8122ba5f2a2169ca5da936b2e5a511740ffb73979381b4229d9188f6dcb22f1f"}, + {file = "PyNaCl-1.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:537a7ccbea22905a0ab36ea58577b39d1fa9b1884869d173b5cf111f006f689f"}, + {file = "PyNaCl-1.4.0-cp38-cp38-win32.whl", hash = "sha256:9c4a7ea4fb81536c1b1f5cc44d54a296f96ae78c1ebd2311bd0b60be45a48d96"}, + {file = "PyNaCl-1.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:7c6092102219f59ff29788860ccb021e80fffd953920c4a8653889c029b2d420"}, + {file = "PyNaCl-1.4.0.tar.gz", hash = "sha256:54e9a2c849c742006516ad56a88f5c74bf2ce92c9f67435187c3c5953b346505"}, +] +regex = [ + {file = "regex-2021.10.23-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:45b65d6a275a478ac2cbd7fdbf7cc93c1982d613de4574b56fd6972ceadb8395"}, + {file = "regex-2021.10.23-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:74d071dbe4b53c602edd87a7476ab23015a991374ddb228d941929ad7c8c922e"}, + {file = "regex-2021.10.23-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:34d870f9f27f2161709054d73646fc9aca49480617a65533fc2b4611c518e455"}, + {file = "regex-2021.10.23-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2fb698037c35109d3c2e30f2beb499e5ebae6e4bb8ff2e60c50b9a805a716f79"}, + {file = "regex-2021.10.23-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cb46b542133999580ffb691baf67410306833ee1e4f58ed06b6a7aaf4e046952"}, + {file = "regex-2021.10.23-cp310-cp310-win32.whl", hash = "sha256:5e9c9e0ce92f27cef79e28e877c6b6988c48b16942258f3bc55d39b5f911df4f"}, + {file = "regex-2021.10.23-cp310-cp310-win_amd64.whl", hash = "sha256:ab7c5684ff3538b67df3f93d66bd3369b749087871ae3786e70ef39e601345b0"}, + {file = "regex-2021.10.23-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:de557502c3bec8e634246588a94e82f1ee1b9dfcfdc453267c4fb652ff531570"}, + {file = "regex-2021.10.23-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee684f139c91e69fe09b8e83d18b4d63bf87d9440c1eb2eeb52ee851883b1b29"}, + {file = "regex-2021.10.23-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5095a411c8479e715784a0c9236568ae72509450ee2226b649083730f3fadfc6"}, + {file = "regex-2021.10.23-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b568809dca44cb75c8ebb260844ea98252c8c88396f9d203f5094e50a70355f"}, + {file = "regex-2021.10.23-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:eb672217f7bd640411cfc69756ce721d00ae600814708d35c930930f18e8029f"}, + {file = "regex-2021.10.23-cp36-cp36m-win32.whl", hash = "sha256:a7a986c45d1099a5de766a15de7bee3840b1e0e1a344430926af08e5297cf666"}, + {file = "regex-2021.10.23-cp36-cp36m-win_amd64.whl", hash = "sha256:6d7722136c6ed75caf84e1788df36397efdc5dbadab95e59c2bba82d4d808a4c"}, + {file = "regex-2021.10.23-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:9f665677e46c5a4d288ece12fdedf4f4204a422bb28ff05f0e6b08b7447796d1"}, + {file = "regex-2021.10.23-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:450dc27483548214314640c89a0f275dbc557968ed088da40bde7ef8fb52829e"}, + {file = "regex-2021.10.23-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:129472cd06062fb13e7b4670a102951a3e655e9b91634432cfbdb7810af9d710"}, + {file = "regex-2021.10.23-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a940ca7e7189d23da2bfbb38973832813eab6bd83f3bf89a977668c2f813deae"}, + {file = "regex-2021.10.23-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:530fc2bbb3dc1ebb17f70f7b234f90a1dd43b1b489ea38cea7be95fb21cdb5c7"}, + {file = "regex-2021.10.23-cp37-cp37m-win32.whl", hash = "sha256:ded0c4a3eee56b57fcb2315e40812b173cafe79d2f992d50015f4387445737fa"}, + {file = "regex-2021.10.23-cp37-cp37m-win_amd64.whl", hash = "sha256:391703a2abf8013d95bae39145d26b4e21531ab82e22f26cd3a181ee2644c234"}, + {file = "regex-2021.10.23-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:be04739a27be55631069b348dda0c81d8ea9822b5da10b8019b789e42d1fe452"}, + {file = "regex-2021.10.23-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13ec99df95003f56edcd307db44f06fbeb708c4ccdcf940478067dd62353181e"}, + {file = "regex-2021.10.23-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:8d1cdcda6bd16268316d5db1038965acf948f2a6f43acc2e0b1641ceab443623"}, + {file = "regex-2021.10.23-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c186691a7995ef1db61205e00545bf161fb7b59cdb8c1201c89b333141c438a"}, + {file = "regex-2021.10.23-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2b20f544cbbeffe171911f6ce90388ad36fe3fad26b7c7a35d4762817e9ea69c"}, + {file = "regex-2021.10.23-cp38-cp38-win32.whl", hash = "sha256:c0938ddd60cc04e8f1faf7a14a166ac939aac703745bfcd8e8f20322a7373019"}, + {file = "regex-2021.10.23-cp38-cp38-win_amd64.whl", hash = "sha256:56f0c81c44638dfd0e2367df1a331b4ddf2e771366c4b9c5d9a473de75e3e1c7"}, + {file = "regex-2021.10.23-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:80bb5d2e92b2258188e7dcae5b188c7bf868eafdf800ea6edd0fbfc029984a88"}, + {file = "regex-2021.10.23-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1dae12321b31059a1a72aaa0e6ba30156fe7e633355e445451e4021b8e122b6"}, + {file = "regex-2021.10.23-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1f2b59c28afc53973d22e7bc18428721ee8ca6079becf1b36571c42627321c65"}, + {file = "regex-2021.10.23-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d134757a37d8640f3c0abb41f5e68b7cf66c644f54ef1cb0573b7ea1c63e1509"}, + {file = "regex-2021.10.23-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:0dcc0e71118be8c69252c207630faf13ca5e1b8583d57012aae191e7d6d28b84"}, + {file = "regex-2021.10.23-cp39-cp39-win32.whl", hash = "sha256:a30513828180264294953cecd942202dfda64e85195ae36c265daf4052af0464"}, + {file = "regex-2021.10.23-cp39-cp39-win_amd64.whl", hash = "sha256:0f7552429dd39f70057ac5d0e897e5bfe211629652399a21671e53f2a9693a4e"}, + {file = "regex-2021.10.23.tar.gz", hash = "sha256:f3f9a91d3cc5e5b0ddf1043c0ae5fa4852f18a1c0050318baf5fc7930ecc1f9c"}, +] +six = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] +tomli = [ + {file = "tomli-1.2.2-py3-none-any.whl", hash = "sha256:f04066f68f5554911363063a30b108d2b5a5b1a010aa8b6132af78489fe3aade"}, + {file = "tomli-1.2.2.tar.gz", hash = "sha256:c6ce0015eb38820eaf32b5db832dbc26deb3dd427bd5f6556cf0acac2c214fee"}, +] +typing-extensions = [ + {file = "typing_extensions-3.10.0.2-py2-none-any.whl", hash = "sha256:d8226d10bc02a29bcc81df19a26e56a9647f8b0a6d4a83924139f4a8b01f17b7"}, + {file = "typing_extensions-3.10.0.2-py3-none-any.whl", hash = "sha256:f1d25edafde516b146ecd0613dabcc61409817af4766fbbcfb8d1ad4ec441a34"}, + {file = "typing_extensions-3.10.0.2.tar.gz", hash = "sha256:49f75d16ff11f1cd258e1b988ccff82a3ca5570217d7ad8c5f48205dd99a677e"}, +] +yarl = [ + {file = "yarl-1.5.1-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:db6db0f45d2c63ddb1a9d18d1b9b22f308e52c83638c26b422d520a815c4b3fb"}, + {file = "yarl-1.5.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:17668ec6722b1b7a3a05cc0167659f6c95b436d25a36c2d52db0eca7d3f72593"}, + {file = "yarl-1.5.1-cp35-cp35m-win32.whl", hash = "sha256:040b237f58ff7d800e6e0fd89c8439b841f777dd99b4a9cca04d6935564b9409"}, + {file = "yarl-1.5.1-cp35-cp35m-win_amd64.whl", hash = "sha256:f18d68f2be6bf0e89f1521af2b1bb46e66ab0018faafa81d70f358153170a317"}, + {file = "yarl-1.5.1-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:c52ce2883dc193824989a9b97a76ca86ecd1fa7955b14f87bf367a61b6232511"}, + {file = "yarl-1.5.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:ce584af5de8830d8701b8979b18fcf450cef9a382b1a3c8ef189bedc408faf1e"}, + {file = "yarl-1.5.1-cp36-cp36m-win32.whl", hash = "sha256:df89642981b94e7db5596818499c4b2219028f2a528c9c37cc1de45bf2fd3a3f"}, + {file = "yarl-1.5.1-cp36-cp36m-win_amd64.whl", hash = "sha256:3a584b28086bc93c888a6c2aa5c92ed1ae20932f078c46509a66dce9ea5533f2"}, + {file = "yarl-1.5.1-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:da456eeec17fa8aa4594d9a9f27c0b1060b6a75f2419fe0c00609587b2695f4a"}, + {file = "yarl-1.5.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:bc2f976c0e918659f723401c4f834deb8a8e7798a71be4382e024bcc3f7e23a8"}, + {file = "yarl-1.5.1-cp37-cp37m-win32.whl", hash = "sha256:4439be27e4eee76c7632c2427ca5e73703151b22cae23e64adb243a9c2f565d8"}, + {file = "yarl-1.5.1-cp37-cp37m-win_amd64.whl", hash = "sha256:48e918b05850fffb070a496d2b5f97fc31d15d94ca33d3d08a4f86e26d4e7c5d"}, + {file = "yarl-1.5.1-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:9b930776c0ae0c691776f4d2891ebc5362af86f152dd0da463a6614074cb1b02"}, + {file = "yarl-1.5.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:b3b9ad80f8b68519cc3372a6ca85ae02cc5a8807723ac366b53c0f089db19e4a"}, + {file = "yarl-1.5.1-cp38-cp38-win32.whl", hash = "sha256:f379b7f83f23fe12823085cd6b906edc49df969eb99757f58ff382349a3303c6"}, + {file = "yarl-1.5.1-cp38-cp38-win_amd64.whl", hash = "sha256:9102b59e8337f9874638fcfc9ac3734a0cfadb100e47d55c20d0dc6087fb4692"}, + {file = "yarl-1.5.1.tar.gz", hash = "sha256:c22c75b5f394f3d47105045ea551e08a3e804dc7e01b37800ca35b58f856c3d6"}, +] diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..6d01ab1 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,19 @@ +[tool.poetry] +name = "christmasbot" +version = "0.1.0" +description = "" +authors = ["Your Name "] +license = "GPL-v3.0" + +[tool.poetry.dependencies] +python = "^3.8" +lavalink = "^3.1.4" +humanize = "^3.12.0" +"discord.py" = { git = "https://github.com/iDevision/enhanced-discord.py", branch = "2.0", extras = ["voice", "speed"] } + +[tool.poetry.dev-dependencies] +black = {version = "^21.9b0", allow-prereleases = true} + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" diff --git a/tables/__init__.py b/tables/__init__.py deleted file mode 100644 index b4d5877..0000000 --- a/tables/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -import sqlalchemy as sa -from .autojoin import table as autojoin -from typing import Dict - - -def get_tables(metadata: sa.MetaData) -> Dict[str, sa.Table]: - return {name: table for name, table in [autojoin(metadata)]} diff --git a/tables/autojoin.py b/tables/autojoin.py deleted file mode 100644 index 31e4e38..0000000 --- a/tables/autojoin.py +++ /dev/null @@ -1,12 +0,0 @@ -import sqlalchemy as sa - - -def table(metadata: sa.MetaData) -> sa.Table: - tablename = 'autojoin' - - tableobject = sa.Table( - tablename, metadata, - sa.Column('guild_id', sa.BigInteger, primary_key=True), - sa.Column('channel_id', sa.BigInteger)) - - return (tablename, tableobject) diff --git a/utils/EmbedGenerator.py b/utils/EmbedGenerator.py index a11eed2..0d66ff8 100644 --- a/utils/EmbedGenerator.py +++ b/utils/EmbedGenerator.py @@ -7,26 +7,21 @@ from discord.ext.commands import Context class EmbedGenerator: @staticmethod - async def Error(ctx: Context, message: str, **kwargs): + async def Error(ctx: Context, message: str, **kwargs) -> Embed: color = ctx.bot.colors["embed"] - em = Embed(title='Error:', description=message, color=color) + em = Embed(title="Error:", description=message, color=color) return await EmbedGenerator.SendWithFooter(ctx, em, **kwargs) @staticmethod - async def Message(ctx: Context, - title: str, - message: Optional[str] = '', - **kwargs): + async def Message(ctx: Context, title: str, message: Optional[str] = "", **kwargs): color = ctx.bot.colors["embed"] em = Embed(title=title, description=message, color=color) return await EmbedGenerator.SendWithFooter(ctx, em, **kwargs) @staticmethod - async def Image(ctx: Context, - title: str, - url: str, - message: Optional[str] = '', - **kwargs): + async def Image( + ctx: Context, title: str, url: str, message: Optional[str] = "", **kwargs + ): color = ctx.bot.colors["embed"] em = Embed(title=title, description=message, url=url, color=color) em.set_image(url=url) @@ -39,10 +34,9 @@ class EmbedGenerator: return await EmbedGenerator.SendWithFooter(ctx, em, **kwargs) @staticmethod - async def SendWithFooter(ctx: Context, em: Embed, - **kwargs) -> discord.Message: - avatar = ctx.author.avatar_url_as() - em.set_footer(text=f'Requested by: {ctx.author}', icon_url=avatar) - if kwargs.get('no_send', False): + async def SendWithFooter(ctx: Context, em: Embed, **kwargs) -> discord.Message: + avatar = ctx.author.avatar.with_static_format("jpeg") + em.set_footer(text=f"Requested by: {ctx.author}", icon_url=avatar) + if kwargs.get("no_send", False): return em return await ctx.send(embed=em, **kwargs) diff --git a/utils/database.py b/utils/database.py deleted file mode 100644 index 7e54614..0000000 --- a/utils/database.py +++ /dev/null @@ -1,32 +0,0 @@ -import aiomysql -import asyncio -from bot import CloudKid -from aiomysql.sa import SAConnection -from sqlalchemy import Table -from typing import Sequence - - -class AutoJoin: - @staticmethod - async def get_channels(bot: CloudKid) -> Sequence[tuple]: - autojoin: Table = bot.database.tables["autojoin"] - conn: SAConnection = await bot.database.engine.acquire() - result = await conn.execute(autojoin.select()) - channels: Sequence[tuple] = await result.fetchall() - return channels - - @staticmethod - async def update_channel(bot, guild_id, channel_id): - autojoin: Table = bot.database.tables["autojoin"] - conn: SAConnection = await bot.database.engine.acquire() - await conn.execute(autojoin.insert().values({ - "guild_id": guild_id, - "channel_id": channel_id - })) - - @staticmethod - async def del_channel(bot, guild_id): - autojoin: Table = bot.database.tables["autojoin"] - conn: SAConnection = await bot.database.engine.acquire() - await conn.execute( - autojoin.delete().where(autojoin.c.guild_id == guild_id)) diff --git a/utils/metadata.py b/utils/metadata.py deleted file mode 100644 index eb22dc2..0000000 --- a/utils/metadata.py +++ /dev/null @@ -1,60 +0,0 @@ -import re -import struct -import sys -import urllib.request as urllib2 -import aiohttp - -key = "b9803d262fab977f:04c791f69496359d638fc634e60c08d0" - - -def hasNumbers(inputString): - return any(char.isdigit() for char in inputString) - - -def process_stream_title(title: str) -> list: - l = title.split(' - ', 1) - if l[0].isdigit(): - return l[1].rsplit(" - ", 1) - return title.rsplit(" - ", 1) - - -async def fetch_metadata(): - url = "https://azuracast.exobot.site/api/nowplaying" - async with aiohttp.ClientSession() as session: - async with session.get(url) as response: - data = await response.json() - source = data["playing_next"]["text"] - name, vid = process_stream_title(source["title"]) - return {"name": name, "thumbnail": data["playing_next"]["art"]} - - -async def get_metadata(): - """Deprecated: use fetch_metadata()""" - url = 'https://icecast.exobot.site/stream' # radio stream - encoding = 'iso-8859-1' # default: iso-8859-1 for mp3 and utf-8 for ogg streams - request = urllib2.Request(url, headers={'Icy-MetaData': - 1}) # request metadata - response = urllib2.urlopen(request) - # print(response.headers, file=sys.stderr) - metaint = int(response.headers['icy-metaint']) - for _ in range(10): # title may be empty initially, try several times - response.read(metaint) # skip to metadata - metadata_length = struct.unpack( - 'B', response.read(1))[0] * 16 # length byte - metadata = response.read(metadata_length).rstrip(b'\0') - # print(metadata, file=sys.stderr) - # extract title from the metadata - m = re.search(br"StreamTitle='([^']*)';", metadata) - if m: - title = m.group(1) - if title: - break - else: - sys.exit('no title found') - - title = title.decode(encoding, errors='replace') - #print(f"DEBUG: {title}") - if hasNumbers(str(title[:4])): - return title[7:][:-14] - else: - return title[:-14] diff --git a/utils/paginator.py b/utils/paginator.py index 2bf4f32..55067eb 100644 --- a/utils/paginator.py +++ b/utils/paginator.py @@ -43,13 +43,10 @@ class Pages: permissions: discord.Permissions Our permissions for the channel. """ - def __init__(self, - ctx, - *, - entries, - per_page=12, - show_entry_count=True, - hide_no_results=False): + + def __init__( + self, ctx, *, entries, per_page=12, show_entry_count=True, hide_no_results=False + ): self.hide_no_results = hide_no_results self.bot = ctx.bot self.entries = entries @@ -65,15 +62,19 @@ class Pages: self.paginating = len(entries) > per_page self.show_entry_count = show_entry_count self.reaction_emojis = [ - ('\N{BLACK LEFT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR}', - self.first_page), - ('\N{BLACK LEFT-POINTING TRIANGLE}', self.previous_page), - ('\N{BLACK RIGHT-POINTING TRIANGLE}', self.next_page), - ('\N{BLACK RIGHT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR}', - self.last_page), - ('\N{INPUT SYMBOL FOR NUMBERS}', self.numbered_page), - ('\N{BLACK SQUARE FOR STOP}', self.stop_pages), - ('\N{INFORMATION SOURCE}', self.show_help), + ( + "\N{BLACK LEFT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR}", + self.first_page, + ), + ("\N{BLACK LEFT-POINTING TRIANGLE}", self.previous_page), + ("\N{BLACK RIGHT-POINTING TRIANGLE}", self.next_page), + ( + "\N{BLACK RIGHT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR}", + self.last_page, + ), + ("\N{INPUT SYMBOL FOR NUMBERS}", self.numbered_page), + ("\N{BLACK SQUARE FOR STOP}", self.stop_pages), + ("\N{INFORMATION SOURCE}", self.show_help), ] if ctx.guild is not None: @@ -82,63 +83,64 @@ class Pages: self.permissions = self.channel.permissions_for(ctx.bot.user) if not self.permissions.embed_links: - raise CannotPaginate('Bot does not have embed links permission.') + raise CannotPaginate("Bot does not have embed links permission.") if not self.permissions.send_messages: - raise CannotPaginate('Bot cannot send messages.') + raise CannotPaginate("Bot cannot send messages.") if self.paginating: # verify we can actually use the pagination session if not self.permissions.add_reactions: - raise CannotPaginate( - 'Bot does not have add reactions permission.') + raise CannotPaginate("Bot does not have add reactions permission.") if not self.permissions.read_message_history: raise CannotPaginate( - 'Bot does not have Read Message History permission.') + "Bot does not have Read Message History permission." + ) def get_page(self, page): base = (page - 1) * self.per_page - return self.entries[base:base + self.per_page] + return self.entries[base : base + self.per_page] async def show_page(self, page, *, first=False): # noinspection PyAttributeOutsideInit self.current_page = page entries = self.get_page(page) p = [] - for index, entry in enumerate(entries, - 1 + ((page - 1) * self.per_page)): - p.append(f'{index}. {entry}') + for index, entry in enumerate(entries, 1 + ((page - 1) * self.per_page)): + p.append(f"{index}. {entry}") if self.maximum_pages > 1: if self.show_entry_count: - text = f'Page {page}/{self.maximum_pages}' \ - f' ({len(self.entries)} entries)' + text = ( + f"Page {page}/{self.maximum_pages}" + f" ({len(self.entries)} entries)" + ) else: - text = f'Page {page}/{self.maximum_pages}' + text = f"Page {page}/{self.maximum_pages}" self.embed.set_footer(text=text) if not self.paginating: - self.embed.description = '\n'.join(p) + self.embed.description = "\n".join(p) return await self.channel.send(embed=self.embed) if not first: - self.embed.description = '\n'.join(p) + self.embed.description = "\n".join(p) await self.message.edit(embed=self.embed) return - p.append('') - p.append('Confused? React with \N{INFORMATION SOURCE} for more info.') - self.embed.description = '\n'.join(p) + p.append("") + p.append("Confused? React with \N{INFORMATION SOURCE} for more info.") + self.embed.description = "\n".join(p) self.message = await self.channel.send(embed=self.embed) - await self.message.add_reaction('🔣') + await self.message.add_reaction("🔣") async def add_rest_reactions(self): - await self.message.remove_reaction('🔣', self.message.guild.me) + await self.message.remove_reaction("🔣", self.message.guild.me) for (reaction, _) in self.reaction_emojis: - if self.maximum_pages == 2 and reaction in ('\u23ed', '\u23ee'): + if self.maximum_pages == 2 and reaction in ("\u23ed", "\u23ee"): # no |<< or >>| buttons if we only have two pages # we can't forbid it if someone ends up using it but remove # it from the default set @@ -174,20 +176,19 @@ class Pages: """lets you type a page number to go to""" # noinspection PyListCreation to_delete = [] - to_delete.append(await - self.channel.send('What page do you want to go to?')) + to_delete.append(await self.channel.send("What page do you want to go to?")) def message_check(m): - return m.author == self.author and \ - self.channel == m.channel and \ - m.content.isdigit() + return ( + m.author == self.author + and self.channel == m.channel + and m.content.isdigit() + ) try: - msg = await self.bot.wait_for('message', - check=message_check, - timeout=30.0) + msg = await self.bot.wait_for("message", check=message_check, timeout=30.0) except asyncio.TimeoutError: - to_delete.append(await self.channel.send('Took too long.')) + to_delete.append(await self.channel.send("Took too long.")) await asyncio.sleep(5) else: page = int(msg.content) @@ -195,8 +196,11 @@ class Pages: if page != 0 and page <= self.maximum_pages: await self.show_page(page) else: - to_delete.append(await self.channel.send( - f'Invalid page given. ({page}/{self.maximum_pages})')) + to_delete.append( + await self.channel.send( + f"Invalid page given. ({page}/{self.maximum_pages})" + ) + ) await asyncio.sleep(5) # noinspection PyBroadException @@ -208,19 +212,20 @@ class Pages: async def show_help(self): """shows this message""" messages = [ - 'Welcome to the interactive paginator!\n', - 'This interactively allows you to see pages ' - 'of text by navigating with ' - 'reactions. They are as follows:\n' + "Welcome to the interactive paginator!\n", + "This interactively allows you to see pages " + "of text by navigating with " + "reactions. They are as follows:\n", ] for (emoji, func) in self.reaction_emojis: - messages.append(f'{emoji} {func.__doc__}') + messages.append(f"{emoji} {func.__doc__}") - self.embed.description = '\n'.join(messages) + self.embed.description = "\n".join(messages) self.embed.clear_fields() self.embed.set_footer( - text=f'We were on page {self.current_page} before this message.') + text=f"We were on page {self.current_page} before this message." + ) await self.message.edit(embed=self.embed) async def go_back_to_current_page(): @@ -241,7 +246,7 @@ class Pages: if reaction.message.id != self.message.id: return False - if reaction.emoji == '🔣': + if reaction.emoji == "🔣": self.match = self.add_rest_reactions return True @@ -258,7 +263,7 @@ class Pages: if not self.entries and not self.hide_no_results: # I just say no results found because that's my most common use # case. - return await self.channel.send('No results found.') + return await self.channel.send("No results found.") first_page = self.show_page(1, first=True) if not self.paginating: @@ -270,7 +275,8 @@ class Pages: while self.paginating: try: reaction, user = await self.bot.wait_for( - 'reaction_add', check=self.react_check, timeout=120.0) + "reaction_add", check=self.react_check, timeout=120.0 + ) except asyncio.TimeoutError: self.paginating = False # noinspection PyBroadException @@ -285,13 +291,14 @@ class Pages: try: await self.message.remove_reaction(reaction, user) except Exception: - pass # can't remove it so don't bother doing so + pass # can't remove it so don't bother doing so await self.match() class EmbedPages: """Similar to Pages, but you use [`discord.Embed`]""" + def __init__(self, ctx, *, embeds): self.bot = ctx.bot self.embeds = embeds @@ -302,15 +309,19 @@ class EmbedPages: self.maximum_pages = pages self.paginating = len(embeds) > 1 self.reaction_emojis = [ - ('\N{BLACK LEFT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR}', - self.first_page), - ('\N{BLACK LEFT-POINTING TRIANGLE}', self.previous_page), - ('\N{BLACK RIGHT-POINTING TRIANGLE}', self.next_page), - ('\N{BLACK RIGHT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR}', - self.last_page), - ('\N{INPUT SYMBOL FOR NUMBERS}', self.numbered_page), - ('\N{BLACK SQUARE FOR STOP}', self.stop_pages), - ('\N{INFORMATION SOURCE}', self.show_help), + ( + "\N{BLACK LEFT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR}", + self.first_page, + ), + ("\N{BLACK LEFT-POINTING TRIANGLE}", self.previous_page), + ("\N{BLACK RIGHT-POINTING TRIANGLE}", self.next_page), + ( + "\N{BLACK RIGHT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR}", + self.last_page, + ), + ("\N{INPUT SYMBOL FOR NUMBERS}", self.numbered_page), + ("\N{BLACK SQUARE FOR STOP}", self.stop_pages), + ("\N{INFORMATION SOURCE}", self.show_help), ] if ctx.guild is not None: @@ -319,20 +330,20 @@ class EmbedPages: self.permissions = self.channel.permissions_for(ctx.bot.user) if not self.permissions.embed_links: - raise CannotPaginate('Bot does not have embed links permission.') + raise CannotPaginate("Bot does not have embed links permission.") if not self.permissions.send_messages: - raise CannotPaginate('Bot cannot send messages.') + raise CannotPaginate("Bot cannot send messages.") if self.paginating: # verify we can actually use the pagination session if not self.permissions.add_reactions: - raise CannotPaginate( - 'Bot does not have add reactions permission.') + raise CannotPaginate("Bot does not have add reactions permission.") if not self.permissions.read_message_history: raise CannotPaginate( - 'Bot does not have Read Message History permission.') + "Bot does not have Read Message History permission." + ) async def show_page(self, page, *, first=False): # noinspection PyAttributeOutsideInit @@ -341,7 +352,7 @@ class EmbedPages: p = [] if self.maximum_pages > 1: - text = f'Page {page}/{self.maximum_pages}' + text = f"Page {page}/{self.maximum_pages}" embed.set_footer(text=text) @@ -351,19 +362,20 @@ class EmbedPages: if not first: return await self.message.edit(embed=embed) - p.append('') - p.append('Confused? React with \N{INFORMATION SOURCE} for more info.') - embed.description = '' if embed.description == discord.Embed.Empty \ - else embed.description - embed.description += '\n'.join(p) + p.append("") + p.append("Confused? React with \N{INFORMATION SOURCE} for more info.") + embed.description = ( + "" if embed.description == discord.Embed.Empty else embed.description + ) + embed.description += "\n".join(p) self.message = await self.channel.send(embed=embed) - await self.message.add_reaction('🔣') + await self.message.add_reaction("🔣") async def add_rest_reactions(self): - await self.message.remove_reaction('🔣', self.message.guild.me) + await self.message.remove_reaction("🔣", self.message.guild.me) for (reaction, _) in self.reaction_emojis: - if self.maximum_pages == 2 and reaction in ('\u23ed', '\u23ee'): + if self.maximum_pages == 2 and reaction in ("\u23ed", "\u23ee"): # no |<< or >>| buttons if we only have two pages # we can't forbid it if someone ends up using it but remove # it from the default set @@ -399,20 +411,19 @@ class EmbedPages: """lets you type a page number to go to""" # noinspection PyListCreation to_delete = [] - to_delete.append(await - self.channel.send('What page do you want to go to?')) + to_delete.append(await self.channel.send("What page do you want to go to?")) def message_check(m): - return m.author == self.author and \ - self.channel == m.channel and \ - m.content.isdigit() + return ( + m.author == self.author + and self.channel == m.channel + and m.content.isdigit() + ) try: - msg = await self.bot.wait_for('message', - check=message_check, - timeout=30.0) + msg = await self.bot.wait_for("message", check=message_check, timeout=30.0) except asyncio.TimeoutError: - to_delete.append(await self.channel.send('Took too long.')) + to_delete.append(await self.channel.send("Took too long.")) await asyncio.sleep(5) else: page = int(msg.content) @@ -420,8 +431,11 @@ class EmbedPages: if page != 0 and page <= self.maximum_pages: await self.show_page(page) else: - to_delete.append(await self.channel.send( - f'Invalid page given. ({page}/{self.maximum_pages})')) + to_delete.append( + await self.channel.send( + f"Invalid page given. ({page}/{self.maximum_pages})" + ) + ) await asyncio.sleep(5) # noinspection PyBroadException @@ -433,21 +447,22 @@ class EmbedPages: async def show_help(self): """shows this message""" messages = [ - 'Welcome to the interactive paginator!\n', - 'This interactively allows you to see pages ' - 'of text by navigating with ' - 'reactions. They are as follows:\n' + "Welcome to the interactive paginator!\n", + "This interactively allows you to see pages " + "of text by navigating with " + "reactions. They are as follows:\n", ] for (emoji, func) in self.reaction_emojis: - messages.append(f'{emoji} {func.__doc__}') + messages.append(f"{emoji} {func.__doc__}") embed = discord.Embed() - embed.description = '\n'.join(messages) + embed.description = "\n".join(messages) embed.clear_fields() embed.set_footer( - text=f'We were on page {self.current_page} before this message.') + text=f"We were on page {self.current_page} before this message." + ) await self.message.edit(embed=embed) async def go_back_to_current_page(): @@ -468,7 +483,7 @@ class EmbedPages: if reaction.message.id != self.message.id: return False - if reaction.emoji == '🔣': + if reaction.emoji == "🔣": self.match = self.add_rest_reactions return True @@ -492,7 +507,8 @@ class EmbedPages: while self.paginating: try: reaction, user = await self.bot.wait_for( - 'reaction_add', check=self.react_check, timeout=120.0) + "reaction_add", check=self.react_check, timeout=120.0 + ) except asyncio.TimeoutError: self.paginating = False # noinspection PyBroadException @@ -507,7 +523,7 @@ class EmbedPages: try: await self.message.remove_reaction(reaction, user) except Exception: - pass # can't remove it so don't bother doing so + pass # can't remove it so don't bother doing so await self.match() @@ -516,6 +532,7 @@ class FieldPages(Pages): """Similar to Pages except entries should be a list of tuples having (key, value) to show as embed fields instead. """ + async def show_page(self, page, *, first=False): # noinspection PyAttributeOutsideInit self.current_page = page @@ -529,10 +546,12 @@ class FieldPages(Pages): if self.maximum_pages > 1: if self.show_entry_count: - text = f'Page {page}/{self.maximum_pages} ' \ - f'({len(self.entries)} entries)' + text = ( + f"Page {page}/{self.maximum_pages} " + f"({len(self.entries)} entries)" + ) else: - text = f'Page {page}/{self.maximum_pages}' + text = f"Page {page}/{self.maximum_pages}" self.embed.set_footer(text=text) @@ -545,7 +564,7 @@ class FieldPages(Pages): self.message = await self.channel.send(embed=self.embed) for (reaction, _) in self.reaction_emojis: - if self.maximum_pages == 2 and reaction in ('\u23ed', '\u23ee'): + if self.maximum_pages == 2 and reaction in ("\u23ed", "\u23ee"): # no |<< or >>| buttons if we only have two pages # we can't forbid it if someone ends up using it but remove # it from the default set @@ -559,7 +578,7 @@ class FieldPages(Pages): # ?help command # -> could be a subcommand -_mention = re.compile(r'<@!?([0-9]{1,19})>') +_mention = re.compile(r"<@!?([0-9]{1,19})>") def cleanup_prefix(bot, prefix): @@ -567,7 +586,7 @@ def cleanup_prefix(bot, prefix): if m: user = bot.get_user(int(m.group(1))) if user: - return f'@{user.name} ' + return f"@{user.name} " return prefix @@ -586,39 +605,40 @@ def _command_signature(cmd): result = [cmd.qualified_name] if cmd.usage: result.append(cmd.usage) - return ' '.join(result) + return " ".join(result) params = cmd.clean_params if not params: - return ' '.join(result) + return " ".join(result) for name, param in params.items(): if param.default is not param.empty: # We don't want None or '' to trigger the [name=value] case and # instead it should do [name] since [name=None] or [name=] are # not exactly useful for the user. - should_print = param.default if isinstance( - param.default, str) else param.default is not None + should_print = ( + param.default + if isinstance(param.default, str) + else param.default is not None + ) if should_print: - result.append(f'[{name}={param.default!r}]') + result.append(f"[{name}={param.default!r}]") else: - result.append(f'[{name}]') + result.append(f"[{name}]") elif param.kind == param.VAR_POSITIONAL: - result.append(f'[{name}...]') + result.append(f"[{name}...]") else: - result.append(f'<{name}>') + result.append(f"<{name}>") - return ' '.join(result) + return " ".join(result) class HelpPaginator(Pages): def __init__(self, ctx, entries, *, per_page=4): - super().__init__(ctx, - entries=entries, - per_page=per_page, - hide_no_results=True) + super().__init__(ctx, entries=entries, per_page=per_page, hide_no_results=True) self.reaction_emojis.append( - ('\N{WHITE QUESTION MARK ORNAMENT}', self.show_bot_help)) + ("\N{WHITE QUESTION MARK ORNAMENT}", self.show_bot_help) + ) self.total = len(entries) @classmethod @@ -630,12 +650,11 @@ class HelpPaginator(Pages): # remove the ones we can't run entries = [ - cmd for cmd in entries - if (await _can_run(cmd, ctx)) and not cmd.hidden + cmd for cmd in entries if (await _can_run(cmd, ctx)) and not cmd.hidden ] self = cls(ctx, entries) - self.title = f'{cog_name} Commands'.upper() + self.title = f"{cog_name} Commands".upper() self.description = inspect.getdoc(cog) self.prefix = cleanup_prefix(ctx.bot, ctx.prefix) @@ -651,17 +670,16 @@ class HelpPaginator(Pages): entries = [] else: entries = [ - cmd for cmd in entries - if (await _can_run(cmd, ctx)) and not cmd.hidden + cmd for cmd in entries if (await _can_run(cmd, ctx)) and not cmd.hidden ] self = cls(ctx, entries) self.title = command.signature if command.description: - self.description = f'{command.description}\n\n{command.help}' + self.description = f"{command.description}\n\n{command.help}" else: - self.description = command.help or 'No help given.' + self.description = command.help or "No help given." self.prefix = cleanup_prefix(ctx.bot, ctx.prefix) return self @@ -669,7 +687,7 @@ class HelpPaginator(Pages): @classmethod async def from_bot(cls, ctx): def key(c): - return c.cog_name or '\u200bMisc' + return c.cog_name or "\u200bMisc" entries = sorted(ctx.bot.commands, key=key) nested_pages = [] @@ -681,8 +699,7 @@ class HelpPaginator(Pages): for cog, commands in itertools.groupby(entries, key=key): plausible = [ - cmd for cmd in commands - if (await _can_run(cmd, ctx)) and not cmd.hidden + cmd for cmd in commands if (await _can_run(cmd, ctx)) and not cmd.hidden ] if len(plausible) == 0: continue @@ -691,14 +708,14 @@ class HelpPaginator(Pages): if description is None: description = discord.Embed.Empty else: - description = inspect.getdoc( - description) or discord.Embed.Empty + description = inspect.getdoc(description) or discord.Embed.Empty - nested_pages.extend((cog, description, plausible[i:i + per_page]) - for i in range(0, len(plausible), per_page)) + nested_pages.extend( + (cog, description, plausible[i : i + per_page]) + for i in range(0, len(plausible), per_page) + ) - self = cls(ctx, nested_pages, - per_page=1) # this forces the pagination session + self = cls(ctx, nested_pages, per_page=1) # this forces the pagination session self.prefix = cleanup_prefix(ctx.bot, ctx.prefix) # swap the get_page implementation with @@ -713,7 +730,7 @@ class HelpPaginator(Pages): # noinspection PyAttributeOutsideInit def get_bot_page(self, page): cog, description, commands = self.entries[page - 1] - self.title = f'{cog} Commands' + self.title = f"{cog} Commands" self.description = description return commands @@ -734,13 +751,15 @@ class HelpPaginator(Pages): signature = _command_signature for entry in entries: - self.embed.add_field(name=signature(entry), - value=entry.short_doc or "No help given", - inline=False) + self.embed.add_field( + name=signature(entry), + value=entry.short_doc or "No help given", + inline=False, + ) if self.maximum_pages: self.embed.set_author( - name=f'Page {page}/{self.maximum_pages} ({self.total} commands)' + name=f"Page {page}/{self.maximum_pages} ({self.total} commands)" ) if not self.paginating: @@ -752,7 +771,7 @@ class HelpPaginator(Pages): self.message = await self.channel.send(embed=self.embed) for (reaction, _) in self.reaction_emojis: - if self.maximum_pages == 2 and reaction in ('\u23ed', '\u23ee'): + if self.maximum_pages == 2 and reaction in ("\u23ed", "\u23ee"): # no |<< or >>| buttons if we only have two pages # we can't forbid it if someone ends up using it but remove # it from the default set @@ -763,19 +782,20 @@ class HelpPaginator(Pages): async def show_help(self): """shows this message""" - self.embed.title = 'Paginator help' - self.embed.description = 'Hello! Welcome to the help page.' + self.embed.title = "Paginator help" + self.embed.description = "Hello! Welcome to the help page." - messages = [ - f'{emoji} {func.__doc__}' for emoji, func in self.reaction_emojis - ] + messages = [f"{emoji} {func.__doc__}" for emoji, func in self.reaction_emojis] self.embed.clear_fields() - self.embed.add_field(name='What are these reactions for?', - value='\n'.join(messages), - inline=False) + self.embed.add_field( + name="What are these reactions for?", + value="\n".join(messages), + inline=False, + ) self.embed.set_footer( - text=f'We were on page {self.current_page} before this message.') + text=f"We were on page {self.current_page} before this message." + ) await self.message.edit(embed=self.embed) async def go_back_to_current_page(): @@ -787,31 +807,34 @@ class HelpPaginator(Pages): async def show_bot_help(self): """shows how to use the bot""" - self.embed.title = 'Using the bot' - self.embed.description = 'Hello! Welcome to the help page.' + self.embed.title = "Using the bot" + self.embed.description = "Hello! Welcome to the help page." self.embed.clear_fields() - entries = (('', - 'This means the argument is __**required**__.'), - ('[argument]', - 'This means the argument is __**optional**__.'), - ('[A|B]', - 'This means the it can be __**either A or B**__.'), - ('[argument...]', - 'This means you can have multiple arguments.\n' - 'Now that you know the basics, it should be ' - 'noted that...\n' - '__**You do not type in the brackets!**__')) + entries = ( + ("", "This means the argument is __**required**__."), + ("[argument]", "This means the argument is __**optional**__."), + ("[A|B]", "This means the it can be __**either A or B**__."), + ( + "[argument...]", + "This means you can have multiple arguments.\n" + "Now that you know the basics, it should be " + "noted that...\n" + "__**You do not type in the brackets!**__", + ), + ) self.embed.add_field( - name='How do I use this bot?', - value='Reading the bot signature is pretty simple.') + name="How do I use this bot?", + value="Reading the bot signature is pretty simple.", + ) for name, value in entries: self.embed.add_field(name=name, value=value, inline=False) self.embed.set_footer( - text=f'We were on page {self.current_page} before this message.') + text=f"We were on page {self.current_page} before this message." + ) await self.message.edit(embed=self.embed) async def go_back_to_current_page():