feat(rmenus): Support negative prices.
This commit is contained in:
@@ -49,16 +49,20 @@ class CoinSetting(IntegerSetting):
|
|||||||
|
|
||||||
if num > cls._max:
|
if num > cls._max:
|
||||||
t = ctx_translator.get().t
|
t = ctx_translator.get().t
|
||||||
raise UserInputError(t(_p(
|
raise UserInputError(
|
||||||
|
t(_p(
|
||||||
'settype:coin|parse|error:too_large',
|
'settype:coin|parse|error:too_large',
|
||||||
"Provided number of coins was too high!"
|
"You cannot set this to more than {coin}**{max}**!"
|
||||||
))) from None
|
)).format(coin=conf.emojis.coin, max=cls._max)
|
||||||
|
) from None
|
||||||
elif num < cls._min:
|
elif num < cls._min:
|
||||||
t = ctx_translator.get().t
|
t = ctx_translator.get().t
|
||||||
raise UserInputError(t(_p(
|
raise UserInputError(
|
||||||
'settype:coin|parse|error:too_large',
|
t(_p(
|
||||||
"Provided number of coins was too low!"
|
'settype:coin|parse|error:too_small',
|
||||||
))) from None
|
"You cannot set this to less than {coin}**{min}**!"
|
||||||
|
)).format(coin=conf.emojis.coin, min=cls._min)
|
||||||
|
) from None
|
||||||
|
|
||||||
return num
|
return num
|
||||||
|
|
||||||
|
|||||||
@@ -1192,7 +1192,7 @@ class RoleMenuCog(LionCog):
|
|||||||
label: Optional[appcmds.Range[str, 1, 100]] = None,
|
label: Optional[appcmds.Range[str, 1, 100]] = None,
|
||||||
emoji: Optional[appcmds.Range[str, 0, 100]] = None,
|
emoji: Optional[appcmds.Range[str, 0, 100]] = None,
|
||||||
description: 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,
|
duration: Optional[Transform[int, DurationTransformer(60)]] = None,
|
||||||
):
|
):
|
||||||
# Type checking guards
|
# Type checking guards
|
||||||
@@ -1448,7 +1448,7 @@ class RoleMenuCog(LionCog):
|
|||||||
label: Optional[appcmds.Range[str, 1, 100]] = None,
|
label: Optional[appcmds.Range[str, 1, 100]] = None,
|
||||||
emoji: Optional[appcmds.Range[str, 0, 100]] = None,
|
emoji: Optional[appcmds.Range[str, 0, 100]] = None,
|
||||||
description: 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,
|
duration: Optional[Transform[int, DurationTransformer(60)]] = None,
|
||||||
):
|
):
|
||||||
# Type checking wards
|
# Type checking wards
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ class RoleMenu:
|
|||||||
await menu.attach()
|
await menu.attach()
|
||||||
return menu
|
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.
|
Fetch the message the menu is attached to.
|
||||||
"""
|
"""
|
||||||
@@ -529,11 +529,17 @@ class RoleMenu:
|
|||||||
"Role removed"
|
"Role removed"
|
||||||
))
|
))
|
||||||
)
|
)
|
||||||
if total_refund:
|
if total_refund > 0:
|
||||||
embed.description = t(_p(
|
embed.description = t(_p(
|
||||||
'rolemenu|deselect|success:refund|desc',
|
'rolemenu|deselect|success:refund|desc',
|
||||||
"You have removed **{role}**, and been refunded {coin} **{amount}**."
|
"You have removed **{role}**, and been refunded {coin} **{amount}**."
|
||||||
)).format(role=role.name, coin=self.bot.config.emojis.coin, amount=total_refund)
|
)).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:
|
else:
|
||||||
embed.description = t(_p(
|
embed.description = t(_p(
|
||||||
'rolemenu|deselect|success:norefund|desc',
|
'rolemenu|deselect|success:norefund|desc',
|
||||||
@@ -551,7 +557,7 @@ class RoleMenu:
|
|||||||
raise UserInputError(
|
raise UserInputError(
|
||||||
t(_p(
|
t(_p(
|
||||||
'rolemenu|select|error:required_role',
|
'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)
|
)).format(role=name)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -647,7 +653,7 @@ class RoleMenu:
|
|||||||
"Role equipped"
|
"Role equipped"
|
||||||
))
|
))
|
||||||
)
|
)
|
||||||
if price > 0:
|
if price:
|
||||||
embed.description = t(_p(
|
embed.description = t(_p(
|
||||||
'rolemenu|select|success:purchase|desc',
|
'rolemenu|select|success:purchase|desc',
|
||||||
"You have purchased the role **{role}** for {coin}**{amount}**"
|
"You have purchased the role **{role}** for {coin}**{amount}**"
|
||||||
@@ -665,6 +671,7 @@ class RoleMenu:
|
|||||||
)).format(
|
)).format(
|
||||||
timestamp=discord.utils.format_dt(expiry)
|
timestamp=discord.utils.format_dt(expiry)
|
||||||
)
|
)
|
||||||
|
# TODO Event logging
|
||||||
return embed
|
return embed
|
||||||
|
|
||||||
async def interactive_selection(self, interaction: discord.Interaction, menuroleid: int):
|
async def interactive_selection(self, interaction: discord.Interaction, menuroleid: int):
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
|
from typing import Optional
|
||||||
import discord
|
import discord
|
||||||
|
|
||||||
from settings import ModelData
|
from settings import ModelData
|
||||||
from settings.groups import SettingGroup, ModelConfig, SettingDotDict
|
from settings.groups import SettingGroup, ModelConfig, SettingDotDict
|
||||||
from settings.setting_types import (
|
from settings.setting_types import (
|
||||||
RoleSetting, BoolSetting, StringSetting, DurationSetting, EmojiSetting
|
RoleSetting, StringSetting, DurationSetting, EmojiSetting
|
||||||
)
|
)
|
||||||
from core.setting_types import CoinSetting
|
from core.setting_types import CoinSetting
|
||||||
from utils.ui import AButton, AsComponents
|
from utils.ui import AButton, AsComponents
|
||||||
from meta.errors import UserInputError
|
from meta.errors import UserInputError
|
||||||
|
from meta import conf
|
||||||
from babel.translator import ctx_translator
|
from babel.translator import ctx_translator
|
||||||
|
from constants import MAX_COINS
|
||||||
|
|
||||||
from .data import RoleMenuData
|
from .data import RoleMenuData
|
||||||
from . import babel
|
from . import babel
|
||||||
@@ -74,6 +77,9 @@ class RoleMenuRoleOptions(SettingGroup):
|
|||||||
"This menu item will now give the role {role}."
|
"This menu item will now give the role {role}."
|
||||||
)).format(role=self.formatted)
|
)).format(role=self.formatted)
|
||||||
return resp
|
return resp
|
||||||
|
else:
|
||||||
|
raise ValueError("Attempt to call update_message without a value.")
|
||||||
|
|
||||||
|
|
||||||
@RoleMenuRoleConfig.register_model_setting
|
@RoleMenuRoleConfig.register_model_setting
|
||||||
class Label(ModelData, StringSetting):
|
class Label(ModelData, StringSetting):
|
||||||
@@ -138,7 +144,9 @@ class RoleMenuRoleOptions(SettingGroup):
|
|||||||
return button
|
return button
|
||||||
|
|
||||||
@classmethod
|
@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)
|
emojistr = await super()._parse_string(parent_id, string, interaction=interaction, **kwargs)
|
||||||
if emojistr and interaction is not None:
|
if emojistr and interaction is not None:
|
||||||
# Use the interaction to test
|
# Use the interaction to test
|
||||||
@@ -151,7 +159,7 @@ class RoleMenuRoleOptions(SettingGroup):
|
|||||||
view=view,
|
view=view,
|
||||||
)
|
)
|
||||||
except discord.HTTPException:
|
except discord.HTTPException:
|
||||||
t = interaction.client.translator.t
|
t = ctx_translator.get().t
|
||||||
raise UserInputError(t(_p(
|
raise UserInputError(t(_p(
|
||||||
'roleset:emoji|error:test_emoji',
|
'roleset:emoji|error:test_emoji',
|
||||||
"The selected emoji `{emoji}` is invalid or has been deleted."
|
"The selected emoji `{emoji}` is invalid or has been deleted."
|
||||||
@@ -218,34 +226,43 @@ class RoleMenuRoleOptions(SettingGroup):
|
|||||||
_display_name = _p('roleset:price', "price")
|
_display_name = _p('roleset:price', "price")
|
||||||
_desc = _p(
|
_desc = _p(
|
||||||
'roleset:price|desc',
|
'roleset:price|desc',
|
||||||
"Price of the role, in LionCoins."
|
"Price of the role, in LionCoins. May be negative."
|
||||||
)
|
)
|
||||||
_long_desc = _p(
|
_long_desc = _p(
|
||||||
'roleset:price|long_desc',
|
'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(
|
_accepts = _p(
|
||||||
'roleset:price|accepts',
|
'roleset:price|accepts',
|
||||||
"Amount of coins that the role costs."
|
"Amount of coins that the role costs when equipped."
|
||||||
)
|
)
|
||||||
_default = 0
|
_default = 0
|
||||||
|
_min = - MAX_COINS
|
||||||
_model = RoleMenuData.RoleMenuRole
|
_model = RoleMenuData.RoleMenuRole
|
||||||
_column = RoleMenuData.RoleMenuRole.price.name
|
_column = RoleMenuData.RoleMenuRole.price.name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def update_message(self) -> str:
|
def update_message(self) -> str:
|
||||||
t = ctx_translator.get().t
|
t = ctx_translator.get().t
|
||||||
value = self.value
|
value = self.value or 0
|
||||||
if value:
|
if value > 0:
|
||||||
resp = t(_p(
|
resp = t(_p(
|
||||||
'roleset:price|set_response:set',
|
'roleset:price|set_response:positive',
|
||||||
"This role will now cost {price} to equip."
|
"Equipping this role will now cost {coin}**{price}**."
|
||||||
)).format(price=self.formatted)
|
)).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:
|
else:
|
||||||
resp = t(_p(
|
resp = t(_p(
|
||||||
'roleset:price|set_response:unset',
|
'roleset:price|set_response:negative',
|
||||||
"This role will now be free to equip from this role menu."
|
"Equipping this role will now reward {coin}**{price}**."
|
||||||
))
|
)).format(price=-value, coin=conf.emojis.coin)
|
||||||
return resp
|
return resp
|
||||||
|
|
||||||
@RoleMenuRoleConfig.register_model_setting
|
@RoleMenuRoleConfig.register_model_setting
|
||||||
|
|||||||
Reference in New Issue
Block a user