1
0
Fork 0
mirror of synced 2024-05-17 19:12:33 +12:00
This commit is contained in:
phxntxm 2018-09-23 14:23:27 -05:00
parent 501e5b9452
commit ad0b4a6362
13 changed files with 82 additions and 67 deletions

View file

@ -45,8 +45,9 @@ class Administration:
# Try to simulate the message, to ensure they haven't provided an invalid phrase # Try to simulate the message, to ensure they haven't provided an invalid phrase
try: try:
message.format(loser="player1", winner="player2") message.format(loser="player1", winner="player2")
except: except Exception:
await ctx.send("That is an invalid format! The winner needs to be labeled with {winner} and the loser with {loser}") await ctx.send("That is an invalid format! The winner needs to be "
"labeled with {winner} and the loser with {loser}")
return return
# Now simply load the current messages # Now simply load the current messages
@ -81,7 +82,7 @@ class Administration:
if m.author == ctx.message.author and m.channel == ctx.message.channel: if m.author == ctx.message.author and m.channel == ctx.message.channel:
try: try:
return bool(int(m.content)) return bool(int(m.content))
except: except Exception:
return False return False
else: else:
return False return False
@ -165,7 +166,7 @@ class Administration:
# Try to simulate the message, to ensure they haven't provided an invalid phrase # Try to simulate the message, to ensure they haven't provided an invalid phrase
try: try:
message.format(user="user") message.format(user="user")
except: except Exception:
await ctx.send("That is an invalid format! The user being hugged needs to be labeled with {user}") await ctx.send("That is an invalid format! The user being hugged needs to be labeled with {user}")
return return
@ -198,7 +199,7 @@ class Administration:
if m.author == ctx.message.author and m.channel == ctx.message.channel: if m.author == ctx.message.author and m.channel == ctx.message.channel:
try: try:
return bool(int(m.content)) return bool(int(m.content))
except: except Exception:
return False return False
else: else:
return False return False
@ -1007,7 +1008,9 @@ class Administration:
if "is_owner" in func.__qualname__: if "is_owner" in func.__qualname__:
await ctx.send("You need to own the bot to run this command") await ctx.send("You need to own the bot to run this command")
return return
await ctx.send("You are required to have `manage_guild` permissions to run `{}`".format(cmd.qualified_name)) await ctx.send("You are required to have `manage_guild` permissions to run `{}`".format(
cmd.qualified_name
))
return return
# Perms will be an attribute if custom_perms is found no matter what, so no need to check this # Perms will be an attribute if custom_perms is found no matter what, so no need to check this

View file

