Source code for utils.django_permissions

"""
Django Permission Integration for Discord Bot

This module provides integration with the Django web interface's admin panel
permission system, allowing the Discord bot to use the same server-specific
permission configuration that users set through the web interface.
"""

import json
import sqlite3
from pathlib import Path

import discord
import yaml


[docs] def get_django_database_path() -> str | None: """Get the Django database path from configuration. Returns: Optional[str]: Path to Django SQLite database, None if not found. """ try: # Try multiple locations for bot configuration config_paths = [ Path("~/.config/weakauras-bot/token.yml").expanduser(), Path("~/weakauras-bot-config/token.yml").expanduser(), Path("settings/token.yml"), ] for config_path in config_paths: if config_path.exists(): with open(config_path, encoding="utf-8") as f: config = yaml.safe_load(f) database_url = config.get("django", {}).get( "database_url", "sqlite:///~/weakauras-bot-data/statistics.db" ) if database_url.startswith("sqlite:///"): db_path = database_url.replace("sqlite:///", "") # Expand user path if it starts with ~ if db_path.startswith("~"): return str(Path(db_path).expanduser()) return db_path break except Exception: pass # Default fallback to external data directory return str(Path("~/weakauras-bot-data/statistics.db").expanduser())
[docs] def get_server_permission_config(guild_id: int) -> dict | None: """Get server permission configuration from Django database. Args: guild_id: Discord guild ID to get permissions for. Returns: Optional[Dict]: Permission configuration dict, None if not found. """ db_path = get_django_database_path() if not db_path or not Path(db_path).exists(): return None try: conn = sqlite3.connect(db_path) conn.row_factory = sqlite3.Row # Access columns by name cursor = conn.cursor() # Query the admin_panel_serverpermissionconfig table cursor.execute( """ SELECT * FROM admin_panel_serverpermissionconfig WHERE guild_id = ? """, (str(guild_id),), ) row = cursor.fetchone() conn.close() if row: # Convert row to dictionary and parse JSON fields config = dict(row) # Parse JSON fields json_fields = [ "admin_roles", "moderator_roles", "trusted_user_roles", "custom_admin_panel_roles", "custom_create_roles", "custom_edit_roles", "custom_delete_roles", "custom_use_roles", ] for field in json_fields: if config.get(field): try: config[field] = json.loads(config[field]) except json.JSONDecodeError: config[field] = [] else: config[field] = [] return config except Exception: pass return None
[docs] def check_server_permission( member: discord.Member, guild_id: int, permission_type: str ) -> bool: """Check if a Discord member has the specified permission for a server. Args: member: Discord member to check permissions for. guild_id: Discord guild ID. permission_type: Type of permission ('create_macros', 'edit_macros', 'delete_macros', etc.). Returns: bool: True if user has the specified permission, False otherwise. """ # Get server permission configuration config = get_server_permission_config(guild_id) if not config: # No configuration found, fall back to basic admin checking # Check if user is server owner if member.guild.owner_id == member.id: return True # Check for basic Discord admin permissions return ( member.guild_permissions.administrator or member.guild_permissions.manage_guild ) # Get permission level for this permission type permission_level = config.get(permission_type, "admin_only") # Server owner always has access if member.guild.owner_id == member.id: return True # Check permission levels if permission_level == "everyone": return True if permission_level == "server_owner": return member.guild.owner_id == member.id if permission_level == "admin_only": return _check_admin_access(member, config) if permission_level == "moderators": return _check_moderator_access(member, config) or _check_admin_access( member, config ) if permission_level == "trusted_users": return ( _check_trusted_user_access(member, config) or _check_moderator_access(member, config) or _check_admin_access(member, config) ) if permission_level == "custom_roles": custom_field = f"custom_{permission_type.replace('_macros', '').replace('admin_panel_access', 'admin_panel')}_roles" custom_roles = config.get(custom_field, []) return _check_role_access(member, custom_roles) return False
def _check_admin_access(member: discord.Member, config: dict) -> bool: """Check if member has admin access.""" # Check Discord permissions first if required if ( config.get("require_discord_permissions", True) and member.guild_permissions.administrator ): return True # Check admin roles admin_roles = config.get("admin_roles", []) return _check_role_access(member, admin_roles) def _check_moderator_access(member: discord.Member, config: dict) -> bool: """Check if member has moderator access.""" # Check Discord permissions first if required if config.get("require_discord_permissions", True) and ( member.guild_permissions.administrator or member.guild_permissions.manage_guild or member.guild_permissions.manage_channels ): return True # Check moderator roles moderator_roles = config.get("moderator_roles", []) return _check_role_access(member, moderator_roles) def _check_trusted_user_access(member: discord.Member, config: dict) -> bool: """Check if member has trusted user access.""" # Check trusted user roles trusted_roles = config.get("trusted_user_roles", []) return _check_role_access(member, trusted_roles) def _check_role_access(member: discord.Member, required_roles: list[str]) -> bool: """Check if member has any of the required roles (case-insensitive).""" if not required_roles: return False member_role_names = [role.name.lower() for role in member.roles] required_role_names = [role.lower() for role in required_roles] return any(role_name in member_role_names for role_name in required_role_names)
[docs] def get_permission_error_message( permission_type: str, config: dict | None = None ) -> str: """Get a user-friendly error message for permission denial. Args: permission_type: Type of permission that was denied. config: Server permission configuration (optional). Returns: str: User-friendly error message explaining what's required. """ if not config: return "You need server administrator permissions or the admin role to use this command." permission_level = config.get(permission_type, "admin_only") if permission_level == "server_owner": return "Only the server owner can use this command." if permission_level == "admin_only": admin_roles = config.get("admin_roles", []) if admin_roles: roles_text = ", ".join(f"'{role}'" for role in admin_roles) return f"You need either Discord administrator permissions or one of these roles: {roles_text}" return "You need Discord administrator permissions to use this command." if permission_level == "moderators": moderator_roles = config.get("moderator_roles", []) admin_roles = config.get("admin_roles", []) all_roles = admin_roles + moderator_roles if all_roles: roles_text = ", ".join(f"'{role}'" for role in all_roles) return f"You need either Discord moderator/administrator permissions or one of these roles: {roles_text}" return "You need Discord moderator or administrator permissions to use this command." if permission_level == "trusted_users": return "You need to have a trusted user role or higher to use this command." if permission_level == "custom_roles": return ( "You need to have one of the configured custom roles to use this command." ) return "You don't have permission to use this command."