mirror of
https://github.com/zvx-echo6/meshai.git
synced 2026-05-21 23:24:44 +02:00
Update TUI configurator with all new config sections
Major expansion of the configurator TUI to support all new config options: Main Menu: - Reorganized into 3 tables: Core Settings (1-6), Advanced Settings (7-12), Features (13-16) - Added options 7-16 for all new config sections New Submenus Added: - Rate Limits (7): messages_per_minute, global rate, cooldown, burst allowance - Safety & Filtering (8): profanity filter, blocked phrases, require_mention, emergency keywords - User Management (9): blocklist, allowlist, admin nodes, VIP nodes with interactive editors - Commands (10): enable/disable, prefix, disabled commands, custom commands editor - Personality (11): system prompt override, context injection, personas editor - Logging (12): log level, file path, rotation settings, log verbosity toggles - Web Status Page (14): enable, port, display options, authentication - Announcements (15): enable, interval, channel, messages editor, random order - Webhooks (16): enable, URL, events selection Updated Submenus: - History (6): Added memory settings (enabled, window_size, summarize_threshold) All config options are now accessible through the TUI - no need to edit YAML files directly. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
165da72d8d
commit
1747edd150
1 changed files with 693 additions and 13 deletions
|
|
@ -65,24 +65,55 @@ class Configurator:
|
|||
self._clear()
|
||||
self._show_header()
|
||||
|
||||
table = Table(box=box.ROUNDED, show_header=False)
|
||||
# Page 1 - Core Settings
|
||||
table = Table(box=box.ROUNDED, show_header=False, title="[bold]Core Settings[/bold]")
|
||||
table.add_column("Option", style="cyan", width=4)
|
||||
table.add_column("Description", style="white")
|
||||
table.add_column("Status", style="dim")
|
||||
|
||||
table.add_row("1", "Bot Settings", f"@{self.config.bot.name}")
|
||||
table.add_row("2", "Connection", f"{self.config.connection.type}")
|
||||
table.add_row("3", "LLM Backend", f"{self.config.llm.backend}")
|
||||
table.add_row("4", "Weather", f"{self.config.weather.primary}")
|
||||
table.add_row("5", "Response Settings", f"{self.config.response.max_length}ch")
|
||||
table.add_row("6", "Channel Filtering", f"{self.config.channels.mode}")
|
||||
table.add_row("7", "History Settings", f"{self.config.history.max_messages_per_user} msgs")
|
||||
table.add_row("8", "Run Setup Wizard", "[dim]First-time setup[/dim]")
|
||||
table.add_row("0", "Save & Exit", self._get_modified_indicator())
|
||||
table.add_row("3", "LLM Backend", f"{self.config.llm.backend}/{self.config.llm.model}")
|
||||
table.add_row("4", "Response Settings", f"{self.config.response.max_length}ch max")
|
||||
table.add_row("5", "Channel Filtering", f"{self.config.channels.mode}")
|
||||
table.add_row("6", "History & Memory", f"{self.config.history.max_messages_per_user} msgs")
|
||||
|
||||
console.print(table)
|
||||
console.print()
|
||||
|
||||
# Page 2 - Advanced Settings
|
||||
table2 = Table(box=box.ROUNDED, show_header=False, title="[bold]Advanced Settings[/bold]")
|
||||
table2.add_column("Option", style="cyan", width=4)
|
||||
table2.add_column("Description", style="white")
|
||||
table2.add_column("Status", style="dim")
|
||||
|
||||
table2.add_row("7", "Rate Limits", f"{self.config.rate_limits.messages_per_minute}/min")
|
||||
table2.add_row("8", "Safety & Filtering", self._status_icon(self.config.safety.filter_profanity))
|
||||
table2.add_row("9", "User Management", f"{len(self.config.users.blocklist)} blocked")
|
||||
table2.add_row("10", "Commands", f"prefix: {self.config.commands.prefix}")
|
||||
table2.add_row("11", "Personality", f"{len(self.config.personality.personas)} personas")
|
||||
table2.add_row("12", "Logging", f"{self.config.logging.level}")
|
||||
|
||||
console.print(table2)
|
||||
console.print()
|
||||
|
||||
# Page 3 - Features
|
||||
table3 = Table(box=box.ROUNDED, show_header=False, title="[bold]Features[/bold]")
|
||||
table3.add_column("Option", style="cyan", width=4)
|
||||
table3.add_column("Description", style="white")
|
||||
table3.add_column("Status", style="dim")
|
||||
|
||||
table3.add_row("13", "Weather", f"{self.config.weather.primary}")
|
||||
table3.add_row("14", "Web Status Page", self._status_icon(self.config.web_status.enabled))
|
||||
table3.add_row("15", "Announcements", self._status_icon(self.config.announcements.enabled))
|
||||
table3.add_row("16", "Webhooks", self._status_icon(self.config.integrations.webhook.enabled))
|
||||
table3.add_row("", "", "")
|
||||
table3.add_row("20", "Setup Wizard", "[dim]First-time setup[/dim]")
|
||||
table3.add_row("0", "Save & Exit", self._get_modified_indicator())
|
||||
|
||||
console.print(table3)
|
||||
console.print()
|
||||
|
||||
choice = IntPrompt.ask("Select option", default=0)
|
||||
|
||||
if choice == 0:
|
||||
|
|
@ -95,14 +126,32 @@ class Configurator:
|
|||
elif choice == 3:
|
||||
self._llm_settings()
|
||||
elif choice == 4:
|
||||
self._weather_settings()
|
||||
elif choice == 5:
|
||||
self._response_settings()
|
||||
elif choice == 6:
|
||||
elif choice == 5:
|
||||
self._channel_settings()
|
||||
elif choice == 7:
|
||||
elif choice == 6:
|
||||
self._history_settings()
|
||||
elif choice == 7:
|
||||
self._rate_limits_settings()
|
||||
elif choice == 8:
|
||||
self._safety_settings()
|
||||
elif choice == 9:
|
||||
self._users_settings()
|
||||
elif choice == 10:
|
||||
self._commands_settings()
|
||||
elif choice == 11:
|
||||
self._personality_settings()
|
||||
elif choice == 12:
|
||||
self._logging_settings()
|
||||
elif choice == 13:
|
||||
self._weather_settings()
|
||||
elif choice == 14:
|
||||
self._web_status_settings()
|
||||
elif choice == 15:
|
||||
self._announcements_settings()
|
||||
elif choice == 16:
|
||||
self._webhook_settings()
|
||||
elif choice == 20:
|
||||
self._setup_wizard()
|
||||
|
||||
def _show_header(self) -> None:
|
||||
|
|
@ -434,7 +483,7 @@ class Configurator:
|
|||
"""History settings submenu."""
|
||||
while True:
|
||||
self._clear()
|
||||
console.print("[bold]History Settings[/bold]\n")
|
||||
console.print("[bold]History & Memory Settings[/bold]\n")
|
||||
|
||||
table = Table(box=box.ROUNDED)
|
||||
table.add_column("Option", style="cyan", width=4)
|
||||
|
|
@ -445,6 +494,12 @@ class Configurator:
|
|||
table.add_row("1", "Database File", self.config.history.database)
|
||||
table.add_row("2", "Max Messages Per User", str(self.config.history.max_messages_per_user))
|
||||
table.add_row("3", "Conversation Timeout", f"{timeout_hours}h")
|
||||
table.add_row("4", "Auto Cleanup", self._status_icon(self.config.history.auto_cleanup))
|
||||
table.add_row("5", "Max Age (days)", str(self.config.history.max_age_days))
|
||||
table.add_row("", "[bold]Memory[/bold]", "")
|
||||
table.add_row("6", "Memory Enabled", self._status_icon(self.config.memory.enabled))
|
||||
table.add_row("7", "Window Size", str(self.config.memory.window_size))
|
||||
table.add_row("8", "Summarize Threshold", str(self.config.memory.summarize_threshold))
|
||||
table.add_row("0", "Back", "")
|
||||
|
||||
console.print(table)
|
||||
|
|
@ -472,6 +527,631 @@ class Configurator:
|
|||
if seconds != self.config.history.conversation_timeout:
|
||||
self.config.history.conversation_timeout = seconds
|
||||
self.modified = True
|
||||
elif choice == 4:
|
||||
value = Confirm.ask("Enable auto cleanup?", default=self.config.history.auto_cleanup)
|
||||
if value != self.config.history.auto_cleanup:
|
||||
self.config.history.auto_cleanup = value
|
||||
self.modified = True
|
||||
elif choice == 5:
|
||||
value = IntPrompt.ask("Max age (days)", default=self.config.history.max_age_days)
|
||||
if value != self.config.history.max_age_days:
|
||||
self.config.history.max_age_days = value
|
||||
self.modified = True
|
||||
elif choice == 6:
|
||||
value = Confirm.ask("Enable memory?", default=self.config.memory.enabled)
|
||||
if value != self.config.memory.enabled:
|
||||
self.config.memory.enabled = value
|
||||
self.modified = True
|
||||
elif choice == 7:
|
||||
value = IntPrompt.ask("Window size", default=self.config.memory.window_size)
|
||||
if value != self.config.memory.window_size:
|
||||
self.config.memory.window_size = value
|
||||
self.modified = True
|
||||
elif choice == 8:
|
||||
value = IntPrompt.ask("Summarize threshold", default=self.config.memory.summarize_threshold)
|
||||
if value != self.config.memory.summarize_threshold:
|
||||
self.config.memory.summarize_threshold = value
|
||||
self.modified = True
|
||||
|
||||
def _rate_limits_settings(self) -> None:
|
||||
"""Rate limits settings submenu."""
|
||||
while True:
|
||||
self._clear()
|
||||
console.print("[bold]Rate Limits[/bold]\n")
|
||||
|
||||
table = Table(box=box.ROUNDED)
|
||||
table.add_column("Option", style="cyan", width=4)
|
||||
table.add_column("Setting", style="white")
|
||||
table.add_column("Value", style="green")
|
||||
|
||||
table.add_row("1", "Messages Per Minute (per user)", str(self.config.rate_limits.messages_per_minute))
|
||||
table.add_row("2", "Global Messages Per Minute", str(self.config.rate_limits.global_messages_per_minute))
|
||||
table.add_row("3", "Cooldown (seconds)", str(self.config.rate_limits.cooldown_seconds))
|
||||
table.add_row("4", "Burst Allowance", str(self.config.rate_limits.burst_allowance))
|
||||
table.add_row("0", "Back", "")
|
||||
|
||||
console.print(table)
|
||||
console.print()
|
||||
|
||||
choice = IntPrompt.ask("Select option", default=0)
|
||||
|
||||
if choice == 0:
|
||||
return
|
||||
elif choice == 1:
|
||||
value = IntPrompt.ask("Messages per minute", default=self.config.rate_limits.messages_per_minute)
|
||||
if value != self.config.rate_limits.messages_per_minute:
|
||||
self.config.rate_limits.messages_per_minute = value
|
||||
self.modified = True
|
||||
elif choice == 2:
|
||||
value = IntPrompt.ask("Global messages per minute", default=self.config.rate_limits.global_messages_per_minute)
|
||||
if value != self.config.rate_limits.global_messages_per_minute:
|
||||
self.config.rate_limits.global_messages_per_minute = value
|
||||
self.modified = True
|
||||
elif choice == 3:
|
||||
value = float(Prompt.ask("Cooldown (seconds)", default=str(self.config.rate_limits.cooldown_seconds)))
|
||||
if value != self.config.rate_limits.cooldown_seconds:
|
||||
self.config.rate_limits.cooldown_seconds = value
|
||||
self.modified = True
|
||||
elif choice == 4:
|
||||
value = IntPrompt.ask("Burst allowance", default=self.config.rate_limits.burst_allowance)
|
||||
if value != self.config.rate_limits.burst_allowance:
|
||||
self.config.rate_limits.burst_allowance = value
|
||||
self.modified = True
|
||||
|
||||
def _safety_settings(self) -> None:
|
||||
"""Safety and filtering settings submenu."""
|
||||
while True:
|
||||
self._clear()
|
||||
console.print("[bold]Safety & Filtering[/bold]\n")
|
||||
|
||||
table = Table(box=box.ROUNDED)
|
||||
table.add_column("Option", style="cyan", width=4)
|
||||
table.add_column("Setting", style="white")
|
||||
table.add_column("Value", style="green")
|
||||
|
||||
blocked_str = ", ".join(self.config.safety.blocked_phrases[:3])
|
||||
if len(self.config.safety.blocked_phrases) > 3:
|
||||
blocked_str += f"... (+{len(self.config.safety.blocked_phrases) - 3})"
|
||||
emergency_str = ", ".join(self.config.safety.emergency_keywords[:3])
|
||||
|
||||
table.add_row("1", "Max Response Length", str(self.config.safety.max_response_length))
|
||||
table.add_row("2", "Filter Profanity", self._status_icon(self.config.safety.filter_profanity))
|
||||
table.add_row("3", "Blocked Phrases", blocked_str or "[dim]none[/dim]")
|
||||
table.add_row("4", "Require @mention", self._status_icon(self.config.safety.require_mention))
|
||||
table.add_row("5", "Ignore Self", self._status_icon(self.config.safety.ignore_self))
|
||||
table.add_row("6", "Emergency Keywords", emergency_str)
|
||||
table.add_row("0", "Back", "")
|
||||
|
||||
console.print(table)
|
||||
console.print()
|
||||
|
||||
choice = IntPrompt.ask("Select option", default=0)
|
||||
|
||||
if choice == 0:
|
||||
return
|
||||
elif choice == 1:
|
||||
value = IntPrompt.ask("Max response length", default=self.config.safety.max_response_length)
|
||||
if value != self.config.safety.max_response_length:
|
||||
self.config.safety.max_response_length = value
|
||||
self.modified = True
|
||||
elif choice == 2:
|
||||
value = Confirm.ask("Filter profanity?", default=self.config.safety.filter_profanity)
|
||||
if value != self.config.safety.filter_profanity:
|
||||
self.config.safety.filter_profanity = value
|
||||
self.modified = True
|
||||
elif choice == 3:
|
||||
console.print("\n[dim]Current:[/dim]", ", ".join(self.config.safety.blocked_phrases) or "none")
|
||||
value = Prompt.ask("Blocked phrases (comma-separated)", default=",".join(self.config.safety.blocked_phrases))
|
||||
phrases = [p.strip() for p in value.split(",") if p.strip()]
|
||||
if phrases != self.config.safety.blocked_phrases:
|
||||
self.config.safety.blocked_phrases = phrases
|
||||
self.modified = True
|
||||
elif choice == 4:
|
||||
value = Confirm.ask("Require @mention?", default=self.config.safety.require_mention)
|
||||
if value != self.config.safety.require_mention:
|
||||
self.config.safety.require_mention = value
|
||||
self.modified = True
|
||||
elif choice == 5:
|
||||
value = Confirm.ask("Ignore self messages?", default=self.config.safety.ignore_self)
|
||||
if value != self.config.safety.ignore_self:
|
||||
self.config.safety.ignore_self = value
|
||||
self.modified = True
|
||||
elif choice == 6:
|
||||
value = Prompt.ask("Emergency keywords (comma-separated)", default=",".join(self.config.safety.emergency_keywords))
|
||||
keywords = [k.strip() for k in value.split(",") if k.strip()]
|
||||
if keywords != self.config.safety.emergency_keywords:
|
||||
self.config.safety.emergency_keywords = keywords
|
||||
self.modified = True
|
||||
|
||||
def _users_settings(self) -> None:
|
||||
"""User management settings submenu."""
|
||||
while True:
|
||||
self._clear()
|
||||
console.print("[bold]User Management[/bold]\n")
|
||||
|
||||
table = Table(box=box.ROUNDED)
|
||||
table.add_column("Option", style="cyan", width=4)
|
||||
table.add_column("Setting", style="white")
|
||||
table.add_column("Value", style="green")
|
||||
|
||||
table.add_row("1", "Blocklist", f"{len(self.config.users.blocklist)} users")
|
||||
table.add_row("2", "Allowlist Only Mode", self._status_icon(self.config.users.allowlist_only))
|
||||
table.add_row("3", "Allowlist", f"{len(self.config.users.allowlist)} users")
|
||||
table.add_row("4", "Admin Nodes", f"{len(self.config.users.admin_nodes)} users")
|
||||
table.add_row("5", "VIP Nodes (bypass limits)", f"{len(self.config.users.vip_nodes)} users")
|
||||
table.add_row("0", "Back", "")
|
||||
|
||||
console.print(table)
|
||||
console.print()
|
||||
|
||||
choice = IntPrompt.ask("Select option", default=0)
|
||||
|
||||
if choice == 0:
|
||||
return
|
||||
elif choice == 1:
|
||||
self._edit_node_list("Blocklist", self.config.users.blocklist)
|
||||
elif choice == 2:
|
||||
value = Confirm.ask("Allowlist only mode?", default=self.config.users.allowlist_only)
|
||||
if value != self.config.users.allowlist_only:
|
||||
self.config.users.allowlist_only = value
|
||||
self.modified = True
|
||||
elif choice == 3:
|
||||
self._edit_node_list("Allowlist", self.config.users.allowlist)
|
||||
elif choice == 4:
|
||||
self._edit_node_list("Admin Nodes", self.config.users.admin_nodes)
|
||||
elif choice == 5:
|
||||
self._edit_node_list("VIP Nodes", self.config.users.vip_nodes)
|
||||
|
||||
def _edit_node_list(self, name: str, node_list: list) -> None:
|
||||
"""Edit a list of node IDs."""
|
||||
while True:
|
||||
self._clear()
|
||||
console.print(f"[bold]{name}[/bold]\n")
|
||||
|
||||
if node_list:
|
||||
for i, node in enumerate(node_list, 1):
|
||||
console.print(f" {i}. {node}")
|
||||
else:
|
||||
console.print(" [dim]No nodes[/dim]")
|
||||
|
||||
console.print("\n[cyan]a[/cyan] Add node")
|
||||
console.print("[cyan]r[/cyan] Remove node")
|
||||
console.print("[cyan]0[/cyan] Back")
|
||||
console.print()
|
||||
|
||||
choice = Prompt.ask("Select", default="0")
|
||||
|
||||
if choice == "0":
|
||||
return
|
||||
elif choice.lower() == "a":
|
||||
value = Prompt.ask("Node ID (e.g., !abc12345)")
|
||||
if value and value not in node_list:
|
||||
node_list.append(value)
|
||||
self.modified = True
|
||||
elif choice.lower() == "r":
|
||||
if node_list:
|
||||
idx = IntPrompt.ask("Remove which number", default=1)
|
||||
if 1 <= idx <= len(node_list):
|
||||
node_list.pop(idx - 1)
|
||||
self.modified = True
|
||||
|
||||
def _commands_settings(self) -> None:
|
||||
"""Commands settings submenu."""
|
||||
while True:
|
||||
self._clear()
|
||||
console.print("[bold]Commands[/bold]\n")
|
||||
|
||||
table = Table(box=box.ROUNDED)
|
||||
table.add_column("Option", style="cyan", width=4)
|
||||
table.add_column("Setting", style="white")
|
||||
table.add_column("Value", style="green")
|
||||
|
||||
table.add_row("1", "Commands Enabled", self._status_icon(self.config.commands.enabled))
|
||||
table.add_row("2", "Prefix", self.config.commands.prefix)
|
||||
table.add_row("3", "Disabled Commands", ", ".join(self.config.commands.disabled_commands) or "[dim]none[/dim]")
|
||||
table.add_row("4", "Custom Commands", f"{len(self.config.commands.custom_commands)} defined")
|
||||
table.add_row("0", "Back", "")
|
||||
|
||||
console.print(table)
|
||||
console.print()
|
||||
|
||||
choice = IntPrompt.ask("Select option", default=0)
|
||||
|
||||
if choice == 0:
|
||||
return
|
||||
elif choice == 1:
|
||||
value = Confirm.ask("Enable commands?", default=self.config.commands.enabled)
|
||||
if value != self.config.commands.enabled:
|
||||
self.config.commands.enabled = value
|
||||
self.modified = True
|
||||
elif choice == 2:
|
||||
value = Prompt.ask("Command prefix", default=self.config.commands.prefix)
|
||||
if value != self.config.commands.prefix:
|
||||
self.config.commands.prefix = value
|
||||
self.modified = True
|
||||
elif choice == 3:
|
||||
console.print("\n[dim]Built-in: help, ping, reset, status, weather[/dim]")
|
||||
value = Prompt.ask("Disabled commands (comma-separated)", default=",".join(self.config.commands.disabled_commands))
|
||||
commands = [c.strip() for c in value.split(",") if c.strip()]
|
||||
if commands != self.config.commands.disabled_commands:
|
||||
self.config.commands.disabled_commands = commands
|
||||
self.modified = True
|
||||
elif choice == 4:
|
||||
self._custom_commands_editor()
|
||||
|
||||
def _custom_commands_editor(self) -> None:
|
||||
"""Edit custom commands."""
|
||||
while True:
|
||||
self._clear()
|
||||
console.print("[bold]Custom Commands[/bold]\n")
|
||||
|
||||
if self.config.commands.custom_commands:
|
||||
for name, data in self.config.commands.custom_commands.items():
|
||||
response = data.get("response", data) if isinstance(data, dict) else data
|
||||
console.print(f" [cyan]{self.config.commands.prefix}{name}[/cyan] → {response[:50]}...")
|
||||
else:
|
||||
console.print(" [dim]No custom commands[/dim]")
|
||||
|
||||
console.print("\n[cyan]a[/cyan] Add command")
|
||||
console.print("[cyan]r[/cyan] Remove command")
|
||||
console.print("[cyan]0[/cyan] Back")
|
||||
console.print()
|
||||
|
||||
choice = Prompt.ask("Select", default="0")
|
||||
|
||||
if choice == "0":
|
||||
return
|
||||
elif choice.lower() == "a":
|
||||
name = Prompt.ask("Command name (without prefix)")
|
||||
if name:
|
||||
response = Prompt.ask("Response text")
|
||||
if response:
|
||||
self.config.commands.custom_commands[name] = {"response": response}
|
||||
self.modified = True
|
||||
elif choice.lower() == "r":
|
||||
if self.config.commands.custom_commands:
|
||||
name = Prompt.ask("Command name to remove")
|
||||
if name in self.config.commands.custom_commands:
|
||||
del self.config.commands.custom_commands[name]
|
||||
self.modified = True
|
||||
|
||||
def _personality_settings(self) -> None:
|
||||
"""Personality settings submenu."""
|
||||
while True:
|
||||
self._clear()
|
||||
console.print("[bold]Personality[/bold]\n")
|
||||
|
||||
table = Table(box=box.ROUNDED)
|
||||
table.add_column("Option", style="cyan", width=4)
|
||||
table.add_column("Setting", style="white")
|
||||
table.add_column("Value", style="green")
|
||||
|
||||
prompt_preview = self.config.personality.system_prompt[:40] + "..." if self.config.personality.system_prompt else "[dim]using LLM default[/dim]"
|
||||
table.add_row("1", "System Prompt Override", prompt_preview)
|
||||
table.add_row("2", "Context Injection", self.config.personality.context_injection[:30] + "..." if self.config.personality.context_injection else "[dim]none[/dim]")
|
||||
table.add_row("3", "Personas", f"{len(self.config.personality.personas)} defined")
|
||||
table.add_row("0", "Back", "")
|
||||
|
||||
console.print(table)
|
||||
console.print()
|
||||
|
||||
choice = IntPrompt.ask("Select option", default=0)
|
||||
|
||||
if choice == 0:
|
||||
return
|
||||
elif choice == 1:
|
||||
console.print("\n[dim]Current:[/dim]")
|
||||
console.print(self.config.personality.system_prompt or "(none)")
|
||||
if Confirm.ask("\nEdit system prompt?", default=False):
|
||||
value = Prompt.ask("New system prompt (empty to clear)")
|
||||
if value != self.config.personality.system_prompt:
|
||||
self.config.personality.system_prompt = value
|
||||
self.modified = True
|
||||
elif choice == 2:
|
||||
console.print("\n[dim]Variables: {time}, {sender_name}, {channel}[/dim]")
|
||||
value = Prompt.ask("Context injection template", default=self.config.personality.context_injection)
|
||||
if value != self.config.personality.context_injection:
|
||||
self.config.personality.context_injection = value
|
||||
self.modified = True
|
||||
elif choice == 3:
|
||||
self._personas_editor()
|
||||
|
||||
def _personas_editor(self) -> None:
|
||||
"""Edit personas."""
|
||||
while True:
|
||||
self._clear()
|
||||
console.print("[bold]Personas[/bold]\n")
|
||||
|
||||
if self.config.personality.personas:
|
||||
for name, data in self.config.personality.personas.items():
|
||||
trigger = data.get("trigger", f"!{name}") if isinstance(data, dict) else f"!{name}"
|
||||
console.print(f" [cyan]{name}[/cyan] (trigger: {trigger})")
|
||||
else:
|
||||
console.print(" [dim]No personas defined[/dim]")
|
||||
|
||||
console.print("\n[cyan]a[/cyan] Add persona")
|
||||
console.print("[cyan]r[/cyan] Remove persona")
|
||||
console.print("[cyan]0[/cyan] Back")
|
||||
console.print()
|
||||
|
||||
choice = Prompt.ask("Select", default="0")
|
||||
|
||||
if choice == "0":
|
||||
return
|
||||
elif choice.lower() == "a":
|
||||
name = Prompt.ask("Persona name")
|
||||
if name:
|
||||
trigger = Prompt.ask("Trigger command", default=f"!{name}")
|
||||
prompt = Prompt.ask("System prompt for this persona")
|
||||
if prompt:
|
||||
self.config.personality.personas[name] = {"trigger": trigger, "prompt": prompt}
|
||||
self.modified = True
|
||||
elif choice.lower() == "r":
|
||||
if self.config.personality.personas:
|
||||
name = Prompt.ask("Persona name to remove")
|
||||
if name in self.config.personality.personas:
|
||||
del self.config.personality.personas[name]
|
||||
self.modified = True
|
||||
|
||||
def _logging_settings(self) -> None:
|
||||
"""Logging settings submenu."""
|
||||
while True:
|
||||
self._clear()
|
||||
console.print("[bold]Logging[/bold]\n")
|
||||
|
||||
table = Table(box=box.ROUNDED)
|
||||
table.add_column("Option", style="cyan", width=4)
|
||||
table.add_column("Setting", style="white")
|
||||
table.add_column("Value", style="green")
|
||||
|
||||
table.add_row("1", "Log Level", self.config.logging.level)
|
||||
table.add_row("2", "Log File", self.config.logging.file or "[dim]console only[/dim]")
|
||||
table.add_row("3", "Max File Size (MB)", str(self.config.logging.max_size_mb))
|
||||
table.add_row("4", "Backup Count", str(self.config.logging.backup_count))
|
||||
table.add_row("5", "Log Messages", self._status_icon(self.config.logging.log_messages))
|
||||
table.add_row("6", "Log Responses", self._status_icon(self.config.logging.log_responses))
|
||||
table.add_row("7", "Log API Calls", self._status_icon(self.config.logging.log_api_calls))
|
||||
table.add_row("0", "Back", "")
|
||||
|
||||
console.print(table)
|
||||
console.print()
|
||||
|
||||
choice = IntPrompt.ask("Select option", default=0)
|
||||
|
||||
if choice == 0:
|
||||
return
|
||||
elif choice == 1:
|
||||
console.print("\n[cyan]1.[/cyan] DEBUG")
|
||||
console.print("[cyan]2.[/cyan] INFO")
|
||||
console.print("[cyan]3.[/cyan] WARNING")
|
||||
console.print("[cyan]4.[/cyan] ERROR")
|
||||
sel = IntPrompt.ask("Select", default=2)
|
||||
levels = {1: "DEBUG", 2: "INFO", 3: "WARNING", 4: "ERROR"}
|
||||
value = levels.get(sel, "INFO")
|
||||
if value != self.config.logging.level:
|
||||
self.config.logging.level = value
|
||||
self.modified = True
|
||||
elif choice == 2:
|
||||
value = Prompt.ask("Log file path (empty for console only)", default=self.config.logging.file)
|
||||
if value != self.config.logging.file:
|
||||
self.config.logging.file = value
|
||||
self.modified = True
|
||||
elif choice == 3:
|
||||
value = IntPrompt.ask("Max file size (MB)", default=self.config.logging.max_size_mb)
|
||||
if value != self.config.logging.max_size_mb:
|
||||
self.config.logging.max_size_mb = value
|
||||
self.modified = True
|
||||
elif choice == 4:
|
||||
value = IntPrompt.ask("Backup count", default=self.config.logging.backup_count)
|
||||
if value != self.config.logging.backup_count:
|
||||
self.config.logging.backup_count = value
|
||||
self.modified = True
|
||||
elif choice == 5:
|
||||
value = Confirm.ask("Log messages?", default=self.config.logging.log_messages)
|
||||
if value != self.config.logging.log_messages:
|
||||
self.config.logging.log_messages = value
|
||||
self.modified = True
|
||||
elif choice == 6:
|
||||
value = Confirm.ask("Log responses?", default=self.config.logging.log_responses)
|
||||
if value != self.config.logging.log_responses:
|
||||
self.config.logging.log_responses = value
|
||||
self.modified = True
|
||||
elif choice == 7:
|
||||
value = Confirm.ask("Log API calls?", default=self.config.logging.log_api_calls)
|
||||
if value != self.config.logging.log_api_calls:
|
||||
self.config.logging.log_api_calls = value
|
||||
self.modified = True
|
||||
|
||||
def _web_status_settings(self) -> None:
|
||||
"""Web status page settings submenu."""
|
||||
while True:
|
||||
self._clear()
|
||||
console.print("[bold]Web Status Page[/bold]\n")
|
||||
|
||||
table = Table(box=box.ROUNDED)
|
||||
table.add_column("Option", style="cyan", width=4)
|
||||
table.add_column("Setting", style="white")
|
||||
table.add_column("Value", style="green")
|
||||
|
||||
table.add_row("1", "Enabled", self._status_icon(self.config.web_status.enabled))
|
||||
table.add_row("2", "Port", str(self.config.web_status.port))
|
||||
table.add_row("3", "Show Uptime", self._status_icon(self.config.web_status.show_uptime))
|
||||
table.add_row("4", "Show Message Count", self._status_icon(self.config.web_status.show_message_count))
|
||||
table.add_row("5", "Show Connected Nodes", self._status_icon(self.config.web_status.show_connected_nodes))
|
||||
table.add_row("6", "Show Recent Activity", self._status_icon(self.config.web_status.show_recent_activity))
|
||||
table.add_row("7", "Require Auth", self._status_icon(self.config.web_status.require_auth))
|
||||
table.add_row("8", "Auth Password", "****" if self.config.web_status.auth_password else "[dim]not set[/dim]")
|
||||
table.add_row("0", "Back", "")
|
||||
|
||||
console.print(table)
|
||||
console.print()
|
||||
|
||||
choice = IntPrompt.ask("Select option", default=0)
|
||||
|
||||
if choice == 0:
|
||||
return
|
||||
elif choice == 1:
|
||||
value = Confirm.ask("Enable web status?", default=self.config.web_status.enabled)
|
||||
if value != self.config.web_status.enabled:
|
||||
self.config.web_status.enabled = value
|
||||
self.modified = True
|
||||
elif choice == 2:
|
||||
value = IntPrompt.ask("Port", default=self.config.web_status.port)
|
||||
if value != self.config.web_status.port:
|
||||
self.config.web_status.port = value
|
||||
self.modified = True
|
||||
elif choice == 3:
|
||||
value = Confirm.ask("Show uptime?", default=self.config.web_status.show_uptime)
|
||||
if value != self.config.web_status.show_uptime:
|
||||
self.config.web_status.show_uptime = value
|
||||
self.modified = True
|
||||
elif choice == 4:
|
||||
value = Confirm.ask("Show message count?", default=self.config.web_status.show_message_count)
|
||||
if value != self.config.web_status.show_message_count:
|
||||
self.config.web_status.show_message_count = value
|
||||
self.modified = True
|
||||
elif choice == 5:
|
||||
value = Confirm.ask("Show connected nodes?", default=self.config.web_status.show_connected_nodes)
|
||||
if value != self.config.web_status.show_connected_nodes:
|
||||
self.config.web_status.show_connected_nodes = value
|
||||
self.modified = True
|
||||
elif choice == 6:
|
||||
value = Confirm.ask("Show recent activity?", default=self.config.web_status.show_recent_activity)
|
||||
if value != self.config.web_status.show_recent_activity:
|
||||
self.config.web_status.show_recent_activity = value
|
||||
self.modified = True
|
||||
elif choice == 7:
|
||||
value = Confirm.ask("Require authentication?", default=self.config.web_status.require_auth)
|
||||
if value != self.config.web_status.require_auth:
|
||||
self.config.web_status.require_auth = value
|
||||
self.modified = True
|
||||
elif choice == 8:
|
||||
value = Prompt.ask("Password", password=True)
|
||||
if value:
|
||||
self.config.web_status.auth_password = value
|
||||
self.modified = True
|
||||
|
||||
def _announcements_settings(self) -> None:
|
||||
"""Announcements settings submenu."""
|
||||
while True:
|
||||
self._clear()
|
||||
console.print("[bold]Announcements[/bold]\n")
|
||||
|
||||
table = Table(box=box.ROUNDED)
|
||||
table.add_column("Option", style="cyan", width=4)
|
||||
table.add_column("Setting", style="white")
|
||||
table.add_column("Value", style="green")
|
||||
|
||||
table.add_row("1", "Enabled", self._status_icon(self.config.announcements.enabled))
|
||||
table.add_row("2", "Interval (hours)", str(self.config.announcements.interval_hours))
|
||||
table.add_row("3", "Channel", str(self.config.announcements.channel))
|
||||
table.add_row("4", "Messages", f"{len(self.config.announcements.messages)} defined")
|
||||
table.add_row("5", "Random Order", self._status_icon(self.config.announcements.random_order))
|
||||
table.add_row("0", "Back", "")
|
||||
|
||||
console.print(table)
|
||||
console.print()
|
||||
|
||||
choice = IntPrompt.ask("Select option", default=0)
|
||||
|
||||
if choice == 0:
|
||||
return
|
||||
elif choice == 1:
|
||||
value = Confirm.ask("Enable announcements?", default=self.config.announcements.enabled)
|
||||
if value != self.config.announcements.enabled:
|
||||
self.config.announcements.enabled = value
|
||||
self.modified = True
|
||||
elif choice == 2:
|
||||
value = IntPrompt.ask("Interval (hours)", default=self.config.announcements.interval_hours)
|
||||
if value != self.config.announcements.interval_hours:
|
||||
self.config.announcements.interval_hours = value
|
||||
self.modified = True
|
||||
elif choice == 3:
|
||||
value = IntPrompt.ask("Channel", default=self.config.announcements.channel)
|
||||
if value != self.config.announcements.channel:
|
||||
self.config.announcements.channel = value
|
||||
self.modified = True
|
||||
elif choice == 4:
|
||||
self._announcements_messages_editor()
|
||||
elif choice == 5:
|
||||
value = Confirm.ask("Random order?", default=self.config.announcements.random_order)
|
||||
if value != self.config.announcements.random_order:
|
||||
self.config.announcements.random_order = value
|
||||
self.modified = True
|
||||
|
||||
def _announcements_messages_editor(self) -> None:
|
||||
"""Edit announcement messages."""
|
||||
while True:
|
||||
self._clear()
|
||||
console.print("[bold]Announcement Messages[/bold]\n")
|
||||
|
||||
if self.config.announcements.messages:
|
||||
for i, msg in enumerate(self.config.announcements.messages, 1):
|
||||
console.print(f" {i}. {msg[:60]}...")
|
||||
else:
|
||||
console.print(" [dim]No messages[/dim]")
|
||||
|
||||
console.print("\n[cyan]a[/cyan] Add message")
|
||||
console.print("[cyan]r[/cyan] Remove message")
|
||||
console.print("[cyan]0[/cyan] Back")
|
||||
console.print()
|
||||
|
||||
choice = Prompt.ask("Select", default="0")
|
||||
|
||||
if choice == "0":
|
||||
return
|
||||
elif choice.lower() == "a":
|
||||
value = Prompt.ask("Message text")
|
||||
if value:
|
||||
self.config.announcements.messages.append(value)
|
||||
self.modified = True
|
||||
elif choice.lower() == "r":
|
||||
if self.config.announcements.messages:
|
||||
idx = IntPrompt.ask("Remove which number", default=1)
|
||||
if 1 <= idx <= len(self.config.announcements.messages):
|
||||
self.config.announcements.messages.pop(idx - 1)
|
||||
self.modified = True
|
||||
|
||||
def _webhook_settings(self) -> None:
|
||||
"""Webhook settings submenu."""
|
||||
while True:
|
||||
self._clear()
|
||||
console.print("[bold]Webhooks[/bold]\n")
|
||||
|
||||
table = Table(box=box.ROUNDED)
|
||||
table.add_column("Option", style="cyan", width=4)
|
||||
table.add_column("Setting", style="white")
|
||||
table.add_column("Value", style="green")
|
||||
|
||||
table.add_row("1", "Enabled", self._status_icon(self.config.integrations.webhook.enabled))
|
||||
table.add_row("2", "URL", self.config.integrations.webhook.url or "[dim]not set[/dim]")
|
||||
table.add_row("3", "Events", ", ".join(self.config.integrations.webhook.events))
|
||||
table.add_row("0", "Back", "")
|
||||
|
||||
console.print(table)
|
||||
console.print()
|
||||
|
||||
choice = IntPrompt.ask("Select option", default=0)
|
||||
|
||||
if choice == 0:
|
||||
return
|
||||
elif choice == 1:
|
||||
value = Confirm.ask("Enable webhooks?", default=self.config.integrations.webhook.enabled)
|
||||
if value != self.config.integrations.webhook.enabled:
|
||||
self.config.integrations.webhook.enabled = value
|
||||
self.modified = True
|
||||
elif choice == 2:
|
||||
value = Prompt.ask("Webhook URL", default=self.config.integrations.webhook.url)
|
||||
if value != self.config.integrations.webhook.url:
|
||||
self.config.integrations.webhook.url = value
|
||||
self.modified = True
|
||||
elif choice == 3:
|
||||
console.print("\n[dim]Available: message_received, response_sent, error, startup, shutdown[/dim]")
|
||||
value = Prompt.ask("Events (comma-separated)", default=",".join(self.config.integrations.webhook.events))
|
||||
events = [e.strip() for e in value.split(",") if e.strip()]
|
||||
if events != self.config.integrations.webhook.events:
|
||||
self.config.integrations.webhook.events = events
|
||||
self.modified = True
|
||||
|
||||
def _setup_wizard(self) -> None:
|
||||
"""First-time setup wizard."""
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue