From ae3080b0de846ad3f5a45be39f34a2e1503dbdae Mon Sep 17 00:00:00 2001 From: Slipstream Date: Sat, 10 May 2025 11:14:46 -0600 Subject: [PATCH] asd --- api_service/dashboard_api_endpoints.py | 120 +++++++++++++++++++++++-- api_service/dashboard_web/js/main.js | 91 ++++++++++++++++--- 2 files changed, 192 insertions(+), 19 deletions(-) diff --git a/api_service/dashboard_api_endpoints.py b/api_service/dashboard_api_endpoints.py index 0e7b3bb..183894d 100644 --- a/api_service/dashboard_api_endpoints.py +++ b/api_service/dashboard_api_endpoints.py @@ -105,19 +105,121 @@ async def get_user_guilds( ): """Get all guilds the user is an admin of.""" try: - # This would normally fetch guilds from Discord API or the bot - # For now, we'll return a mock response - # TODO: Replace mock data with actual API call to Discord - guilds = [ - Guild(id="123456789", name="My Awesome Server", icon_url="https://cdn.discordapp.com/icons/123456789/abc123def456ghi789jkl012mno345pqr.png"), - Guild(id="987654321", name="Another Great Server", icon_url="https://cdn.discordapp.com/icons/987654321/zyx987wvu654tsr321qpo098mlk765jih.png") - ] - return guilds + # First, try to use the real implementation from api_server.py + try: + # Import the dashboard_get_user_guilds function from api_server + from api_service.api_server import dashboard_get_user_guilds + + # Call the function with the user + guilds_data = await dashboard_get_user_guilds(user) + + # Convert the data to Guild objects + guilds = [] + for guild in guilds_data: + # Create icon URL if icon is available + icon_url = None + if guild.get('icon'): + icon_url = f"https://cdn.discordapp.com/icons/{guild['id']}/{guild['icon']}.png" + + guilds.append(Guild( + id=guild['id'], + name=guild['name'], + icon_url=icon_url + )) + + log.info(f"Successfully fetched {len(guilds)} guilds for user {user.get('user_id')} using api_server implementation") + return guilds + + except ImportError as e: + log.warning(f"Could not import dashboard_get_user_guilds from api_server: {e}") + # Fall back to direct implementation + except Exception as e: + log.warning(f"Error using dashboard_get_user_guilds from api_server: {e}") + # Fall back to direct implementation + + # Direct implementation as fallback + from global_bot_accessor import get_bot_instance + import aiohttp + + # Check if settings_manager is available + bot = get_bot_instance() + if not bot or not bot.pg_pool: + log.warning("Bot instance or PostgreSQL pool not available for get_user_guilds") + # Fall back to mock data since we can't access the real data + log.info("Using mock data for user guilds as fallback") + return [ + Guild(id="123456789", name="My Awesome Server (Mock)", icon_url=None), + Guild(id="987654321", name="Another Great Server (Mock)", icon_url=None) + ] + + # Get Discord API URLs from environment or use defaults + DISCORD_API_URL = "https://discord.com/api/v10" + DISCORD_USER_GUILDS_URL = f"{DISCORD_API_URL}/users/@me/guilds" + + # Get access token from user + access_token = user.get('access_token') + if not access_token: + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Access token not found in user session" + ) + + # Create headers for Discord API request + user_headers = {'Authorization': f'Bearer {access_token}'} + + # Create a temporary aiohttp session + async with aiohttp.ClientSession() as session: + # 1. Fetch guilds user is in from Discord + log.debug(f"Fetching user guilds from {DISCORD_USER_GUILDS_URL}") + async with session.get(DISCORD_USER_GUILDS_URL, headers=user_headers) as resp: + if resp.status == 401: + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Discord API authentication failed. Please log in again." + ) + resp.raise_for_status() + user_guilds = await resp.json() + log.debug(f"Fetched {len(user_guilds)} guilds for user {user.get('user_id')}") + + # 2. Get bot guilds from the bot instance + bot_guild_ids = set() + if bot.guilds: + bot_guild_ids = {guild.id for guild in bot.guilds} + log.debug(f"Bot is in {len(bot_guild_ids)} guilds") + else: + log.warning("Bot has no guilds or bot.guilds is not accessible") + + # 3. Filter user guilds + manageable_guilds = [] + ADMINISTRATOR_PERMISSION = 0x8 + for guild in user_guilds: + guild_id = int(guild['id']) + permissions = int(guild['permissions']) + + if (permissions & ADMINISTRATOR_PERMISSION) == ADMINISTRATOR_PERMISSION and guild_id in bot_guild_ids: + # Create icon URL if icon is available + icon_url = None + if guild.get('icon'): + icon_url = f"https://cdn.discordapp.com/icons/{guild['id']}/{guild['icon']}.png" + + manageable_guilds.append(Guild( + id=guild['id'], + name=guild['name'], + icon_url=icon_url + )) + + log.info(f"Found {len(manageable_guilds)} manageable guilds for user {user.get('user_id')}") + return manageable_guilds + + except HTTPException: + # Re-raise HTTP exceptions + raise except Exception as e: log.error(f"Error getting user guilds: {e}") + # Return a more user-friendly error message raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=f"Error getting user guilds: {str(e)}" + detail="Failed to retrieve your Discord servers. Please try again later." ) @router.get("/guilds/{guild_id}/channels", response_model=List[Channel]) diff --git a/api_service/dashboard_web/js/main.js b/api_service/dashboard_web/js/main.js index 6dd31c4..7ff08a9 100644 --- a/api_service/dashboard_web/js/main.js +++ b/api_service/dashboard_web/js/main.js @@ -467,7 +467,7 @@ function loadUserGuilds() { serverListContainer.innerHTML = ''; // Clear loading state if (!guilds || guilds.length === 0) { - serverListContainer.innerHTML = '

