ForcePilot/backend/package/yuxi/channel/extensions/confluence/attachments.py
Kris f943c31ce4 feat(confluence): 新增Confluence渠道插件完整实现
新增了完整的Confluence集成插件,包含以下核心功能:
1.  基础认证与配置管理,支持API Token和OAuth2两种认证方式
2.  评论去重、权限控制与提及解析
3.  知识库检索与页面内容处理
4.  评论收发、编辑删除与流式回复支持
5.  附件与页面标签管理
6.  内容属性存储与AI元数据管理
7.  Webhook事件接收与处理
8.  完整的插件配置与状态检查
2026-05-21 10:43:42 +08:00

107 lines
3.7 KiB
Python

import logging
from pathlib import Path
from yuxi.channel.extensions.confluence.gateway import ConfluenceGateway
from yuxi.channel.extensions.confluence.types import OutboundResult
logger = logging.getLogger(__name__)
class ConfluenceAttachmentManager:
def __init__(self, gateway: ConfluenceGateway):
self._gateway = gateway
async def list_attachments(
self, page_id: str, account_id: str = "default",
) -> list[dict]:
client = self._gateway.get_client(account_id)
if client is None:
return []
try:
result = await client._request(
"GET", f"/wiki/api/v2/pages/{page_id}/attachments",
)
return result.get("results", [])
except Exception as e:
logger.error("Failed to list attachments for page %s: %s", page_id, e)
return []
async def get_attachment(
self, attachment_id: str, account_id: str = "default",
) -> dict | None:
client = self._gateway.get_client(account_id)
if client is None:
return None
try:
return await client._request(
"GET", f"/wiki/api/v2/attachments/{attachment_id}",
)
except Exception as e:
logger.error("Failed to get attachment %s: %s", attachment_id, e)
return None
async def upload_attachment(
self,
page_id: str,
file_path: str,
filename: str | None = None,
account_id: str = "default",
) -> OutboundResult:
client = self._gateway.get_client(account_id)
if client is None:
return OutboundResult(success=False, error="Client not found")
path = Path(file_path)
if not path.exists():
return OutboundResult(success=False, error=f"File not found: {file_path}")
fname = filename or path.name
try:
result = await client._request(
"POST",
f"/wiki/api/v2/pages/{page_id}/attachments",
files={"file": (fname, path.read_bytes(), "application/octet-stream")},
)
return OutboundResult(success=True, comment_id=result.get("id", ""))
except Exception as e:
logger.error("Failed to upload attachment to page %s: %s", page_id, e)
return OutboundResult(success=False, error=str(e))
async def download_attachment(
self,
page_id: str,
filename: str,
save_path: str,
account_id: str = "default",
) -> str | None:
client = self._gateway.get_client(account_id)
if client is None:
return None
try:
url = f"/download/attachments/{page_id}/{filename}"
if client._http is None:
raise RuntimeError("Client not initialized")
resp = await client._http.get(url)
resp.raise_for_status()
Path(save_path).write_bytes(resp.content)
return save_path
except Exception as e:
logger.error("Failed to download attachment %s: %s", filename, e)
return None
async def delete_attachment(
self, attachment_id: str, account_id: str = "default",
) -> OutboundResult:
client = self._gateway.get_client(account_id)
if client is None:
return OutboundResult(success=False, error="Client not found")
try:
await client._request(
"DELETE", f"/wiki/api/v2/attachments/{attachment_id}",
)
return OutboundResult(success=True, comment_id=attachment_id)
except Exception as e:
logger.error("Failed to delete attachment %s: %s", attachment_id, e)
return OutboundResult(success=False, error=str(e))