@ -16,7 +16,8 @@ battle_outcomes = \
"As they were battling, {loser} was struck by lightning! {winner} you lucked out this time!", "As they were battling, {loser} was struck by lightning! {winner} you lucked out this time!",
"{loser} tried to dive at {winner} while fighting, somehow they missed and landed in quicksand." "{loser} tried to dive at {winner} while fighting, somehow they missed and landed in quicksand."
"Try paying more attention next time {loser}", "Try paying more attention next time {loser}",
"{loser} got a little...heated during the battle and ended up getting set on fire. {winner} wins by remaining cool", "{loser} got a little...heated during the battle and ended up getting set on fire. "
"{winner} wins by remaining cool",
"Princess Celestia came in and banished {loser} to the moon. Good luck getting into any battles up there", "Princess Celestia came in and banished {loser} to the moon. Good luck getting into any battles up there",
"{loser} took an arrow to the knee, they are no longer an adventurer. Keep on adventuring {winner}", "{loser} took an arrow to the knee, they are no longer an adventurer. Keep on adventuring {winner}",
"Common sense should make it obvious not to get into battle with {winner}. Apparently {loser} didn't get the memo", "Common sense should make it obvious not to get into battle with {winner}. Apparently {loser} didn't get the memo",
@ -31,20 +32,24 @@ battle_outcomes = \
"{winner} realized it was high noon, {loser} never even saw it coming.", "{winner} realized it was high noon, {loser} never even saw it coming.",
"{loser} spontaneously combusted...lol rip", "{loser} spontaneously combusted...lol rip",
"after many turns {winner} summons exodia and {loser} is sent to the shadow realm", "after many turns {winner} summons exodia and {loser} is sent to the shadow realm",
"{winner} and {loser} sit down for an intense game of chess, in the heat of the moment {winner} forgot they were playing a " "{winner} and {loser} sit down for an intense game of chess, "
"in the heat of the moment {winner} forgot they were playing a "
"game and summoned a real knight", "game and summoned a real knight",
"{winner} challenges {loser} to rock paper scissors, unfortunately for {loser}, {winner} chose scissors and stabbed them", "{winner} challenges {loser} to rock paper scissors, "
"unfortunately for {loser}, {winner} chose scissors and stabbed them",
"{winner} goes back in time and becomes {loser}'s best friend, winning without ever throwing a punch", "{winner} goes back in time and becomes {loser}'s best friend, winning without ever throwing a punch",
"{loser} trips down some stairs on their way to the battle with {winner}", "{loser} trips down some stairs on their way to the battle with {winner}",
"{winner} books {loser} a one way ticket to Flugendorf prison", "{winner} books {loser} a one way ticket to Flugendorf prison",
"{loser} was already dead", "{loser} was already dead",
"{loser} was crushed under the weight of expectations", "{loser} was crushed under the weight of expectations",
"{loser} was wearing a redshirt and it was their first day", "{loser} was wearing a redshirt and it was their first day",
"{winner} and {loser} were walking along when suddenly {loser} got kidnapped by a flying monkey; hope they had water with them", "{winner} and {loser} were walking along when suddenly {loser} "
"got kidnapped by a flying monkey; hope they had water with them",
"{winner} brought an army to a fist fight, {loser} never saw their opponent once", "{winner} brought an army to a fist fight, {loser} never saw their opponent once",
"{winner} used multiple simultaneous devestating defensive deep strikes to overwhelm {loser}", "{winner} used multiple simultaneous devestating defensive deep strikes to overwhelm {loser}",
"{winner} and {loser} engage in a dance off; {winner} wiped the floor with {loser}", "{winner} and {loser} engage in a dance off; {winner} wiped the floor with {loser}",
"{loser} tried to hide in the sand to catch {winner} off guard, unfortunately looks like a Giant Antlion had the same " "{loser} tried to hide in the sand to catch {winner} off guard, "
"unfortunately looks like a Giant Antlion had the same "
"idea for him", "idea for him",
"{loser} was busy playing trash videogames the night before the fight and collapsed before {winner}", "{loser} was busy playing trash videogames the night before the fight and collapsed before {winner}",
"{winner} threw a sick meme and {loser} totally got PRANK'D", "{winner} threw a sick meme and {loser} totally got PRANK'D",
@ -54,7 +59,8 @@ battle_outcomes = \
"Looks like {loser} didn't put enough points into kazoo playing, who knew they would have needed it", "Looks like {loser} didn't put enough points into kazoo playing, who knew they would have needed it",
"{loser} was too scared by the illuminati and extra-dimensional talking horses to show up", "{loser} was too scared by the illuminati and extra-dimensional talking horses to show up",
"{loser} didn't press x enough to not die", "{loser} didn't press x enough to not die",
"{winner} and {loser} go fishing to settle their debate, {winner} caught a sizeable fish and {loser} caught a boot older than time", "{winner} and {loser} go fishing to settle their debate, "
"{winner} caught a sizeable fish and {loser} caught a boot older than time",
"{winner} did a hero landing and {loser} was so surprised they gave up immediately"] "{winner} did a hero landing and {loser} was so surprised they gave up immediately"]
hugs = \ hugs = \
@ -152,7 +158,7 @@ class Interaction:
@commands.guild_only() @commands.guild_only()
@utils.custom_perms(send_messages=True) @utils.custom_perms(send_messages=True)
@utils.check_restricted() @utils.check_restricted()
async def hug(self, ctx, user = None): async def hug(self, ctx, user=None):
"""Makes me hug a person! """Makes me hug a person!
EXAMPLE: !hug @Someone EXAMPLE: !hug @Someone
@ -170,7 +176,6 @@ class Interaction:
await ctx.send("Error: Could not find user: {}".format(user)) await ctx.send("Error: Could not find user: {}".format(user))
return return
# Lets get the settings # Lets get the settings
settings = self.bot.db.load('server_settings', key=ctx.message.guild.id) or {} settings = self.bot.db.load('server_settings', key=ctx.message.guild.id) or {}
# Get the custom messages we can use # Get the custom messages we can use
@ -194,14 +199,16 @@ class Interaction:
@commands.cooldown(1, 20, BucketType.user) @commands.cooldown(1, 20, BucketType.user)
@utils.custom_perms(send_messages=True) @utils.custom_perms(send_messages=True)
@utils.check_restricted() @utils.check_restricted()
async def battle(self, ctx, player2 = None): async def battle(self, ctx, player2=None):
"""Challenges the mentioned user to a battle """Challenges the mentioned user to a battle
EXAMPLE: !battle @player2 EXAMPLE: !battle @player2
RESULT: A battle to the death""" RESULT: A battle to the death"""
# First check if everyone was mentioned # First check if everyone was mentioned
if ctx.message.mention_everyone: if ctx.message.mention_everyone:
await ctx.send("You want to battle {} people? Good luck with that...".format(len(ctx.message.channel.members) - 1)) await ctx.send("You want to battle {} people? Good luck with that...".format(
len(ctx.message.channel.members) - 1)
)
return return
# Then check if nothing was provided # Then check if nothing was provided
if player2 is None: if player2 is None:
@ -311,7 +318,9 @@ class Interaction:
new_loser_rank, _ = self.bot.br.get_server_rank(loser) new_loser_rank, _ = self.bot.br.get_server_rank(loser)
fmt = msg.content fmt = msg.content
if old_winner_rank: if old_winner_rank:
fmt += "\n{} - Rank: {} ( +{} )".format(winner.display_name, new_winner_rank, old_winner_rank - new_winner_rank) fmt += "\n{} - Rank: {} ( +{} )".format(
winner.display_name, new_winner_rank, old_winner_rank - new_winner_rank
)
else: else:
fmt += "\n{} - Rank: {}".format(winner.display_name, new_winner_rank) fmt += "\n{} - Rank: {}".format(winner.display_name, new_winner_rank)
if old_loser_rank: if old_loser_rank:
@ -321,7 +330,7 @@ class Interaction:
try: try:
await msg.edit(content=fmt) await msg.edit(content=fmt)
except: except Exception:
pass pass
@commands.command() @commands.command()

