1
0
Fork 0
mirror of synced 2024-06-03 03:04:33 +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, opts = {'command_prefix': utils.command_prefix,
'description': utils.bot_description, 'description': utils.bot_description,
'pm_help': None, 'pm_help': None,
'shard_count': utils.shard_count,
'shard_id': utils.shard_id,
'command_not_found': ''} 'command_not_found': ''}
bot = commands.Bot(**opts) bot = commands.AutoShardedBot(**opts)
logging.basicConfig(level=logging.WARNING, filename='bonfire.log') 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) m, s = divmod(error.retry_after, 60)
fmt = "This command is on cooldown! Hold your horses! >:c\nTry again in {} minutes and {} seconds" \ fmt = "This command is on cooldown! Hold your horses! >:c\nTry again in {} minutes and {} seconds" \
.format(round(m), round(s)) .format(round(m), round(s))
await bot.send_message(ctx.message.channel, fmt) await ctx.message.channel.send(fmt)
elif isinstance(error, commands.NoPrivateMessage): elif isinstance(error, commands.NoPrivateMessage):
fmt = "This command cannot be used in a private message" 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): elif isinstance(error, commands.MissingRequiredArgument):
await bot.send_message(ctx.message.channel, error) await ctx.message.channel.send(error)
else: else:
now = datetime.datetime.now() now = datetime.datetime.now()
with open("error_log", 'a') as f: 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 # 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 # To do this, it needs to be able to access this instance of Blackjack
game = Game(self.bot, message, self) 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) @utils.custom_perms(send_messages=True)
async def blackjack(self, ctx): async def blackjack(self, ctx):
"""Creates a game/joins the current running game of blackjack """Creates a game/joins the current running game of blackjack
EXAMPLE: !blackjack EXAMPLE: !blackjack
RESULT: A new game of blackjack!""" RESULT: A new game of blackjack!"""
# Get this server's game if it exists # Get this guild's game if it exists
game = self.games.get(ctx.message.server.id) game = self.games.get(ctx.message.guild.id)
# If it doesn't, start one # If it doesn't, start one
if game is None: if game is None:
self.create_game(ctx.message) self.create_game(ctx.message)
@ -54,15 +54,15 @@ class Blackjack:
# If it worked, they're ready to play # If it worked, they're ready to play
if status: if status:
fmt = "{} has joined the game of blackjack, and will be able to play next round!" 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: else:
# Otherwise, lets check *why* they couldn't join # Otherwise, lets check *why* they couldn't join
if game.playing(ctx.message.author): 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: 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) @utils.custom_perms(send_messages=True)
async def blackjack_leave(self, ctx): async def blackjack_leave(self, ctx):
"""Leaves the current game of blackjack """Leaves the current game of blackjack
@ -70,37 +70,37 @@ class Blackjack:
EXAMPLE: !blackjack leave EXAMPLE: !blackjack leave
RESULT: You stop losing money in blackjack""" RESULT: You stop losing money in blackjack"""
# Get this server's game if it exists # Get this guild's game if it exists
game = self.games.get(ctx.message.server.id) game = self.games.get(ctx.message.guild.id)
if game is None: 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 return
status = game.leave(ctx.message.author) status = game.leave(ctx.message.author)
if status: 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: 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']) @blackjack.command(no_pm=True, name='forcestop', aliases=['stop'])
@utils.custom_perms(manage_server=True) @utils.custom_perms(manage_guild=True)
async def blackjack_stop(self, ctx): async def blackjack_stop(self, ctx):
"""Forces the game to stop, mostly for use if someone has gone afk """Forces the game to stop, mostly for use if someone has gone afk
EXAMPLE: !blackjack forcestop EXAMPLE: !blackjack forcestop
RESULT: No more blackjack spam""" RESULT: No more blackjack spam"""
# Get this server's game if it exists # Get this guild's game if it exists
game = self.games.get(ctx.message.server.id) game = self.games.get(ctx.message.guild.id)
if game is None: 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 return
game.task.cancel() game.task.cancel()
del self.games[ctx.message.server.id] del self.games[ctx.message.guild.id]
await self.bot.say("The blackjack game running here has just ended") await ctx.send("The blackjack game running here has just ended")
def FOIL(a, b): def FOIL(a, b):
@ -226,7 +226,7 @@ class Game:
async def game_task(self): async def game_task(self):
"""The task to handle the entire game""" """The task to handle the entire game"""
while len(self.players) > 0: 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 # First wait for bets
await self.bet_task() await self.bet_task()
@ -253,25 +253,25 @@ class Game:
await self.cleanup() await self.cleanup()
# If we reach the end of this loop, that means there are no more players # 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): async def dealer_task(self):
"""The task handling the dealer's play after all players have stood""" """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) 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: while True:
await asyncio.sleep(1) await asyncio.sleep(1)
if self.dealer.bust: if self.dealer.bust:
fmt = msg.content + "\n\nDealer has busted!!" fmt = msg.content + "\n\nDealer has busted!!"
await self.bot.edit_message(msg, fmt) await msg.edit(content=fmt)
return return
for num in self.dealer.count: for num in self.dealer.count:
if num > 16: if num > 16:
return return
self.hit(self.dealer) self.hit(self.dealer)
fmt = "It is the dealer's turn to play\n\n{}".format(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): async def round_task(self):
"""The task handling the round itself, asking each person to hit or stand""" """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') # 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 # 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 # First lets handle the blackjacks
for entry in [p for p in self.players if p['status'] == 'blackjack']: for entry in [p for p in self.players if p['status'] == 'blackjack']:
player = entry['player'] player = entry['player']
fmt = "You got a blackjack {0.member.mention}!\n\n{0}".format(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 # 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')]: for entry in [p for p in self.players if p['status'] == 'playing' and hasattr(p['player'], 'bet')]:
player = entry['player'] player = entry['player']
# Let them know it's their turn to play # Let them know it's their turn to play
fmt = "It is your turn to play {0.member.mention}\n\n{0}".format(player) 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 # 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 # them to hit or stand
@ -302,37 +306,28 @@ class Game:
# Ask if they want to hit or stand # Ask if they want to hit or stand
fmt = "Hit, stand, or double?" fmt = "Hit, stand, or double?"
await self.bot.send_message(self.channel, fmt) await self.channel.send(fmt)
msg = await self.bot.wait_for_message(timeout=60, author=player.member, channel=self.channel,
check=check)
# If they took to long, make them stand so the next person can play try:
if msg is None: msg = await self.bot.wait_for('message', timeout=60, check=check)
await self.bot.send_message(self.channel, "Took to long! You're standing!") except asyncio.TimeoutError:
await self.channel.send("Took to long! You're standing!")
entry['status'] = 'stand' entry['status'] = 'stand'
# If they want to hit else:
elif 'hit' in msg.content.lower(): # If they want to hit
self.hit(player) if 'hit' in msg.content.lower():
await self.bot.send_message(self.channel, player) self.hit(player)
# If they want to stand await self.channel.send(player)
elif 'stand' in msg.content.lower(): # If they want to stand
self.stand(player) elif 'stand' in msg.content.lower():
elif 'double' in msg.content.lower(): self.stand(player)
self.double(player) elif 'double' in msg.content.lower():
await self.bot.send_message(self.channel, player) self.double(player)
# TODO: Handle double, split await self.channel.send(player)
# TODO: Handle double, split
async def bet_task(self): async def bet_task(self):
"""Performs the task of betting""" """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 # 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 # 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 # 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] entry = players[0]
player = entry['player'] 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" \ 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" \ "Current min bet is {1}, current max bet is {2}\n" \
"Place your bet now (please provide only the number;" \ "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) "'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.channel.send(fmt)
await self.bot.send_message(self.channel, "You took too long! You're sitting this round out")
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' 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: else:
num = int(msg.content) if msg.content.lower() == 'skip':
# Set the new bet, and remove it from this players chip total await self.channel.send("Alright, you've been removed from the game")
if num <= player.chips: self.leave(player.member)
player.bet = num
player.chips -= num
entry['status'] = 'bet'
else: 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 # Call this so that we can correct the list, if someone has left or join
self.player_cleanup() self.player_cleanup()
@ -464,7 +474,7 @@ class Game:
# Add that to the main string # Add that to the main string
fmt += "Blackjacks: {}\n".format(_fmt) 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 # Do the same for the dealers hand
cards = list(self.dealer.hand.draw(self.dealer.hand.count)) cards = list(self.dealer.hand.draw(self.dealer.hand.count))
@ -481,10 +491,10 @@ class Game:
self.players.extend(self._added_players) self.players.extend(self._added_players)
self._added_players.clear() 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: for entry in self.players:
m = entry['player'].member 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']) self._removed_players.append(entry['player'])
# Remove the players who left # Remove the players who left

View file

@ -10,7 +10,7 @@ from enum import Enum
class Chess: class Chess:
def __init__(self, bot): def __init__(self, bot):
self.bot = 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]} # Format should be {'server_id': [Game, Game, Game]}
self.games = {} self.games = {}
@ -91,18 +91,18 @@ class Chess:
return MoveStatus.invalid return MoveStatus.invalid
def get_game(self, player): def get_game(self, player):
"""Provides the game this player is playing, in this server""" """Provides the game this player is playing, in this guild"""
server_games = self.games.get(player.server.id, []) guild_games = self.games.get(player.guild.id, [])
for game in server_games: for game in guild_games:
if player in game.challengers.values(): if player in game.challengers.values():
return game return game
return None return None
def in_game(self, player): def in_game(self, player):
"""Checks to see if a player is playing in a game right now, in this server""" """Checks to see if a player is playing in a game right now, in this guild"""
server_games = self.games.get(player.server.id, []) guild_games = self.games.get(player.guild.id, [])
for game in server_games: for game in guild_games:
if player in game.challengers.values(): if player in game.challengers.values():
return True return True
@ -111,13 +111,13 @@ class Chess:
def start_game(self, player1, player2): def start_game(self, player1, player2):
game = Game(player1, player2) game = Game(player1, player2)
try: try:
self.games[player1.server.id].append(game) self.games[player1.guild.id].append(game)
except KeyError: except KeyError:
self.games[player1.server.id] = [game] self.games[player1.guild.id] = [game]
return game return game
@commands.group(pass_contxt=True, invoke_without_command=True) @commands.group(invoke_without_command=True)
@checks.custom_perms(send_messages=True) @checks.custom_perms(send_messages=True)
async def chess(self, ctx, *, move): async def chess(self, ctx, *, move):
"""Moves a piece based on the notation provided """Moves a piece based on the notation provided
@ -142,47 +142,48 @@ class Chess:
EXAMPLE: !rook to d4""" EXAMPLE: !rook to d4"""
result = self.play(ctx.message.author, move) result = self.play(ctx.message.author, move)
if result is MoveStatus.invalid: 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: 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: 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: elif result is MoveStatus.valid:
game = self.get_game(ctx.message.author) game = self.get_game(ctx.message.author)
link = game.draw_board() link = game.draw_board()
await self.bot.upload(link) await self.bot.upload(link)
@commands.command(pass_context=True) @commands.command()
@checks.custom_perms(send_messages=True) @checks.custom_perms(send_messages=True)
async def chess_start(self, ctx, player2: discord.Member): async def chess_start(self, ctx, player2: discord.Member):
"""Starts a chess game with another player """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 EXAMPLE: !chess start @Victim
RESULT: A new chess game! Good luck~""" RESULT: A new chess game! Good luck~"""
# Lets first check our permissions; we're not going to create a text based board # 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 # 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: if not ctx.message.channel.permissions_for(ctx.message.guild.me).attach_files:
await self.bot.say( 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") "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 return
# Make sure the author and player 2 are not in a game already # Make sure the author and player 2 are not in a game already
if self.in_game(ctx.message.author): 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 return
if self.in_game(player2): 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 return
# Start the game # Start the game
game = self.start_game(ctx.message.author, player2) game = self.start_game(ctx.message.author, player2)
player1 = game.challengers.get('white') player1 = game.challengers.get('white')
await self.bot.say( 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( "{} you have started a chess game with {}\n\n{} will be white this game, and is going first.\nIf you need "
ctx.message.author.display_name, player2.display_name, ctx.prefix)) "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): class MoveStatus(Enum):
@ -209,7 +210,7 @@ class Game:
self.reset_board() self.reset_board()
# The point of chess revolves around the king's position # 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.w_king_pos = (0, 4)
self.b_king_pos = (7, 4) self.b_king_pos = (7, 4)
@ -262,7 +263,7 @@ class Game:
colour = 'black' colour = 'black'
king_pos = self.b_king_pos king_pos = self.b_king_pos
if not can_castle[colour][pos]: if not self.can_castle[colour][pos]:
return False return False
# During castling, the row should never change # During castling, the row should never change
@ -388,7 +389,7 @@ class Game:
def check(self): def check(self):
"""Checks our current board, and checks (based on whose turn it is) if we're in a 'check' state""" """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 # 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 x, row in enumerate(self.board):
for y, piece in enumerate(row): 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): 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 calendar
import pendulum import pendulum
import datetime import datetime
import math
class Core: class Core:
@ -23,30 +22,7 @@ class Core:
self.results_per_page = 10 self.results_per_page = 10
self.commands = None self.commands = None
def find_command(self, command): @commands.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)
@utils.custom_perms(send_messages=True) @utils.custom_perms(send_messages=True)
async def help(self, ctx, *, message=None): async def help(self, ctx, *, message=None):
"""This command is used to provide a link to the help URL. """This command is used to provide a link to the help URL.
@ -65,7 +41,7 @@ class Core:
try: try:
page = int(message) page = int(message)
except: except:
cmd = self.find_command(message) cmd = self.bot.get_command(message)
if cmd is None: if cmd is None:
entries = sorted(utils.get_all_commands(self.bot)) entries = sorted(utils.get_all_commands(self.bot))
@ -73,7 +49,7 @@ class Core:
pages = utils.Pages(self.bot, message=ctx.message, entries=entries) pages = utils.Pages(self.bot, message=ctx.message, entries=entries)
await pages.paginate(start_page=page) await pages.paginate(start_page=page)
except utils.CannotPaginate as e: except utils.CannotPaginate as e:
await self.bot.say(str(e)) await ctx.send(str(e))
else: else:
# Get the description for a command # Get the description for a command
description = cmd.help description = cmd.help
@ -100,11 +76,11 @@ class Core:
if subcommands: if subcommands:
embed.add_field(name='Subcommands', value="\n".join(subcommands), inline=False) embed.add_field(name='Subcommands', value="\n".join(subcommands), inline=False)
await self.bot.say(embed=embed) await ctx.send(embed=embed)
@commands.command() @commands.command()
@utils.custom_perms(send_messages=True) @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 command can be used to print the current MOTD (Message of the day)
This will most likely not be updated every day, however messages will still be pushed to this every now and then This will most likely not be updated every day, however messages will still be pushed to this every now and then
@ -126,10 +102,10 @@ class Core:
motd = latest_motd['motd'] motd = latest_motd['motd']
# This will be hit if we do not have any entries for motd # This will be hit if we do not have any entries for motd
except TypeError: except TypeError:
await self.bot.say("No message of the day!") await ctx.send("No message of the day!")
else: else:
fmt = "Last updated: {}\n\n{}".format(date, motd) fmt = "Last updated: {}\n\n{}".format(date, motd)
await self.bot.say(fmt) await ctx.send(fmt)
else: else:
try: try:
r_filter = pendulum.parse(date) r_filter = pendulum.parse(date)
@ -137,18 +113,18 @@ class Core:
date = motd[0]['date'] date = motd[0]['date']
motd = motd[0]['motd'] motd = motd[0]['motd']
fmt = "Message of the day for {}:\n\n{}".format(date, motd) fmt = "Message of the day for {}:\n\n{}".format(date, motd)
await self.bot.say(fmt) await ctx.send(fmt)
# This one will be hit if we return None for that day # This one will be hit if we return None for that day
except TypeError: 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 # This will be hit if pendulum fails to parse the date passed
except ValueError: except ValueError:
now = pendulum.utcnow().to_date_string() 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() @commands.command()
@utils.custom_perms(send_messages=True) @utils.custom_perms(send_messages=True)
async def calendar(self, month: str = None, year: int = None): async def calendar(self, ctx, month: str = None, year: int = None):
"""Provides a printout of the current month's calendar """Provides a printout of the current month's calendar
Provide month and year to print the calendar of that year and month Provide month and year to print the calendar of that year and month
@ -176,30 +152,23 @@ class Core:
else: else:
month = months.get(month.lower()) month = months.get(month.lower())
if month is None: if month is None:
await self.bot.say("Please provide a valid Month!") await ctx.send("Please provide a valid Month!")
return return
# If year was not passed, use the current year # If year was not passed, use the current year
if year is None: if year is None:
year = datetime.datetime.today().year year = datetime.datetime.today().year
# Here we create the actual "text" calendar that we are printing # Here we create the actual "text" calendar that we are printing
cal = calendar.TextCalendar().formatmonth(year, month) cal = calendar.TextCalendar().formatmonth(year, month)
await self.bot.say("```\n{}```".format(cal)) await ctx.send("```\n{}```".format(cal))
@commands.command() @commands.command()
@utils.custom_perms(send_messages=True) @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""" """This command can be used to print out some of my information"""
# fmt is a dictionary so we can set the key to it's output, then print both # fmt is a dictionary so we can set the key to it's output, then print both
# The only real use of doing it this way is easier editing if the info # The only real use of doing it this way is easier editing if the info
# in this command is changed # in this command is changed
bot_data = await 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 # Create the original embed object
opts = {'title': 'Dev Server', opts = {'title': 'Dev Server',
'description': 'Join the server above for any questions/suggestions about me.', 'description': 'Join the server above for any questions/suggestions about me.',
@ -207,8 +176,8 @@ class Core:
embed = discord.Embed(**opts) embed = discord.Embed(**opts)
# Add the normal values # Add the normal values
embed.add_field(name='Total Servers', value=total_data['server_count']) embed.add_field(name='Total Servers', value=len(self.bot.servers))
embed.add_field(name='Total Members', value=total_data['member_count']) embed.add_field(name='Total Members', value=len(set(self.bot.get_all_members())))
# Count the variable values; hangman, tictactoe, etc. # Count the variable values; hangman, tictactoe, etc.
hm_games = len(self.bot.get_cog('Hangman').games) 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.add_field(name='Uptime', value=(pendulum.utcnow() - self.bot.uptime).in_words())
embed.set_footer(text=self.bot.description) embed.set_footer(text=self.bot.description)
await self.bot.say(embed=embed) await ctx.send(embed=embed)
@commands.command() @commands.command()
@utils.custom_perms(send_messages=True) @utils.custom_perms(send_messages=True)
async def uptime(self): async def uptime(self, ctx):
"""Provides a printout of the current bot's uptime """Provides a printout of the current bot's uptime
EXAMPLE: !uptime EXAMPLE: !uptime
RESULT: A BAJILLION DAYS""" 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']) @commands.command(aliases=['invite'])
@utils.custom_perms(send_messages=True) @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 """Provides a link that you can use to add me to a server
EXAMPLE: !addbot EXAMPLE: !addbot
@ -261,13 +230,17 @@ class Core:
perms.embed_links = True perms.embed_links = True
perms.read_message_history = True perms.read_message_history = True
perms.attach_files = 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() 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{}" 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))) .format(discord.utils.oauth_url(app_info.id, perms)))
@commands.command() @commands.command()
@utils.custom_perms(send_messages=True) @utils.custom_perms(send_messages=True)
async def doggo(self): async def doggo(self, ctx):
"""Use this to print a random doggo image. """Use this to print a random doggo image.
EXAMPLE: !doggo EXAMPLE: !doggo
@ -275,11 +248,11 @@ class Core:
# Find a random image based on how many we currently have # Find a random image based on how many we currently have
f = random.SystemRandom().choice(glob.glob('images/doggo*')) f = random.SystemRandom().choice(glob.glob('images/doggo*'))
with open(f, 'rb') as f: with open(f, 'rb') as f:
await self.bot.upload(f) await ctx.send(file=f)
@commands.command() @commands.command()
@utils.custom_perms(send_messages=True) @utils.custom_perms(send_messages=True)
async def snek(self): async def snek(self, ctx):
"""Use this to print a random snek image. """Use this to print a random snek image.
EXAMPLE: !snek EXAMPLE: !snek
@ -287,11 +260,11 @@ class Core:
# Find a random image based on how many we currently have # Find a random image based on how many we currently have
f = random.SystemRandom().choice(glob.glob('images/snek*')) f = random.SystemRandom().choice(glob.glob('images/snek*'))
with open(f, 'rb') as f: with open(f, 'rb') as f:
await self.bot.upload(f) await ctx.send(file=f)
@commands.command() @commands.command()
@utils.custom_perms(send_messages=True) @utils.custom_perms(send_messages=True)
async def joke(self): async def joke(self, ctx):
"""Prints a random riddle """Prints a random riddle
EXAMPLE: !joke EXAMPLE: !joke
@ -302,13 +275,13 @@ class Core:
try: try:
fortune = subprocess.check_output( fortune = subprocess.check_output(
fortune_command.split()).decode("utf-8") fortune_command.split()).decode("utf-8")
await self.bot.say(fortune) await ctx.send(fortune)
except discord.HTTPException: except discord.HTTPException:
pass pass
else: else:
break break
@commands.command(pass_context=True) @commands.command()
@utils.custom_perms(send_messages=True) @utils.custom_perms(send_messages=True)
async def roll(self, ctx, notation: str = "d6"): async def roll(self, ctx, notation: str = "d6"):
"""Rolls a die based on the notation given """Rolls a die based on the notation given
@ -325,7 +298,7 @@ class Core:
# Check if something like ed3 was provided, or something else entirely # Check if something like ed3 was provided, or something else entirely
# was provided # was provided
except (AttributeError, ValueError): 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 return
# Dice will be None if d# was provided, assume this means 1d# # Dice will be None if d# was provided, assume this means 1d#
@ -334,16 +307,16 @@ class Core:
# have it set # have it set
dice = int(dice) dice = int(dice)
if dice > 10: 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 return
if num > 100: 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 return
if num <= 1: 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 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) total = sum(nums)
value_str = ", ".join("{}".format(x) for x in 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}!' fmt = '{0.message.author.name} has rolled a {2} sided die and got the number {3}!'
else: else:
fmt = '{0.message.author.name} has rolled {1}, {2} sided dice and got the numbers {3}, for a total of {4}!' 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): def setup(bot):

