WIP Cleaning up the code base + impl slash commands

This commit is contained in:
2021-10-30 22:05:40 +02:00
parent 425d3a3a92
commit bcf3d957a8
16 changed files with 973 additions and 541 deletions
+1 -1
View File
@@ -1,3 +1,3 @@
{ {
"python.formatting.provider": "yapf" "python.formatting.provider": "black"
} }
-17
View File
@@ -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"
+2 -2
View File
@@ -1,2 +1,2 @@
# CloudKid # ChristmasBot
CloudKid Music Bot Christmas Music Bot
+31 -43
View File
@@ -1,68 +1,55 @@
import discord import discord
from discord.colour import Color
from discord.ext import commands from discord.ext import commands
import sys import sys
import json import json
import asyncio import asyncio
import sqlalchemy as sa from typing import Any, Dict, Sequence
from aiomysql.sa import Engine from discord import Message
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
class Database(): class Database:
def __init__(self, engine: Engine): def __init__(self):
self.loop = asyncio.get_event_loop() 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 = [ 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: discord.Intents = discord.Intents.none()
intents.voice_states = True intents.voice_states = True
intents.guild_messages = True intents.guild_messages = True
intents.guilds = True
intents.messages = True
self.config = config 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, super().__init__(
description='CloudKid Radio', command_prefix=self.prefix_callable,
case_insensitive=True, description=self.config["info"].get("description"),
case_insensitive=False,
fetch_offline_members=False, fetch_offline_members=False,
intents=intents) intents=intents,
slash_command_guilds=[227431704426446848],
)
self.database = None async def prefix_callable(self, _, msg: Message):
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):
return commands.when_mentioned_or(*self.config["prefixes"])(self, msg) return commands.when_mentioned_or(*self.config["prefixes"])(self, msg)
async def load_cogs(self, cog_names: Sequence[str]): async def load_cogs(self, cog_names: Sequence[str]):
for cog in cog_names: for cog in cog_names:
try: try:
self.load_extension(cog) self.load_extension(cog)
print(f'Succesfully loaded extension {cog}.') print(f"Succesfully loaded extension {cog}.")
except Exception as e: except Exception as e:
print(f'Failed to load extension {cog}.\n\t{e}', print(f"Failed to load extension {cog}.\n\t{e}", file=sys.stderr)
file=sys.stderr)
async def on_ready(self): async def on_ready(self):
print(f"Logged in as: {self.user}") 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.change_presence(activity=discord.Game(text))
await self.load_cogs(self.INITIAL_EXTENSIONS) await self.load_cogs(self.INITIAL_EXTENSIONS)
def process_colors( def process_colours(self, colors: Dict[str, str]) -> Dict[str, Color]:
self, colors: Mapping[str, str]) -> Mapping[str, discord.Color]: colour_dict: Dict[str, Color] = {}
for name, color in colors.items(): for name, color in colors.items():
colors[name] = discord.Color(int(color, 16)) colour_dict[name] = Color(int(color, 16))
return colors return colour_dict
if __name__ == "__main__": 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") token = config.pop("token")
CloudKid(config).run(token, reconnect=True) bot = ChristmasBot(config)
bot.run(token, reconnect=True)
+48 -49
View File
@@ -2,12 +2,12 @@ import discord
from discord.ext import tasks, commands from discord.ext import tasks, commands
import time import time
from discord.ext.commands import Context
import humanize import humanize
import datetime import datetime
import lavalink import lavalink
from utils import metadata from utils.EmbedGenerator import EmbedGenerator
from utils.paginator import HelpPaginator from utils.paginator import HelpPaginator
from discord import Status
class InformationCog(commands.Cog, name="Information"): class InformationCog(commands.Cog, name="Information"):
@@ -21,49 +21,49 @@ class InformationCog(commands.Cog, name="Information"):
await self.bot.wait_until_ready() await self.bot.wait_until_ready()
title = "ck!connect | ck!help" title = "ck!connect | ck!help"
if self.is_help_msg: if self.is_help_msg:
title = (await metadata.fetch_metadata())["name"] title = "Tfoe broer"
self.is_help_msg = not self.is_help_msg self.is_help_msg = not self.is_help_msg
await self.bot.change_presence(activity=discord.Game(title)) await self.bot.change_presence(activity=discord.Game(title))
@commands.cooldown(rate=1, per=5, type=commands.BucketType.user) @commands.cooldown(rate=1, per=5, type=commands.BucketType.user)
@commands.command(description='PONG!', aliases=['pong']) @commands.command(description="PONG!", aliases=["pong"])
async def ping(self, ctx): async def ping(self, ctx: Context):
"""Test the latency""" """Test the latency"""
avatar = ctx.author.avatar_url_as(static_format='jpeg') avatar = ctx.author.avatar.with_static_format("jpeg")
emoji = discord.utils.get(ctx.bot.emojis, name='loading') emoji = discord.utils.get(ctx.bot.emojis, name="loading")
start = time.monotonic() start = time.monotonic()
msg = await ctx.send(embed=discord.Embed( msg = await ctx.send(
description=f'{emoji} Calculating ping')) embed=discord.Embed(description=f"{emoji} Calculating ping")
)
millis = (time.monotonic() - start) * 1000 millis = (time.monotonic() - start) * 1000
heartbeat = ctx.bot.latency * 1000 heartbeat = ctx.bot.latency * 1000
embed = discord.Embed(color=discord.Color.blue()) embed = discord.Embed(color=discord.Color.blue())
embed.add_field(name=':heartbeat: Heartbeat', embed.add_field(
value=f'`{heartbeat:,.2f}ms`', name=":heartbeat: Heartbeat", value=f"`{heartbeat:,.2f}ms`", inline=True
inline=True) )
embed.add_field(name=':file_cabinet: ACK', embed.add_field(
value=f'`{millis:,.2f}ms`', name=":file_cabinet: ACK", value=f"`{millis:,.2f}ms`", inline=True
inline=True) )
embed.set_footer(text=f"Requested by: {ctx.author}", embed.set_footer(text=f"Requested by: {ctx.author}", icon_url=f"{avatar}")
icon_url=f"{avatar}")
await msg.edit(embed=embed) 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) @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!""" """Gets the invite link!"""
color = self.bot.colors["embed"] link = f"https://discord.com/oauth2/authorize?client_id=643555373814382593&permissions=3230720&scope=bot%20applications.commands"
avatar = ctx.author.avatar_url_as(static_format='jpeg') embed = await EmbedGenerator.Message(ctx, "Add our bot to your server:", link)
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}")
await ctx.send(embed=embed) 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.cooldown(rate=1, per=5, type=commands.BucketType.user)
@commands.command() async def wlinfo(self, ctx: Context):
async def wlinfo(self, ctx):
"""Retrieve various Node/Server/Player information.""" """Retrieve various Node/Server/Player information."""
player = self.bot.lavalink.player_manager.get(ctx.guild.id) player = self.bot.lavalink.player_manager.get(ctx.guild.id)
node = player.node node = player.node
@@ -73,28 +73,29 @@ class InformationCog(commands.Cog, name="Information"):
free = humanize.naturalsize(node.stats.memory_free) free = humanize.naturalsize(node.stats.memory_free)
cpu = node.stats.cpu_cores cpu = node.stats.cpu_cores
fmt = f'**WaveLink:** `{lavalink.__version__}`\n\n' \ fmt = (
f'Connected to `{len(self.bot.lavalink.nodes)}` nodes.\n' \ f"**WaveLink:** `{lavalink.__version__}`\n\n"
f'Best available Node `{self.bot.lavalink.get_best_node().__repr__()}`\n' \ f"Connected to `{len(self.bot.lavalink.nodes)}` nodes.\n"
f'`{len(self.bot.lavalink.players)}` players are distributed on nodes.\n' \ f"Best available Node `{self.bot.lavalink.get_best_node().__repr__()}`\n"
f'`{node.stats.players}` players are distributed on server.\n' \ f"`{len(self.bot.lavalink.players)}` players are distributed on nodes.\n"
f'`{node.stats.playing_players}` players are playing on server.\n\n' \ f"`{node.stats.players}` players are distributed on server.\n"
f'Server Memory: `{used}/{total}` | `({free} free)`\n' \ f"`{node.stats.playing_players}` players are playing on server.\n\n"
f'Server CPU: `{cpu}`\n\n' \ f"Server Memory: `{used}/{total}` | `({free} free)`\n"
f'Server Uptime: `{datetime.timedelta(milliseconds=node.stats.uptime)}`' f"Server CPU: `{cpu}`\n\n"
f"Server Uptime: `{datetime.timedelta(milliseconds=node.stats.uptime)}`"
)
await ctx.send(fmt) 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) @commands.cooldown(1, 1, commands.BucketType.user)
async def about(self, ctx, command: str = None): async def about(self, ctx: Context):
"""Exobot command list""" """Exobot command list"""
if command: if None:
entity = self.bot.get_cog(command) or self.bot.get_command(command) entity = self.bot.get_cog(command) or self.bot.get_command(command)
if entity is None: if entity is None:
clean = command.replace('@', '@\u200b') clean = command.replace("@", "@\u200b")
return await ctx.send( return await ctx.send(f'Command or category "{clean}" not found.')
f'Command or category "{clean}" not found.')
elif isinstance(entity, discord.ext.commands.Command): elif isinstance(entity, discord.ext.commands.Command):
p = await HelpPaginator.from_command(ctx, entity) p = await HelpPaginator.from_command(ctx, entity)
else: else:
@@ -111,7 +112,7 @@ class InformationCog(commands.Cog, name="Information"):
display_cogs = { display_cogs = {
"Information": ":information_source:", "Information": ":information_source:",
"Music": ":musical_note:", "Music": ":musical_note:",
"Settings": ":gear:" "Settings": ":gear:",
} }
for cog_name, cog_icon in display_cogs.items(): for cog_name, cog_icon in display_cogs.items():
@@ -119,13 +120,11 @@ class InformationCog(commands.Cog, name="Information"):
if not cog: if not cog:
continue continue
cogname_str = f"{cog_icon} {cog_name}" cogname_str = f"{cog_icon} {cog_name}"
commands = [ commands = [f"`{cmd.name}`" for cmd in cog.get_commands() if not cmd.hidden]
f"`{cmd.name}`" for cmd in cog.get_commands() if not cmd.hidden
]
commands_str = ", ".join(commands) commands_str = ", ".join(commands)
embed.add_field(name=cogname_str, value=commands_str, inline=False) 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) embed.set_footer(text=f"Requested by: {ctx.author}", icon_url=avatar)
await ctx.send(embed=embed) await ctx.send(embed=embed)
+45 -48
View File
@@ -1,13 +1,10 @@
import discord import discord
from discord import channel
from discord.ext import commands from discord.ext import commands
import time
import lavalink import lavalink
import re
import random
import asyncio import asyncio
from utils.database import AutoJoin from bot import ChristmasBot
from utils import metadata
from utils.EmbedGenerator import EmbedGenerator from utils.EmbedGenerator import EmbedGenerator
@@ -16,12 +13,12 @@ class MusicCog(commands.Cog, name="Music"):
self.bot = bot self.bot = bot
self.stream = "https://azuracast.exobot.site/radio/8000/radio.opus" 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 = lavalink.Client(bot.user.id)
bot.lavalink.add_node('de-1.rivalmc.net', 2333, '12345', 'eu', bot.lavalink.add_node("de-1.rivalmc.net", 2333, "12345", "eu", "poggers")
'poggers') bot.add_listener(
bot.add_listener(self.bot.lavalink.voice_update_handler, self.bot.lavalink.voice_update_handler, "on_socket_response"
'on_socket_response') )
lavalink.add_event_hook(self.track_hook) lavalink.add_event_hook(self.track_hook)
@@ -29,19 +26,20 @@ class MusicCog(commands.Cog, name="Music"):
async def async_init(self): async def async_init(self):
await self.bot.wait_until_ready() await self.bot.wait_until_ready()
channels = await AutoJoin.get_channels(self.bot)
channels = []
# We startup to fast #NOTPOGGERS # We startup to fast #NOTPOGGERS
await asyncio.sleep(5) await asyncio.sleep(5)
for x in channels: for channel in channels:
guild = self.bot.get_guild(x[0]) guild = self.bot.get_guild(channel[0])
player = self.bot.lavalink.player_manager.create(x[0], player = self.bot.lavalink.player_manager.create(
endpoint=str( channel[0], endpoint=str(guild.region)
guild.region)) )
track = await player.node.get_tracks(self.stream) track = await player.node.get_tracks(self.stream)
if not player.is_playing: if not player.is_playing:
await player.play(track["tracks"][0]) 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): 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."""
@@ -56,31 +54,32 @@ class MusicCog(commands.Cog, name="Music"):
async def ensure_voice(self, ctx): async def ensure_voice(self, ctx):
"""This check ensures that the bot and command author are in the same voicechannel.""" """This check ensures that the bot and command author are in the same voicechannel."""
player = self.bot.lavalink.player_manager.create(ctx.guild.id, player = self.bot.lavalink.player_manager.create(
endpoint=str( ctx.guild.id, endpoint=str(ctx.guild.region)
ctx.guild.region)) )
should_connect = ctx.command.name in ('connect', ) should_connect = ctx.command.name in ("connect",)
if not ctx.author.voice or not ctx.author.voice.channel: 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 player.is_connected:
if not should_connect: if not should_connect:
raise commands.CommandInvokeError('Not connected.') raise commands.CommandInvokeError("Not connected.")
permissions = ctx.author.voice.channel.permissions_for(ctx.me) 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( raise commands.CommandInvokeError(
'I need the `CONNECT` and `SPEAK` permissions.') "I need the `CONNECT` and `SPEAK` permissions."
)
player.store('channel', ctx.channel.id) player.store("channel", ctx.channel.id)
await self.connect_to(ctx.guild.id, await self.connect_to(ctx.guild.id, str(ctx.author.voice.channel.id))
str(ctx.author.voice.channel.id))
else: else:
if int(player.channel_id) != ctx.author.voice.channel.id: if int(player.channel_id) != ctx.author.voice.channel.id:
raise commands.CommandInvokeError( raise commands.CommandInvokeError("You need to be in my voicechannel.")
'You need to be in my voicechannel.')
async def track_hook(self, event): async def track_hook(self, event):
if isinstance(event, lavalink.events.QueueEndEvent): if isinstance(event, lavalink.events.QueueEndEvent):
@@ -92,56 +91,54 @@ class MusicCog(commands.Cog, name="Music"):
ws = self.bot._connection._get_websocket(guild_id) ws = self.bot._connection._get_websocket(guild_id)
await ws.voice_state(str(guild_id), channel_id) await ws.voice_state(str(guild_id), channel_id)
@commands.command(name='connect') @commands.command(name="connect")
async def connect(self, ctx): async def connect(self, ctx):
"""Starts vibing.""" """Starts vibing."""
player = self.bot.lavalink.player_manager.get(ctx.guild.id) player = self.bot.lavalink.player_manager.get(ctx.guild.id)
results = await player.node.get_tracks(self.stream) results = await player.node.get_tracks(self.stream)
if not results or not results['tracks']: if not results or not results["tracks"]:
return await ctx.send('Nothing found!') return await ctx.send("Nothing found!")
if results['loadType'] == 'PLAYLIST_LOADED': if results["loadType"] == "PLAYLIST_LOADED":
tracks = results['tracks'] tracks = results["tracks"]
for track in tracks: for track in tracks:
player.add(requester=ctx.author.id, track=track) player.add(requester=ctx.author.id, track=track)
else: else:
track = results['tracks'][0] track = results["tracks"][0]
track = lavalink.models.AudioTrack(track, track = lavalink.models.AudioTrack(track, ctx.author.id, recommended=True)
ctx.author.id,
recommended=True)
player.add(requester=ctx.author.id, track=track) player.add(requester=ctx.author.id, track=track)
if not player.is_playing: if not player.is_playing:
await player.play() await player.play()
@commands.command(aliases=['dc']) @commands.command(aliases=["dc"])
async def disconnect(self, ctx): 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) player = self.bot.lavalink.player_manager.get(ctx.guild.id)
if not player.is_connected: if not player.is_connected:
return await ctx.send('Not connected.') return await ctx.send("Not connected.")
if not ctx.author.voice or ( if not ctx.author.voice or (
player.is_connected player.is_connected
and ctx.author.voice.channel.id != int(player.channel_id)): and ctx.author.voice.channel.id != int(player.channel_id)
return await ctx.send('You\'re not in my voicechannel!') ):
return await ctx.send("You're not in my voicechannel!")
player.queue.clear() player.queue.clear()
await player.stop() await player.stop()
await self.connect_to(ctx.guild.id, None) 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): async def now_playing(self, ctx):
"""Stop and disconnect the player and controller.""" """Stop and disconnect the player and controller."""
np = await metadata.fetch_metadata()
em = discord.Embed(color=self.bot.colors["embed"]) em = discord.Embed(color=self.bot.colors["embed"])
em.set_thumbnail(url=np["thumbnail"]) # em.set_thumbnail(url=np["thumbnail"])
em.add_field(name="Currently playing:", value=np["name"]) em.add_field(name="Currently playing:", value="Some song")
await EmbedGenerator.SendWithFooter(ctx, em) await EmbedGenerator.SendWithFooter(ctx, em)
def setup(bot): def setup(bot: ChristmasBot):
bot.add_cog(MusicCog(bot)) bot.add_cog(MusicCog(bot))
+73 -61
View File
@@ -11,32 +11,36 @@ from io import BytesIO
from platform import python_version from platform import python_version
from contextlib import redirect_stdout from contextlib import redirect_stdout
from discord.ext.commands.context import Context
from bot import ChristmasBot
class OwnerCog(commands.Cog): class OwnerCog(commands.Cog):
def __init__(self, bot): def __init__(self, bot: ChristmasBot):
self.bot = bot self.bot = bot
self._last_result = None self._last_result = None
@staticmethod @staticmethod
def cleanup_code(content): def cleanup_code(content):
if content.startswith('```') and content.endswith('```'): if content.startswith("```") and content.endswith("```"):
return '\n'.join(content.split('\n')[1:-1]) return "\n".join(content.split("\n")[1:-1])
return content.strip('` \n') return content.strip("` \n")
# Hidden means it won't show up on the default help. # 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() @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.""" """Command which Loads a Module."""
try: try:
self.bot.load_extension(cog) self.bot.load_extension(cog)
except Exception as e: except Exception as e:
await ctx.send(f'**`ERROR:`** {type(e).__name__} - {e}') await ctx.send(f"**`ERROR:`** {type(e).__name__} - {e}")
else: else:
await ctx.send('**`SUCCESS`**') await ctx.send("**`SUCCESS`**")
@commands.command(name='unload', hidden=True) @commands.command(name="unload", hidden=True)
@commands.is_owner() @commands.is_owner()
async def _cog_unload(self, ctx, *, cog: str): async def _cog_unload(self, ctx, *, cog: str):
"""Command which Unloads a Module.""" """Command which Unloads a Module."""
@@ -44,11 +48,11 @@ class OwnerCog(commands.Cog):
try: try:
self.bot.unload_extension(cog) self.bot.unload_extension(cog)
except Exception as e: except Exception as e:
await ctx.send(f'**`ERROR:`** {type(e).__name__} - {e}') await ctx.send(f"**`ERROR:`** {type(e).__name__} - {e}")
else: else:
await ctx.send('**`SUCCESS`**') await ctx.send("**`SUCCESS`**")
@commands.command(name='reload', hidden=True) @commands.command(name="reload", hidden=True)
@commands.is_owner() @commands.is_owner()
async def _cog_reload(self, ctx, *, cog: str): async def _cog_reload(self, ctx, *, cog: str):
"""Command which Reloads a Module.""" """Command which Reloads a Module."""
@@ -57,35 +61,29 @@ class OwnerCog(commands.Cog):
self.bot.unload_extension(cog) self.bot.unload_extension(cog)
self.bot.load_extension(cog) self.bot.load_extension(cog)
except Exception as e: except Exception as e:
await ctx.send(f'**`ERROR:`** {type(e).__name__} - {e}') await ctx.send(f"**`ERROR:`** {type(e).__name__} - {e}")
else: else:
await ctx.send('**`SUCCESS`**') await ctx.send("**`SUCCESS`**")
@commands.command(name='shutdown', hidden=True) @commands.command(name="shutdown", hidden=True)
@commands.is_owner() @commands.is_owner()
async def shutdown(self, ctx): async def shutdown(self, ctx):
"""Command which shutdowns the bot.""" """Command which shutdowns the bot."""
await ctx.bot.logout() await ctx.bot.logout()
@commands.is_owner() @commands.is_owner()
@commands.command(pass_context=True, @commands.command(pass_context=True, hidden=True, name="eval", aliases=["evaluate"])
hidden=True,
name='eval',
aliases=['evaluate'])
async def _eval(self, ctx, *, body: str): async def _eval(self, ctx, *, body: str):
env = { env = {
'bot': self.bot, "bot": self.bot,
'ctx': ctx, "ctx": ctx,
'channel': ctx.channel, "channel": ctx.channel,
'author': ctx.author, "author": ctx.author,
'guild': ctx.guild, "guild": ctx.guild,
'message': ctx.message, "message": ctx.message,
'_': self._last_result "_": 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()) env.update(globals())
body = self.cleanup_code(body) body = self.cleanup_code(body)
@@ -98,26 +96,34 @@ class OwnerCog(commands.Cog):
exec(to_compile, env) exec(to_compile, env)
except Exception as e: except Exception as e:
# await ctx.message.add_reaction('naokoerror:447495055603662849') # await ctx.message.add_reaction('naokoerror:447495055603662849')
fooem = discord.Embed(color=0xff0000) fooem = discord.Embed(color=0xFF0000)
fooem.add_field(name="Code evaluation was not successful.", fooem.add_field(
value=f'```\n{e.__class__.__name__}: {e}\n```') name="Code evaluation was not successful.",
fooem.set_footer(text=f"Evaluated using Python {python_version()}", value=f"```\n{e.__class__.__name__}: {e}\n```",
icon_url="http://i.imgur.com/9EftiVK.png") )
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.send(embed=fooem)
# await ctx.message.remove_reaction('a:loading:452489773396000778', member=ctx.me) # await ctx.message.remove_reaction('a:loading:452489773396000778', member=ctx.me)
func = env['func'] func = env["func"]
try: try:
with redirect_stdout(stdout): with redirect_stdout(stdout):
ret = await func() ret = await func()
except Exception as e: except Exception as e:
value = stdout.getvalue() value = stdout.getvalue()
# await ctx.message.add_reaction('naokoerror:447495055603662849') # await ctx.message.add_reaction('naokoerror:447495055603662849')
fooem = discord.Embed(color=0xff0000) fooem = discord.Embed(color=0xFF0000)
fooem.add_field(name="Code evaluation was not successful.", fooem.add_field(
value=f'```\n{value}{traceback.format_exc()}\n```') name="Code evaluation was not successful.",
fooem.set_footer(text=f"Evaluated using Python {python_version()}", value=f"```\n{value}{traceback.format_exc()}\n```",
icon_url="http://i.imgur.com/9EftiVK.png") )
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.send(embed=fooem)
try: try:
# await ctx.message.remove_reaction('a:loading:452489773396000778', member=ctx.me) # await ctx.message.remove_reaction('a:loading:452489773396000778', member=ctx.me)
@@ -129,7 +135,8 @@ class OwnerCog(commands.Cog):
value = stdout.getvalue() value = stdout.getvalue()
try: try:
await ctx.message.remove_reaction( await ctx.message.remove_reaction(
'a:loading:452489773396000778', member=ctx.me) "a:loading:452489773396000778", member=ctx.me
)
# await ctx.message.add_reaction('naokotick:447494238872141827') # await ctx.message.add_reaction('naokotick:447494238872141827')
except Exception: except Exception:
pass pass
@@ -137,37 +144,42 @@ class OwnerCog(commands.Cog):
if ret is None: if ret is None:
if value: if value:
sfooem = discord.Embed(color=0x170041) sfooem = discord.Embed(color=0x170041)
sfooem.add_field(name="Code evaluation was successful!", sfooem.add_field(
value=f'```\n{value}\n```') name="Code evaluation was successful!",
value=f"```\n{value}\n```",
)
sfooem.set_footer( sfooem.set_footer(
text=f"Evaluated using Python {python_version()}", 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) await ctx.send(embed=sfooem)
else: else:
self._last_result = ret self._last_result = ret
ssfooem = discord.Embed(color=0x170041) ssfooem = discord.Embed(color=0x170041)
ssfooem.add_field(name="Code evaluation was successful!", ssfooem.add_field(
value=f'```\n{value}{ret}\n```') name="Code evaluation was successful!",
value=f"```\n{value}{ret}\n```",
)
ssfooem.set_footer( ssfooem.set_footer(
text=f"Evaluated using Python {python_version()}", 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) await ctx.send(embed=ssfooem)
@commands.is_owner() @commands.is_owner()
@commands.command(hidden=True, aliases=['exec']) @commands.command(hidden=True, aliases=["exec"])
async def execute(self, ctx, *, text: str): async def execute(self, ctx, *, text: str):
"""Do a shell command.""" """Do a shell command."""
message = await ctx.send(f"Loading...") message = await ctx.send(f"Loading...")
proc = await asyncio.create_subprocess_shell(text, proc = await asyncio.create_subprocess_shell(
stdin=None, text, stdin=None, stderr=PIPE, stdout=PIPE
stderr=PIPE, )
stdout=PIPE) out = (await proc.stdout.read()).decode("utf-8").strip()
out = (await proc.stdout.read()).decode('utf-8').strip() err = (await proc.stderr.read()).decode("utf-8").strip()
err = (await proc.stderr.read()).decode('utf-8').strip()
if not out and not err: if not out and not err:
await message.delete() await message.delete()
return await ctx.message.add_reaction('👌') return await ctx.message.add_reaction("👌")
content = "" content = ""
@@ -178,12 +190,12 @@ class OwnerCog(commands.Cog):
if len(content) > 1500: if len(content) > 1500:
try: try:
data = BytesIO(content.encode('utf-8')) data = BytesIO(content.encode("utf-8"))
await message.delete() await message.delete()
await ctx.send(content=f"The result was a bit too long..", await ctx.send(
file=discord.File( content=f"The result was a bit too long..",
data, file=discord.File(data, filename=f"result_{int(time.time())}.txt"),
filename=f"result_{int(time.time())}.txt")) )
except asyncio.TimeoutError as e: except asyncio.TimeoutError as e:
await message.delete() await message.delete()
return await ctx.send(e) return await ctx.send(e)
@@ -191,5 +203,5 @@ class OwnerCog(commands.Cog):
await message.edit(content=f"```fix\n{content}\n```") await message.edit(content=f"```fix\n{content}\n```")
def setup(bot): def setup(bot: ChristmasBot):
bot.add_cog(OwnerCog(bot)) bot.add_cog(OwnerCog(bot))
+9 -16
View File
@@ -1,33 +1,26 @@
from discord.ext import commands from discord.ext import commands
from utils.database import AutoJoin
import discord
from utils.EmbedGenerator import EmbedGenerator from utils.EmbedGenerator import EmbedGenerator
from bot import ChristmasBot
from discord.ext.commands import Context from discord.ext.commands import Context
class SettingsCog(commands.Cog, name="Settings"): class SettingsCog(commands.Cog, name="Settings"):
def __init__(self, bot): def __init__(self, bot: ChristmasBot):
self.bot = bot 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.group(aliases=["aj"], invoke_without_command=True)
@commands.cooldown(rate=1, per=5, type=commands.BucketType.user) @commands.cooldown(rate=1, per=5, type=commands.BucketType.user)
async def autojoin(self, ctx): async def autojoin(self, ctx: Context):
await EmbedGenerator.Message( await EmbedGenerator.Message(
ctx, "Autojoin", ctx,
f"Usage:\n\n`{ctx.prefix}autojoin set`\n`{ctx.prefix}autojoin unset`" "Autojoin",
f"Usage:\n\n`{ctx.prefix}autojoin set`\n`{ctx.prefix}autojoin unset`",
) )
@autojoin.command(name="set") @autojoin.command(name="set")
@commands.has_permissions(manage_channels=True) @commands.has_permissions(manage_channels=True)
@commands.cooldown(rate=1, per=5, type=commands.BucketType.user) @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 vc = ctx.author.voice.channel
await AutoJoin.update_channel(self.bot, ctx.guild.id, vc.id) await AutoJoin.update_channel(self.bot, ctx.guild.id, vc.id)
await EmbedGenerator.Message(ctx, "Autojoin", "`enabled`") await EmbedGenerator.Message(ctx, "Autojoin", "`enabled`")
@@ -35,11 +28,11 @@ class SettingsCog(commands.Cog, name="Settings"):
@autojoin.command(name="unset") @autojoin.command(name="unset")
@commands.has_permissions(manage_channels=True) @commands.has_permissions(manage_channels=True)
@commands.cooldown(rate=1, per=5, type=commands.BucketType.user) @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 vc = ctx.author.voice.channel
await AutoJoin.del_channel(self.bot, ctx.guild.id) await AutoJoin.del_channel(self.bot, ctx.guild.id)
await EmbedGenerator.Message(ctx, "Autojoin", "`disabled`") await EmbedGenerator.Message(ctx, "Autojoin", "`disabled`")
def setup(bot): def setup(bot: ChristmasBot):
bot.add_cog(SettingsCog(bot)) bot.add_cog(SettingsCog(bot))
Generated
+535
View File
@@ -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"},
]
+19
View File
@@ -0,0 +1,19 @@
[tool.poetry]
name = "christmasbot"
version = "0.1.0"
description = ""
authors = ["Your Name <you@example.com>"]
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"
-7
View File
@@ -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)]}
-12
View File
@@ -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)
+10 -16
View File
@@ -7,26 +7,21 @@ from discord.ext.commands import Context
class EmbedGenerator: class EmbedGenerator:
@staticmethod @staticmethod
async def Error(ctx: Context, message: str, **kwargs): async def Error(ctx: Context, message: str, **kwargs) -> Embed:
color = ctx.bot.colors["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) return await EmbedGenerator.SendWithFooter(ctx, em, **kwargs)
@staticmethod @staticmethod
async def Message(ctx: Context, async def Message(ctx: Context, title: str, message: Optional[str] = "", **kwargs):
title: str,
message: Optional[str] = '',
**kwargs):
color = ctx.bot.colors["embed"] color = ctx.bot.colors["embed"]
em = Embed(title=title, description=message, color=color) em = Embed(title=title, description=message, color=color)
return await EmbedGenerator.SendWithFooter(ctx, em, **kwargs) return await EmbedGenerator.SendWithFooter(ctx, em, **kwargs)
@staticmethod @staticmethod
async def Image(ctx: Context, async def Image(
title: str, ctx: Context, title: str, url: str, message: Optional[str] = "", **kwargs
url: str, ):
message: Optional[str] = '',
**kwargs):
color = ctx.bot.colors["embed"] color = ctx.bot.colors["embed"]
em = Embed(title=title, description=message, url=url, color=color) em = Embed(title=title, description=message, url=url, color=color)
em.set_image(url=url) em.set_image(url=url)
@@ -39,10 +34,9 @@ class EmbedGenerator:
return await EmbedGenerator.SendWithFooter(ctx, em, **kwargs) return await EmbedGenerator.SendWithFooter(ctx, em, **kwargs)
@staticmethod @staticmethod
async def SendWithFooter(ctx: Context, em: Embed, async def SendWithFooter(ctx: Context, em: Embed, **kwargs) -> discord.Message:
**kwargs) -> discord.Message: avatar = ctx.author.avatar.with_static_format("jpeg")
avatar = ctx.author.avatar_url_as() em.set_footer(text=f"Requested by: {ctx.author}", icon_url=avatar)
em.set_footer(text=f'Requested by: {ctx.author}', icon_url=avatar) if kwargs.get("no_send", False):
if kwargs.get('no_send', False):
return em return em
return await ctx.send(embed=em, **kwargs) return await ctx.send(embed=em, **kwargs)
-32
View File
@@ -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))
-60
View File
@@ -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]
+188 -165
View File
@@ -43,13 +43,10 @@ class Pages:
permissions: discord.Permissions permissions: discord.Permissions
Our permissions for the channel. Our permissions for the channel.
""" """
def __init__(self,
ctx, def __init__(
*, self, ctx, *, entries, per_page=12, show_entry_count=True, hide_no_results=False
entries, ):
per_page=12,
show_entry_count=True,
hide_no_results=False):
self.hide_no_results = hide_no_results self.hide_no_results = hide_no_results
self.bot = ctx.bot self.bot = ctx.bot
self.entries = entries self.entries = entries
@@ -65,15 +62,19 @@ class Pages:
self.paginating = len(entries) > per_page self.paginating = len(entries) > per_page
self.show_entry_count = show_entry_count self.show_entry_count = show_entry_count
self.reaction_emojis = [ self.reaction_emojis = [
('\N{BLACK LEFT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR}', (
self.first_page), "\N{BLACK LEFT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR}",
('\N{BLACK LEFT-POINTING TRIANGLE}', self.previous_page), self.first_page,
('\N{BLACK RIGHT-POINTING TRIANGLE}', self.next_page), ),
('\N{BLACK RIGHT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR}', ("\N{BLACK LEFT-POINTING TRIANGLE}", self.previous_page),
self.last_page), ("\N{BLACK RIGHT-POINTING TRIANGLE}", self.next_page),
('\N{INPUT SYMBOL FOR NUMBERS}', self.numbered_page), (
('\N{BLACK SQUARE FOR STOP}', self.stop_pages), "\N{BLACK RIGHT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR}",
('\N{INFORMATION SOURCE}', self.show_help), 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: if ctx.guild is not None:
@@ -82,20 +83,20 @@ class Pages:
self.permissions = self.channel.permissions_for(ctx.bot.user) self.permissions = self.channel.permissions_for(ctx.bot.user)
if not self.permissions.embed_links: 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: if not self.permissions.send_messages:
raise CannotPaginate('Bot cannot send messages.') raise CannotPaginate("Bot cannot send messages.")
if self.paginating: if self.paginating:
# verify we can actually use the pagination session # verify we can actually use the pagination session
if not self.permissions.add_reactions: if not self.permissions.add_reactions:
raise CannotPaginate( raise CannotPaginate("Bot does not have add reactions permission.")
'Bot does not have add reactions permission.')
if not self.permissions.read_message_history: if not self.permissions.read_message_history:
raise CannotPaginate( raise CannotPaginate(
'Bot does not have Read Message History permission.') "Bot does not have Read Message History permission."
)
def get_page(self, page): def get_page(self, page):
base = (page - 1) * self.per_page base = (page - 1) * self.per_page
@@ -106,39 +107,40 @@ class Pages:
self.current_page = page self.current_page = page
entries = self.get_page(page) entries = self.get_page(page)
p = [] p = []
for index, entry in enumerate(entries, for index, entry in enumerate(entries, 1 + ((page - 1) * self.per_page)):
1 + ((page - 1) * self.per_page)): p.append(f"{index}. {entry}")
p.append(f'{index}. {entry}')
if self.maximum_pages > 1: if self.maximum_pages > 1:
if self.show_entry_count: if self.show_entry_count:
text = f'Page {page}/{self.maximum_pages}' \ text = (
f' ({len(self.entries)} entries)' f"Page {page}/{self.maximum_pages}"
f" ({len(self.entries)} entries)"
)
else: else:
text = f'Page {page}/{self.maximum_pages}' text = f"Page {page}/{self.maximum_pages}"
self.embed.set_footer(text=text) self.embed.set_footer(text=text)
if not self.paginating: if not self.paginating:
self.embed.description = '\n'.join(p) self.embed.description = "\n".join(p)
return await self.channel.send(embed=self.embed) return await self.channel.send(embed=self.embed)
if not first: if not first:
self.embed.description = '\n'.join(p) self.embed.description = "\n".join(p)
await self.message.edit(embed=self.embed) await self.message.edit(embed=self.embed)
return return
p.append('') p.append("")
p.append('Confused? React with \N{INFORMATION SOURCE} for more info.') p.append("Confused? React with \N{INFORMATION SOURCE} for more info.")
self.embed.description = '\n'.join(p) self.embed.description = "\n".join(p)
self.message = await self.channel.send(embed=self.embed) 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): 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: 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 # no |<< or >>| buttons if we only have two pages
# we can't forbid it if someone ends up using it but remove # we can't forbid it if someone ends up using it but remove
# it from the default set # it from the default set
@@ -174,20 +176,19 @@ class Pages:
"""lets you type a page number to go to""" """lets you type a page number to go to"""
# noinspection PyListCreation # noinspection PyListCreation
to_delete = [] to_delete = []
to_delete.append(await to_delete.append(await self.channel.send("What page do you want to go to?"))
self.channel.send('What page do you want to go to?'))
def message_check(m): def message_check(m):
return m.author == self.author and \ return (
self.channel == m.channel and \ m.author == self.author
m.content.isdigit() and self.channel == m.channel
and m.content.isdigit()
)
try: try:
msg = await self.bot.wait_for('message', msg = await self.bot.wait_for("message", check=message_check, timeout=30.0)
check=message_check,
timeout=30.0)
except asyncio.TimeoutError: 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) await asyncio.sleep(5)
else: else:
page = int(msg.content) page = int(msg.content)
@@ -195,8 +196,11 @@ class Pages:
if page != 0 and page <= self.maximum_pages: if page != 0 and page <= self.maximum_pages:
await self.show_page(page) await self.show_page(page)
else: else:
to_delete.append(await self.channel.send( to_delete.append(
f'Invalid page given. ({page}/{self.maximum_pages})')) await self.channel.send(
f"Invalid page given. ({page}/{self.maximum_pages})"
)
)
await asyncio.sleep(5) await asyncio.sleep(5)
# noinspection PyBroadException # noinspection PyBroadException
@@ -208,19 +212,20 @@ class Pages:
async def show_help(self): async def show_help(self):
"""shows this message""" """shows this message"""
messages = [ messages = [
'Welcome to the interactive paginator!\n', "Welcome to the interactive paginator!\n",
'This interactively allows you to see pages ' "This interactively allows you to see pages "
'of text by navigating with ' "of text by navigating with "
'reactions. They are as follows:\n' "reactions. They are as follows:\n",
] ]
for (emoji, func) in self.reaction_emojis: 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.clear_fields()
self.embed.set_footer( 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) await self.message.edit(embed=self.embed)
async def go_back_to_current_page(): async def go_back_to_current_page():
@@ -241,7 +246,7 @@ class Pages:
if reaction.message.id != self.message.id: if reaction.message.id != self.message.id:
return False return False
if reaction.emoji == '🔣': if reaction.emoji == "🔣":
self.match = self.add_rest_reactions self.match = self.add_rest_reactions
return True return True
@@ -258,7 +263,7 @@ class Pages:
if not self.entries and not self.hide_no_results: if not self.entries and not self.hide_no_results:
# I just say no results found because that's my most common use # I just say no results found because that's my most common use
# case. # 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) first_page = self.show_page(1, first=True)
if not self.paginating: if not self.paginating:
@@ -270,7 +275,8 @@ class Pages:
while self.paginating: while self.paginating:
try: try:
reaction, user = await self.bot.wait_for( 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: except asyncio.TimeoutError:
self.paginating = False self.paginating = False
# noinspection PyBroadException # noinspection PyBroadException
@@ -292,6 +298,7 @@ class Pages:
class EmbedPages: class EmbedPages:
"""Similar to Pages, but you use [`discord.Embed`]""" """Similar to Pages, but you use [`discord.Embed`]"""
def __init__(self, ctx, *, embeds): def __init__(self, ctx, *, embeds):
self.bot = ctx.bot self.bot = ctx.bot
self.embeds = embeds self.embeds = embeds
@@ -302,15 +309,19 @@ class EmbedPages:
self.maximum_pages = pages self.maximum_pages = pages
self.paginating = len(embeds) > 1 self.paginating = len(embeds) > 1
self.reaction_emojis = [ self.reaction_emojis = [
('\N{BLACK LEFT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR}', (
self.first_page), "\N{BLACK LEFT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR}",
('\N{BLACK LEFT-POINTING TRIANGLE}', self.previous_page), self.first_page,
('\N{BLACK RIGHT-POINTING TRIANGLE}', self.next_page), ),
('\N{BLACK RIGHT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR}', ("\N{BLACK LEFT-POINTING TRIANGLE}", self.previous_page),
self.last_page), ("\N{BLACK RIGHT-POINTING TRIANGLE}", self.next_page),
('\N{INPUT SYMBOL FOR NUMBERS}', self.numbered_page), (
('\N{BLACK SQUARE FOR STOP}', self.stop_pages), "\N{BLACK RIGHT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR}",
('\N{INFORMATION SOURCE}', self.show_help), 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: if ctx.guild is not None:
@@ -319,20 +330,20 @@ class EmbedPages:
self.permissions = self.channel.permissions_for(ctx.bot.user) self.permissions = self.channel.permissions_for(ctx.bot.user)
if not self.permissions.embed_links: 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: if not self.permissions.send_messages:
raise CannotPaginate('Bot cannot send messages.') raise CannotPaginate("Bot cannot send messages.")
if self.paginating: if self.paginating:
# verify we can actually use the pagination session # verify we can actually use the pagination session
if not self.permissions.add_reactions: if not self.permissions.add_reactions:
raise CannotPaginate( raise CannotPaginate("Bot does not have add reactions permission.")
'Bot does not have add reactions permission.')
if not self.permissions.read_message_history: if not self.permissions.read_message_history:
raise CannotPaginate( 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): async def show_page(self, page, *, first=False):
# noinspection PyAttributeOutsideInit # noinspection PyAttributeOutsideInit
@@ -341,7 +352,7 @@ class EmbedPages:
p = [] p = []
if self.maximum_pages > 1: if self.maximum_pages > 1:
text = f'Page {page}/{self.maximum_pages}' text = f"Page {page}/{self.maximum_pages}"
embed.set_footer(text=text) embed.set_footer(text=text)
@@ -351,19 +362,20 @@ class EmbedPages:
if not first: if not first:
return await self.message.edit(embed=embed) return await self.message.edit(embed=embed)
p.append('') p.append("")
p.append('Confused? React with \N{INFORMATION SOURCE} for more info.') p.append("Confused? React with \N{INFORMATION SOURCE} for more info.")
embed.description = '' if embed.description == discord.Embed.Empty \ embed.description = (
else embed.description "" if embed.description == discord.Embed.Empty else embed.description
embed.description += '\n'.join(p) )
embed.description += "\n".join(p)
self.message = await self.channel.send(embed=embed) self.message = await self.channel.send(embed=embed)
await self.message.add_reaction('🔣') await self.message.add_reaction("🔣")
async def add_rest_reactions(self): 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: 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 # no |<< or >>| buttons if we only have two pages
# we can't forbid it if someone ends up using it but remove # we can't forbid it if someone ends up using it but remove
# it from the default set # it from the default set
@@ -399,20 +411,19 @@ class EmbedPages:
"""lets you type a page number to go to""" """lets you type a page number to go to"""
# noinspection PyListCreation # noinspection PyListCreation
to_delete = [] to_delete = []
to_delete.append(await to_delete.append(await self.channel.send("What page do you want to go to?"))
self.channel.send('What page do you want to go to?'))
def message_check(m): def message_check(m):
return m.author == self.author and \ return (
self.channel == m.channel and \ m.author == self.author
m.content.isdigit() and self.channel == m.channel
and m.content.isdigit()
)
try: try:
msg = await self.bot.wait_for('message', msg = await self.bot.wait_for("message", check=message_check, timeout=30.0)
check=message_check,
timeout=30.0)
except asyncio.TimeoutError: 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) await asyncio.sleep(5)
else: else:
page = int(msg.content) page = int(msg.content)
@@ -420,8 +431,11 @@ class EmbedPages:
if page != 0 and page <= self.maximum_pages: if page != 0 and page <= self.maximum_pages:
await self.show_page(page) await self.show_page(page)
else: else:
to_delete.append(await self.channel.send( to_delete.append(
f'Invalid page given. ({page}/{self.maximum_pages})')) await self.channel.send(
f"Invalid page given. ({page}/{self.maximum_pages})"
)
)
await asyncio.sleep(5) await asyncio.sleep(5)
# noinspection PyBroadException # noinspection PyBroadException
@@ -433,21 +447,22 @@ class EmbedPages:
async def show_help(self): async def show_help(self):
"""shows this message""" """shows this message"""
messages = [ messages = [
'Welcome to the interactive paginator!\n', "Welcome to the interactive paginator!\n",
'This interactively allows you to see pages ' "This interactively allows you to see pages "
'of text by navigating with ' "of text by navigating with "
'reactions. They are as follows:\n' "reactions. They are as follows:\n",
] ]
for (emoji, func) in self.reaction_emojis: for (emoji, func) in self.reaction_emojis:
messages.append(f'{emoji} {func.__doc__}') messages.append(f"{emoji} {func.__doc__}")
embed = discord.Embed() embed = discord.Embed()
embed.description = '\n'.join(messages) embed.description = "\n".join(messages)
embed.clear_fields() embed.clear_fields()
embed.set_footer( 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) await self.message.edit(embed=embed)
async def go_back_to_current_page(): async def go_back_to_current_page():
@@ -468,7 +483,7 @@ class EmbedPages:
if reaction.message.id != self.message.id: if reaction.message.id != self.message.id:
return False return False
if reaction.emoji == '🔣': if reaction.emoji == "🔣":
self.match = self.add_rest_reactions self.match = self.add_rest_reactions
return True return True
@@ -492,7 +507,8 @@ class EmbedPages:
while self.paginating: while self.paginating:
try: try:
reaction, user = await self.bot.wait_for( 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: except asyncio.TimeoutError:
self.paginating = False self.paginating = False
# noinspection PyBroadException # noinspection PyBroadException
@@ -516,6 +532,7 @@ class FieldPages(Pages):
"""Similar to Pages except entries should be a list of """Similar to Pages except entries should be a list of
tuples having (key, value) to show as embed fields instead. tuples having (key, value) to show as embed fields instead.
""" """
async def show_page(self, page, *, first=False): async def show_page(self, page, *, first=False):
# noinspection PyAttributeOutsideInit # noinspection PyAttributeOutsideInit
self.current_page = page self.current_page = page
@@ -529,10 +546,12 @@ class FieldPages(Pages):
if self.maximum_pages > 1: if self.maximum_pages > 1:
if self.show_entry_count: if self.show_entry_count:
text = f'Page {page}/{self.maximum_pages} ' \ text = (
f'({len(self.entries)} entries)' f"Page {page}/{self.maximum_pages} "
f"({len(self.entries)} entries)"
)
else: else:
text = f'Page {page}/{self.maximum_pages}' text = f"Page {page}/{self.maximum_pages}"
self.embed.set_footer(text=text) self.embed.set_footer(text=text)
@@ -545,7 +564,7 @@ class FieldPages(Pages):
self.message = await self.channel.send(embed=self.embed) self.message = await self.channel.send(embed=self.embed)
for (reaction, _) in self.reaction_emojis: 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 # no |<< or >>| buttons if we only have two pages
# we can't forbid it if someone ends up using it but remove # we can't forbid it if someone ends up using it but remove
# it from the default set # it from the default set
@@ -559,7 +578,7 @@ class FieldPages(Pages):
# ?help command # ?help command
# -> could be a subcommand # -> 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): def cleanup_prefix(bot, prefix):
@@ -567,7 +586,7 @@ def cleanup_prefix(bot, prefix):
if m: if m:
user = bot.get_user(int(m.group(1))) user = bot.get_user(int(m.group(1)))
if user: if user:
return f'@{user.name} ' return f"@{user.name} "
return prefix return prefix
@@ -586,39 +605,40 @@ def _command_signature(cmd):
result = [cmd.qualified_name] result = [cmd.qualified_name]
if cmd.usage: if cmd.usage:
result.append(cmd.usage) result.append(cmd.usage)
return ' '.join(result) return " ".join(result)
params = cmd.clean_params params = cmd.clean_params
if not params: if not params:
return ' '.join(result) return " ".join(result)
for name, param in params.items(): for name, param in params.items():
if param.default is not param.empty: if param.default is not param.empty:
# We don't want None or '' to trigger the [name=value] case and # We don't want None or '' to trigger the [name=value] case and
# instead it should do [name] since [name=None] or [name=] are # instead it should do [name] since [name=None] or [name=] are
# not exactly useful for the user. # not exactly useful for the user.
should_print = param.default if isinstance( should_print = (
param.default, str) else param.default is not None param.default
if isinstance(param.default, str)
else param.default is not None
)
if should_print: if should_print:
result.append(f'[{name}={param.default!r}]') result.append(f"[{name}={param.default!r}]")
else: else:
result.append(f'[{name}]') result.append(f"[{name}]")
elif param.kind == param.VAR_POSITIONAL: elif param.kind == param.VAR_POSITIONAL:
result.append(f'[{name}...]') result.append(f"[{name}...]")
else: else:
result.append(f'<{name}>') result.append(f"<{name}>")
return ' '.join(result) return " ".join(result)
class HelpPaginator(Pages): class HelpPaginator(Pages):
def __init__(self, ctx, entries, *, per_page=4): def __init__(self, ctx, entries, *, per_page=4):
super().__init__(ctx, super().__init__(ctx, entries=entries, per_page=per_page, hide_no_results=True)
entries=entries,
per_page=per_page,
hide_no_results=True)
self.reaction_emojis.append( 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) self.total = len(entries)
@classmethod @classmethod
@@ -630,12 +650,11 @@ class HelpPaginator(Pages):
# remove the ones we can't run # remove the ones we can't run
entries = [ entries = [
cmd for cmd in entries cmd for cmd in entries if (await _can_run(cmd, ctx)) and not cmd.hidden
if (await _can_run(cmd, ctx)) and not cmd.hidden
] ]
self = cls(ctx, entries) self = cls(ctx, entries)
self.title = f'{cog_name} Commands'.upper() self.title = f"{cog_name} Commands".upper()
self.description = inspect.getdoc(cog) self.description = inspect.getdoc(cog)
self.prefix = cleanup_prefix(ctx.bot, ctx.prefix) self.prefix = cleanup_prefix(ctx.bot, ctx.prefix)
@@ -651,17 +670,16 @@ class HelpPaginator(Pages):
entries = [] entries = []
else: else:
entries = [ entries = [
cmd for cmd in entries cmd for cmd in entries if (await _can_run(cmd, ctx)) and not cmd.hidden
if (await _can_run(cmd, ctx)) and not cmd.hidden
] ]
self = cls(ctx, entries) self = cls(ctx, entries)
self.title = command.signature self.title = command.signature
if command.description: if command.description:
self.description = f'{command.description}\n\n{command.help}' self.description = f"{command.description}\n\n{command.help}"
else: 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) self.prefix = cleanup_prefix(ctx.bot, ctx.prefix)
return self return self
@@ -669,7 +687,7 @@ class HelpPaginator(Pages):
@classmethod @classmethod
async def from_bot(cls, ctx): async def from_bot(cls, ctx):
def key(c): def key(c):
return c.cog_name or '\u200bMisc' return c.cog_name or "\u200bMisc"
entries = sorted(ctx.bot.commands, key=key) entries = sorted(ctx.bot.commands, key=key)
nested_pages = [] nested_pages = []
@@ -681,8 +699,7 @@ class HelpPaginator(Pages):
for cog, commands in itertools.groupby(entries, key=key): for cog, commands in itertools.groupby(entries, key=key):
plausible = [ plausible = [
cmd for cmd in commands cmd for cmd in commands if (await _can_run(cmd, ctx)) and not cmd.hidden
if (await _can_run(cmd, ctx)) and not cmd.hidden
] ]
if len(plausible) == 0: if len(plausible) == 0:
continue continue
@@ -691,14 +708,14 @@ class HelpPaginator(Pages):
if description is None: if description is None:
description = discord.Embed.Empty description = discord.Embed.Empty
else: else:
description = inspect.getdoc( description = inspect.getdoc(description) or discord.Embed.Empty
description) or discord.Embed.Empty
nested_pages.extend((cog, description, plausible[i:i + per_page]) nested_pages.extend(
for i in range(0, len(plausible), per_page)) (cog, description, plausible[i : i + per_page])
for i in range(0, len(plausible), per_page)
)
self = cls(ctx, nested_pages, self = cls(ctx, nested_pages, per_page=1) # this forces the pagination session
per_page=1) # this forces the pagination session
self.prefix = cleanup_prefix(ctx.bot, ctx.prefix) self.prefix = cleanup_prefix(ctx.bot, ctx.prefix)
# swap the get_page implementation with # swap the get_page implementation with
@@ -713,7 +730,7 @@ class HelpPaginator(Pages):
# noinspection PyAttributeOutsideInit # noinspection PyAttributeOutsideInit
def get_bot_page(self, page): def get_bot_page(self, page):
cog, description, commands = self.entries[page - 1] cog, description, commands = self.entries[page - 1]
self.title = f'{cog} Commands' self.title = f"{cog} Commands"
self.description = description self.description = description
return commands return commands
@@ -734,13 +751,15 @@ class HelpPaginator(Pages):
signature = _command_signature signature = _command_signature
for entry in entries: for entry in entries:
self.embed.add_field(name=signature(entry), self.embed.add_field(
name=signature(entry),
value=entry.short_doc or "No help given", value=entry.short_doc or "No help given",
inline=False) inline=False,
)
if self.maximum_pages: if self.maximum_pages:
self.embed.set_author( 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: if not self.paginating:
@@ -752,7 +771,7 @@ class HelpPaginator(Pages):
self.message = await self.channel.send(embed=self.embed) self.message = await self.channel.send(embed=self.embed)
for (reaction, _) in self.reaction_emojis: 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 # no |<< or >>| buttons if we only have two pages
# we can't forbid it if someone ends up using it but remove # we can't forbid it if someone ends up using it but remove
# it from the default set # it from the default set
@@ -763,19 +782,20 @@ class HelpPaginator(Pages):
async def show_help(self): async def show_help(self):
"""shows this message""" """shows this message"""
self.embed.title = 'Paginator help' self.embed.title = "Paginator help"
self.embed.description = 'Hello! Welcome to the help page.' self.embed.description = "Hello! Welcome to the help page."
messages = [ messages = [f"{emoji} {func.__doc__}" for emoji, func in self.reaction_emojis]
f'{emoji} {func.__doc__}' for emoji, func in self.reaction_emojis
]
self.embed.clear_fields() self.embed.clear_fields()
self.embed.add_field(name='What are these reactions for?', self.embed.add_field(
value='\n'.join(messages), name="What are these reactions for?",
inline=False) value="\n".join(messages),
inline=False,
)
self.embed.set_footer( 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) await self.message.edit(embed=self.embed)
async def go_back_to_current_page(): async def go_back_to_current_page():
@@ -787,31 +807,34 @@ class HelpPaginator(Pages):
async def show_bot_help(self): async def show_bot_help(self):
"""shows how to use the bot""" """shows how to use the bot"""
self.embed.title = 'Using the bot' self.embed.title = "Using the bot"
self.embed.description = 'Hello! Welcome to the help page.' self.embed.description = "Hello! Welcome to the help page."
self.embed.clear_fields() self.embed.clear_fields()
entries = (('<argument>', entries = (
'This means the argument is __**required**__.'), ("<argument>", "This means the argument is __**required**__."),
('[argument]', ("[argument]", "This means the argument is __**optional**__."),
'This means the argument is __**optional**__.'), ("[A|B]", "This means the it can be __**either A or B**__."),
('[A|B]', (
'This means the it can be __**either A or B**__.'), "[argument...]",
('[argument...]', "This means you can have multiple arguments.\n"
'This means you can have multiple arguments.\n' "Now that you know the basics, it should be "
'Now that you know the basics, it should be ' "noted that...\n"
'noted that...\n' "__**You do not type in the brackets!**__",
'__**You do not type in the brackets!**__')) ),
)
self.embed.add_field( self.embed.add_field(
name='How do I use this bot?', name="How do I use this bot?",
value='Reading the bot signature is pretty simple.') value="Reading the bot signature is pretty simple.",
)
for name, value in entries: for name, value in entries:
self.embed.add_field(name=name, value=value, inline=False) self.embed.add_field(name=name, value=value, inline=False)
self.embed.set_footer( 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) await self.message.edit(embed=self.embed)
async def go_back_to_current_page(): async def go_back_to_current_page():