feat(rmenus): Support negative prices.

This commit is contained in:
2023-09-23 15:28:12 +03:00
parent 576d7cf02f
commit 5675f72853
4 changed files with 56 additions and 28 deletions

View File

@@ -49,16 +49,20 @@ class CoinSetting(IntegerSetting):
if num > cls._max:
t = ctx_translator.get().t
raise UserInputError(t(_p(
'settype:coin|parse|error:too_large',
"Provided number of coins was too high!"
))) from None
raise UserInputError(
t(_p(
'settype:coin|parse|error:too_large',
"You cannot set this to more than {coin}**{max}**!"
)).format(coin=conf.emojis.coin, max=cls._max)
) from None
elif num < cls._min:
t = ctx_translator.get().t
raise UserInputError(t(_p(
'settype:coin|parse|error:too_large',
"Provided number of coins was too low!"
))) from None
raise UserInputError(
t(_p(
'settype:coin|parse|error:too_small',
"You cannot set this to less than {coin}**{min}**!"
)).format(coin=conf.emojis.coin, min=cls._min)
) from None
return num

View File

@@ -1192,7 +1192,7 @@ class RoleMenuCog(LionCog):
label: Optional[appcmds.Range[str, 1, 100]] = None,
emoji: Optional[appcmds.Range[str, 0, 100]] = None,
description: Optional[appcmds.Range[str, 0, 100]] = None,
price: Optional[appcmds.Range[int, 0, MAX_COINS]] = None,
price: Optional[appcmds.Range[int, -MAX_COINS, MAX_COINS]] = None,
duration: Optional[Transform[int, DurationTransformer(60)]] = None,
):
# Type checking guards
@@ -1448,7 +1448,7 @@ class RoleMenuCog(LionCog):
label: Optional[appcmds.Range[str, 1, 100]] = None,
emoji: Optional[appcmds.Range[str, 0, 100]] = None,
description: Optional[appcmds.Range[str, 0, 100]] = None,
price: Optional[appcmds.Range[int, 0, MAX_COINS]] = None,
price: Optional[appcmds.Range[int, -MAX_COINS, MAX_COINS]] = None,
duration: Optional[Transform[int, DurationTransformer(60)]] = None,
):
# Type checking wards

View File

@@ -165,7 +165,7 @@ class RoleMenu:
await menu.attach()
return menu
async def fetch_message(self, refresh=False):
async def fetch_message(self, refresh=False) -> Optional[discord.Message]:
"""
Fetch the message the menu is attached to.
"""
@@ -529,11 +529,17 @@ class RoleMenu:
"Role removed"
))
)
if total_refund:
if total_refund > 0:
embed.description = t(_p(
'rolemenu|deselect|success:refund|desc',
"You have removed **{role}**, and been refunded {coin} **{amount}**."
)).format(role=role.name, coin=self.bot.config.emojis.coin, amount=total_refund)
if total_refund < 0:
# TODO: Consider disallowing them from removing roles if their balance would go negative
embed.description = t(_p(
'rolemenu|deselect|success:negrefund|desc',
"You have removed **{role}**, and have lost {coin} **{amount}**."
)).format(role=role.name, coin=self.bot.config.emojis.coin, amount=-total_refund)
else:
embed.description = t(_p(
'rolemenu|deselect|success:norefund|desc',
@@ -551,7 +557,7 @@ class RoleMenu:
raise UserInputError(
t(_p(
'rolemenu|select|error:required_role',
"You need to have the **{role}** role to use this!"
"You need to have the role **{role}** required to use this menu!"
)).format(role=name)
)
@@ -647,7 +653,7 @@ class RoleMenu:
"Role equipped"
))
)
if price > 0:
if price:
embed.description = t(_p(
'rolemenu|select|success:purchase|desc',
"You have purchased the role **{role}** for {coin}**{amount}**"
@@ -665,6 +671,7 @@ class RoleMenu:
)).format(
timestamp=discord.utils.format_dt(expiry)
)
# TODO Event logging
return embed
async def interactive_selection(self, interaction: discord.Interaction, menuroleid: int):

View File

@@ -1,14 +1,17 @@
from typing import Optional
import discord
from settings import ModelData
from settings.groups import SettingGroup, ModelConfig, SettingDotDict
from settings.setting_types import (
RoleSetting, BoolSetting, StringSetting, DurationSetting, EmojiSetting
RoleSetting, StringSetting, DurationSetting, EmojiSetting
)
from core.setting_types import CoinSetting
from utils.ui import AButton, AsComponents
from meta.errors import UserInputError
from meta import conf
from babel.translator import ctx_translator
from constants import MAX_COINS
from .data import RoleMenuData
from . import babel
@@ -74,6 +77,9 @@ class RoleMenuRoleOptions(SettingGroup):
"This menu item will now give the role {role}."
)).format(role=self.formatted)
return resp
else:
raise ValueError("Attempt to call update_message without a value.")
@RoleMenuRoleConfig.register_model_setting
class Label(ModelData, StringSetting):
@@ -138,7 +144,9 @@ class RoleMenuRoleOptions(SettingGroup):
return button
@classmethod
async def _parse_string(cls, parent_id, string: str, interaction: discord.Interaction = None, **kwargs):
async def _parse_string(cls, parent_id, string: str,
interaction: Optional[discord.Interaction] = None,
**kwargs):
emojistr = await super()._parse_string(parent_id, string, interaction=interaction, **kwargs)
if emojistr and interaction is not None:
# Use the interaction to test
@@ -151,7 +159,7 @@ class RoleMenuRoleOptions(SettingGroup):
view=view,
)
except discord.HTTPException:
t = interaction.client.translator.t
t = ctx_translator.get().t
raise UserInputError(t(_p(
'roleset:emoji|error:test_emoji',
"The selected emoji `{emoji}` is invalid or has been deleted."
@@ -218,34 +226,43 @@ class RoleMenuRoleOptions(SettingGroup):
_display_name = _p('roleset:price', "price")
_desc = _p(
'roleset:price|desc',
"Price of the role, in LionCoins."
"Price of the role, in LionCoins. May be negative."
)
_long_desc = _p(
'roleset:price|long_desc',
"How much the role costs when selected, in LionCoins."
"How many LionCoins should be deducted from a member's account "
"when they equip this role through this menu.\n"
"The price may be negative, in which case the member will instead be rewarded "
"coins when they equip the role."
)
_accepts = _p(
'roleset:price|accepts',
"Amount of coins that the role costs."
"Amount of coins that the role costs when equipped."
)
_default = 0
_min = - MAX_COINS
_model = RoleMenuData.RoleMenuRole
_column = RoleMenuData.RoleMenuRole.price.name
@property
def update_message(self) -> str:
t = ctx_translator.get().t
value = self.value
if value:
value = self.value or 0
if value > 0:
resp = t(_p(
'roleset:price|set_response:set',
"This role will now cost {price} to equip."
)).format(price=self.formatted)
'roleset:price|set_response:positive',
"Equipping this role will now cost {coin}**{price}**."
)).format(price=value, coin=conf.emojis.coin)
elif value == 0:
resp = t(_p(
'roleset:price|set_response:zero',
"Equipping this role is now free."
))
else:
resp = t(_p(
'roleset:price|set_response:unset',
"This role will now be free to equip from this role menu."
))
'roleset:price|set_response:negative',
"Equipping this role will now reward {coin}**{price}**."
)).format(price=-value, coin=conf.emojis.coin)
return resp
@RoleMenuRoleConfig.register_model_setting