View file

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

View file

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

View file

@ -48,38 +48,17 @@ class StatsUpdate:
log.info('bots.discord.pw statistics returned {} for {}'.format(resp.status, payload)) log.info('bots.discord.pw statistics returned {} for {}'.format(resp.status, payload))
async def on_server_join(self, server): 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()) self.bot.loop.create_task(self.update())
async def on_server_leave(self, server): 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()) self.bot.loop.create_task(self.update())
async def on_ready(self): 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()) self.bot.loop.create_task(self.update())
async def on_member_join(self, member): async def on_member_join(self, member):
server = member.server guild = member.guild
r_filter = {'server_id': server.id} r_filter = {'server_id': guild.id}
notifications = await config.get_content('user_notifications', r_filter) notifications = await config.get_content('user_notifications', r_filter)
try: try:
@ -91,12 +70,12 @@ class StatsUpdate:
if not channel_id: if not channel_id:
return return
channel = server.get_channel(channel_id) channel = guild.get_channel(channel_id)
await self.bot.send_message(channel, "Welcome to the '{0.server.name}' server {0.mention}!".format(member)) await channel.send("Welcome to the '{0.server.name}' server {0.mention}!".format(member))
async def on_member_remove(self, member): async def on_member_remove(self, member):
server = member.server guild = member.guild
r_filter = {'server_id': server.id} r_filter = {'server_id': guild.id}
notifications = await config.get_content('user_notifications', r_filter) notifications = await config.get_content('user_notifications', r_filter)
try: try:
@ -109,9 +88,7 @@ class StatsUpdate:
return return
channel = server.get_channel(channel_id) channel = server.get_channel(channel_id)
await self.bot.send_message(channel, await channelsend("{0} has left the server, I hope it wasn't because of something I said :c".format(member.display_name))
"{0} has left the server, I hope it wasn't because of something I said :c".format(
member.display_name))
def setup(bot): def setup(bot):

