# modules/permissions.py import json import os import globals import discord from globals import logger PERMISSIONS_FILE = "permissions.json" TWITCH_CONFIG_FILE = "settings/twitch_channels_config.json" ########################## # Load Configurations ########################## def load_json_file(file_path): """Loads JSON data from a file, returns an empty dict if missing or invalid.""" if not os.path.exists(file_path): return {} try: with open(file_path, "r", encoding="utf-8") as file: return json.load(file) except json.JSONDecodeError as e: logger.error(f"Error parsing JSON file {file_path}: {e}") return {} def load_permissions(): """Dynamically loads permissions from `permissions.json`.""" return load_json_file(PERMISSIONS_FILE) def load_twitch_config(): """Dynamically loads Twitch-specific command allow/deny lists.""" return load_json_file(TWITCH_CONFIG_FILE) ########################## # Role Mapping ########################## def map_roles(platform: str, user_roles: list, context_identifier: str = None) -> list: """ Maps platform-specific roles to a unified role system. Supports per-guild (Discord) and per-channel (Twitch) overrides. :param platform: "discord" or "twitch" :param user_roles: List of raw roles/badges from the platform :param context_identifier: Guild ID (for Discord) or Channel Name (for Twitch) :return: List of mapped roles """ permissions = load_permissions() role_mappings = permissions.get("role_mappings", {}).get(platform, {}) # Allow per-context overrides if context_identifier: specific_mappings = permissions.get("role_mappings", {}).get(f"{platform}_{context_identifier}", {}) role_mappings.update(specific_mappings) # Override defaults mapped_roles = [role_mappings.get(role.lower(), role.lower()) for role in user_roles] return list(set(mapped_roles)) if mapped_roles else ["everyone"] ########################## # Permissions Checks ########################## def has_permission(command_name: str, user_id: str, user_roles: list, platform: str, context_identifier: str = None) -> bool: """ Checks if a user has permission to execute a command. :param command_name: The command to check :param user_id: The user's ID :param user_roles: The user's roles/badges :param platform: "discord" or "twitch" :param context_identifier: Guild ID (for Discord) or Channel Name (for Twitch) :return: True if the user has permission, False otherwise """ permissions = load_permissions() command_perms = permissions.get("commands", {}).get(command_name, {}) # Extract permission settings min_role = command_perms.get("min_role", "") allowed_roles = command_perms.get("allowed_roles", []) allowed_users = command_perms.get("allowed_users", []) # Auto-allow if no specific rules exist if not min_role and not allowed_roles and not allowed_users: return True # Explicit user whitelist if user_id in allowed_users: return True # Convert platform roles mapped_roles = map_roles(platform, user_roles, context_identifier) # Check minimum required role if min_role: role_hierarchy = ["everyone", "follower", "subscriber", "vip", "moderator", "admin", "owner"] user_role_level = max([role_hierarchy.index(role) for role in mapped_roles if role in role_hierarchy], default=0) min_role_level = role_hierarchy.index(min_role) if min_role in role_hierarchy else 0 if user_role_level >= min_role_level: return True # Check explicitly allowed roles if any(role in allowed_roles for role in mapped_roles): return True return False ########################## # Twitch Command Filtering ########################## def is_command_allowed_twitch(command_name: str, channel_name: str) -> bool: """ Checks if a command is allowed in a specific Twitch channel. :param command_name: The command being checked :param channel_name: The Twitch channel name :return: True if allowed, False if blocked """ twitch_config = load_twitch_config() channel_config = twitch_config.get(channel_name.lower(), {}) if not channel_config: return False # Default to deny if no config exists mode = channel_config.get("commands_filter_mode", "exclude") filtered_commands = channel_config.get("commands_filtered", []) if mode == "exclude" and command_name in filtered_commands: return False if mode == "include" and command_name not in filtered_commands: return False return True # modules/permissions.py # Load permission settings # def load_permissions(): # """Loads the permissions from JSON.""" # if not os.path.exists(PERMISSIONS_FILE): # return {} # with open(PERMISSIONS_FILE, "r", encoding="utf-8") as file: # return json.load(file) # def map_roles(platform: str, user_roles: list) -> list: # """ # Maps platform-specific roles to a unified role system. # :param platform: "discord" or "twitch" # :param user_roles: A list of raw roles/badges from the platform # :return: A list of mapped roles based on the JSON role mapping # """ # permissions = load_permissions() # role_mappings = permissions.get("role_mappings", {}).get(platform, {}) # mapped_roles = [] # for role in user_roles: # normalized_role = role.lower() # mapped_role = role_mappings.get(normalized_role, None) # if mapped_role: # mapped_roles.append(mapped_role) # return mapped_roles if mapped_roles else ["everyone"] # def has_permission(command_name: str, user_id: str, user_roles: list, platform: str) -> bool: # """ # Checks if a user has permission to run a command. # :param command_name: The name of the command being checked. # :param user_id: The ID of the user requesting the command. # :param user_roles: A list of roles/badges the user has (platform-specific). # :param platform: "discord" or "twitch" # :return: True if the user has permission, otherwise False. # """ # permissions = load_permissions() # command_perms = permissions.get("commands", {}).get(command_name, {}) # # Extract settings # min_role = command_perms.get("min_role", "") # allowed_roles = command_perms.get("allowed_roles", []) # allowed_users = command_perms.get("allowed_users", []) # # If no min_role and no allowed_roles/users, it's open to everyone # if not min_role and not allowed_roles and not allowed_users: # return True # # Check if user is explicitly allowed # if user_id in allowed_users: # return True # Bypass role check # # Convert platform-specific roles to mapped roles # mapped_roles = map_roles(platform, user_roles) # # If a min_role is set, check against it # if min_role: # role_hierarchy = ["everyone", "follower", "subscriber", "vip", "moderator", "admin", "owner"] # user_role_level = max([role_hierarchy.index(role) for role in mapped_roles if role in role_hierarchy], default=0) # min_role_level = role_hierarchy.index(min_role) if min_role in role_hierarchy else 0 # if user_role_level >= min_role_level: # return True # # Check if the user has any explicitly allowed roles # if any(role in allowed_roles for role in mapped_roles): # return True # return False def has_custom_vc_permission(member: discord.Member, vc_owner_id: int = None) -> bool: """ Checks if the given member is either the channel's owner (vc_owner_id) or a recognized moderator (one of the roles in MODERATOR_ROLE_IDS). Returns True if they can manage the channel, False otherwise. """ MODERATOR_ROLE_IDS = { 896715419681947650, # Pack Owner 1345410182674513920, # Pack Host 958415559370866709, # Discord-specific moderator 896715186357043200, # Pack-general moderator } # 1) Check if user is the “owner” of the channel if vc_owner_id is not None and member.id == vc_owner_id: return True # 2) Check if the user has any of the allowed moderator roles user_role_ids = {r.id for r in member.roles} if MODERATOR_ROLE_IDS.intersection(user_role_ids): return True # Otherwise, no permission return False