1
0
Fork 0
mirror of synced 2024-05-20 12:32:26 +12:00

Updated for rewrite: Batch 2

This commit is contained in:
phxntxm 2017-03-07 16:35:30 -06:00
commit a05b768274
29 changed files with 884 additions and 973 deletions

10
bot.py
View file

@ -15,11 +15,9 @@ from cogs import utils
opts = {'command_prefix': utils.command_prefix,
'description': utils.bot_description,
'pm_help': None,
'shard_count': utils.shard_count,
'shard_id': utils.shard_id,
'command_not_found': ''}
bot = commands.Bot(**opts)
bot = commands.AutoShardedBot(**opts)
logging.basicConfig(level=logging.WARNING, filename='bonfire.log')
@ -104,12 +102,12 @@ async def on_command_error(error, ctx):
m, s = divmod(error.retry_after, 60)
fmt = "This command is on cooldown! Hold your horses! >:c\nTry again in {} minutes and {} seconds" \
.format(round(m), round(s))
await bot.send_message(ctx.message.channel, fmt)
await ctx.message.channel.send(fmt)
elif isinstance(error, commands.NoPrivateMessage):
fmt = "This command cannot be used in a private message"
await bot.send_message(ctx.message.channel, fmt)
await ctx.message.channel.send(fmt)
elif isinstance(error, commands.MissingRequiredArgument):
await bot.send_message(ctx.message.channel, error)
await ctx.message.channel.send(error)
else:
now = datetime.datetime.now()
with open("error_log", 'a') as f:

View file

@ -34,17 +34,17 @@ class Blackjack:
# When we're done with the game, we need to delete the game itself and remove it's instance from games
# To do this, it needs to be able to access this instance of Blackjack
game = Game(self.bot, message, self)
self.games[message.server.id] = game
self.games[message.guild.id] = game
@commands.group(pass_context=True, no_pm=True, aliases=['bj'], invoke_without_command=True)
@commands.group(no_pm=True, aliases=['bj'], invoke_without_command=True)
@utils.custom_perms(send_messages=True)
async def blackjack(self, ctx):
"""Creates a game/joins the current running game of blackjack
EXAMPLE: !blackjack
RESULT: A new game of blackjack!"""
# Get this server's game if it exists
game = self.games.get(ctx.message.server.id)
# Get this guild's game if it exists
game = self.games.get(ctx.message.guild.id)
# If it doesn't, start one
if game is None:
self.create_game(ctx.message)
@ -54,15 +54,15 @@ class Blackjack:
# If it worked, they're ready to play
if status:
fmt = "{} has joined the game of blackjack, and will be able to play next round!"
await self.bot.say(fmt.format(ctx.message.author.display_name))
await ctx.send(fmt.format(ctx.message.author.display_name))
else:
# Otherwise, lets check *why* they couldn't join
if game.playing(ctx.message.author):
await self.bot.say("You are already playing! Wait for your turn!")
await ctx.send("You are already playing! Wait for your turn!")
else:
await self.bot.say("There are already a max number of players playing/waiting to play!")
await ctx.send("There are already a max number of players playing/waiting to play!")
@blackjack.command(pass_context=True, no_pm=True, name='leave', aliases=['quit'])
@blackjack.command(no_pm=True, name='leave', aliases=['quit'])
@utils.custom_perms(send_messages=True)
async def blackjack_leave(self, ctx):
"""Leaves the current game of blackjack
@ -70,37 +70,37 @@ class Blackjack:
EXAMPLE: !blackjack leave
RESULT: You stop losing money in blackjack"""
# Get this server's game if it exists
game = self.games.get(ctx.message.server.id)
# Get this guild's game if it exists
game = self.games.get(ctx.message.guild.id)
if game is None:
await self.bot.say("There are currently no games of Blackjack running!")
await ctx.send("There are currently no games of Blackjack running!")
return
status = game.leave(ctx.message.author)
if status:
await self.bot.say("You have left the game, and will be removed at the end of this round")
await ctx.send("You have left the game, and will be removed at the end of this round")
else:
await self.bot.say("Either you have already bet, or you are not even playing right now!")
await ctx.send("Either you have already bet, or you are not even playing right now!")
@blackjack.command(pass_context=True, no_pm=True, name='forcestop', aliases=['stop'])
@utils.custom_perms(manage_server=True)
@blackjack.command(no_pm=True, name='forcestop', aliases=['stop'])
@utils.custom_perms(manage_guild=True)
async def blackjack_stop(self, ctx):
"""Forces the game to stop, mostly for use if someone has gone afk
EXAMPLE: !blackjack forcestop
RESULT: No more blackjack spam"""
# Get this server's game if it exists
game = self.games.get(ctx.message.server.id)
# Get this guild's game if it exists
game = self.games.get(ctx.message.guild.id)
if game is None:
await self.bot.say("There are currently no games of Blackjack running!")
await ctx.send("There are currently no games of Blackjack running!")
return
game.task.cancel()
del self.games[ctx.message.server.id]
await self.bot.say("The blackjack game running here has just ended")
del self.games[ctx.message.guild.id]
await ctx.send("The blackjack game running here has just ended")
def FOIL(a, b):
@ -226,7 +226,7 @@ class Game:
async def game_task(self):
"""The task to handle the entire game"""
while len(self.players) > 0:
await self.bot.send_message(self.channel, "A new round has started!!")
await self.channel.send("A new round has started!!")
# First wait for bets
await self.bet_task()
@ -253,25 +253,25 @@ class Game:
await self.cleanup()
# If we reach the end of this loop, that means there are no more players
del self.bj.games[self.channel.server.id]
del self.bj.games[self.channel.guild.id]
async def dealer_task(self):
"""The task handling the dealer's play after all players have stood"""
fmt = "It is the dealer's turn to play\n\n{}".format(self.dealer)
msg = await self.bot.send_message(self.channel, fmt)
msg = await self.channel.send(fmt)
while True:
await asyncio.sleep(1)
if self.dealer.bust:
fmt = msg.content + "\n\nDealer has busted!!"
await self.bot.edit_message(msg, fmt)
await msg.edit(content=fmt)
return
for num in self.dealer.count:
if num > 16:
return
self.hit(self.dealer)
fmt = "It is the dealer's turn to play\n\n{}".format(self.dealer)
msg = await self.bot.edit_message(msg, fmt)
msg = await msg.edit(content=fmt)
async def round_task(self):
"""The task handling the round itself, asking each person to hit or stand"""
@ -280,21 +280,25 @@ class Game:
# A differen task will handle if a player hit blackjack to start (so they would not be 'playing')
# Our check to make sure a valid 'command' was provided
check = lambda m: m.content.lower() in ['hit', 'stand', 'double']
def check(m):
if m.channel == self.channel and m.author == player.member:
return m.content.lower() in ['hit', 'stand', 'double']
else:
return False
# First lets handle the blackjacks
for entry in [p for p in self.players if p['status'] == 'blackjack']:
player = entry['player']
fmt = "You got a blackjack {0.member.mention}!\n\n{0}".format(player)
await self.bot.send_message(self.channel, fmt)
await self.channel.send(fmt)
# Loop through each player (as long as their status is playing) and they have bet chips
for entry in [p for p in self.players if p['status'] == 'playing' and hasattr(p['player'], 'bet')]:
player = entry['player']
# Let them know it's their turn to play
fmt = "It is your turn to play {0.member.mention}\n\n{0}".format(player)
await self.bot.send_message(self.channel, fmt)
await self.channel.send(fmt)
# If they're not playing anymore (i.e. they busted, are standing, etc.) then we don't want to keep asking
# them to hit or stand
@ -302,37 +306,28 @@ class Game:
# Ask if they want to hit or stand
fmt = "Hit, stand, or double?"
await self.bot.send_message(self.channel, fmt)
msg = await self.bot.wait_for_message(timeout=60, author=player.member, channel=self.channel,
check=check)
await self.channel.send(fmt)
# If they took to long, make them stand so the next person can play
if msg is None:
await self.bot.send_message(self.channel, "Took to long! You're standing!")
try:
msg = await self.bot.wait_for('message', timeout=60, check=check)
except asyncio.TimeoutError:
await self.channel.send("Took to long! You're standing!")
entry['status'] = 'stand'
# If they want to hit
elif 'hit' in msg.content.lower():
self.hit(player)
await self.bot.send_message(self.channel, player)
# If they want to stand
elif 'stand' in msg.content.lower():
self.stand(player)
elif 'double' in msg.content.lower():
self.double(player)
await self.bot.send_message(self.channel, player)
# TODO: Handle double, split
else:
# If they want to hit
if 'hit' in msg.content.lower():
self.hit(player)
await self.channel.send(player)
# If they want to stand
elif 'stand' in msg.content.lower():
self.stand(player)
elif 'double' in msg.content.lower():
self.double(player)
await self.channel.send(player)
# TODO: Handle double, split
async def bet_task(self):
"""Performs the task of betting"""
def check(_msg):
"""Makes sure the message provided is within the min and max bets"""
try:
msg_length = int(_msg.content)
return self.min_bet <= msg_length <= self.max_bet
except ValueError:
return _msg.content.lower() == 'skip'
# There is one situation that we want to allow that means we cannot loop through players like normally would
# be the case: Betting has started; while one person is betting, another joins This means our list has
# changed, and neither based on the length or looping through the list itself will handle this To handle
@ -348,28 +343,43 @@ class Game:
entry = players[0]
player = entry['player']
def check(_msg):
"""Makes sure the message provided is within the min and max bets"""
if _msg.channel == self.channel and _msg.author == player.member:
try:
msg_length = int(_msg.content)
return self.min_bet <= msg_length <= self.max_bet
except ValueError:
return _msg.content.lower() == 'skip'
else:
return False
fmt = "Your turn to bet {0.member.mention}, your current chips are: {0.chips}\n" \
"Current min bet is {1}, current max bet is {2}\n" \
"Place your bet now (please provide only the number;" \
"'skip' if you would like to leave this game)".format(player, self.min_bet, self.max_bet)
await self.bot.send_message(self.channel, fmt)
msg = await self.bot.wait_for_message(timeout=60, author=player.member, channel=self.channel, check=check)
if msg is None:
await self.bot.send_message(self.channel, "You took too long! You're sitting this round out")
await self.channel.send(fmt)
try:
msg = await self.bot.wait_for("message", timeout=60, check=check)
except asyncio.TimeoutError:
await self.channel.send("You took too long! You're sitting this round out")
entry['status'] = 'stand'
elif msg.content.lower() == 'skip':
await self.bot.send_message(self.channel, "Alright, you've been removed from the game")
self.leave(player.member)
else:
num = int(msg.content)
# Set the new bet, and remove it from this players chip total
if num <= player.chips:
player.bet = num
player.chips -= num
entry['status'] = 'bet'
if msg.content.lower() == 'skip':
await self.channel.send("Alright, you've been removed from the game")
self.leave(player.member)
else:
await self.bot.send_message(self.channel, "You can't bet more than you have!!")
num = int(msg.content)
# Set the new bet, and remove it from this players chip total
if num <= player.chips:
player.bet = num
player.chips -= num
entry['status'] = 'bet'
else:
await self.channel.send("You can't bet more than you have!!")
# Call this so that we can correct the list, if someone has left or join
self.player_cleanup()
@ -464,7 +474,7 @@ class Game:
# Add that to the main string
fmt += "Blackjacks: {}\n".format(_fmt)
await self.bot.send_message(self.channel, fmt)
await self.channel.send(fmt)
# Do the same for the dealers hand
cards = list(self.dealer.hand.draw(self.dealer.hand.count))
@ -481,10 +491,10 @@ class Game:
self.players.extend(self._added_players)
self._added_players.clear()
# What we want to do is remove any players that are in the game and have left the server
# What we want to do is remove any players that are in the game and have left the guild
for entry in self.players:
m = entry['player'].member
if m not in self.channel.server.members:
if m not in self.channel.guild.members:
self._removed_players.append(entry['player'])
# Remove the players who left

View file