View file

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

View file

@ -85,7 +85,7 @@ class Interaction:
self.battles = {} self.battles = {}
def user_battling(self, ctx, player2=None): 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 no one is battling, obviously the user is not battling
if battling is None: if battling is None:
@ -101,14 +101,14 @@ class Interaction:
# Handles removing the author from the dictionary of battles # Handles removing the author from the dictionary of battles
def battling_off(self, ctx): 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 player_id = ctx.message.author.id
# Create a new dictionary, exactly the way the last one was setup # Create a new dictionary, exactly the way the last one was setup
# But don't include any that have the author's ID # 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 self.battles[ctx.message.guild.id] = {p1: p2 for p1, p2 in battles.items() if
not p2 == player_id and not p1 == player_id} 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) @utils.custom_perms(send_messages=True)
async def hug(self, ctx, user: discord.Member = None): async def hug(self, ctx, user: discord.Member = None):
"""Makes me hug a person! """Makes me hug a person!
@ -119,9 +119,9 @@ class Interaction:
user = ctx.message.author user = ctx.message.author
fmt = random.SystemRandom().choice(hugs) 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) @utils.custom_perms(send_messages=True)
async def avatar(self, ctx, member: discord.Member = None): 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) """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 member = ctx.message.author
url = member.avatar_url 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) file = await utils.download_image(url)
if file is None: if file is None:
await self.bot.say(url) await ctx.send(url)
else: else:
if '.gif' in url: if '.gif' in url:
filename = 'avatar.gif' filename = 'avatar.gif'
else: else:
filename = 'avatar.webp' filename = 'avatar.webp'
file = utils.convert_to_jpeg(file) file = utils.convert_to_jpeg(file)
await self.bot.upload(file, filename=filename) await ctx.send(file=file, filename=filename)
else: 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) @commands.cooldown(1, 180, BucketType.user)
@utils.custom_perms(send_messages=True) @utils.custom_perms(send_messages=True)
async def battle(self, ctx, player2: discord.Member): async def battle(self, ctx, player2: discord.Member):
@ -157,29 +157,29 @@ class Interaction:
RESULT: A battle to the death""" RESULT: A battle to the death"""
if ctx.message.author.id == player2.id: if ctx.message.author.id == player2.id:
ctx.command.reset_cooldown(ctx) 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 return
if self.bot.user.id == player2.id: if self.bot.user.id == player2.id:
ctx.command.reset_cooldown(ctx) 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 return
if self.user_battling(ctx, player2): if self.user_battling(ctx, player2):
ctx.command.reset_cooldown(ctx) 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 return
# Add the author and player provided in a new battle # 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 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" \ fmt = "{0.message.author.mention} has challenged you to a battle {1.mention}\n" \
"{0.prefix}accept or {0.prefix}decline" "{0.prefix}accept or {0.prefix}decline"
# Add a call to turn off battling, if the battle is not accepted/declined in 3 minutes # 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) 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) @utils.custom_perms(send_messages=True)
async def accept(self, ctx): async def accept(self, ctx):
"""Accepts the battle challenge """Accepts the battle challenge
@ -188,13 +188,13 @@ class Interaction:
RESULT: Hopefully the other person's death""" RESULT: Hopefully the other person's death"""
# This is a check to make sure that the author is the one being BATTLED # This is a check to make sure that the author is the one being BATTLED
# And not the one that started the battle # 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] p1 = [p1_id for p1_id, p2_id in battles.items() if p2_id == ctx.message.author.id]
if len(p1) == 0: 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 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 battleP2 = ctx.message.author
# Get a random win message from our list # 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 # 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 # All we need to do is change what order the challengers are printed/added as a paramater
if random.SystemRandom().randint(0, 1): 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) await utils.update_records('battle_records', battleP1, battleP2)
else: 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) 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) @utils.custom_perms(send_messages=True)
async def decline(self, ctx): async def decline(self, ctx):
"""Declines the battle challenge """Declines the battle challenge
@ -220,23 +220,23 @@ class Interaction:
RESULT: You chicken out""" RESULT: You chicken out"""
# This is a check to make sure that the author is the one being BATTLED # This is a check to make sure that the author is the one being BATTLED
# And not the one that started the battle # 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] p1 = [p1_id for p1_id, p2_id in battles.items() if p2_id == ctx.message.author.id]
if len(p1) == 0: 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 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 battleP2 = ctx.message.author
# There's no need to update the stats for the members if they declined the battle # There's no need to update the stats for the members if they declined the battle
self.battling_off(ctx) 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) @commands.cooldown(1, 180, BucketType.user)
@utils.custom_perms(send_messages=True) @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 """Boops the mentioned person
EXAMPLE: !boop @OtherPerson EXAMPLE: !boop @OtherPerson
@ -244,18 +244,18 @@ class Interaction:
booper = ctx.message.author booper = ctx.message.author
if boopee is None: if boopee is None:
ctx.command.reset_cooldown(ctx) 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 return
# To keep formatting easier, keep it either "" or the message with a space in front # To keep formatting easier, keep it either "" or the message with a space in front
if message is not None: if message is not None:
message = " " + message message = " " + message
if boopee.id == booper.id: if boopee.id == booper.id:
ctx.command.reset_cooldown(ctx) 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 return
if boopee.id == self.bot.user.id: if boopee.id == self.bot.user.id:
ctx.command.reset_cooldown(ctx) 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 return
r_filter = {'member_id': booper.id} r_filter = {'member_id': booper.id}
@ -275,7 +275,7 @@ class Interaction:
amount = 1 amount = 1
fmt = "{0.mention} has just booped {1.mention}{3}! That's {2} times now!" 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): def setup(bot):

View file

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

View file

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

View file

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

View file

@ -1,13 +1,9 @@
from .utils import config from . import utils
from .utils import checks
from .utils import images
from discord.ext import commands from discord.ext import commands
import discord 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 # 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 # 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 # 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', check_g_stats = ["eliminations", "deaths", 'kpd', 'wins', 'losses', 'time_played',
'cards', 'damage_done', 'healing_done', 'multikills'] 'cards', 'damage_done', 'healing_done', 'multikills']
check_o_stats = ['wins'] check_o_stats = ['wins']
MAX_RETRIES = 5
class Overwatch: class Overwatch:
@ -23,29 +18,6 @@ class Overwatch:
def __init__(self, bot): def __init__(self, bot):
self.bot = 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) @commands.group(no_pm=True)
async def ow(self): async def ow(self):
@ -55,29 +27,31 @@ class Overwatch:
Capitalization also matters""" Capitalization also matters"""
pass pass
@ow.command(name="stats", pass_context=True) @ow.command(name="stats")
@checks.custom_perms(send_messages=True) @utils.custom_perms(send_messages=True)
async def ow_stats(self, ctx, user: discord.Member = None, hero: str = ""): async def ow_stats(self, ctx, user: discord.Member = None, hero: str = ""):
"""Prints out a basic overview of a member's stats """Prints out a basic overview of a member's stats
Provide a hero after the member to get stats for that specific hero Provide a hero after the member to get stats for that specific hero
EXAMPLE: !ow stats @OtherPerson Junkrat EXAMPLE: !ow stats @OtherPerson Junkrat
RESULT: Whether or not you should unfriend this person because they're a dirty rat""" 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 user = user or ctx.message.author
r_filter = {'member_id': user.id} 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: 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 return
# This API sometimes takes a while to look up information, so send a message saying we're processing # 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'] bt = ow_stats[0]['battletag']
if hero == "": if hero == "":
# If no hero was provided, we just want the base stats for a player # 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] region = [x for x in data.keys() if data[x] is not None][0]
stats = data[region]['stats']['quickplay'] stats = data[region]['stats']['quickplay']
@ -86,8 +60,8 @@ class Overwatch:
else: else:
# If there was a hero provided, search for a user's data on that hero # If there was a hero provided, search for a user's data on that hero
hero = hero.lower().replace('-', '') hero = hero.lower().replace('-', '')
endpoint = "{}/heroes".format(bt) url = BASE_URL + "{}/heroes".format(bt)
data = await self._request(None, endpoint) data = await utils.request(url)
region = [x for x in data.keys() if data[x] is not None][0] region = [x for x in data.keys() if data[x] is not None][0]
stats = data[region]['heroes']['stats']['quickplay'].get(hero) stats = data[region]['heroes']['stats']['quickplay'].get(hero)
@ -95,7 +69,7 @@ class Overwatch:
if stats is None: if stats is None:
fmt = "I couldn't find data with that hero, make sure that is a valid hero, " \ 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) "otherwise {} has never used the hero {} before!".format(user.display_name, hero)
await self.bot.say(fmt) await ctx.send(fmt)
return return
# Same list comprehension as before # Same list comprehension as before
@ -104,55 +78,57 @@ class Overwatch:
for k, r in stats['hero_stats'].items(): for k, r in stats['hero_stats'].items():
output_data.append((k.title().replace("_", " "), r)) output_data.append((k.title().replace("_", " "), r))
try: try:
banner = await images.create_banner(user, "Overwatch", output_data) banner = await utils.create_banner(user, "Overwatch", output_data)
await self.bot.upload(banner) await ctx.send(file=banner)
except (FileNotFoundError, discord.Forbidden): except (FileNotFoundError, discord.Forbidden):
fmt = "\n".join("{}: {}".format(k, r) for k, r in output_data) 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") @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): async def add(self, ctx, bt: str):
"""Saves your battletag for looking up information """Saves your battletag for looking up information
EXAMPLE: !ow add Username#1234 EXAMPLE: !ow add Username#1234
RESULT: Your battletag is now saved""" RESULT: Your battletag is now saved"""
await ctx.message.channel.trigger_typing()
# Battletags are normally provided like name#id # Battletags are normally provided like name#id
# However the API needs this to be a -, so repliace # with - if it exists # However the API needs this to be a -, so repliace # with - if it exists
bt = bt.replace("#", "-") bt = bt.replace("#", "-")
r_filter = {'member_id': ctx.message.author.id} 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 # 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 # 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 # If it's not, let them know exactly how to format their tag
endpoint = "{}/stats".format(bt) url = BASE_URL + "{}/stats".format(bt)
data = await self._request(None, endpoint) data = await utils.request(url)
if data is None: if data is None:
await self.bot.say("Profile does not exist! Battletags are picky, " await ctx.send("Profile does not exist! Battletags are picky, "
"format needs to be `user#xxxx`. Capitalization matters") "format needs to be `user#xxxx`. Capitalization matters")
return return
# Now just save the battletag # Now just save the battletag
entry = {'member_id': ctx.message.author.id, 'battletag': bt} entry = {'member_id': ctx.message.author.id, 'battletag': bt}
update = {'battletag': bt} update = {'battletag': bt}
# Try adding this first, if that fails, update the saved entry # Try adding this first, if that fails, update the saved entry
if not await config.add_content('overwatch', entry, r_filter): if not await utils.add_content('overwatch', entry, r_filter):
await config.update_content('overwatch', update, r_filter) await utils.update_content('overwatch', update, r_filter)
await self.bot.say("I have just saved your battletag {}".format(ctx.message.author.mention)) await ctx.send("I have just saved your battletag {}".format(ctx.message.author.mention))
@ow.command(pass_context=True, name="delete", aliases=['remove']) @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): async def delete(self, ctx):
"""Removes your battletag from the records """Removes your battletag from the records
EXAMPLE: !ow delete EXAMPLE: !ow delete
RESULT: Your battletag is no longer saved""" RESULT: Your battletag is no longer saved"""
r_filter = {'member_id': ctx.message.author.id} r_filter = {'member_id': ctx.message.author.id}
if await config.remove_content('overwatch', r_filter): if await utils.remove_content('overwatch', r_filter):
await self.bot.say("I no longer have your battletag saved {}".format(ctx.message.author.mention)) await ctx.send("I no longer have your battletag saved {}".format(ctx.message.author.mention))
else: 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): def setup(bot):