View file

@ -61,7 +61,7 @@ class Links:
# Get the next sibling, find the span where the description is, and get the text from this # Get the next sibling, find the span where the description is, and get the text from this
try: try:
description = element.next_sibling.find('span', class_='st').text description = element.next_sibling.find('span', class_='st').text
except: except Exception:
description = "" description = ""
# Add this to our text we'll use to send # Add this to our text we'll use to send

View file

@ -133,7 +133,7 @@ class Miscallaneous:
await ctx.send(fmt) await ctx.send(fmt)
try: try:
await ctx.message.delete() await ctx.message.delete()
except: except Exception:
pass pass
@commands.command() @commands.command()
@ -209,7 +209,7 @@ class Miscallaneous:
value += 'Memory: {:.2f} MiB'.format(memory_usage) value += 'Memory: {:.2f} MiB'.format(memory_usage)
value += '\nCPU: {}%'.format(cpu_usage) value += '\nCPU: {}%'.format(cpu_usage)
if hasattr(self.bot, 'uptime'): if hasattr(self.bot, 'uptime'):
value += "\nUptime: {}".format((pendulum.utcnow() - self.bot.uptime).in_words()) value += "\nUptime: {}".format((pendulum.now(tz="UTC") - self.bot.uptime).in_words())
embed.add_field(name=name, value=value, inline=False) embed.add_field(name=name, value=value, inline=False)
# Setup the user and guild statistics # Setup the user and guild statistics
@ -256,7 +256,7 @@ class Miscallaneous:
EXAMPLE: !uptime EXAMPLE: !uptime
RESULT: A BAJILLION DAYS""" RESULT: A BAJILLION DAYS"""
if hasattr(self.bot, 'uptime'): if hasattr(self.bot, 'uptime'):
await ctx.send("Uptime: ```\n{}```".format((pendulum.utcnow() - self.bot.uptime).in_words())) await ctx.send("Uptime: ```\n{}```".format((pendulum.now(tz="UTC") - self.bot.uptime).in_words()))
else: else:
await ctx.send("I've just restarted and not quite ready yet...gimme time I'm not a morning pony :c") await ctx.send("I've just restarted and not quite ready yet...gimme time I'm not a morning pony :c")
@ -348,7 +348,9 @@ class Miscallaneous:
value_str = ", ".join("{}".format(x) for x in nums) value_str = ", ".join("{}".format(x) for x in nums)
if dice == 1: if dice == 1:
fmt = '{0.message.author.name} has rolled a {1} sided die and got the number {2}!'.format(ctx, num, value_str) fmt = '{0.message.author.name} has rolled a {1} sided die and got the number {2}!'.format(
ctx, num, value_str
)
if add or subtract: if add or subtract:
fmt += "\nTotal: {} ({}".format(total, subtotal) fmt += "\nTotal: {} ({}".format(total, subtotal)
if add: if add:
@ -357,7 +359,9 @@ class Miscallaneous:
fmt += " - {}".format(subtract) fmt += " - {}".format(subtract)
fmt += ")" fmt += ")"
else: else:
fmt = '{0.message.author.name} has rolled {1}, {2} sided dice and got the numbers {3}!'.format(ctx, dice, num, value_str) fmt = '{0.message.author.name} has rolled {1}, {2} sided dice and got the numbers {3}!'.format(
ctx, dice, num, value_str
)
if add or subtract: if add or subtract:
fmt += "\nTotal: {} ({}".format(total, subtotal) fmt += "\nTotal: {} ({}".format(total, subtotal)
if add: if add:

