refactor: provider-agnostic bot command system + Gitea commands
Refactored the monolithic command handler (707 lines) into a pluggable provider-handler architecture: - Abstract ProviderCommandHandler interface (base.py) - Handler dispatch registry routes commands by provider type - Extracted all Immich logic into ImmichCommandHandler - New GiteaCommandHandler with /status, /repos, /issues, /prs, /commits - Multi-provider routing: groups context by provider type, finds handler - handler.py reduced to ~280 line thin orchestrator Gitea commands: - Extended GiteaClient with get_repo_issues, get_repo_pulls, get_repo_commits - 30 Jinja2 command templates (15 EN + 15 RU) - Gitea capabilities updated with 6 commands + 15 command_slots - Default command config + command template config seeded on startup - Rate limiting: Gitea API commands share "api" category (15s cooldown) Also: - Command configs API accepts "gitea" provider type - System command configs (user_id=0) visible to all users - Webhook URL shown on Gitea provider card and edit form - Scan interval hidden for webhook-based providers
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
"""Abstract provider command handler interface."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Any
|
||||
|
||||
from ..database.models import CommandTracker, CommandConfig, ServiceProvider, TelegramBot
|
||||
|
||||
|
||||
class ProviderCommandHandler(ABC):
|
||||
"""Base class for provider-specific bot command handlers.
|
||||
|
||||
Each provider (Immich, Gitea, etc.) implements this interface to handle
|
||||
its own set of commands. The dispatch layer routes commands to the
|
||||
correct handler based on the provider type.
|
||||
"""
|
||||
|
||||
provider_type: str
|
||||
|
||||
@abstractmethod
|
||||
def get_provider_commands(self) -> set[str]:
|
||||
"""Return the set of command names this handler owns.
|
||||
|
||||
These are provider-specific commands (e.g., 'albums' for Immich,
|
||||
'repos' for Gitea). Universal commands like 'help' and 'start'
|
||||
are handled by the main dispatcher.
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
async def handle(
|
||||
self,
|
||||
cmd: str,
|
||||
args: str,
|
||||
count: int,
|
||||
locale: str,
|
||||
response_mode: str,
|
||||
providers_map: dict[int, ServiceProvider],
|
||||
cmd_templates: dict[str, dict[str, str]],
|
||||
bot: TelegramBot,
|
||||
ctx_tuples: list[tuple[CommandTracker, CommandConfig, ServiceProvider]],
|
||||
) -> str | list[dict[str, Any]] | None:
|
||||
"""Handle a provider-specific command.
|
||||
|
||||
Args:
|
||||
cmd: The command name (without '/').
|
||||
args: Arguments after the command.
|
||||
count: Number of results to return.
|
||||
locale: User's locale ('en', 'ru').
|
||||
response_mode: 'media' or 'text'.
|
||||
providers_map: Provider instances keyed by ID.
|
||||
cmd_templates: Template slots {slot_name: {locale: template}}.
|
||||
bot: The Telegram bot instance.
|
||||
ctx_tuples: Command context tuples for this provider type.
|
||||
|
||||
Returns:
|
||||
Text response, media list, or None if unhandled.
|
||||
"""
|
||||
|
||||
def get_rate_categories(self) -> dict[str, str]:
|
||||
"""Return rate limit category mapping for this provider's commands.
|
||||
|
||||
Keys are command names, values are category strings.
|
||||
Commands not listed default to 'default' category.
|
||||
"""
|
||||
return {}
|
||||
Reference in New Issue
Block a user