145 lines
5.9 KiB
Python
145 lines
5.9 KiB
Python
import json
|
|
import math
|
|
import typing as t
|
|
from datetime import datetime
|
|
|
|
import discord
|
|
from redbot.core import bank
|
|
from redbot.core.bot import Red
|
|
from redbot.core.i18n import Translator
|
|
|
|
from . import Base
|
|
|
|
_ = Translator("ExtendedEconomy", __file__)
|
|
|
|
|
|
class PaydayClaimInformation(t.NamedTuple):
|
|
member: discord.Member
|
|
channel: t.Union[discord.TextChannel, discord.Thread, discord.ForumChannel]
|
|
message: discord.Message
|
|
amount: int
|
|
old_balance: int
|
|
new_balance: int
|
|
|
|
def to_dict(self) -> dict:
|
|
return {
|
|
"member": self.member.id,
|
|
"channel": self.channel.id,
|
|
"message": self.message.id,
|
|
"amount": self.amount,
|
|
"old_balance": self.old_balance,
|
|
"new_balance": self.new_balance,
|
|
}
|
|
|
|
def to_json(self) -> str:
|
|
return json.dumps(self.to_dict())
|
|
|
|
|
|
class CommandCost(Base):
|
|
"""
|
|
- cost: the base cost of the command
|
|
- duration: the time period in seconds in which the command usage is tracked and used to calculate the cost
|
|
- level: the minimum permission level of the command in which the cost is applied
|
|
- admin: admins and above can use the command for free
|
|
- mod: mods and above can use the command for free
|
|
- all: everyone must pay the cost to use the command
|
|
- user: all users must pay the cost to use the command unless they are mod or admin
|
|
- global: the cost is applied to all users globally
|
|
- prompt: how will the user be prompted to confirm the cost (Slash commands will always use silent mode)
|
|
- text: the bot will send a text message asking the user to confirm the cost with yes or no
|
|
- reaction: the bot will send a message with emoji reactions to confirm the cost
|
|
- button: the bot will send a message with buttons to confirm the cost
|
|
- silent: the bot will not prompt the user to confirm the cost
|
|
- notify: the bot will simply notify the user of the cost without asking for confirmation
|
|
- modifier: the type of cost modifier
|
|
- static: the cost is a fixed amount
|
|
- percent: the cost is a percentage of the user's balance on top of the base cost
|
|
- exponential: the cost increases exponentially based on how frequenty the command is used
|
|
- Ex: cost gets doubled every time the command is used within a set time period
|
|
- linear: the cost increases linearly based on how frequently the command is used
|
|
- Ex: cost gets increased by a fixed amount every time the command is used within a set time period
|
|
- value: the value of the cost modifier depends on the modifier type
|
|
- static: this will be 0 and does nothing
|
|
- percent: value will be the percentage of the user's balance to add to the base cost
|
|
- exponential: value will be the base cost multiplier
|
|
- linear: value will multiplied by the number of uses in the last hour to get the cost increase
|
|
- uses: a list of lists containing the user ID and the timestamp of the command usage
|
|
"""
|
|
|
|
cost: int
|
|
duration: int
|
|
level: t.Literal["admin", "mod", "all", "user", "global"]
|
|
prompt: t.Literal["text", "reaction", "button", "silent", "notify"]
|
|
modifier: t.Literal["static", "percent", "exponential", "linear"]
|
|
value: float
|
|
uses: t.List[t.List[t.Union[int, float]]] = []
|
|
|
|
async def get_cost(self, bot: Red, user: t.Union[discord.Member, discord.User]) -> int:
|
|
if self.level == "global" and user.id in bot.owner_ids:
|
|
return 0
|
|
elif self.level != "global":
|
|
free = [
|
|
await bot.is_admin(user) and self.level in ["admin", "mod"],
|
|
await bot.is_mod(user) and self.level == "mod",
|
|
]
|
|
if any(free):
|
|
return 0
|
|
if self.modifier == "static":
|
|
return self.cost
|
|
if self.modifier == "percent":
|
|
bal = await bank.get_balance(user)
|
|
return math.ceil(self.cost + (bal * self.value))
|
|
min_time = datetime.now().timestamp() - self.duration
|
|
uses_in_duration = len([u for u in self.uses if u[0] == user.id and u[1] > min_time])
|
|
if self.modifier == "exponential":
|
|
return math.ceil(self.cost + self.value * (2**uses_in_duration))
|
|
if self.modifier == "linear":
|
|
return math.ceil(self.cost + (self.value * uses_in_duration))
|
|
raise ValueError(f"Invalid cost modifier: {self.modifier}")
|
|
|
|
def update_usage(self, user_id: int):
|
|
self.uses.append([user_id, datetime.now().timestamp()])
|
|
self.uses = [u for u in self.uses if u[1] > datetime.now().timestamp() - self.duration]
|
|
|
|
|
|
class LogChannels(Base):
|
|
default_log_channel: int = 0
|
|
# Event log channel overrides
|
|
set_balance: int = 0
|
|
transfer_credits: int = 0
|
|
bank_wipe: int = 0
|
|
prune: int = 0
|
|
set_global: int = 0
|
|
payday_claim: int = 0
|
|
|
|
auto_claim: int = 0
|
|
|
|
|
|
class GuildSettings(Base):
|
|
logs: LogChannels = LogChannels()
|
|
command_costs: t.Dict[str, CommandCost] = {}
|
|
transfer_tax: float = 0.0
|
|
transfer_tax_whitelist: t.List[int] = [] # Role IDs that are exempt from transfer tax
|
|
|
|
# Unique to GuildSettings (local bank only)
|
|
stack_paydays: bool = False
|
|
auto_claim_roles: t.List[int] = [] # Role IDs that auto claim paydays
|
|
role_bonuses: t.Dict[int, float] = {} # Role ID: bonus multiplier
|
|
|
|
|
|
class DB(Base):
|
|
configs: t.Dict[int, GuildSettings] = {}
|
|
delete_after: t.Union[int, None] = None
|
|
|
|
logs: LogChannels = LogChannels()
|
|
command_costs: t.Dict[str, CommandCost] = {}
|
|
transfer_tax: float = 0.0
|
|
|
|
auto_payday_claim: bool = False # If True, guilds that set auto_claim_roles will auto claim paydays
|
|
|
|
# Allow prices per guild when global bank is enabled
|
|
# per_guild_override: bool = False
|
|
|
|
def get_conf(self, guild: discord.Guild | int) -> GuildSettings:
|
|
gid = guild if isinstance(guild, int) else guild.id
|
|
return self.configs.setdefault(gid, GuildSettings())
|