View file

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

View file

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

View file

@ -1,6 +1,7 @@
from discord.ext import commands from discord.ext import commands
from .utils import config import discord
from .utils import checks
from . import utils
import random import random
import pendulum import pendulum
@ -27,13 +28,13 @@ class Raffle:
async def check_raffles(self): async def check_raffles(self):
# This is used to periodically check the current raffles, and see if they have ended yet # 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 # 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: if raffles is None:
return return
for raffle in raffles: 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 # Check to see if this cog can find the server in question
if server is None: 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 # 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 # We don't have to wait for it however, so create a task for it
r_filter = {'id': raffle_id} 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: try:
await self.bot.send_message(server, fmt) await server.send(fmt)
except discord.Forbidden: except discord.Forbidden:
pass pass
@commands.command(pass_context=True, no_pm=True) @commands.command(no_pm=True)
@checks.custom_perms(send_messages=True) @utils.custom_perms(send_messages=True)
async def raffles(self, ctx): async def raffles(self, ctx):
"""Used to print the current running raffles on the server """Used to print the current running raffles on the server
EXAMPLE: !raffles EXAMPLE: !raffles
RESULT: A list of the raffles setup on this server""" RESULT: A list of the raffles setup on this server"""
r_filter = {'server_id': ctx.message.server.id} r_filter = {'server_id': ctx.message.guild.id}
raffles = await config.get_content('raffles', r_filter) raffles = await utils.get_content('raffles', r_filter)
if raffles is None: 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 return
fmt = "\n\n".join("**Raffle:** {}\n**Title:** {}\n**Total Entrants:** {}\n**Ends:** {} UTC".format( fmt = "\n\n".join("**Raffle:** {}\n**Title:** {}\n**Total Entrants:** {}\n**Ends:** {} UTC".format(
@ -98,10 +99,10 @@ class Raffle:
raffle['title'], raffle['title'],
len(raffle['entrants']), len(raffle['entrants']),
raffle['expires']) for num, raffle in enumerate(raffles)) 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) @commands.group(no_pm=True, invoke_without_command=True)
@checks.custom_perms(send_messages=True) @utils.custom_perms(send_messages=True)
async def raffle(self, ctx, raffle_id: int = 0): async def raffle(self, ctx, raffle_id: int = 0):
"""Used to enter a raffle running on this server """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 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!""" RESULT: You've entered the first raffle!"""
# Lets let people use 1 - (length of raffles) and handle 0 base ourselves # Lets let people use 1 - (length of raffles) and handle 0 base ourselves
raffle_id -= 1 raffle_id -= 1
r_filter = {'server_id': ctx.message.server.id} r_filter = {'server_id': ctx.message.guild.id}
author = ctx.message.author author = ctx.message.author
raffles = await config.get_content('raffles', r_filter) raffles = await utils.get_content('raffles', r_filter)
if raffles is None: 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 return
raffle_count = len(raffles) raffle_count = len(raffles)
@ -125,38 +126,38 @@ class Raffle:
entrants = raffles[0]['entrants'] entrants = raffles[0]['entrants']
# Lets make sure that the user hasn't already entered the raffle # Lets make sure that the user hasn't already entered the raffle
if author.id in entrants: 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 return
entrants.append(author.id) entrants.append(author.id)
# Since we have no good thing to filter things off of, lets use the internal rethinkdb id # Since we have no good thing to filter things off of, lets use the internal rethinkdb id
r_filter = {'id': raffles[0]['id']} r_filter = {'id': raffles[0]['id']}
update = {'entrants': entrants} update = {'entrants': entrants}
await config.update_content('raffles', update, r_filter) await utils.update_content('raffles', update, r_filter)
await self.bot.say("{} you have just entered the raffle!".format(author.mention)) await ctx.send("{} you have just entered the raffle!".format(author.mention))
# Otherwise, make sure the author gave a valid raffle_id # Otherwise, make sure the author gave a valid raffle_id
elif raffle_id in range(raffle_count - 1): elif raffle_id in range(raffle_count - 1):
entrants = raffles[raffle_id]['entrants'] entrants = raffles[raffle_id]['entrants']
# Lets make sure that the user hasn't already entered the raffle # Lets make sure that the user hasn't already entered the raffle
if author.id in entrants: 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 return
entrants.append(author.id) entrants.append(author.id)
# Since we have no good thing to filter things off of, lets use the internal rethinkdb 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']} r_filter = {'id': raffles[raffle_id]['id']}
update = {'entrants': entrants} update = {'entrants': entrants}
await config.update_content('raffles', update, r_filter) await utils.update_content('raffles', update, r_filter)
await self.bot.say("{} you have just entered the raffle!".format(author.mention)) await ctx.send("{} you have just entered the raffle!".format(author.mention))
else: else:
fmt = "Please provide a valid raffle ID, as there are more than one setup on the server! " \ 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( "There are currently `{}` raffles running, use {}raffles to view the current running raffles".format(
raffle_count, ctx.prefix) 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']) @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): async def raffle_create(self, ctx):
"""This is used in order to create a new server raffle """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""" RESULT: A follow-along for setting up a new raffle"""
author = ctx.message.author author = ctx.message.author
server = ctx.message.server server = ctx.message.guild
channel = ctx.message.channel channel = ctx.message.channel
now = pendulum.utcnow() 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!") "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: if msg is None:
await self.bot.say("You took too long! >:c") await ctx.send("You took too long! >:c")
return return
title = msg.content 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? " \ 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 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" "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 # 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 def check(m):
msg = await self.bot.wait_for_message(author=author, channel=channel, timeout=120, check=check) 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: if msg is None:
await self.bot.say("You took too long! >:c") await ctx.send("You took too long! >:c")
return return
# Lets get the length provided, based on the number and type passed # 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 # Now lets ensure this meets our min/max
if "minute" in term and (num < 10 or num > 129600): 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") "Length provided out of range! The minimum for this is 10 minutes, and the maximum is 3 months")
return return
elif "hour" in term and num > 2160: 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") "Length provided out of range! The minimum for this is 10 minutes, and the maximum is 3 months")
return return
elif "day" in term and num > 90: 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") "Length provided out of range! The minimum for this is 10 minutes, and the maximum is 3 months")
return return
elif "week" in term and num > 12: 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") "Length provided out of range! The minimum for this is 10 minutes, and the maximum is 3 months")
return return
elif "month" in term and num > 3: 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") "Length provided out of range! The minimum for this is 10 minutes, and the maximum is 3 months")
return return
# Pendulum only accepts the plural version of terms, lets make sure this is added # 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 # 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} payload = {term: num}
expires = now.add(**payload) expires = now.add(**payload)
@ -231,8 +239,8 @@ class Raffle:
'server_id': server.id} 'server_id': server.id}
# We don't want to pass a filter to this, because we can have multiple raffles per server # 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 utils.add_content('raffles', entry)
await self.bot.say("I have just saved your new raffle!") await ctx.send("I have just saved your new raffle!")
def setup(bot): def setup(bot):

View file

@ -1,6 +1,7 @@
from discord.ext import commands from discord.ext import commands
import discord import discord
from .utils import checks
from . import utils
import re import re
import asyncio import asyncio
@ -12,8 +13,8 @@ class Roles:
def __init__(self, bot): def __init__(self, bot):
self.bot = bot self.bot = bot
@commands.group(aliases=['roles'], invoke_without_command=True, no_pm=True, pass_context=True) @commands.group(aliases=['roles'], invoke_without_command=True, no_pm=True)
@checks.custom_perms(send_messages=True) @utils.custom_perms(send_messages=True)
async def role(self, ctx): async def role(self, ctx):
"""This command can be used to modify the roles on the server. """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 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""" RESULT: A list of all your roles"""
# Simply get a list of all roles in this server and send them # 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] 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) @role.command(name='remove', no_pm=True)
@checks.custom_perms(manage_roles=True) @utils.custom_perms(manage_roles=True)
async def remove_role(self, ctx): async def remove_role(self, ctx):
"""Use this to remove roles from a number of members """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""" 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 # 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: 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 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] server_roles = [role for role in ctx.message.server.roles if not role.is_everyone]
# First get the list of all mentioned users # First get the list of all mentioned users
members = ctx.message.mentions 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 no users are mentioned, ask the author for a list of the members they want to remove the role from
if len(members) == 0: if len(members) == 0:
await self.bot.say("Please provide the list of members you want to remove a role from") await ctx.send("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) msg = await self.bot.wait_for('message', check=check, timeout=60)
if msg is None: 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 return
if len(msg.mentions) == 0: 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 return
# Override members if everything has gone alright, and then continue # Override members if everything has gone alright, and then continue
members = msg.mentions members = msg.mentions
# This allows the user to remove multiple roles from the list of users, if they want. # 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. " 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. " "Make sure the roles, if more than one is provided, are separate by commas. "
"Here is a list of this server's roles:" "Here is a list of this server's roles:"
"```\n{}```".format("\n".join([r.name for r in server_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: 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 return
# Split the content based on commas, using regex so we can split if a space was not provided or if it was # 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 no valid roles were given, let them know that and return
if len(roles) == 0: 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 return
# Otherwise, remove the roles from each member given # Otherwise, remove the roles from each member given
for member in members: for member in members:
await self.bot.remove_roles(member, *roles) await self.bot.remove_roles(member, *roles)
await self.bot.say("I have just removed the following roles:```\n{}``` from the following 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]))) "```\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) @role.command(name='add', no_pm=True)
@checks.custom_perms(manage_roles=True) @utils.custom_perms(manage_roles=True)
async def add_role(self, ctx): async def add_role(self, ctx):
"""Use this to add a role to multiple members. """Use this to add a role to multiple members.
Provide the list of members, and I'll ask for the role 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""" 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 # 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: 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 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. # 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] server_roles = [role for role in ctx.message.server.roles if not role.is_everyone]
members = ctx.message.mentions members = ctx.message.mentions
if len(members) == 0: if len(members) == 0:
await self.bot.say("Please provide the list of members you want to add a role to") await ctx.send("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) msg = await self.bot.wait_for('message', check=check, timeout=60)
if msg is None: 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 return
if len(msg.mentions) == 0: 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 return
members = msg.mentions members = msg.mentions
await self.bot.say("Alright, please provide the roles you would like to add to this member. " 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. " "Make sure the roles, if more than one is provided, are separate by commas. "
"Here is a list of this server's roles:" "Here is a list of this server's roles:"
"```\n{}```".format("\n".join([r.name for r in server_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: 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 return
role_names = re.split(', ?', msg.content) role_names = re.split(', ?', msg.content)
roles = [] roles = []
@ -127,16 +131,16 @@ class Roles:
roles.append(_role) roles.append(_role)
if len(roles) == 0: 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 return
for member in members: for member in members:
await self.bot.add_roles(member, *roles) await member.add_roles(*roles)
await self.bot.say("I have just added the following roles:```\n{}``` to the following members:" 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]))) "```\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) @role.command(name='delete', no_pm=True)
@checks.custom_perms(manage_roles=True) @utils.custom_perms(manage_roles=True)
async def delete_role(self, ctx, *, role: discord.Role = None): async def delete_role(self, ctx, *, role: discord.Role = None):
"""This command can be used to delete one of the roles from the server """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""" RESULT: No more role called StupidRole"""
# No use in running through everything if the bot cannot manage roles # 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: 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 return
# If no role was given, get the current roles on the server and ask which ones they'd like to remove # 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: if role is None:
server_roles = [role for role in ctx.message.server.roles if not role.is_everyone] 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:" "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]))) "```\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 # 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 # 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 # We can use that fact to simply use just that as our check
check = lambda m: discord.utils.get(server_roles, name=m.content) def check(m):
msg = await self.bot.wait_for_message(author=ctx.message.author, channel=ctx.message.channel, check=check) 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: 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 return
# If we have gotten here, based on our previous check, we know that the content provided is a valid role. # 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 # Due to that, no need for any error checking here
role = discord.utils.get(server_roles, name=msg.content) role = discord.utils.get(server_roles, name=msg.content)
await self.bot.delete_role(ctx.message.server, role) await role.delete()
await self.bot.say("I have just removed the role {} from this server".format(role.name)) 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) @role.command(name='create', no_pm=True)
@checks.custom_perms(manage_roles=True) @utils.custom_perms(manage_roles=True)
async def create_role(self, ctx): async def create_role(self, ctx):
"""This command can be used to create a new role for this server """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 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""" RESULT: A follow along in order to create a new role"""
# No use in running through everything if the bot cannot create the 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: 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 return
# Save a couple variables that will be used repeatedly # Save a couple variables that will be used repeatedly
@ -190,48 +199,64 @@ class Roles:
channel = ctx.message.channel channel = ctx.message.channel
# A couple checks that will be used in the wait_for_message's # 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 def num_seperated_check(m):
yes_no_check = lambda m: re.search("(yes|no)", m.content.lower()) is not None if m.author == author and m.channel == channel:
members_check = lambda m: len(m.mentions) > 0 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 # 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") "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: 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 return
name = msg.content name = msg.content
# Print a list of all the permissions available, then ask for which ones need to be active on this new role # 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)] 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)) 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 " 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" "the numbers, seperated by commas, of the permissions you want this role to have.\n"
"```\n{}```".format(fmt)) "```\n{}```".format(fmt))
# For this we're going to give a couple extra minutes before we timeout # 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 # 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: 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 return
# Check if any integer's were provided that are within the length of the list of permissions # 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)] 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 # 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)") 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, author=author, channel=channel, check=yes_no_check) msg = await self.bot.wait_for('message', timeout=60.0, check=yes_no_check)
if msg is None: 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 return
hoist = True if msg.content.lower() == "yes" else False hoist = True if msg.content.lower() == "yes" else False
# Check if this role should be able to be mentioned # 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)") await ctx.send("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) msg = await self.bot.wait_for('message', timeout=60.0, check=yes_no_check)
if msg is None: 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 return
mentionable = True if msg.content.lower() == "yes" else False mentionable = True if msg.content.lower() == "yes" else False
@ -248,21 +273,21 @@ class Roles:
'mentionable': mentionable 'mentionable': mentionable
} }
# Create the role, and wait a second, sometimes it goes too quickly and we get a role with 'new role' to print # 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 asyncio.sleep(1)
await self.bot.say("We did it! You just created the new role {}\nIf you want to add this role" 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)) " 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) 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 # 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: if msg is None:
return return
# Otherwise members were mentioned, add the new role to them now # Otherwise members were mentioned, add the new role to them now
for member in msg.mentions: 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) 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): def setup(bot):

View file

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

View file

@ -1,8 +1,9 @@
from discord.ext import commands
from .utils import config
from .utils import checks
import re import re
from discord.ext import commands
from . import utils
class Tags: class Tags:
"""This class contains all the commands for custom tags""" """This class contains all the commands for custom tags"""
@ -10,39 +11,39 @@ class Tags:
def __init__(self, bot): def __init__(self, bot):
self.bot = bot self.bot = bot
@commands.command(pass_context=True, no_pm=True) @commands.command(no_pm=True)
@checks.custom_perms(send_messages=True) @utils.custom_perms(send_messages=True)
async def tags(self, ctx): async def tags(self, ctx):
"""Prints all the custom tags that this server currently has """Prints all the custom tags that this server currently has
EXAMPLE: !tags EXAMPLE: !tags
RESULT: All tags setup on this server""" 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 # Simple generator that adds a tag to the list to print, if the tag is for this server
try: try:
fmt = "\n".join("{}".format(tag['tag']) for tag in tags) 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: 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) @commands.group(invoke_without_command=True, no_pm=True)
@checks.custom_perms(send_messages=True) @utils.custom_perms(send_messages=True)
async def tag(self, ctx, *, tag: str): async def tag(self, ctx, *, tag: str):
"""This can be used to call custom tags """This can be used to call custom tags
The format to call a custom tag is !tag <tag> The format to call a custom tag is !tag <tag>
EXAMPLE: !tag butts EXAMPLE: !tag butts
RESULT: Whatever you setup for the butts tag!!""" RESULT: Whatever you setup for the butts tag!!"""
r_filter = lambda row: (row['server_id'] == ctx.message.server.id) & (row['tag'] == tag) r_filter = lambda row: (row['server_id'] == ctx.message.guild.id) & (row['tag'] == tag)
tags = await config.get_content('tags', r_filter) tags = await utils.get_content('tags', r_filter)
if tags is None: if tags is None:
await self.bot.say('That tag does not exist!') await ctx.send('That tag does not exist!')
return return
# We shouldn't ever have two tags of the same name, so just get the first result # 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) @tag.command(name='add', aliases=['create', 'start'], no_pm=True)
@checks.custom_perms(kick_members=True) @utils.custom_perms(kick_members=True)
async def add_tag(self, ctx, *, result: str): async def add_tag(self, ctx, *, result: str):
"""Use this to add a new tag that can be used in this server """Use this to add a new tag that can be used in this server
Format to add a tag is !tag add <tag> - <result> Format to add a tag is !tag add <tag> - <result>
@ -56,43 +57,43 @@ class Tags:
tag_result = match.group(2).strip() tag_result = match.group(2).strip()
# Next two checks are just to ensure there was a valid match found # Next two checks are just to ensure there was a valid match found
except AttributeError: except AttributeError:
await self.bot.say( await ctx.send(
"Please provide the format for the tag in: {}tag add <tag> - <result>".format(ctx.prefix)) "Please provide the format for the tag in: {}tag add <tag> - <result>".format(ctx.prefix))
return return
# If our regex failed to find the content (aka they provided the wrong format) # If our regex failed to find the content (aka they provided the wrong format)
if len(tag) == 0 or len(tag_result) == 0: 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)) "Please provide the format for the tag in: {}tag add <tag> - <result>".format(ctx.prefix))
return return
# Make sure the tag created does not mention everyone/here # Make sure the tag created does not mention everyone/here
if '@everyone' in tag_result or '@here' in tag_result: 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 return
entry = {'server_id': ctx.message.server.id, 'tag': tag, 'result': tag_result} entry = {'server_id': ctx.message.guild.id, 'tag': tag, 'result': tag_result}
r_filter = lambda row: (row['server_id'] == ctx.message.server.id) & (row['tag'] == tag) 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 # 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): if await utils.add_content('tags', entry, r_filter):
await self.bot.say( await ctx.send(
"I have just added the tag `{0}`! You can call this tag by entering !tag {0}".format(tag)) "I have just added the tag `{0}`! You can call this tag by entering !tag {0}".format(tag))
else: else:
await config.update_content('tags', entry, r_filter) await utils.update_content('tags', entry, r_filter)
await self.bot.say( await ctx.send(
"I have just updated the tag `{0}`! You can call this tag by entering !tag {0}".format(tag)) "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) @tag.command(name='delete', aliases=['remove', 'stop'], no_pm=True)
@checks.custom_perms(kick_members=True) @utils.custom_perms(kick_members=True)
async def del_tag(self, ctx, *, tag: str): async def del_tag(self, ctx, *, tag: str):
"""Use this to remove a tag from use for this server """Use this to remove a tag from use for this server
Format to delete a tag is !tag delete <tag> Format to delete a tag is !tag delete <tag>
EXAMPLE: !tag delete stupid_tag EXAMPLE: !tag delete stupid_tag
RESULT: Deletes that stupid tag""" RESULT: Deletes that stupid tag"""
r_filter = lambda row: (row['server_id'] == ctx.message.server.id) & (row['tag'] == tag) r_filter = lambda row: (row['server_id'] == ctx.message.guild.id) & (row['tag'] == tag)
if await config.remove_content('tags', r_filter): if await utils.remove_content('tags', r_filter):
await self.bot.say('I have just removed the tag `{}`'.format(tag)) await ctx.send('I have just removed the tag `{}`'.format(tag))
else: 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)) "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 from discord.ext import commands
import discord import discord
from .utils import config from . import utils
from .utils import checks
from .utils import utilities
import re import re
import random import random
@ -110,8 +108,8 @@ class TicTacToe:
# Return whoever is x's so that we know who is going first # Return whoever is x's so that we know who is going first
return self.boards[server_id].challengers['x'] return self.boards[server_id].challengers['x']
@commands.group(pass_context=True, aliases=['tic', 'tac', 'toe'], no_pm=True, invoke_without_command=True) @commands.group(aliases=['tic', 'tac', 'toe'], no_pm=True, invoke_without_command=True)
@checks.custom_perms(send_messages=True) @utils.custom_perms(send_messages=True)
async def tictactoe(self, ctx, *, option: str): async def tictactoe(self, ctx, *, option: str):
"""Updates the current server's tic-tac-toe board """Updates the current server's tic-tac-toe board
You obviously need to be one of the players to use this You obviously need to be one of the players to use this
@ -121,15 +119,15 @@ class TicTacToe:
EXAMPLE: !tictactoe middle top EXAMPLE: !tictactoe middle top
RESULT: Your piece is placed in the very top space, in the middle""" RESULT: Your piece is placed in the very top space, in the middle"""
player = ctx.message.author 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 # Need to make sure the board exists before allowing someone to play
if not board: 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 return
# Now just make sure the person can play, this will fail if o's are up and x tries to play # 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 # Or if someone else entirely tries to play
if not board.can_play(player): if not board.can_play(player):
await self.bot.say("You cannot play right now!") await ctx.send("You cannot play right now!")
return return
# Search for the positions in the option given, the actual match doesn't matter, just need to check if it exists # 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 # Just a bit of logic to ensure nothing that doesn't make sense is given
if top and bottom: 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 return
if left and right: 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 return
# Make sure at least something was given # Make sure at least something was given
if not top and not bottom and not left and not right and not middle: 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 return
x = 0 x = 0
@ -181,7 +179,7 @@ class TicTacToe:
# board.update will handle which letter is placed # 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 it returns false however, then someone has already played in that spot and nothing was updated
if not board.update(x, y): if not board.update(x, y):
await self.bot.say("Someone has already played there!") await ctx.send("Someone has already played there!")
return return
# Next check if there's a winner # Next check if there's a winner
winner = board.check() winner = board.check()
@ -189,25 +187,25 @@ class TicTacToe:
# Get the loser based on whether or not the winner is x's # 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 # 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'] 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, await ctx.send("{} has won this game of TicTacToe, better luck next time {}".format(winner.display_name,
loser.display_name)) loser.display_name))
# Handle updating ratings based on the winner and loser # 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 # 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: 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 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(): if board.full():
await self.bot.say("This game has ended in a tie!") await ctx.send("This game has ended in a tie!")
del self.boards[ctx.message.server.id] 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 # If no one has won, and the game has not ended in a tie, print the new updated board
else: else:
player_turn = board.challengers.get('x') if board.X_turn else board.challengers.get('o') 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) 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) @tictactoe.command(name='start', aliases=['challenge', 'create'], no_pm=True)
@checks.custom_perms(send_messages=True) @utils.custom_perms(send_messages=True)
async def start_game(self, ctx, player2: discord.Member): async def start_game(self, ctx, player2: discord.Member):
"""Starts a game of tictactoe with another player """Starts a game of tictactoe with another player
@ -216,32 +214,32 @@ class TicTacToe:
player1 = ctx.message.author player1 = ctx.message.author
# For simplicities sake, only allow one game on a server at a time. # 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 # 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: if self.boards.get(ctx.message.guild.id) is not None:
await self.bot.say("Sorry but only one Tic-Tac-Toe game can be running per server!") await ctx.send("Sorry but only one Tic-Tac-Toe game can be running per server!")
return return
# Make sure we're not being challenged, I always win anyway # Make sure we're not being challenged, I always win anyway
if player2 == ctx.message.server.me: if player2 == ctx.message.guild.me:
await self.bot.say("You want to play? Alright lets play.\n\nI win, so quick you didn't even notice it.") await ctx.send("You want to play? Alright lets play.\n\nI win, so quick you didn't even notice it.")
return return
if player2 == player1: 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 return
# Create the board and return who has been decided to go first # 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) fmt = "A tictactoe game has just started between {} and {}".format(player1.display_name, player2.display_name)
# Print the board too just because # 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 # We don't need to do anything weird with assigning x_player to something
# it is already a member object, just use it # 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! " \ 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) .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) @tictactoe.command(name='delete', aliases=['stop', 'remove', 'end'], no_pm=True)
@checks.custom_perms(kick_members=True) @utils.custom_perms(kick_members=True)
async def stop_game(self, ctx): async def stop_game(self, ctx):
"""Force stops a game of tictactoe """Force stops a game of tictactoe
This should realistically only be used in a situation like one player leaves This should realistically only be used in a situation like one player leaves
@ -249,12 +247,12 @@ class TicTacToe:
EXAMPLE: !tictactoe stop EXAMPLE: !tictactoe stop
RESULT: No more tictactoe!""" RESULT: No more tictactoe!"""
if self.boards.get(ctx.message.server.id) is None: if self.boards.get(ctx.message.guild.id) is None:
await self.bot.say("There are no tictactoe games running on this server!") await ctx.send("There are no tictactoe games running on this server!")
return return
del self.boards[ctx.message.server.id] del self.boards[ctx.message.guild.id]
await self.bot.say("I have just stopped the game of TicTacToe, a new should be able to be started now!") await ctx.send("I have just stopped the game of TicTacToe, a new should be able to be started now!")
def setup(bot): def setup(bot):

