[Bank] Allow bank managers to set the maximum allowed balance in the bank (#2926)

* Removes `MAX_BALANCE` from bank, user `bank.get_max_balance()` now
`[p]bankset maxbal` can be used to set the maximum bank balance

Signed-off-by: Guy <guyreis96@gmail.com>

* Removes `MAX_BALANCE` from bank, user `bank.get_max_balance()` now
`[p]bankset maxbal` can be used to set the maximum bank balance

Signed-off-by: Guy <guyreis96@gmail.com>

* Removes `MAX_BALANCE` from bank, user `bank.get_max_balance()` now
`[p]bankset maxbal` can be used to set the maximum bank balance

Signed-off-by: Guy <guyreis96@gmail.com>

* Rename method 🤦

Signed-off-by: Guy <guyreis96@gmail.com>

* Updated this to be aware of #2925

Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com>

* Addressed Flames review + Fixed 1 bug in errors.py + `[p]leaderboard` and `[p]bank balance` will set the users balance to max balance if the bank maxbal is lower than the previous user balance

Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com>

* Missed this clarification

Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com>

* address Flames review

Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com>
This commit is contained in:
Draper 2019-08-30 02:05:31 +01:00 committed by Michael H
parent b490942bcd
commit e04eed4a89
6 changed files with 159 additions and 26 deletions

View File

@ -0,0 +1 @@
Removed :cons:`bank.MAX_BALANCE`, use :meth:`bank.get_max_balance()` from now.

View File

@ -0,0 +1 @@
`[p]bankset maxbal` can be used to set the maximum bank balance.

View File

@ -86,11 +86,12 @@ class Bank(commands.Cog):
settings = _(
"Bank settings:\n\nBank name: {bank_name}\nCurrency: {currency_name}\n"
"Default balance: {default_balance}"
"Default balance: {default_balance}\nMaximum allowed balance: {maximum_bal}"
).format(
bank_name=bank_name,
currency_name=currency_name,
default_balance=humanize_number(default_balance),
maximum_bal=humanize_number(await bank.get_max_balance(ctx.guild)),
)
await ctx.send(box(settings))
@ -130,4 +131,21 @@ class Bank(commands.Cog):
await bank.set_currency_name(name, ctx.guild)
await ctx.send(_("Currency name has been set to: {name}").format(name=name))
@bankset.command(name="maxbal")
@check_global_setting_guildowner()
async def bankset_maxbal(self, ctx: commands.Context, *, amount: int):
"""Set the maximum balance a user can get."""
try:
await bank.set_max_balance(amount, ctx.guild)
except ValueError:
# noinspection PyProtectedMember
return await ctx.send(
_("Amount must be greater than zero and less than {max}.").format(
max=humanize_number(bank._MAX_BALANCE)
)
)
await ctx.send(
_("Maximum balance has been set to: {amount}").format(amount=humanize_number(amount))
)
# ENDSECTION

View File

@ -1,7 +1,7 @@
import calendar
import logging
import random
from collections import defaultdict, deque
from collections import defaultdict, deque, namedtuple
from enum import Enum
from typing import cast, Iterable
@ -20,6 +20,7 @@ T_ = Translator("Economy", __file__)
logger = logging.getLogger("red.economy")
NUM_ENC = "\N{COMBINING ENCLOSING KEYCAP}"
MOCK_MEMBER = namedtuple("Member", "id guild")
class SMReel(Enum):
@ -159,7 +160,10 @@ class Economy(commands.Cog):
bal = await bank.get_balance(user)
currency = await bank.get_currency_name(ctx.guild)
max_bal = await bank.get_max_balance(ctx.guild)
if bal > max_bal:
bal = max_bal
await bank.set_balance(user, bal)
await ctx.send(
_("{user}'s balance is {num} {currency}").format(
user=user.display_name, num=humanize_number(bal), currency=currency
@ -363,6 +367,7 @@ class Economy(commands.Cog):
"""
guild = ctx.guild
author = ctx.author
max_bal = await bank.get_max_balance(ctx.guild)
if top < 1:
top = 10
if await bank.is_global() and show_global:
@ -372,6 +377,9 @@ class Economy(commands.Cog):
bank_sorted = await bank.get_leaderboard(positions=top, guild=guild)
try:
bal_len = len(humanize_number(bank_sorted[0][1]["balance"]))
bal_len_max = len(humanize_number(max_bal))
if bal_len > bal_len_max:
bal_len = bal_len_max
# first user is the largest we'll see
except IndexError:
return await ctx.send(_("There are no accounts in the bank."))
@ -394,8 +402,12 @@ class Economy(commands.Cog):
if await ctx.bot.is_owner(ctx.author):
user_id = f"({str(acc[0])})"
name = f"{acc[1]['name']} {user_id}"
balance = humanize_number(acc[1]["balance"])
balance = acc[1]["balance"]
if balance > max_bal:
balance = max_bal
await bank.set_balance(MOCK_MEMBER(acc[0], guild), balance)
balance = humanize_number(balance)
if acc[0] != author.id:
temp_msg += (
f"{f'{humanize_number(pos)}.': <{pound_len+2}} "
@ -560,7 +572,8 @@ class Economy(commands.Cog):
"Slot cooldown: {slot_time}\n"
"Payday amount: {payday_amount}\n"
"Payday cooldown: {payday_time}\n"
"Amount given at account registration: {register_amount}"
"Amount given at account registration: {register_amount}\n"
"Maximum allowed balance: {maximum_bal}"
).format(
slot_min=humanize_number(await conf.SLOT_MIN()),
slot_max=humanize_number(await conf.SLOT_MAX()),
@ -568,6 +581,7 @@ class Economy(commands.Cog):
payday_time=humanize_number(await conf.PAYDAY_TIME()),
payday_amount=humanize_number(await conf.PAYDAY_CREDITS()),
register_amount=humanize_number(await bank.get_default_balance(guild)),
maximum_bal=humanize_number(await bank.get_max_balance(guild)),
)
)
)
@ -639,9 +653,13 @@ class Economy(commands.Cog):
async def paydayamount(self, ctx: commands.Context, creds: int):
"""Set the amount earned each payday."""
guild = ctx.guild
if creds <= 0 or creds > bank.MAX_BALANCE:
await ctx.send(_("Har har so funny."))
return
max_balance = await bank.get_max_balance(ctx.guild)
if creds <= 0 or creds > max_balance:
return await ctx.send(
_("Amount must be greater than zero and less than {maxbal}.").format(
maxbal=humanize_number(max_balance)
)
)
credits_name = await bank.get_currency_name(guild)
if await bank.is_global():
await self.config.PAYDAY_CREDITS.set(creds)
@ -657,9 +675,13 @@ class Economy(commands.Cog):
async def rolepaydayamount(self, ctx: commands.Context, role: discord.Role, creds: int):
"""Set the amount earned each payday for a role."""
guild = ctx.guild
if creds <= 0 or creds > bank.MAX_BALANCE:
await ctx.send(_("Har har so funny."))
return
max_balance = await bank.get_max_balance(ctx.guild)
if creds <= 0 or creds > max_balance:
return await ctx.send(
_("Amount must be greater than zero and less than {maxbal}.").format(
maxbal=humanize_number(max_balance)
)
)
credits_name = await bank.get_currency_name(guild)
if await bank.is_global():
await ctx.send(_("The bank must be per-server for per-role paydays to work."))
@ -676,10 +698,16 @@ class Economy(commands.Cog):
async def registeramount(self, ctx: commands.Context, creds: int):
"""Set the initial balance for new bank accounts."""
guild = ctx.guild
if creds < 0:
creds = 0
max_balance = await bank.get_max_balance(ctx.guild)
credits_name = await bank.get_currency_name(guild)
try:
await bank.set_default_balance(creds, guild)
except ValueError:
return await ctx.send(
_("Amount must be greater than or equal to zero and less than {maxbal}.").format(
maxbal=humanize_number(max_balance)
)
)
await ctx.send(
_("Registering an account will now give {num} {currency}.").format(
num=humanize_number(creds), currency=credits_name

View File

@ -12,7 +12,6 @@ from .i18n import Translator
_ = Translator("Bank API", __file__)
__all__ = [
"MAX_BALANCE",
"Account",
"get_balance",
"set_balance",
@ -30,20 +29,28 @@ __all__ = [
"set_currency_name",
"get_default_balance",
"set_default_balance",
"get_max_balance",
"set_max_balance",
"cost",
"AbortPurchase",
]
MAX_BALANCE = 2 ** 63 - 1
_MAX_BALANCE = 2 ** 63 - 1
_DEFAULT_GLOBAL = {
"is_global": False,
"bank_name": "Twentysix bank",
"currency": "credits",
"default_balance": 100,
"max_balance": _MAX_BALANCE,
}
_DEFAULT_GUILD = {"bank_name": "Twentysix bank", "currency": "credits", "default_balance": 100}
_DEFAULT_GUILD = {
"bank_name": "Twentysix bank",
"currency": "credits",
"default_balance": 100,
"max_balance": _MAX_BALANCE,
}
_DEFAULT_MEMBER = {"name": "", "balance": 0, "created_at": 0}
@ -186,10 +193,11 @@ async def set_balance(member: discord.Member, amount: int) -> int:
"""
if amount < 0:
raise ValueError("Not allowed to have negative balance.")
if amount > MAX_BALANCE:
max_bal = await get_max_balance(member.guild)
if amount > max_bal:
currency = await get_currency_name(member.guild)
raise errors.BalanceTooHigh(
user=member.display_name, max_balance=MAX_BALANCE, currency_name=currency
user=member.display_name, max_balance=max_bal, currency_name=currency
)
if await is_global():
group = _conf.user(member)
@ -329,10 +337,12 @@ async def transfer_credits(from_: discord.Member, to: discord.Member, amount: in
)
)
if await get_balance(to) + amount > MAX_BALANCE:
max_bal = await get_max_balance(to.guild)
if await get_balance(to) + amount > max_bal:
currency = await get_currency_name(to.guild)
raise errors.BalanceTooHigh(
user=to.display_name, max_balance=MAX_BALANCE, currency_name=currency
user=to.display_name, max_balance=max_bal, currency_name=currency
)
await withdraw_credits(from_, amount)
@ -635,6 +645,75 @@ async def set_currency_name(name: str, guild: discord.Guild = None) -> str:
return name
async def get_max_balance(guild: discord.Guild = None) -> int:
"""Get the max balance for the bank.
Parameters
----------
guild : `discord.Guild`, optional
The guild to get the max balance for (required if bank is
guild-specific).
Returns
-------
int
The maximum allowed balance.
Raises
------
RuntimeError
If the bank is guild-specific and guild was not provided.
"""
if await is_global():
return await _conf.max_balance()
elif guild is not None:
return await _conf.guild(guild).max_balance()
else:
raise RuntimeError("Guild must be provided.")
async def set_max_balance(amount: int, guild: discord.Guild = None) -> int:
"""Set the maximum balance for the bank.
Parameters
----------
amount : int
The new maximum balance.
guild : `discord.Guild`, optional
The guild to set the max balance for (required if bank is
guild-specific).
Returns
-------
int
The new maximum balance.
Raises
------
RuntimeError
If the bank is guild-specific and guild was not provided.
ValueError
If the amount is less than 0 or higher than 2 ** 63 - 1.
"""
if not (0 < amount <= _MAX_BALANCE):
raise ValueError(
"Amount must be greater than zero and less than {max}.".format(
max=humanize_number(_MAX_BALANCE, override_locale="en_US")
)
)
if await is_global():
await _conf.max_balance.set(amount)
elif guild is not None:
await _conf.guild(guild).max_balance.set(amount)
else:
raise RuntimeError(
"Guild must be provided if setting the maximum balance of a guild-specific bank."
)
return amount
async def get_default_balance(guild: discord.Guild = None) -> int:
"""Get the current default balance amount.
@ -684,12 +763,18 @@ async def set_default_balance(amount: int, guild: discord.Guild = None) -> int:
RuntimeError
If the bank is guild-specific and guild was not provided.
ValueError
If the amount is invalid.
If the amount is less than 0 or higher than the max allowed balance.
"""
amount = int(amount)
if amount < 0:
raise ValueError("Amount must be greater than zero.")
max_bal = await get_max_balance(guild)
if not (0 < amount <= max_bal):
raise ValueError(
"Amount must be greater than zero and less than {max}.".format(
max=humanize_number(max_bal, override_locale="en_US")
)
)
if await is_global():
await _conf.default_balance.set(amount)
@ -714,7 +799,7 @@ def cost(amount: int):
You can intentionally refund by raising `AbortPurchase`
(this error will be consumed and not show to users)
Other exceptions will propogate and will be handled by Red's (and/or
Other exceptions will propagate and will be handled by Red's (and/or
any other configured) error handling.
"""
if not isinstance(amount, int) or amount < 0:

View File

@ -46,7 +46,7 @@ class BalanceTooHigh(BankError, OverflowError):
self.currency_name = currency_name
def __str__(self) -> str:
return _("{user}'s balance cannot rise above max {currency}.").format(
return _("{user}'s balance cannot rise above {max} {currency}.").format(
user=self.user, max=humanize_number(self.max_balance), currency=self.currency_name
)