mirror of
https://github.com/zvx-echo6/meshai.git
synced 2026-05-22 15:44:39 +02:00
Add comprehensive config options matching fq51bbs
New features: - Rate limiting (per-user and global) - Enhanced logging with file rotation - LLM fallback backend support - Safety filtering (profanity, blocked phrases, emergency keywords) - User management (blocklist, allowlist, admin/VIP nodes) - Custom commands with static responses - Personality/prompt templates with persona switching - Web status page with JSON API - Periodic announcements/broadcasts - Webhook integrations New modules: - rate_limiter.py - Per-user and global rate limiting - safety.py - Response filtering and user access control - personality.py - Prompt templates and persona management - web_status.py - Simple web status dashboard - announcements.py - Periodic broadcast scheduler - webhook.py - Webhook notification client - log_setup.py - Enhanced logging configuration - backends/fallback.py - LLM fallback wrapper Config expanded from ~50 to ~200 lines with full documentation. 🤖 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
04248bc692
commit
165da72d8d
13 changed files with 1796 additions and 48 deletions
|
|
@ -8,11 +8,38 @@ from .base import CommandContext, CommandHandler
|
|||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class CustomCommandHandler(CommandHandler):
|
||||
"""Handler for user-defined static response commands."""
|
||||
|
||||
def __init__(self, name: str, response: str, description: str = "Custom command"):
|
||||
self._name = name
|
||||
self._response = response
|
||||
self._description = description
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def description(self) -> str:
|
||||
return self._description
|
||||
|
||||
@property
|
||||
def usage(self) -> str:
|
||||
return f"!{self._name}"
|
||||
|
||||
async def execute(self, args: str, context: CommandContext) -> str:
|
||||
return self._response
|
||||
|
||||
|
||||
class CommandDispatcher:
|
||||
"""Registry and dispatcher for bang commands."""
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, prefix: str = "!", disabled_commands: Optional[list[str]] = None):
|
||||
self._commands: dict[str, CommandHandler] = {}
|
||||
self._custom_commands: dict[str, str] = {}
|
||||
self.prefix = prefix
|
||||
self.disabled_commands = set(c.upper() for c in (disabled_commands or []))
|
||||
|
||||
def register(self, handler: CommandHandler) -> None:
|
||||
"""Register a command handler.
|
||||
|
|
@ -21,9 +48,40 @@ class CommandDispatcher:
|
|||
handler: CommandHandler instance to register
|
||||
"""
|
||||
name = handler.name.upper()
|
||||
if name in self.disabled_commands:
|
||||
logger.debug(f"Skipping disabled command: !{handler.name}")
|
||||
return
|
||||
self._commands[name] = handler
|
||||
logger.debug(f"Registered command: !{handler.name}")
|
||||
|
||||
def register_custom(self, name: str, response: str, description: str = "Custom command") -> None:
|
||||
"""Register a custom static response command.
|
||||
|
||||
Args:
|
||||
name: Command name (without prefix)
|
||||
response: Static response text
|
||||
description: Command description for help
|
||||
"""
|
||||
handler = CustomCommandHandler(name, response, description)
|
||||
self.register(handler)
|
||||
self._custom_commands[name.upper()] = response
|
||||
|
||||
def unregister(self, name: str) -> bool:
|
||||
"""Unregister a command.
|
||||
|
||||
Args:
|
||||
name: Command name to remove
|
||||
|
||||
Returns:
|
||||
True if command was removed, False if not found
|
||||
"""
|
||||
name = name.upper()
|
||||
if name in self._commands:
|
||||
del self._commands[name]
|
||||
self._custom_commands.pop(name, None)
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_commands(self) -> list[CommandHandler]:
|
||||
"""Get all registered command handlers."""
|
||||
return list(self._commands.values())
|
||||
|
|
@ -35,25 +93,25 @@ class CommandDispatcher:
|
|||
text: Message text to check
|
||||
|
||||
Returns:
|
||||
True if text starts with !
|
||||
True if text starts with command prefix
|
||||
"""
|
||||
return text.strip().startswith("!")
|
||||
return text.strip().startswith(self.prefix)
|
||||
|
||||
def parse(self, text: str) -> tuple[Optional[str], str]:
|
||||
"""Parse command and arguments from text.
|
||||
|
||||
Args:
|
||||
text: Message text starting with !
|
||||
text: Message text starting with command prefix
|
||||
|
||||
Returns:
|
||||
Tuple of (command_name, arguments) or (None, "") if invalid
|
||||
"""
|
||||
text = text.strip()
|
||||
if not text.startswith("!"):
|
||||
if not text.startswith(self.prefix):
|
||||
return None, ""
|
||||
|
||||
# Remove ! prefix
|
||||
text = text[1:]
|
||||
# Remove prefix
|
||||
text = text[len(self.prefix):]
|
||||
|
||||
# Split into command and args
|
||||
parts = text.split(maxsplit=1)
|
||||
|
|
@ -96,21 +154,48 @@ class CommandDispatcher:
|
|||
return f"Error: {str(e)[:100]}"
|
||||
|
||||
|
||||
def create_dispatcher() -> CommandDispatcher:
|
||||
"""Create and populate command dispatcher with default commands."""
|
||||
def create_dispatcher(
|
||||
prefix: str = "!",
|
||||
disabled_commands: Optional[list[str]] = None,
|
||||
custom_commands: Optional[dict] = None,
|
||||
) -> CommandDispatcher:
|
||||
"""Create and populate command dispatcher with default commands.
|
||||
|
||||
Args:
|
||||
prefix: Command prefix (default: "!")
|
||||
disabled_commands: List of command names to disable
|
||||
custom_commands: Dict of name -> response for custom commands
|
||||
|
||||
Returns:
|
||||
Configured CommandDispatcher
|
||||
"""
|
||||
from .help import HelpCommand
|
||||
from .ping import PingCommand
|
||||
from .reset import ResetCommand
|
||||
from .status import StatusCommand
|
||||
from .weather import WeatherCommand
|
||||
|
||||
dispatcher = CommandDispatcher()
|
||||
dispatcher = CommandDispatcher(prefix=prefix, disabled_commands=disabled_commands)
|
||||
|
||||
# Register all commands
|
||||
# Register all built-in commands
|
||||
dispatcher.register(HelpCommand(dispatcher))
|
||||
dispatcher.register(PingCommand())
|
||||
dispatcher.register(ResetCommand())
|
||||
dispatcher.register(StatusCommand())
|
||||
dispatcher.register(WeatherCommand())
|
||||
|
||||
# Register custom commands
|
||||
if custom_commands:
|
||||
for name, response in custom_commands.items():
|
||||
if isinstance(response, dict):
|
||||
# Support dict format: {response: "...", description: "..."}
|
||||
dispatcher.register_custom(
|
||||
name,
|
||||
response.get("response", ""),
|
||||
response.get("description", "Custom command"),
|
||||
)
|
||||
else:
|
||||
# Simple string response
|
||||
dispatcher.register_custom(name, str(response))
|
||||
|
||||
return dispatcher
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue