Files
wxauto_api/plugins/base.py
T
2026-04-07 14:11:45 +08:00

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
}