新增了完整的Confluence集成插件,包含以下核心功能: 1. 基础认证与配置管理,支持API Token和OAuth2两种认证方式 2. 评论去重、权限控制与提及解析 3. 知识库检索与页面内容处理 4. 评论收发、编辑删除与流式回复支持 5. 附件与页面标签管理 6. 内容属性存储与AI元数据管理 7. Webhook事件接收与处理 8. 完整的插件配置与状态检查
120 lines
4.1 KiB
Python
120 lines
4.1 KiB
Python
import asyncio
|
|
import logging
|
|
|
|
from yuxi.channel.extensions.confluence.format import ADFBuilder
|
|
from yuxi.channel.extensions.confluence.gateway import ConfluenceGateway
|
|
from yuxi.channel.extensions.confluence.types import OutboundResult
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
MAX_VERSION_RETRY = 3
|
|
RETRY_BASE_DELAY = 1.0
|
|
|
|
|
|
class ConfluencePageWriter:
|
|
def __init__(self, gateway: ConfluenceGateway):
|
|
self._gateway = gateway
|
|
|
|
async def create_page(
|
|
self,
|
|
space_id: str,
|
|
title: str,
|
|
content: str,
|
|
parent_page_id: 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")
|
|
|
|
adf_body = ADFBuilder.from_markdown(content)
|
|
payload = {
|
|
"spaceId": space_id,
|
|
"status": "current",
|
|
"title": title,
|
|
"body": {
|
|
"representation": "atlas_doc_format",
|
|
"value": adf_body,
|
|
},
|
|
}
|
|
if parent_page_id:
|
|
payload["parentId"] = parent_page_id
|
|
|
|
try:
|
|
result = await client._request("POST", "/wiki/api/v2/pages", json=payload)
|
|
return OutboundResult(success=True, comment_id=result.get("id", ""))
|
|
except Exception as e:
|
|
return OutboundResult(success=False, error=str(e))
|
|
|
|
async def update_page_with_conflict_retry(
|
|
self,
|
|
page_id: str,
|
|
title: str,
|
|
content: str,
|
|
space_id: str,
|
|
version_comment: str = "AI 自动更新",
|
|
account_id: str = "default",
|
|
) -> OutboundResult:
|
|
client = self._gateway.get_client(account_id)
|
|
if client is None:
|
|
return OutboundResult(success=False, error="Client not found")
|
|
|
|
for attempt in range(MAX_VERSION_RETRY):
|
|
try:
|
|
page = await client.get_page(page_id)
|
|
except Exception as e:
|
|
return OutboundResult(success=False, error=f"Read page failed: {e}")
|
|
|
|
current_version = page.get("version", {}).get("number", 1)
|
|
|
|
adf_body = ADFBuilder.from_markdown(content)
|
|
payload = {
|
|
"id": page_id,
|
|
"status": "current",
|
|
"title": title,
|
|
"spaceId": space_id,
|
|
"body": {
|
|
"representation": "atlas_doc_format",
|
|
"value": adf_body,
|
|
},
|
|
"version": {
|
|
"number": current_version + 1,
|
|
"message": version_comment,
|
|
},
|
|
}
|
|
|
|
try:
|
|
result = await client._request(
|
|
"PUT", f"/wiki/api/v2/pages/{page_id}", json=payload
|
|
)
|
|
return OutboundResult(success=True, comment_id=result.get("id", ""))
|
|
except Exception as e:
|
|
status = getattr(getattr(e, "response", None), "status_code", 0)
|
|
if status == 409 and attempt < MAX_VERSION_RETRY - 1:
|
|
delay = RETRY_BASE_DELAY * (2 ** attempt)
|
|
logger.info(
|
|
"Page version conflict, retry %d/%d after %.1fs",
|
|
attempt + 1, MAX_VERSION_RETRY, delay,
|
|
)
|
|
await asyncio.sleep(delay)
|
|
continue
|
|
return OutboundResult(success=False, error=str(e))
|
|
|
|
return OutboundResult(success=False, error="Max retries exceeded")
|
|
|
|
async def generate_meeting_notes(
|
|
self,
|
|
space_id: str,
|
|
title: str,
|
|
transcript: str,
|
|
account_id: str = "default",
|
|
) -> OutboundResult:
|
|
prompt = f"""请根据以下会议记录生成结构化的会议纪要:
|
|
|
|
{transcript}
|
|
|
|
格式要求:
|
|
- 标题使用会议纪要标题
|
|
- 分节:参会人员、会议摘要、讨论要点、决议事项、待办事项
|
|
- 每个待办事项标注负责人和截止时间"""
|
|
return await self.create_page(space_id, title, prompt, account_id=account_id) |