message / plugins /cache /main.py
hunian
refactor(plugins): 插件短名并统一 MCP tool 为 {plugin}-{tool}
cc826a1
Raw
History Blame Contribute Delete
6.16 kB
import os
import asyncio
import logging
import time
import aiofiles
from datetime import datetime
from pathlib import Path
from typing import Dict, List, Optional
logger = logging.getLogger(__name__)
class FileCachePlugin:
"""文件缓存插件"""
def __init__(self):
self.name = "cache"
self.enabled = False
self.cache_dir = None
self.temp_dir = None
self.permanent_dir = None
self.file_metadata = {} # 存储文件元数据:{file_id: {"name": str, "type": str, "created": timestamp, "is_temp": bool}}
def on_enable(self):
"""插件启用时调用"""
self.enabled = True
self._init_directories()
self._load_metadata()
logger.info(f"{self.name} 已启用")
def on_disable(self):
"""插件禁用时调用"""
self.enabled = False
try:
from . import api
api.stop_cleanup_task()
except Exception as e:
logger.error(f"停止清理任务失败: {e}")
logger.info(f"{self.name} 已禁用")
def _init_directories(self):
"""初始化缓存目录"""
plugin_dir = Path(__file__).parent / "cache"
plugin_dir.mkdir(exist_ok=True)
self.cache_dir = plugin_dir
self.temp_dir = plugin_dir / "temp"
self.permanent_dir = plugin_dir / "permanent"
self.temp_dir.mkdir(exist_ok=True)
self.permanent_dir.mkdir(exist_ok=True)
def _load_metadata(self):
"""从缓存目录加载文件元数据"""
self.file_metadata = {}
# 加载临时文件
if self.temp_dir.exists():
for file_path in self.temp_dir.iterdir():
if file_path.is_file():
file_id = file_path.stem
stat = file_path.stat()
self.file_metadata[file_id] = {
"name": file_path.name,
"type": "temp",
"created": stat.st_mtime,
"path": str(file_path),
}
# 加载永久文件
if self.permanent_dir.exists():
for file_path in self.permanent_dir.iterdir():
if file_path.is_file():
file_id = file_path.stem
stat = file_path.stat()
self.file_metadata[file_id] = {
"name": file_path.name,
"type": "permanent",
"created": stat.st_mtime,
"path": str(file_path),
}
async def cleanup_expired_files(self):
"""清理超过8小时的临时文件"""
current_time = time.time()
expired_files = []
for file_id, metadata in list(self.file_metadata.items()):
if metadata["type"] == "temp":
file_age = current_time - metadata["created"]
# 8小时 = 28800秒
if file_age > 28800:
expired_files.append(file_id)
for file_id in expired_files:
await self.delete_file(file_id)
async def save_file(
self,
file_id: str,
file_content: bytes,
original_filename: str,
is_temp: bool = True,
) -> bool:
"""保存文件到缓存目录"""
try:
target_dir = self.temp_dir if is_temp else self.permanent_dir
file_path = target_dir / original_filename
# 如果文件已存在,添加时间戳以避免重名
if file_path.exists():
name, suffix = (
original_filename.rsplit(".", 1)
if "." in original_filename
else (original_filename, "")
)
timestamp = int(time.time() * 1000)
original_filename = (
f"{name}_{timestamp}.{suffix}" if suffix else f"{name}_{timestamp}"
)
file_path = target_dir / original_filename
# 保存文件
async with aiofiles.open(file_path, "wb") as f:
await f.write(file_content)
# 记录元数据
self.file_metadata[file_id] = {
"name": original_filename,
"type": "temp" if is_temp else "permanent",
"created": time.time(),
"path": str(file_path),
}
return True
except Exception as e:
logger.error(f"保存文件失败: {e}")
return False
async def delete_file(self, file_id: str) -> bool:
"""删除文件"""
try:
if file_id not in self.file_metadata:
return False
file_path = Path(self.file_metadata[file_id]["path"])
if file_path.exists():
file_path.unlink()
del self.file_metadata[file_id]
return True
except Exception as e:
logger.error(f"删除文件失败: {e}")
return False
def get_file_list(self) -> List[Dict]:
"""获取文件列表"""
file_list = []
for file_id, metadata in self.file_metadata.items():
file_list.append(
{
"file_id": file_id,
"name": metadata["name"],
"type": metadata["type"],
"created": datetime.fromtimestamp(metadata["created"]).isoformat(),
"url": f"/plugins/cache/api/file/{file_id}",
}
)
return file_list
def get_file_path(self, file_id: str) -> Optional[str]:
"""获取文件路径"""
if file_id in self.file_metadata:
return self.file_metadata[file_id]["path"]
return None
def get_status(self) -> Dict:
"""获取插件状态"""
return {
"name": self.name,
"enabled": self.enabled,
"file_count": len(self.file_metadata),
"cache_dir": str(self.cache_dir),
}
# 创建全局实例
plugin = FileCachePlugin()