async def create_match_debug(self, ctx, amount: int, active: bool, challenger: discord.Member, recipient: discord.Member, accepted: bool, count: int): """ """ logger.debug( f'create_match_debug: {amount}, {active}, {challenger}, {recipient}, {accepted}, {count}' ) x = 0 while x < count: matches.create_match( ctx, None, amount, accounts.get_account(ctx, DbHandler.db_cnc, challenger), accounts.get_account(ctx, DbHandler.db_cnc, recipient), active, accepted) x += 1
async def accept(self, ctx: commands.Context): """Accepts a match you were challenged to Examples: - `$accept` **Arguments** - `<ctx>` The context used to send confirmations. """ logger.debug(f'accept: {ctx}, {ctx.author}') # Get player from database for user, then get a match_id player = acc.get_account(ctx, DbHandler.db_cnc, ctx.author) match_id = database_operation.find_match_by_player_id(DbHandler.db_cnc, player.id) logger.debug(f'player: {player}, match_id: {match_id}') # Checks if match id is 0/None, gives user message and returns to exit if not match_id: logger.debug(f'match_id is zero') await du.code_message(ctx, 'You don\'t have a match to accept') return # Get match_info to get marble amount match = ma.get_match(ctx, match_id) # database_operation.get_match_info_by_id(DbHandler.db_cnc, match_id) # Checks if match_info is valid if not match: logger.debug('match_info is zero') await du.code_message(ctx, 'Unable to get match info') return # Check if user accepting is participant2 if player.id != match.recipient.id: logger.debug(f'{ctx.author} tried to accept a match they aren\'t the recipient of') await du.code_message(ctx, 'You\'re not the recipient of a match') return # Updates match accepted flag in database, checks if write was successful and gives message try: match.accepted = True # database_operation.update_match_accepted(DbHandler.db_cnc, match_id): except commands.CommandError as e: logger.debug(f'Unable to update match accepted flag') await du.code_message(ctx, 'Was unable to accept match', 3) return # Subtracts marbles from user player.marbles -= match.amount await du.code_message(ctx, f'Match {match.id} accepted, now open for betting.' f'\nType \'$start\' to close the betting and start the match')
async def wins(self, ctx: commands.Context, member: Union[discord.Member, str] = None): """Prints a users wins, loses, and winrate Examples: - `$wins @Sophia` - `$wins` **Arguments** - `<ctx>` The context used to send confirmations. - `<member>` The user who's data you want to print. If omitted will defaults to your own data. """ logging.debug(f'wins: {member}') # Check if member is None, use ctx.author if None if member is None: account = acc.get_account(ctx, DbHandler.db_cnc, ctx.author) else: account = acc.get_account(ctx, DbHandler.db_cnc, member) logger.debug(f'account: {account}') # Set winrate to 0 if wins is zero, otherwise calculate winrate if account.wins == 0: player_winrate = 0 else: player_winrate = 100 * (account.wins / (account.wins + account.loses)) await du.code_message( ctx, f'{account.nickname}\n' f'Wins: {account.wins}' f'\nLoses: {account.loses}' f'\nWinrate: {player_winrate:.2f}%')
async def nick(self, ctx: commands.Context, nickname: str): logger.debug(f'nick: {nickname}') # Check if nickname is empty if not nickname.strip(): await ctx.send('Nickname cannot be empty') return # Check if nickname contains whitespace if " " in nickname: await ctx.send('Nickname cannot contain whitespace') return account = acc.get_account(ctx, DbHandler.db_cnc, ctx.author) account.nickname = nickname await ctx.send(f'New nickname: "{account.nickname}"')
async def leaderboard(self, ctx: commands.Context, stat: str, members: Union[discord.Member, str] = None): """Lists top 10 players by winrate, or a specific users position on leaderboard Examples: - `$leaderboard winrate @Sophia` - `$leaderboard` - `$leaderboard wins` **Arguments** - `<ctx>` The context used to send confirmations. - `<stat>` String of stat to get - `<members>` The member who's position on leaderboard you'd like to receive. """ logger.debug(f'leaderboard: {members}') member_account = None # Get member account if members: member_account = acc.get_account(ctx, DbHandler.db_cnc, members) logger.debug(f'member_account: {member_account}') # Get accounts of all users on server player_info = acc.get_account_server_all(ctx, DbHandler.db_cnc, ctx.guild.id) # Creation function to get stats based on string stat_get = operator.attrgetter(stat) players = [] players = sorted(player_info, key=stat_get, reverse=True) if members is not None: for player in players: if player.id == member_account.id: if stat == 'winrate': await du.code_message( ctx, f'{member_account.nickname} is rank #{players.index(player) + 1}, ' f'with a win-rate of {stat_get(player):.2f}%') else: await du.code_message( ctx, f'{member_account.nickname} is rank #{players.index(player) + 1}, ' f'with {stat_get(player)}') return return text = f"Leaderboard top 10 {stat}\n\n" for player in players[0:10]: if stat == 'winrate': text += f'#{players.index(player) + 1} {player.nickname}: {stat_get(player):.2f}%\n' else: text += f'#{players.index(player) + 1} {player.nickname}: {stat_get(player)}\n' await du.code_message(ctx, text)
async def match_history(self, ctx: commands.Context, member: Union[discord.Member, str] = None, vs: Union[discord.Member, str] = None): """Show match history of user. Example: - `$match_history @Sophia' - `$match_history @Ness' - `$match_history @Sophia @Ness' **Arguments** - `<member>` The user to show the match history of. If omitted, defaults to your own history. - `<vs>` The user to limit the match history to only games with them """ logger.debug(f'match_history: {member}, {vs}') # Declare player2 as none for failsafe with ma.get_matches_all player2 = None # Check if member is None, if it is, set member to ctx.author if not member: member = ctx.author # Check if vs exists, get player2 if it does if vs: player2 = acc.get_account(ctx, DbHandler.db_cnc, vs) # Get player1 and their match history player1 = acc.get_account(ctx, DbHandler.db_cnc, member) match_history = ma.get_matches_all(ctx, player1, player2, True) # Check if match_history is not 0 if not match_history: await du.code_message(ctx, 'No match history') return # Instantiate text and match_list to be appended later text = '' match_list = match_history # Set pages to amount of match_list/10 in an even amount, cur_page to last page, and active to true text = '' pages = math.ceil(len(match_list) / 10) cur_page = pages - 1 # Used to loop waiting for a react active = True # Generate page from match_list for i in range(cur_page * 10, (cur_page * 10) + 10): if i < len(match_list): text += self.generate_match_text( match_list[i]) # text += str(match_list[i]) # If pages is greater than one, add a page counter, if not set active to False if pages > 1: text += f'Page {cur_page+1} of {pages}\n' else: active = False # Create message with return of du.code_message message = await du.code_message(ctx, text) # If pages greater than one, add reaction controls if pages > 1: await message.add_reaction('\U00002B05') # ⬅️ await message.add_reaction('\U000027A1') # ➡️ # Method to check if react is the correction with the correct user def check(reaction, user): return user == ctx.author and str( reaction.emoji) in ['\U00002B05', '\U000027A1'] # While loop while active: try: # page set to start of codeblock page = '```\n' # wait till we get a reaction, fill reaction, user with output of 'reaction_add' reaction, user = await self.bot.wait_for('reaction_add', timeout=60, check=check) # If reaction is left and cur_page is greater than 0 if str(reaction.emoji) == '\U00002B05' and cur_page > 0: # ⬅️️ # Set current page to one less than current cur_page -= 1 # For range of pages for current list append match_list to page for i in range(cur_page * 10, cur_page * 10 + 10): page += self.generate_match_text( match_list[i]) # match_list[i] # Add page counter and edit message with page page += f'Page {cur_page+1} of {pages}\n```' await message.edit(content=page) # Remove users reaction await message.remove_reaction(reaction, user) # If reaction is right and cur_page is less than pages-1 elif str(reaction.emoji ) == '\U000027A1' and cur_page < pages - 1: # ➡️ # Set current page to one more than current cur_page += 1 # For range of pages for current list append match_list to page for i in range(cur_page * 10, cur_page * 10 + 10): if i < len(match_list): page += self.generate_match_text( match_list[i]) # match_list[i] # Add page counter and edit message with page page += f'Page {cur_page+1} of {pages}\n```' await message.edit(content=page) # Remove users reaction await message.remove_reaction(reaction, user) else: # Remove reaction if it's anything else await message.remove_reaction(reaction, user) except asyncio.TimeoutError: # When 'reaction_add' throws exception, set active to False to end loop active = False # Get cached message to remove reactions cached_msg = discord.utils.get(self.bot.cached_messages, id=message.id) for reactions in cached_msg.reactions: await reactions.remove(self.bot.user)
async def bet_history(self, ctx, member: Union[discord.Member, str] = None, bet_target: Union[discord.Member, str] = None): """Prints bet history of user Examples: - `$bet_history @Sophia' - `$bet_history @Ness' - `$bet_history @Sophia @Ness' **Arguments** - `<member>` The user to who's bet history you want to print. If omitted defaults to your own history. - '<bet_target>' The user you want to limit bets on to. """ logger.debug(f'bet_history: {member}, {bet_target}') # Declare bet_target_acc as failsafe for bets.get_bet_all bet_target_acc = None # If member is None set member to ctx.author if not member: member = ctx.author # If bet_target is not None, get bet_target info for specific search if bet_target: bet_target_acc = acc.get_account(ctx, DbHandler.db_cnc, bet_target) # Get bettor info and bet_history bettor = acc.get_account(ctx, DbHandler.db_cnc, member) bet_history = bets.get_bet_all(ctx, bettor, bet_target_acc, True) # Check if bet_history is filled if not bet_history: await du.code_message(ctx, 'No bet history') return # Create variables to be appended text = '' bet_list = bet_history # Set pages to bet_list/10 even, cur_page to pages-1 and active to True pages = math.ceil(len(bet_list) / 10) cur_page = pages - 1 active = True # Generate first page to be displayed with cur_page for i in range(cur_page * 10, (cur_page * 10) + 10): if i < len(bet_list): text += self.generate_bet_text(bet_list[i]) # If pages is greater than one, append a page counter if pages > 1: text += f'Page {cur_page+1} of {pages}\n' else: active = False # Send message message = await du.code_message(ctx, text) # If pages is greater than one add reactions for control if pages > 1: await message.add_reaction('\U00002B05') await message.add_reaction('\U000027A1') # Function to check if reaction = ctx.author def check(reaction, user): return user == ctx.author and str( reaction.emoji) in ['\U00002B05', '\U000027A1'] # loop for reaction controls while active: try: # Set page to start of codeblock page = '```\n' # wait till we get a reaction, fill reaction, user with output of 'reaction_add' reaction, user = await self.bot.wait_for('reaction_add', timeout=60, check=check) # Check if reaction is left, and cur_page greater than zero if str(reaction.emoji) == '\U00002B05' and cur_page > 0: # ⬅️ # Set cur_page to current value minus one cur_page -= 1 # Generate current page with cur_page for i in range(cur_page * 10, cur_page * 10 + 10): page += self.generate_bet_text( bet_list[i]) # bet_list[i] # Append page counter and edit message with page page += f'Page {cur_page+1} of {pages}\n```' await message.edit(content=page) # Remove user reaction await message.remove_reaction(reaction, user) # Check if reaction is right, and cur_page less than pages-1 elif str(reaction.emoji ) == '\U000027A1' and cur_page < pages - 1: # ➡️ # Set cur_page to current value plus one cur_page += 1 # Generate current page with cur_page for i in range(cur_page * 10, cur_page * 10 + 10): if i < len(bet_list): page += self.generate_bet_text( bet_list[i]) # bet_list[i] # Append page counter and edit message with page page += f'Page {cur_page+1} of {pages}\n```' await message.edit(content=page) # Remove user reaction await message.remove_reaction(reaction, user) else: await message.remove_reaction(reaction, user) except asyncio.TimeoutError: # When 'reaction_add' gets a timeout, set active to false to end loop active = False # Get cached message to remove all reactions cached_msg = discord.utils.get(self.bot.cached_messages, id=message.id) for reactions in cached_msg.reactions: await reactions.remove(self.bot.user)
async def friendlies(self, ctx: commands.Context, member: Union[discord.Member, str]): logger.debug(f'friendlies: {member}') # Get players Accounts player1 = acc.get_account(ctx, DbHandler.db_cnc, ctx.author) if not player1: logger.error('No player1_id found') await du.code_message(ctx, 'Unable to get player info', 3) player2 = acc.get_account(ctx, DbHandler.db_cnc, member) if not player2: logger.error('No player1_id found') await du.code_message(ctx, 'Unable to get player info', 3) # Check if player1 is player2 if player1.id == player2.id: await du.code_message(ctx, 'No self friendlies', 3) return # Function to check if reaction and user def check_member(reaction, user): return user == player2.member and str(reaction.emoji) == '\U00002705' # ✅️ # Set needed variables active = True now = datetime.utcnow() hardcoded_time = datetime(now.year, now.month, now.day, 4, 0, 0, 0) # Get players last used time player1_last_used = player1.friendly_last_used player2_last_used = player2.friendly_last_used # Check if both players have access to the command if not player1_last_used or player1_last_used < hardcoded_time: if not player2_last_used or player2_last_used < hardcoded_time: pass else: await self.send_error(ctx, hardcoded_time, player2) return else: await self.send_error(ctx, hardcoded_time, player1) return # Send message to have users react to message = await du.code_message(ctx, f'Please react to this message with ✅ to accept the friendly') # Add reaction to message await message.add_reaction('\U00002705') while active: try: reaction, user = await self.bot.wait_for('reaction_add', timeout=60, check=check_member) if str(reaction) == '\U00002705': player1.marbles += 1 player1.friendly_last_used = datetime.utcnow() player2.marbles += 1 player2.friendly_last_used = datetime.utcnow() await du.code_message(ctx, f"We've added a marble to your accounts for playing friendlies today.\n" f"{player1.nickname}: {player1.marbles}\n" f"{player2.nickname}: {player2.marbles}") except asyncio.TimeoutError: # When 'reaction_add' gets a timeout, set active to false to end loop active = False # Get cached message to remove all reactions cached_msg = discord.utils.get(self.bot.cached_messages, id=message.id) for reactions in cached_msg.reactions: await reactions.remove(self.bot.user)
async def match(self, ctx: commands.Context, member: Union[discord.Member, str], marbles: int, game: str = 'melee', form: str = 'Bo3'): """Challenge a user to a marble match Examples: - `$match @Sophia 10` - `$match @Ness 1` **Arguments** - `<ctx>` The context used to send confirmations. - `<member>` The user you want to challenge to a marble match - `<marbles>` The amount of marbles for the match """ logging.debug(f'match: {member}, {marbles}') # Check if marbles is less than one, gives a message to user and returns to exit if marbles < 1: logger.debug(f'{ctx.author} attempted to create a match with < 1 marbles') await du.code_message(ctx, 'You\'re a terrible person who made Soph have to program this.' '\n No negatives or zero', 3) return # Get and check if Account exists, gives a message to user and returns if no Account challenger = acc.get_account(ctx, DbHandler.db_cnc, ctx.author) if not challenger: logger.debug(f'Unable to get Account for {ctx.author}') await du.code_message(ctx, f'Unable to get {ctx.author.display_name}\'s account info', 3) return recipient = acc.get_account(ctx, DbHandler.db_cnc, member) if not recipient: logger.debug(f'Unable to get Account for {member}') await du.code_message(ctx, f'Unable to get {member.display_name}\'s account info', 3) return # Check if challenger and recipient are the same, gives a message to user and returns to exit if challenger.id == recipient.id: logger.debug(f'{ctx.author} passed themselves as member in match command') await du.code_message(ctx, 'You cannot challenge yourself to a match', 3) return # Checks if challenger has a match already going if database_operation.find_match_by_player_id(DbHandler.db_cnc, challenger.id): logger.debug(f'{ctx.author} attempted to start a match with one already made') await du.code_message(ctx, 'You already have an match going', 3) return # Checks if recipient has a match already going if database_operation.find_match_by_player_id(DbHandler.db_cnc, recipient.id): logger.debug(f'{ctx.author}attempted to start a match while {recipient} has a match already') await du.code_message(ctx, 'They already have a match going') return # Checks if challenger/recipient has enough marbles to create a match if challenger.marbles < marbles: logger.debug(f'{ctx.author} does not have enough marbles for this match') await du.code_message(ctx, f'You do not have enough marbles for this match,' f' you have {challenger.marbles}', 3) return if recipient.marbles < marbles: logger.debug(f'{member} does not have enough marbles for this match') await du.code_message(ctx, f'They do not have enough marbles for this match,' f' they have {recipient.marbles}', 3) return # Creates the match with the players match = ma.create_match(ctx, None, marbles, challenger, recipient, game=game, format=form) logger.debug(f'match: {match}') # Checks if match_id is valid, to verify match was created if not match: logger.debug('match is zero, failed to create match') await du.code_message(ctx, 'Failed to create match', 3) return # Subtracts marbles from challenger challenger.marbles -= marbles await du.code_message(ctx, f'{challenger.nickname} challenged {recipient.nickname} ' f'to a marble match for {marbles} ' f'marbles' f'\nType \'$accept\' to accept their challenge.' f'\nType \'$close\' to decline the challenge.' f'\nMatch ID: {match.id}')
async def match_win(self, ctx: commands.Context, member: Union[discord.Member, str] = None): """Sets the winner of a marble match Examples: - `$winner @Sophia` - `$winner @Ness` **Arguments** - `<ctx>` The context used to send confirmations. - `<member>' Member to select as the winner. """ logger.debug(f'match_win: {member}') # Checks if member is None, then sets member to ctx.author if true if member is None: member = ctx.author # Gets winner/match_id to process match and do integrity checks winner = acc.get_account(ctx, DbHandler.db_cnc, member) match_id = database_operation.find_match_by_player_id(DbHandler.db_cnc, winner.id) logger.debug(f'winner: {winner}, match_id: {match_id}') # Checks if they have an active match if not match_id: logger.debug('match_id is zero') await du.code_message(ctx, 'They have no active match', 3) return # Get match info from match_id try: match = ma.get_match(ctx, match_id) logger.debug(f'match: {match}') except commands.CommandError as e: logger.error(f'Unable to get match: {e}') await du.code_message(ctx, 'Unable to process match', 3) return # Set loser to other participant # Checks if participant1 is winner if true sets loser to participant2, or participant1 otherwise if winner == match.challenger: loser = match.recipient else: loser = match.challenger logger.debug(f'loser: {loser}') try: # Set match winner match.winner = winner # Add marbles to winner winner.marbles += match.amount * 2 # Adds win/lose to winner/loser winner.wins += 1 loser.loses += 1 except commands.CommandError as e: logger.error(f'Unable to update player stats: {e}') await du.code_message(ctx, 'Was unable to update player stats please try again', 3) return logger.debug('Processed bets') # Creates a entry of match in match_history table, deletes from matches try: match.is_history = True match.match_time = datetime.utcnow() logger.debug('Updated match info') match.create_history() logger.debug('Created match_history for match') if bets.process_bets(ctx, match): logger.debug('Processed bets') except commands.CommandError as e: logger.error(f'Unable to create matches_history entry') await du.code_message(ctx, f'Failed to add match to history or to delete from matches: {match.id}', 3) return await du.code_message(ctx, f'{winner.nickname} is the winner, gaining a total of {match.amount} marbles!')
async def bet(self, ctx: commands.Context, bet_target: Union[discord.Member, str], match_id: int, marbles: int): """Places a bet on a active and not started match Example: - `$bet @Sophia 12 5` - `$bet @Ness 12 0` **Arguments** - `<winner>` The player you think will win the match. - `<match_id>` The id of the match you'd like to bet on - `<marbles>` Amount of marbles you'd like to bet on the match. """ logger.debug(f'bet: {bet_target}, {match_id}, {marbles}') # Check if marbles is less than 0, return if not if marbles < 0: await du.code_message(ctx, 'Marbles cannot be negative') return # Get bettor/bet_target_id accounts bettor_acc = acc.get_account(ctx, DbHandler.db_cnc, ctx.author) bet_target_acc = acc.get_account(ctx, DbHandler.db_cnc, bet_target) logger.debug(f'bettor: {bettor_acc}, bet_target: {bet_target_acc}') # Check if marble count for bettor is greater than marbles if bettor_acc.marbles < marbles: await du.code_message( ctx, f'You do not have enough marbles for this bet\n' f'You have {bettor_acc.marbles} and need {marbles}') return # Get match_info and validate match_info = ma.get_match( ctx, match_id ) # database_operation.get_match_info_by_id(DbHandler.db_cnc, match_id) if not match_info: await du.code_message(ctx, 'Invalid match id') return # Check if match is started if match_info.active: await du.code_message(ctx, 'Match has started and betting is closed') return # Check if match is accepted if not match_info.accepted: await du.code_message( ctx, 'You can only bet on matches that both players have accepted') return # Check to make sure person placing bet is not in the match if match_info.challenger == bettor_acc or match_info.recipient == bettor_acc: await du.code_message(ctx, 'You cannot bet on matches you are in') return # Check to make sure bet_target_id is in match if match_info.challenger.id != bet_target_acc.id: if match_info.recipient.id != bet_target_acc.id: await du.code_message(ctx, 'Player is not in this match') return # Get bet_id if exists, bet_id = database_operation.find_bet(DbHandler.db_cnc, match_id, bettor_acc.id) # If bet exists, and marbles is zero delete bet if bet_id != 0: # Get bet with bet_id if it's not zero bet_info = bets.get_bet(ctx, bet_id) if marbles == 0: # Delete bet, and return marbles to bettor bet_info.delete_bet() bettor_acc.marbles += bet_info.amount await du.code_message(ctx, 'Bet deleted') return # Update bet # Add marbles back to user, then subtract the new amount bettor_acc.marbles += bet_info.amount bettor_acc.marbles -= marbles bet_info.bet_target = bet_target_acc bet_info.amount = marbles await du.code_message(ctx, 'Bet updated') return # Return if marbles is less than 1 if marbles < 1: await du.code_message(ctx, 'Cannot be zero') return # Create bet bets.create_bet(ctx, None, marbles, match_info, bettor_acc, bet_target_acc) # Take marbles bettor_acc.marbles = bettor_acc.marbles - marbles await du.code_message(ctx, 'Bet submitted')