View file

@ -2,18 +2,14 @@ from discord.ext import commands
from . import utils from . import utils
import re
import glob
import asyncio import asyncio
import aiohttp
import discord import discord
import inspect import inspect
import pendulum
import textwrap import textwrap
import traceback import traceback
import subprocess import subprocess
from contextlib import redirect_stdout
import io import io
from contextlib import redirect_stdout
def get_syntax_error(e): def get_syntax_error(e):
@ -30,7 +26,8 @@ class Owner:
self._last_result = None self._last_result = None
self.sessions = set() self.sessions = set()
def cleanup_code(self, content): @staticmethod
def cleanup_code(content):
"""Automatically removes code blocks from the code.""" """Automatically removes code blocks from the code."""
# remove ```py\n``` # remove ```py\n```
if content.startswith('```') and content.endswith('```'): if content.startswith('```') and content.endswith('```'):
@ -93,7 +90,6 @@ class Owner:
await self.bot.owner.send(embed=embed) await self.bot.owner.send(embed=embed)
@commands.command(hidden=True) @commands.command(hidden=True)
@commands.check(utils.is_owner) @commands.check(utils.is_owner)
async def repl(self, ctx): async def repl(self, ctx):
@ -229,14 +225,14 @@ class Owner:
try: try:
with redirect_stdout(stdout): with redirect_stdout(stdout):
ret = await func() ret = await func()
except Exception as e: except Exception:
value = stdout.getvalue() value = stdout.getvalue()
await ctx.send('```py\n{}{}\n```'.format(value, traceback.format_exc())) await ctx.send('```py\n{}{}\n```'.format(value, traceback.format_exc()))
else: else:
value = stdout.getvalue() value = stdout.getvalue()
try: try:
await ctx.message.add_reaction('\u2705') await ctx.message.add_reaction('\u2705')
except: except Exception:
pass pass
try: try:

View file

@ -44,7 +44,7 @@ class Raffle:
title = r['title'] title = r['title']
entrants = r['entrants'] entrants = r['entrants']
now = pendulum.utcnow() now = pendulum.now(tz="UTC")
expires = pendulum.parse(r['expires']) expires = pendulum.parse(r['expires'])
# Now lets compare and see if this raffle has ended, if not just continue # Now lets compare and see if this raffle has ended, if not just continue
@ -197,7 +197,7 @@ class Raffle:
author = ctx.message.author author = ctx.message.author
server = ctx.message.guild server = ctx.message.guild
channel = ctx.message.channel channel = ctx.message.channel
now = pendulum.utcnow() now = pendulum.now(tz="UTC")
await ctx.send( await ctx.send(
"Ready to start a new raffle! Please respond with the title you would like to use for this raffle!") "Ready to start a new raffle! Please respond with the title you would like to use for this raffle!")

View file

