1
0
forked from sass/tipibot

5 Commits

Author SHA1 Message Date
Rene Arumetsa
0cdd8dac63 Update env variables 2026-06-21 22:49:46 +03:00
Rene Arumetsa
6101a278e7 Admin check now a dict 2026-06-21 22:40:57 +03:00
Rene Arumetsa
1e9ec56761 Update tipidice emoij to be animated 2026-06-03 18:50:06 +03:00
Rene Arumetsa
a96488fe9e Added eco server emoij ID 2026-06-03 18:45:46 +03:00
Rene Arumetsa
25cf60d2e1 Fix conflict 2026-06-03 18:38:40 +03:00
4 changed files with 59 additions and 65 deletions

5
bot.py
View File

@@ -594,12 +594,7 @@ class HelpSelect(discord.ui.Select):
@tree.command(name="help", description=S.CMD["help"])
async def cmd_help(interaction: discord.Interaction):
<<<<<<< HEAD
is_admin = is_bot_admin(interaction.user)
=======
member = interaction.user
is_admin = isinstance(member, discord.Member) and is_bot_admin(member)
>>>>>>> 42f7bae68124fa6a9824780ba17b46d00f3f2b36
await interaction.response.send_message(
embed=_help_embed("üldine"), view=HelpView(is_admin), ephemeral=True
)

View File

