async def update_guild_prefix(self, ctx, prefix): """Updates the command prefix for the Discord server. .. Note:: Requires `administrator` privilege. :param ctx: The invocation context. :param prefix: The command prefix to update to. """ user = DiscordUser(ctx.author) if len(prefix) > 3: embed = quickembed.error( desc='Prefix can not be longer than 3 characters', user=user) else: user.update_guild_info(ctx.guild, prefix) stored_prefix = user.guild_info(ctx.guild.id)['prefix'] if prefix == stored_prefix: embed = quickembed.success( desc='Prefix updated to **{}**'.format(stored_prefix), user=user, ) else: embed = quickembed.error(desc='Failed to update prefix', user=user) await ctx.send(embed=embed)
async def unmute_member(self, ctx, member: discord.Member): """Unmutes a member by removing their `Muted` role. .. Note:: Requires `Manage Roles` privilege. .. Note:: A `Muted` role must exist with the proper permissions. It's a simple role that can only read the channels and not send messages. :param ctx: The invocation context. :param member: """ user = DiscordUser(ctx.author) role = discord.utils.find(lambda r: r.name == 'Muted', ctx.guild.roles) if not role: embed = quickembed.error(desc='`Muted` role does not exist', user=user) elif role in member.roles: await member.remove_roles(role) embed = quickembed.success(desc='Unmuted {}'.format(member), user=user) else: embed = quickembed.error( desc='{} is already unmuted'.format(member), user=user) await ctx.send(embed=embed)
async def rate_match(self, ctx, *args): """Adds a 0-5 star rating to a `Match`. .. Note: If no `match_id` is provided, the rating will be added to the most recently closed `Match`. :param ctx: The invocation context. :param args: Must be either `[rating]` or `[match_id] [rating]`. Rating must be between 0-5. :return: """ user = DiscordUser(ctx.author) try: if len(args) == 1: match_id = None rating = float(args[0]) else: match_id = int(args[0]) rating = float(args[1]) except Exception: msg = ('Invalid `!rate` command\n' '`!rate [rating]` (rates last match)\n' '`!rate [match_id] [rating]`') embed = quickembed.error(desc=msg, user=user) await ctx.send(embed=embed) return if not match_id: rows = user.search_match_by_recent_completed() if not rows: msg = 'No current match set to rate' embed = quickembed.error(desc=msg, user=user) await ctx.send(embed=embed) return match_id = rows[0].id response = user.rate_match(match_id, rating) if response['success']: match = Match(match_id) stars = '' for i in range(1, 6): if rating >= i: stars += '★' else: stars += '☆' msg = 'Rated `Match {}` {} ({})\n{}'.format( match_id, stars, rating, match.info_text_short()) embed = quickembed.success(desc=msg, user=user) else: msg = response['message'] embed = quickembed.error(desc=msg, user=user) await ctx.send(embed=embed)
async def open_matches(self, ctx): """Lists the available `Match`es to bet on. .. Note: If the number of available `Match`es exceeds 5, they will be displayed with their `short view`. If it does not exceed 5, their full details will be displayed as individual messages. :param ctx: The invocation context. """ user = DiscordUser(ctx.author) rows = user.search_match_by_open_bets() if len(rows) > 5: embed = quickembed.info( desc='Short View - Use `!match [id]` for full view') embed.set_author(name='Open Bet Matches') for row in rows: match = Match(row.id) embed.add_field( name='[Match {}]'.format(match.id), value='{}'.format(match.info_text_short()), inline=True, ) await ctx.send(embed=embed) elif len(rows) > 0: for row in rows: await ctx.send(embed=Match(row.id).info_embed()) else: await ctx.send(embed=quickembed.error( desc='No open bet matches available', user=user))
async def register_user(self, ctx): """Registers the Discord user and provides a response. If the user is already registered, they will be notified. Otherwise, a confirmation request is created before registering the account. The user will have 15 seconds to respond with a `[Y/N]`. If `[Y]`, the account will attempt to register, the response is then delivered. If `[N]`, the request is cancelled. :param ctx: The invocation context. """ embed = None user = DiscordUser(ctx.author) if user.is_registered(): embed = quickembed.success( desc='Your Discord is already registered', user=user) else: text_question = ( '[Y/N]\nYour Discord is not linked to an existing ' 'Matches account (http://idleuser.com/projects/matches)\n' 'Would you like to register a new account?') embed_question = quickembed.question(user=user, desc=text_question) await ctx.send(embed=embed_question) confirm = await self.bot.wait_for('message', check=checks.confirm(ctx.author), timeout=15.0) confirm.content = confirm.content.upper() if confirm.content == 'Y': response = user.register() if response['success']: user.refresh() embed = quickembed.success(desc=response['message'], user=user) logger.info('`{}` registered'.format(user.username)) else: embed = quickembed.error(desc=response['message'], user=user) logger.warning('`{}` failed to register - {}'.format( user.username, response['message'])) elif confirm.content == 'N': embed = quickembed.error(user=user, desc='Account registration cancelled') if embed: await ctx.send(embed=embed)
async def recent_match_info(self, ctx): """Displays details for the last completed `Match`. :param ctx: The invocation context. """ user = DiscordUser(ctx.author) rows = user.search_match_by_recent_completed() if rows: embed = Match(rows[0].id).info_embed() else: embed = quickembed.error(desc='No match found', user=user) await ctx.send(embed=embed)
async def match_info(self, ctx, match_id=None): """Display info the `Match`. :param ctx: The invocation context. :param match_id: The `Match` id. """ user = DiscordUser(ctx.author) try: match_id = int(match_id) except Exception as e: match_id = None logger.debug('match_info: {}'.format(e)) msg = 'Invalid `!match` command\n`!match [match_id]`' await ctx.send(embed=quickembed.error(desc=msg, user=user)) if match_id: rows = user.search_match_by_id(match_id) if rows: await ctx.send(embed=Match(rows[0].id).info_embed()) else: await ctx.send(embed=quickembed.error( desc='Match `{}` not found'.format(match_id), user=user))
async def superstar_tweets(self, ctx, name, limit=1): """Displays the last tweets a Superstar has posted. .. note:: A 30 seconds timer is set to prevent spamming. :param ctx: The invocation context. :param name: The name of the `Superstar` to search for. :param limit: The amount of messages to get. Must be 1-5. Default is 1. """ user = DiscordUser(ctx.author) embed = None try: limit = 1 if limit < 1 else limit limit = 5 if limit > 5 else limit except Exception as e: logger.debug('superstar_tweets:{}'.format(e)) embed = quickembed.error( desc='Invalid `!tweets` command\n`!tweets [superstar]`', user=user ) if not embed: rows = user.search_superstar_by_name(name) if not rows: embed = quickembed.error( desc='Unable to find superstar `{}`'.format(name), user=user ) await ctx.send(embed=embed) else: superstar = Superstar(rows[0].id) if superstar.twitter_id: tweets = self.latest_tweets(superstar.twitter_id, limit) for tweet in tweets: await ctx.send( 'https://twitter.com/statuses/{}'.format(tweet.id) ) else: embed = quickembed.error( desc='Unable to find Tweets for `{}`'.format(superstar.name), ) await ctx.send(embed=embed)
async def user_join_royalrumble(self, ctx): """TODO: Enters the user into the current Royal Rumble event by providing them an entry number. :param ctx: The invocation context. """ user = DiscordUser(ctx.author) response = user.join_royalrumble() if response['success']: embed = quickembed.success(desc='Entry Number: `{}`'.format( response['message']), user=user) else: embed = quickembed.error(desc=response['message'], user=user) await ctx.send(embed=embed)
async def superstar_info(self, ctx, *, name): """Displays the `Superstar`'s information. .. Note: If the `name` matches multiple `Superstar`s, a list of the matching `Superstar`s is presented. The requester would then have to select one of the options available. :param ctx: The invocation context. :param name: The name of the `Superstar`. """ user = DiscordUser(ctx.author) superstar_list = user.search_superstar_by_name(name) if not superstar_list: embed = quickembed.error( desc="Unable to find Superstars matching '{}'".format(name), user=user) else: if len(superstar_list) > 1: msg = 'Select Superstar from List ...\n```' for i, e in enumerate(superstar_list): msg = msg + '{}. {}\n'.format(i + 1, e.name) msg = msg + '```' await ctx.send(embed=quickembed.question(desc=msg, user=user)) response = await self.bot.wait_for('message', check=checks.is_number( ctx.author), timeout=15.0) try: index = int(response.content) embed = Superstar(superstar_list[index - 1].id).info_embed() except (ValueError, IndexError): embed = quickembed.error(desc='Invalid index', user=user) else: embed = Superstar(superstar_list[0].id).info_embed() await ctx.send(embed=embed)
async def add_discord_command(self, ctx, command, *, response): """Inserts a quick chatroom command. :param ctx: The invocation context. :param command: The command name to add. :param response: The response for the command. """ user = DiscordUser(ctx.author) command = '!{}'.format(command.strip('!')) res = user.add_chatroom_command(command, response) if res['success']: embed = quickembed.success( desc='Command `{}` updated'.format(command), user=user) else: embed = quickembed.error(desc='Failed', user=user) await ctx.send(embed=embed)
async def change_volume(self, ctx, volume: int): """Updates the current audio player's volume. .. Note:: The volume received is divided by 100 because the player's volume value must be a float between 0 and 1. Think of the input as a percentage. :param ctx: The invocation context. :param volume: The percentage of the audio. Should be 0-100. """ user = DiscordUser(ctx.author) if ctx.voice_client is None: await ctx.send(embed=quickembed.error( desc='Not connected to a voice channel', user=user)) ctx.voice_client.source.volume = volume / 100 await ctx.send(embed=quickembed.info( desc='Changed volume to {}%'.format(volume), user=user))
async def update_discord_command(self, ctx, command, *, response): """Updates a quick chatroom command. .. Note:: Only the bot owner can use this. :param ctx: The invocation context. :param command: The command name to update. :param response: The updated response for the command. """ user = DiscordUser(ctx.author) command = '!{}'.format(command.strip('!')) res = user.update_chatroom_command(command, response) if res['success']: embed = quickembed.success( desc='Command `{}` updated'.format(command), user=user) else: embed = quickembed.error(desc='Failed', user=user) await ctx.send(embed=embed)
async def ensure_voice(self, ctx): """Checks if the voice channel can be started. .. Note:: * The requester must be in a voice channel. * Stops a current player if it currently exists. :param ctx: The invocation context. """ if ctx.voice_client is None: if ctx.author.voice: await ctx.author.voice.channel.connect() else: await ctx.send(embed=quickembed.error( desc='You must be in a voice channel to use that command', user=DiscordUser(ctx.author), )) raise commands.CommandError( 'Author not connected to a voice channel') elif ctx.voice_client.is_playing(): ctx.voice_client.stop()
async def scheduler_pending(self, ctx): """Displays a list of pending alert messages. .. note:: Only the bot owner can use this. :param ctx: The invocation context. """ if self.scheduled_payloads.items(): user = DiscordUser(ctx.author) embed = quickembed.info(desc="Today's Scheduled Alerts (PT)", user=user) embed.add_field( name='\u200b', value='\n'.join([ '{1} - **{0}**'.format(k, v['task_datetime']) for k, v in self.scheduled_payloads.items() ]), ) else: embed = quickembed.error(desc='Nothing scheduled for today') await ctx.send(embed=embed)
async def user_current_bets(self, ctx): """Displays the user's current bets for `Match`es. :param ctx: The invocation context. """ user = DiscordUser(ctx.author) bets = user.current_bets() if bets: msg = "```{}```".format('\n'.join([ 'Match {}\n\t{:,} points on {}\n\t' 'Potential Winnings: {:,} ({}%)'.format( bet['match_id'], bet['points'], bet['contestants'], bet['potential_cut_points'], bet['potential_cut_pct'] * 100, ) for bet in bets ])) embed = quickembed.general(desc='Current Bets', user=user) embed.add_field(name='\u200b', value=msg, inline=False) else: embed = quickembed.error(desc='No current bets placed', user=user) await ctx.send(embed=embed)
async def on_command_error(ctx, error): """Called when an error through a command occurs. Common instances are handled appropriately. If error is not handled, the error is raised. :param ctx: The invocation context. :param error: The command error. """ msg = None if isinstance(error, commands.CommandNotFound): return elif isinstance(error, commands.CommandOnCooldown): msg = 'Slow down! Try again in {:.1f} seconds.'.format( error.retry_after) elif isinstance(error, GuildNotOriginError): logger.error('GuildNotOriginError: {0.command} - ' '{0.guild.name}(0.guild.id) - ' '{0.author}'.format(ctx)) return elif isinstance(error, UserNotRegisteredError): msg = ( 'Your Discord is not linked to an existing Matches account.\n' 'Use `!register` or visit http://idleuser.com/projects/matches to link ' 'to your existing account.') elif isinstance(error, commands.CommandError): logger.error('CommandError: {0} - ' '{1.guild.name}({1.guild.id}) - ' '{1.author} - {1.command}'.format(error, ctx)) return elif isinstance(error, commands.CommandInvokeError): if isinstance(error.original, asyncio.TimeoutError): msg = 'Took too long to confirm. Try again.' if msg: await ctx.send( embed=quickembed.error(desc=msg, user=DiscordUser(ctx.author))) else: raise error
async def donate_bucks(self, ctx, amount: int, member: discord.Member): """Sends another user FJBucks from their own wallet. .. Important:: Work in progress. :param ctx: The invocation context. :param amount: The amount of FJBucks to send :param member: The Discord member to send to. """ user = DiscordUser(ctx.author) recipient = DiscordUser(member) if not recipient.is_registered(): embed = quickembed.error(desc='{} is not registered'.format( recipient.name), user=user) else: recipient.fjbucks_transaction(amount, 'owner authorized') embed = quickembed.success( desc='Donated **{} FJBucks** to {}'.format( amount, recipient.name), user=user, ) await ctx.send(embed=embed)
async def place_match_bet(self, ctx, *args): """Places a `Match` bet. .. Note: A bet requires: * The `bet amount` * The `match_id` * The `team` The user can provide them sequentially: !bet [bet_amount] [match_id] [team] Or they can insert a `Superstar`'s name (`Match` contestant): !bet [bet_amount] [contestant] The `match_id` and `team` is found by cross-referencing the `Superstar` name with open-bet `Match` contestants. :param ctx: The invocation context. :param args: Must be either `[bet_amount] [match_id] [team]` or `[bet_amount] [contestant]` """ user = DiscordUser(ctx.author) bet = None match_id = None team = None superstar_name = None try: bet = int(args[0].replace(',', '')) if len(args) == 3 and args[1].isdigit() and args[2].isdigit(): match_id = int(args[1]) team = int(args[2]) elif len(args) > 1: superstar_name = ' '.join(args[1:]) rows = user.search_match_by_open_bets_and_superstar_name( superstar_name) match_id = rows[ 0].id if rows else False # use first match found if not match_id: embed = quickembed.error( desc='Unable to find an open match for contestant `{}`' .format(superstar_name), user=user, ) await ctx.send(embed=embed) return else: raise except Exception as e: logger.debug('place_match_bet: {}'.format(e)) msg = ('Invalid `!bet` command\n' '`!bet [bet_amount] [contestant]`\n' '`!bet [bet_amount] [match_id] [team]`') await ctx.send(embed=quickembed.error(desc=msg, user=user)) return match = Match(match_id) if not team and superstar_name: team = match.team_by_contestant(superstar_name) response = user.validate_bet(match_id, team, bet) if response['success']: question_embed = quickembed.question(desc='[Y/N] Place this bet?', user=user) question_embed.add_field(name='Info', value=match.info_text_short(), inline=False) question_embed.add_field(name='Betting', value='{:,}'.format(bet), inline=True) question_embed.add_field(name='Betting On', value=match.teams[team]['members'], inline=True) await ctx.send(embed=question_embed) confirm = await self.bot.wait_for('message', check=checks.confirm(ctx.author), timeout=15.0) confirm.content = confirm.content.upper() if confirm.content == 'Y': response = user.place_bet(match_id, team, bet) if response['success']: msg = 'Placed `{:,}` point bet on `{}`'.format( bet, match.teams[team]['members']) embed = quickembed.success(desc=msg, user=user) else: embed = quickembed.error(desc=response['message'], user=user) elif confirm.content == 'N': embed = quickembed.error(desc='Bet cancelled', user=user) else: embed = quickembed.error(desc=response['message'], user=user) await ctx.send(embed=embed)