From a422cc70f0e814516acfecbd68221150950d8d80 Mon Sep 17 00:00:00 2001 From: ReaperOfVeriod Date: Sun, 25 Oct 2026 05:41:19 +0100 Subject: [PATCH] Add bot command definition editor --- src/App.svelte | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/src/App.svelte b/src/App.svelte index cfd616d..6fd781b 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -14,6 +14,8 @@ let authenticated = false; let mustReplaceCredentials = false; let username = ''; + // Keeps the command-template example literal while Svelte parses the surrounding markup. + const user_name = '${user_name}'; let password = ''; let newUsername = ''; let newPassword = ''; @@ -78,6 +80,18 @@ let botCommands: Array<{ id: string; name: string; description: string; permission_level: string; aliases: string[]; tags: string[]; cooldown_seconds: number; cooldown_scope: string; response_template: string; error_template: string | null; enabled: boolean }> = []; let publicBotCommands: Array<{ id: string; name: string; description: string; permission_level: string; aliases: string[]; tags: string[] }> = []; let botCommandError = ''; + let commandFormId = ''; + let commandFormName = ''; + let commandFormDescription = ''; + let commandFormPermission = 'everyone'; + let commandFormAliases = ''; + let commandFormTags = ''; + let commandFormCooldown = 0; + let commandFormCooldownScope = 'global'; + let commandFormResponse = ''; + let commandFormErrorResponse = ''; + let commandFormEnabled = true; + let commandFormBusy = false; let botSecrets: Array<{ id: string; name: string; description: string; used_by: string[] }> = []; let secretName = ''; let secretDescription = ''; @@ -695,6 +709,70 @@ } } + function resetCommandForm() { + commandFormId = ''; + commandFormName = ''; + commandFormDescription = ''; + commandFormPermission = 'everyone'; + commandFormAliases = ''; + commandFormTags = ''; + commandFormCooldown = 0; + commandFormCooldownScope = 'global'; + commandFormResponse = ''; + commandFormErrorResponse = ''; + commandFormEnabled = true; + } + + function editCommand(command: (typeof botCommands)[number]) { + commandFormId = command.id; + commandFormName = command.name; + commandFormDescription = command.description; + commandFormPermission = command.permission_level; + commandFormAliases = command.aliases.join(', '); + commandFormTags = command.tags.join(', '); + commandFormCooldown = command.cooldown_seconds; + commandFormCooldownScope = command.cooldown_scope; + commandFormResponse = command.response_template; + commandFormErrorResponse = command.error_template ?? ''; + commandFormEnabled = command.enabled; + } + + async function saveCommand() { + commandFormBusy = true; + botCommandError = ''; + const aliases = commandFormAliases.split(',').map((value) => value.trim()).filter(Boolean); + const tags = commandFormTags.split(',').map((value) => value.trim()).filter(Boolean); + try { + const response = await fetch(commandFormId ? `${serverAddress}/api/v1/bot/commands/${encodeURIComponent(commandFormId)}` : `${serverAddress}/api/v1/bot/commands`, { + method: commandFormId ? 'PATCH' : 'POST', + headers: { 'content-type': 'application/json', authorization: `Bearer ${sessionToken}` }, + body: JSON.stringify({ name: commandFormName, description: commandFormDescription, permission_level: commandFormPermission, aliases, tags, cooldown_seconds: Number(commandFormCooldown), cooldown_scope: commandFormCooldownScope, response_template: commandFormResponse, error_template: commandFormErrorResponse || null, enabled: commandFormEnabled }), + }); + if (!response.ok) throw new Error('The command could not be saved. Check its name, aliases, and fields.'); + resetCommandForm(); + await loadBotCommands(); + await loadPublicCommands(); + } catch (error) { + botCommandError = error instanceof Error ? error.message : 'The command could not be saved.'; + } finally { + commandFormBusy = false; + } + } + + async function deleteCommand(id: string) { + if (!window.confirm('Delete this command permanently?')) return; + botCommandError = ''; + try { + const response = await fetch(`${serverAddress}/api/v1/bot/commands/${encodeURIComponent(id)}`, { method: 'DELETE', headers: { authorization: `Bearer ${sessionToken}` } }); + if (!response.ok) throw new Error('The command could not be deleted.'); + if (commandFormId === id) resetCommandForm(); + await loadBotCommands(); + await loadPublicCommands(); + } catch (error) { + botCommandError = error instanceof Error ? error.message : 'The command could not be deleted.'; + } + } + async function loadPublicCommands() { try { const response = await fetch(`${serverAddress}/api/v1/bot/commands`); @@ -1041,6 +1119,7 @@ {:else if activeView === 'Bot'} +
{commandFormId ? 'EDIT COMMAND' : 'NEW COMMAND'}
BOT COMMANDS
PUBLIC COMMAND CATALOG
{#if botCommandError}
{botCommandError}
{:else if botCommands.length}
{#each botCommands as command}
!{command.name}{command.description}{#if command.tags.length}
{command.tags.join(' · ')}
{/if}
{command.permission_level.toUpperCase()}{#if command.cooldown_seconds}{command.cooldown_seconds}s / {command.cooldown_scope}{/if}
{/each}
{:else}
No enabled commandsCreate commands on the server to make them available here.
{/if}
SECRETS MANAGEMENT
Values are encrypted and cannot be read after saving.
{#if botSecrets.length}
{#each botSecrets as secret}
{secret.name}{secret.description || 'No description'}{#if secret.used_by.length}
USED BY: {secret.used_by.join(', ')}
{/if}
{/each}
{:else}
There are no secrets yet.
{/if}{#if secretError}
{secretError}
{/if}
{:else if activeView === 'Overlays'}
OVERLAYS
{#if selectedOverlayId || overlayName}
LIVE PREVIEW
{#if overlayError}
{overlayError}
{/if}{:else}
Select or create an overlayDrafts autosave when explicitly saved; publishing controls the browser source.
{/if}