View file

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

View file

@ -24,9 +24,10 @@ async def db_check():
except r.errors.ReqlDriverError: except r.errors.ReqlDriverError:
print("Cannot connect to the RethinkDB instance with the following information: {}".format(db_opts)) 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"\ 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") " RethinkDB instance, and you have provided the correct database information in config.yml")
quit() quit()
return
# Get the current databases and check if the one we need is there # Get the current databases and check if the one we need is there
dbs = await r.db_list().run(conn) dbs = await r.db_list().run(conn)
@ -48,6 +49,7 @@ async def db_check():
await r.table_create(table).run(conn) await r.table_create(table).run(conn)
print("Done checking tables!") print("Done checking tables!")
def is_owner(ctx): def is_owner(ctx):
return ctx.message.author.id in config.owner_ids return ctx.message.author.id in config.owner_ids
@ -80,10 +82,3 @@ def custom_perms(**perms):
predicate.perms = perms predicate.perms = perms
return commands.check(predicate) 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 asyncio
import discord import discord
class CannotPaginate(Exception): class CannotPaginate(Exception):
pass pass
class Pages: class Pages:
"""Implements a paginator that queries the user for the """Implements a paginator that queries the user for the
pagination interface. pagination interface.
@ -13,6 +15,7 @@ class Pages:
If the user does not reply within 2 minutes, the pagination If the user does not reply within 2 minutes, the pagination
interface exits automatically. interface exits automatically.
""" """
def __init__(self, bot, *, message, entries, per_page=10): def __init__(self, bot, *, message, entries, per_page=10):
self.bot = bot self.bot = bot
self.entries = entries self.entries = entries
@ -30,12 +33,12 @@ class Pages:
('\N{BLACK LEFT-POINTING TRIANGLE}', self.previous_page), ('\N{BLACK LEFT-POINTING TRIANGLE}', self.previous_page),
('\N{BLACK RIGHT-POINTING TRIANGLE}', self.next_page), ('\N{BLACK RIGHT-POINTING TRIANGLE}', self.next_page),
('\N{BLACK RIGHT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR}', self.last_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{BLACK SQUARE FOR STOP}', self.stop_pages),
('\N{INFORMATION SOURCE}', self.show_help), ('\N{INFORMATION SOURCE}', self.show_help),
] ]
server = self.message.server server = self.message.guild
if server is not None: if server is not None:
self.permissions = self.message.channel.permissions_for(server.me) self.permissions = self.message.channel.permissions_for(server.me)
else: else:
@ -59,11 +62,11 @@ class Pages:
if not self.paginating: if not self.paginating:
self.embed.description = '\n'.join(p) 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: if not first:
self.embed.description = '\n'.join(p) self.embed.description = '\n'.join(p)
await self.bot.edit_message(self.message, embed=self.embed) await self.message.edit(embed=self.embed)
return return
# verify we can actually use the pagination session # verify we can actually use the pagination session
@ -76,7 +79,7 @@ class Pages:
p.append('') p.append('')
p.append('Confused? React with \N{INFORMATION SOURCE} for more info.') p.append('Confused? React with \N{INFORMATION SOURCE} for more info.')
self.embed.description = '\n'.join(p) self.embed.description = '\n'.join(p)
self.message = await self.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: for (reaction, _) in self.reaction_emojis:
if self.maximum_pages == 2 and reaction in ('\u23ed', '\u23ee'): if self.maximum_pages == 2 and reaction in ('\u23ed', '\u23ee'):
# no |<< or >>| buttons if we only have two pages # no |<< or >>| buttons if we only have two pages
@ -84,7 +87,7 @@ class Pages:
# it from the default set # it from the default set
continue continue
try: try:
await self.bot.add_reaction(self.message, reaction) await self.message.add_reaction(reaction)
except discord.NotFound: except discord.NotFound:
# If the message isn't found, we don't care about clearing anything # If the message isn't found, we don't care about clearing anything
return return
@ -116,40 +119,45 @@ class Pages:
async def numbered_page(self): async def numbered_page(self):
"""lets you type a page number to go to""" """lets you type a page number to go to"""
to_delete = [] to_delete = []
to_delete.append(await self.bot.send_message(self.message.channel, 'What page do you want to go to?')) to_delete.append(await self.message.channel.send('What page do you want to go to?'))
msg = await self.bot.wait_for_message(author=self.author, channel=self.message.channel, def check(m):
check=lambda m: m.content.isdigit(), timeout=30.0) 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: if msg is not None:
page = int(msg.content) page = int(msg.content)
to_delete.append(msg) to_delete.append(msg)
if page != 0 and page <= self.maximum_pages: if page != 0 and page <= self.maximum_pages:
await self.show_page(page) await self.show_page(page)
else: else:
to_delete.append(await self.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) await asyncio.sleep(5)
else: 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) await asyncio.sleep(5)
try: try:
await self.bot.delete_messages(to_delete) await self.message.channel.delete_messages(to_delete)
except Exception: except Exception:
pass pass
async def show_help(self): async def show_help(self):
"""shows this message""" """shows this message"""
e = discord.Embed() e = discord.Embed()
messages = ['Welcome to the interactive paginator!\n'] messages = ['Welcome to the interactive paginator!\n',
messages.append('This interactively allows you to see pages of text by navigating with ' \ 'This interactively allows you to see pages of text by navigating with '
'reactions. They are as follows:\n') 'reactions. They are as follows:\n']
for (emoji, func) in self.reaction_emojis: for (emoji, func) in self.reaction_emojis:
messages.append('%s %s' % (emoji, func.__doc__)) messages.append('%s %s' % (emoji, func.__doc__))
e.description = '\n'.join(messages) 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) 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(): async def go_back_to_current_page():
await asyncio.sleep(60.0) await asyncio.sleep(60.0)
@ -159,11 +167,11 @@ class Pages:
async def stop_pages(self): async def stop_pages(self):
"""stops the interactive pagination session""" """stops the interactive pagination session"""
await self.bot.delete_message(self.message) await self.message.delete()
self.paginating = False self.paginating = False
def react_check(self, reaction, user): 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 return False
for (emoji, func) in self.reaction_emojis: for (emoji, func) in self.reaction_emojis:
@ -177,19 +185,19 @@ class Pages:
await self.show_page(start_page, first=True) await self.show_page(start_page, first=True)
while self.paginating: 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: if react is None:
self.paginating = False self.paginating = False
try: try:
await self.bot.clear_reactions(self.message) await self.message.clear_reactions()
except: except:
pass pass
finally: finally:
break break
try: try:
await self.bot.remove_reaction(self.message, react.reaction.emoji, react.user) await self.message.remove_reaction(react.reaction.emoji, react.user)
except: 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() await self.match()