@ -7,6 +7,7 @@ from discord.ext import commands
from . import utils from . import utils
class Roulette: class Roulette:
def __init__(self, bot): def __init__(self, bot):
@ -59,7 +60,8 @@ class Roulette:
@utils.check_restricted() @utils.check_restricted()
async def roulette_start(self, ctx, time: int=5): async def roulette_start(self, ctx, time: int=5):
"""Starts a roulette, that will end in one of the entrants being kicked from the server """Starts a roulette, that will end in one of the entrants being kicked from the server
By default, the roulette will end in 5 minutes; provide a number (up to 30) to change how many minutes until it ends By default, the roulette will end in 5 minutes; provide a number (up to 30)
to change how many minutes until it ends
EXAMPLE: !roulette start EXAMPLE: !roulette start
RESULT: A new roulette game!""" RESULT: A new roulette game!"""
@ -69,7 +71,7 @@ class Roulette:
else: else:
game = self.start_game(ctx.message.guild, time) game = self.start_game(ctx.message.guild, time)
if game: if game:
await ctx.send("A new roulette game has just started! A random entrant will be kicked in {} minutes."\ await ctx.send("A new roulette game has just started! A random entrant will be kicked in {} minutes."
" Type {}roulette to join this roulette...good luck~".format(game.time_left, ctx.prefix)) " Type {}roulette to join this roulette...good luck~".format(game.time_left, ctx.prefix))
else: else:
await ctx.send("There is already a roulette game running on this server!") await ctx.send("There is already a roulette game running on this server!")
@ -83,10 +85,14 @@ class Roulette:
return return
try: try:
fmt = "The unlucky member to be kicked is {}; hopefully someone invites them back".format(member.display_name) fmt = "The unlucky member to be kicked is {}; hopefully someone invites them back :)".format(
member.display_name
)
await member.kick() await member.kick()
except discord.Forbidden: except discord.Forbidden:
fmt = "Well, the unlucky member chosen was {} but I can't kick you...so kick yourself please?".format(member.display_name) fmt = "Well, the unlucky member chosen was {} but I can't kick you...so kick yourself please?".format(
member.display_name
)
await ctx.send(fmt) await ctx.send(fmt)
@ -96,11 +102,11 @@ class Game:
def __init__(self, guild, time): def __init__(self, guild, time):
self.entrants = [] self.entrants = []
self.server = guild self.server = guild
self.end_time = pendulum.utcnow().add(minutes=time) self.end_time = pendulum.now(tz="UTC").add(minutes=time)
@property @property
def time_left(self): def time_left(self):
return (self.end_time - pendulum.utcnow()).in_words() return (self.end_time - pendulum.now(tz="UTC")).in_words()
def join(self, member): def join(self, member):
"""Adds a member to the list of entrants""" """Adds a member to the list of entrants"""
@ -116,5 +122,6 @@ class Game:
except IndexError: except IndexError:
return None return None
def setup(bot): def setup(bot):
bot.add_cog(Roulette(bot)) bot.add_cog(Roulette(bot))

View file

@ -28,24 +28,21 @@ class Spotify:
try: try:
delay = await self.get_api_token() delay = await self.get_api_token()
except Exception as error: except Exception as error:
delay = 2400
with open("error_log", 'a') as f: with open("error_log", 'a') as f:
traceback.print_tb(error.__traceback__, file=f) traceback.print_tb(error.__traceback__, file=f)
print('{0.__class__.__name__}: {0}'.format(error), file=f) print('{0.__class__.__name__}: {0}'.format(error), file=f)
delay = 2400
finally: finally:
await asyncio.sleep(delay) await asyncio.sleep(delay)
async def get_api_token(self): async def get_api_token(self):
url = "https://accounts.spotify.com/api/token" url = "https://accounts.spotify.com/api/token"
opts = {"grant_type": "client_credentials"} opts = {"grant_type": "client_credentials"}
while True: async with aiohttp.ClientSession(headers=self.headers) as session:
async with aiohttp.ClientSession(headers=self.headers) as session: response = await session.post(url, data=opts)
response = await session.post(url, data=opts) data = await response.json()
data = await response.json() self._token = data.get("access_token")
self._token = data.get("access_token") return data.get("expires_in")
return data.get("expires_in")
await asyncio.sleep(data.get("expires_in", 2400))
@commands.group(invoke_without_command=True) @commands.group(invoke_without_command=True)
@utils.custom_perms(send_messages=True) @utils.custom_perms(send_messages=True)
@ -87,7 +84,5 @@ class Spotify:
await ctx.send("Couldn't find a song for:\n{}".format(query)) await ctx.send("Couldn't find a song for:\n{}".format(query))
def setup(bot): def setup(bot):
bot.add_cog(Spotify(bot)) bot.add_cog(Spotify(bot))

