Merge branch 'master' of https://github.com/Phxntxm/Bonfire
This commit is contained in:
commit
e3826ce3aa
12 changed files with 363 additions and 262 deletions
3
bot.py
3
bot.py
|
@ -5,6 +5,7 @@ import logging
|
||||||
import datetime
|
import datetime
|
||||||
import pendulum
|
import pendulum
|
||||||
import os
|
import os
|
||||||
|
import aiohttp
|
||||||
|
|
||||||
os.chdir(os.path.dirname(os.path.realpath(__file__)))
|
os.chdir(os.path.dirname(os.path.realpath(__file__)))
|
||||||
|
|
||||||
|
@ -88,6 +89,8 @@ async def on_command_error(error, ctx):
|
||||||
return
|
return
|
||||||
elif isinstance(error.original, discord.HTTPException) and 'empty message' in str(error.original):
|
elif isinstance(error.original, discord.HTTPException) and 'empty message' in str(error.original):
|
||||||
return
|
return
|
||||||
|
elif isinstance(error.original, aiohttp.ClientOSError):
|
||||||
|
return
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
|
@ -379,6 +379,11 @@ class Game:
|
||||||
|
|
||||||
for entry in self.players:
|
for entry in self.players:
|
||||||
player = entry['player']
|
player = entry['player']
|
||||||
|
# Quick check here to ensure the player isn't someone who got added
|
||||||
|
# Specifically right after the betting phase
|
||||||
|
if not hasattr(player, 'bet'):
|
||||||
|
continue
|
||||||
|
|
||||||
hand = player.hand
|
hand = player.hand
|
||||||
count = max(player.count)
|
count = max(player.count)
|
||||||
|
|
||||||
|
|
169
cogs/core.py
169
cogs/core.py
|
@ -1,8 +1,7 @@
|
||||||
import discord
|
import discord
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
from .utils import checks
|
|
||||||
from .utils import config
|
from . import utils
|
||||||
from .utils import utilities
|
|
||||||
|
|
||||||
import subprocess
|
import subprocess
|
||||||
import glob
|
import glob
|
||||||
|
@ -24,119 +23,6 @@ class Core:
|
||||||
self.results_per_page = 10
|
self.results_per_page = 10
|
||||||
self.commands = None
|
self.commands = None
|
||||||
|
|
||||||
async def on_reaction_add(self, reaction, user):
|
|
||||||
# Make sure that this is a normal user who pressed the button
|
|
||||||
# Also make sure that this is even a message we should be paying attention to
|
|
||||||
if user.bot or reaction.message.id not in self.help_embeds:
|
|
||||||
return
|
|
||||||
|
|
||||||
# If right is clicked
|
|
||||||
if '\u27A1' in reaction.emoji:
|
|
||||||
embed = self.next_page(reaction.message.id)
|
|
||||||
# If left is clicked
|
|
||||||
elif '\u2B05' in reaction.emoji:
|
|
||||||
embed = self.prev_page(reaction.message.id)
|
|
||||||
else:
|
|
||||||
return
|
|
||||||
|
|
||||||
await self.bot.edit_message(reaction.message, embed=embed)
|
|
||||||
try:
|
|
||||||
await self.bot.remove_reaction(reaction.message, reaction.emoji, user)
|
|
||||||
except discord.Forbidden:
|
|
||||||
pass
|
|
||||||
|
|
||||||
async def on_reaction_remove(self, reaction, user):
|
|
||||||
# Make sure that this is a normal user who pressed the button
|
|
||||||
# Also make sure that this is even a message we should be paying attention to
|
|
||||||
# We need reaction_add and reaction_move for cases like PM's, where the bot cannot remove a reaction
|
|
||||||
# This causes an error when the bot tries to remove the reaction, leaving it in place
|
|
||||||
# So the next click from a user will remove it, not add it.
|
|
||||||
if user.bot or reaction.message.id not in self.help_embeds:
|
|
||||||
return
|
|
||||||
|
|
||||||
# If right is clicked
|
|
||||||
if '\u27A1' in reaction.emoji:
|
|
||||||
embed = self.next_page(reaction.message.id)
|
|
||||||
# If left is clicked
|
|
||||||
elif '\u2B05' in reaction.emoji:
|
|
||||||
embed = self.prev_page(reaction.message.id)
|
|
||||||
else:
|
|
||||||
return
|
|
||||||
|
|
||||||
await self.bot.edit_message(reaction.message, embed=embed)
|
|
||||||
try:
|
|
||||||
await self.bot.remove_reaction(reaction.message, reaction.emoji, user)
|
|
||||||
except discord.Forbidden:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def determine_commands(self, page):
|
|
||||||
"""Returns the list of commands to use per page"""
|
|
||||||
|
|
||||||
end_index = self.results_per_page * page
|
|
||||||
start_index = end_index - self.results_per_page
|
|
||||||
|
|
||||||
return self.commands[start_index:end_index]
|
|
||||||
|
|
||||||
def prev_page(self, message_id):
|
|
||||||
"""Goes to the previus page"""
|
|
||||||
total_commands = len(self.commands)
|
|
||||||
# Increase the page count by one
|
|
||||||
page = self.help_embeds.get(message_id) - 1
|
|
||||||
|
|
||||||
total_pages = math.ceil(total_commands / self.results_per_page)
|
|
||||||
|
|
||||||
# If we hit the zeroith page, set to the very last page
|
|
||||||
if page <= 0:
|
|
||||||
page = total_pages
|
|
||||||
# Set the new page
|
|
||||||
self.help_embeds[message_id] = page
|
|
||||||
# Now create our new embed
|
|
||||||
return self.create_help_embed(message_id=message_id)
|
|
||||||
|
|
||||||
def next_page(self, message_id):
|
|
||||||
"""Goes to the next page for this message"""
|
|
||||||
total_commands = len(self.commands)
|
|
||||||
# Increase the page count by one
|
|
||||||
page = self.help_embeds.get(message_id) + 1
|
|
||||||
|
|
||||||
total_pages = math.ceil(total_commands / self.results_per_page)
|
|
||||||
|
|
||||||
# Make sure we don't reach past what we should; if we do, reset to page 1
|
|
||||||
if page > total_pages:
|
|
||||||
page = 1
|
|
||||||
|
|
||||||
# Set the new page
|
|
||||||
self.help_embeds[message_id] = page
|
|
||||||
# Now create our new embed
|
|
||||||
return self.create_help_embed(message_id=message_id)
|
|
||||||
|
|
||||||
def create_help_embed(self, message_id=None, page=1):
|
|
||||||
# If a message_id is provided, we need to get the new page (this is being sent by next/prev page buttons)
|
|
||||||
if message_id is not None:
|
|
||||||
page = self.help_embeds.get(message_id)
|
|
||||||
|
|
||||||
# Refresh our command list
|
|
||||||
self.commands = sorted(utilities.get_all_commands(self.bot))
|
|
||||||
|
|
||||||
# Calculate the total amount of pages needed
|
|
||||||
total_commands = len(self.commands)
|
|
||||||
total_pages = math.ceil(total_commands / self.results_per_page)
|
|
||||||
|
|
||||||
# Lets make sure that if a page was provided, it is within our range of pages available
|
|
||||||
if page < 1 or page > total_pages:
|
|
||||||
page = 1
|
|
||||||
|
|
||||||
# First create the embed object
|
|
||||||
opts = {"title": "Command List [{}/{}]".format(page, total_pages),
|
|
||||||
"description": "Run help on a specific command for more information on it!"}
|
|
||||||
embed = discord.Embed(**opts)
|
|
||||||
|
|
||||||
# Add each field for the commands for this page
|
|
||||||
fmt = "\n".join(self.determine_commands(page))
|
|
||||||
embed.add_field(name="Commands", value=fmt, inline=False)
|
|
||||||
|
|
||||||
return embed
|
|
||||||
|
|
||||||
def find_command(self, command):
|
def find_command(self, command):
|
||||||
# This method ensures the command given is valid. We need to loop through commands
|
# This method ensures the command given is valid. We need to loop through commands
|
||||||
# As self.bot.commands only includes parent commands
|
# As self.bot.commands only includes parent commands
|
||||||
|
@ -161,7 +47,7 @@ class Core:
|
||||||
return cmd
|
return cmd
|
||||||
|
|
||||||
@commands.command(pass_context=True)
|
@commands.command(pass_context=True)
|
||||||
@checks.custom_perms(send_messages=True)
|
@utils.custom_perms(send_messages=True)
|
||||||
async def help(self, ctx, *, message=None):
|
async def help(self, ctx, *, message=None):
|
||||||
"""This command is used to provide a link to the help URL.
|
"""This command is used to provide a link to the help URL.
|
||||||
This can be called on a command to provide more information about that command
|
This can be called on a command to provide more information about that command
|
||||||
|
@ -172,13 +58,6 @@ class Core:
|
||||||
|
|
||||||
cmd = None
|
cmd = None
|
||||||
page = 1
|
page = 1
|
||||||
if ctx.message.server:
|
|
||||||
perms = ctx.message.server.me.permissions_in(ctx.message.channel)
|
|
||||||
if not (perms.embed_links and perms.add_reactions):
|
|
||||||
fmt = "I need the permissions `embed_links` and `add_reactions` to send my help message! " \
|
|
||||||
" Otherwise you can use this link to view available commands {}".format(
|
|
||||||
"http://bonfirebot.readthedocs.io/en/latest/")
|
|
||||||
await self.bot.say(fmt)
|
|
||||||
|
|
||||||
if message is not None:
|
if message is not None:
|
||||||
# If something is provided, it can either be the page number or a command
|
# If something is provided, it can either be the page number or a command
|
||||||
|
@ -189,14 +68,12 @@ class Core:
|
||||||
cmd = self.find_command(message)
|
cmd = self.find_command(message)
|
||||||
|
|
||||||
if cmd is None:
|
if cmd is None:
|
||||||
embed = self.create_help_embed(page=page)
|
entries = sorted(utils.get_all_commands(self.bot))
|
||||||
msg = await self.bot.say(embed=embed)
|
try:
|
||||||
|
pages = utils.Pages(self.bot, message=ctx.message, entries=entries)
|
||||||
# Add the arrows for previous and next page
|
await pages.paginate()
|
||||||
await self.bot.add_reaction(msg, '\N{LEFTWARDS BLACK ARROW}')
|
except utils.CannotPaginate as e:
|
||||||
await self.bot.add_reaction(msg, '\N{BLACK RIGHTWARDS ARROW}')
|
await self.bot.say(str(e))
|
||||||
# The only thing we need to record about this message, is the page number, starting at 1
|
|
||||||
self.help_embeds[msg.id] = page
|
|
||||||
else:
|
else:
|
||||||
# Get the description for a command
|
# Get the description for a command
|
||||||
description = cmd.help
|
description = cmd.help
|
||||||
|
@ -209,7 +86,7 @@ class Core:
|
||||||
example = None
|
example = None
|
||||||
result = None
|
result = None
|
||||||
# Also get the subcommands for this command, if they exist
|
# Also get the subcommands for this command, if they exist
|
||||||
subcommands = [x for x in utilities._get_all_commands(cmd) if x != cmd.qualified_name]
|
subcommands = [x for x in utils.get_subcommands(cmd) if x != cmd.qualified_name]
|
||||||
|
|
||||||
# The rest is simple, create the embed, set the thumbail to me, add all fields if they exist
|
# The rest is simple, create the embed, set the thumbail to me, add all fields if they exist
|
||||||
embed = discord.Embed(title=cmd.qualified_name)
|
embed = discord.Embed(title=cmd.qualified_name)
|
||||||
|
@ -226,7 +103,7 @@ class Core:
|
||||||
await self.bot.say(embed=embed)
|
await self.bot.say(embed=embed)
|
||||||
|
|
||||||
@commands.command()
|
@commands.command()
|
||||||
@checks.custom_perms(send_messages=True)
|
@utils.custom_perms(send_messages=True)
|
||||||
async def motd(self, *, date=None):
|
async def motd(self, *, date=None):
|
||||||
"""This command can be used to print the current MOTD (Message of the day)
|
"""This command can be used to print the current MOTD (Message of the day)
|
||||||
This will most likely not be updated every day, however messages will still be pushed to this every now and then
|
This will most likely not be updated every day, however messages will still be pushed to this every now and then
|
||||||
|
@ -234,7 +111,7 @@ class Core:
|
||||||
EXAMPLE: !motd
|
EXAMPLE: !motd
|
||||||
RESULT: 'This is an example message of the day!'"""
|
RESULT: 'This is an example message of the day!'"""
|
||||||
if date is None:
|
if date is None:
|
||||||
motd = await config.get_content('motd')
|
motd = await utils.get_content('motd')
|
||||||
try:
|
try:
|
||||||
# Lets set this to the first one in the list first
|
# Lets set this to the first one in the list first
|
||||||
latest_motd = motd[0]
|
latest_motd = motd[0]
|
||||||
|
@ -256,7 +133,7 @@ class Core:
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
r_filter = pendulum.parse(date)
|
r_filter = pendulum.parse(date)
|
||||||
motd = await config.get_content('motd', r_filter)
|
motd = await utils.get_content('motd', r_filter)
|
||||||
date = motd[0]['date']
|
date = motd[0]['date']
|
||||||
motd = motd[0]['motd']
|
motd = motd[0]['motd']
|
||||||
fmt = "Message of the day for {}:\n\n{}".format(date, motd)
|
fmt = "Message of the day for {}:\n\n{}".format(date, motd)
|
||||||
|
@ -270,7 +147,7 @@ class Core:
|
||||||
await self.bot.say("Invalid date format! Try like {}".format(now))
|
await self.bot.say("Invalid date format! Try like {}".format(now))
|
||||||
|
|
||||||
@commands.command()
|
@commands.command()
|
||||||
@checks.custom_perms(send_messages=True)
|
@utils.custom_perms(send_messages=True)
|
||||||
async def calendar(self, month: str = None, year: int = None):
|
async def calendar(self, month: str = None, year: int = None):
|
||||||
"""Provides a printout of the current month's calendar
|
"""Provides a printout of the current month's calendar
|
||||||
Provide month and year to print the calendar of that year and month
|
Provide month and year to print the calendar of that year and month
|
||||||
|
@ -309,14 +186,14 @@ class Core:
|
||||||
await self.bot.say("```\n{}```".format(cal))
|
await self.bot.say("```\n{}```".format(cal))
|
||||||
|
|
||||||
@commands.command()
|
@commands.command()
|
||||||
@checks.custom_perms(send_messages=True)
|
@utils.custom_perms(send_messages=True)
|
||||||
async def info(self):
|
async def info(self):
|
||||||
"""This command can be used to print out some of my information"""
|
"""This command can be used to print out some of my information"""
|
||||||
# fmt is a dictionary so we can set the key to it's output, then print both
|
# fmt is a dictionary so we can set the key to it's output, then print both
|
||||||
# The only real use of doing it this way is easier editing if the info
|
# The only real use of doing it this way is easier editing if the info
|
||||||
# in this command is changed
|
# in this command is changed
|
||||||
|
|
||||||
bot_data = await config.get_content('bot_data')
|
bot_data = await utils.get_content('bot_data')
|
||||||
total_data = {'member_count': 0,
|
total_data = {'member_count': 0,
|
||||||
'server_count': 0}
|
'server_count': 0}
|
||||||
for entry in bot_data:
|
for entry in bot_data:
|
||||||
|
@ -326,7 +203,7 @@ class Core:
|
||||||
# Create the original embed object
|
# Create the original embed object
|
||||||
opts = {'title': 'Dev Server',
|
opts = {'title': 'Dev Server',
|
||||||
'description': 'Join the server above for any questions/suggestions about me.',
|
'description': 'Join the server above for any questions/suggestions about me.',
|
||||||
'url': config.dev_server}
|
'url': utils.dev_server}
|
||||||
embed = discord.Embed(**opts)
|
embed = discord.Embed(**opts)
|
||||||
|
|
||||||
# Add the normal values
|
# Add the normal values
|
||||||
|
@ -359,7 +236,7 @@ class Core:
|
||||||
await self.bot.say(embed=embed)
|
await self.bot.say(embed=embed)
|
||||||
|
|
||||||
@commands.command()
|
@commands.command()
|
||||||
@checks.custom_perms(send_messages=True)
|
@utils.custom_perms(send_messages=True)
|
||||||
async def uptime(self):
|
async def uptime(self):
|
||||||
"""Provides a printout of the current bot's uptime
|
"""Provides a printout of the current bot's uptime
|
||||||
|
|
||||||
|
@ -368,7 +245,7 @@ class Core:
|
||||||
await self.bot.say("Uptime: ```\n{}```".format((pendulum.utcnow() - self.bot.uptime).in_words()))
|
await self.bot.say("Uptime: ```\n{}```".format((pendulum.utcnow() - self.bot.uptime).in_words()))
|
||||||
|
|
||||||
@commands.command(aliases=['invite'])
|
@commands.command(aliases=['invite'])
|
||||||
@checks.custom_perms(send_messages=True)
|
@utils.custom_perms(send_messages=True)
|
||||||
async def addbot(self):
|
async def addbot(self):
|
||||||
"""Provides a link that you can use to add me to a server
|
"""Provides a link that you can use to add me to a server
|
||||||
|
|
||||||
|
@ -389,7 +266,7 @@ class Core:
|
||||||
.format(discord.utils.oauth_url(app_info.id, perms)))
|
.format(discord.utils.oauth_url(app_info.id, perms)))
|
||||||
|
|
||||||
@commands.command()
|
@commands.command()
|
||||||
@checks.custom_perms(send_messages=True)
|
@utils.custom_perms(send_messages=True)
|
||||||
async def doggo(self):
|
async def doggo(self):
|
||||||
"""Use this to print a random doggo image.
|
"""Use this to print a random doggo image.
|
||||||
|
|
||||||
|
@ -401,7 +278,7 @@ class Core:
|
||||||
await self.bot.upload(f)
|
await self.bot.upload(f)
|
||||||
|
|
||||||
@commands.command()
|
@commands.command()
|
||||||
@checks.custom_perms(send_messages=True)
|
@utils.custom_perms(send_messages=True)
|
||||||
async def snek(self):
|
async def snek(self):
|
||||||
"""Use this to print a random snek image.
|
"""Use this to print a random snek image.
|
||||||
|
|
||||||
|
@ -413,7 +290,7 @@ class Core:
|
||||||
await self.bot.upload(f)
|
await self.bot.upload(f)
|
||||||
|
|
||||||
@commands.command()
|
@commands.command()
|
||||||
@checks.custom_perms(send_messages=True)
|
@utils.custom_perms(send_messages=True)
|
||||||
async def joke(self):
|
async def joke(self):
|
||||||
"""Prints a random riddle
|
"""Prints a random riddle
|
||||||
|
|
||||||
|
@ -432,7 +309,7 @@ class Core:
|
||||||
break
|
break
|
||||||
|
|
||||||
@commands.command(pass_context=True)
|
@commands.command(pass_context=True)
|
||||||
@checks.custom_perms(send_messages=True)
|
@utils.custom_perms(send_messages=True)
|
||||||
async def roll(self, ctx, notation: str = "d6"):
|
async def roll(self, ctx, notation: str = "d6"):
|
||||||
"""Rolls a die based on the notation given
|
"""Rolls a die based on the notation given
|
||||||
Format should be #d#
|
Format should be #d#
|
||||||
|
|
|
@ -77,6 +77,8 @@ class Deviantart:
|
||||||
if result is None:
|
if result is None:
|
||||||
params['username'] = da_name
|
params['username'] = da_name
|
||||||
data = await utils.request(self.base_url, payload=params)
|
data = await utils.request(self.base_url, payload=params)
|
||||||
|
if data is None:
|
||||||
|
continue
|
||||||
|
|
||||||
result = data['results'][0]
|
result = data['results'][0]
|
||||||
cache[da_name] = result
|
cache[da_name] = result
|
||||||
|
|
|
@ -142,6 +142,7 @@ class Interaction:
|
||||||
filename = 'avatar.gif'
|
filename = 'avatar.gif'
|
||||||
else:
|
else:
|
||||||
filename = 'avatar.webp'
|
filename = 'avatar.webp'
|
||||||
|
file = utils.convert_to_jpeg(file)
|
||||||
await self.bot.upload(file, filename=filename)
|
await self.bot.upload(file, filename=filename)
|
||||||
else:
|
else:
|
||||||
await self.bot.say(url)
|
await self.bot.say(url)
|
||||||
|
|
91
cogs/mod.py
91
cogs/mod.py
|
@ -1,6 +1,6 @@
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
from .utils import checks
|
|
||||||
from .utils import config
|
from . import utils
|
||||||
|
|
||||||
import discord
|
import discord
|
||||||
import re
|
import re
|
||||||
|
@ -40,7 +40,7 @@ class Mod:
|
||||||
return cmd
|
return cmd
|
||||||
|
|
||||||
@commands.command(pass_context=True, no_pm=True, aliases=['nick'])
|
@commands.command(pass_context=True, no_pm=True, aliases=['nick'])
|
||||||
@checks.custom_perms(kick_members=True)
|
@utils.custom_perms(kick_members=True)
|
||||||
async def nickname(self, ctx, *, name=None):
|
async def nickname(self, ctx, *, name=None):
|
||||||
"""Used to set the nickname for Bonfire (provide no nickname and it will reset)
|
"""Used to set the nickname for Bonfire (provide no nickname and it will reset)
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ class Mod:
|
||||||
await self.bot.say("\N{OK HAND SIGN}")
|
await self.bot.say("\N{OK HAND SIGN}")
|
||||||
|
|
||||||
@commands.command(no_pm=True)
|
@commands.command(no_pm=True)
|
||||||
@checks.custom_perms(kick_members=True)
|
@utils.custom_perms(kick_members=True)
|
||||||
async def kick(self, member: discord.Member):
|
async def kick(self, member: discord.Member):
|
||||||
"""Used to kick a member from this server
|
"""Used to kick a member from this server
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ class Mod:
|
||||||
await self.bot.say("But I can't, muh permissions >:c")
|
await self.bot.say("But I can't, muh permissions >:c")
|
||||||
|
|
||||||
@commands.command(pass_context=True, no_pm=True)
|
@commands.command(pass_context=True, no_pm=True)
|
||||||
@checks.custom_perms(ban_members=True)
|
@utils.custom_perms(ban_members=True)
|
||||||
async def unban(self, ctx, member_id: int):
|
async def unban(self, ctx, member_id: int):
|
||||||
"""Used to unban a member from this server
|
"""Used to unban a member from this server
|
||||||
Due to the fact that I cannot find a user without being in a server with them
|
Due to the fact that I cannot find a user without being in a server with them
|
||||||
|
@ -84,7 +84,7 @@ class Mod:
|
||||||
await self.bot.say("Sorry, I failed to unban that user!")
|
await self.bot.say("Sorry, I failed to unban that user!")
|
||||||
|
|
||||||
@commands.command(pass_context=True, no_pm=True)
|
@commands.command(pass_context=True, no_pm=True)
|
||||||
@checks.custom_perms(ban_members=True)
|
@utils.custom_perms(ban_members=True)
|
||||||
async def ban(self, ctx, *, member):
|
async def ban(self, ctx, *, member):
|
||||||
"""Used to ban a member
|
"""Used to ban a member
|
||||||
This can be used to ban someone preemptively as well.
|
This can be used to ban someone preemptively as well.
|
||||||
|
@ -120,7 +120,7 @@ class Mod:
|
||||||
await self.bot.say("Sorry, I failed to ban that user!")
|
await self.bot.say("Sorry, I failed to ban that user!")
|
||||||
|
|
||||||
@commands.command(pass_context=True, no_pm=True)
|
@commands.command(pass_context=True, no_pm=True)
|
||||||
@checks.custom_perms(kick_members=True)
|
@utils.custom_perms(kick_members=True)
|
||||||
async def alerts(self, ctx, channel: discord.Channel):
|
async def alerts(self, ctx, channel: discord.Channel):
|
||||||
"""This command is used to set a channel as the server's 'notifications' channel
|
"""This command is used to set a channel as the server's 'notifications' channel
|
||||||
Any notifications (like someone going live on Twitch, or Picarto) will go to that channel
|
Any notifications (like someone going live on Twitch, or Picarto) will go to that channel
|
||||||
|
@ -130,13 +130,13 @@ class Mod:
|
||||||
r_filter = {'server_id': ctx.message.server.id}
|
r_filter = {'server_id': ctx.message.server.id}
|
||||||
entry = {'server_id': ctx.message.server.id,
|
entry = {'server_id': ctx.message.server.id,
|
||||||
'channel_id': channel.id}
|
'channel_id': channel.id}
|
||||||
if not await config.add_content('server_alerts', entry, r_filter):
|
if not await utils.add_content('server_alerts', entry, r_filter):
|
||||||
await config.update_content('server_alerts', entry, r_filter)
|
await utils.update_content('server_alerts', entry, r_filter)
|
||||||
await self.bot.say("I have just changed this server's 'notifications' channel"
|
await self.bot.say("I have just changed this server's 'notifications' channel"
|
||||||
"\nAll notifications will now go to `{}`".format(channel))
|
"\nAll notifications will now go to `{}`".format(channel))
|
||||||
|
|
||||||
@commands.command(pass_context=True, no_pm=True)
|
@commands.command(pass_context=True, no_pm=True)
|
||||||
@checks.custom_perms(kick_members=True)
|
@utils.custom_perms(kick_members=True)
|
||||||
async def usernotify(self, ctx, on_off: str):
|
async def usernotify(self, ctx, on_off: str):
|
||||||
"""This command can be used to set whether or not you want user notificaitons to show
|
"""This command can be used to set whether or not you want user notificaitons to show
|
||||||
This will save what channel you run this command in, that will be the channel used to send the notification to
|
This will save what channel you run this command in, that will be the channel used to send the notification to
|
||||||
|
@ -151,8 +151,8 @@ class Mod:
|
||||||
r_filter = {'server_id': ctx.message.server.id}
|
r_filter = {'server_id': ctx.message.server.id}
|
||||||
entry = {'server_id': ctx.message.server.id,
|
entry = {'server_id': ctx.message.server.id,
|
||||||
'channel_id': on_off}
|
'channel_id': on_off}
|
||||||
if not await config.add_content('user_notifications', entry, r_filter):
|
if not await utils.add_content('user_notifications', entry, r_filter):
|
||||||
await config.update_content('user_notifications', entry, r_filter)
|
await utils.update_content('user_notifications', entry, r_filter)
|
||||||
fmt = "notify" if on_off else "not notify"
|
fmt = "notify" if on_off else "not notify"
|
||||||
await self.bot.say("This server will now {} if someone has joined or left".format(fmt))
|
await self.bot.say("This server will now {} if someone has joined or left".format(fmt))
|
||||||
|
|
||||||
|
@ -164,33 +164,33 @@ class Mod:
|
||||||
await self.bot.say('Invalid subcommand passed: {0.subcommand_passed}'.format(ctx))
|
await self.bot.say('Invalid subcommand passed: {0.subcommand_passed}'.format(ctx))
|
||||||
|
|
||||||
@nsfw.command(name="add", pass_context=True)
|
@nsfw.command(name="add", pass_context=True)
|
||||||
@checks.custom_perms(kick_members=True)
|
@utils.custom_perms(kick_members=True)
|
||||||
async def nsfw_add(self, ctx):
|
async def nsfw_add(self, ctx):
|
||||||
"""Registers this channel as a 'nsfw' channel
|
"""Registers this channel as a 'nsfw' channel
|
||||||
|
|
||||||
EXAMPLE: !nsfw add
|
EXAMPLE: !nsfw add
|
||||||
RESULT: ;)"""
|
RESULT: ;)"""
|
||||||
r_filter = {'channel_id': ctx.message.channel.id}
|
r_filter = {'channel_id': ctx.message.channel.id}
|
||||||
if await config.add_content('nsfw_channels', r_filter, r_filter):
|
if await utils.add_content('nsfw_channels', r_filter, r_filter):
|
||||||
await self.bot.say("This channel has just been registered as 'nsfw'! Have fun you naughties ;)")
|
await self.bot.say("This channel has just been registered as 'nsfw'! Have fun you naughties ;)")
|
||||||
else:
|
else:
|
||||||
await self.bot.say("This channel is already registered as 'nsfw'!")
|
await self.bot.say("This channel is already registered as 'nsfw'!")
|
||||||
|
|
||||||
@nsfw.command(name="remove", aliases=["delete"], pass_context=True)
|
@nsfw.command(name="remove", aliases=["delete"], pass_context=True)
|
||||||
@checks.custom_perms(kick_members=True)
|
@utils.custom_perms(kick_members=True)
|
||||||
async def nsfw_remove(self, ctx):
|
async def nsfw_remove(self, ctx):
|
||||||
"""Removes this channel as a 'nsfw' channel
|
"""Removes this channel as a 'nsfw' channel
|
||||||
|
|
||||||
EXAMPLE: !nsfw remove
|
EXAMPLE: !nsfw remove
|
||||||
RESULT: ;("""
|
RESULT: ;("""
|
||||||
r_filter = {'channel_id': ctx.message.channel.id}
|
r_filter = {'channel_id': ctx.message.channel.id}
|
||||||
if await config.remove_content('nsfw_channels', r_filter):
|
if await utils.remove_content('nsfw_channels', r_filter):
|
||||||
await self.bot.say("This channel has just been unregistered as a nsfw channel")
|
await self.bot.say("This channel has just been unregistered as a nsfw channel")
|
||||||
else:
|
else:
|
||||||
await self.bot.say("This channel is not registered as a ''nsfw' channel!")
|
await self.bot.say("This channel is not registered as a ''nsfw' channel!")
|
||||||
|
|
||||||
@commands.command(pass_context=True)
|
@commands.command(pass_context=True)
|
||||||
@checks.custom_perms(kick_members=True)
|
@utils.custom_perms(kick_members=True)
|
||||||
async def say(self, ctx, *, msg: str):
|
async def say(self, ctx, *, msg: str):
|
||||||
"""Tells the bot to repeat what you say
|
"""Tells the bot to repeat what you say
|
||||||
|
|
||||||
|
@ -204,7 +204,7 @@ class Mod:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@commands.group(pass_context=True, invoke_without_command=True, no_pm=True)
|
@commands.group(pass_context=True, invoke_without_command=True, no_pm=True)
|
||||||
@checks.custom_perms(send_messages=True)
|
@utils.custom_perms(send_messages=True)
|
||||||
async def perms(self, ctx, *, command: str = None):
|
async def perms(self, ctx, *, command: str = None):
|
||||||
"""This command can be used to print the current allowed permissions on a specific command
|
"""This command can be used to print the current allowed permissions on a specific command
|
||||||
This supports groups as well as subcommands; pass no argument to print a list of available permissions
|
This supports groups as well as subcommands; pass no argument to print a list of available permissions
|
||||||
|
@ -217,7 +217,7 @@ class Mod:
|
||||||
return
|
return
|
||||||
|
|
||||||
r_filter = {'server_id': ctx.message.server.id}
|
r_filter = {'server_id': ctx.message.server.id}
|
||||||
server_perms = await config.get_content('custom_permissions', r_filter)
|
server_perms = await utils.get_content('custom_permissions', r_filter)
|
||||||
try:
|
try:
|
||||||
server_perms = server_perms[0]
|
server_perms = server_perms[0]
|
||||||
except TypeError:
|
except TypeError:
|
||||||
|
@ -231,14 +231,14 @@ class Mod:
|
||||||
perms_value = server_perms.get(cmd.qualified_name)
|
perms_value = server_perms.get(cmd.qualified_name)
|
||||||
if perms_value is None:
|
if perms_value is None:
|
||||||
# If we don't find custom permissions, get the required permission for a command
|
# If we don't find custom permissions, get the required permission for a command
|
||||||
# based on what we set in checks.custom_perms, if custom_perms isn't found, we'll get an IndexError
|
# based on what we set in utils.custom_perms, if custom_perms isn't found, we'll get an IndexError
|
||||||
try:
|
try:
|
||||||
custom_perms = [func for func in cmd.checks if "custom_perms" in func.__qualname__][0]
|
custom_perms = [func for func in cmd.utils if "custom_perms" in func.__qualname__][0]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
# Loop through and check if there is a check called is_owner
|
# Loop through and check if there is a check called is_owner
|
||||||
# If we loop through and don't find one, this means that the only other choice is to be
|
# If we loop through and don't find one, this means that the only other choice is to be
|
||||||
# Able to manage the server (for the checks on perm commands)
|
# Able to manage the server (for the utils on perm commands)
|
||||||
for func in cmd.checks:
|
for func in cmd.utils:
|
||||||
if "is_owner" in func.__qualname__:
|
if "is_owner" in func.__qualname__:
|
||||||
await self.bot.say("You need to own the bot to run this command")
|
await self.bot.say("You need to own the bot to run this command")
|
||||||
return
|
return
|
||||||
|
@ -304,7 +304,7 @@ class Mod:
|
||||||
# The other case is if I'm using the default has_permissions case
|
# The other case is if I'm using the default has_permissions case
|
||||||
# Which means I do not want to check custom permissions at all
|
# Which means I do not want to check custom permissions at all
|
||||||
# Currently the second case is only on adding and removing permissions, to avoid abuse on these
|
# Currently the second case is only on adding and removing permissions, to avoid abuse on these
|
||||||
for check in cmd.checks:
|
for check in cmd.utils:
|
||||||
if "is_owner" == check.__name__ or re.search("has_permissions", str(check)) is not None:
|
if "is_owner" == check.__name__ or re.search("has_permissions", str(check)) is not None:
|
||||||
await self.bot.say("This command cannot have custom permissions setup!")
|
await self.bot.say("This command cannot have custom permissions setup!")
|
||||||
return
|
return
|
||||||
|
@ -317,11 +317,11 @@ class Mod:
|
||||||
# In this case, I'm going the other way around, to make the least queries
|
# In this case, I'm going the other way around, to make the least queries
|
||||||
# As custom permissions are probably going to be ran multiple times per server
|
# As custom permissions are probably going to be ran multiple times per server
|
||||||
# Whereas in most other cases, the command is probably going to be ran once/few times per server
|
# Whereas in most other cases, the command is probably going to be ran once/few times per server
|
||||||
if not await config.update_content('custom_permissions', entry, r_filter):
|
if not await utils.update_content('custom_permissions', entry, r_filter):
|
||||||
await config.add_content('custom_permissions', entry, r_filter)
|
await utils.add_content('custom_permissions', entry, r_filter)
|
||||||
|
|
||||||
# Same case as prefixes, for now, trigger a manual update
|
# Same case as prefixes, for now, trigger a manual update
|
||||||
self.bot.loop.create_task(config.cache['custom_permissions'].update())
|
self.bot.loop.create_task(utils.cache['custom_permissions'].update())
|
||||||
await self.bot.say("I have just added your custom permissions; "
|
await self.bot.say("I have just added your custom permissions; "
|
||||||
"you now need to have `{}` permissions to use the command `{}`".format(permissions, command))
|
"you now need to have `{}` permissions to use the command `{}`".format(permissions, command))
|
||||||
|
|
||||||
|
@ -341,14 +341,14 @@ class Mod:
|
||||||
return
|
return
|
||||||
|
|
||||||
r_filter = {'server_id': ctx.message.server.id}
|
r_filter = {'server_id': ctx.message.server.id}
|
||||||
await config.replace_content('custom_permissions', r.row.without(cmd.qualified_name), r_filter)
|
await utils.replace_content('custom_permissions', r.row.without(cmd.qualified_name), r_filter)
|
||||||
await self.bot.say("I have just removed the custom permissions for {}!".format(cmd))
|
await self.bot.say("I have just removed the custom permissions for {}!".format(cmd))
|
||||||
|
|
||||||
# Same case as prefixes, for now, trigger a manual update
|
# Same case as prefixes, for now, trigger a manual update
|
||||||
self.bot.loop.create_task(config.cache['custom_permissions'].update())
|
self.bot.loop.create_task(utils.cache['custom_permissions'].update())
|
||||||
|
|
||||||
@commands.command(pass_context=True, no_pm=True)
|
@commands.command(pass_context=True, no_pm=True)
|
||||||
@checks.custom_perms(manage_server=True)
|
@utils.custom_perms(manage_server=True)
|
||||||
async def prefix(self, ctx, *, prefix: str):
|
async def prefix(self, ctx, *, prefix: str):
|
||||||
"""This command can be used to set a custom prefix per server
|
"""This command can be used to set a custom prefix per server
|
||||||
|
|
||||||
|
@ -361,8 +361,8 @@ class Mod:
|
||||||
entry = {'server_id': ctx.message.server.id,
|
entry = {'server_id': ctx.message.server.id,
|
||||||
'prefix': prefix}
|
'prefix': prefix}
|
||||||
|
|
||||||
if not await config.add_content('prefixes', entry, r_filter):
|
if not await utils.add_content('prefixes', entry, r_filter):
|
||||||
await config.update_content('prefixes', entry, r_filter)
|
await utils.update_content('prefixes', entry, r_filter)
|
||||||
|
|
||||||
if prefix is None:
|
if prefix is None:
|
||||||
fmt = "I have just cleared your custom prefix, the default prefix will have to be used now"
|
fmt = "I have just cleared your custom prefix, the default prefix will have to be used now"
|
||||||
|
@ -372,7 +372,7 @@ class Mod:
|
||||||
await self.bot.say(fmt)
|
await self.bot.say(fmt)
|
||||||
|
|
||||||
@commands.command(pass_context=True, no_pm=True)
|
@commands.command(pass_context=True, no_pm=True)
|
||||||
@checks.custom_perms(manage_messages=True)
|
@utils.custom_perms(manage_messages=True)
|
||||||
async def purge(self, ctx, limit: int = 100):
|
async def purge(self, ctx, limit: int = 100):
|
||||||
"""This command is used to a purge a number of messages from the channel
|
"""This command is used to a purge a number of messages from the channel
|
||||||
|
|
||||||
|
@ -388,7 +388,7 @@ class Mod:
|
||||||
" I can only bulk delete messages that are under 14 days old.")
|
" I can only bulk delete messages that are under 14 days old.")
|
||||||
|
|
||||||
@commands.command(pass_context=True, no_pm=True)
|
@commands.command(pass_context=True, no_pm=True)
|
||||||
@checks.custom_perms(manage_messages=True)
|
@utils.custom_perms(manage_messages=True)
|
||||||
async def prune(self, ctx, limit: int = 100):
|
async def prune(self, ctx, limit: int = 100):
|
||||||
"""This command can be used to prune messages from certain members
|
"""This command can be used to prune messages from certain members
|
||||||
Mention any user you want to prune messages from; if no members are mentioned, the messages removed will be mine
|
Mention any user you want to prune messages from; if no members are mentioned, the messages removed will be mine
|
||||||
|
@ -430,14 +430,14 @@ class Mod:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@commands.group(aliases=['rule'], pass_context=True, no_pm=True, invoke_without_command=True)
|
@commands.group(aliases=['rule'], pass_context=True, no_pm=True, invoke_without_command=True)
|
||||||
@checks.custom_perms(send_messages=True)
|
@utils.custom_perms(send_messages=True)
|
||||||
async def rules(self, ctx, rule: int = None):
|
async def rules(self, ctx, rule: int = None):
|
||||||
"""This command can be used to view the current rules on the server
|
"""This command can be used to view the current rules on the server
|
||||||
|
|
||||||
EXAMPLE: !rules 5
|
EXAMPLE: !rules 5
|
||||||
RESULT: Rule 5 is printed"""
|
RESULT: Rule 5 is printed"""
|
||||||
r_filter = {'server_id': ctx.message.server.id}
|
r_filter = {'server_id': ctx.message.server.id}
|
||||||
rules = await config.get_content('rules', r_filter)
|
rules = await utils.get_content('rules', r_filter)
|
||||||
try:
|
try:
|
||||||
rules = rules[0]['rules']
|
rules = rules[0]['rules']
|
||||||
except TypeError:
|
except TypeError:
|
||||||
|
@ -448,9 +448,12 @@ class Mod:
|
||||||
return
|
return
|
||||||
|
|
||||||
if rule is None:
|
if rule is None:
|
||||||
# Enumerate the list, so that we can print the number and the rule for each rule
|
try:
|
||||||
fmt = "\n".join("{}) {}".format(num + 1, rule) for num, rule in enumerate(rules))
|
pages = utils.Pages(self.bot, message=ctx.message, entries=rules, per_page=5)
|
||||||
await self.bot.say('```\n{}```'.format(fmt))
|
pages.title = "Rules for {}".format(ctx.message.server.name)
|
||||||
|
await pages.paginate()
|
||||||
|
except utils.CannotPaginate as e:
|
||||||
|
await self.bot.say(str(e))
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
fmt = rules[rule - 1]
|
fmt = rules[rule - 1]
|
||||||
|
@ -460,7 +463,7 @@ class Mod:
|
||||||
await self.bot.say("Rule {}: \"{}\"".format(rule, fmt))
|
await self.bot.say("Rule {}: \"{}\"".format(rule, fmt))
|
||||||
|
|
||||||
@rules.command(name='add', aliases=['create'], pass_context=True, no_pm=True)
|
@rules.command(name='add', aliases=['create'], pass_context=True, no_pm=True)
|
||||||
@checks.custom_perms(manage_server=True)
|
@utils.custom_perms(manage_server=True)
|
||||||
async def rules_add(self, ctx, *, rule: str):
|
async def rules_add(self, ctx, *, rule: str):
|
||||||
"""Adds a rule to this server's rules
|
"""Adds a rule to this server's rules
|
||||||
|
|
||||||
|
@ -470,13 +473,13 @@ class Mod:
|
||||||
entry = {'server_id': ctx.message.server.id,
|
entry = {'server_id': ctx.message.server.id,
|
||||||
'rules': [rule]}
|
'rules': [rule]}
|
||||||
update = {'rules': r.row['rules'].append(rule)}
|
update = {'rules': r.row['rules'].append(rule)}
|
||||||
if not await config.update_content('rules', update, r_filter):
|
if not await utils.update_content('rules', update, r_filter):
|
||||||
await config.add_content('rules', entry, r_filter)
|
await utils.add_content('rules', entry, r_filter)
|
||||||
|
|
||||||
await self.bot.say("I have just saved your new rule, use the rules command to view this server's current rules")
|
await self.bot.say("I have just saved your new rule, use the rules command to view this server's current rules")
|
||||||
|
|
||||||
@rules.command(name='remove', aliases=['delete'], pass_context=True, no_pm=True)
|
@rules.command(name='remove', aliases=['delete'], pass_context=True, no_pm=True)
|
||||||
@checks.custom_perms(manage_server=True)
|
@utils.custom_perms(manage_server=True)
|
||||||
async def rules_delete(self, ctx, rule: int):
|
async def rules_delete(self, ctx, rule: int):
|
||||||
"""Removes one of the rules from the list of this server's rules
|
"""Removes one of the rules from the list of this server's rules
|
||||||
Provide a number to delete that rule
|
Provide a number to delete that rule
|
||||||
|
@ -485,7 +488,7 @@ class Mod:
|
||||||
RESULT: Freedom from opression!"""
|
RESULT: Freedom from opression!"""
|
||||||
r_filter = {'server_id': ctx.message.server.id}
|
r_filter = {'server_id': ctx.message.server.id}
|
||||||
update = {'rules': r.row['rules'].delete_at(rule - 1)}
|
update = {'rules': r.row['rules'].delete_at(rule - 1)}
|
||||||
if not await config.update_content('rules', update, r_filter):
|
if not await utils.update_content('rules', update, r_filter):
|
||||||
await self.bot.say("That is not a valid rule number, try running the command again.")
|
await self.bot.say("That is not a valid rule number, try running the command again.")
|
||||||
else:
|
else:
|
||||||
await self.bot.say("I have just removed that rule from your list of rules!")
|
await self.bot.say("I have just removed that rule from your list of rules!")
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
from .utils import *
|
|
||||||
from .voice_utilities import *
|
from .voice_utilities import *
|
||||||
|
|
||||||
import discord
|
import discord
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
|
|
||||||
|
from . import utils
|
||||||
|
|
||||||
import math
|
import math
|
||||||
import time
|
import time
|
||||||
import asyncio
|
import asyncio
|
||||||
|
@ -146,6 +147,8 @@ class Music:
|
||||||
async def create_voice_client(self, channel):
|
async def create_voice_client(self, channel):
|
||||||
"""Creates a voice client and saves it"""
|
"""Creates a voice client and saves it"""
|
||||||
# First join the channel and get the VoiceClient that we'll use to save per server
|
# First join the channel and get the VoiceClient that we'll use to save per server
|
||||||
|
await self.remove_voice_client(channel.server)
|
||||||
|
|
||||||
server = channel.server
|
server = channel.server
|
||||||
state = self.get_voice_state(server)
|
state = self.get_voice_state(server)
|
||||||
voice = self.bot.voice_client_in(server)
|
voice = self.bot.voice_client_in(server)
|
||||||
|
@ -154,17 +157,22 @@ class Music:
|
||||||
try:
|
try:
|
||||||
if voice is None:
|
if voice is None:
|
||||||
state.voice = await self.bot.join_voice_channel(channel)
|
state.voice = await self.bot.join_voice_channel(channel)
|
||||||
return True
|
if state.voice:
|
||||||
|
return True
|
||||||
elif voice.channel == channel:
|
elif voice.channel == channel:
|
||||||
state.voice = voice
|
state.voice = voice
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
|
# This shouldn't theoretically ever happen yet it does. Thanks Discord
|
||||||
await voice.disconnect()
|
await voice.disconnect()
|
||||||
state.voice = await self.bot.join_voice_channel(channel)
|
state.voice = await self.bot.join_voice_channel(channel)
|
||||||
return True
|
if state.voice:
|
||||||
except (discord.ClientException, socket.gaierror):
|
return True
|
||||||
|
except (discord.ClientException, socket.gaierror, ConnectionResetError):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
async def remove_voice_client(self, server):
|
async def remove_voice_client(self, server):
|
||||||
"""Removes any voice clients from a server
|
"""Removes any voice clients from a server
|
||||||
|
@ -314,7 +322,7 @@ class Music:
|
||||||
|
|
||||||
|
|
||||||
@commands.command(pass_context=True, no_pm=True)
|
@commands.command(pass_context=True, no_pm=True)
|
||||||
@checks.custom_perms(send_messages=True)
|
@utils.custom_perms(send_messages=True)
|
||||||
async def progress(self, ctx):
|
async def progress(self, ctx):
|
||||||
"""Provides the progress of the current song"""
|
"""Provides the progress of the current song"""
|
||||||
|
|
||||||
|
@ -338,7 +346,7 @@ class Music:
|
||||||
await self.bot.say(fmt)
|
await self.bot.say(fmt)
|
||||||
|
|
||||||
@commands.command(no_pm=True)
|
@commands.command(no_pm=True)
|
||||||
@checks.custom_perms(send_messages=True)
|
@utils.custom_perms(send_messages=True)
|
||||||
async def join(self, *, channel: discord.Channel):
|
async def join(self, *, channel: discord.Channel):
|
||||||
"""Joins a voice channel."""
|
"""Joins a voice channel."""
|
||||||
try:
|
try:
|
||||||
|
@ -354,7 +362,7 @@ class Music:
|
||||||
await self.bot.say('Ready to play audio in ' + channel.name)
|
await self.bot.say('Ready to play audio in ' + channel.name)
|
||||||
|
|
||||||
@commands.command(pass_context=True, no_pm=True)
|
@commands.command(pass_context=True, no_pm=True)
|
||||||
@checks.custom_perms(send_messages=True)
|
@utils.custom_perms(send_messages=True)
|
||||||
async def summon(self, ctx):
|
async def summon(self, ctx):
|
||||||
"""Summons the bot to join your voice channel."""
|
"""Summons the bot to join your voice channel."""
|
||||||
# This method will be invoked by other commands, so we should return True or False instead of just returning
|
# This method will be invoked by other commands, so we should return True or False instead of just returning
|
||||||
|
@ -381,7 +389,7 @@ class Music:
|
||||||
return success
|
return success
|
||||||
|
|
||||||
@commands.command(pass_context=True, no_pm=True)
|
@commands.command(pass_context=True, no_pm=True)
|
||||||
@checks.custom_perms(send_messages=True)
|
@utils.custom_perms(send_messages=True)
|
||||||
async def play(self, ctx, *, song: str):
|
async def play(self, ctx, *, song: str):
|
||||||
"""Plays a song.
|
"""Plays a song.
|
||||||
If there is a song currently in the queue, then it is
|
If there is a song currently in the queue, then it is
|
||||||
|
@ -399,7 +407,7 @@ class Music:
|
||||||
state = self.get_voice_state(ctx.message.server)
|
state = self.get_voice_state(ctx.message.server)
|
||||||
|
|
||||||
# First check if we are connected to a voice channel at all, if not summon to the channel the author is in
|
# First check if we are connected to a voice channel at all, if not summon to the channel the author is in
|
||||||
# Since summon checks if the author is in a channel, we don't need to handle that here, just return if it failed
|
# Since summon utils if the author is in a channel, we don't need to handle that here, just return if it failed
|
||||||
if state.voice is None:
|
if state.voice is None:
|
||||||
success = await ctx.invoke(self.summon)
|
success = await ctx.invoke(self.summon)
|
||||||
if not success:
|
if not success:
|
||||||
|
@ -413,6 +421,12 @@ class Music:
|
||||||
author_channel = ctx.message.author.voice.voice_channel
|
author_channel = ctx.message.author.voice.voice_channel
|
||||||
my_channel = ctx.message.server.me.voice.voice_channel
|
my_channel = ctx.message.server.me.voice.voice_channel
|
||||||
|
|
||||||
|
if my_channel is None:
|
||||||
|
# If we're here this means that after 3 attempts...4 different "failsafes"...
|
||||||
|
# Discord has returned saying the connection was successful, and returned a None connection
|
||||||
|
await self.bot.say("I failed to connect to the channel! Please try again soon")
|
||||||
|
return
|
||||||
|
|
||||||
# To try to avoid some abuse, ensure the requester is actually in our channel
|
# To try to avoid some abuse, ensure the requester is actually in our channel
|
||||||
if my_channel != author_channel:
|
if my_channel != author_channel:
|
||||||
await self.bot.say("You are not currently in the channel; please join before trying to request a song.")
|
await self.bot.say("You are not currently in the channel; please join before trying to request a song.")
|
||||||
|
@ -466,7 +480,7 @@ class Music:
|
||||||
await self.bot.say('Enqueued ' + str(_entry))
|
await self.bot.say('Enqueued ' + str(_entry))
|
||||||
|
|
||||||
@commands.command(pass_context=True, no_pm=True)
|
@commands.command(pass_context=True, no_pm=True)
|
||||||
@checks.custom_perms(kick_members=True)
|
@utils.custom_perms(kick_members=True)
|
||||||
async def volume(self, ctx, value: int = None):
|
async def volume(self, ctx, value: int = None):
|
||||||
"""Sets the volume of the currently playing song."""
|
"""Sets the volume of the currently playing song."""
|
||||||
|
|
||||||
|
@ -485,7 +499,7 @@ class Music:
|
||||||
await self.bot.say('Set the volume to {:.0%}'.format(player.volume))
|
await self.bot.say('Set the volume to {:.0%}'.format(player.volume))
|
||||||
|
|
||||||
@commands.command(pass_context=True, no_pm=True)
|
@commands.command(pass_context=True, no_pm=True)
|
||||||
@checks.custom_perms(kick_members=True)
|
@utils.custom_perms(kick_members=True)
|
||||||
async def pause(self, ctx):
|
async def pause(self, ctx):
|
||||||
"""Pauses the currently played song."""
|
"""Pauses the currently played song."""
|
||||||
state = self.get_voice_state(ctx.message.server)
|
state = self.get_voice_state(ctx.message.server)
|
||||||
|
@ -493,7 +507,7 @@ class Music:
|
||||||
state.player.pause()
|
state.player.pause()
|
||||||
|
|
||||||
@commands.command(pass_context=True, no_pm=True)
|
@commands.command(pass_context=True, no_pm=True)
|
||||||
@checks.custom_perms(kick_members=True)
|
@utils.custom_perms(kick_members=True)
|
||||||
async def resume(self, ctx):
|
async def resume(self, ctx):
|
||||||
"""Resumes the currently played song."""
|
"""Resumes the currently played song."""
|
||||||
state = self.get_voice_state(ctx.message.server)
|
state = self.get_voice_state(ctx.message.server)
|
||||||
|
@ -501,7 +515,7 @@ class Music:
|
||||||
state.player.resume()
|
state.player.resume()
|
||||||
|
|
||||||
@commands.command(pass_context=True, no_pm=True)
|
@commands.command(pass_context=True, no_pm=True)
|
||||||
@checks.custom_perms(kick_members=True)
|
@utils.custom_perms(kick_members=True)
|
||||||
async def stop(self, ctx):
|
async def stop(self, ctx):
|
||||||
"""Stops playing audio and leaves the voice channel.
|
"""Stops playing audio and leaves the voice channel.
|
||||||
This also clears the queue.
|
This also clears the queue.
|
||||||
|
@ -525,7 +539,7 @@ class Music:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@commands.command(pass_context=True, no_pm=True)
|
@commands.command(pass_context=True, no_pm=True)
|
||||||
@checks.custom_perms(send_messages=True)
|
@utils.custom_perms(send_messages=True)
|
||||||
async def eta(self, ctx):
|
async def eta(self, ctx):
|
||||||
"""Provides an ETA on when your next song will play"""
|
"""Provides an ETA on when your next song will play"""
|
||||||
# Note: There is no way to tell how long a song has been playing, or how long there is left on a song
|
# Note: There is no way to tell how long a song has been playing, or how long there is left on a song
|
||||||
|
@ -565,7 +579,7 @@ class Music:
|
||||||
await self.bot.say("ETA till your next play is: {0[0]}m {0[1]}s".format(divmod(round(count, 0), 60)))
|
await self.bot.say("ETA till your next play is: {0[0]}m {0[1]}s".format(divmod(round(count, 0), 60)))
|
||||||
|
|
||||||
@commands.command(pass_context=True, no_pm=True)
|
@commands.command(pass_context=True, no_pm=True)
|
||||||
@checks.custom_perms(send_messages=True)
|
@utils.custom_perms(send_messages=True)
|
||||||
async def queue(self, ctx):
|
async def queue(self, ctx):
|
||||||
"""Provides a printout of the songs that are in the queue"""
|
"""Provides a printout of the songs that are in the queue"""
|
||||||
state = self.get_voice_state(ctx.message.server)
|
state = self.get_voice_state(ctx.message.server)
|
||||||
|
@ -581,14 +595,14 @@ class Music:
|
||||||
self.bot.loop.create_task(self.queue_embed_task(state, ctx.message.channel, ctx.message.author))
|
self.bot.loop.create_task(self.queue_embed_task(state, ctx.message.channel, ctx.message.author))
|
||||||
|
|
||||||
@commands.command(pass_context=True, no_pm=True)
|
@commands.command(pass_context=True, no_pm=True)
|
||||||
@checks.custom_perms(send_messages=True)
|
@utils.custom_perms(send_messages=True)
|
||||||
async def queuelength(self, ctx):
|
async def queuelength(self, ctx):
|
||||||
"""Prints the length of the queue"""
|
"""Prints the length of the queue"""
|
||||||
await self.bot.say("There are a total of {} songs in the queue"
|
await self.bot.say("There are a total of {} songs in the queue"
|
||||||
.format(len(self.get_voice_state(ctx.message.server).songs.entries)))
|
.format(len(self.get_voice_state(ctx.message.server).songs.entries)))
|
||||||
|
|
||||||
@commands.command(pass_context=True, no_pm=True)
|
@commands.command(pass_context=True, no_pm=True)
|
||||||
@checks.custom_perms(send_messages=True)
|
@utils.custom_perms(send_messages=True)
|
||||||
async def skip(self, ctx):
|
async def skip(self, ctx):
|
||||||
"""Vote to skip a song. The song requester can automatically skip.
|
"""Vote to skip a song. The song requester can automatically skip.
|
||||||
approximately 1/3 of the members in the voice channel
|
approximately 1/3 of the members in the voice channel
|
||||||
|
@ -620,7 +634,7 @@ class Music:
|
||||||
await self.bot.say('You have already voted to skip this song.')
|
await self.bot.say('You have already voted to skip this song.')
|
||||||
|
|
||||||
@commands.command(pass_context=True, no_pm=True)
|
@commands.command(pass_context=True, no_pm=True)
|
||||||
@checks.custom_perms(kick_members=True)
|
@utils.custom_perms(kick_members=True)
|
||||||
async def modskip(self, ctx):
|
async def modskip(self, ctx):
|
||||||
"""Forces a song skip, can only be used by a moderator"""
|
"""Forces a song skip, can only be used by a moderator"""
|
||||||
state = self.get_voice_state(ctx.message.server)
|
state = self.get_voice_state(ctx.message.server)
|
||||||
|
@ -632,7 +646,7 @@ class Music:
|
||||||
await self.bot.say('Song has just been skipped.')
|
await self.bot.say('Song has just been skipped.')
|
||||||
|
|
||||||
@commands.command(pass_context=True, no_pm=True)
|
@commands.command(pass_context=True, no_pm=True)
|
||||||
@checks.custom_perms(send_messages=True)
|
@utils.custom_perms(send_messages=True)
|
||||||
async def playing(self, ctx):
|
async def playing(self, ctx):
|
||||||
"""Shows info about the currently played song."""
|
"""Shows info about the currently played song."""
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
|
|
||||||
from .utils import config
|
from . import utils
|
||||||
from .utils import checks
|
|
||||||
from .utils import utilities
|
|
||||||
|
|
||||||
import re
|
import re
|
||||||
import glob
|
import glob
|
||||||
|
@ -23,7 +21,7 @@ class Owner:
|
||||||
self.bot = bot
|
self.bot = bot
|
||||||
|
|
||||||
@commands.command()
|
@commands.command()
|
||||||
@commands.check(checks.is_owner)
|
@commands.check(utils.is_owner)
|
||||||
async def motd_push(self, *, message):
|
async def motd_push(self, *, message):
|
||||||
"""Used to push a new message to the message of the day"""
|
"""Used to push a new message to the message of the day"""
|
||||||
date = pendulum.utcnow().to_date_string()
|
date = pendulum.utcnow().to_date_string()
|
||||||
|
@ -31,12 +29,12 @@ class Owner:
|
||||||
entry = {'motd': message, 'date': date}
|
entry = {'motd': message, 'date': date}
|
||||||
# Try to add this, if there's an entry for that date, lets update it to make sure only one motd is sent a day
|
# Try to add this, if there's an entry for that date, lets update it to make sure only one motd is sent a day
|
||||||
# I should be managing this myself, more than one should not be sent in a day
|
# I should be managing this myself, more than one should not be sent in a day
|
||||||
if await config.add_content('motd', entry, r_filter):
|
if await utils.add_content('motd', entry, r_filter):
|
||||||
await config.update_content('motd', entry, r_filter)
|
await utils.update_content('motd', entry, r_filter)
|
||||||
await self.bot.say("New motd update for {}!".format(date))
|
await self.bot.say("New motd update for {}!".format(date))
|
||||||
|
|
||||||
@commands.command(pass_context=True)
|
@commands.command(pass_context=True)
|
||||||
@commands.check(checks.is_owner)
|
@commands.check(utils.is_owner)
|
||||||
async def debug(self, ctx):
|
async def debug(self, ctx):
|
||||||
"""Executes code"""
|
"""Executes code"""
|
||||||
# Eval and exec have different useful purposes, so use both
|
# Eval and exec have different useful purposes, so use both
|
||||||
|
@ -65,7 +63,7 @@ class Owner:
|
||||||
await self.bot.say(fmt.format(type(error).__name__, error))
|
await self.bot.say(fmt.format(type(error).__name__, error))
|
||||||
|
|
||||||
@commands.command(pass_context=True)
|
@commands.command(pass_context=True)
|
||||||
@commands.check(checks.is_owner)
|
@commands.check(utils.is_owner)
|
||||||
async def shutdown(self, ctx):
|
async def shutdown(self, ctx):
|
||||||
"""Shuts the bot down"""
|
"""Shuts the bot down"""
|
||||||
fmt = 'Shutting down, I will miss you {0.author.name}'
|
fmt = 'Shutting down, I will miss you {0.author.name}'
|
||||||
|
@ -74,21 +72,21 @@ class Owner:
|
||||||
await self.bot.close()
|
await self.bot.close()
|
||||||
|
|
||||||
@commands.command()
|
@commands.command()
|
||||||
@commands.check(checks.is_owner)
|
@commands.check(utils.is_owner)
|
||||||
async def name(self, newNick: str):
|
async def name(self, newNick: str):
|
||||||
"""Changes the bot's name"""
|
"""Changes the bot's name"""
|
||||||
await self.bot.edit_profile(username=newNick)
|
await self.bot.edit_profile(username=newNick)
|
||||||
await self.bot.say('Changed username to ' + newNick)
|
await self.bot.say('Changed username to ' + newNick)
|
||||||
|
|
||||||
@commands.command()
|
@commands.command()
|
||||||
@commands.check(checks.is_owner)
|
@commands.check(utils.is_owner)
|
||||||
async def status(self, *, status: str):
|
async def status(self, *, status: str):
|
||||||
"""Changes the bot's 'playing' status"""
|
"""Changes the bot's 'playing' status"""
|
||||||
await self.bot.change_status(discord.Game(name=status, type=0))
|
await self.bot.change_status(discord.Game(name=status, type=0))
|
||||||
await self.bot.say("Just changed my status to '{0}'!".format(status))
|
await self.bot.say("Just changed my status to '{0}'!".format(status))
|
||||||
|
|
||||||
@commands.command()
|
@commands.command()
|
||||||
@commands.check(checks.is_owner)
|
@commands.check(utils.is_owner)
|
||||||
async def load(self, *, module: str):
|
async def load(self, *, module: str):
|
||||||
"""Loads a module"""
|
"""Loads a module"""
|
||||||
|
|
||||||
|
@ -106,7 +104,7 @@ class Owner:
|
||||||
await self.bot.say(fmt.format(type(error).__name__, error))
|
await self.bot.say(fmt.format(type(error).__name__, error))
|
||||||
|
|
||||||
@commands.command()
|
@commands.command()
|
||||||
@commands.check(checks.is_owner)
|
@commands.check(utils.is_owner)
|
||||||
async def unload(self, *, module: str):
|
async def unload(self, *, module: str):
|
||||||
"""Unloads a module"""
|
"""Unloads a module"""
|
||||||
|
|
||||||
|
@ -119,7 +117,7 @@ class Owner:
|
||||||
await self.bot.say("I have just unloaded the {} module".format(module))
|
await self.bot.say("I have just unloaded the {} module".format(module))
|
||||||
|
|
||||||
@commands.command()
|
@commands.command()
|
||||||
@commands.check(checks.is_owner)
|
@commands.check(utils.is_owner)
|
||||||
async def reload(self, *, module: str):
|
async def reload(self, *, module: str):
|
||||||
"""Reloads a module"""
|
"""Reloads a module"""
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
import discord
|
import discord
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
|
|
||||||
from .utils import config
|
from . import utils
|
||||||
from .utils import checks
|
|
||||||
from .utils import images
|
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
@ -30,7 +28,7 @@ class Stats:
|
||||||
return cmd
|
return cmd
|
||||||
|
|
||||||
@commands.command(no_pm=True, pass_context=True)
|
@commands.command(no_pm=True, pass_context=True)
|
||||||
@checks.custom_perms(send_messages=True)
|
@utils.custom_perms(send_messages=True)
|
||||||
async def serverinfo(self, ctx):
|
async def serverinfo(self, ctx):
|
||||||
"""Provides information about the server
|
"""Provides information about the server
|
||||||
|
|
||||||
|
@ -65,12 +63,12 @@ class Stats:
|
||||||
await self.bot.say(embed=embed)
|
await self.bot.say(embed=embed)
|
||||||
|
|
||||||
@commands.group(no_pm=True)
|
@commands.group(no_pm=True)
|
||||||
@checks.custom_perms(send_messages=True)
|
@utils.custom_perms(send_messages=True)
|
||||||
async def command(self):
|
async def command(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@command.command(no_pm=True, pass_context=True, name="stats")
|
@command.command(no_pm=True, pass_context=True, name="stats")
|
||||||
@checks.custom_perms(send_messages=True)
|
@utils.custom_perms(send_messages=True)
|
||||||
async def command_stats(self, ctx, *, command):
|
async def command_stats(self, ctx, *, command):
|
||||||
"""This command can be used to view some usage stats about a specific command
|
"""This command can be used to view some usage stats about a specific command
|
||||||
|
|
||||||
|
@ -82,7 +80,7 @@ class Stats:
|
||||||
return
|
return
|
||||||
|
|
||||||
r_filter = {'command': cmd.qualified_name}
|
r_filter = {'command': cmd.qualified_name}
|
||||||
command_stats = await config.get_content('command_usage', r_filter)
|
command_stats = await utils.get_content('command_usage', r_filter)
|
||||||
try:
|
try:
|
||||||
command_stats = command_stats[0]
|
command_stats = command_stats[0]
|
||||||
except TypeError:
|
except TypeError:
|
||||||
|
@ -98,7 +96,7 @@ class Stats:
|
||||||
("Total Usage", total_usage),
|
("Total Usage", total_usage),
|
||||||
("Your Usage", member_usage),
|
("Your Usage", member_usage),
|
||||||
("This Server's Usage", server_usage)]
|
("This Server's Usage", server_usage)]
|
||||||
banner = await images.create_banner(ctx.message.author, "Command Stats", data)
|
banner = await utils.create_banner(ctx.message.author, "Command Stats", data)
|
||||||
await self.bot.upload(banner)
|
await self.bot.upload(banner)
|
||||||
except (FileNotFoundError, discord.Forbidden):
|
except (FileNotFoundError, discord.Forbidden):
|
||||||
fmt = "The command {} has been used a total of {} times\n" \
|
fmt = "The command {} has been used a total of {} times\n" \
|
||||||
|
@ -109,7 +107,7 @@ class Stats:
|
||||||
await self.bot.say(fmt)
|
await self.bot.say(fmt)
|
||||||
|
|
||||||
@command.command(no_pm=True, pass_context=True, name="leaderboard")
|
@command.command(no_pm=True, pass_context=True, name="leaderboard")
|
||||||
@checks.custom_perms(send_messages=True)
|
@utils.custom_perms(send_messages=True)
|
||||||
async def command_leaderboard(self, ctx, option="server"):
|
async def command_leaderboard(self, ctx, option="server"):
|
||||||
"""This command can be used to print a leaderboard of commands
|
"""This command can be used to print a leaderboard of commands
|
||||||
Provide 'server' to print a leaderboard for this server
|
Provide 'server' to print a leaderboard for this server
|
||||||
|
@ -120,7 +118,7 @@ class Stats:
|
||||||
if re.search('(author|me)', option):
|
if re.search('(author|me)', option):
|
||||||
author = ctx.message.author
|
author = ctx.message.author
|
||||||
# First lets get all the command usage
|
# First lets get all the command usage
|
||||||
command_stats = await config.get_content('command_usage')
|
command_stats = await utils.get_content('command_usage')
|
||||||
# Now use a dictionary comprehension to get just the command name, and usage
|
# Now use a dictionary comprehension to get just the command name, and usage
|
||||||
# Based on the author's usage of the command
|
# Based on the author's usage of the command
|
||||||
stats = {data['command']: data['member_usage'].get(author.id) for data in command_stats
|
stats = {data['command']: data['member_usage'].get(author.id) for data in command_stats
|
||||||
|
@ -133,7 +131,7 @@ class Stats:
|
||||||
# As this can include, for example, all 3 if there are only 3 entries
|
# As this can include, for example, all 3 if there are only 3 entries
|
||||||
try:
|
try:
|
||||||
top_5 = [(data[0], data[1]) for data in sorted_stats[:5]]
|
top_5 = [(data[0], data[1]) for data in sorted_stats[:5]]
|
||||||
banner = await images.create_banner(ctx.message.author, "Your command usage", top_5)
|
banner = await utils.create_banner(ctx.message.author, "Your command usage", top_5)
|
||||||
await self.bot.upload(banner)
|
await self.bot.upload(banner)
|
||||||
except (FileNotFoundError, discord.Forbidden):
|
except (FileNotFoundError, discord.Forbidden):
|
||||||
top_5 = "\n".join("{}: {}".format(data[0], data[1]) for data in sorted_stats[:5])
|
top_5 = "\n".join("{}: {}".format(data[0], data[1]) for data in sorted_stats[:5])
|
||||||
|
@ -142,7 +140,7 @@ class Stats:
|
||||||
elif re.search('server', option):
|
elif re.search('server', option):
|
||||||
# This is exactly the same as above, except server usage instead of member usage
|
# This is exactly the same as above, except server usage instead of member usage
|
||||||
server = ctx.message.server
|
server = ctx.message.server
|
||||||
command_stats = await config.get_content('command_usage')
|
command_stats = await utils.get_content('command_usage')
|
||||||
stats = {data['command']: data['server_usage'].get(server.id) for data in command_stats
|
stats = {data['command']: data['server_usage'].get(server.id) for data in command_stats
|
||||||
if data['server_usage'].get(server.id, 0) > 0}
|
if data['server_usage'].get(server.id, 0) > 0}
|
||||||
sorted_stats = sorted(stats.items(), key=lambda x: x[1], reverse=True)
|
sorted_stats = sorted(stats.items(), key=lambda x: x[1], reverse=True)
|
||||||
|
@ -154,14 +152,14 @@ class Stats:
|
||||||
await self.bot.say("That is not a valid option, valid options are: `server` or `me`")
|
await self.bot.say("That is not a valid option, valid options are: `server` or `me`")
|
||||||
|
|
||||||
@commands.command(pass_context=True, no_pm=True)
|
@commands.command(pass_context=True, no_pm=True)
|
||||||
@checks.custom_perms(send_messages=True)
|
@utils.custom_perms(send_messages=True)
|
||||||
async def mostboops(self, ctx):
|
async def mostboops(self, ctx):
|
||||||
"""Shows the person you have 'booped' the most, as well as how many times
|
"""Shows the person you have 'booped' the most, as well as how many times
|
||||||
|
|
||||||
EXAMPLE: !mostboops
|
EXAMPLE: !mostboops
|
||||||
RESULT: You've booped @OtherPerson 351253897120935712093572193057310298 times!"""
|
RESULT: You've booped @OtherPerson 351253897120935712093572193057310298 times!"""
|
||||||
r_filter = {'member_id': ctx.message.author.id}
|
r_filter = {'member_id': ctx.message.author.id}
|
||||||
boops = await config.get_content('boops', r_filter)
|
boops = await utils.get_content('boops', r_filter)
|
||||||
if boops is None:
|
if boops is None:
|
||||||
await self.bot.say("You have not booped anyone {} Why the heck not...?".format(ctx.message.author.mention))
|
await self.bot.say("You have not booped anyone {} Why the heck not...?".format(ctx.message.author.mention))
|
||||||
return
|
return
|
||||||
|
@ -182,17 +180,17 @@ class Stats:
|
||||||
|
|
||||||
member = discord.utils.find(lambda m: m.id == most_id, self.bot.get_all_members())
|
member = discord.utils.find(lambda m: m.id == most_id, self.bot.get_all_members())
|
||||||
await self.bot.say("{0} you have booped {1} the most amount of times, coming in at {2} times".format(
|
await self.bot.say("{0} you have booped {1} the most amount of times, coming in at {2} times".format(
|
||||||
ctx.message.author.mention, member.mention, most_boops))
|
ctx.message.author.mention, member.display_name, most_boops))
|
||||||
|
|
||||||
@commands.command(pass_context=True, no_pm=True)
|
@commands.command(pass_context=True, no_pm=True)
|
||||||
@checks.custom_perms(send_messages=True)
|
@utils.custom_perms(send_messages=True)
|
||||||
async def listboops(self, ctx):
|
async def listboops(self, ctx):
|
||||||
"""Lists all the users you have booped and the amount of times
|
"""Lists all the users you have booped and the amount of times
|
||||||
|
|
||||||
EXAMPLE: !listboops
|
EXAMPLE: !listboops
|
||||||
RESULT: The list of your booped members!"""
|
RESULT: The list of your booped members!"""
|
||||||
r_filter = {'member_id': ctx.message.author.id}
|
r_filter = {'member_id': ctx.message.author.id}
|
||||||
boops = await config.get_content('boops', r_filter)
|
boops = await utils.get_content('boops', r_filter)
|
||||||
if boops is None:
|
if boops is None:
|
||||||
await self.bot.say("You have not booped anyone {} Why the heck not...?".format(ctx.message.author.mention))
|
await self.bot.say("You have not booped anyone {} Why the heck not...?".format(ctx.message.author.mention))
|
||||||
return
|
return
|
||||||
|
@ -210,7 +208,7 @@ class Stats:
|
||||||
try:
|
try:
|
||||||
output = [("{0.display_name}".format(ctx.message.server.get_member(m_id)), amt)
|
output = [("{0.display_name}".format(ctx.message.server.get_member(m_id)), amt)
|
||||||
for m_id, amt in sorted_booped_members]
|
for m_id, amt in sorted_booped_members]
|
||||||
banner = await images.create_banner(ctx.message.author, "Your booped victims", output)
|
banner = await utils.create_banner(ctx.message.author, "Your booped victims", output)
|
||||||
await self.bot.upload(banner)
|
await self.bot.upload(banner)
|
||||||
except (FileNotFoundError, discord.Forbidden):
|
except (FileNotFoundError, discord.Forbidden):
|
||||||
output = "\n".join(
|
output = "\n".join(
|
||||||
|
@ -219,7 +217,7 @@ class Stats:
|
||||||
await self.bot.say("You have booped:```\n{}```".format(output))
|
await self.bot.say("You have booped:```\n{}```".format(output))
|
||||||
|
|
||||||
@commands.command(pass_context=True, no_pm=True)
|
@commands.command(pass_context=True, no_pm=True)
|
||||||
@checks.custom_perms(send_messages=True)
|
@utils.custom_perms(send_messages=True)
|
||||||
async def leaderboard(self, ctx):
|
async def leaderboard(self, ctx):
|
||||||
"""Prints a leaderboard of everyone in the server's battling record
|
"""Prints a leaderboard of everyone in the server's battling record
|
||||||
|
|
||||||
|
@ -227,32 +225,27 @@ class Stats:
|
||||||
RESULT: A leaderboard of this server's battle records"""
|
RESULT: A leaderboard of this server's battle records"""
|
||||||
# Create a list of the ID's of all members in this server, for comparison to the records saved
|
# Create a list of the ID's of all members in this server, for comparison to the records saved
|
||||||
server_member_ids = [member.id for member in ctx.message.server.members]
|
server_member_ids = [member.id for member in ctx.message.server.members]
|
||||||
battles = await config.get_content('battle_records')
|
battles = await utils.get_content('battle_records')
|
||||||
battles = [battle for battle in battles if battle['member_id'] in server_member_ids]
|
battles = [battle for battle in battles if battle['member_id'] in server_member_ids]
|
||||||
|
|
||||||
# Sort the members based on their rating
|
# Sort the members based on their rating
|
||||||
sorted_members = sorted(battles, key=lambda k: k['rating'], reverse=True)
|
sorted_members = sorted(battles, key=lambda k: k['rating'], reverse=True)
|
||||||
|
|
||||||
output = []
|
output = []
|
||||||
count = 1
|
|
||||||
for x in sorted_members:
|
for x in sorted_members:
|
||||||
member_id = x['member_id']
|
member_id = x['member_id']
|
||||||
rating = x['rating']
|
rating = x['rating']
|
||||||
member = ctx.message.server.get_member(member_id)
|
member = ctx.message.server.get_member(member_id)
|
||||||
output.append((count, "{} (Rating: {})".format(member.display_name, rating)))
|
output.append("{} (Rating: {})".format(member.display_name, rating))
|
||||||
count += 1
|
|
||||||
if count >= 11:
|
|
||||||
break
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
banner = await images.create_banner(ctx.message.author, "Battling Leaderboard", output)
|
pages = utils.Pages(self.bot, message=ctx.message, entries=output)
|
||||||
await self.bot.upload(banner)
|
await pages.paginate()
|
||||||
except (FileNotFoundError, discord.Forbidden):
|
except utils.CannotPaginate as e:
|
||||||
fmt = "\n".join("#{}) {}".format(key, value) for key, value in output)
|
await self.bot.say(str(e))
|
||||||
await self.bot.say("Battling leaderboard for this server:```\n{}```".format(fmt))
|
|
||||||
|
|
||||||
@commands.command(pass_context=True, no_pm=True)
|
@commands.command(pass_context=True, no_pm=True)
|
||||||
@checks.custom_perms(send_messages=True)
|
@utils.custom_perms(send_messages=True)
|
||||||
async def stats(self, ctx, member: discord.Member = None):
|
async def stats(self, ctx, member: discord.Member = None):
|
||||||
"""Prints the battling stats for you, or the user provided
|
"""Prints the battling stats for you, or the user provided
|
||||||
|
|
||||||
|
@ -262,7 +255,7 @@ class Stats:
|
||||||
|
|
||||||
# For this one, we don't want to pass a filter, as we do need all battle records
|
# For this one, we don't want to pass a filter, as we do need all battle records
|
||||||
# We need this because we want to make a comparison for overall rank
|
# We need this because we want to make a comparison for overall rank
|
||||||
all_members = await config.get_content('battle_records')
|
all_members = await utils.get_content('battle_records')
|
||||||
|
|
||||||
# Make a list comprehension to just check if the user has battled
|
# Make a list comprehension to just check if the user has battled
|
||||||
if len([entry for entry in all_members if entry['member_id'] == member.id]) == 0:
|
if len([entry for entry in all_members if entry['member_id'] == member.id]) == 0:
|
||||||
|
@ -288,7 +281,7 @@ class Stats:
|
||||||
title = 'Stats for {}'.format(member.display_name)
|
title = 'Stats for {}'.format(member.display_name)
|
||||||
fmt = [('Record', record), ('Server Rank', '{}/{}'.format(server_rank, len(server_members))),
|
fmt = [('Record', record), ('Server Rank', '{}/{}'.format(server_rank, len(server_members))),
|
||||||
('Overall Rank', '{}/{}'.format(total_rank, len(all_members))), ('Rating', rating)]
|
('Overall Rank', '{}/{}'.format(total_rank, len(all_members))), ('Rating', rating)]
|
||||||
banner = await images.create_banner(member, title, fmt)
|
banner = await utils.create_banner(member, title, fmt)
|
||||||
await self.bot.upload(banner)
|
await self.bot.upload(banner)
|
||||||
except (FileNotFoundError, discord.Forbidden):
|
except (FileNotFoundError, discord.Forbidden):
|
||||||
fmt = 'Stats for {}:\n\tRecord: {}\n\tServer Rank: {}/{}\n\tOverall Rank: {}/{}\n\tRating: {}'
|
fmt = 'Stats for {}:\n\tRecord: {}\n\tServer Rank: {}/{}\n\tOverall Rank: {}/{}\n\tRating: {}'
|
||||||
|
|
|
@ -3,3 +3,4 @@ from .checks import is_owner, custom_perms, is_pm
|
||||||
from .config import *
|
from .config import *
|
||||||
from .utilities import *
|
from .utilities import *
|
||||||
from .images import create_banner
|
from .images import create_banner
|
||||||
|
from .paginator import Pages, CannotPaginate
|
||||||
|
|
192
cogs/utils/paginator.py
Normal file
192
cogs/utils/paginator.py
Normal file
|
@ -0,0 +1,192 @@
|
||||||
|
import asyncio
|
||||||
|
import discord
|
||||||
|
|
||||||
|
class CannotPaginate(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class Pages:
|
||||||
|
"""Implements a paginator that queries the user for the
|
||||||
|
pagination interface.
|
||||||
|
|
||||||
|
Pages are 1-index based, not 0-index based.
|
||||||
|
|
||||||
|
If the user does not reply within 2 minutes, the pagination
|
||||||
|
interface exits automatically.
|
||||||
|
"""
|
||||||
|
def __init__(self, bot, *, message, entries, per_page=10):
|
||||||
|
self.bot = bot
|
||||||
|
self.entries = entries
|
||||||
|
self.message = message
|
||||||
|
self.author = message.author
|
||||||
|
self.per_page = per_page
|
||||||
|
pages, left_over = divmod(len(self.entries), self.per_page)
|
||||||
|
if left_over:
|
||||||
|
pages += 1
|
||||||
|
self.maximum_pages = pages
|
||||||
|
self.embed = discord.Embed()
|
||||||
|
self.paginating = len(entries) > per_page
|
||||||
|
self.reaction_emojis = [
|
||||||
|
('\N{BLACK LEFT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR}', self.first_page),
|
||||||
|
('\N{BLACK LEFT-POINTING TRIANGLE}', self.previous_page),
|
||||||
|
('\N{BLACK RIGHT-POINTING TRIANGLE}', self.next_page),
|
||||||
|
('\N{BLACK RIGHT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR}', self.last_page),
|
||||||
|
('\N{INPUT SYMBOL FOR NUMBERS}', self.numbered_page ),
|
||||||
|
('\N{BLACK SQUARE FOR STOP}', self.stop_pages),
|
||||||
|
('\N{INFORMATION SOURCE}', self.show_help),
|
||||||
|
]
|
||||||
|
|
||||||
|
server = self.message.server
|
||||||
|
if server is not None:
|
||||||
|
self.permissions = self.message.channel.permissions_for(server.me)
|
||||||
|
else:
|
||||||
|
self.permissions = self.message.channel.permissions_for(self.bot.user)
|
||||||
|
|
||||||
|
if not self.permissions.embed_links:
|
||||||
|
raise CannotPaginate('Bot does not have embed links permission.')
|
||||||
|
|
||||||
|
def get_page(self, page):
|
||||||
|
base = (page - 1) * self.per_page
|
||||||
|
return self.entries[base:base + self.per_page]
|
||||||
|
|
||||||
|
async def show_page(self, page, *, first=False):
|
||||||
|
self.current_page = page
|
||||||
|
entries = self.get_page(page)
|
||||||
|
p = []
|
||||||
|
for t in enumerate(entries, 1 + ((page - 1) * self.per_page)):
|
||||||
|
p.append('%s. %s' % t)
|
||||||
|
|
||||||
|
self.embed.set_footer(text='Page %s/%s (%s entries)' % (page, self.maximum_pages, len(self.entries)))
|
||||||
|
|
||||||
|
if not self.paginating:
|
||||||
|
self.embed.description = '\n'.join(p)
|
||||||
|
return await self.bot.send_message(self.message.channel, embed=self.embed)
|
||||||
|
|
||||||
|
if not first:
|
||||||
|
self.embed.description = '\n'.join(p)
|
||||||
|
await self.bot.edit_message(self.message, embed=self.embed)
|
||||||
|
return
|
||||||
|
|
||||||
|
# verify we can actually use the pagination session
|
||||||
|
if not self.permissions.add_reactions:
|
||||||
|
raise CannotPaginate('Bot does not have add reactions permission.')
|
||||||
|
|
||||||
|
if not self.permissions.read_message_history:
|
||||||
|
raise CannotPaginate('Bot does not have Read Message History permission.')
|
||||||
|
|
||||||
|
p.append('')
|
||||||
|
p.append('Confused? React with \N{INFORMATION SOURCE} for more info.')
|
||||||
|
self.embed.description = '\n'.join(p)
|
||||||
|
self.message = await self.bot.send_message(self.message.channel, embed=self.embed)
|
||||||
|
for (reaction, _) in self.reaction_emojis:
|
||||||
|
if self.maximum_pages == 2 and reaction in ('\u23ed', '\u23ee'):
|
||||||
|
# no |<< or >>| buttons if we only have two pages
|
||||||
|
# we can't forbid it if someone ends up using it but remove
|
||||||
|
# it from the default set
|
||||||
|
continue
|
||||||
|
|
||||||
|
await self.bot.add_reaction(self.message, reaction)
|
||||||
|
|
||||||
|
async def checked_show_page(self, page):
|
||||||
|
if page != 0 and page <= self.maximum_pages:
|
||||||
|
await self.show_page(page)
|
||||||
|
|
||||||
|
async def first_page(self):
|
||||||
|
"""goes to the first page"""
|
||||||
|
await self.show_page(1)
|
||||||
|
|
||||||
|
async def last_page(self):
|
||||||
|
"""goes to the last page"""
|
||||||
|
await self.show_page(self.maximum_pages)
|
||||||
|
|
||||||
|
async def next_page(self):
|
||||||
|
"""goes to the next page"""
|
||||||
|
await self.checked_show_page(self.current_page + 1)
|
||||||
|
|
||||||
|
async def previous_page(self):
|
||||||
|
"""goes to the previous page"""
|
||||||
|
await self.checked_show_page(self.current_page - 1)
|
||||||
|
|
||||||
|
async def show_current_page(self):
|
||||||
|
if self.paginating:
|
||||||
|
await self.show_page(self.current_page)
|
||||||
|
|
||||||
|
async def numbered_page(self):
|
||||||
|
"""lets you type a page number to go to"""
|
||||||
|
to_delete = []
|
||||||
|
to_delete.append(await self.bot.send_message(self.message.channel, 'What page do you want to go to?'))
|
||||||
|
msg = await self.bot.wait_for_message(author=self.author, channel=self.message.channel,
|
||||||
|
check=lambda m: m.content.isdigit(), timeout=30.0)
|
||||||
|
if msg is not None:
|
||||||
|
page = int(msg.content)
|
||||||
|
to_delete.append(msg)
|
||||||
|
if page != 0 and page <= self.maximum_pages:
|
||||||
|
await self.show_page(page)
|
||||||
|
else:
|
||||||
|
to_delete.append(await self.bot.say('Invalid page given. (%s/%s)' % (page, self.maximum_pages)))
|
||||||
|
await asyncio.sleep(5)
|
||||||
|
else:
|
||||||
|
to_delete.append(await self.bot.send_message(self.message.channel, 'Took too long.'))
|
||||||
|
await asyncio.sleep(5)
|
||||||
|
|
||||||
|
try:
|
||||||
|
await self.bot.delete_messages(to_delete)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
async def show_help(self):
|
||||||
|
"""shows this message"""
|
||||||
|
e = discord.Embed()
|
||||||
|
messages = ['Welcome to the interactive paginator!\n']
|
||||||
|
messages.append('This interactively allows you to see pages of text by navigating with ' \
|
||||||
|
'reactions. They are as follows:\n')
|
||||||
|
|
||||||
|
for (emoji, func) in self.reaction_emojis:
|
||||||
|
messages.append('%s %s' % (emoji, func.__doc__))
|
||||||
|
|
||||||
|
e.description = '\n'.join(messages)
|
||||||
|
e.colour = 0x738bd7 # blurple
|
||||||
|
e.set_footer(text='We were on page %s before this message.' % self.current_page)
|
||||||
|
await self.bot.edit_message(self.message, embed=e)
|
||||||
|
|
||||||
|
async def go_back_to_current_page():
|
||||||
|
await asyncio.sleep(60.0)
|
||||||
|
await self.show_current_page()
|
||||||
|
|
||||||
|
self.bot.loop.create_task(go_back_to_current_page())
|
||||||
|
|
||||||
|
async def stop_pages(self):
|
||||||
|
"""stops the interactive pagination session"""
|
||||||
|
await self.bot.delete_message(self.message)
|
||||||
|
self.paginating = False
|
||||||
|
|
||||||
|
def react_check(self, reaction, user):
|
||||||
|
if user is None or user.id != self.author.id:
|
||||||
|
return False
|
||||||
|
|
||||||
|
for (emoji, func) in self.reaction_emojis:
|
||||||
|
if reaction.emoji == emoji:
|
||||||
|
self.match = func
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
async def paginate(self):
|
||||||
|
"""Actually paginate the entries and run the interactive loop if necessary."""
|
||||||
|
await self.show_page(1, first=True)
|
||||||
|
|
||||||
|
while self.paginating:
|
||||||
|
react = await self.bot.wait_for_reaction(message=self.message, check=self.react_check, timeout=120.0)
|
||||||
|
if react is None:
|
||||||
|
self.paginating = False
|
||||||
|
try:
|
||||||
|
await self.bot.clear_reactions(self.message)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
finally:
|
||||||
|
break
|
||||||
|
|
||||||
|
try:
|
||||||
|
await self.bot.remove_reaction(self.message, react.reaction.emoji, react.user)
|
||||||
|
except:
|
||||||
|
pass # can't remove it so don't bother doing so
|
||||||
|
|
||||||
|
await self.match()
|
|
@ -1,8 +1,20 @@
|
||||||
import aiohttp
|
import aiohttp
|
||||||
import io
|
from io import BytesIO
|
||||||
import inspect
|
import inspect
|
||||||
|
|
||||||
from . import config
|
from . import config
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
def convert_to_jpeg(pfile):
|
||||||
|
# Open the file given
|
||||||
|
img = Image.open(pfile)
|
||||||
|
# Create the BytesIO object we'll use as our new "file"
|
||||||
|
new_file = BytesIO()
|
||||||
|
# Save to this file as jpeg
|
||||||
|
img.save(new_file, format='JPEG')
|
||||||
|
# In order to use the file, we need to seek back to the 0th position
|
||||||
|
new_file.seek(0)
|
||||||
|
return new_file
|
||||||
|
|
||||||
def get_all_commands(bot):
|
def get_all_commands(bot):
|
||||||
"""Returns a list of all command names for the bot"""
|
"""Returns a list of all command names for the bot"""
|
||||||
|
@ -14,17 +26,17 @@ def get_all_commands(bot):
|
||||||
# Only the command itself will be yielded if there are no children
|
# Only the command itself will be yielded if there are no children
|
||||||
for cmd_name in parent_command_names:
|
for cmd_name in parent_command_names:
|
||||||
cmd = bot.commands.get(cmd_name)
|
cmd = bot.commands.get(cmd_name)
|
||||||
for child_cmd in _get_all_commands(cmd):
|
for child_cmd in get_subcommands(cmd):
|
||||||
all_commands.append(child_cmd)
|
all_commands.append(child_cmd)
|
||||||
|
|
||||||
return all_commands
|
return all_commands
|
||||||
|
|
||||||
def _get_all_commands(command):
|
def get_subcommands(command):
|
||||||
yield command.qualified_name
|
yield command.qualified_name
|
||||||
try:
|
try:
|
||||||
non_aliases = set(cmd.name for cmd in command.commands.values())
|
non_aliases = set(cmd.name for cmd in command.commands.values())
|
||||||
for cmd_name in non_aliases:
|
for cmd_name in non_aliases:
|
||||||
yield from _get_all_commands(command.commands[cmd_name])
|
yield from get_subcommands(command.commands[cmd_name])
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -61,7 +73,7 @@ async def download_image(url):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# Then wrap it in a BytesIO object, to be used like an actual file
|
# Then wrap it in a BytesIO object, to be used like an actual file
|
||||||
image = io.BytesIO(bts)
|
image = BytesIO(bts)
|
||||||
return image
|
return image
|
||||||
|
|
||||||
async def request(url, *, headers=None, payload=None, method='GET', attr='json'):
|
async def request(url, *, headers=None, payload=None, method='GET', attr='json'):
|
||||||
|
|
Loading…
Reference in a new issue