View file

@ -5,6 +5,7 @@ import inspect
from . import config from . import config
from PIL import Image from PIL import Image
def convert_to_jpeg(pfile): def convert_to_jpeg(pfile):
# Open the file given # Open the file given
img = Image.open(pfile) img = Image.open(pfile)
@ -16,6 +17,7 @@ def convert_to_jpeg(pfile):
new_file.seek(0) new_file.seek(0)
return new_file return new_file
def get_all_commands(bot): def get_all_commands(bot):
"""Returns a list of all command names for the bot""" """Returns a list of all command names for the bot"""
# First lets create a set of all the parent names # First lets create a set of all the parent names
@ -31,6 +33,7 @@ def get_all_commands(bot):
return all_commands return all_commands
def get_subcommands(command): def get_subcommands(command):
yield command.qualified_name yield command.qualified_name
try: try:
@ -40,33 +43,9 @@ def get_subcommands(command):
except AttributeError: except AttributeError:
pass 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): async def download_image(url):
"""Returns a file-like object based on the URL provided""" """Returns a file-like object based on the URL provided"""
headers = {'User-Agent': config.user_agent}
# Simply read the image, to get the bytes # Simply read the image, to get the bytes
bts = await request(url, attr='read') bts = await request(url, attr='read')
if bts is None: if bts is None:
@ -76,10 +55,12 @@ async def download_image(url):
image = BytesIO(bts) image = BytesIO(bts)
return image return image
async def request(url, *, headers=None, payload=None, method='GET', attr='json'): async def request(url, *, headers=None, payload=None, method='GET', attr='json'):
# Make sure our User Agent is what's set, and ensure it's sent even if no headers are passed # 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 = {}
headers['User-Agent'] = config.user_agent headers['User-Agent'] = config.user_agent
# Try 5 times # Try 5 times
@ -112,6 +93,7 @@ async def request(url, *, headers=None, payload=None, method='GET', attr='json')
except: except:
continue continue
async def update_records(key, winner, loser): async def update_records(key, winner, loser):
# We're using the Harkness scale to rate # We're using the Harkness scale to rate
# http://opnetchessclub.wikidot.com/harkness-rating-system # http://opnetchessclub.wikidot.com/harkness-rating-system

View file

@ -32,6 +32,7 @@ youtube_dl.utils.bug_reports_message = lambda: ''
''' '''
class Downloader: class Downloader:
def __init__(self, download_folder=None): def __init__(self, download_folder=None):
self.thread_pool = ThreadPoolExecutor(max_workers=2) self.thread_pool = ThreadPoolExecutor(max_workers=2)
@ -48,7 +49,6 @@ class Downloader:
otmpl = self.safe_ytdl.params['outtmpl'] otmpl = self.safe_ytdl.params['outtmpl']
self.safe_ytdl.params['outtmpl'] = os.path.join(download_folder, otmpl) self.safe_ytdl.params['outtmpl'] = os.path.join(download_folder, otmpl)
@property @property
def ytdl(self): def ytdl(self):
return self.safe_ytdl return self.safe_ytdl
@ -61,7 +61,8 @@ class Downloader:
""" """
if callable(on_error): if callable(on_error):
try: 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: except Exception as e:
@ -79,7 +80,9 @@ class Downloader:
if retry_on_error: if retry_on_error:
return await self.safe_extract_info(loop, *args, **kwargs) return await self.safe_extract_info(loop, *args, **kwargs)
else: 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): 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 os
import traceback import traceback
import time import time
import discord import aiohttp
from discord import Embed
from hashlib import md5 from hashlib import md5
from .exceptions import ExtractionError from .exceptions import ExtractionError
async def get_header(session, url, headerfield=None, *, timeout=5): async def get_header(session, url, headerfield=None, *, timeout=5):
with aiohttp.Timeout(timeout): with aiohttp.Timeout(timeout):
async with session.head(url) as response: async with session.head(url) as response:
@ -16,6 +18,7 @@ async def get_header(session, url, headerfield=None, *, timeout=5):
else: else:
return response.headers return response.headers
def md5sum(filename, limit=0): def md5sum(filename, limit=0):
fhash = md5() fhash = md5()
with open(filename, "rb") as f: with open(filename, "rb") as f:
@ -23,6 +26,7 @@ def md5sum(filename, limit=0):
fhash.update(chunk) fhash.update(chunk)
return fhash.hexdigest()[-limit:] return fhash.hexdigest()[-limit:]
class BasePlaylistEntry: class BasePlaylistEntry:
def __init__(self): def __init__(self):
self.filename = None self.filename = None
@ -48,8 +52,8 @@ class BasePlaylistEntry:
def get_ready_future(self): 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 Returns a future that will fire when the song is ready to be played. The future will either fire with the
as to why the song download failed. result (being the entry) or an exception as to why the song download failed.
""" """
future = asyncio.Future() future = asyncio.Future()
if self.is_downloaded: if self.is_downloaded:
@ -159,7 +163,7 @@ class URLPlaylistEntry(BasePlaylistEntry):
'type': self.meta[i].__class__.__name__, 'type': self.meta[i].__class__.__name__,
'id': self.meta[i].id, 'id': self.meta[i].id,
'name': self.meta[i].name '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) # Actually I think I can just getattr instead, getattr(discord, type)
} }
@ -271,16 +275,17 @@ class URLPlaylistEntry(BasePlaylistEntry):
else: else:
# Move the temporary file to it's final location. # Move the temporary file to it's final location.
os.rename(unhashed_fname, self.filename) os.rename(unhashed_fname, self.filename)
def to_embed(self): def to_embed(self):
"""Returns an embed that can be used to display information about this particular song""" """Returns an embed that can be used to display information about this particular song"""
# Create the embed object we'll use # Create the embed object we'll use
embed = discord.Embed() embed = Embed()
# Fill in the simple things # Fill in the simple things
embed.add_field(name='Title', value=self.title, inline=False) embed.add_field(name='Title', value=self.title, inline=False)
embed.add_field(name='Requester', value=self.requester.display_name, inline=False) embed.add_field(name='Requester', value=self.requester.display_name, inline=False)
# Get the current length of the song and display this # Get the current length of the song and display this
length = divmod(round(self.length, 0), 60) length = divmod(round(self.length, 0), 60)
fmt = "{0[0]}m {0[1]}s".format(length) 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 # And return the embed we created
return embed return embed

View file

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

View file

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