No servers found where you have admin permissions.

'; + serverListContainer.innerHTML = '
No servers found where you have admin permissions.
'; return; } @@ -477,18 +477,64 @@ function loadUserGuilds() { guildElement.style.cursor = 'pointer'; guildElement.dataset.guildId = guild.id; - const iconElement = document.createElement('img'); - iconElement.className = 'server-icon'; - iconElement.src = guild.icon_url || 'img/default-icon.png'; // Provide a default icon path - iconElement.alt = `${guild.name} icon`; - iconElement.width = 50; - iconElement.height = 50; + // Check if we have an icon URL + if (guild.icon_url) { + // Create an image element for the server icon + const iconElement = document.createElement('img'); + iconElement.className = 'server-icon'; + iconElement.src = guild.icon_url; + iconElement.alt = `${guild.name} icon`; + iconElement.width = 50; + iconElement.height = 50; + + // Add error handling for icon loading + iconElement.onerror = function() { + console.log(`Failed to load icon for server ${guild.name}, using text fallback`); + // Replace the img with a text-based fallback + const fallbackIcon = createTextFallbackIcon(guild.name); + this.parentNode.replaceChild(fallbackIcon, this); + }; + + guildElement.appendChild(iconElement); + } else { + // No icon URL provided, use text-based fallback immediately + const fallbackIcon = createTextFallbackIcon(guild.name); + guildElement.appendChild(fallbackIcon); + } + + // Helper function to create a text-based fallback icon + function createTextFallbackIcon(serverName) { + const fallbackDiv = document.createElement('div'); + fallbackDiv.className = 'server-icon-fallback'; + fallbackDiv.style.width = '50px'; + fallbackDiv.style.height = '50px'; + fallbackDiv.style.backgroundColor = '#5865F2'; // Discord blue + fallbackDiv.style.borderRadius = '50%'; + fallbackDiv.style.display = 'flex'; + fallbackDiv.style.alignItems = 'center'; + fallbackDiv.style.justifyContent = 'center'; + fallbackDiv.style.color = 'white'; + fallbackDiv.style.fontWeight = 'bold'; + fallbackDiv.style.fontSize = '18px'; + + // Get the first letter of each word in the server name + const initials = serverName + .split(' ') + .map(word => word.charAt(0)) + .join('') + .substring(0, 2) + .toUpperCase(); + + fallbackDiv.textContent = initials; + + return fallbackDiv; + } const nameElement = document.createElement('span'); nameElement.className = 'server-name'; nameElement.textContent = guild.name; - guildElement.appendChild(iconElement); + // Icon is already appended in the icon creation logic above guildElement.appendChild(nameElement); guildElement.addEventListener('click', () => { @@ -514,8 +560,33 @@ function loadUserGuilds() { }) .catch(error => { console.error('Error loading user guilds:', error); - serverListContainer.innerHTML = '

Error loading servers. Please try again.

'; - Toast.error('Failed to load your servers.'); + + // Create a more detailed error message with troubleshooting steps + let errorMessage = '
'; + errorMessage += '

Error Loading Servers

'; + errorMessage += '

We encountered a problem while trying to load your servers. This could be due to:

'; + errorMessage += ''; + errorMessage += '

Troubleshooting steps:

'; + errorMessage += '
    '; + errorMessage += '
  1. Refresh the page and try again
  2. '; + errorMessage += '
  3. Check that the bot is properly configured with database access
  4. '; + errorMessage += '
  5. Ensure the Discord bot token is properly set
  6. '; + errorMessage += '
'; + errorMessage += ''; + errorMessage += '
'; + + serverListContainer.innerHTML = errorMessage; + + // Add event listener to the retry button + document.getElementById('retry-load-guilds').addEventListener('click', () => { + loadUserGuilds(); // Retry loading guilds + }); + + Toast.error('Failed to load your servers. See details on screen.'); }); }