# /// # base.py # 描述:插件基类定义,插件系统核心接口 # 作者:AI Generated # 创建日期:2026-04-05 # 更新日期:2026-04-06 # /// from abc import ABC, abstractmethod from typing import Any, Dict, List, Optional, Callable from enum import Enum class PluginType(Enum): MESSAGE_HANDLER = "message_handler" DATA_SOURCE = "data_source" ACTION_TRIGGER = "action_trigger" AI_AGENT = "ai_agent" SCHEDULED_TASK = "scheduled_task" HTTP_SCHEDULED_SENDER = "http_scheduled_sender" CUSTOM = "custom" class PluginBase(ABC): plugin_name: str = "" plugin_version: str = "1.0.0" plugin_type: PluginType = PluginType.CUSTOM plugin_description: str = "" plugin_author: str = "" def __init__(self): self.enabled = False self.config: Dict[str, Any] = {} self._metadata: Dict[str, Any] = {} @abstractmethod def initialize(self, config: Dict[str, Any]) -> bool: pass @abstractmethod def execute(self, params: Dict[str, Any]) -> Dict[str, Any]: pass def enable(self): self.enabled = True def disable(self): self.enabled = False def get_info(self) -> Dict[str, Any]: return { "name": self.plugin_name, "version": self.plugin_version, "type": self.plugin_type.value, "description": self.plugin_description, "author": self.plugin_author, "enabled": self.enabled, "config": self.config, "metadata": self._metadata, } def set_metadata(self, key: str, value: Any): self._metadata[key] = value def get_metadata(self, key: str, default: Any = None) -> Any: return self._metadata.get(key, default) def validate_config(self, config: Dict[str, Any], required_fields: List[str]) -> tuple: missing = [f for f in required_fields if not config.get(f)] if missing: return False, f"缺少必填字段: {', '.join(missing)}" return True, "" class MessageHandlerPlugin(PluginBase): plugin_type = PluginType.MESSAGE_HANDLER @abstractmethod def handle_message(self, message: Dict[str, Any]) -> Optional[Dict[str, Any]]: pass def should_handle(self, message: Dict[str, Any]) -> bool: return True class DataSourcePlugin(PluginBase): plugin_type = PluginType.DATA_SOURCE @abstractmethod def fetch_data(self, query: str, params: Dict[str, Any]) -> List[Dict[str, Any]]: pass def get_schema(self) -> Dict[str, Any]: return { "type": "object", "properties": {} } class ActionTriggerPlugin(PluginBase): plugin_type = PluginType.ACTION_TRIGGER @abstractmethod def trigger(self, action: str, params: Dict[str, Any]) -> Dict[str, Any]: pass def get_supported_actions(self) -> List[str]: return [] class AIAgentPlugin(PluginBase): plugin_type = PluginType.AI_AGENT @abstractmethod def process(self, input_text: str, context: Dict[str, Any]) -> str: pass @abstractmethod def get_response(self, messages: List[Dict[str, str]]) -> str: pass def get_system_prompt(self) -> str: return "" class ScheduledTaskPlugin(PluginBase): plugin_type = PluginType.SCHEDULED_TASK @abstractmethod def get_cron_expression(self) -> str: pass @abstractmethod def should_run_and_get_next(self) -> tuple: pass @abstractmethod def run_task(self) -> Dict[str, Any]: pass def on_task_success(self, result: Dict[str, Any]): pass def on_task_error(self, error: str): pass def notify(self, title: str, message: str, level: str = "info") -> bool: from services.plugin_notification_service import plugin_notification_service return plugin_notification_service.send_notification( plugin_name=self.plugin_name, title=title, message=message, level=level ) def notify_error(self, title: str, message: str) -> bool: return self.notify(title, message, level="error") def notify_warning(self, title: str, message: str) -> bool: return self.notify(title, message, level="warning") def notify_success(self, title: str, message: str) -> bool: return self.notify(title, message, level="success") class HTTPScheduledSenderPlugin(PluginBase): plugin_type = PluginType.HTTP_SCHEDULED_SENDER @abstractmethod def get_cron_expression(self) -> str: pass @abstractmethod def should_run_and_get_next(self) -> tuple: pass @abstractmethod def run_task(self) -> Dict[str, Any]: pass @abstractmethod def fetch_data(self) -> Any: pass @abstractmethod def parse_items(self, data: Any) -> List[Dict[str, Any]]: pass @abstractmethod def format_message(self, items: List[Dict[str, Any]]) -> str: pass class PluginRegistry: _plugins: Dict[str, PluginBase] = {} _callbacks: List[Callable] = [] _event_handlers: Dict[str, List[Callable]] = {} @classmethod def register(cls, plugin: PluginBase, name: str = None) -> bool: plugin_name = name or plugin.plugin_name if plugin_name in cls._plugins: return False cls._plugins[plugin_name] = plugin cls._emit_event("plugin_registered", plugin) return True @classmethod def unregister(cls, name: str) -> bool: if name in cls._plugins: plugin = cls._plugins[name] del cls._plugins[name] cls._emit_event("plugin_unregistered", plugin) return True return False @classmethod def get(cls, name: str) -> Optional[PluginBase]: return cls._plugins.get(name) @classmethod def get_all(cls) -> List[PluginBase]: return list(cls._plugins.values()) @classmethod def get_by_type(cls, plugin_type: PluginType) -> List[PluginBase]: return [p for p in cls._plugins.values() if p.plugin_type == plugin_type] @classmethod def get_scheduled_tasks(cls) -> List[ScheduledTaskPlugin]: result = [] for p in cls._plugins.values(): if p.plugin_type == PluginType.SCHEDULED_TASK and p.enabled: result.append(p) elif p.plugin_type == PluginType.HTTP_SCHEDULED_SENDER and p.enabled: result.append(p) return result @classmethod def enable(cls, name: str) -> bool: plugin = cls.get(name) if plugin: plugin.enable() cls._emit_event("plugin_enabled", plugin) return True return False @classmethod def disable(cls, name: str) -> bool: plugin = cls.get(name) if plugin: plugin.disable() cls._emit_event("plugin_disabled", plugin) return True return False @classmethod def on_register(cls, callback: Callable): cls._callbacks.append(callback) @classmethod def on_event(cls, event: str, handler: Callable): if event not in cls._event_handlers: cls._event_handlers[event] = [] cls._event_handlers[event].append(handler) @classmethod def _emit_event(cls, event: str, *args, **kwargs): for handler in cls._event_handlers.get(event, []): try: handler(*args, **kwargs) except Exception: pass @classmethod def get_plugin_types(cls) -> Dict[str, str]: return { pt.value: pt.name.replace("_", " ").title() for pt in PluginType }