311 lines
15 KiB
Python
311 lines
15 KiB
Python
from __future__ import annotations
|
|
|
|
import datetime
|
|
import logging
|
|
from collections.abc import Awaitable, Callable
|
|
|
|
import discord
|
|
from discord import app_commands
|
|
|
|
import economy
|
|
import strings as S
|
|
|
|
|
|
async def _dm_user(bot: discord.Client, user_id: int, msg: str) -> None:
|
|
"""Best-effort DM to a user."""
|
|
try:
|
|
user = bot.get_user(user_id) or await bot.fetch_user(user_id)
|
|
await user.send(msg)
|
|
except Exception:
|
|
pass
|
|
|
|
|
|
def register_economy_admin_commands(
|
|
tree: app_commands.CommandTree,
|
|
bot: discord.Client,
|
|
log: logging.Logger,
|
|
cd_ts: Callable[[datetime.timedelta], str],
|
|
apply_level_role: Callable[[discord.Member, int, int], Awaitable[None]],
|
|
) -> None:
|
|
@tree.command(name="adminseason", description=S.CMD["adminseason"])
|
|
@app_commands.guild_only()
|
|
@app_commands.default_permissions(manage_guild=True)
|
|
@app_commands.describe(top_n=S.OPT["adminseason_top_n"])
|
|
async def cmd_adminseason(interaction: discord.Interaction, top_n: int = 10):
|
|
await interaction.response.defer(ephemeral=True)
|
|
top = await economy.do_season_reset(top_n)
|
|
guild = interaction.guild
|
|
|
|
if guild:
|
|
all_role_names = {name for _, name in economy.LEVEL_ROLES}
|
|
for role_name in all_role_names:
|
|
role = discord.utils.find(lambda r: r.name == role_name, guild.roles)
|
|
if not role:
|
|
continue
|
|
for member in list(role.members):
|
|
try:
|
|
await member.remove_roles(role, reason="Season reset")
|
|
except discord.Forbidden:
|
|
pass
|
|
|
|
medals = ["🥇", "🥈", "🥉"]
|
|
lines = []
|
|
for i, (uid, exp, lvl) in enumerate(top):
|
|
member = guild.get_member(int(uid)) if guild else None
|
|
name = member.display_name if member else S.LEADERBOARD_UI["unknown_user"].format(uid=uid)
|
|
prefix = medals[i] if i < 3 else f"**{i + 1}.**"
|
|
lines.append(S.SEASON["entry"].format(prefix=prefix, name=name, exp=exp, level=lvl))
|
|
|
|
embed = discord.Embed(
|
|
title=S.TITLE["adminseason"],
|
|
description=(S.SEASON["top_header"] + "\n" + "\n".join(lines)) if lines else S.SEASON["no_players"],
|
|
color=0xF4C430,
|
|
)
|
|
embed.set_footer(text=S.SEASON["footer"])
|
|
await interaction.followup.send(embed=embed, ephemeral=False)
|
|
await interaction.followup.send(S.SEASON["done"], ephemeral=True)
|
|
log.info("/adminseason triggered by %s (top_n=%d)", interaction.user, top_n)
|
|
|
|
@tree.command(name="admincoins", description=S.CMD["admincoins"])
|
|
@app_commands.guild_only()
|
|
@app_commands.describe(
|
|
kasutaja=S.OPT["admin_kasutaja"],
|
|
kogus=S.OPT["admincoins_kogus"],
|
|
põhjus=S.OPT["admin_põhjus"],
|
|
)
|
|
@app_commands.default_permissions(manage_guild=True)
|
|
async def cmd_admincoins(interaction: discord.Interaction, kasutaja: discord.Member, kogus: int, põhjus: str):
|
|
if kogus == 0:
|
|
await interaction.response.send_message(S.ERR["admincoins_zero"], ephemeral=True)
|
|
return
|
|
res = await economy.do_admin_coins(kasutaja.id, kogus, interaction.user.id, põhjus)
|
|
verb = f"+{kogus}" if kogus > 0 else str(kogus)
|
|
emoji = "💰" if kogus > 0 else "💸"
|
|
await interaction.response.send_message(
|
|
S.ADMIN["coins_done"].format(
|
|
emoji=emoji,
|
|
name=kasutaja.display_name,
|
|
verb=verb,
|
|
coin=economy.COIN,
|
|
balance=f"{res['balance']:,}",
|
|
reason=põhjus,
|
|
),
|
|
ephemeral=True,
|
|
)
|
|
await _dm_user(
|
|
bot,
|
|
kasutaja.id,
|
|
S.ADMIN["coins_dm"].format(
|
|
emoji=emoji,
|
|
verb=verb,
|
|
coin=economy.COIN,
|
|
reason=põhjus,
|
|
balance=f"{res['balance']:,}",
|
|
),
|
|
)
|
|
log.info("ADMINCOINS %s → %s (%s) by %s", verb, kasutaja, põhjus, interaction.user)
|
|
|
|
@tree.command(name="adminjail", description=S.CMD["adminjail"])
|
|
@app_commands.guild_only()
|
|
@app_commands.describe(
|
|
kasutaja=S.OPT["admin_kasutaja"],
|
|
minutid=S.OPT["adminjail_minutid"],
|
|
põhjus=S.OPT["admin_põhjus"],
|
|
)
|
|
@app_commands.default_permissions(manage_guild=True)
|
|
async def cmd_adminjail(interaction: discord.Interaction, kasutaja: discord.Member, minutid: int, põhjus: str):
|
|
if minutid <= 0:
|
|
await interaction.response.send_message(S.ERR["positive_duration"], ephemeral=True)
|
|
return
|
|
await economy.do_admin_jail(kasutaja.id, minutid, interaction.user.id, põhjus)
|
|
until_ts = cd_ts(datetime.timedelta(minutes=minutid))
|
|
await interaction.response.send_message(
|
|
S.ADMIN["jail_done"].format(name=kasutaja.display_name, minutes=minutid, ts=until_ts, reason=põhjus),
|
|
ephemeral=True,
|
|
)
|
|
await _dm_user(
|
|
bot,
|
|
kasutaja.id,
|
|
S.ADMIN["jail_dm"].format(minutes=minutid, reason=põhjus, ts=until_ts),
|
|
)
|
|
log.info("ADMINJAIL %s %dmin (%s) by %s", kasutaja, minutid, põhjus, interaction.user)
|
|
|
|
@tree.command(name="adminunjail", description=S.CMD["adminunjail"])
|
|
@app_commands.guild_only()
|
|
@app_commands.describe(kasutaja=S.OPT["admin_kasutaja"])
|
|
@app_commands.default_permissions(manage_guild=True)
|
|
async def cmd_adminunjail(interaction: discord.Interaction, kasutaja: discord.Member):
|
|
await economy.do_admin_unjail(kasutaja.id, interaction.user.id)
|
|
await interaction.response.send_message(
|
|
S.ADMIN["unjail_done"].format(name=kasutaja.display_name), ephemeral=True
|
|
)
|
|
await _dm_user(bot, kasutaja.id, S.ADMIN["unjail_dm"])
|
|
log.info("ADMINUNJAIL %s by %s", kasutaja, interaction.user)
|
|
|
|
@tree.command(name="adminban", description=S.CMD["adminban"])
|
|
@app_commands.guild_only()
|
|
@app_commands.describe(
|
|
kasutaja=S.OPT["admin_kasutaja"],
|
|
põhjus=S.OPT["admin_põhjus"],
|
|
)
|
|
@app_commands.default_permissions(manage_guild=True)
|
|
async def cmd_adminban(interaction: discord.Interaction, kasutaja: discord.Member, põhjus: str):
|
|
if bot.user and kasutaja.id == bot.user.id:
|
|
await interaction.response.send_message(S.ERR["admin_ban_bot"], ephemeral=True)
|
|
return
|
|
await economy.do_admin_ban(kasutaja.id, interaction.user.id, põhjus)
|
|
await interaction.response.send_message(
|
|
S.ADMIN["ban_done"].format(name=kasutaja.display_name, reason=põhjus),
|
|
ephemeral=True,
|
|
)
|
|
await _dm_user(bot, kasutaja.id, S.ADMIN["ban_dm"].format(reason=põhjus))
|
|
log.info("ADMINBAN %s (%s) by %s", kasutaja, põhjus, interaction.user)
|
|
|
|
@tree.command(name="adminunban", description=S.CMD["adminunban"])
|
|
@app_commands.guild_only()
|
|
@app_commands.describe(kasutaja=S.OPT["admin_kasutaja"])
|
|
@app_commands.default_permissions(manage_guild=True)
|
|
async def cmd_adminunban(interaction: discord.Interaction, kasutaja: discord.Member):
|
|
await economy.do_admin_unban(kasutaja.id, interaction.user.id)
|
|
await interaction.response.send_message(
|
|
S.ADMIN["unban_done"].format(name=kasutaja.display_name), ephemeral=True
|
|
)
|
|
await _dm_user(bot, kasutaja.id, S.ADMIN["unban_dm"])
|
|
log.info("ADMINUNBAN %s by %s", kasutaja, interaction.user)
|
|
|
|
@tree.command(name="adminreset", description=S.CMD["adminreset"])
|
|
@app_commands.guild_only()
|
|
@app_commands.describe(
|
|
kasutaja=S.OPT["admin_kasutaja"],
|
|
põhjus=S.OPT["admin_põhjus"],
|
|
)
|
|
@app_commands.default_permissions(manage_guild=True)
|
|
async def cmd_adminreset(interaction: discord.Interaction, kasutaja: discord.Member, põhjus: str):
|
|
if bot.user and kasutaja.id == bot.user.id:
|
|
await interaction.response.send_message(S.ERR["admin_reset_bot"], ephemeral=True)
|
|
return
|
|
await economy.do_admin_reset(kasutaja.id, interaction.user.id)
|
|
await interaction.response.send_message(
|
|
S.ADMIN["reset_done"].format(name=kasutaja.display_name, reason=põhjus),
|
|
ephemeral=True,
|
|
)
|
|
await _dm_user(bot, kasutaja.id, S.ADMIN["reset_dm"].format(reason=põhjus))
|
|
log.info("ADMINRESET %s (%s) by %s", kasutaja, põhjus, interaction.user)
|
|
|
|
@tree.command(name="adminview", description=S.CMD["adminview"])
|
|
@app_commands.guild_only()
|
|
@app_commands.describe(kasutaja=S.OPT["admin_kasutaja"])
|
|
@app_commands.default_permissions(manage_guild=True)
|
|
async def cmd_adminview(interaction: discord.Interaction, kasutaja: discord.Member):
|
|
res = await economy.do_admin_inspect(kasutaja.id)
|
|
data = res["data"]
|
|
items_str = ", ".join(data.get("items", [])) or "-"
|
|
uses = data.get("item_uses", {})
|
|
uses_str = ", ".join(f"{k}:{v}" for k, v in uses.items()) if uses else "-"
|
|
jailed = data.get("jailed_until") or "-"
|
|
banned = S.ADMINVIEW_UI["banned_yes"] if data.get("eco_banned") else S.ADMINVIEW_UI["banned_no"]
|
|
exp = data.get("exp", 0)
|
|
level = economy.get_level(exp)
|
|
prestige_lvl = data.get("prestige_level", 0)
|
|
prestige_pp = data.get("prestige_points", 0)
|
|
total_fish = data.get("total_fish_caught", 0)
|
|
inv_fish = len(data.get("fish_inventory") or [])
|
|
embed = discord.Embed(
|
|
title=S.ADMINVIEW_UI["title"].format(name=kasutaja.display_name),
|
|
color=0x5865F2,
|
|
)
|
|
embed.add_field(name=S.ADMINVIEW_UI["f_balance"], value=f"{data.get('balance', 0):,} {economy.COIN}", inline=True)
|
|
embed.add_field(name=S.ADMINVIEW_UI["f_exp"], value=S.ADMINVIEW_UI["exp_val"].format(exp=f"{exp:,}", level=level), inline=True)
|
|
embed.add_field(name=S.ADMINVIEW_UI["f_streak"], value=str(data.get("daily_streak", 0)), inline=True)
|
|
embed.add_field(name=S.ADMINVIEW_UI["f_banned"], value=banned, inline=True)
|
|
embed.add_field(name=S.ADMINVIEW_UI["f_jailed"], value=jailed, inline=True)
|
|
embed.add_field(name=S.ADMINVIEW_UI["f_prestige"], value=S.ADMINVIEW_UI["prestige_val"].format(level=prestige_lvl, pp=prestige_pp), inline=True)
|
|
embed.add_field(name=S.ADMINVIEW_UI["f_fish"], value=S.ADMINVIEW_UI["fish_val"].format(caught=total_fish, inv=inv_fish), inline=True)
|
|
embed.add_field(name=S.ADMINVIEW_UI["f_items"], value=items_str, inline=False)
|
|
embed.add_field(name=S.ADMINVIEW_UI["f_uses"], value=uses_str, inline=False)
|
|
embed.add_field(name=S.ADMINVIEW_UI["f_last_daily"], value=data.get("last_daily") or "-", inline=True)
|
|
embed.add_field(name=S.ADMINVIEW_UI["f_last_work"], value=data.get("last_work") or "-", inline=True)
|
|
embed.add_field(name=S.ADMINVIEW_UI["f_last_crime"], value=data.get("last_crime") or "-", inline=True)
|
|
embed.add_field(name=S.ADMINVIEW_UI["f_last_fish"], value=data.get("last_fish") or "-", inline=True)
|
|
embed.set_footer(text=S.ADMINVIEW_UI["footer"].format(uid=kasutaja.id))
|
|
await interaction.response.send_message(embed=embed, ephemeral=True)
|
|
log.info("ADMINVIEW %s by %s", kasutaja, interaction.user)
|
|
|
|
@tree.command(name="adminexp", description=S.CMD["adminexp"])
|
|
@app_commands.guild_only()
|
|
@app_commands.describe(
|
|
kasutaja=S.OPT["admin_kasutaja"],
|
|
kogus=S.OPT["adminexp_kogus"],
|
|
põhjus=S.OPT["admin_põhjus"],
|
|
)
|
|
@app_commands.default_permissions(manage_guild=True)
|
|
async def cmd_adminexp(interaction: discord.Interaction, kasutaja: discord.Member, kogus: int, põhjus: str):
|
|
if kogus == 0:
|
|
await interaction.response.send_message(S.ERR["admincoins_zero"], ephemeral=True)
|
|
return
|
|
res = await economy.do_admin_exp(kasutaja.id, kogus, interaction.user.id, põhjus)
|
|
verb = f"+{kogus}" if kogus > 0 else str(kogus)
|
|
emoji = "📈" if kogus > 0 else "📉"
|
|
await interaction.response.send_message(
|
|
S.ADMIN["exp_done"].format(
|
|
emoji=emoji,
|
|
name=kasutaja.display_name,
|
|
verb=verb,
|
|
exp=f"{res['exp']:,}",
|
|
level=res["new_level"],
|
|
reason=põhjus,
|
|
),
|
|
ephemeral=True,
|
|
)
|
|
await _dm_user(
|
|
bot,
|
|
kasutaja.id,
|
|
S.ADMIN["exp_dm"].format(
|
|
emoji=emoji,
|
|
verb=verb,
|
|
reason=põhjus,
|
|
exp=f"{res['exp']:,}",
|
|
level=res["new_level"],
|
|
),
|
|
)
|
|
if res["level_changed"]:
|
|
member = interaction.guild.get_member(kasutaja.id) if interaction.guild else None
|
|
if member:
|
|
await apply_level_role(member, res["new_level"], res["old_level"])
|
|
log.info("ADMINEXP %s → %s (%s) by %s", verb, kasutaja, põhjus, interaction.user)
|
|
|
|
@tree.command(name="adminitem", description=S.CMD["adminitem"])
|
|
@app_commands.guild_only()
|
|
@app_commands.describe(
|
|
kasutaja=S.OPT["admin_kasutaja"],
|
|
ese=S.OPT["adminitem_ese"],
|
|
tegevus=S.OPT["adminitem_tegevus"],
|
|
)
|
|
@app_commands.default_permissions(manage_guild=True)
|
|
async def cmd_adminitem(interaction: discord.Interaction, kasutaja: discord.Member, ese: str, tegevus: str):
|
|
action = tegevus.strip().lower()
|
|
if action not in ("anna", "eemalda"):
|
|
await interaction.response.send_message(S.ADMIN["item_invalid"].format(item_id=tegevus), ephemeral=True)
|
|
return
|
|
action_key = "give" if action == "anna" else "remove"
|
|
res = await economy.do_admin_item(kasutaja.id, ese, action_key, interaction.user.id)
|
|
if not res["ok"]:
|
|
if res["reason"] == "invalid_item":
|
|
await interaction.response.send_message(S.ADMIN["item_invalid"].format(item_id=ese), ephemeral=True)
|
|
elif res["reason"] == "not_owned":
|
|
await interaction.response.send_message(
|
|
S.ADMIN["item_not_owned"].format(name=kasutaja.display_name, item_id=ese),
|
|
ephemeral=True,
|
|
)
|
|
else:
|
|
await interaction.response.send_message(S.ERR["generic_error"].format(error=res["reason"]), ephemeral=True)
|
|
return
|
|
item_name = economy.SHOP[ese]["name"]
|
|
if action_key == "give":
|
|
await interaction.response.send_message(S.ADMIN["item_given"].format(item=item_name, name=kasutaja.display_name), ephemeral=True)
|
|
await _dm_user(bot, kasutaja.id, S.ADMIN["item_dm_given"].format(item=item_name))
|
|
else:
|
|
await interaction.response.send_message(S.ADMIN["item_removed"].format(item=item_name, name=kasutaja.display_name), ephemeral=True)
|
|
await _dm_user(bot, kasutaja.id, S.ADMIN["item_dm_removed"].format(item=item_name))
|
|
log.info("ADMINITEM %s %s %s by %s", action_key, ese, kasutaja, interaction.user)
|