"""Abstract provider command handler interface.""" from __future__ import annotations from abc import ABC, abstractmethod from dataclasses import dataclass, field from typing import Any from ..database.models import ( CommandConfig, CommandTracker, CommandTrackerListener, ServiceProvider, TelegramBot, ) @dataclass(frozen=True) class CommandResponse: """A single response from one tracker's command execution.""" text: str | None = None media: list[dict[str, Any]] = field(default_factory=list) 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. Each handler call receives a single (tracker, config, provider) context. """ 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, provider: ServiceProvider, cmd_templates: dict[str, dict[str, str]], bot: TelegramBot, tracker: CommandTracker, config: CommandConfig, *, listener: CommandTrackerListener | None = None, page: int = 1, ) -> CommandResponse | None: """Handle a provider-specific command for a single tracker. 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' (from this tracker's config). provider: The service provider instance for this tracker. cmd_templates: Template slots for this tracker's command template config. bot: The Telegram bot instance. tracker: The command tracker being dispatched. config: The command config for this tracker. Returns: A CommandResponse, 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 {}