@@ -43,21 +43,28 @@ BIRTHDAY_WINDOW_DAYS = int(os.getenv("BIRTHDAY_WINDOW_DAYS", "7"))
BASE_ROLE_IDS: list[int] = [1478304631930228779, 1478302278862766190]
def _parse_admin_roles(raw: str) -> dict[int, int]:
"""Parse BOT_ADMIN_ROLES env var as "guild_id:role_id,guild_id:role_id"."""
result: dict[int, int] = {}
for pair in raw.split(","):
pair = pair.strip()
if not pair:
def _parse_admin_roles(raw: str) -> dict[int, set[int]]:
"""Parse DISCORD_ADMIN_ROLES env var as "guild_id:role_id[:role_id...],guild_id:role_id...".
Multiple admin roles per guild are colon-separated; guild entries are comma-separated.
Repeating a guild_id across entries merges its roles.
"""
result: dict[int, set[int]] = {}
for entry in raw.split(","):
entry = entry.strip()
if not entry:
continue
guild_str, _, role_str = pair.partition(":")
if not role_str:
raise SystemExit(f"BOT_ADMIN_ROLES: expected 'guild_id:role_id', got {pair!r}")
result[int(guild_str)] = int(role_str)
parts = entry.split(":")
if len(parts) < 2 or not all(p.strip() for p in parts):
raise SystemExit(
f"DISCORD_ADMIN_ROLES: expected 'guild_id:role_id[:role_id...]', got {entry!r}"
)
guild_id = int(parts[0].strip())
result.setdefault(guild_id, set()).update(int(p.strip()) for p in parts[1:])
return result
BOT_ADMIN_ROLES: dict[int, int] = _parse_admin_roles(os.getenv("BOT_ADMIN_ROLES", ""))
BOT_ADMIN_ROLES: dict[int, set[int]] = _parse_admin_roles(os.getenv("DISCORD_ADMIN_ROLES", ""))
PB_URL = os.getenv("PB_URL", "http://127.0.0.1:8090")
PB_ADMIN_EMAIL = os.getenv("PB_ADMIN_EMAIL", "")
@@ -75,26 +82,3 @@ PB_ECONOMY_COLLECTION_ECONOMY = (
PB_ECONOMY_COLLECTION = (
PB_ECONOMY_COLLECTION_ECONOMY if BOT_PROFILE == "economy" else PB_ECONOMY_COLLECTION_DEV
)
def _parse_admin_roles() -> dict[int, int]:
"""Parse BOT_ADMIN_ROLES env var (format: guild_id:role_id,guild_id:role_id)."""
raw = os.getenv("BOT_ADMIN_ROLES", "").strip()
if not raw:
return {}
result: dict[int, int] = {}
for pair in raw.split(","):
pair = pair.strip()
if not pair:
continue
parts = pair.split(":")
if len(parts) != 2:
continue
try:
result[int(parts[0].strip())] = int(parts[1].strip())
except ValueError:
continue
return result
BOT_ADMIN_ROLES: dict[int, int] = _parse_admin_roles()

View File

@@ -6,38 +6,22 @@ from discord import app_commands
import config
<<<<<<< HEAD
def is_bot_admin(member: discord.abc.User | None) -> bool:
"""True when the member has the configured admin role for their guild."""
"""True when the member has any of the configured admin roles for their guild."""
if not isinstance(member, discord.Member) or member.guild is None:
return False
=======
def is_bot_admin(member: discord.Member) -> bool:
"""Return True if the member has the configured bot-admin role for their guild."""
>>>>>>> 42f7bae68124fa6a9824780ba17b46d00f3f2b36
role_id = config.BOT_ADMIN_ROLES.get(member.guild.id)
if role_id is None:
admin_role_ids = config.BOT_ADMIN_ROLES.get(member.guild.id)
if not admin_role_ids:
return False
return any(r.id == role_id for r in member.roles)
return any(r.id in admin_role_ids for r in member.roles)
def bot_admin_check():
<<<<<<< HEAD
"""Slash-command decorator that gates execution behind ``is_bot_admin``."""
async def predicate(interaction: discord.Interaction) -> bool:
if is_bot_admin(interaction.user):
return True
raise app_commands.MissingPermissions(["bot_admin_role"])
=======
"""Slash-command check decorator: raises MissingPermissions if not a bot admin."""
def predicate(interaction: discord.Interaction) -> bool:
member = interaction.user
if not isinstance(member, discord.Member):
raise app_commands.MissingPermissions(["bot_admin"])
if not is_bot_admin(member):
raise app_commands.MissingPermissions(["bot_admin"])
return True
>>>>>>> 42f7bae68124fa6a9824780ba17b46d00f3f2b36
return app_commands.check(predicate)

View File

@@ -37,7 +37,7 @@ _DEV: dict[str, str] = {
"TipiTROLL": "<:TipiTROLL:1483431380166774895>",
"TipICRY": "<:TipICRY:1483431288852709387>",
"TipiSKULL": "<:TipiSKULL:1483431378929451028>",
"TipiDICE": "<:TipiDICE:1485923107108556950>",
"TipiDICE": "<a:TipiDICE:1485923107108556950>",
"TipiYKS": "<:TipiYKS:1483103190491856916>",
"TipiKAKS": "<:TipiKAKS:1483103215841972404>",
"TipiKOLM": "<:TipiKOLM:1483103217846980781>",
@@ -47,11 +47,42 @@ _DEV: dict[str, str] = {
"TipiSLOTS": "<a:TipiSLOTS:1483444233863037101>",
}
# Production application emoji IDs. Replace each ID with the one from the
# prod application in the dev portal. The dev IDs below are placeholders so
# this dict is structurally complete — they will NOT render in prod because
# they belong to a different application.
_ECONOMY: dict[str, str] = dict(_DEV)
# Production application emoji IDs (from the TipiBOT application's dev-portal
# Emojis tab). The display name in <:name:id> is cosmetic — Discord resolves
# by ID — so we keep the same logical names as _DEV even when the prod portal
# uses a different upload name (e.g. prod's "TipiFIVE" → key "TipiVIIS").
_ECONOMY: dict[str, str] = {
"TipiCOIN": "<:TipiCOIN:1511754551747940485>",
"TipiFIRE": "<:TipiFIRE:1511754615761272862>",
"TipiHIIR": "<:TipiHIIR:1511754556105822218>",
"TipiMATT": "<:TipiMATT:1511754595448131665>",
"TipiKLAPID": "<:TipiKLAPID:1511754589798666433>",
"TipiPILET": "<:TipiPILET:1511754560593727652>",
"TipiBULL": "<:TipiBULL:1511754564179595264>",
"TipiLAP": "<:TipiLAP:1511754558970269907>",
"TipiVAC": "<:TipiVAC:1511754562413924442>",
"TipiLAUD": "<:TipiLAUD:1511754592759713985>",
"TipiSERVER": "<:TipiSERVER:1511754601186066534>",
"TipiMIC": "<:TipiMIC:1511754597016801310>",
"TipiKLAVA": "<:TipiKLAVA:1511754567648542780>",
"TipiMONITOR": "<:TipiMONITOR:1511754570722971868>",
"TipiCAT": "<:TipiCAT:1511754566092193853>",
"TipiMONITOR2": "<:TipiMONITOR2:1511754598732402689>",
"TipiKARIKAS": "<:TipiKARIKAS:1511754574405435502>",
"TipiTOOL": "<:TipiTOOL:1511754572522061874>",
"TipiHEART": "<:TipiHEART:1511754608299737139>",
"TipiTROLL": "<:TipiTROLL:1511754612775063832>",
"TipICRY": "<:TipICRY:1511754603308515368>",
"TipiSKULL": "<:TipiSKULL:1511754610195566622>",
"TipiDICE": "<a:TipiDICE:1511753607119376504>",
"TipiYKS": "<:TipiYKS:1511754576368373951>",
"TipiKAKS": "<:TipiKAKS:1511754577928523997>",
"TipiKOLM": "<:TipiKOLM:1511754581078442005>",
"TipiNELI": "<:TipiNELI:1511754582571880509>",
"TipiVIIS": "<:TipiVIIS:1511754584182227005>",
"TipiKUUS": "<:TipiKUUS:1511754586262736977>",
"TipiSLOTS": "<a:TipiSLOTS:1511754521188106431>",
}
_EMOJI_SETS = {