View file

@ -6,9 +6,6 @@ import asyncio
import discord import discord
import re import re
import traceback import traceback
import logging
log = logging.getLogger()
class Twitch: class Twitch:
@ -23,7 +20,8 @@ class Twitch:
self.task = bot.loop.create_task(self.check_channels()) self.task = bot.loop.create_task(self.check_channels())
def _form_embed(self, data): @staticmethod
def _form_embed(data):
if not data: if not data:
return None return None
# I want to make the least API calls possible, however there's a few things to note here: # I want to make the least API calls possible, however there's a few things to note here:

View file

@ -5,6 +5,7 @@ from functools import cmp_to_key
suits = ['S', 'C', 'H', 'D'] suits = ['S', 'C', 'H', 'D']
faces = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A'] faces = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A']
class Deck: class Deck:
def __init__(self, prefill=True): def __init__(self, prefill=True):
# itertools.product creates us a tuple based on every output of our faces and suits # itertools.product creates us a tuple based on every output of our faces and suits

View file

@ -1,7 +1,5 @@
import ruamel.yaml as yaml import ruamel.yaml as yaml
import asyncio import asyncio
import rethinkdb as r
import pendulum
loop = asyncio.get_event_loop() loop = asyncio.get_event_loop()
global_config = {} global_config = {}

View file

@ -28,6 +28,8 @@ class Pages:
self.maximum_pages = pages self.maximum_pages = pages
self.embed = discord.Embed() self.embed = discord.Embed()
self.paginating = len(entries) > per_page self.paginating = len(entries) > per_page
self.current_page = 0
self.match = None
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}', self.first_page),
('\N{BLACK LEFT-POINTING TRIANGLE}', self.previous_page), ('\N{BLACK LEFT-POINTING TRIANGLE}', self.previous_page),
@ -121,8 +123,8 @@ class Pages:
async def numbered_page(self): async def numbered_page(self):
"""lets you type a page number to go to""" """lets you type a page number to go to"""
to_delete = [] start_message = await self.message.channel.send('What page do you want to go to?')
to_delete.append(await self.message.channel.send('What page do you want to go to?')) to_delete = [start_message]
def check(m): def check(m):
if m.author == self.author and m.channel == self.message.channel: if m.author == self.author and m.channel == self.message.channel:
@ -199,18 +201,19 @@ class Pages:
self.paginating = False self.paginating = False
try: try:
await self.message.clear_reactions() await self.message.clear_reactions()
except: except Exception:
pass pass
finally: finally:
break break
try: try:
await self.message.remove_reaction(react.emoji, user) await self.message.remove_reaction(react.emoji, user)
except: except Exception:
pass # can't remove it so don't bother doing so pass # can't remove it so don't bother doing so
await self.match() await self.match()
class DetailedPages(Pages): class DetailedPages(Pages):
"""A class built on the normal Paginator, except with the idea that you want one 'thing' per page """A class built on the normal Paginator, except with the idea that you want one 'thing' per page
This allows the ability to have more data on a page, more fields, etc. and page through each 'thing'""" This allows the ability to have more data on a page, more fields, etc. and page through each 'thing'"""

View file

@ -1,8 +1,9 @@
beautifulsoup4==4.6.0 aiohttp
Pillow==4.2.0 Pillow==4.2.0
rethinkdb==2.3.0.post6 rethinkdb
ruamel.yaml==0.14.12 ruamel.yaml
psutil==5.2.2 psutil
pendulum==1.2.0 pendulum
beautifulsoup4
-e git+https://github.com/Rapptz/discord.py@rewrite#egg=discord.py[voice] -e git+https://github.com/Rapptz/discord.py@rewrite#egg=discord.py[voice]
-e git+https://github.com/khazhyk/osuapi.git#egg=osuapi -e git+https://github.com/khazhyk/osuapi.git#egg=osuapi