# -*- coding: utf-8 -*- """ Basisklasse für Trixy-Plugins. Plugins erweitern diese Klasse und implementieren ihre Funktionalität. """ import json from abc import ABC, abstractmethod from pathlib import Path from typing import Any, TYPE_CHECKING from trixy_core.utils.debug import pdebug, perror, pinfo if TYPE_CHECKING: from trixy_core.application import IApplication class TrixyPlugin(ABC): """ Abstrakte Basisklasse für alle Trixy-Plugins. Ein Plugin muss in einem eigenen Verzeichnis unter ./plugins/ liegen und folgende Struktur haben: - main.py: Plugin-Klasse die TrixyPlugin erweitert - config.json: Plugin-Konfiguration Attribute: NAME: Eindeutiger Plugin-Name VERSION: Plugin-Version DESCRIPTION: Kurze Beschreibung AUTHOR: Autor des Plugins """ NAME: str = "" VERSION: str = "1.0.0" DESCRIPTION: str = "" AUTHOR: str = "" def __init__( self, application: "IApplication", plugin_path: Path, config: dict[str, Any] | None = None ) -> None: """ Initialisiert das Plugin. Args: application: Referenz zur Hauptanwendung plugin_path: Pfad zum Plugin-Verzeichnis config: Optionale Konfiguration """ self._application = application self._plugin_path = plugin_path self._config: dict[str, Any] = config or {} self._enabled = True @property def application(self) -> "IApplication": """Gibt die Anwendungsreferenz zurück.""" return self._application @property def plugin_path(self) -> Path: """Gibt den Plugin-Pfad zurück.""" return self._plugin_path @property def config(self) -> dict[str, Any]: """Gibt die Plugin-Konfiguration zurück.""" return self._config @config.setter def config(self, value: dict[str, Any]) -> None: """Setzt die Plugin-Konfiguration.""" self._config = value @property def enabled(self) -> bool: """Ist das Plugin aktiviert?""" return self._enabled @enabled.setter def enabled(self, value: bool) -> None: """Aktiviert oder deaktiviert das Plugin.""" self._enabled = value @property def name(self) -> str: """Gibt den Plugin-Namen zurück.""" return self.NAME or self.__class__.__name__ def is_enabled(self) -> bool: """Prüft, ob das Plugin aktiviert ist.""" return self._enabled def get_config_value(self, key: str, default: Any = None) -> Any: """ Gibt einen Konfigurationswert zurück. Args: key: Schlüssel (unterstützt Punkt-Notation: "section.key") default: Standardwert Returns: Konfigurationswert oder default """ keys = key.split(".") value = self._config for k in keys: if isinstance(value, dict) and k in value: value = value[k] else: return default return value def set_config_value(self, key: str, value: Any) -> None: """ Setzt einen Konfigurationswert. Args: key: Schlüssel (unterstützt Punkt-Notation) value: Zu setzender Wert """ keys = key.split(".") config = self._config for k in keys[:-1]: if k not in config: config[k] = {} config = config[k] config[keys[-1]] = value def reload_config(self) -> bool: """ Lädt die Konfiguration neu aus der Datei. Returns: True bei Erfolg """ config_path = self._plugin_path / "config.json" if not config_path.exists(): pdebug(f"Keine config.json für Plugin: {self.name}") return False try: with open(config_path, "r", encoding="utf-8") as f: self._config = json.load(f) pinfo(f"Plugin-Konfiguration neu geladen: {self.name}") return True except Exception as e: perror(f"Fehler beim Laden der Plugin-Konfiguration: {e}") return False def save_config(self) -> bool: """ Speichert die Konfiguration in die Datei. Returns: True bei Erfolg """ config_path = self._plugin_path / "config.json" try: with open(config_path, "w", encoding="utf-8") as f: json.dump(self._config, f, indent=2, ensure_ascii=False) pinfo(f"Plugin-Konfiguration gespeichert: {self.name}") return True except Exception as e: perror(f"Fehler beim Speichern der Plugin-Konfiguration: {e}") return False @abstractmethod async def on_load(self) -> None: """ Wird aufgerufen, wenn das Plugin geladen wird. Hier sollten Event-Handler registriert und Initialisierungen durchgeführt werden. """ pass @abstractmethod async def on_unload(self) -> None: """ Wird aufgerufen, wenn das Plugin entladen wird. Hier sollten Ressourcen freigegeben werden. """ pass async def on_enable(self) -> None: """Wird aufgerufen, wenn das Plugin aktiviert wird.""" pass async def on_disable(self) -> None: """Wird aufgerufen, wenn das Plugin deaktiviert wird.""" pass async def on_config_change(self, old_config: dict[str, Any]) -> None: """ Wird aufgerufen, wenn sich die Konfiguration ändert. Args: old_config: Vorherige Konfiguration """ pass def get_config_tool_tabs(self) -> list[dict[str, Any]]: """ Gibt zusaetzliche Tabs fuer das Config-Tool zurueck. Plugins koennen diese Methode ueberschreiben, um benutzerdefinierte Tabs in der Plugin-Detailansicht des Config-Tools anzubieten. Returns: Liste von Tab-Definitionen, z.B.: [{"title": "Test", "description": "TTS-Test Seite"}] """ return [] def __repr__(self) -> str: return f""