Files
AEmotionStudio 0809d28431 fix(pr): Address PR feedback Round 3 (#5)
- Fix Config attribute access paths

- Secure permission decorator against DMs

- Fix webhook JSON serialization
2026-01-14 16:53:53 -08:00

96 lines
3.4 KiB
Python

import logging
from typing import Optional, List, Dict, Union
import discord
from enum import Enum
from ..database.repository import Repository
from ..database.models import PermissionLevel, ServerRole, User
logger = logging.getLogger(__name__)
class Permissions(Enum):
USER = "user"
GENERATOR = "generator"
ADMIN = "admin"
class PermissionService:
"""Manages role-based permissions."""
def __init__(self, repository: Repository):
self.repo = repository
def get_permission_hierarchy(self) -> Dict[str, int]:
return {
Permissions.USER.value: 1,
Permissions.GENERATOR.value: 2,
Permissions.ADMIN.value: 3
}
async def get_user_permission_level(self, member: Union[discord.Member, discord.User]) -> str:
"""
Determine the highest permission level for a user in a guild.
Default is USER.
Administrator permission in Discord implies ADMIN level.
"""
# Handle DMs or non-guild context where we have User instead of Member
if isinstance(member, discord.User):
return Permissions.USER.value
if member.guild_permissions.administrator:
return Permissions.ADMIN.value
# Fetch configured roles for this server
server_roles = await self.repo.get_server_roles(str(member.guild.id))
if not server_roles:
return Permissions.USER.value
hierarchy = self.get_permission_hierarchy()
current_level = Permissions.USER.value
current_score = hierarchy[current_level]
# Check user's roles against configured roles
member_role_ids = [str(r.id) for r in member.roles]
for server_role in server_roles:
if server_role.role_discord_id in member_role_ids:
level = server_role.permission_level
if level in hierarchy and hierarchy[level] > current_score:
current_level = level
current_score = hierarchy[level]
return current_level
async def check_permission(self, member: discord.Member, required_level: str) -> bool:
"""Check if user meets the required permission level."""
user_level = await self.get_user_permission_level(member)
hierarchy = self.get_permission_hierarchy()
return hierarchy.get(user_level, 0) >= hierarchy.get(required_level, 0)
# Helper decorator for checking permissions in commands
def require_permission(level: str):
async def predicate(interaction: discord.Interaction):
if not interaction.guild:
await interaction.response.send_message("⛔ This command cannot be used in DMs.", ephemeral=True)
return False
# We need to access the bot instance to get the permission service
bot = interaction.client
if not hasattr(bot, "permission_service"):
logger.error("Bot instance missing permission_service")
return False
has_perm = await bot.permission_service.check_permission(interaction.user, level)
if not has_perm:
await interaction.response.send_message(
f"⛔ You need **{level.upper()}** permission to use this command.",
ephemeral=True
)
return has_perm
return discord.app_commands.check(predicate)