This commit is contained in:
Slipstream 2025-05-03 18:47:47 -06:00
parent 3c4c776e54
commit 2e1071fb40
Signed by: slipstream
GPG Key ID: 13E498CE010AC6FD
2 changed files with 495 additions and 5 deletions

View File

@ -229,6 +229,10 @@ try:
# Add the dashboard router to the dashboard API app
dashboard_api_app.include_router(dashboard_router)
log.info("Dashboard API endpoints loaded successfully")
# Add direct routes for test-welcome and test-goodbye endpoints
# These routes need to be defined after the dependencies are defined
# We'll add them later
except ImportError as e:
log.error(f"Could not import dashboard API endpoints: {e}")
log.error("Dashboard API endpoints will not be available")
@ -1406,17 +1410,16 @@ async def dashboard_update_guild_settings(
# --- Dashboard Command Permission Endpoints ---
@dashboard_api_app.get("/guilds/{guild_id}/permissions", response_model=CommandPermissionsResponse, tags=["Dashboard Guild Settings"])
@dashboard_api_app.get("/guilds/{guild_id}/command-permissions", tags=["Dashboard Guild Settings"])
async def dashboard_get_all_guild_command_permissions(
async def dashboard_get_all_guild_command_permissions_map(
guild_id: int,
current_user: dict = Depends(get_dashboard_user),
_: bool = Depends(verify_dashboard_guild_admin) # Underscore indicates unused but required dependency
):
"""Fetches all command permissions currently set for the guild for the dashboard."""
"""Fetches all command permissions currently set for the guild for the dashboard as a map."""
if not settings_manager:
raise HTTPException(status_code=500, detail="Internal server error: Settings manager not available.")
log.info(f"Dashboard: Fetching all command permissions for guild {guild_id} requested by user {current_user['user_id']}")
log.info(f"Dashboard: Fetching all command permissions map for guild {guild_id} requested by user {current_user['user_id']}")
permissions_map: Dict[str, List[str]] = {}
try:
if settings_manager.pg_pool:
@ -1440,6 +1443,141 @@ async def dashboard_get_all_guild_command_permissions(
log.exception(f"Dashboard: Database error fetching all command permissions for guild {guild_id}: {e}")
raise HTTPException(status_code=500, detail="Failed to fetch command permissions.")
@dashboard_api_app.get("/guilds/{guild_id}/command-permissions", tags=["Dashboard Guild Settings"])
async def dashboard_get_all_guild_command_permissions(
guild_id: int,
current_user: dict = Depends(get_dashboard_user),
_: bool = Depends(verify_dashboard_guild_admin) # Underscore indicates unused but required dependency
):
"""Fetches all command permissions currently set for the guild for the dashboard as an array of objects."""
if not settings_manager:
raise HTTPException(status_code=500, detail="Internal server error: Settings manager not available.")
log.info(f"Dashboard: Fetching all command permissions for guild {guild_id} requested by user {current_user['user_id']}")
permissions_list = []
try:
if settings_manager.pg_pool:
async with settings_manager.pg_pool.acquire() as conn:
records = await conn.fetch(
"SELECT command_name, allowed_role_id FROM command_permissions WHERE guild_id = $1 ORDER BY command_name, allowed_role_id",
guild_id
)
# Get role information to include role names
bot_headers = {'Authorization': f'Bot {settings.DISCORD_BOT_TOKEN}'}
roles = []
try:
async with http_session.get(f"https://discord.com/api/v10/guilds/{guild_id}/roles", headers=bot_headers) as resp:
if resp.status == 200:
roles = await resp.json()
except Exception as e:
log.warning(f"Failed to fetch role information: {e}")
# Create a map of role IDs to role names
role_map = {str(role["id"]): role["name"] for role in roles} if roles else {}
for record in records:
cmd = record['command_name']
role_id_str = str(record['allowed_role_id'])
role_name = role_map.get(role_id_str, f"Role ID: {role_id_str}")
permissions_list.append({
"command": cmd,
"role_id": role_id_str,
"role_name": role_name
})
else:
log.error("Dashboard: settings_manager pg_pool not initialized.")
return permissions_list
except Exception as e:
log.exception(f"Dashboard: Database error fetching all command permissions for guild {guild_id}: {e}")
raise HTTPException(status_code=500, detail="Failed to fetch command permissions.")
@dashboard_api_app.post("/guilds/{guild_id}/test-welcome", status_code=status.HTTP_200_OK, tags=["Dashboard Guild Settings"])
async def dashboard_test_welcome_message(
guild_id: int,
_user: dict = Depends(get_dashboard_user), # Underscore prefix to indicate unused parameter
_: bool = Depends(verify_dashboard_guild_admin) # Underscore indicates unused but required dependency
):
"""Test the welcome message for a guild."""
try:
# Get welcome settings
welcome_channel_id_str = await settings_manager.get_setting(guild_id, 'welcome_channel_id')
welcome_message_template = await settings_manager.get_setting(guild_id, 'welcome_message', default="Welcome {user} to {server}!")
# Check if welcome channel is set
if not welcome_channel_id_str or welcome_channel_id_str == "__NONE__":
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Welcome channel not configured"
)
# In a real implementation, this would send a test message to the welcome channel
# For now, we'll just return a success message with the formatted message
formatted_message = welcome_message_template.format(
user="@TestUser",
username="TestUser",
server=f"Server {guild_id}"
)
return {
"message": "Test welcome message sent",
"channel_id": welcome_channel_id_str,
"formatted_message": formatted_message
}
except HTTPException:
# Re-raise HTTP exceptions
raise
except Exception as e:
log.error(f"Error testing welcome message for guild {guild_id}: {e}")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Error testing welcome message: {str(e)}"
)
@dashboard_api_app.post("/guilds/{guild_id}/test-goodbye", status_code=status.HTTP_200_OK, tags=["Dashboard Guild Settings"])
async def dashboard_test_goodbye_message(
guild_id: int,
_user: dict = Depends(get_dashboard_user), # Underscore prefix to indicate unused parameter
_: bool = Depends(verify_dashboard_guild_admin) # Underscore indicates unused but required dependency
):
"""Test the goodbye message for a guild."""
try:
# Get goodbye settings
goodbye_channel_id_str = await settings_manager.get_setting(guild_id, 'goodbye_channel_id')
goodbye_message_template = await settings_manager.get_setting(guild_id, 'goodbye_message', default="{username} has left the server.")
# Check if goodbye channel is set
if not goodbye_channel_id_str or goodbye_channel_id_str == "__NONE__":
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Goodbye channel not configured"
)
# In a real implementation, this would send a test message to the goodbye channel
# For now, we'll just return a success message with the formatted message
formatted_message = goodbye_message_template.format(
username="TestUser",
server=f"Server {guild_id}"
)
return {
"message": "Test goodbye message sent",
"channel_id": goodbye_channel_id_str,
"formatted_message": formatted_message
}
except HTTPException:
# Re-raise HTTP exceptions
raise
except Exception as e:
log.error(f"Error testing goodbye message for guild {guild_id}: {e}")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Error testing goodbye message: {str(e)}"
)
@dashboard_api_app.post("/guilds/{guild_id}/permissions", status_code=status.HTTP_201_CREATED, tags=["Dashboard Guild Settings"])
@dashboard_api_app.post("/guilds/{guild_id}/command-permissions", status_code=status.HTTP_201_CREATED, tags=["Dashboard Guild Settings"])
async def dashboard_add_guild_command_permission(

View File

@ -470,7 +470,8 @@ function loadGuildSettings(guildId) {
loadGuildRoles(guildId);
loadGuildCommands(guildId);
// Set up welcome/leave message test buttons
// Set up event listeners for buttons
setupSaveSettingsButtons(guildId);
setupWelcomeLeaveTestButtons(guildId);
})
.catch(error => {
@ -897,6 +898,357 @@ function removeCommandPermission(guildId, command, roleId) {
* Set up welcome/leave message test buttons
* @param {string} guildId - The guild ID
*/
/**
* Set up event listeners for save settings buttons
* @param {string} guildId - The guild ID
*/
function setupSaveSettingsButtons(guildId) {
// Save prefix button
const savePrefixButton = document.getElementById('save-prefix-button');
if (savePrefixButton) {
savePrefixButton.addEventListener('click', () => {
// Get prefix value
const prefix = document.getElementById('prefix-input').value;
if (!prefix) {
Toast.error('Please enter a prefix');
return;
}
// Show loading state
savePrefixButton.disabled = true;
savePrefixButton.classList.add('btn-loading');
// Send request to API
API.patch(`/dashboard/api/guilds/${guildId}/settings`, {
prefix: prefix
})
.then(() => {
// Show success message
Toast.success('Prefix saved successfully');
// Show feedback
const prefixFeedback = document.getElementById('prefix-feedback');
if (prefixFeedback) {
prefixFeedback.textContent = 'Prefix saved successfully';
prefixFeedback.className = 'success';
// Clear feedback after a few seconds
setTimeout(() => {
prefixFeedback.textContent = '';
prefixFeedback.className = '';
}, 3000);
}
})
.catch(error => {
console.error('Error saving prefix:', error);
Toast.error('Failed to save prefix. Please try again.');
// Show error feedback
const prefixFeedback = document.getElementById('prefix-feedback');
if (prefixFeedback) {
prefixFeedback.textContent = 'Error saving prefix. Please try again.';
prefixFeedback.className = 'error';
}
})
.finally(() => {
// Remove loading state
savePrefixButton.disabled = false;
savePrefixButton.classList.remove('btn-loading');
});
});
}
// Save welcome settings button
const saveWelcomeButton = document.getElementById('save-welcome-button');
if (saveWelcomeButton) {
saveWelcomeButton.addEventListener('click', () => {
// Get welcome settings
const welcomeChannelId = document.getElementById('welcome-channel').value;
const welcomeMessage = document.getElementById('welcome-message').value;
// Show loading state
saveWelcomeButton.disabled = true;
saveWelcomeButton.classList.add('btn-loading');
// Send request to API
API.patch(`/dashboard/api/guilds/${guildId}/settings`, {
welcome_channel_id: welcomeChannelId,
welcome_message: welcomeMessage
})
.then(() => {
// Show success message
Toast.success('Welcome settings saved successfully');
// Show feedback
const welcomeFeedback = document.getElementById('welcome-feedback');
if (welcomeFeedback) {
welcomeFeedback.textContent = 'Welcome settings saved successfully';
welcomeFeedback.className = 'success';
// Clear feedback after a few seconds
setTimeout(() => {
welcomeFeedback.textContent = '';
welcomeFeedback.className = '';
}, 3000);
}
})
.catch(error => {
console.error('Error saving welcome settings:', error);
Toast.error('Failed to save welcome settings. Please try again.');
// Show error feedback
const welcomeFeedback = document.getElementById('welcome-feedback');
if (welcomeFeedback) {
welcomeFeedback.textContent = 'Error saving welcome settings. Please try again.';
welcomeFeedback.className = 'error';
}
})
.finally(() => {
// Remove loading state
saveWelcomeButton.disabled = false;
saveWelcomeButton.classList.remove('btn-loading');
});
});
}
// Disable welcome button
const disableWelcomeButton = document.getElementById('disable-welcome-button');
if (disableWelcomeButton) {
disableWelcomeButton.addEventListener('click', () => {
// Confirm disable
if (!confirm('Are you sure you want to disable welcome messages?')) {
return;
}
// Show loading state
disableWelcomeButton.disabled = true;
disableWelcomeButton.classList.add('btn-loading');
// Send request to API
API.patch(`/dashboard/api/guilds/${guildId}/settings`, {
welcome_channel_id: null,
welcome_message: null
})
.then(() => {
// Show success message
Toast.success('Welcome messages disabled');
// Clear welcome settings inputs
const welcomeChannel = document.getElementById('welcome-channel');
const welcomeMessage = document.getElementById('welcome-message');
const welcomeChannelSelect = document.getElementById('welcome-channel-select');
if (welcomeChannel) welcomeChannel.value = '';
if (welcomeMessage) welcomeMessage.value = '';
if (welcomeChannelSelect) welcomeChannelSelect.value = '';
// Show feedback
const welcomeFeedback = document.getElementById('welcome-feedback');
if (welcomeFeedback) {
welcomeFeedback.textContent = 'Welcome messages disabled';
welcomeFeedback.className = 'success';
// Clear feedback after a few seconds
setTimeout(() => {
welcomeFeedback.textContent = '';
welcomeFeedback.className = '';
}, 3000);
}
})
.catch(error => {
console.error('Error disabling welcome messages:', error);
Toast.error('Failed to disable welcome messages. Please try again.');
// Show error feedback
const welcomeFeedback = document.getElementById('welcome-feedback');
if (welcomeFeedback) {
welcomeFeedback.textContent = 'Error disabling welcome messages. Please try again.';
welcomeFeedback.className = 'error';
}
})
.finally(() => {
// Remove loading state
disableWelcomeButton.disabled = false;
disableWelcomeButton.classList.remove('btn-loading');
});
});
}
// Save goodbye settings button
const saveGoodbyeButton = document.getElementById('save-goodbye-button');
if (saveGoodbyeButton) {
saveGoodbyeButton.addEventListener('click', () => {
// Get goodbye settings
const goodbyeChannelId = document.getElementById('goodbye-channel').value;
const goodbyeMessage = document.getElementById('goodbye-message').value;
// Show loading state
saveGoodbyeButton.disabled = true;
saveGoodbyeButton.classList.add('btn-loading');
// Send request to API
API.patch(`/dashboard/api/guilds/${guildId}/settings`, {
goodbye_channel_id: goodbyeChannelId,
goodbye_message: goodbyeMessage
})
.then(() => {
// Show success message
Toast.success('Goodbye settings saved successfully');
// Show feedback
const goodbyeFeedback = document.getElementById('goodbye-feedback');
if (goodbyeFeedback) {
goodbyeFeedback.textContent = 'Goodbye settings saved successfully';
goodbyeFeedback.className = 'success';
// Clear feedback after a few seconds
setTimeout(() => {
goodbyeFeedback.textContent = '';
goodbyeFeedback.className = '';
}, 3000);
}
})
.catch(error => {
console.error('Error saving goodbye settings:', error);
Toast.error('Failed to save goodbye settings. Please try again.');
// Show error feedback
const goodbyeFeedback = document.getElementById('goodbye-feedback');
if (goodbyeFeedback) {
goodbyeFeedback.textContent = 'Error saving goodbye settings. Please try again.';
goodbyeFeedback.className = 'error';
}
})
.finally(() => {
// Remove loading state
saveGoodbyeButton.disabled = false;
saveGoodbyeButton.classList.remove('btn-loading');
});
});
}
// Disable goodbye button
const disableGoodbyeButton = document.getElementById('disable-goodbye-button');
if (disableGoodbyeButton) {
disableGoodbyeButton.addEventListener('click', () => {
// Confirm disable
if (!confirm('Are you sure you want to disable goodbye messages?')) {
return;
}
// Show loading state
disableGoodbyeButton.disabled = true;
disableGoodbyeButton.classList.add('btn-loading');
// Send request to API
API.patch(`/dashboard/api/guilds/${guildId}/settings`, {
goodbye_channel_id: null,
goodbye_message: null
})
.then(() => {
// Show success message
Toast.success('Goodbye messages disabled');
// Clear goodbye settings inputs
const goodbyeChannel = document.getElementById('goodbye-channel');
const goodbyeMessage = document.getElementById('goodbye-message');
const goodbyeChannelSelect = document.getElementById('goodbye-channel-select');
if (goodbyeChannel) goodbyeChannel.value = '';
if (goodbyeMessage) goodbyeMessage.value = '';
if (goodbyeChannelSelect) goodbyeChannelSelect.value = '';
// Show feedback
const goodbyeFeedback = document.getElementById('goodbye-feedback');
if (goodbyeFeedback) {
goodbyeFeedback.textContent = 'Goodbye messages disabled';
goodbyeFeedback.className = 'success';
// Clear feedback after a few seconds
setTimeout(() => {
goodbyeFeedback.textContent = '';
goodbyeFeedback.className = '';
}, 3000);
}
})
.catch(error => {
console.error('Error disabling goodbye messages:', error);
Toast.error('Failed to disable goodbye messages. Please try again.');
// Show error feedback
const goodbyeFeedback = document.getElementById('goodbye-feedback');
if (goodbyeFeedback) {
goodbyeFeedback.textContent = 'Error disabling goodbye messages. Please try again.';
goodbyeFeedback.className = 'error';
}
})
.finally(() => {
// Remove loading state
disableGoodbyeButton.disabled = false;
disableGoodbyeButton.classList.remove('btn-loading');
});
});
}
// Save cogs button
const saveCogsButton = document.getElementById('save-cogs-button');
if (saveCogsButton) {
saveCogsButton.addEventListener('click', () => {
// Get cog settings
const cogsPayload = {};
const cogCheckboxes = document.querySelectorAll('#cogs-list input[type="checkbox"]');
cogCheckboxes.forEach(checkbox => {
// Extract cog name from checkbox ID (format: cog-{name})
const cogName = checkbox.id.replace('cog-', '');
cogsPayload[cogName] = checkbox.checked;
});
// Show loading state
saveCogsButton.disabled = true;
saveCogsButton.classList.add('btn-loading');
// Send request to API
API.patch(`/dashboard/api/guilds/${guildId}/settings`, {
cogs: cogsPayload
})
.then(() => {
// Show success message
Toast.success('Module settings saved successfully');
// Show feedback
const cogsFeedback = document.getElementById('cogs-feedback');
if (cogsFeedback) {
cogsFeedback.textContent = 'Module settings saved successfully';
cogsFeedback.className = 'success';
// Clear feedback after a few seconds
setTimeout(() => {
cogsFeedback.textContent = '';
cogsFeedback.className = '';
}, 3000);
}
})
.catch(error => {
console.error('Error saving module settings:', error);
Toast.error('Failed to save module settings. Please try again.');
// Show error feedback
const cogsFeedback = document.getElementById('cogs-feedback');
if (cogsFeedback) {
cogsFeedback.textContent = 'Error saving module settings. Please try again.';
cogsFeedback.className = 'error';
}
})
.finally(() => {
// Remove loading state
saveCogsButton.disabled = false;
saveCogsButton.classList.remove('btn-loading');
});
});
}
}
function setupWelcomeLeaveTestButtons(guildId) {
// Welcome message test button
const testWelcomeButton = document.getElementById('test-welcome-button');