From f83e3cc3e7ea4b00d8e69ac1ece5d5352706824a Mon Sep 17 00:00:00 2001 From: palmtree5 <3577255+palmtree5@users.noreply.github.com> Date: Tue, 20 Mar 2018 14:22:10 -0800 Subject: [PATCH] [V3 Economy] Expand payday output (#1386) * [V3 Economy] implement suggestions from #1371 * Fix a typo * Add functions for getting leaderboard and leaderboard position * Use the new functions to get leaderboard position and leaderboard (overrides https://github.com/Cog-Creators/Red-DiscordBot/pull/1435) * Actually implement showing only guild members on leaderboard when bank is global * get_leaderboard_position needs to be awaited * For global bank, pass None for guild to get_leaderboard when trying to find position * Remove some unneeded code * Wrong index... * Combine 3 messages into 1 * Fix guild leaderboard while bank is global * add missing parentheses * Modify the leaderboard formatting * More work on leaderboard formatting * no subtraction --- redbot/cogs/economy/economy.py | 67 +++++++++++++------------- redbot/core/bank.py | 87 +++++++++++++++++++++------------- 2 files changed, 88 insertions(+), 66 deletions(-) diff --git a/redbot/cogs/economy/economy.py b/redbot/cogs/economy/economy.py index 6a0ddbd29..d5481f78c 100644 --- a/redbot/cogs/economy/economy.py +++ b/redbot/cogs/economy/economy.py @@ -235,14 +235,17 @@ class Economy: await bank.deposit_credits(author, await self.config.PAYDAY_CREDITS()) next_payday = cur_time + await self.config.PAYDAY_TIME() await self.config.user(author).next_payday.set(next_payday) - await ctx.send( - _("{} Here, take some {}. Enjoy! (+{}" - " {}!)").format( - author.mention, credits_name, - str(await self.config.PAYDAY_CREDITS()), - credits_name - ) - ) + + pos = await bank.get_leaderboard_position(author) + await ctx.send(_( + "{0.mention} Here, take some {1}. Enjoy! (+{2}\n\n" + "You currently have {3} {1}.\n\n" + "You are currently #{4} on the leaderboard!" + ).format( + author, credits_name, str(await self.config.PAYDAY_CREDITS()), + str(await bank.get_balance(author)), pos + )) + else: dtime = self.display_time(next_payday - cur_time) await ctx.send( @@ -255,12 +258,15 @@ class Economy: await bank.deposit_credits(author, await self.config.guild(guild).PAYDAY_CREDITS()) next_payday = cur_time + await self.config.guild(guild).PAYDAY_TIME() await self.config.member(author).next_payday.set(next_payday) - await ctx.send( - _("{} Here, take some {}. Enjoy! (+{}" - " {}!)").format( - author.mention, credits_name, - str(await self.config.guild(guild).PAYDAY_CREDITS()), - credits_name)) + pos = await bank.get_leaderboard_position(author) + await ctx.send(_( + "{0.mention} Here, take some {1}. Enjoy! (+{2})\n\n" + "You currently have {3} {1}.\n\n" + "You are currently #{4} on the leaderboard!" + ).format( + author, credits_name, str(await self.config.PAYDAY_CREDITS()), + str(await bank.get_balance(author)), pos + )) else: dtime = self.display_time(next_payday - cur_time) await ctx.send( @@ -269,7 +275,7 @@ class Economy: @commands.command() @guild_only_check() - async def leaderboard(self, ctx: commands.Context, top: int = 10): + async def leaderboard(self, ctx: commands.Context, top: int = 10, show_global: bool=False): """Prints out the leaderboard Defaults to top 10""" @@ -277,26 +283,23 @@ class Economy: guild = ctx.guild if top < 1: top = 10 - if await bank.is_global(): - bank_sorted = sorted(await bank.get_global_accounts(), - key=lambda x: x.balance, reverse=True) - else: - bank_sorted = sorted(await bank.get_guild_accounts(guild), - key=lambda x: x.balance, reverse=True) + if await bank.is_global() and show_global: # show_global is only applicable if bank is global + guild = None + bank_sorted = await bank.get_leaderboard(positions=top, guild=guild) if len(bank_sorted) < top: top = len(bank_sorted) - topten = bank_sorted[:top] highscore = "" - place = 1 - for acc in topten: - dname = str(acc.name) - if len(dname) >= 23 - len(str(acc.balance)): - dname = dname[:(23 - len(str(acc.balance))) - 3] - dname += "... " - highscore += str(place).ljust(len(str(top)) + 1) - highscore += dname.ljust(23 - len(str(acc.balance))) - highscore += str(acc.balance) + "\n" - place += 1 + for pos, acc in enumerate(bank_sorted, 1): + pos = pos + poswidth = 2 + name = acc[1]["name"] + namewidth = 35 + balance = acc[1]["balance"] + balwidth = 2 + highscore += "{pos: <{poswidth}} {name: <{namewidth}s} {balance: >{balwidth}}\n".format( + pos=pos, poswidth=poswidth, name=name, namewidth=namewidth, + balance=balance, balwidth=balwidth + ) if highscore != "": for page in pagify(highscore, shorten_by=12): await ctx.send(box(page, lang="py")) diff --git a/redbot/core/bank.py b/redbot/core/bank.py index 51ec1b69b..1130850f0 100644 --- a/redbot/core/bank.py +++ b/redbot/core/bank.py @@ -243,7 +243,7 @@ async def deposit_credits(member: discord.Member, amount: int) -> int: """ if _invalid_amount(amount): - raise ValueError("Invalid withdrawal amount {} <= 0".format(amount)) + raise ValueError("Invalid deposit amount {} <= 0".format(amount)) bal = await get_balance(member) return await set_balance(member, amount + bal) @@ -287,62 +287,81 @@ async def wipe_bank(): await _conf.clear_all_members() -async def get_guild_accounts(guild: discord.Guild) -> List[Account]: - """Get all account data for the given guild. +async def get_leaderboard(positions: int=None, guild: discord.Guild=None) -> List[tuple]: + """ + Gets the bank's leaderboard Parameters ---------- + positions : `int` + The number of positions to get guild : discord.Guild - The guild to get accounts for. + The guild to get the leaderboard of. If the bank is global and this + is provided, get only guild members on the leaderboard Returns ------- - `list` of `Account` - A list of all guild accounts. + `list` of `tuple` + The sorted leaderboard in the form of :code:`(user_id, raw_account)` Raises ------ - RuntimeError - If the bank is currently global. + TypeError + If the bank is guild-specific and no guild was specified """ if await is_global(): - raise RuntimeError("The bank is currently global.") - - ret = [] - accs = await _conf.all_members(guild) - for user_id, acc in accs.items(): - acc_data = acc.copy() # There ya go kowlin - acc_data['created_at'] = _decode_time(acc_data['created_at']) - ret.append(Account(**acc_data)) - return ret + raw_accounts = await _conf.all_users() + if guild is not None: + tmp = raw_accounts.copy() + for acc in tmp: + if not guild.get_member(acc): + del raw_accounts[acc] + else: + if guild is None: + raise TypeError("Expected a guild, got NoneType object instead!") + raw_accounts = await _conf.all_members(guild) + sorted_acc = sorted(raw_accounts.items(), key=lambda x: x[1]['balance'], reverse=True) + if positions is None: + return sorted_acc + else: + return sorted_acc[:positions] -async def get_global_accounts() -> List[Account]: - """Get all global account data. +async def get_leaderboard_position(member: Union[discord.User, discord.Member]) -> Union[int, None]: + """ + Get the leaderboard position for the specified user + + Parameters + ---------- + member : `discord.User` or `discord.Member` + The user to get the leaderboard position of Returns ------- - `list` of `Account` - A list of all global accounts. + `int` + The position of the user on the leaderboard Raises ------ - RuntimeError - If the bank is currently guild specific. + TypeError + If the bank is currently guild-specific and a `discord.User` object was passed in """ - if not await is_global(): - raise RuntimeError("The bank is not currently global.") - - ret = [] - accs = await _conf.all_users() # this is a dict of user -> acc - for user_id, acc in accs.items(): - acc_data = acc.copy() - acc_data['created_at'] = _decode_time(acc_data['created_at']) - ret.append(Account(**acc_data)) - - return ret + if await is_global(): + guild = None + else: + guild = member.guild if hasattr(member, "guild") else None + try: + leaderboard = await get_leaderboard(None, guild) + except TypeError: + raise + else: + pos = discord.utils.find(lambda x: x[1][0] == member.id, enumerate(leaderboard, 1)) + if pos is None: + return None + else: + return pos[0] async def get_account(member: Union[discord.Member, discord.User]) -> Account: