281 lines
7.6 KiB
Python
281 lines
7.6 KiB
Python
# ///
|
|
# 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
|
|
}
|