@ -10,7 +10,7 @@ from enum import Enum
class Chess:
def __init__(self, bot):
self.bot = bot
# Our format for games is going to be a little different, because we do want to allow multiple games per server
# Our format for games is going to be a little different, because we do want to allow multiple games per guild
# Format should be {'server_id': [Game, Game, Game]}
self.games = {}
@ -91,18 +91,18 @@ class Chess:
return MoveStatus.invalid
def get_game(self, player):
"""Provides the game this player is playing, in this server"""
server_games = self.games.get(player.server.id, [])
for game in server_games:
"""Provides the game this player is playing, in this guild"""
guild_games = self.games.get(player.guild.id, [])
for game in guild_games:
if player in game.challengers.values():
return game
return None
def in_game(self, player):
"""Checks to see if a player is playing in a game right now, in this server"""
server_games = self.games.get(player.server.id, [])
for game in server_games:
"""Checks to see if a player is playing in a game right now, in this guild"""
guild_games = self.games.get(player.guild.id, [])
for game in guild_games:
if player in game.challengers.values():
return True
@ -111,13 +111,13 @@ class Chess:
def start_game(self, player1, player2):
game = Game(player1, player2)
try:
self.games[player1.server.id].append(game)
self.games[player1.guild.id].append(game)
except KeyError:
self.games[player1.server.id] = [game]
self.games[player1.guild.id] = [game]
return game
@commands.group(pass_contxt=True, invoke_without_command=True)
@commands.group(invoke_without_command=True)
@checks.custom_perms(send_messages=True)
async def chess(self, ctx, *, move):
"""Moves a piece based on the notation provided
@ -142,47 +142,48 @@ class Chess:
EXAMPLE: !rook to d4"""
result = self.play(ctx.message.author, move)
if result is MoveStatus.invalid:
await self.bot.say("That was an invalid move!")
await ctx.send("That was an invalid move!")
elif result is MoveStatus.wrong_turn:
await self.bot.say("It is not your turn to play on your game in this server!")
await ctx.send("It is not your turn to play on your game in this guild!")
elif result is MoveStatus.no_game:
await self.bot.say("You are not currently playing a game on this server!")
await ctx.send("You are not currently playing a game on this guild!")
elif result is MoveStatus.valid:
game = self.get_game(ctx.message.author)
link = game.draw_board()
await self.bot.upload(link)
@commands.command(pass_context=True)
@commands.command()
@checks.custom_perms(send_messages=True)
async def chess_start(self, ctx, player2: discord.Member):
"""Starts a chess game with another player
You can play one game on a single server at a time
You can play one game on a single guild at a time
EXAMPLE: !chess start @Victim
RESULT: A new chess game! Good luck~"""
# Lets first check our permissions; we're not going to create a text based board
# So we need to be able to attach files in order to send the board
if not ctx.message.channel.permissions_for(ctx.message.server.me).attach_files:
await self.bot.say(
if not ctx.message.channel.permissions_for(ctx.message.guild.me).attach_files:
await ctx.send(
"I need to be able to send pictures to provide the board! Please ask someone with mange roles permission, to grant me attach files permission if you want to play this")
return
# Make sure the author and player 2 are not in a game already
if self.in_game(ctx.message.author):
await self.bot.say("Sorry, but you can only be in one game per server at a time")
await ctx.send("Sorry, but you can only be in one game per guild at a time")
return
if self.in_game(player2):
await self.bot.say("Sorry, but {} is already in a game on this server!".format(player2.display_name))
await ctx.send("Sorry, but {} is already in a game on this guild!".format(player2.display_name))
return
# Start the game
game = self.start_game(ctx.message.author, player2)
player1 = game.challengers.get('white')
await self.bot.say(
"{} you have started a chess game with {}\n\n{} will be white this game, and is going first.\nIf you need information about the notation used to play, run {}help chess".format(
ctx.message.author.display_name, player2.display_name, ctx.prefix))
await ctx.send(
"{} you have started a chess game with {}\n\n{} will be white this game, and is going first.\nIf you need "
"information about the notation used to play, run {}help chess".format(
ctx.message.author.display_name, player2.display_name, player1, ctx.prefix))
class MoveStatus(Enum):
@ -209,7 +210,7 @@ class Game:
self.reset_board()
# The point of chess revolves around the king's position
# Due to this, we're going to use the king's position a lot, so lets save this variable
# Due to this, we're going to use the king's position a lot, so lets save this variable
self.w_king_pos = (0, 4)
self.b_king_pos = (7, 4)
@ -262,7 +263,7 @@ class Game:
colour = 'black'
king_pos = self.b_king_pos
if not can_castle[colour][pos]:
if not self.can_castle[colour][pos]:
return False
# During castling, the row should never change
@ -388,7 +389,7 @@ class Game:
def check(self):
"""Checks our current board, and checks (based on whose turn it is) if we're in a 'check' state"""
# To check for a check, what we should do is loop through the board
# Then check if it's the the current players turn's piece, and compare to moving to the king's position
# Then check if it's the the current players turn's piece, and compare to moving to the king's position
for x, row in enumerate(self.board):
for y, piece in enumerate(row):
if self.white_turn and re.search('B.', piece) and self.valid_move((x, y), self.b_king_pos):

View file

@ -10,7 +10,6 @@ import re
import calendar
import pendulum
import datetime
import math
class Core:
@ -23,30 +22,7 @@ class Core:
self.results_per_page = 10
self.commands = None
def find_command(self, command):
# This method ensures the command given is valid. We need to loop through commands
# As self.bot.commands only includes parent commands
# So we are splitting the command in parts, looping through the commands
# And getting the subcommand based on the next part
# If we try to access commands of a command that isn't a group
# We'll hit an AttributeError, meaning an invalid command was given
# If we loop through and don't find anything, cmd will still be None
# And we'll report an invalid was given as well
cmd = None
for part in command.split():
try:
if cmd is None:
cmd = self.bot.commands.get(part)
else:
cmd = cmd.commands.get(part)
except AttributeError:
cmd = None
break
return cmd
@commands.command(pass_context=True)
@commands.command()
@utils.custom_perms(send_messages=True)
async def help(self, ctx, *, message=None):
"""This command is used to provide a link to the help URL.
@ -65,7 +41,7 @@ class Core:
try:
page = int(message)
except:
cmd = self.find_command(message)
cmd = self.bot.get_command(message)
if cmd is None:
entries = sorted(utils.get_all_commands(self.bot))
@ -73,7 +49,7 @@ class Core:
pages = utils.Pages(self.bot, message=ctx.message, entries=entries)
await pages.paginate(start_page=page)
except utils.CannotPaginate as e:
await self.bot.say(str(e))
await ctx.send(str(e))
else:
# Get the description for a command
description = cmd.help
@ -100,11 +76,11 @@ class Core:
if subcommands:
embed.add_field(name='Subcommands', value="\n".join(subcommands), inline=False)
await self.bot.say(embed=embed)
await ctx.send(embed=embed)
@commands.command()
@utils.custom_perms(send_messages=True)
async def motd(self, *, date=None):
async def motd(self, ctx, *, date=None):
"""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
@ -126,10 +102,10 @@ class Core:
motd = latest_motd['motd']
# This will be hit if we do not have any entries for motd
except TypeError:
await self.bot.say("No message of the day!")
await ctx.send("No message of the day!")
else:
fmt = "Last updated: {}\n\n{}".format(date, motd)
await self.bot.say(fmt)
await ctx.send(fmt)
else:
try:
r_filter = pendulum.parse(date)
@ -137,18 +113,18 @@ class Core:
date = motd[0]['date']
motd = motd[0]['motd']
fmt = "Message of the day for {}:\n\n{}".format(date, motd)
await self.bot.say(fmt)
await ctx.send(fmt)
# This one will be hit if we return None for that day
except TypeError:
await self.bot.say("No message of the day for {}!".format(date))
await ctx.send("No message of the day for {}!".format(date))
# This will be hit if pendulum fails to parse the date passed
except ValueError:
now = pendulum.utcnow().to_date_string()
await self.bot.say("Invalid date format! Try like {}".format(now))
await ctx.send("Invalid date format! Try like {}".format(now))
@commands.command()
@utils.custom_perms(send_messages=True)
async def calendar(self, month: str = None, year: int = None):
async def calendar(self, ctx, month: str = None, year: int = None):
"""Provides a printout of the current month's calendar
Provide month and year to print the calendar of that year and month
@ -176,30 +152,23 @@ class Core:
else:
month = months.get(month.lower())
if month is None:
await self.bot.say("Please provide a valid Month!")
await ctx.send("Please provide a valid Month!")
return
# If year was not passed, use the current year
if year is None:
year = datetime.datetime.today().year
# Here we create the actual "text" calendar that we are printing
cal = calendar.TextCalendar().formatmonth(year, month)
await self.bot.say("```\n{}```".format(cal))
await ctx.send("```\n{}```".format(cal))
@commands.command()
@utils.custom_perms(send_messages=True)
async def info(self):
async def info(self, ctx):
"""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
# The only real use of doing it this way is easier editing if the info
# in this command is changed
bot_data = await utils.get_content('bot_data')
total_data = {'member_count': 0,
'server_count': 0}
for entry in bot_data:
total_data['member_count'] += entry['member_count']
total_data['server_count'] += entry['server_count']
# Create the original embed object
opts = {'title': 'Dev Server',
'description': 'Join the server above for any questions/suggestions about me.',
@ -207,8 +176,8 @@ class Core:
embed = discord.Embed(**opts)
# Add the normal values
embed.add_field(name='Total Servers', value=total_data['server_count'])
embed.add_field(name='Total Members', value=total_data['member_count'])
embed.add_field(name='Total Servers', value=len(self.bot.servers))
embed.add_field(name='Total Members', value=len(set(self.bot.get_all_members())))
# Count the variable values; hangman, tictactoe, etc.
hm_games = len(self.bot.get_cog('Hangman').games)
@ -233,20 +202,20 @@ class Core:
embed.add_field(name='Uptime', value=(pendulum.utcnow() - self.bot.uptime).in_words())
embed.set_footer(text=self.bot.description)
await self.bot.say(embed=embed)
await ctx.send(embed=embed)
@commands.command()
@utils.custom_perms(send_messages=True)
async def uptime(self):
async def uptime(self, ctx):
"""Provides a printout of the current bot's uptime
EXAMPLE: !uptime
RESULT: A BAJILLION DAYS"""
await self.bot.say("Uptime: ```\n{}```".format((pendulum.utcnow() - self.bot.uptime).in_words()))
await ctx.send("Uptime: ```\n{}```".format((pendulum.utcnow() - self.bot.uptime).in_words()))
@commands.command(aliases=['invite'])
@utils.custom_perms(send_messages=True)
async def addbot(self):
async def addbot(self, ctx):
"""Provides a link that you can use to add me to a server
EXAMPLE: !addbot
@ -261,13 +230,17 @@ class Core:
perms.embed_links = True
perms.read_message_history = True
perms.attach_files = True
perms.speak = True
perms.connect = True
perms.attach_files = True
perms.add_reactions = True
app_info = await self.bot.application_info()
await self.bot.say("Use this URL to add me to a server that you'd like!\n{}"
.format(discord.utils.oauth_url(app_info.id, perms)))
await ctx.send("Use this URL to add me to a server that you'd like!\n{}"
.format(discord.utils.oauth_url(app_info.id, perms)))
@commands.command()
@utils.custom_perms(send_messages=True)
async def doggo(self):
async def doggo(self, ctx):
"""Use this to print a random doggo image.
EXAMPLE: !doggo
@ -275,11 +248,11 @@ class Core:
# Find a random image based on how many we currently have
f = random.SystemRandom().choice(glob.glob('images/doggo*'))
with open(f, 'rb') as f:
await self.bot.upload(f)
await ctx.send(file=f)
@commands.command()
@utils.custom_perms(send_messages=True)
async def snek(self):
async def snek(self, ctx):
"""Use this to print a random snek image.
EXAMPLE: !snek
@ -287,11 +260,11 @@ class Core:
# Find a random image based on how many we currently have
f = random.SystemRandom().choice(glob.glob('images/snek*'))
with open(f, 'rb') as f:
await self.bot.upload(f)
await ctx.send(file=f)
@commands.command()
@utils.custom_perms(send_messages=True)
async def joke(self):
async def joke(self, ctx):
"""Prints a random riddle
EXAMPLE: !joke
@ -302,13 +275,13 @@ class Core:
try:
fortune = subprocess.check_output(
fortune_command.split()).decode("utf-8")
await self.bot.say(fortune)
await ctx.send(fortune)
except discord.HTTPException:
pass
else:
break
@commands.command(pass_context=True)
@commands.command()
@utils.custom_perms(send_messages=True)
async def roll(self, ctx, notation: str = "d6"):
"""Rolls a die based on the notation given
@ -325,7 +298,7 @@ class Core:
# Check if something like ed3 was provided, or something else entirely
# was provided
except (AttributeError, ValueError):
await self.bot.say("Please provide the die notation in #d#!")
await ctx.send("Please provide the die notation in #d#!")
return
# Dice will be None if d# was provided, assume this means 1d#
@ -334,16 +307,16 @@ class Core:
# have it set
dice = int(dice)
if dice > 10:
await self.bot.say("I'm not rolling more than 10 dice, I have tiny hands")
await ctx.send("I'm not rolling more than 10 dice, I have tiny hands")
return
if num > 100:
await self.bot.say("What die has more than 100 sides? Please, calm down")
await ctx.send("What die has more than 100 sides? Please, calm down")
return
if num <= 1:
await self.bot.say("A {} sided die? You know that's impossible right?".format(num))
await ctx.send("A {} sided die? You know that's impossible right?".format(num))
return
nums = [random.SystemRandom().randint(1, num) for i in range(0, int(dice))]
nums = [random.SystemRandom().randint(1, num) for _ in range(0, int(dice))]
total = sum(nums)
value_str = ", ".join("{}".format(x) for x in nums)
@ -351,7 +324,7 @@ class Core:
fmt = '{0.message.author.name} has rolled a {2} sided die and got the number {3}!'
else:
fmt = '{0.message.author.name} has rolled {1}, {2} sided dice and got the numbers {3}, for a total of {4}!'
await self.bot.say(fmt.format(ctx, dice, num, value_str, total))
await ctx.send(fmt.format(ctx, dice, num, value_str, total))
def setup(bot):

View file

@ -59,9 +59,7 @@ class Deviantart:
try:
for entry in content:
user = discord.utils.get(self.bot.get_all_members(), id=entry['member_id'])
# If we're sharded, we might not be able to find this user.
# If the bot is not in the server with the member either
# If the bot is not in the server with the member, we might not be able to find this user.
if user is None:
continue
@ -94,10 +92,10 @@ class Deviantart:
if last_updated_id is not None:
fmt = "There has been a new post by an artist you are subscribed to!\n\n" \
"**Title:** {}\n**User:** {}\n**URL:** {}".format(
result['title'],
result['author']['username'],
result['url'])
await self.bot.send_message(user, fmt)
result['title'],
result['author']['username'],
result['url'])
await user.send(fmt)
# Now we can update the user's last updated for this DA
# We want to do this whether or not our last if statement was met
r_filter = {'member_id': user.id}
@ -110,12 +108,12 @@ class Deviantart:
@commands.group()
@utils.custom_perms(send_messages=True)
async def da(self):
async def da(self, ctx):
"""This provides a sort of 'RSS' feed for subscribed to artists.
Subscribe to artists, and I will PM you when new posts come out from these artists"""
pass
@da.command(pass_context=True, name='sub', aliases=['add', 'subscribe'])
@da.command(name='sub', aliases=['add', 'subscribe'])
@utils.custom_perms(send_messages=True)
async def da_sub(self, ctx, *, username):
"""This can be used to add a feed to your notifications.
@ -130,7 +128,7 @@ class Deviantart:
if content is None:
entry = {'member_id': ctx.message.author.id, 'subbed': [username], 'last_updated': {}}
await utils.add_content('deviantart', entry, r_filter)
await self.bot.say("You have just subscribed to {}!".format(username))
await ctx.send("You have just subscribed to {}!".format(username))
elif content[0]['subbed'] is None or username not in content[0]['subbed']:
if content[0]['subbed'] is None:
sub_list = [username]
@ -138,9 +136,9 @@ class Deviantart:
content[0]['subbed'].append(username)
sub_list = content[0]['subbed']
await utils.update_content('deviantart', {'subbed': sub_list}, r_filter)
await self.bot.say("You have just subscribed to {}!".format(username))
await ctx.send("You have just subscribed to {}!".format(username))
else:
await self.bot.say("You are already subscribed to that user!")
await ctx.send("You are already subscribed to that user!")
@da.command(pass_context=True, name='unsub', aliases=['delete', 'remove', 'unsubscribe'])
@utils.custom_perms(send_messages=True)
@ -153,13 +151,13 @@ class Deviantart:
content = await utils.get_content('deviantart', r_filter)
if content is None or content[0]['subbed'] is None:
await self.bot.say("You are not subscribed to anyone at the moment!")
await ctx.send("You are not subscribed to anyone at the moment!")
elif username in content[0]['subbed']:
content[0]['subbed'].remove(username)
await utils.update_content('deviantart', {'subbed': content[0]['subbed']}, r_filter)
await self.bot.say("You have just unsubscribed from {}!".format(username))
await ctx.send("You have just unsubscribed from {}!".format(username))
else:
await self.bot.say("You are not subscribed to that user!")
await ctx.send("You are not subscribed to that user!")
def setup(bot):

View file

@ -19,7 +19,7 @@ class Music:
async def on_voice_state_update(self, before, after):
pass
@commands.command(pass_context=True, no_pm=True, enabled=False)
@commands.command(no_pm=True, enabled=False)
@checks.custom_perms(send_messages=True)
async def progress(self, ctx):
"""Provides the progress of the current song
@ -28,7 +28,7 @@ class Music:
RESULT: 532 minutes! (Hopefully not)"""
pass
@commands.command(pass_context=True, no_pm=True, enabled=False)
@commands.command(no_pm=True, enabled=False)
@checks.custom_perms(send_messages=True)
async def join(self, ctx, *, channel: discord.Channel):
"""Joins a voice channel.
@ -37,7 +37,7 @@ class Music:
RESULT: I'm in the Music voice channel!"""
pass
@commands.command(pass_context=True, no_pm=True, enabled=False)
@commands.command(no_pm=True, enabled=False)
@checks.custom_perms(send_messages=True)
async def summon(self, ctx):
"""Summons the bot to join your voice channel.
@ -46,7 +46,7 @@ class Music:
RESULT: I'm in your voice channel!"""
pass
@commands.command(pass_context=True, no_pm=True, enabled=False)
@commands.command(no_pm=True, enabled=False)
@checks.custom_perms(send_messages=True)
async def play(self, ctx, *, song: str):
"""Plays a song.
@ -61,7 +61,7 @@ class Music:
"""
pass
@commands.command(pass_context=True, no_pm=True, enabled=False)
@commands.command(no_pm=True, enabled=False)
@checks.custom_perms(kick_members=True)
async def volume(self, ctx, value: int = None):
"""Sets the volume of the currently playing song.
@ -70,7 +70,7 @@ class Music:
RESULT: My volume is now set to 50"""
pass
@commands.command(pass_context=True, no_pm=True, enabled=False)
@commands.command(no_pm=True, enabled=False)
@checks.custom_perms(kick_members=True)
async def pause(self, ctx):
"""Pauses the currently played song.
@ -79,7 +79,7 @@ class Music:
RESULT: I'm paused!"""
pass
@commands.command(pass_context=True, no_pm=True, enabled=False)
@commands.command(no_pm=True, enabled=False)
@checks.custom_perms(kick_members=True)
async def resume(self, ctx):
"""Resumes the currently played song.
@ -88,7 +88,7 @@ class Music:
RESULT: Ain't paused no more!"""
pass
@commands.command(pass_context=True, no_pm=True, enabled=False)
@commands.command(no_pm=True, enabled=False)
@checks.custom_perms(kick_members=True)
async def stop(self, ctx):
"""Stops playing audio and leaves the voice channel.
@ -98,7 +98,7 @@ class Music:
RESULT: No more music"""
pass
@commands.command(pass_context=True, no_pm=True, enabled=False)
@commands.command(no_pm=True, enabled=False)
@checks.custom_perms(send_messages=True)
async def eta(self, ctx):
"""Provides an ETA on when your next song will play
@ -107,7 +107,7 @@ class Music:
RESULT: 5,000 days! Lol have fun"""
pass
@commands.command(pass_context=True, no_pm=True, enabled=False)
@commands.command(no_pm=True, enabled=False)
@checks.custom_perms(send_messages=True)
async def queue(self, ctx):
"""Provides a printout of the songs that are in the queue.
@ -121,7 +121,7 @@ class Music:
RESULT: A list of shitty songs you probably don't wanna listen to"""
pass
@commands.command(pass_context=True, no_pm=True, enabled=False)
@commands.command(no_pm=True, enabled=False)
@checks.custom_perms(send_messages=True)
async def queuelength(self, ctx):
"""Prints the length of the queue
@ -130,7 +130,7 @@ class Music:
RESULT: Probably 10 songs"""
pass
@commands.command(pass_context=True, no_pm=True, enabled=False)
@commands.command(no_pm=True, enabled=False)
@checks.custom_perms(send_messages=True)
async def skip(self, ctx):
"""Vote to skip a song. The song requester can automatically skip.
@ -142,7 +142,7 @@ class Music:
"""
pass
@commands.command(pass_context=True, no_pm=True, enabled=False)
@commands.command(no_pm=True, enabled=False)
@checks.custom_perms(kick_members=True)
async def modskip(self, ctx):
"""Forces a song skip, can only be used by a moderator
@ -151,7 +151,7 @@ class Music:
RESULT: No more terrible song :D"""
pass
@commands.command(pass_context=True, no_pm=True, enabled=False)
@commands.command(no_pm=True, enabled=False)
@checks.custom_perms(send_messages=True)
async def playing(self, ctx):
"""Shows info about the currently played song.

View file

@ -48,38 +48,17 @@ class StatsUpdate:
log.info('bots.discord.pw statistics returned {} for {}'.format(resp.status, payload))
async def on_server_join(self, server):
r_filter = {'shard_id': config.shard_id}
server_count = len(self.bot.servers)
member_count = len(set(self.bot.get_all_members()))
entry = {'server_count': server_count, 'member_count': member_count, "shard_id": config.shard_id}
# Check if this was successful, if it wasn't, that means a new shard was added and we need to add that entry
if not await config.update_content('bot_data', entry, r_filter):
await config.add_content('bot_data', entry, r_filter)
self.bot.loop.create_task(self.update())
async def on_server_leave(self, server):
r_filter = {'shard_id': config.shard_id}
server_count = len(self.bot.servers)
member_count = len(set(self.bot.get_all_members()))
entry = {'server_count': server_count, 'member_count': member_count, "shard_id": config.shard_id}
# Check if this was successful, if it wasn't, that means a new shard was added and we need to add that entry
if not await config.update_content('bot_data', entry, r_filter):
await config.add_content('bot_data', entry, r_filter)
self.bot.loop.create_task(self.update())
async def on_ready(self):
r_filter = {'shard_id': config.shard_id}
server_count = len(self.bot.servers)
member_count = len(set(self.bot.get_all_members()))
entry = {'server_count': server_count, 'member_count': member_count, "shard_id": config.shard_id}
# Check if this was successful, if it wasn't, that means a new shard was added and we need to add that entry
if not await config.update_content('bot_data', entry, r_filter):
await config.add_content('bot_data', entry, r_filter)
self.bot.loop.create_task(self.update())
async def on_member_join(self, member):
server = member.server
r_filter = {'server_id': server.id}
guild = member.guild
r_filter = {'server_id': guild.id}
notifications = await config.get_content('user_notifications', r_filter)
try:
@ -91,12 +70,12 @@ class StatsUpdate:
if not channel_id:
return
channel = server.get_channel(channel_id)
await self.bot.send_message(channel, "Welcome to the '{0.server.name}' server {0.mention}!".format(member))
channel = guild.get_channel(channel_id)
await channel.send("Welcome to the '{0.server.name}' server {0.mention}!".format(member))
async def on_member_remove(self, member):
server = member.server
r_filter = {'server_id': server.id}
guild = member.guild
r_filter = {'server_id': guild.id}
notifications = await config.get_content('user_notifications', r_filter)
try:
@ -109,9 +88,7 @@ class StatsUpdate:
return
channel = server.get_channel(channel_id)
await self.bot.send_message(channel,
"{0} has left the server, I hope it wasn't because of something I said :c".format(
member.display_name))
await channelsend("{0} has left the server, I hope it wasn't because of something I said :c".format(member.display_name))
def setup(bot):

View file

@ -104,10 +104,10 @@ class Hangman:
def create(self, word, ctx):
# Create a new game, then save it as the server's game
game = Game(word)
self.games[ctx.message.server.id] = game
self.games[ctx.message.guild.id] = game
return game
@commands.group(aliases=['hm'], pass_context=True, no_pm=True, invoke_without_command=True)
@commands.group(aliases=['hm'], no_pm=True, invoke_without_command=True)
@commands.cooldown(1, 7, BucketType.user)
@checks.custom_perms(send_messages=True)
async def hangman(self, ctx, *, guess):
@ -115,10 +115,10 @@ class Hangman:
EXAMPLE: !hangman e (or) !hangman The Phrase!
RESULT: Hopefully a win!"""
game = self.games.get(ctx.message.server.id)
game = self.games.get(ctx.message.guild.id)
if not game:
ctx.command.reset_cooldown(ctx)
await self.bot.say("There are currently no hangman games running!")
await ctx.send("There are currently no hangman games running!")
return
# Check if we are guessing a letter or a phrase. Only one letter can be guessed at a time
@ -128,7 +128,7 @@ class Hangman:
if len(guess) == 1:
if guess in game.guessed_letters:
ctx.command.reset_cooldown(ctx)
await self.bot.say("That letter has already been guessed!")
await ctx.send("That letter has already been guessed!")
# Return here as we don't want to count this as a failure
return
if game.guess_letter(guess):
@ -143,16 +143,16 @@ class Hangman:
if game.win():
fmt += " You guys got it! The word was `{}`".format(game.word)
del self.games[ctx.message.server.id]
del self.games[ctx.message.guild.id]
elif game.failed():
fmt += " Sorry, you guys failed...the word was `{}`".format(game.word)
del self.games[ctx.message.server.id]
del self.games[ctx.message.guild.id]
else:
fmt += str(game)
await self.bot.say(fmt)
await ctx.send(fmt)
@hangman.command(name='create', aliases=['start'], no_pm=True, pass_context=True)
@hangman.command(name='create', aliases=['start'], no_pm=True)
@checks.custom_perms(send_messages=True)
async def create_hangman(self, ctx):
"""This is used to create a new hangman game
@ -163,16 +163,16 @@ class Hangman:
# Only have one hangman game per server, since anyone
# In a server (except the creator) can guess towards the current game
if self.games.get(ctx.message.server.id) is not None:
await self.bot.say("Sorry but only one Hangman game can be running per server!")
if self.games.get(ctx.message.guild.id) is not None:
await ctx.send("Sorry but only one Hangman game can be running per server!")
return
game = self.create(random.SystemRandom().choice(phrases), ctx)
# Let them know the game has started, then print the current game so that the blanks are shown
await self.bot.say(
await ctx.send(
"Alright, a hangman game has just started, you can start guessing now!\n{}".format(str(game)))
@hangman.command(name='delete', aliases=['stop', 'remove', 'end'], pass_context=True, no_pm=True)
@hangman.command(name='delete', aliases=['stop', 'remove', 'end'], no_pm=True)
@checks.custom_perms(kick_members=True)
async def stop_game(self, ctx):
"""Force stops a game of hangman
@ -181,12 +181,12 @@ class Hangman:
EXAMPLE: !hangman stop
RESULT: No more men being hung"""
if self.games.get(ctx.message.server.id) is None:
await self.bot.say("There are no Hangman games running on this server!")
if self.games.get(ctx.message.guild.id) is None:
await ctx.send("There are no Hangman games running on this server!")
return
del self.games[ctx.message.server.id]
await self.bot.say("I have just stopped the game of Hangman, a new should be able to be started now!")
del self.games[ctx.message.guild.id]
await ctx.send("I have just stopped the game of Hangman, a new should be able to be started now!")
def setup(bot):

View file

@ -85,7 +85,7 @@ class Interaction:
self.battles = {}
def user_battling(self, ctx, player2=None):
battling = self.battles.get(ctx.message.server.id)
battling = self.battles.get(ctx.message.guild.id)
# If no one is battling, obviously the user is not battling
if battling is None:
@ -101,14 +101,14 @@ class Interaction:
# Handles removing the author from the dictionary of battles
def battling_off(self, ctx):
battles = self.battles.get(ctx.message.server.id) or {}
battles = self.battles.get(ctx.message.guild.id) or {}
player_id = ctx.message.author.id
# Create a new dictionary, exactly the way the last one was setup
# But don't include any that have the author's ID
self.battles[ctx.message.server.id] = {p1: p2 for p1, p2 in battles.items() if
not p2 == player_id and not p1 == player_id}
self.battles[ctx.message.guild.id] = {p1: p2 for p1, p2 in battles.items() if
not p2 == player_id and not p1 == player_id}
@commands.command(pass_context=True, no_pm=True)
@commands.command(no_pm=True)
@utils.custom_perms(send_messages=True)
async def hug(self, ctx, user: discord.Member = None):
"""Makes me hug a person!
@ -119,9 +119,9 @@ class Interaction:
user = ctx.message.author
fmt = random.SystemRandom().choice(hugs)
await self.bot.say(fmt.format(user.display_name))
await ctx.send(fmt.format(user.display_name))
@commands.command(pass_context=True, no_pm=True)
@commands.command(no_pm=True)
@utils.custom_perms(send_messages=True)
async def avatar(self, ctx, member: discord.Member = None):
"""Provides an image for the provided person's avatar (yours if no other member is provided)
@ -133,21 +133,21 @@ class Interaction:
member = ctx.message.author
url = member.avatar_url
if ctx.message.server.me.permissions_in(ctx.message.channel).attach_files:
if ctx.message.guild.me.permissions_in(ctx.message.channel).attach_files:
file = await utils.download_image(url)
if file is None:
await self.bot.say(url)
await ctx.send(url)
else:
if '.gif' in url:
filename = 'avatar.gif'
else:
filename = 'avatar.webp'
file = utils.convert_to_jpeg(file)
await self.bot.upload(file, filename=filename)
await ctx.send(file=file, filename=filename)
else:
await self.bot.say(url)
await ctx.send(url)
@commands.group(pass_context=True, no_pm=True, invoke_without_command=True)
@commands.group(no_pm=True, invoke_without_command=True)
@commands.cooldown(1, 180, BucketType.user)
@utils.custom_perms(send_messages=True)
async def battle(self, ctx, player2: discord.Member):
@ -157,29 +157,29 @@ class Interaction:
RESULT: A battle to the death"""
if ctx.message.author.id == player2.id:
ctx.command.reset_cooldown(ctx)
await self.bot.say("Why would you want to battle yourself? Suicide is not the answer")
await ctx.send("Why would you want to battle yourself? Suicide is not the answer")
return
if self.bot.user.id == player2.id:
ctx.command.reset_cooldown(ctx)
await self.bot.say("I always win, don't even try it.")
await ctx.send("I always win, don't even try it.")
return
if self.user_battling(ctx, player2):
ctx.command.reset_cooldown(ctx)
await self.bot.say("You or the person you are trying to battle is already in a battle!")
await ctx.send("You or the person you are trying to battle is already in a battle!")
return
# Add the author and player provided in a new battle
battles = self.battles.get(ctx.message.server.id) or {}
battles = self.battles.get(ctx.message.guild.id) or {}
battles[ctx.message.author.id] = player2.id
self.battles[ctx.message.server.id] = battles
self.battles[ctx.message.guild.id] = battles
fmt = "{0.message.author.mention} has challenged you to a battle {1.mention}\n" \
"{0.prefix}accept or {0.prefix}decline"
# Add a call to turn off battling, if the battle is not accepted/declined in 3 minutes
self.bot.loop.call_later(180, self.battling_off, ctx)
await self.bot.say(fmt.format(ctx, player2))
await ctx.send(fmt.format(ctx, player2))
@commands.command(pass_context=True, no_pm=True)
@commands.command(no_pm=True)
@utils.custom_perms(send_messages=True)
async def accept(self, ctx):
"""Accepts the battle challenge
@ -188,13 +188,13 @@ class Interaction:
RESULT: Hopefully the other person's death"""
# This is a check to make sure that the author is the one being BATTLED
# And not the one that started the battle
battles = self.battles.get(ctx.message.server.id) or {}
battles = self.battles.get(ctx.message.guild.id) or {}
p1 = [p1_id for p1_id, p2_id in battles.items() if p2_id == ctx.message.author.id]
if len(p1) == 0:
await self.bot.say("You are not currently being challenged to a battle!")
await ctx.send("You are not currently being challenged to a battle!")
return
battleP1 = discord.utils.find(lambda m: m.id == p1[0], ctx.message.server.members)
battleP1 = discord.utils.find(lambda m: m.id == p1[0], ctx.message.guild.members)
battleP2 = ctx.message.author
# Get a random win message from our list
@ -205,13 +205,13 @@ class Interaction:
# Randomize the order of who is printed/sent to the update system
# All we need to do is change what order the challengers are printed/added as a paramater
if random.SystemRandom().randint(0, 1):
await self.bot.say(fmt.format(battleP1.mention, battleP2.mention))
await ctx.send(fmt.format(battleP1.mention, battleP2.mention))
await utils.update_records('battle_records', battleP1, battleP2)
else:
await self.bot.say(fmt.format(battleP2.mention, battleP1.mention))
await ctx.send(fmt.format(battleP2.mention, battleP1.mention))
await utils.update_records('battle_records', battleP2, battleP1)
@commands.command(pass_context=True, no_pm=True)
@commands.command(no_pm=True)
@utils.custom_perms(send_messages=True)
async def decline(self, ctx):
"""Declines the battle challenge
@ -220,23 +220,23 @@ class Interaction:
RESULT: You chicken out"""
# This is a check to make sure that the author is the one being BATTLED
# And not the one that started the battle
battles = self.battles.get(ctx.message.server.id) or {}
battles = self.battles.get(ctx.message.guild.id) or {}
p1 = [p1_id for p1_id, p2_id in battles.items() if p2_id == ctx.message.author.id]
if len(p1) == 0:
await self.bot.say("You are not currently being challenged to a battle!")
await ctx.send("You are not currently being challenged to a battle!")
return
battleP1 = discord.utils.find(lambda m: m.id == p1[0], ctx.message.server.members)
battleP1 = discord.utils.find(lambda m: m.id == p1[0], ctx.message.guild.members)
battleP2 = ctx.message.author
# There's no need to update the stats for the members if they declined the battle
self.battling_off(ctx)
await self.bot.say("{0} has chickened out! What a loser~".format(battleP2.mention, battleP1.mention))
await ctx.send("{0} has chickened out! What a loser~".format(battleP2.mention, battleP1.mention))
@commands.command(pass_context=True, no_pm=True)
@commands.command(no_pm=True)
@commands.cooldown(1, 180, BucketType.user)
@utils.custom_perms(send_messages=True)
async def boop(self, ctx, boopee: discord.Member = None, *, message = ""):
async def boop(self, ctx, boopee: discord.Member = None, *, message=""):
"""Boops the mentioned person
EXAMPLE: !boop @OtherPerson
@ -244,18 +244,18 @@ class Interaction:
booper = ctx.message.author
if boopee is None:
ctx.command.reset_cooldown(ctx)
await self.bot.say("You try to boop the air, the air boops back. Be afraid....")
await ctx.send("You try to boop the air, the air boops back. Be afraid....")
return
# To keep formatting easier, keep it either "" or the message with a space in front
if message is not None:
message = " " + message
if boopee.id == booper.id:
ctx.command.reset_cooldown(ctx)
await self.bot.say("You can't boop yourself! Silly...")
await ctx.send("You can't boop yourself! Silly...")
return
if boopee.id == self.bot.user.id:
ctx.command.reset_cooldown(ctx)
await self.bot.say("Why the heck are you booping me? Get away from me >:c")
await ctx.send("Why the heck are you booping me? Get away from me >:c")
return
r_filter = {'member_id': booper.id}
@ -275,7 +275,7 @@ class Interaction:
amount = 1
fmt = "{0.mention} has just booped {1.mention}{3}! That's {2} times now!"
await self.bot.say(fmt.format(booper, boopee, amount, message))
await ctx.send(fmt.format(booper, boopee, amount, message))
def setup(bot):

View file

@ -17,13 +17,15 @@ class Links:
def __init__(self, bot):
self.bot = bot
@commands.command(pass_context=True, aliases=['g'])
@commands.command(aliases=['g'])
@utils.custom_perms(send_messages=True)
async def google(self, ctx, *, query: str):
"""Searches google for a provided query
EXAMPLE: !g Random cat pictures!
RESULT: Links to sites with random cat pictures!"""
await ctx.message.channel.trigger_typing()
url = "https://www.google.com/search"
# Turn safe filter on or off, based on whether or not this is a nsfw channel
@ -43,7 +45,7 @@ class Links:
data = await utils.request(url, payload=params, attr='text')
if data is None:
await self.bot.send_message(ctx.message.channel, "I failed to connect to google! (That can happen??)")
await ctx.send("I failed to connect to google! (That can happen??)")
return
# Convert to a BeautifulSoup element and loop through each result clasified by h3 tags with a class of 'r'
@ -55,7 +57,7 @@ class Links:
try:
result_url = re.search('(?<=q=).*(?=&sa=)', element.find('a').get('href')).group(0)
except AttributeError:
await self.bot.say("I couldn't find any results for {}!".format(query))
await ctx.send("I couldn't find any results for {}!".format(query))
return
# Get the next sibling, find the span where the description is, and get the text from this
@ -68,15 +70,17 @@ class Links:
fmt += '\n\n**URL**: <{}>\n**Description**: {}'.format(result_url, description)
fmt = "**Top 3 results for the query** _{}_:{}".format(query, fmt)
await self.bot.say(fmt)
await ctx.send(fmt)
@commands.command(aliases=['yt'], pass_context=True)
@commands.command(aliases=['yt'])
@utils.custom_perms(send_messages=True)
async def youtube(self, ctx, *, query: str):
"""Searches youtube for a provided query
EXAMPLE: !youtube Cat videos!
RESULT: Cat videos!"""
await ctx.message.channel.trigger_typing()
key = utils.youtube_key
url = "https://www.googleapis.com/youtube/v3/search"
params = {'key': key,
@ -87,13 +91,13 @@ class Links:
data = await utils.request(url, payload=params)
if data is None:
await self.bot.send_message(ctx.message.channel, "Sorry but I failed to connect to youtube!")
await ctx.send("Sorry but I failed to connect to youtube!")
return
try:
result = data['items'][0]
except IndexError:
await self.bot.say("I could not find any results with the search term {}".format(query))
await ctx.send("I could not find any results with the search term {}".format(query))
return
result_url = "https://youtube.com/watch?v={}".format(result['id']['videoId'])
@ -101,15 +105,17 @@ class Links:
description = result['snippet']['description']
fmt = "**Title:** {}\n\n**Description:** {}\n\n**URL:** <{}>".format(title, description, result_url)
await self.bot.say(fmt)
await ctx.send(fmt)
@commands.command(pass_context=True)
@commands.command()
@utils.custom_perms(send_messages=True)
async def wiki(self, ctx, *, query: str):
"""Pulls the top match for a specific term from wikipedia, and returns the result
EXAMPLE: !wiki Test
RESULT: A link to the wikipedia article for the word test"""
await ctx.message.channel.trigger_typing()
# All we need to do is search for the term provided, so the action, list, and format never need to change
base_url = "https://en.wikipedia.org/w/api.php"
params = {"action": "query",
@ -120,11 +126,11 @@ class Links:
data = await utils.request(base_url, payload=params)
if data is None:
await self.bot.send_message(ctx.message.channel, "Sorry but I failed to connect to Wikipedia!")
await ctx.send("Sorry but I failed to connect to Wikipedia!")
return
if len(data['query']['search']) == 0:
await self.bot.say("I could not find any results with that term, I tried my best :c")
await ctx.send("I could not find any results with that term, I tried my best :c")
return
# Wiki articles' URLs are in the format https://en.wikipedia.org/wiki/[Titlehere]
# Replace spaces with %20
@ -136,44 +142,48 @@ class Links:
snippet = re.sub('</span>', '', snippet)
snippet = re.sub('&quot;', '"', snippet)
await self.bot.say(
await ctx.send(
"Here is the best match I found with the query `{}`:\nURL: <{}>\nSnippet: \n```\n{}```".format(query, url,
snippet))
@commands.command(pass_context=True)
@commands.command()
@utils.custom_perms(send_messages=True)
async def urban(self, ctx, *, msg: str):
"""Pulls the top urbandictionary.com definition for a term
EXAMPLE: !urban a normal phrase
RESULT: Probably something lewd; this is urban dictionary we're talking about"""
await ctx.message.channel.trigger_typing()
url = "http://api.urbandictionary.com/v0/define"
params = {"term": msg}
try:
data = await utils.request(url, payload=params)
if data is None:
await self.bot.send_message(ctx.message.channel, "Sorry but I failed to connect to urban dictionary!")
await ctx.send("Sorry but I failed to connect to urban dictionary!")
return
# List is the list of definitions found, if it's empty then nothing was found
if len(data['list']) == 0:
await self.bot.say("No result with that term!")
await ctx.send("No result with that term!")
# If the list is not empty, use the first result and print it's defintion
else:
await self.bot.say(data['list'][0]['definition'])
await ctx.send(data['list'][0]['definition'])
# Urban dictionary has some long definitions, some might not be able to be sent
except discord.HTTPException:
await self.bot.say('```\nError: Definition is too long for me to send```')
await ctx.send('```\nError: Definition is too long for me to send```')
except KeyError:
await self.bot.say("Sorry but I failed to connect to urban dictionary!")
await ctx.send("Sorry but I failed to connect to urban dictionary!")
@commands.command(pass_context=True)
@commands.command()
@utils.custom_perms(send_messages=True)
async def derpi(self, ctx, *search: str):
"""Provides a random image from the first page of derpibooru.org for the following term
EXAMPLE: !derpi Rainbow Dash
RESULT: A picture of Rainbow Dash!"""
await ctx.message.channel.trigger_typing()
if len(search) > 0:
url = 'https://derpibooru.org/search.json'
@ -192,18 +202,16 @@ class Links:
else:
params['q'] += ", safe"
await self.bot.say("Looking up an image with those tags....")
try:
# Get the response from derpibooru and parse the 'search' result from it
data = await utils.request(url, payload=params)
if data is None:
await self.bot.send_message(ctx.message.channel, "Sorry but I failed to connect to Derpibooru!")
await ctx.send("Sorry but I failed to connect to Derpibooru!")
return
results = data['search']
except KeyError:
await self.bot.say("No results with that search term, {0}!".format(ctx.message.author.mention))
await ctx.send("No results with that search term, {0}!".format(ctx.message.author.mention))
return
# The first request we've made ensures there are results
@ -215,7 +223,7 @@ class Links:
params['page'] = random.SystemRandom().randint(1, pages)
data = await utils.request(url, payload=params)
if data is None:
await self.bot.say("Sorry but I failed to connect to Derpibooru!")
await ctx.send("Sorry but I failed to connect to Derpibooru!")
return
# Now get the results again
results = data['search']
@ -224,16 +232,16 @@ class Links:
index = random.SystemRandom().randint(0, len(results) - 1)
image_link = 'https://derpibooru.org/{}'.format(results[index]['id'])
else:
await self.bot.say("No results with that search term, {0}!".format(ctx.message.author.mention))
await ctx.send("No results with that search term, {0}!".format(ctx.message.author.mention))
return
else:
# If no search term was provided, search for a random image
# .url will be the URL we end up at, not the one requested.
# https://derpibooru.org/images/random redirects to a random image, so this is exactly what we want
image_link = await utils.request('https://derpibooru.org/images/random', attr='url')
await self.bot.say(image_link)
await ctx.send(image_link)
@commands.command(pass_context=True)
@commands.command()
@utils.custom_perms(send_messages=True)
async def e621(self, ctx, *, tags: str):
"""Searches for a random image from e621.net
@ -242,19 +250,17 @@ class Links:
EXAMPLE: !e621 dragon
RESULT: A picture of a dragon (hopefully, screw your tagging system e621)"""
await ctx.message.channel.trigger_typing()
# This changes the formatting for queries, so we don't
# Have to use e621's stupid formatting when using the command
tags = tags.replace(' ', '_')
tags = tags.replace(',_', ' ')
url = 'https://e621.net/post/index.json'
params = {'limit': 320,
'tags': tags}
# e621 provides a way to change how many images can be shown on one request
# This gives more of a chance of random results, however it causes the lookup to take longer than most
# Due to this, send a message saying we're looking up the information first
await self.bot.say("Looking up an image with those tags....")
r_filter = {'channel_id': ctx.message.channel.id}
nsfw_channels = await utils.get_content("nsfw_channels", r_filter)
@ -266,8 +272,7 @@ class Links:
data = await utils.request(url, payload=params)
if data is None:
await self.bot.send_message(ctx.message.channel,
"Sorry, I had trouble connecting at the moment; please try again later")
await ctx.send("Sorry, I had trouble connecting at the moment; please try again later")
return
# Try to find an image from the list. If there were no results, we're going to attempt to find
@ -276,9 +281,9 @@ class Links:
# i.e. it responded with a 404/504/etc.
try:
rand_image = data[random.SystemRandom().randint(0, len(data) - 1)]['file_url']
await self.bot.say(rand_image)
await ctx.send(rand_image)
except (ValueError, KeyError):
await self.bot.say("No results with that tag {}".format(ctx.message.author.mention))
await ctx.send("No results with that tag {}".format(ctx.message.author.mention))
return

View file

@ -16,53 +16,30 @@ class Mod:
def __init__(self, bot):
self.bot = bot
def find_command(self, command):
# This method ensures the command given is valid. We need to loop through commands
# As self.bot.commands only includes parent commands
# So we are splitting the command in parts, looping through the commands
# And getting the subcommand based on the next part
# If we try to access commands of a command that isn't a group
# We'll hit an AttributeError, meaning an invalid command was given
# If we loop through and don't find anything, cmd will still be None
# And we'll report an invalid was given as well
cmd = None
for part in command.split():
try:
if cmd is None:
cmd = self.bot.commands.get(part)
else:
cmd = cmd.commands.get(part)
except AttributeError:
cmd = None
break
return cmd
@commands.command(pass_context=True, no_pm=True, aliases=['nick'])
@commands.command(no_pm=True, aliases=['nick'])
@utils.custom_perms(kick_members=True)
async def nickname(self, ctx, *, name=None):
"""Used to set the nickname for Bonfire (provide no nickname and it will reset)
EXAMPLE: !nick Music Bot
RESULT: My nickname is now music bot"""
await self.bot.change_nickname(ctx.message.server.me, name)
await self.bot.say("\N{OK HAND SIGN}")
RESULT: My nickname is now Music Bot"""
await ctx.message.server.me.edit(nick=name)
await ctx.send("\N{OK HAND SIGN}")
@commands.command(no_pm=True)
@utils.custom_perms(kick_members=True)
async def kick(self, member: discord.Member):
async def kick(self, ctx, member: discord.Member):
"""Used to kick a member from this server
EXAMPLE: !kick @Member
RESULT: They're kicked from the server?"""
try:
await self.bot.kick(member)
await self.bot.say("\N{OK HAND SIGN}")
await member.kick()
await ctx.send("\N{OK HAND SIGN}")
except discord.Forbidden:
await self.bot.say("But I can't, muh permissions >:c")
await ctx.send("But I can't, muh permissions >:c")
@commands.command(pass_context=True, no_pm=True)
@commands.command(no_pm=True)
@utils.custom_perms(ban_members=True)
async def unban(self, ctx, member_id: int):
"""Used to unban a member from this server
@ -74,16 +51,15 @@ class Mod:
# Lets only accept an int for this method, in order to ensure only an ID is provided
# Due to that though, we need to ensure a string is passed as the member's ID
member = discord.Object(id=str(member_id))
try:
await self.bot.unban(ctx.message.server, member)
await self.bot.say("\N{OK HAND SIGN}")
await discord.http.unban(member_id, ctx.guild.id)
await ctx.send("\N{OK HAND SIGN}")
except discord.Forbidden:
await self.bot.say("But I can't, muh permissions >:c")
await ctx.send("But I can't, muh permissions >:c")
except discord.HTTPException:
await self.bot.say("Sorry, I failed to unban that user!")
await ctx.send("Sorry, I failed to unban that user!")
@commands.command(pass_context=True, no_pm=True)
@commands.command(no_pm=True)
@utils.custom_perms(ban_members=True)
async def ban(self, ctx, *, member):
"""Used to ban a member
@ -95,31 +71,35 @@ class Mod:
# Lets first check if a user ID was provided, as that will be the easiest case to ban
if member.isdigit():
# First convert it to a discord object based on the ID that was given
member = discord.Object(id=member)
# Next, to ban from the server the API takes a server obejct and uses that ID
# So set "this" server as the member's server. This creates the "fake" member we need
member.server = ctx.message.server
try:
await discord.http.ban(member, ctx.guild.id)
await ctx.send("\N{OK HAND SIGN}")
except discord.Forbidden:
await ctx.send("But I can't, muh permissions >:c")
except discord.HTTPException:
await ctx.send("Sorry, I failed to ban that user!")
finally:
return
else:
# If no ID was provided, lets try to convert what was given using the internal coverter
converter = commands.converter.UserConverter(ctx, member)
converter = commands.converter.MemberConverter(ctx, member)
try:
member = converter.convert()
except commands.converter.BadArgument:
await self.bot.say(
await ctx.send(
'{} does not appear to be a valid member. If this member is not in this server, please provide '
'their ID'.format(member))
return
# Now lets try actually banning the member we've been given
try:
await self.bot.ban(member)
await self.bot.say("\N{OK HAND SIGN}")
await member.ban()
await ctx.send("\N{OK HAND SIGN}")
except discord.Forbidden:
await self.bot.say("But I can't, muh permissions >:c")
await ctx.send("But I can't, muh permissions >:c")
except discord.HTTPException:
await self.bot.say("Sorry, I failed to ban that user!")
await ctx.send("Sorry, I failed to ban that user!")
@commands.command(pass_context=True, no_pm=True)
@commands.command(no_pm=True)
@utils.custom_perms(kick_members=True)
async def alerts(self, ctx, channel: discord.Channel):
"""This command is used to set a channel as the server's 'notifications' channel
@ -132,10 +112,10 @@ class Mod:
'channel_id': channel.id}
if not await utils.add_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"
"\nAll notifications will now go to `{}`".format(channel))
await ctx.send("I have just changed this server's 'notifications' channel"
"\nAll notifications will now go to `{}`".format(channel))
@commands.command(pass_context=True, no_pm=True)
@commands.command(no_pm=True)
@utils.custom_perms(kick_members=True)
async def usernotify(self, ctx, on_off: str):
"""This command can be used to set whether or not you want user notificaitons to show
@ -148,22 +128,21 @@ class Mod:
# So we base this channel on it's own and not from alerts
# When mod logging becomes available, that will be kept to it's own channel if wanted as well
on_off = ctx.message.channel.id if re.search("(on|yes|true)", on_off.lower()) else None
r_filter = {'server_id': ctx.message.server.id}
entry = {'server_id': ctx.message.server.id,
r_filter = {'server_id': ctx.message.guild.id}
entry = {'server_id': ctx.message.guild.id,
'channel_id': on_off}
if not await utils.add_content('user_notifications', entry, r_filter):
await utils.update_content('user_notifications', entry, r_filter)
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 ctx.send("This server will now {} if someone has joined or left".format(fmt))
@commands.group(pass_context=True)
@commands.group()
async def nsfw(self, ctx):
"""Handles adding or removing a channel as a nsfw channel"""
# This command isn't meant to do anything, so just send an error if an invalid subcommand is passed
if ctx.invoked_subcommand is None:
await self.bot.say('Invalid subcommand passed: {0.subcommand_passed}'.format(ctx))
pass
@nsfw.command(name="add", pass_context=True)
@nsfw.command(name="add")
@utils.custom_perms(kick_members=True)
async def nsfw_add(self, ctx):
"""Registers this channel as a 'nsfw' channel
@ -172,11 +151,11 @@ class Mod:
RESULT: ;)"""
r_filter = {'channel_id': ctx.message.channel.id}
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 ctx.send("This channel has just been registered as 'nsfw'! Have fun you naughties ;)")
else:
await self.bot.say("This channel is already registered as 'nsfw'!")
await ctx.send("This channel is already registered as 'nsfw'!")
@nsfw.command(name="remove", aliases=["delete"], pass_context=True)
@nsfw.command(name="remove", aliases=["delete"])
@utils.custom_perms(kick_members=True)
async def nsfw_remove(self, ctx):
"""Removes this channel as a 'nsfw' channel
@ -185,11 +164,11 @@ class Mod:
RESULT: ;("""
r_filter = {'channel_id': ctx.message.channel.id}
if await utils.remove_content('nsfw_channels', r_filter):
await self.bot.say("This channel has just been unregistered as a nsfw channel")
await ctx.send("This channel has just been unregistered as a nsfw channel")
else:
await self.bot.say("This channel is not registered as a ''nsfw' channel!")
await ctx.send("This channel is not registered as a ''nsfw' channel!")
@commands.command(pass_context=True)
@commands.command()
@utils.custom_perms(kick_members=True)
async def say(self, ctx, *, msg: str):
"""Tells the bot to repeat what you say
@ -197,13 +176,13 @@ class Mod:
EXAMPLE: !say I really like orange juice
RESULT: I really like orange juice"""
fmt = "\u200B{}".format(msg)
await self.bot.say(fmt)
await ctx.send(fmt)
try:
await self.bot.delete_message(ctx.message)
await ctx.message.delete()
except:
pass
@commands.group(pass_context=True, invoke_without_command=True, no_pm=True)
@commands.group(invoke_without_command=True, no_pm=True)
@utils.custom_perms(send_messages=True)
async def perms(self, ctx, *, command: str = None):
"""This command can be used to print the current allowed permissions on a specific command
@ -212,20 +191,20 @@ class Mod:
EXAMPLE: !perms help RESULT: Hopefully a result saying you just need send_messages permissions; otherwise lol
this server's admin doesn't like me """
if command is None:
await self.bot.say(
await ctx.send(
"Valid permissions are: ```\n{}```".format("\n".join("{}".format(i) for i in valid_perms)))
return
r_filter = {'server_id': ctx.message.server.id}
r_filter = {'server_id': ctx.message.guild.id}
server_perms = await utils.get_content('custom_permissions', r_filter)
try:
server_perms = server_perms[0]
except TypeError:
server_perms = {}
cmd = self.find_command(command)
cmd = self.bot.get_command(command)
if cmd is None:
await self.bot.say("That is not a valid command!")
await ctx.send("That is not a valid command!")
return
perms_value = server_perms.get(cmd.qualified_name)
@ -240,15 +219,15 @@ class Mod:
# Able to manage the server (for the utils on perm commands)
for func in cmd.utils:
if "is_owner" in func.__qualname__:
await self.bot.say("You need to own the bot to run this command")
await ctx.send("You need to own the bot to run this command")
return
await self.bot.say(
await ctx.send(
"You are required to have `manage_server` permissions to run `{}`".format(cmd.qualified_name))
return
# Perms will be an attribute if custom_perms is found no matter what, so no need to check this
perms = "\n".join(attribute for attribute, setting in custom_perms.perms.items() if setting)
await self.bot.say(
await ctx.send(
"You are required to have `{}` permissions to run `{}`".format(perms, cmd.qualified_name))
else:
# Permissions are saved as bit values, so create an object based on that value
@ -256,10 +235,10 @@ class Mod:
# There's no need to check for errors here, as we ensure a permission is valid when adding it
permissions = discord.Permissions(perms_value)
needed_perm = [perm[0] for perm in permissions if perm[1]][0]
await self.bot.say("You need to have the permission `{}` "
"to use the command `{}` in this server".format(needed_perm, command))
await ctx.send("You need to have the permission `{}` "
"to use the command `{}` in this server".format(needed_perm, command))
@perms.command(name="add", aliases=["setup,create"], pass_context=True, no_pm=True)
@perms.command(name="add", aliases=["setup,create"], no_pm=True)
@commands.has_permissions(manage_server=True)
async def add_perms(self, ctx, *msg: str):
"""Sets up custom permissions on the provided command
@ -274,8 +253,8 @@ class Mod:
try:
permissions = msg[len(msg) - 1]
except IndexError:
await self.bot.say("Please provide the permissions you want to setup, the format for this must be in:\n"
"`perms add <command> <permission>`")
await ctx.send("Please provide the permissions you want to setup, the format for this must be in:\n"
"`perms add <command> <permission>`")
return
# If a user can run a command, they have to have send_messages permissions; so use this as the base
@ -288,15 +267,15 @@ class Mod:
try:
setattr(perm_obj, permissions, True)
except AttributeError:
await self.bot.say("{} does not appear to be a valid permission! Valid permissions are: ```\n{}```"
.format(permissions, "\n".join(valid_perms)))
await ctx.send("{} does not appear to be a valid permission! Valid permissions are: ```\n{}```"
.format(permissions, "\n".join(valid_perms)))
return
perm_value = perm_obj.value
cmd = self.find_command(command)
cmd = self.bot.get_command(command)
if cmd is None:
await self.bot.say(
await ctx.send(
"That command does not exist! You can't have custom permissions on a non-existant command....")
return
@ -305,12 +284,12 @@ class Mod:
# 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
for check in cmd.checks:
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!")
if "is_owner" == check.__name__ or "has_permissions" not in str(check):
await ctx.send("This command cannot have custom permissions setup!")
return
r_filter = {'server_id': ctx.message.server.id}
entry = {'server_id': ctx.message.server.id,
r_filter = {'server_id': ctx.message.guild.id}
entry = {'server_id': ctx.message.guild.id,
cmd.qualified_name: perm_value}
# In all other cases, I've used add_content before update_content
@ -322,10 +301,10 @@ class Mod:
# Same case as prefixes, for now, trigger a manual update
self.bot.loop.create_task(utils.cache['custom_permissions'].update())
await self.bot.say("I have just added your custom permissions; "
"you now need to have `{}` permissions to use the command `{}`".format(permissions, command))
await ctx.send("I have just added your custom permissions; "
"you now need to have `{}` permissions to use the command `{}`".format(permissions, command))
@perms.command(name="remove", aliases=["delete"], pass_context=True, no_pm=True)
@perms.command(name="remove", aliases=["delete"], no_pm=True)
@commands.has_permissions(manage_server=True)
async def remove_perms(self, ctx, *, command: str):
"""Removes the custom permissions setup on the command specified
@ -333,32 +312,32 @@ class Mod:
EXAMPLE: !perms remove play
RESULT: Freedom!"""
cmd = self.find_command(command)
cmd = self.bot.get_command(command)
if cmd is None:
await self.bot.say(
await ctx.send(
"That command does not exist! You can't have custom permissions on a non-existant command....")
return
r_filter = {'server_id': ctx.message.server.id}
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 ctx.send("I have just removed the custom permissions for {}!".format(cmd))
# Same case as prefixes, for now, trigger a manual update
self.bot.loop.create_task(utils.cache['custom_permissions'].update())
@commands.command(pass_context=True, no_pm=True)
@utils.custom_perms(manage_server=True)
@commands.command(no_pm=True)
@utils.custom_perms(manage_guild=True)
async def prefix(self, ctx, *, prefix: str):
"""This command can be used to set a custom prefix per server
EXAMPLE: !prefix new_prefix
RESULT: You probably screwing it up and not realizing you now need to do new_prefixprefix"""
r_filter = {'server_id': ctx.message.server.id}
r_filter = {'server_id': ctx.message.guild.id}
if prefix.lower().strip() == "none":
prefix = None
entry = {'server_id': ctx.message.server.id,
entry = {'server_id': ctx.message.guild.id,
'prefix': prefix}
if not await utils.add_content('prefixes', entry, r_filter):
@ -369,9 +348,9 @@ class Mod:
else:
fmt = "I have just updated the prefix for this server; you now need to call commands with `{0}`. " \
"For example, you can call this command again with {0}prefix".format(prefix)
await self.bot.say(fmt)
await ctx.send(fmt)
@commands.command(pass_context=True, no_pm=True)
@commands.command(no_pm=True)
@utils.custom_perms(manage_messages=True)
async def purge(self, ctx, limit: int = 100):
"""This command is used to a purge a number of messages from the channel
@ -379,18 +358,18 @@ class Mod:
EXAMPLE: !purge 50
RESULT: -50 messages in this channel"""
if not ctx.message.channel.permissions_for(ctx.message.server.me).manage_messages:
await self.bot.say("I do not have permission to delete messages...")
await ctx.send("I do not have permission to delete messages...")
return
try:
await self.bot.purge_from(ctx.message.channel, limit=limit)
await ctx.message.channel.purge(limit=limit)
except discord.HTTPException:
await self.bot.send_message(ctx.message.channel, "Detected messages that are too far "
"back for me to delete; I can only bulk delete messages"
" that are under 14 days old.")
@commands.command(pass_context=True, no_pm=True)
@commands.command(no_pm=True)
@utils.custom_perms(manage_messages=True)
async def prune(self, ctx, limit = None):
async def prune(self, ctx, limit=None):
"""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
If no limit is provided, then 100 will be used. This is also the max limit we can use
@ -414,8 +393,9 @@ class Mod:
# If no members are provided, assume we're trying to prune our own messages
members = ctx.message.mentions
roles = ctx.message.role_mentions
if len(members) == 0:
members = [ctx.message.server.me]
members = [ctx.message.guild.me]
# Our check for if a message should be deleted
def check(m):
@ -437,36 +417,37 @@ class Mod:
async for msg in self.bot.logs_from(ctx.message.channel, before=ctx.message):
if check(msg):
try:
await self.bot.delete_message(msg)
await msg.delete()
count += 1
except:
pass
if count >= limit:
break
msg = await self.bot.say("{} messages succesfully deleted".format(count))
msg = await ctx.send("{} messages succesfully deleted".format(count))
await asyncio.sleep(5)
try:
await self.bot.delete_message(msg)
await self.bot.delete_message(ctx.message)
except discord.NotFound:
await msg.delete()
await ctx.message.delete()
except:
pass
@commands.group(aliases=['rule'], pass_context=True, no_pm=True, invoke_without_command=True)
@commands.group(aliases=['rule'], no_pm=True, invoke_without_command=True)
@utils.custom_perms(send_messages=True)
async def rules(self, ctx, rule: int = None):
"""This command can be used to view the current rules on the server
EXAMPLE: !rules 5
RESULT: Rule 5 is printed"""
r_filter = {'server_id': ctx.message.server.id}
r_filter = {'server_id': ctx.message.guild.id}
rules = await utils.get_content('rules', r_filter)
try:
rules = rules[0]['rules']
except TypeError:
await self.bot.say("This server currently has no rules on it! I see you like to live dangerously...")
await ctx.send("This server currently has no rules on it! I see you like to live dangerously...")
return
if len(rules) == 0:
await self.bot.say("This server currently has no rules on it! I see you like to live dangerously...")
await ctx.send("This server currently has no rules on it! I see you like to live dangerously...")
return
if rule is None:
@ -475,16 +456,16 @@ class Mod:
pages.title = "Rules for {}".format(ctx.message.server.name)
await pages.paginate()
except utils.CannotPaginate as e:
await self.bot.say(str(e))
await ctx.send(str(e))
else:
try:
fmt = rules[rule - 1]
except IndexError:
await self.bot.say("That rules does not exist.")
await ctx.send("That rules does not exist.")
return
await self.bot.say("Rule {}: \"{}\"".format(rule, fmt))
await ctx.send("Rule {}: \"{}\"".format(rule, fmt))
@rules.command(name='add', aliases=['create'], pass_context=True, no_pm=True)
@rules.command(name='add', aliases=['create'], no_pm=True)
@utils.custom_perms(manage_server=True)
async def rules_add(self, ctx, *, rule: str):
"""Adds a rule to this server's rules
@ -498,9 +479,9 @@ class Mod:
if not await utils.update_content('rules', update, 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 ctx.send("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'], no_pm=True)
@utils.custom_perms(manage_server=True)
async def rules_delete(self, ctx, rule: int):
"""Removes one of the rules from the list of this server's rules
@ -511,9 +492,9 @@ class Mod:
r_filter = {'server_id': ctx.message.server.id}
update = {'rules': r.row['rules'].delete_at(rule - 1)}
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 ctx.send("That is not a valid rule number, try running the command again.")
else:
await self.bot.say("I have just removed that rule from your list of rules!")
await ctx.send("I have just removed that rule from your list of rules!")
def setup(bot):

View file

@ -1,46 +1,18 @@
from .utils import config
from .utils import checks
from .utils import images
from . import utils
from discord.ext import commands
import discord
import aiohttp
# https://github.com/ppy/osu-api/wiki
base_url = 'https://osu.ppy.sh/api/'
BASE_URL = 'https://osu.ppy.sh/api/'
MAX_RETRIES = 5
class Osu:
def __init__(self, bot):
self.bot = bot
self.headers = {'User-Agent': config.user_agent}
self.key = config.osu_key
async def _request(self, payload, endpoint):
"""Handles requesting to the API"""
# Format the URL we'll need based on the base_url, and the endpoint we want to hit
url = "{}{}".format(base_url, endpoint)
# Check if our key was added, if it wasn't, add it
key = payload.get('k', self.key)
payload['k'] = key
# Attempt to connect up to our max retries
for x in range(MAX_RETRIES):
try:
async with aiohttp.get(url, headers=self.headers, params=payload) as r:
# If we failed to connect, attempt again
if r.status != 200:
continue
data = await r.json()
return data
# If any error happened when making the request, attempt again
except:
continue
self.key = utils.osu_key
self.payload = {'k': self.key}
async def find_beatmap(self, query):
"""Finds a beatmap ID based on the first match of searching a beatmap"""
@ -48,21 +20,22 @@ class Osu:
async def get_beatmap(self, b_id):
"""Gets beatmap info based on the ID provided"""
params = {'b': b_id}
endpoint = 'get_beatmaps'
data = await self._request(params, endpoint)
payload = self.payload.copy()
payload['b'] = b_id
url = BASE_URL + 'get_beatmaps'
data = await utils.request(url, payload=payload)
try:
return data[0]
except IndexError:
except (IndexError, TypeError):
return None
@commands.group(pass_context=True, invoke_without_command=True)
@checks.custom_perms(send_messages=True)
@commands.group(invoke_without_command=True)
@utils.custom_perms(send_messages=True)
async def osu(self, ctx):
pass
@osu.command(name='scores', aliases=['score'], pass_context=True)
@checks.custom_perms(send_messages=True)
@osu.command(name='scores', aliases=['score'])
@utils.custom_perms(send_messages=True)
async def osu_user_scores(self, ctx, user, song=1):
"""Used to get the top scores for a user
You can provide either your Osu ID or your username
@ -73,7 +46,7 @@ class Osu:
EXAMPLE: !osu MyUsername 5
RESULT: Info about your 5th best song"""
await self.bot.say("Looking up your Osu information...")
await ctx.send("Looking up your Osu information...")
# To make this easy for the user, it's indexed starting at 1, so lets subtract by 1
song -= 1
# Make sure the song is not negative however, if so set to 0
@ -93,17 +66,18 @@ class Osu:
'countmiss': 'misses',
'perfect': 'got_max_combo'}
params = {'u': user,
'limit': 100}
# The endpoint that we're accessing to get this informatin
endpoint = 'get_user_best'
data = await self._request(params, endpoint)
payload = self.payload.copy()
payload['u'] = user
payload['limit'] = 100
# The endpoint that we're accessing to get this information
url = BASE_URL + 'get_user_beat'
data = await utils.request(url, payload=payload)
try:
data = data[song]
except IndexError:
if len(data) == 0:
await self.bot.say("I could not find any top songs for the user {}".format(user))
except (IndexError, TypeError):
if data is not None and len(data) == 0:
await ctx.send("I could not find any top songs for the user {}".format(user))
return
else:
data = data[len(data) - 1]
@ -130,14 +104,14 @@ class Osu:
# Attempt to create our banner and upload that
# If we can't find the images needed, or don't have permissions, just send a message instead
try:
banner = await images.create_banner(ctx.message.author, "Osu User Stats", fmt)
banner = await utils.create_banner(ctx.message.author, "Osu User Stats", fmt)
await self.bot.upload(banner)
except (FileNotFoundError, discord.Forbidden):
_fmt = "\n".join("{}: {}".format(k, r) for k, r in fmt)
await self.bot.say("```\n{}```".format(_fmt))
await ctx.send("```\n{}```".format(_fmt))
@osu.command(name='user', pass_context=True)
@checks.custom_perms(send_messages=True)
@utils.custom_perms(send_messages=True)
async def osu_user_info(self, ctx, *, user):
"""Used to get information about a specific user
You can provide either your Osu ID or your username
@ -147,7 +121,7 @@ class Osu:
EXAMPLE: !osu user MyUserName
RESULT: Info about your user"""
await self.bot.say("Looking up your Osu information...")
await ctx.send("Looking up your Osu information...")
# A list of the possible values we'll receive, that we want to display
wanted_info = ['username', 'playcount', 'ranked_score', 'pp_rank', 'level', 'pp_country_rank',
'accuracy', 'country', 'pp_country_rank', 'count_rank_s', 'count_rank_a']
@ -159,16 +133,17 @@ class Osu:
'count_rank_a': 'total_a_ranks'}
# The paramaters that we'll send to osu to get the information needed
params = {'u': user}
# The endpoint that we're accessing to get this informatin
endpoint = 'get_user'
data = await self._request(params, endpoint)
payload = self.payload.copy()
payload['u'] = user
# The endpoint that we're accessing to get this information
url = BASE_URL + 'get_user'
data = await utils.request(url, payload=payload)
# Make sure we found a result, we should only find one with the way we're searching
try:
data = data[0]
except IndexError:
await self.bot.say("I could not find anyone with the user name/id of {}".format(user))
except (IndexError, TypeError):
await ctx.send("I could not find anyone with the user name/id of {}".format(user))
return
# Now lets create our dictionary needed to create the image
@ -181,11 +156,11 @@ class Osu:
# Attempt to create our banner and upload that
# If we can't find the images needed, or don't have permissions, just send a message instead
try:
banner = await images.create_banner(ctx.message.author, "Osu User Stats", fmt)
await self.bot.upload(banner)
banner = await utils.create_banner(ctx.message.author, "Osu User Stats", fmt)
await ctx.send(file=banner)
except (FileNotFoundError, discord.Forbidden):
_fmt = "\n".join("{}: {}".format(k, r) for k, r in fmt.items())
await self.bot.say("```\n{}```".format(_fmt))
await ctx.send("```\n{}```".format(_fmt))
def setup(bot):

View file

@ -1,13 +1,9 @@
from .utils import config
from .utils import checks
from .utils import images
from . import utils
from discord.ext import commands
import discord
import aiohttp
base_url = "https://api.owapi.net/api/v3/u/"
BASE_URL = "https://api.owapi.net/api/v3/u/"
# This is a list of the possible things that we may want to retrieve from the stats
# The API returns something if it exists, and leaves it out of the data returned entirely if it does not
# For example if you have not won with a character, wins will not exist in the list
@ -15,7 +11,6 @@ base_url = "https://api.owapi.net/api/v3/u/"
check_g_stats = ["eliminations", "deaths", 'kpd', 'wins', 'losses', 'time_played',
'cards', 'damage_done', 'healing_done', 'multikills']
check_o_stats = ['wins']
MAX_RETRIES = 5
class Overwatch:
@ -23,29 +18,6 @@ class Overwatch:
def __init__(self, bot):
self.bot = bot
self.headers = {"User-Agent": config.user_agent}
self.session = aiohttp.ClientSession()
async def _request(self, payload, endpoint):
"""Handles requesting to the API"""
# Format the URL we'll need based on the base_url, and the endpoint we want to hit
url = "{}{}".format(base_url, endpoint)
# Attempt to connect up to our max retries
for x in range(MAX_RETRIES):
try:
async with aiohttp.ClientSession(headers=self.headers) as session:
async with session.get(url, params=payload) as r:
# If we failed to connect, attempt again
if r.status != 200:
continue
data = await r.json()
return data
# If any error happened when making the request, attempt again
except:
continue
@commands.group(no_pm=True)
async def ow(self):
@ -55,29 +27,31 @@ class Overwatch:
Capitalization also matters"""
pass
@ow.command(name="stats", pass_context=True)
@checks.custom_perms(send_messages=True)
@ow.command(name="stats")
@utils.custom_perms(send_messages=True)
async def ow_stats(self, ctx, user: discord.Member = None, hero: str = ""):
"""Prints out a basic overview of a member's stats
Provide a hero after the member to get stats for that specific hero
EXAMPLE: !ow stats @OtherPerson Junkrat
RESULT: Whether or not you should unfriend this person because they're a dirty rat"""
await ctx.message.channel.trigger_typing()
user = user or ctx.message.author
r_filter = {'member_id': user.id}
ow_stats = await config.get_content('overwatch', r_filter)
ow_stats = await utils.get_content('overwatch', r_filter)
if ow_stats is None:
await self.bot.say("I do not have this user's battletag saved!")
await ctx.send("I do not have this user's battletag saved!")
return
# This API sometimes takes a while to look up information, so send a message saying we're processing
await self.bot.say("Searching profile information....")
bt = ow_stats[0]['battletag']
if hero == "":
# If no hero was provided, we just want the base stats for a player
data = await self._request(None, "{}/stats".format(bt))
url = BASE_URL + "{}/stats".format(bt)
data = await utils.request(url)
region = [x for x in data.keys() if data[x] is not None][0]
stats = data[region]['stats']['quickplay']
@ -86,8 +60,8 @@ class Overwatch:
else:
# If there was a hero provided, search for a user's data on that hero
hero = hero.lower().replace('-', '')
endpoint = "{}/heroes".format(bt)
data = await self._request(None, endpoint)
url = BASE_URL + "{}/heroes".format(bt)
data = await utils.request(url)
region = [x for x in data.keys() if data[x] is not None][0]
stats = data[region]['heroes']['stats']['quickplay'].get(hero)
@ -95,7 +69,7 @@ class Overwatch:
if stats is None:
fmt = "I couldn't find data with that hero, make sure that is a valid hero, " \
"otherwise {} has never used the hero {} before!".format(user.display_name, hero)
await self.bot.say(fmt)
await ctx.send(fmt)
return
# Same list comprehension as before
@ -104,55 +78,57 @@ class Overwatch:
for k, r in stats['hero_stats'].items():
output_data.append((k.title().replace("_", " "), r))
try:
banner = await images.create_banner(user, "Overwatch", output_data)
await self.bot.upload(banner)
banner = await utils.create_banner(user, "Overwatch", output_data)
await ctx.send(file=banner)
except (FileNotFoundError, discord.Forbidden):
fmt = "\n".join("{}: {}".format(k, r) for k, r in output_data)
await self.bot.say("Overwatch stats for {}: ```py\n{}```".format(user.name, fmt))
await ctx.send("Overwatch stats for {}: ```py\n{}```".format(user.name, fmt))
@ow.command(pass_context=True, name="add")
@checks.custom_perms(send_messages=True)
@utils.custom_perms(send_messages=True)
async def add(self, ctx, bt: str):
"""Saves your battletag for looking up information
EXAMPLE: !ow add Username#1234
RESULT: Your battletag is now saved"""
await ctx.message.channel.trigger_typing()
# Battletags are normally provided like name#id
# However the API needs this to be a -, so repliace # with - if it exists
bt = bt.replace("#", "-")
r_filter = {'member_id': ctx.message.author.id}
# This API sometimes takes a while to look up information, so send a message saying we're processing
await self.bot.say("Looking up your profile information....")
await ctx.send("Looking up your profile information....")
# All we're doing here is ensuring that the status is 200 when looking up someone's general information
# If it's not, let them know exactly how to format their tag
endpoint = "{}/stats".format(bt)
data = await self._request(None, endpoint)
url = BASE_URL + "{}/stats".format(bt)
data = await utils.request(url)
if data is None:
await self.bot.say("Profile does not exist! Battletags are picky, "
"format needs to be `user#xxxx`. Capitalization matters")
return
await ctx.send("Profile does not exist! Battletags are picky, "
"format needs to be `user#xxxx`. Capitalization matters")
return
# Now just save the battletag
entry = {'member_id': ctx.message.author.id, 'battletag': bt}
update = {'battletag': bt}
# Try adding this first, if that fails, update the saved entry
if not await config.add_content('overwatch', entry, r_filter):
await config.update_content('overwatch', update, r_filter)
await self.bot.say("I have just saved your battletag {}".format(ctx.message.author.mention))
if not await utils.add_content('overwatch', entry, r_filter):
await utils.update_content('overwatch', update, r_filter)
await ctx.send("I have just saved your battletag {}".format(ctx.message.author.mention))
@ow.command(pass_context=True, name="delete", aliases=['remove'])
@checks.custom_perms(send_messages=True)
@utils.custom_perms(send_messages=True)
async def delete(self, ctx):
"""Removes your battletag from the records
EXAMPLE: !ow delete
RESULT: Your battletag is no longer saved"""
r_filter = {'member_id': ctx.message.author.id}
if await config.remove_content('overwatch', r_filter):
await self.bot.say("I no longer have your battletag saved {}".format(ctx.message.author.mention))
if await utils.remove_content('overwatch', r_filter):
await ctx.send("I no longer have your battletag saved {}".format(ctx.message.author.mention))
else:
await self.bot.say("I don't even have your battletag saved {}".format(ctx.message.author.mention))
await ctx.send("I don't even have your battletag saved {}".format(ctx.message.author.mention))
def setup(bot):

View file

@ -4,11 +4,11 @@ from . import utils
import re
import glob
import asyncio
import aiohttp
import discord
import inspect
import aiohttp
import pendulum
import asyncio
class Owner:
@ -19,7 +19,7 @@ class Owner:
@commands.command()
@commands.check(utils.is_owner)
async def motd_push(self, *, message):
async def motd_push(self, ctx, *, message):
"""Used to push a new message to the message of the day"""
date = pendulum.utcnow().to_date_string()
r_filter = {'date': date}
@ -28,15 +28,14 @@ class Owner:
# I should be managing this myself, more than one should not be sent in a day
if await utils.add_content('motd', entry, r_filter):
await utils.update_content('motd', entry, r_filter)
await self.bot.say("New motd update for {}!".format(date))
await ctx.send("New motd update for {}!".format(date))
@commands.command(pass_context=True)
@commands.command()
@commands.check(utils.is_owner)
async def debug(self, ctx, *, code : str):
async def debug(self, ctx, *, code: str):
"""Evaluates code."""
code = code.strip('` ')
python = '```py\n{}\n```'
result = None
env = {
'bot': self.bot,
@ -54,41 +53,41 @@ class Owner:
if inspect.isawaitable(result):
result = await result
except Exception as e:
await self.bot.say(python.format(type(e).__name__ + ': ' + str(e)))
await ctx.send(python.format(type(e).__name__ + ': ' + str(e)))
return
try:
await self.bot.say(python.format(result))
await ctx.send(python.format(result))
except discord.HTTPException:
await self.bot.say("Result is too long for me to send")
await ctx.send("Result is too long for me to send")
except:
pass
@commands.command(pass_context=True)
@commands.command()
@commands.check(utils.is_owner)
async def shutdown(self, ctx):
"""Shuts the bot down"""
fmt = 'Shutting down, I will miss you {0.author.name}'
await self.bot.say(fmt.format(ctx.message))
await ctx.send(fmt.format(ctx.message))
await self.bot.logout()
await self.bot.close()
@commands.command()
@commands.check(utils.is_owner)
async def name(self, newNick: str):
async def name(self, ctx, newNick: str):
"""Changes the bot's name"""
await self.bot.edit_profile(username=newNick)
await self.bot.say('Changed username to ' + newNick)
await ctx.send('Changed username to ' + newNick)
@commands.command()
@commands.check(utils.is_owner)
async def status(self, *, status: str):
async def status(self, ctx, *, status: str):
"""Changes the bot's 'playing' status"""
await self.bot.change_status(discord.Game(name=status, type=0))
await self.bot.say("Just changed my status to '{0}'!".format(status))
await ctx.send("Just changed my status to '{0}'!".format(status))
@commands.command()
@commands.check(utils.is_owner)
async def load(self, *, module: str):
async def load(self, ctx, *, module: str):
"""Loads a module"""
# Do this because I'm too lazy to type cogs.module
@ -99,14 +98,14 @@ class Owner:
# This try catch will catch errors such as syntax errors in the module we are loading
try:
self.bot.load_extension(module)
await self.bot.say("I have just loaded the {} module".format(module))
await ctx.send("I have just loaded the {} module".format(module))
except Exception as error:
fmt = 'An error occurred while processing this request: ```py\n{}: {}\n```'
await self.bot.say(fmt.format(type(error).__name__, error))
await ctx.send(fmt.format(type(error).__name__, error))
@commands.command()
@commands.check(utils.is_owner)
async def unload(self, *, module: str):
async def unload(self, ctx, *, module: str):
"""Unloads a module"""
# Do this because I'm too lazy to type cogs.module
@ -115,11 +114,11 @@ class Owner:
module = "cogs.{}".format(module)
self.bot.unload_extension(module)
await self.bot.say("I have just unloaded the {} module".format(module))
await ctx.send("I have just unloaded the {} module".format(module))
@commands.command()
@commands.check(utils.is_owner)
async def reload(self, *, module: str):
async def reload(self, ctx, *, module: str):
"""Reloads a module"""
# Do this because I'm too lazy to type cogs.module
@ -131,10 +130,10 @@ class Owner:
# This try block will catch errors such as syntax errors in the module we are loading
try:
self.bot.load_extension(module)
await self.bot.say("I have just reloaded the {} module".format(module))
await ctx.send("I have just reloaded the {} module".format(module))
except Exception as error:
fmt = 'An error occurred while processing this request: ```py\n{}: {}\n```'
await self.bot.say(fmt.format(type(error).__name__, error))
await ctx.send(fmt.format(type(error).__name__, error))
def setup(bot):

View file

@ -11,7 +11,7 @@ from discord.ext import commands
from . import utils
log = logging.getLogger()
base_url = 'https://ptvappapi.picarto.tv'
BASE_URL = 'https://ptvappapi.picarto.tv'
# This is a public key for use, I don't care if this is seen
key = '03e26294-b793-11e5-9a41-005056984bd4'
@ -23,10 +23,9 @@ async def online_users():
# It is more efficent on their end to make a query for all online users, and base checks off that
# In place of requesting for /channel and checking if that is online currently, for each channel
# This method is in place to just return all online_users
url = '{}/online/all?key={}'.format(base_url, key)
with aiohttp.ClientSession(headers={"User-Agent": utils.user_agent}) as s:
async with s.get(url) as response:
return await response.json()
url = BASE_URL + '/online/all'
payload = {'key': key}
return await utils.request(url, payload=payload)
except:
return {}
@ -64,24 +63,24 @@ class Picarto:
user = re.search("(?<=picarto.tv/)(.*)", url).group(1)
# Check if they are online right now
if check_online(online_users_list, user):
for server_id in result['servers']:
for guild_id in result['servers']:
# Get the channel to send the message to, based on the saved alert's channel
server = self.bot.get_server(server_id)
if server is None:
guild = self.bot.get_guild(guild_id)
if guild is None:
continue
server_alerts = await utils.get_content('server_alerts', {'server_id': server_id})
guild_alerts = await utils.get_content('server_alerts', {'server_id': guild_id})
try:
channel_id = server_alerts[0]['channel_id']
channel_id = guild_alerts[0]['channel_id']
except (IndexError, TypeError):
channel_id = server_id
channel_id = guild_id
channel = self.bot.get_channel(channel_id)
# Get the member that has just gone live
member = discord.utils.get(server.members, id=m_id)
member = guild.get_member(m_id)
if member is None:
continue
fmt = "{} has just gone live! View their stream at {}".format(member.display_name, url)
await self.bot.send_message(channel, fmt)
await channel.send(fmt)
await utils.update_content('picarto', {'live': 1}, {'member_id': m_id})
for m_id, result in old_online_users.items():
# Get their url and their user based on that url
@ -89,22 +88,25 @@ class Picarto:
user = re.search("(?<=picarto.tv/)(.*)", url).group(1)
# Check if they are online right now
if not check_online(online_users_list, user):
for server_id in result['servers']:
for guild_id in result['servers']:
# Get the channel to send the message to, based on the saved alert's channel
server = self.bot.get_server(server_id)
if server is None:
guild = self.bot.get_guild(guild_id)
if guild is None:
continue
server_alerts = await utils.get_content('server_alerts', {'server_id': server_id})
guild_alerts = await utils.get_content('server_alerts', {'server_id': guild_id})
try:
channel_id = server_alerts[0]['channel_id']
channel_id = guild_alerts[0]['channel_id']
except (IndexError, TypeError):
channel_id = server_id
channel_id = guild_id
channel = self.bot.get_channel(channel_id)
# Get the member that has just gone live
member = discord.utils.get(server.members, id=m_id)
member = guild.get_member(m_id)
if member is None:
continue
fmt = "{} has just gone offline! Catch them next time they stream at {}".format(
member.display_name, url)
await self.bot.send_message(channel, fmt)
await channel.send(fmt)
await utils.update_content('picarto', {'live': 0}, {'member_id': m_id})
await asyncio.sleep(30)
except Exception as e:
@ -112,7 +114,7 @@ class Picarto:
fmt = "{1}\n{0.__class__.__name__}: {0}".format(tb, e)
log.error(fmt)
@commands.group(pass_context=True, invoke_without_command=True, no_pm=True)
@commands.group(invoke_without_command=True, no_pm=True)
@utils.custom_perms(send_messages=True)
async def picarto(self, ctx, member: discord.Member = None):
"""This command can be used to view Picarto stats about a certain member
@ -124,15 +126,17 @@ class Picarto:
r_filter = {'member_id': member.id}
picarto_entry = await utils.get_content('picarto', r_filter)
if picarto_entry is None:
await self.bot.say("That user does not have a picarto url setup!")
await ctx.send("That user does not have a picarto url setup!")
return
member_url = picarto_entry[0]['picarto_url']
# Use regex to get the actual username so that we can make a request to the API
stream = re.search("(?<=picarto.tv/)(.*)", member_url).group(1)
url = '{}/channel/{}?key={}'.format(base_url, stream, key)
async with self.session.get(url, headers=self.headers) as response:
data = await response.json()
url = BASE_URL + '/channel/{}'.format(stream)
payload = {'key': key}
data = await utils.request(url, payload=payload)
# Not everyone has all these settings, so use this as a way to print information if it does, otherwise ignore it
things_to_print = ['channel', 'commissions_enabled', 'is_nsfw', 'program', 'tablet', 'followers',
@ -148,15 +152,15 @@ class Picarto:
fmt2 = "\n".join(
"\t{}: {}".format(i.title().replace("_", " "), result) for i, result in social_links.items())
fmt = "{}\nSocial Links:\n{}".format(fmt, fmt2)
await self.bot.say("Picarto stats for {}: ```\n{}```".format(member.display_name, fmt))
await ctx.send("Picarto stats for {}: ```\n{}```".format(member.display_name, fmt))
@picarto.command(name='add', pass_context=True, no_pm=True)
@picarto.command(name='add', no_pm=True)
@utils.custom_perms(send_messages=True)
async def add_picarto_url(self, ctx, url: str):
"""Saves your user's picarto URL
EXAMPLE: !picarto add MyUsername
RESULT: Your picarto stream is saved, and notifications should go to this server"""
RESULT: Your picarto stream is saved, and notifications should go to this guild"""
# This uses a lookbehind to check if picarto.tv exists in the url given
# If it does, it matches picarto.tv/user and sets the url as that
# Then (in the else) add https://www. to that
@ -171,65 +175,64 @@ class Picarto:
url = "https://www.picarto.tv/{}".format(url)
else:
url = "https://www.{}".format(url)
api_url = '{}/channel/{}?key={}'.format(base_url, re.search("https://www.picarto.tv/(.*)", url).group(1), key)
# Check if we can find a user with the provided information, if we can't just return
async with self.session.get(api_url, headers=self.headers) as response:
if not response.status == 200:
await self.bot.say("That Picarto user does not exist! "
"What would be the point of adding a nonexistant Picarto user? Silly")
return
channel = re.search("https://www.picarto.tv/(.*)", url).group(1)
url = BASE_URL + '/channel/{}'.format(channel)
payload = {'key': key}
data = await utils.request(url, payload=payload)
if not data:
await ctx.send("That Picarto user does not exist! What would be the point of adding a nonexistant Picarto "
"user? Silly")
return
r_filter = {'member_id': ctx.message.author.id}
entry = {'picarto_url': url,
'servers': [ctx.message.server.id],
'servers': [ctx.message.guild.id],
'notifications_on': 1,
'live': 0,
'member_id': ctx.message.author.id}
if await utils.add_content('picarto', entry, r_filter):
await self.bot.say(
"I have just saved your Picarto URL {}, this server will now be notified when you go live".format(
await ctx.send(
"I have just saved your Picarto URL {}, this guild will now be notified when you go live".format(
ctx.message.author.mention))
else:
await utils.update_content('picarto', {'picarto_url': url}, r_filter)
await self.bot.say("I have just updated your Picarto URL")
await ctx.send("I have just updated your Picarto URL")
@picarto.command(name='remove', aliases=['delete'], pass_context=True, no_pm=True)
@picarto.command(name='remove', aliases=['delete'], no_pm=True)
@utils.custom_perms(send_messages=True)
async def remove_picarto_url(self, ctx):
"""Removes your picarto URL"""
r_filter = {'member_id': ctx.message.author.id}
if await utils.remove_content('picarto', r_filter):
await self.bot.say("I am no longer saving your picarto URL {}".format(ctx.message.author.mention))
await ctx.send("I am no longer saving your picarto URL {}".format(ctx.message.author.mention))
else:
await self.bot.say(
await ctx.send(
"I do not have your picarto URL added {}. You can save your picarto url with {}picarto add".format(
ctx.message.author.mention, ctx.prefix))
@picarto.group(pass_context=True, no_pm=True, invoke_without_command=True)
@picarto.group(no_pm=True, invoke_without_command=True)
@utils.custom_perms(send_messages=True)
async def notify(self, ctx):
"""This can be used to turn picarto notifications on or off
Call this command by itself, to add this server to the list of servers to be notified
Call this command by itself, to add this guild to the list of guilds to be notified
EXAMPLE: !picarto notify
RESULT: This server will now be notified of you going live"""
RESULT: This guild will now be notified of you going live"""
r_filter = {'member_id': ctx.message.author.id}
result = await utils.get_content('picarto', r_filter)
# Check if this user is saved at all
if result is None:
await self.bot.say(
await ctx.send(
"I do not have your Picarto URL added {}. You can save your Picarto url with !picarto add".format(
ctx.message.author.mention))
# Then check if this server is already added as one to notify in
elif ctx.message.server.id in result[0]['servers']:
await self.bot.say("I am already set to notify in this server...")
# Then check if this guild is already added as one to notify in
elif ctx.message.guild.id in result[0]['servers']:
await ctx.send("I am already set to notify in this guild...")
else:
await utils.update_content('picarto', {'servers': r.row['servers'].append(ctx.message.server.id)},
r_filter)
await utils.update_content('picarto', {'servers': r.row['servers'].append(ctx.message.guild.id)},
r_filter)
@notify.command(name='on', aliases=['start,yes'], pass_context=True, no_pm=True)
@notify.command(name='on', aliases=['start,yes'], no_pm=True)
@utils.custom_perms(send_messages=True)
async def notify_on(self, ctx):
"""Turns picarto notifications on
@ -238,7 +241,7 @@ class Picarto:
RESULT: Notifications are sent when you go live"""
r_filter = {'member_id': ctx.message.author.id}
await utils.update_content('picarto', {'notifications_on': 1}, r_filter)
await self.bot.say("I will notify if you go live {}, you'll get a bajillion followers I promise c:".format(
await ctx.send("I will notify if you go live {}, you'll get a bajillion followers I promise c:".format(
ctx.message.author.mention))
@notify.command(name='off', aliases=['stop,no'], pass_context=True, no_pm=True)
@ -250,7 +253,7 @@ class Picarto:
RESULT: No more notifications sent when you go live"""
r_filter = {'member_id': ctx.message.author.id}
await utils.update_content('picarto', {'notifications_on': 0}, r_filter)
await self.bot.say(
await ctx.send(
"I will not notify if you go live anymore {}, "
"are you going to stream some lewd stuff you don't want people to see?~".format(
ctx.message.author.mention))

View file

@ -1,6 +1,7 @@
from discord.ext import commands
from .utils import config
from .utils import checks
import discord
from . import utils
import random
import pendulum
@ -27,13 +28,13 @@ class Raffle:
async def check_raffles(self):
# This is used to periodically check the current raffles, and see if they have ended yet
# If the raffle has ended, we'll pick a winner from the entrants
raffles = await config.get_content('raffles')
raffles = await utils.get_content('raffles')
if raffles is None:
return
for raffle in raffles:
server = self.bot.get_server(raffle['server_id'])
server = self.bot.get_guild(raffle['server_id'])
# Check to see if this cog can find the server in question
if server is None:
@ -74,23 +75,23 @@ class Raffle:
# No matter which one of these matches were met, the raffle has ended and we want to remove it
# We don't have to wait for it however, so create a task for it
r_filter = {'id': raffle_id}
self.bot.loop.create_task(config.remove_content('raffles', r_filter))
self.bot.loop.create_task(utils.remove_content('raffles', r_filter))
try:
await self.bot.send_message(server, fmt)
await server.send(fmt)
except discord.Forbidden:
pass
@commands.command(pass_context=True, no_pm=True)
@checks.custom_perms(send_messages=True)
@commands.command(no_pm=True)
@utils.custom_perms(send_messages=True)
async def raffles(self, ctx):
"""Used to print the current running raffles on the server
EXAMPLE: !raffles
RESULT: A list of the raffles setup on this server"""
r_filter = {'server_id': ctx.message.server.id}
raffles = await config.get_content('raffles', r_filter)
r_filter = {'server_id': ctx.message.guild.id}
raffles = await utils.get_content('raffles', r_filter)
if raffles is None:
await self.bot.say("There are currently no raffles setup on this server!")
await ctx.send("There are currently no raffles setup on this server!")
return
fmt = "\n\n".join("**Raffle:** {}\n**Title:** {}\n**Total Entrants:** {}\n**Ends:** {} UTC".format(
@ -98,10 +99,10 @@ class Raffle:
raffle['title'],
len(raffle['entrants']),
raffle['expires']) for num, raffle in enumerate(raffles))
await self.bot.say(fmt)
await ctx.send(fmt)
@commands.group(pass_context=True, no_pm=True, invoke_without_command=True)
@checks.custom_perms(send_messages=True)
@commands.group(no_pm=True, invoke_without_command=True)
@utils.custom_perms(send_messages=True)
async def raffle(self, ctx, raffle_id: int = 0):
"""Used to enter a raffle running on this server
If there is more than one raffle running, provide an ID of the raffle you want to enter
@ -110,12 +111,12 @@ class Raffle:
RESULT: You've entered the first raffle!"""
# Lets let people use 1 - (length of raffles) and handle 0 base ourselves
raffle_id -= 1
r_filter = {'server_id': ctx.message.server.id}
r_filter = {'server_id': ctx.message.guild.id}
author = ctx.message.author
raffles = await config.get_content('raffles', r_filter)
raffles = await utils.get_content('raffles', r_filter)
if raffles is None:
await self.bot.say("There are currently no raffles setup on this server!")
await ctx.send("There are currently no raffles setup on this server!")
return
raffle_count = len(raffles)
@ -125,38 +126,38 @@ class Raffle:
entrants = raffles[0]['entrants']
# Lets make sure that the user hasn't already entered the raffle
if author.id in entrants:
await self.bot.say("You have already entered this raffle!")
await ctx.send("You have already entered this raffle!")
return
entrants.append(author.id)
# Since we have no good thing to filter things off of, lets use the internal rethinkdb id
r_filter = {'id': raffles[0]['id']}
update = {'entrants': entrants}
await config.update_content('raffles', update, r_filter)
await self.bot.say("{} you have just entered the raffle!".format(author.mention))
await utils.update_content('raffles', update, r_filter)
await ctx.send("{} you have just entered the raffle!".format(author.mention))
# Otherwise, make sure the author gave a valid raffle_id
elif raffle_id in range(raffle_count - 1):
entrants = raffles[raffle_id]['entrants']
# Lets make sure that the user hasn't already entered the raffle
if author.id in entrants:
await self.bot.say("You have already entered this raffle!")
await ctx.send("You have already entered this raffle!")
return
entrants.append(author.id)
# Since we have no good thing to filter things off of, lets use the internal rethinkdb id
r_filter = {'id': raffles[raffle_id]['id']}
update = {'entrants': entrants}
await config.update_content('raffles', update, r_filter)
await self.bot.say("{} you have just entered the raffle!".format(author.mention))
await utils.update_content('raffles', update, r_filter)
await ctx.send("{} you have just entered the raffle!".format(author.mention))
else:
fmt = "Please provide a valid raffle ID, as there are more than one setup on the server! " \
"There are currently `{}` raffles running, use {}raffles to view the current running raffles".format(
raffle_count, ctx.prefix)
await self.bot.say(fmt)
await ctx.send(fmt)
@raffle.command(pass_context=True, no_pm=True, name='create', aliases=['start', 'begin', 'add'])
@checks.custom_perms(kick_members=True)
@utils.custom_perms(kick_members=True)
async def raffle_create(self, ctx):
"""This is used in order to create a new server raffle
@ -164,16 +165,18 @@ class Raffle:
RESULT: A follow-along for setting up a new raffle"""
author = ctx.message.author
server = ctx.message.server
server = ctx.message.guild
channel = ctx.message.channel
now = pendulum.utcnow()
await self.bot.say(
await ctx.send(
"Ready to start a new raffle! Please respond with the title you would like to use for this raffle!")
msg = await self.bot.wait_for_message(author=author, channel=channel, timeout=120)
check = lambda m: m.author == author and m.channel == channel
msg = await self.bot.wait_for('message', check=check, timeout=120)
if msg is None:
await self.bot.say("You took too long! >:c")
await ctx.send("You took too long! >:c")
return
title = msg.content
@ -181,13 +184,18 @@ class Raffle:
fmt = "Alright, your new raffle will be titled:\n\n{}\n\nHow long would you like this raffle to run for? " \
"The format should be [number] [length] for example, `2 days` or `1 hour` or `30 minutes` etc. " \
"The minimum for this is 10 minutes, and the maximum is 3 months"
await self.bot.say(fmt.format(title))
await ctx.send(fmt.format(title))
# Our check to ensure that a proper length of time was passed
check = lambda m: re.search("\d+ (minutes?|hours?|days?|weeks?|months?)", m.content.lower()) is not None
msg = await self.bot.wait_for_message(author=author, channel=channel, timeout=120, check=check)
def check(m):
if m.author == author and m.channel == channel:
return re.search("\d+ (minutes?|hours?|days?|weeks?|months?)", m.content.lower()) is not None
else:
return False
msg = await self.bot.wait_for('message', timeout=120, check=check)
if msg is None:
await self.bot.say("You took too long! >:c")
await ctx.send("You took too long! >:c")
return
# Lets get the length provided, based on the number and type passed
@ -197,28 +205,28 @@ class Raffle:
# Now lets ensure this meets our min/max
if "minute" in term and (num < 10 or num > 129600):
await self.bot.say(
await ctx.send(
"Length provided out of range! The minimum for this is 10 minutes, and the maximum is 3 months")
return
elif "hour" in term and num > 2160:
await self.bot.say(
await ctx.send(
"Length provided out of range! The minimum for this is 10 minutes, and the maximum is 3 months")
return
elif "day" in term and num > 90:
await self.bot.say(
await ctx.send(
"Length provided out of range! The minimum for this is 10 minutes, and the maximum is 3 months")
return
elif "week" in term and num > 12:
await self.bot.say(
await ctx.send(
"Length provided out of range! The minimum for this is 10 minutes, and the maximum is 3 months")
return
elif "month" in term and num > 3:
await self.bot.say(
await ctx.send(
"Length provided out of range! The minimum for this is 10 minutes, and the maximum is 3 months")
return
# Pendulum only accepts the plural version of terms, lets make sure this is added
term = term if term.endswith('s') else '{}s'.format(term)
term = term if term.endswith('s') else term + 's'
# If we're in the range, lets just pack this in a dictionary we can pass to set the time we want, then set that
payload = {term: num}
expires = now.add(**payload)
@ -231,8 +239,8 @@ class Raffle:
'server_id': server.id}
# We don't want to pass a filter to this, because we can have multiple raffles per server
await config.add_content('raffles', entry)
await self.bot.say("I have just saved your new raffle!")
await utils.add_content('raffles', entry)
await ctx.send("I have just saved your new raffle!")
def setup(bot):

View file

@ -1,6 +1,7 @@
from discord.ext import commands
import discord
from .utils import checks
from . import utils
import re
import asyncio
@ -12,8 +13,8 @@ class Roles:
def __init__(self, bot):
self.bot = bot
@commands.group(aliases=['roles'], invoke_without_command=True, no_pm=True, pass_context=True)
@checks.custom_perms(send_messages=True)
@commands.group(aliases=['roles'], invoke_without_command=True, no_pm=True)
@utils.custom_perms(send_messages=True)
async def role(self, ctx):
"""This command can be used to modify the roles on the server.
Pass no subcommands and this will print the roles currently available on this server
@ -22,10 +23,10 @@ class Roles:
RESULT: A list of all your roles"""
# Simply get a list of all roles in this server and send them
server_roles = [role.name for role in ctx.message.server.roles if not role.is_everyone]
await self.bot.say("Your server's roles are: ```\n{}```".format("\n".join(server_roles)))
await ctx.send("Your server's roles are: ```\n{}```".format("\n".join(server_roles)))
@role.command(name='remove', pass_context=True, no_pm=True)
@checks.custom_perms(manage_roles=True)
@role.command(name='remove', no_pm=True)
@utils.custom_perms(manage_roles=True)
async def remove_role(self, ctx):
"""Use this to remove roles from a number of members
@ -33,33 +34,35 @@ class Roles:
RESULT: A follow-along to remove the role(s) you want to, from these 3 members"""
# No use in running through everything if the bot cannot manage roles
if not ctx.message.server.me.permissions_in(ctx.message.channel).manage_roles:
await self.bot.say("I can't manage roles in this server, do you not trust me? :c")
await ctx.send("I can't manage roles in this server, do you not trust me? :c")
return
check = lambda m: m.author == ctx.message.author and m.channel == ctx.message.channel
server_roles = [role for role in ctx.message.server.roles if not role.is_everyone]
# First get the list of all mentioned users
members = ctx.message.mentions
# If no users are mentioned, ask the author for a list of the members they want to remove the role from
if len(members) == 0:
await self.bot.say("Please provide the list of members you want to remove a role from")
msg = await self.bot.wait_for_message(author=ctx.message.author, channel=ctx.message.channel)
await ctx.send("Please provide the list of members you want to remove a role from")
msg = await self.bot.wait_for('message', check=check, timeout=60)
if msg is None:
await self.bot.say("You took too long. I'm impatient, don't make me wait")
await ctx.send("You took too long. I'm impatient, don't make me wait")
return
if len(msg.mentions) == 0:
await self.bot.say("I cannot remove a role from someone if you don't provide someone...")
await ctx.send("I cannot remove a role from someone if you don't provide someone...")
return
# Override members if everything has gone alright, and then continue
members = msg.mentions
# This allows the user to remove multiple roles from the list of users, if they want.
await self.bot.say("Alright, please provide the roles you would like to remove from this member. "
"Make sure the roles, if more than one is provided, are separate by commas. "
"Here is a list of this server's roles:"
"```\n{}```".format("\n".join([r.name for r in server_roles])))
msg = await self.bot.wait_for_message(author=ctx.message.author, channel=ctx.message.channel)
await ctx.send("Alright, please provide the roles you would like to remove from this member. "
"Make sure the roles, if more than one is provided, are separate by commas. "
"Here is a list of this server's roles:"
"```\n{}```".format("\n".join([r.name for r in server_roles])))
msg = await self.bot.wait_for('message', check=check, timeout=60)
if msg is None:
await self.bot.say("You took too long. I'm impatient, don't make me wait")
await ctx.send("You took too long. I'm impatient, don't make me wait")
return
# Split the content based on commas, using regex so we can split if a space was not provided or if it was
@ -73,17 +76,17 @@ class Roles:
# If no valid roles were given, let them know that and return
if len(roles) == 0:
await self.bot.say("Please provide a valid role next time!")
await ctx.send("Please provide a valid role next time!")
return
# Otherwise, remove the roles from each member given
for member in members:
await self.bot.remove_roles(member, *roles)
await self.bot.say("I have just removed the following roles:```\n{}``` from the following members:"
"```\n{}```".format("\n".join(role_names), "\n".join([m.display_name for m in members])))
await ctx.send("I have just removed the following roles:```\n{}``` from the following members:"
"```\n{}```".format("\n".join(role_names), "\n".join([m.display_name for m in members])))
@role.command(name='add', pass_context=True, no_pm=True)
@checks.custom_perms(manage_roles=True)
@role.command(name='add', no_pm=True)
@utils.custom_perms(manage_roles=True)
async def add_role(self, ctx):
"""Use this to add a role to multiple members.
Provide the list of members, and I'll ask for the role
@ -93,31 +96,32 @@ class Roles:
RESULT: A follow along to add the roles you want to these 3"""
# No use in running through everything if the bot cannot manage roles
if not ctx.message.server.me.permissions_in(ctx.message.channel).manage_roles:
await self.bot.say("I can't manage roles in this server, do you not trust me? :c")
await ctx.send("I can't manage roles in this server, do you not trust me? :c")
return
check = lambda m: m.author == ctx.message.author and m.channel == ctx.message.channel
# This is exactly the same as removing roles, except we call add_roles instead.
server_roles = [role for role in ctx.message.server.roles if not role.is_everyone]
members = ctx.message.mentions
if len(members) == 0:
await self.bot.say("Please provide the list of members you want to add a role to")
msg = await self.bot.wait_for_message(author=ctx.message.author, channel=ctx.message.channel)
await ctx.send("Please provide the list of members you want to add a role to")
msg = await self.bot.wait_for('message', check=check, timeout=60)
if msg is None:
await self.bot.say("You took too long. I'm impatient, don't make me wait")
await ctx.send("You took too long. I'm impatient, don't make me wait")
return
if len(msg.mentions) == 0:
await self.bot.say("I cannot add a role to someone if you don't provide someone...")
await ctx.send("I cannot add a role to someone if you don't provide someone...")
return
members = msg.mentions
await self.bot.say("Alright, please provide the roles you would like to add to this member. "
"Make sure the roles, if more than one is provided, are separate by commas. "
"Here is a list of this server's roles:"
"```\n{}```".format("\n".join([r.name for r in server_roles])))
await ctx.send("Alright, please provide the roles you would like to add to this member. "
"Make sure the roles, if more than one is provided, are separate by commas. "
"Here is a list of this server's roles:"
"```\n{}```".format("\n".join([r.name for r in server_roles])))
msg = await self.bot.wait_for_message(author=ctx.message.author, channel=ctx.message.channel)
msg = await self.bot.wait_for('message', check=check, timeout=60)
if msg is None:
await self.bot.say("You took too long. I'm impatient, don't make me wait")
await ctx.send("You took too long. I'm impatient, don't make me wait")
return
role_names = re.split(', ?', msg.content)
roles = []
@ -127,16 +131,16 @@ class Roles:
roles.append(_role)
if len(roles) == 0:
await self.bot.say("Please provide a valid role next time!")
await ctx.send("Please provide a valid role next time!")
return
for member in members:
await self.bot.add_roles(member, *roles)
await self.bot.say("I have just added the following roles:```\n{}``` to the following members:"
"```\n{}```".format("\n".join(role_names), "\n".join([m.display_name for m in members])))
await member.add_roles(*roles)
await ctx.send("I have just added the following roles:```\n{}``` to the following members:"
"```\n{}```".format("\n".join(role_names), "\n".join([m.display_name for m in members])))
@role.command(name='delete', pass_context=True, no_pm=True)
@checks.custom_perms(manage_roles=True)
@role.command(name='delete', no_pm=True)
@utils.custom_perms(manage_roles=True)
async def delete_role(self, ctx, *, role: discord.Role = None):
"""This command can be used to delete one of the roles from the server
@ -144,34 +148,39 @@ class Roles:
RESULT: No more role called StupidRole"""
# No use in running through everything if the bot cannot manage roles
if not ctx.message.server.me.permissions_in(ctx.message.channel).manage_roles:
await self.bot.say("I can't delete roles in this server, do you not trust me? :c")
await ctx.send("I can't delete roles in this server, do you not trust me? :c")
return
# If no role was given, get the current roles on the server and ask which ones they'd like to remove
if role is None:
server_roles = [role for role in ctx.message.server.roles if not role.is_everyone]
await self.bot.say(
await ctx.send(
"Which role would you like to remove from the server? Here is a list of this server's roles:"
"```\n{}```".format("\n".join([r.name for r in server_roles])))
# For this method we're only going to delete one role at a time
# This check attempts to find a role based on the content provided, if it can't find one it returns None
# We can use that fact to simply use just that as our check
check = lambda m: discord.utils.get(server_roles, name=m.content)
msg = await self.bot.wait_for_message(author=ctx.message.author, channel=ctx.message.channel, check=check)
def check(m):
if m.author == ctx.message.author and m.channel == ctx.message.channel:
return discord.utils.get(server_roles, name=m.content) is not None
else:
return False
msg = await self.bot.wait_for('message', timeout=60, check=check)
if msg is None:
await self.bot.say("You took too long. I'm impatient, don't make me wait")
await ctx.send("You took too long. I'm impatient, don't make me wait")
return
# If we have gotten here, based on our previous check, we know that the content provided is a valid role.
# Due to that, no need for any error checking here
role = discord.utils.get(server_roles, name=msg.content)
await self.bot.delete_role(ctx.message.server, role)
await self.bot.say("I have just removed the role {} from this server".format(role.name))
await role.delete()
await ctx.send("I have just removed the role {} from this server".format(role.name))
@role.command(name='create', pass_context=True, no_pm=True)
@checks.custom_perms(manage_roles=True)
@role.command(name='create', no_pm=True)
@utils.custom_perms(manage_roles=True)
async def create_role(self, ctx):
"""This command can be used to create a new role for this server
A prompt will follow asking what settings you would like for this new role
@ -181,7 +190,7 @@ class Roles:
RESULT: A follow along in order to create a new role"""
# No use in running through everything if the bot cannot create the role
if not ctx.message.server.me.permissions_in(ctx.message.channel).manage_roles:
await self.bot.say("I can't create roles in this server, do you not trust me? :c")
await ctx.send("I can't create roles in this server, do you not trust me? :c")
return
# Save a couple variables that will be used repeatedly
@ -190,48 +199,64 @@ class Roles:
channel = ctx.message.channel
# A couple checks that will be used in the wait_for_message's
num_separated_check = lambda m: re.search("(\d(, ?| )?|[nN]one)", m.content) is not None
yes_no_check = lambda m: re.search("(yes|no)", m.content.lower()) is not None
members_check = lambda m: len(m.mentions) > 0
def num_seperated_check(m):
if m.author == author and m.channel == channel:
return re.search("(\d(, ?| )?|[nN]one)", m.content) is not None
else:
return False
def yes_no_check(m):
if m.author == author and m.channel == channel:
return re.search("(yes|no)", m.content.lower()) is not None
else:
return False
def members_check(m):
if m.author == author and m.channel == channel:
return len(m.mentions) > 0
else:
return False
author_check = lambda m: m.author == author and m.channel == channel
# Start the checks for the role, get the name of the role first
await self.bot.say(
await ctx.send(
"Alright! I'm ready to create a new role, please respond with the name of the role you want to create")
msg = await self.bot.wait_for_message(timeout=60.0, author=author, channel=channel)
msg = await self.bot.wait_for('message', timeout=60.0, check=author_check)
if msg is None:
await self.bot.say("You took too long. I'm impatient, don't make me wait")
await ctx.send("You took too long. I'm impatient, don't make me wait")
return
name = msg.content
# Print a list of all the permissions available, then ask for which ones need to be active on this new role
all_perms = [p for p in dir(discord.Permissions) if isinstance(getattr(discord.Permissions, p), property)]
fmt = "\n".join("{}) {}".format(i, perm) for i, perm in enumerate(all_perms))
await self.bot.say("Sounds fancy! Here is a list of all the permissions available. Please respond with just "
"the numbers, seperated by commas, of the permissions you want this role to have.\n"
"```\n{}```".format(fmt))
await ctx.send("Sounds fancy! Here is a list of all the permissions available. Please respond with just "
"the numbers, seperated by commas, of the permissions you want this role to have.\n"
"```\n{}```".format(fmt))
# For this we're going to give a couple extra minutes before we timeout
# as it might take a bit to figure out which permissions they want
msg = await self.bot.wait_for_message(timeout=180.0, author=author, channel=channel, check=num_separated_check)
msg = await self.bot.wait_for('message', timeout=180.0, check=num_seperated_check)
if msg is None:
await self.bot.say("You took too long. I'm impatient, don't make me wait")
await ctx.send("You took too long. I'm impatient, don't make me wait")
return
# Check if any integer's were provided that are within the length of the list of permissions
num_permissions = [int(i) for i in re.split(' ?,?', msg.content) if i.isdigit() and int(i) < len(all_perms)]
# Check if this role should be in a separate section on the sidebard, i.e. hoisted
await self.bot.say("Do you want this role to be in a separate section on the sidebar? (yes or no)")
msg = await self.bot.wait_for_message(timeout=60.0, author=author, channel=channel, check=yes_no_check)
await ctx.send("Do you want this role to be in a separate section on the sidebar? (yes or no)")
msg = await self.bot.wait_for('message', timeout=60.0, check=yes_no_check)
if msg is None:
await self.bot.say("You took too long. I'm impatient, don't make me wait")
await ctx.send("You took too long. I'm impatient, don't make me wait")
return
hoist = True if msg.content.lower() == "yes" else False
# Check if this role should be able to be mentioned
await self.bot.say("Do you want this role to be mentionable? (yes or no)")
msg = await self.bot.wait_for_message(timeout=60.0, author=author, channel=channel, check=yes_no_check)
await ctx.send("Do you want this role to be mentionable? (yes or no)")
msg = await self.bot.wait_for('message', timeout=60.0, check=yes_no_check)
if msg is None:
await self.bot.say("You took too long. I'm impatient, don't make me wait")
await ctx.send("You took too long. I'm impatient, don't make me wait")
return
mentionable = True if msg.content.lower() == "yes" else False
@ -248,21 +273,21 @@ class Roles:
'mentionable': mentionable
}
# Create the role, and wait a second, sometimes it goes too quickly and we get a role with 'new role' to print
role = await self.bot.create_role(server, **payload)
role = await server.create_role(**payload)
await asyncio.sleep(1)
await self.bot.say("We did it! You just created the new role {}\nIf you want to add this role"
" to some people, mention them now".format(role.name))
msg = await self.bot.wait_for_message(timeout=60.0, author=author, channel=channel, check=members_check)
await ctx.send("We did it! You just created the new role {}\nIf you want to add this role"
" to some people, mention them now".format(role.name))
msg = await self.bot.wait_for('message', timeout=60.0, check=members_check)
# There's no need to mention the users, so don't send a failure message if they didn't, just return
if msg is None:
return
# Otherwise members were mentioned, add the new role to them now
for member in msg.mentions:
await self.bot.add_roles(member, role)
await member.add_roles(role)
fmt = "\n".join(m.display_name for m in msg.mentions)
await self.bot.say("I have just added the role {} to: ```\n{}```".format(name, fmt))
await ctx.send("I have just added the role {} to: ```\n{}```".format(name, fmt))
def setup(bot):

View file

@ -12,29 +12,14 @@ class Stats:
def __init__(self, bot):
self.bot = bot
def find_command(self, command):
cmd = None
for part in command.split():
try:
if cmd is None:
cmd = self.bot.commands.get(part)
else:
cmd = cmd.commands.get(part)
except AttributeError:
cmd = None
break
return cmd
@commands.command(no_pm=True, pass_context=True)
@commands.command(no_pm=True)
@utils.custom_perms(send_messages=True)
async def serverinfo(self, ctx):
"""Provides information about the server
EXAMPLE: !serverinfo
RESULT: Information about your server!"""
server = ctx.message.server
server = ctx.message.guild
# Create our embed that we'll use for the information
embed = discord.Embed(title=server.name, description="Created on: {}".format(server.created_at.date()))
@ -57,26 +42,23 @@ class Stats:
embed.add_field(name='Channels', value='{} text, {} voice'.format(len(text_channels), len(voice_channels)))
embed.add_field(name='Owner', value=server.owner.display_name)
# Add the shard ID
embed.set_footer(text="Server is on shard: {}/{}".format(self.bot.shard_id+1, self.bot.shard_count))
await ctx.send(embed=embed)
await self.bot.say(embed=embed)
@commands.group(no_pm=True)
@commands.group(no_pm=True, pass_context=False)
@utils.custom_perms(send_messages=True)
async def command(self):
pass
@command.command(no_pm=True, pass_context=True, name="stats")
@command.command(no_pm=True, name="stats")
@utils.custom_perms(send_messages=True)
async def command_stats(self, ctx, *, command):
"""This command can be used to view some usage stats about a specific command
EXAMPLE: !command stats play
RESULT: The realization that this is the only reason people use me ;-;"""
cmd = self.find_command(command)
cmd = self.bot.get_command(command)
if cmd is None:
await self.bot.say("`{}` is not a valid command".format(command))
await ctx.send("`{}` is not a valid command".format(command))
return
r_filter = {'command': cmd.qualified_name}
@ -84,12 +66,12 @@ class Stats:
try:
command_stats = command_stats[0]
except TypeError:
await self.bot.say("That command has never been used! You know I worked hard on that! :c")
await ctx.send("That command has never been used! You know I worked hard on that! :c")
return
total_usage = command_stats['total_usage']
member_usage = command_stats['member_usage'].get(ctx.message.author.id, 0)
server_usage = command_stats['server_usage'].get(ctx.message.server.id, 0)
server_usage = command_stats['server_usage'].get(ctx.message.guild.id, 0)
try:
data = [("Command Name", cmd.qualified_name),
@ -97,16 +79,16 @@ class Stats:
("Your Usage", member_usage),
("This Server's Usage", server_usage)]
banner = await utils.create_banner(ctx.message.author, "Command Stats", data)
await self.bot.upload(banner)
await ctx.send(file=banner)
except (FileNotFoundError, discord.Forbidden):
fmt = "The command {} has been used a total of {} times\n" \
"{} times on this server\n" \
"It has been ran by you, {}, {} times".format(cmd.qualified_name, total_usage, server_usage,
ctx.message.author.display_name, member_usage)
await self.bot.say(fmt)
await ctx.send(fmt)
@command.command(no_pm=True, pass_context=True, name="leaderboard")
@command.command(no_pm=True, name="leaderboard")
@utils.custom_perms(send_messages=True)
async def command_leaderboard(self, ctx, option="server"):
"""This command can be used to print a leaderboard of commands
@ -132,26 +114,26 @@ class Stats:
try:
top_5 = [(data[0], data[1]) for data in sorted_stats[:5]]
banner = await utils.create_banner(ctx.message.author, "Your command usage", top_5)
await self.bot.upload(banner)
await ctx.send(file=banner)
except (FileNotFoundError, discord.Forbidden):
top_5 = "\n".join("{}: {}".format(data[0], data[1]) for data in sorted_stats[:5])
await self.bot.say(
await ctx.send(
"Your top {} most used commands are:\n```\n{}```".format(len(sorted_stats[:5]), top_5))
elif re.search('server', option):
# This is exactly the same as above, except server usage instead of member usage
server = ctx.message.server
server = ctx.message.guild
command_stats = await utils.get_content('command_usage')
stats = {data['command']: data['server_usage'].get(server.id) for data in command_stats
if data['server_usage'].get(server.id, 0) > 0}
sorted_stats = sorted(stats.items(), key=lambda x: x[1], reverse=True)
top_5 = "\n".join("{}: {}".format(data[0], data[1]) for data in sorted_stats[:5])
await self.bot.say(
await ctx.send(
"This server's top {} most used commands are:\n```\n{}```".format(len(sorted_stats[:5]), top_5))
else:
await self.bot.say("That is not a valid option, valid options are: `server` or `me`")
await ctx.send("That is not a valid option, valid options are: `server` or `me`")
@commands.command(pass_context=True, no_pm=True)
@commands.command(no_pm=True)
@utils.custom_perms(send_messages=True)
async def mostboops(self, ctx):
"""Shows the person you have 'booped' the most, as well as how many times
@ -161,14 +143,14 @@ class Stats:
r_filter = {'member_id': ctx.message.author.id}
boops = await utils.get_content('boops', r_filter)
if boops is None:
await self.bot.say("You have not booped anyone {} Why the heck not...?".format(ctx.message.author.mention))
await ctx.send("You have not booped anyone {} Why the heck not...?".format(ctx.message.author.mention))
return
# Just to make this easier, just pay attention to the boops data, now that we have the right entry
boops = boops[0]['boops']
# First get a list of the ID's of all members in this server, for use in list comprehension
server_member_ids = [member.id for member in ctx.message.server.members]
server_member_ids = [member.id for member in ctx.message.guild.members]
# Then get a sorted list, based on the amount of times they've booped the member
# Reverse needs to be true, as we want it to go from highest to lowest
sorted_boops = sorted(boops.items(), key=lambda x: x[1], reverse=True)
@ -179,10 +161,10 @@ class Stats:
most_id, most_boops = sorted_boops[0]
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 ctx.send("{0} you have booped {1} the most amount of times, coming in at {2} times".format(
ctx.message.author.mention, member.display_name, most_boops))
@commands.command(pass_context=True, no_pm=True)
@commands.command(no_pm=True)
@utils.custom_perms(send_messages=True)
async def listboops(self, ctx):
"""Lists all the users you have booped and the amount of times
@ -192,31 +174,31 @@ class Stats:
r_filter = {'member_id': ctx.message.author.id}
boops = await utils.get_content('boops', r_filter)
if boops is None:
await self.bot.say("You have not booped anyone {} Why the heck not...?".format(ctx.message.author.mention))
await ctx.send("You have not booped anyone {} Why the heck not...?".format(ctx.message.author.mention))
return
# Just to make this easier, just pay attention to the boops data, now that we have the right entry
boops = boops[0]['boops']
# Same concept as the mostboops method
server_member_ids = [member.id for member in ctx.message.server.members]
server_member_ids = [member.id for member in ctx.message.guild.members]
booped_members = {m_id: amt for m_id, amt in boops.items() if m_id in server_member_ids}
sorted_booped_members = sorted(booped_members.items(), key=lambda k: k[1], reverse=True)
# Now we only want the first 10 members, so splice this list
sorted_booped_members = sorted_booped_members[:10]
try:
output = [("{0.display_name}".format(ctx.message.server.get_member(m_id)), amt)
output = [("{0.display_name}".format(ctx.message.guild.get_member(m_id)), amt)
for m_id, amt in sorted_booped_members]
banner = await utils.create_banner(ctx.message.author, "Your booped victims", output)
await self.bot.upload(banner)
await ctx.send(file=banner)
except (FileNotFoundError, discord.Forbidden):
output = "\n".join(
"{0.display_name}: {1} times".format(ctx.message.server.get_member(m_id), amt) for
"{0.display_name}: {1} times".format(ctx.message.guild.get_member(m_id), amt) for
m_id, amt in sorted_booped_members)
await self.bot.say("You have booped:```\n{}```".format(output))
await ctx.send("You have booped:```\n{}```".format(output))
@commands.command(pass_context=True, no_pm=True)
@commands.command(no_pm=True)
@utils.custom_perms(send_messages=True)
async def leaderboard(self, ctx):
"""Prints a leaderboard of everyone in the server's battling record
@ -224,7 +206,7 @@ class Stats:
EXAMPLE: !leaderboard
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
server_member_ids = [member.id for member in ctx.message.server.members]
server_member_ids = [member.id for member in ctx.message.guild.members]
battles = await utils.get_content('battle_records')
battles = [battle for battle in battles if battle['member_id'] in server_member_ids]
@ -235,16 +217,16 @@ class Stats:
for x in sorted_members:
member_id = x['member_id']
rating = x['rating']
member = ctx.message.server.get_member(member_id)
member = ctx.message.guild.get_member(member_id)
output.append("{} (Rating: {})".format(member.display_name, rating))
try:
pages = utils.Pages(self.bot, message=ctx.message, entries=output)
await pages.paginate()
except utils.CannotPaginate as e:
await self.bot.say(str(e))
await ctx.send(str(e))
@commands.command(pass_context=True, no_pm=True)
@commands.command(no_pm=True)
@utils.custom_perms(send_messages=True)
async def stats(self, ctx, member: discord.Member = None):
"""Prints the battling stats for you, or the user provided
@ -259,11 +241,11 @@ class Stats:
# 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:
await self.bot.say("That user has not battled yet!")
await ctx.send("That user has not battled yet!")
return
# Same concept as the leaderboard
server_member_ids = [member.id for member in ctx.message.server.members]
server_member_ids = [member.id for member in ctx.message.guild.members]
server_members = [stats for stats in all_members if stats['member_id'] in server_member_ids]
sorted_server_members = sorted(server_members, key=lambda x: x['rating'], reverse=True)
sorted_all_members = sorted(all_members, key=lambda x: x['rating'], reverse=True)
@ -282,12 +264,12 @@ class Stats:
fmt = [('Record', record), ('Server Rank', '{}/{}'.format(server_rank, len(server_members))),
('Overall Rank', '{}/{}'.format(total_rank, len(all_members))), ('Rating', rating)]
banner = await utils.create_banner(member, title, fmt)
await self.bot.upload(banner)
await ctx.send(file=banner)
except (FileNotFoundError, discord.Forbidden):
fmt = 'Stats for {}:\n\tRecord: {}\n\tServer Rank: {}/{}\n\tOverall Rank: {}/{}\n\tRating: {}'
fmt = fmt.format(member.display_name, record, server_rank, len(server_members), total_rank,
len(all_members), rating)
await self.bot.say('```\n{}```'.format(fmt))
await ctx.send('```\n{}```'.format(fmt))
def setup(bot):

View file

@ -1,8 +1,9 @@
from discord.ext import commands
from .utils import config
from .utils import checks
import re
from discord.ext import commands
from . import utils
class Tags:
"""This class contains all the commands for custom tags"""
@ -10,39 +11,39 @@ class Tags:
def __init__(self, bot):
self.bot = bot
@commands.command(pass_context=True, no_pm=True)
@checks.custom_perms(send_messages=True)
@commands.command(no_pm=True)
@utils.custom_perms(send_messages=True)
async def tags(self, ctx):
"""Prints all the custom tags that this server currently has
EXAMPLE: !tags
RESULT: All tags setup on this server"""
tags = await config.get_content('tags', {'server_id': ctx.message.server.id})
tags = await utils.get_content('tags', {'server_id': ctx.message.guild.id})
# Simple generator that adds a tag to the list to print, if the tag is for this server
try:
fmt = "\n".join("{}".format(tag['tag']) for tag in tags)
await self.bot.say('```\n{}```'.format(fmt))
await ctx.send('```\n{}```'.format(fmt))
except TypeError:
await self.bot.say("There are not tags setup on this server!")
await ctx.send("There are not tags setup on this server!")
@commands.group(pass_context=True, invoke_without_command=True, no_pm=True)
@checks.custom_perms(send_messages=True)
@commands.group(invoke_without_command=True, no_pm=True)
@utils.custom_perms(send_messages=True)
async def tag(self, ctx, *, tag: str):
"""This can be used to call custom tags
The format to call a custom tag is !tag <tag>
EXAMPLE: !tag butts
RESULT: Whatever you setup for the butts tag!!"""
r_filter = lambda row: (row['server_id'] == ctx.message.server.id) & (row['tag'] == tag)
tags = await config.get_content('tags', r_filter)
r_filter = lambda row: (row['server_id'] == ctx.message.guild.id) & (row['tag'] == tag)
tags = await utils.get_content('tags', r_filter)
if tags is None:
await self.bot.say('That tag does not exist!')
await ctx.send('That tag does not exist!')
return
# We shouldn't ever have two tags of the same name, so just get the first result
await self.bot.say("\u200B{}".format(tags[0]['result']))
await ctx.send("\u200B{}".format(tags[0]['result']))
@tag.command(name='add', aliases=['create', 'start'], pass_context=True, no_pm=True)
@checks.custom_perms(kick_members=True)
@tag.command(name='add', aliases=['create', 'start'], no_pm=True)
@utils.custom_perms(kick_members=True)
async def add_tag(self, ctx, *, result: str):
"""Use this to add a new tag that can be used in this server
Format to add a tag is !tag add <tag> - <result>
@ -56,43 +57,43 @@ class Tags:
tag_result = match.group(2).strip()
# Next two checks are just to ensure there was a valid match found
except AttributeError:
await self.bot.say(
await ctx.send(
"Please provide the format for the tag in: {}tag add <tag> - <result>".format(ctx.prefix))
return
# If our regex failed to find the content (aka they provided the wrong format)
if len(tag) == 0 or len(tag_result) == 0:
await self.bot.say(
await ctx.send(
"Please provide the format for the tag in: {}tag add <tag> - <result>".format(ctx.prefix))
return
# Make sure the tag created does not mention everyone/here
if '@everyone' in tag_result or '@here' in tag_result:
await self.bot.say("You cannot create a tag that mentions everyone!")
await ctx.send("You cannot create a tag that mentions everyone!")
return
entry = {'server_id': ctx.message.server.id, 'tag': tag, 'result': tag_result}
r_filter = lambda row: (row['server_id'] == ctx.message.server.id) & (row['tag'] == tag)
entry = {'server_id': ctx.message.guild.id, 'tag': tag, 'result': tag_result}
r_filter = lambda row: (row['server_id'] == ctx.message.guild.id) & (row['tag'] == tag)
# Try to create new entry first, if that fails (it already exists) then we update it
if await config.add_content('tags', entry, r_filter):
await self.bot.say(
if await utils.add_content('tags', entry, r_filter):
await ctx.send(
"I have just added the tag `{0}`! You can call this tag by entering !tag {0}".format(tag))
else:
await config.update_content('tags', entry, r_filter)
await self.bot.say(
await utils.update_content('tags', entry, r_filter)
await ctx.send(
"I have just updated the tag `{0}`! You can call this tag by entering !tag {0}".format(tag))
@tag.command(name='delete', aliases=['remove', 'stop'], pass_context=True, no_pm=True)
@checks.custom_perms(kick_members=True)
@tag.command(name='delete', aliases=['remove', 'stop'], no_pm=True)
@utils.custom_perms(kick_members=True)
async def del_tag(self, ctx, *, tag: str):
"""Use this to remove a tag from use for this server
Format to delete a tag is !tag delete <tag>
EXAMPLE: !tag delete stupid_tag
RESULT: Deletes that stupid tag"""
r_filter = lambda row: (row['server_id'] == ctx.message.server.id) & (row['tag'] == tag)
if await config.remove_content('tags', r_filter):
await self.bot.say('I have just removed the tag `{}`'.format(tag))
r_filter = lambda row: (row['server_id'] == ctx.message.guild.id) & (row['tag'] == tag)
if await utils.remove_content('tags', r_filter):
await ctx.send('I have just removed the tag `{}`'.format(tag))
else:
await self.bot.say(
await ctx.send(
"The tag {} does not exist! You can't remove something if it doesn't exist...".format(tag))

View file

@ -1,9 +1,7 @@
from discord.ext import commands
import discord
from .utils import config
from .utils import checks
from .utils import utilities
from . import utils
import re
import random
@ -110,8 +108,8 @@ class TicTacToe:
# Return whoever is x's so that we know who is going first
return self.boards[server_id].challengers['x']
@commands.group(pass_context=True, aliases=['tic', 'tac', 'toe'], no_pm=True, invoke_without_command=True)
@checks.custom_perms(send_messages=True)
@commands.group(aliases=['tic', 'tac', 'toe'], no_pm=True, invoke_without_command=True)
@utils.custom_perms(send_messages=True)
async def tictactoe(self, ctx, *, option: str):
"""Updates the current server's tic-tac-toe board
You obviously need to be one of the players to use this
@ -121,15 +119,15 @@ class TicTacToe:
EXAMPLE: !tictactoe middle top
RESULT: Your piece is placed in the very top space, in the middle"""
player = ctx.message.author
board = self.boards.get(ctx.message.server.id)
board = self.boards.get(ctx.message.guild.id)
# Need to make sure the board exists before allowing someone to play
if not board:
await self.bot.say("There are currently no Tic-Tac-Toe games setup!")
await ctx.send("There are currently no Tic-Tac-Toe games setup!")
return
# Now just make sure the person can play, this will fail if o's are up and x tries to play
# Or if someone else entirely tries to play
if not board.can_play(player):
await self.bot.say("You cannot play right now!")
await ctx.send("You cannot play right now!")
return
# Search for the positions in the option given, the actual match doesn't matter, just need to check if it exists
@ -141,14 +139,14 @@ class TicTacToe:
# Just a bit of logic to ensure nothing that doesn't make sense is given
if top and bottom:
await self.bot.say("That is not a valid location! Use some logic, come on!")
await ctx.send("That is not a valid location! Use some logic, come on!")
return
if left and right:
await self.bot.say("That is not a valid location! Use some logic, come on!")
await ctx.send("That is not a valid location! Use some logic, come on!")
return
# Make sure at least something was given
if not top and not bottom and not left and not right and not middle:
await self.bot.say("Please provide a valid location to play!")
await ctx.send("Please provide a valid location to play!")
return
x = 0
@ -181,7 +179,7 @@ class TicTacToe:
# board.update will handle which letter is placed
# If it returns false however, then someone has already played in that spot and nothing was updated
if not board.update(x, y):
await self.bot.say("Someone has already played there!")
await ctx.send("Someone has already played there!")
return
# Next check if there's a winner
winner = board.check()
@ -189,25 +187,25 @@ class TicTacToe:
# Get the loser based on whether or not the winner is x's
# If the winner is x's, the loser is o's...obviously, and vice-versa
loser = board.challengers['x'] if board.challengers['x'] != winner else board.challengers['o']
await self.bot.say("{} has won this game of TicTacToe, better luck next time {}".format(winner.display_name,
loser.display_name))
await ctx.send("{} has won this game of TicTacToe, better luck next time {}".format(winner.display_name,
loser.display_name))
# Handle updating ratings based on the winner and loser
await utilities.update_records('tictactoe', winner, loser)
await utils.update_records('tictactoe', winner, loser)
# This game has ended, delete it so another one can be made
del self.boards[ctx.message.server.id]
del self.boards[ctx.message.guild.id]
else:
# If no one has won, make sure the game is not full. If it has, delete the board and say it was a tie
if board.full():
await self.bot.say("This game has ended in a tie!")
del self.boards[ctx.message.server.id]
await ctx.send("This game has ended in a tie!")
del self.boards[ctx.message.guild.id]
# If no one has won, and the game has not ended in a tie, print the new updated board
else:
player_turn = board.challengers.get('x') if board.X_turn else board.challengers.get('o')
fmt = str(board) + "\n{} It is now your turn to play!".format(player_turn.display_name)
await self.bot.say(fmt)
await ctx.send(fmt)
@tictactoe.command(name='start', aliases=['challenge', 'create'], pass_context=True, no_pm=True)
@checks.custom_perms(send_messages=True)
@tictactoe.command(name='start', aliases=['challenge', 'create'], no_pm=True)
@utils.custom_perms(send_messages=True)
async def start_game(self, ctx, player2: discord.Member):
"""Starts a game of tictactoe with another player
@ -216,32 +214,32 @@ class TicTacToe:
player1 = ctx.message.author
# For simplicities sake, only allow one game on a server at a time.
# Things can easily get confusing (on the server's end) if we allow more than one
if self.boards.get(ctx.message.server.id) is not None:
await self.bot.say("Sorry but only one Tic-Tac-Toe game can be running per server!")
if self.boards.get(ctx.message.guild.id) is not None:
await ctx.send("Sorry but only one Tic-Tac-Toe game can be running per server!")
return
# Make sure we're not being challenged, I always win anyway
if player2 == ctx.message.server.me:
await self.bot.say("You want to play? Alright lets play.\n\nI win, so quick you didn't even notice it.")
if player2 == ctx.message.guild.me:
await ctx.send("You want to play? Alright lets play.\n\nI win, so quick you didn't even notice it.")
return
if player2 == player1:
await self.bot.say("You can't play yourself, I won't allow it. Go find some friends")
await ctx.send("You can't play yourself, I won't allow it. Go find some friends")
return
# Create the board and return who has been decided to go first
x_player = self.create(ctx.message.server.id, player1, player2)
x_player = self.create(ctx.message.guild.id, player1, player2)
fmt = "A tictactoe game has just started between {} and {}".format(player1.display_name, player2.display_name)
# Print the board too just because
fmt += str(self.boards[ctx.message.server.id])
fmt += str(self.boards[ctx.message.guild.id])
# We don't need to do anything weird with assigning x_player to something
# it is already a member object, just use it
fmt += "I have decided at random, and {} is going to be x's this game. It is your turn first! " \
"Use the {}tictactoe command, and a position, to choose where you want to play"\
"Use the {}tictactoe command, and a position, to choose where you want to play" \
.format(x_player.display_name, ctx.prefix)
await self.bot.say(fmt)
await ctx.send(fmt)
@tictactoe.command(name='delete', aliases=['stop', 'remove', 'end'], pass_context=True, no_pm=True)
@checks.custom_perms(kick_members=True)
@tictactoe.command(name='delete', aliases=['stop', 'remove', 'end'], no_pm=True)
@utils.custom_perms(kick_members=True)
async def stop_game(self, ctx):
"""Force stops a game of tictactoe
This should realistically only be used in a situation like one player leaves
@ -249,12 +247,12 @@ class TicTacToe:
EXAMPLE: !tictactoe stop
RESULT: No more tictactoe!"""
if self.boards.get(ctx.message.server.id) is None:
await self.bot.say("There are no tictactoe games running on this server!")
if self.boards.get(ctx.message.guild.id) is None:
await ctx.send("There are no tictactoe games running on this server!")
return
del self.boards[ctx.message.server.id]
await self.bot.say("I have just stopped the game of TicTacToe, a new should be able to be started now!")
del self.boards[ctx.message.guild.id]
await ctx.send("I have just stopped the game of TicTacToe, a new should be able to be started now!")
def setup(bot):

View file

@ -5,7 +5,6 @@ from . import utils
import aiohttp
import asyncio
import discord
import json
import re
import rethinkdb as r
import traceback
@ -69,12 +68,12 @@ class Twitch:
channel_id = server_id
channel = self.bot.get_channel(channel_id)
# Get the member that has just gone live
member = discord.utils.get(server.members, id=m_id)
member = server.get_member(m_id)
if member is None:
continue
fmt = "{} has just gone live! View their stream at {}".format(member.display_name, url)
await self.bot.send_message(channel, fmt)
await channel.send(fmt)
await utils.update_content('twitch', {'live': 1}, {'member_id': m_id})
for m_id, result in online_users.items():
# Get their url and their user based on that url
@ -93,10 +92,10 @@ class Twitch:
channel_id = server_alerts[0].get('channel_id')
channel = self.bot.get_channel(channel_id)
# Get the member that has just gone live
member = discord.utils.get(server.members, id=m_id)
member = server.get_member(m_id)
fmt = "{} has just gone offline! Catch them next time they stream at {}".format(
member.display_name, url)
await self.bot.send_message(channel, fmt)
await channel.send(fmt)
await utils.update_content('twitch', {'live': 0}, {'member_id': m_id})
await asyncio.sleep(30)
except Exception as e:
@ -104,7 +103,7 @@ class Twitch:
fmt = "{1}\n{0.__class__.__name__}: {0}".format(tb, e)
log.error(fmt)
@commands.group(no_pm=True, invoke_without_command=True, pass_context=True)
@commands.group(no_pm=True, invoke_without_command=True)
@utils.custom_perms(send_messages=True)
async def twitch(self, ctx, *, member: discord.Member = None):
"""Use this command to check the twitch info of a user
@ -116,7 +115,7 @@ class Twitch:
result = await utils.get_content('twitch', {'member_id': member.id})
if result is None:
await self.bot.say("{} has not saved their twitch URL yet!".format(member.name))
await ctx.send("{} has not saved their twitch URL yet!".format(member.name))
return
result = result[0]
@ -131,9 +130,9 @@ class Twitch:
fmt += "\nStatus: {}".format(data['status'])
fmt += "\nFollowers: {}".format(data['followers'])
fmt += "\nURL: {}".format(url)
await self.bot.say("```\n{}```".format(fmt))
await ctx.send("```\n{}```".format(fmt))
@twitch.command(name='add', pass_context=True, no_pm=True)
@twitch.command(name='add', no_pm=True)
@utils.custom_perms(send_messages=True)
async def add_twitch_url(self, ctx, url: str):
"""Saves your user's twitch URL
@ -156,16 +155,15 @@ class Twitch:
url = "https://www.{}".format(url)
# Try to find the channel provided, we'll get a 404 response if it does not exist
with aiohttp.ClientSession() as s:
async with s.get(url) as response:
if not response.status == 200:
await self.bot.say("That twitch user does not exist! "
"What would be the point of adding a nonexistant twitch user? Silly")
return
status = await utils.request(url, attr='status')
if not status == 200:
await ctx.send("That twitch user does not exist! "
"What would be the point of adding a nonexistant twitch user? Silly")
return
r_filter = {'member_id': ctx.message.author.id}
entry = {'twitch_url': url,
'servers': [ctx.message.server.id],
'servers': [ctx.message.guild.id],
'notifications_on': 1,
'live': 0,
'member_id': ctx.message.author.id}
@ -176,9 +174,9 @@ class Twitch:
# Assuming they're not live, and notifications should be on
if not await utils.add_content('twitch', entry, r_filter):
await utils.update_content('twitch', update, r_filter)
await self.bot.say("I have just saved your twitch url {}".format(ctx.message.author.mention))
await ctx.send("I have just saved your twitch url {}".format(ctx.message.author.mention))
@twitch.command(name='remove', aliases=['delete'], pass_context=True, no_pm=True)
@twitch.command(name='remove', aliases=['delete'], no_pm=True)
@utils.custom_perms(send_messages=True)
async def remove_twitch_url(self, ctx):
"""Removes your twitch URL
@ -188,9 +186,9 @@ class Twitch:
# Just try to remove it, if it doesn't exist, nothing is going to happen
r_filter = {'member_id': ctx.message.author.id}
await utils.remove_content('twitch', r_filter)
await self.bot.say("I am no longer saving your twitch URL {}".format(ctx.message.author.mention))
await ctx.send("I am no longer saving your twitch URL {}".format(ctx.message.author.mention))
@twitch.group(pass_context=True, no_pm=True, invoke_without_command=True)
@twitch.group(no_pm=True, invoke_without_command=True)
@utils.custom_perms(send_messages=True)
async def notify(self, ctx):
"""This can be used to modify notification settings for your twitch user
@ -202,17 +200,17 @@ class Twitch:
result = await utils.get_content('twitch', r_filter)
# Check if this user is saved at all
if result is None:
await self.bot.say(
await ctx.send(
"I do not have your twitch URL added {}. You can save your twitch url with !twitch add".format(
ctx.message.author.mention))
# Then check if this server is already added as one to notify in
elif ctx.message.server.id in result[0]['servers']:
await self.bot.say("I am already set to notify in this server...")
elif ctx.message.guild.id in result[0]['servers']:
await ctx.send("I am already set to notify in this server...")
else:
await utils.update_content('twitch', {'servers': r.row['servers'].append(ctx.message.server.id)}, r_filter)
await self.bot.say("This server will now be notified if you go live")
await utils.update_content('twitch', {'servers': r.row['servers'].append(ctx.message.guild.id)}, r_filter)
await ctx.send("This server will now be notified if you go live")
@notify.command(name='on', aliases=['start,yes'], pass_context=True, no_pm=True)
@notify.command(name='on', aliases=['start,yes'], no_pm=True)
@utils.custom_perms(send_messages=True)
async def notify_on(self, ctx):
"""Turns twitch notifications on
@ -221,12 +219,12 @@ class Twitch:
RESULT: Notifications will be sent when you go live"""
r_filter = {'member_id': ctx.message.author.id}
if await utils.update_content('twitch', {"notifications_on": 1}, r_filter):
await self.bot.say("I will notify if you go live {}, you'll get a bajillion followers I promise c:".format(
await ctx.send("I will notify if you go live {}, you'll get a bajillion followers I promise c:".format(
ctx.message.author.mention))
else:
await self.bot.say("I can't notify if you go live if I don't know your twitch URL yet!")
await ctx.send("I can't notify if you go live if I don't know your twitch URL yet!")
@notify.command(name='off', aliases=['stop,no'], pass_context=True, no_pm=True)
@notify.command(name='off', aliases=['stop,no'], no_pm=True)
@utils.custom_perms(send_messages=True)
async def notify_off(self, ctx):
"""Turns twitch notifications off
@ -235,12 +233,12 @@ class Twitch:
RESULT: Notifications will not be sent when you go live"""
r_filter = {'member_id': ctx.message.author.id}
if await utils.update_content('twitch', {"notifications_on": 1}, r_filter):
await self.bot.say(
await ctx.send(
"I will not notify if you go live anymore {}, "
"are you going to stream some lewd stuff you don't want people to see?~".format(
ctx.message.author.mention))
else:
await self.bot.say(
await ctx.send(
"I mean, I'm already not going to notify anyone, because I don't have your twitch URL saved...")

View file

@ -1,5 +1,5 @@
from .cards import Deck
from .checks import is_owner, custom_perms, is_pm, db_check
from .checks import is_owner, custom_perms, db_check
from .config import *
from .utilities import *
from .images import create_banner

View file

@ -24,9 +24,10 @@ async def db_check():
except r.errors.ReqlDriverError:
print("Cannot connect to the RethinkDB instance with the following information: {}".format(db_opts))
print("The RethinkDB instance you have setup may be down, otherwise please ensure you setup a"\
" RethinkDB instance, and you have provided the correct database information in config.yml")
print("The RethinkDB instance you have setup may be down, otherwise please ensure you setup a"
" RethinkDB instance, and you have provided the correct database information in config.yml")
quit()
return
# Get the current databases and check if the one we need is there
dbs = await r.db_list().run(conn)
@ -48,6 +49,7 @@ async def db_check():
await r.table_create(table).run(conn)
print("Done checking tables!")
def is_owner(ctx):
return ctx.message.author.id in config.owner_ids
@ -80,10 +82,3 @@ def custom_perms(**perms):
predicate.perms = perms
return commands.check(predicate)
def is_pm():
def predicate(ctx):
return ctx.message.channel.is_private
return commands.check(predicate)

View file

@ -1,9 +1,11 @@
import asyncio
import discord
class CannotPaginate(Exception):
pass
class Pages:
"""Implements a paginator that queries the user for the
pagination interface.
@ -13,6 +15,7 @@ class Pages:
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
@ -30,12 +33,12 @@ class Pages:
('\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{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
server = self.message.guild
if server is not None:
self.permissions = self.message.channel.permissions_for(server.me)
else:
@ -59,11 +62,11 @@ class Pages:
if not self.paginating:
self.embed.description = '\n'.join(p)
return await self.bot.send_message(self.message.channel, embed=self.embed)
return await self.message.channel.send(embed=self.embed)
if not first:
self.embed.description = '\n'.join(p)
await self.bot.edit_message(self.message, embed=self.embed)
await self.message.edit(embed=self.embed)
return
# verify we can actually use the pagination session
@ -76,7 +79,7 @@ class Pages:
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)
self.message = await self.message.channel.send(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
@ -84,7 +87,7 @@ class Pages:
# it from the default set
continue
try:
await self.bot.add_reaction(self.message, reaction)
await self.message.add_reaction(reaction)
except discord.NotFound:
# If the message isn't found, we don't care about clearing anything
return
@ -116,40 +119,45 @@ class Pages:
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)
to_delete.append(await self.message.channel.send('What page do you want to go to?'))
def check(m):
if m.author == self.author and m.channel == self.message.channel:
return m.content.isdigit()
else:
return False
msg = await self.bot.wait_for('message', check=check, 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)))
to_delete.append(await self.message.channel.send(
'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.'))
to_delete.append(await self.message.channel.send('Took too long.'))
await asyncio.sleep(5)
try:
await self.bot.delete_messages(to_delete)
await self.message.channel.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')
messages = ['Welcome to the interactive paginator!\n',
'This interactively allows you to see pages of text by navigating with '
'reactions. They are as follows:\n']
for (emoji, func) in self.reaction_emojis:
messages.append('%s %s' % (emoji, func.__doc__))
e.description = '\n'.join(messages)
e.colour = 0x738bd7 # blurple
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)
await self.message.edit(embed=e)
async def go_back_to_current_page():
await asyncio.sleep(60.0)
@ -159,11 +167,11 @@ class Pages:
async def stop_pages(self):
"""stops the interactive pagination session"""
await self.bot.delete_message(self.message)
await self.message.delete()
self.paginating = False
def react_check(self, reaction, user):
if user is None or user.id != self.author.id:
if user is None or user.id != self.author.id or reaction.message != self.message:
return False
for (emoji, func) in self.reaction_emojis:
@ -177,19 +185,19 @@ class Pages:
await self.show_page(start_page, first=True)
while self.paginating:
react = await self.bot.wait_for_reaction(message=self.message, check=self.react_check, timeout=120.0)
react = await self.bot.wait_for('reaction', check=self.react_check, timeout=120.0)
if react is None:
self.paginating = False
try:
await self.bot.clear_reactions(self.message)
await self.message.clear_reactions()
except:
pass
finally:
break
try:
await self.bot.remove_reaction(self.message, react.reaction.emoji, react.user)
await self.message.remove_reaction(react.reaction.emoji, react.user)
except:
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()

View file

@ -5,6 +5,7 @@ import inspect
from . import config
from PIL import Image
def convert_to_jpeg(pfile):
# Open the file given
img = Image.open(pfile)
@ -16,6 +17,7 @@ def convert_to_jpeg(pfile):
new_file.seek(0)
return new_file
def get_all_commands(bot):
"""Returns a list of all command names for the bot"""
# First lets create a set of all the parent names
@ -31,6 +33,7 @@ def get_all_commands(bot):
return all_commands
def get_subcommands(command):
yield command.qualified_name
try:
@ -40,33 +43,9 @@ def get_subcommands(command):
except AttributeError:
pass
def find_command(bot, command):
"""Finds a command (be it parent or sub command) based on string given"""
# This method ensures the command given is valid. We need to loop through commands
# As bot.commands only includes parent commands
# So we are splitting the command in parts, looping through the commands
# And getting the subcommand based on the next part
# If we try to access commands of a command that isn't a group
# We'll hit an AttributeError, meaning an invalid command was given
# If we loop through and don't find anything, cmd will still be None
# And we'll report an invalid was given as well
cmd = None
for part in command.split():
try:
if cmd is None:
cmd = bot.commands.get(part)
else:
cmd = cmd.commands.get(part)
except AttributeError:
cmd = None
break
return cmd
async def download_image(url):
"""Returns a file-like object based on the URL provided"""
headers = {'User-Agent': config.user_agent}
# Simply read the image, to get the bytes
bts = await request(url, attr='read')
if bts is None:
@ -76,10 +55,12 @@ async def download_image(url):
image = BytesIO(bts)
return image
async def request(url, *, headers=None, payload=None, method='GET', attr='json'):
# Make sure our User Agent is what's set, and ensure it's sent even if no headers are passed
if headers == None:
if headers is None:
headers = {}
headers['User-Agent'] = config.user_agent
# Try 5 times
@ -112,6 +93,7 @@ async def request(url, *, headers=None, payload=None, method='GET', attr='json')
except:
continue
async def update_records(key, winner, loser):
# We're using the Harkness scale to rate
# http://opnetchessclub.wikidot.com/harkness-rating-system

View file

@ -32,6 +32,7 @@ youtube_dl.utils.bug_reports_message = lambda: ''
'''
class Downloader:
def __init__(self, download_folder=None):
self.thread_pool = ThreadPoolExecutor(max_workers=2)
@ -48,7 +49,6 @@ class Downloader:
otmpl = self.safe_ytdl.params['outtmpl']
self.safe_ytdl.params['outtmpl'] = os.path.join(download_folder, otmpl)
@property
def ytdl(self):
return self.safe_ytdl
@ -61,7 +61,8 @@ class Downloader:
"""
if callable(on_error):
try:
return await loop.run_in_executor(self.thread_pool, functools.partial(self.unsafe_ytdl.extract_info, *args, **kwargs))
return await loop.run_in_executor(self.thread_pool,
functools.partial(self.unsafe_ytdl.extract_info, *args, **kwargs))
except Exception as e:
@ -79,7 +80,9 @@ class Downloader:
if retry_on_error:
return await self.safe_extract_info(loop, *args, **kwargs)
else:
return await loop.run_in_executor(self.thread_pool, functools.partial(self.unsafe_ytdl.extract_info, *args, **kwargs))
return await loop.run_in_executor(self.thread_pool,
functools.partial(self.unsafe_ytdl.extract_info, *args, **kwargs))
async def safe_extract_info(self, loop, *args, **kwargs):
return await loop.run_in_executor(self.thread_pool, functools.partial(self.safe_ytdl.extract_info, *args, **kwargs))
return await loop.run_in_executor(self.thread_pool,
functools.partial(self.safe_ytdl.extract_info, *args, **kwargs))

View file

@ -3,11 +3,13 @@ import json
import os
import traceback
import time
import discord
import aiohttp
from discord import Embed
from hashlib import md5
from .exceptions import ExtractionError
async def get_header(session, url, headerfield=None, *, timeout=5):
with aiohttp.Timeout(timeout):
async with session.head(url) as response:
@ -16,6 +18,7 @@ async def get_header(session, url, headerfield=None, *, timeout=5):
else:
return response.headers
def md5sum(filename, limit=0):
fhash = md5()
with open(filename, "rb") as f:
@ -23,6 +26,7 @@ def md5sum(filename, limit=0):
fhash.update(chunk)
return fhash.hexdigest()[-limit:]
class BasePlaylistEntry:
def __init__(self):
self.filename = None
@ -48,8 +52,8 @@ class BasePlaylistEntry:
def get_ready_future(self):
"""
Returns a future that will fire when the song is ready to be played. The future will either fire with the result (being the entry) or an exception
as to why the song download failed.
Returns a future that will fire when the song is ready to be played. The future will either fire with the
result (being the entry) or an exception as to why the song download failed.
"""
future = asyncio.Future()
if self.is_downloaded:
@ -159,7 +163,7 @@ class URLPlaylistEntry(BasePlaylistEntry):
'type': self.meta[i].__class__.__name__,
'id': self.meta[i].id,
'name': self.meta[i].name
} for i in self.meta
} for i in self.meta
}
# Actually I think I can just getattr instead, getattr(discord, type)
}
@ -271,16 +275,17 @@ class URLPlaylistEntry(BasePlaylistEntry):
else:
# Move the temporary file to it's final location.
os.rename(unhashed_fname, self.filename)
def to_embed(self):
"""Returns an embed that can be used to display information about this particular song"""
# Create the embed object we'll use
embed = discord.Embed()
embed = Embed()
# Fill in the simple things
embed.add_field(name='Title', value=self.title, inline=False)
embed.add_field(name='Requester', value=self.requester.display_name, inline=False)
# Get the current length of the song and display this
length = divmod(round(self.length, 0), 60)
fmt = "{0[0]}m {0[1]}s".format(length)
embed.add_field(name='Duration', value=fmt,inline=False)
embed.add_field(name='Duration', value=fmt, inline=False)
# And return the embed we created
return embed

View file

@ -1,6 +1,7 @@
import shutil
import textwrap
# Base class for exceptions
class MusicbotException(Exception):
def __init__(self, message, *, expire_in=0):
@ -15,14 +16,17 @@ class MusicbotException(Exception):
def message_no_format(self):
return self._message
# Something went wrong during the processing of a command
class CommandError(MusicbotException):
pass
# Something went wrong during the processing of a song/ytdl stuff
class ExtractionError(MusicbotException):
pass
# The no processing entry type failed and an entry was a playlist/vice versa
class WrongEntryTypeError(ExtractionError):
def __init__(self, message, is_playlist, use_url):
@ -30,12 +34,14 @@ class WrongEntryTypeError(ExtractionError):
self.is_playlist = is_playlist
self.use_url = use_url
# The user doesn't have permission to use a command
class PermissionsError(CommandError):
@property
def message(self):
return "You don't have permission to use that command.\nReason: " + self._message
# Error with pretty formatting for hand-holding users through various errors
class HelpfulError(MusicbotException):
def __init__(self, issue, solution, *, preface="An error has occured:\n", expire_in=0):
@ -48,14 +54,14 @@ class HelpfulError(MusicbotException):
def message(self):
return ("\n{}\n{}\n{}\n").format(
self.preface,
self._pretty_wrap(self.issue, " Problem: "),
self._pretty_wrap(self.issue, " Problem: "),
self._pretty_wrap(self.solution, " Solution: "))
@property
def message_no_format(self):
return "\n{}\n{}\n{}\n".format(
self.preface,
self._pretty_wrap(self.issue, " Problem: ", width=None),
self._pretty_wrap(self.issue, " Problem: ", width=None),
self._pretty_wrap(self.solution, " Solution: ", width=None))
@staticmethod
@ -72,17 +78,21 @@ class HelpfulError(MusicbotException):
return ''.join([l1, *lx])
class HelpfulWarning(HelpfulError):
pass
# Base class for control signals
class Signal(Exception):
pass
# signal to restart the bot
class RestartSignal(Signal):
pass
# signal to end the bot "gracefully"
class TerminateSignal(Signal):
pass

View file

@ -4,7 +4,7 @@ from collections import deque
from itertools import islice
from random import shuffle
from .entry import URLPlaylistEntry
from .entry import URLPlaylistEntry, get_header
from .exceptions import ExtractionError, WrongEntryTypeError
from .event_emitter import EventEmitter