d58e38d58f
PR #14 从旧分支复制文件导致回退了 PR #10 的 lint 修复。 修复内容: - autoflake 移除未使用导入/变量 - autopep8 修复缩进/空格 - 手动修复 F821(pathlib→Path), F541(f-string), F841(未使用变量) - 所有修复均通过 flake8 --max-line-length=120 --extend-ignore=E501 检查 (0 errors)
124 lines
4.4 KiB
Python
124 lines
4.4 KiB
Python
"""Mail 失败通知 — 以 system 身份通知发件人"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import json
|
||
import logging
|
||
from datetime import datetime
|
||
from pathlib import Path
|
||
from typing import Optional
|
||
|
||
from src.blackboard.models import Task
|
||
from src.blackboard.operations import Blackboard
|
||
from src.config.agents import AGENT_IDS
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
|
||
# 邮件通知正文模板(统一模板,包含所有可能的失败原因和建议)
|
||
_NOTIFY_TEMPLATE = """你的邮件投递失败了。
|
||
|
||
📧 原始邮件:「{title}」
|
||
👤 收件人:{to_agent}
|
||
❌ 失败原因:{reason}
|
||
|
||
常见失败原因及处理建议:
|
||
• no_reply_found:收件人未回复。建议重发邮件,或通过黑板任务方式联系
|
||
• auth_failed:收件人认证失败。需检查 Agent 配置,联系姜维(jiangwei-infra)排查
|
||
• crash_limit:收件人处理时多次崩溃。系统异常,建议稍后重试
|
||
• task_timeout:处理超时。建议重发或通过其他方式联系
|
||
• 其他原因:建议联系副军师(pangtong-fujunshi)排查
|
||
|
||
——系统自动通知"""
|
||
|
||
|
||
def _is_mail_project(db_path: Path) -> bool:
|
||
"""从 db_path 推断是否为 _mail 项目"""
|
||
path_str = str(db_path)
|
||
return "/_mail/" in path_str or path_str.endswith("_mail.db")
|
||
|
||
|
||
def notify_mail_failed(db_path: Path, original_mail_id: str,
|
||
reason: str, detail: Optional[dict] = None) -> None:
|
||
"""Mail 失败后以 system 身份给发件人发通知邮件
|
||
|
||
直接通过 Blackboard 创建 Task,不走 HTTP API。
|
||
防递归:检查原邮件 must_hives.system_notify,为 true 则跳过。
|
||
发件人不是有效 Agent(如 system)→ 通知庞统代处理,避免广播风暴。
|
||
"""
|
||
try:
|
||
bb = Blackboard(db_path)
|
||
original = bb.get_task(original_mail_id)
|
||
if not original:
|
||
logger.warning(
|
||
"notify_mail_failed: original mail %s not found",
|
||
original_mail_id)
|
||
return
|
||
|
||
# 解析原邮件元数据
|
||
meta = json.loads(original.must_haves) if original.must_haves else {}
|
||
|
||
# 防递归:系统通知邮件失败不再发通知
|
||
if meta.get("system_notify"):
|
||
logger.info(
|
||
"Mail %s: system notify mail failed, skipping recursive notification",
|
||
original_mail_id)
|
||
return
|
||
|
||
# 获取发件人(优先 assigned_by,fallback must_haves.from)
|
||
from_agent = original.assigned_by or meta.get("from", "")
|
||
to_agent = original.assignee or ""
|
||
title = original.title or ""
|
||
|
||
if not from_agent:
|
||
logger.warning(
|
||
"notify_mail_failed: cannot determine sender for mail %s",
|
||
original_mail_id)
|
||
return
|
||
|
||
# 发件人不是有效 Agent(如 system)→ 通知庞统代处理,不触发广播
|
||
target_agent = from_agent
|
||
if from_agent not in AGENT_IDS:
|
||
logger.warning("Mail %s: sender '%s' is not a valid agent, routing failure notice to pangtong-fujunshi",
|
||
original_mail_id, from_agent)
|
||
target_agent = "pangtong-fujunshi"
|
||
|
||
# 构造通知正文
|
||
text = _NOTIFY_TEMPLATE.format(
|
||
title=title,
|
||
to_agent=to_agent,
|
||
reason=reason,
|
||
)
|
||
|
||
# 创建通知邮件 Task
|
||
notify_id = f"mail-{int(datetime.now().timestamp() * 1000)}"
|
||
notify_meta = {
|
||
"type": "inform",
|
||
"performative": "inform",
|
||
"is_read": False,
|
||
"conversation_id": meta.get("conversation_id", ""),
|
||
"in_reply_to": original_mail_id,
|
||
"from": "system",
|
||
"system_notify": True,
|
||
}
|
||
|
||
notify_task = Task(
|
||
id=notify_id,
|
||
title=f"[投递失败] {title}",
|
||
description=text,
|
||
assignee=target_agent,
|
||
assigned_by="system",
|
||
must_haves=json.dumps(notify_meta, ensure_ascii=False),
|
||
task_type="mail",
|
||
status="pending",
|
||
)
|
||
bb.create_task(notify_task)
|
||
logger.info("Mail %s: sent failure notification to %s (original_sender=%s, reason=%s, notify_id=%s)",
|
||
original_mail_id, target_agent, from_agent, reason, notify_id)
|
||
|
||
except Exception as e:
|
||
logger.warning(
|
||
"notify_mail_failed: failed to send notification for mail %s: %s",
|
||
original_mail_id,
|
||
e)
|