|
|
@@ -876,3 +876,87 @@ async def check_approval_status(
|
|
|
except Exception as e:
|
|
|
logger.error("check_approval_status 失败: %s", e, exc_info=True)
|
|
|
return ToolResult(title="check_approval_status 失败", output=str(e))
|
|
|
+
|
|
|
+
|
|
|
+# ═══════════════════════════════════════════
|
|
|
+# 飞书文字消息(用于执行后向运营汇报)
|
|
|
+# ═══════════════════════════════════════════
|
|
|
+
|
|
|
+@tool(description="向运营飞书发送纯文本消息(用于执行后汇报 diff、确认、质疑回应等非审批场景)")
|
|
|
+async def send_feishu_text_message(
|
|
|
+ ctx: ToolContext,
|
|
|
+ text: str,
|
|
|
+ to_operator: bool = True,
|
|
|
+ to_project_chat: bool = True,
|
|
|
+) -> ToolResult:
|
|
|
+ """
|
|
|
+ 向运营个人 IM 和/或投放项目群发送一条纯文本消息。
|
|
|
+
|
|
|
+ 使用场景(必须显式调用):
|
|
|
+ - 收到"部分批准"反馈后,发送执行 diff 表(本轮执行 X 条 / 保留 Y 条 / 不变 Z 条)
|
|
|
+ - 质疑型反馈的详细回应(对比、历史、ROI 置信度)
|
|
|
+ - 连续 2 轮未达成一致时主动提议"本轮暂停"
|
|
|
+ - 任何"告知但不需要审批"的汇报
|
|
|
+
|
|
|
+ ⚠️ 不要用于:首次审批请求(用 send_approval_request);表格链接发送(用 import_to_feishu)。
|
|
|
+
|
|
|
+ Args:
|
|
|
+ text: 消息正文(支持换行,建议 < 2 KB 单屏可读)。
|
|
|
+ to_operator: 发送到运营个人 IM(FEISHU_OPERATOR_OPEN_ID)。默认 True。
|
|
|
+ to_project_chat: 抄送到投放项目群(FEISHU_AD_PROJECT_CHAT_ID)。默认 True;未配置时自动跳过。
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ ToolResult,包含发送目标清单和状态。
|
|
|
+ """
|
|
|
+ if not IM_ENABLED:
|
|
|
+ return ToolResult(title="IM 未启用", output="IM_ENABLED=False,消息未发送")
|
|
|
+
|
|
|
+ if not text or not text.strip():
|
|
|
+ return ToolResult(title="消息为空", output="text 参数为空,拒绝发送")
|
|
|
+
|
|
|
+ sent_targets: List[str] = []
|
|
|
+ failed_targets: List[str] = []
|
|
|
+
|
|
|
+ if to_operator and FEISHU_OPERATOR_OPEN_ID:
|
|
|
+ try:
|
|
|
+ _feishu.send_message(to=FEISHU_OPERATOR_OPEN_ID, text=text)
|
|
|
+ sent_targets.append(f"个人 IM ({FEISHU_OPERATOR_OPEN_ID[:12]}...)")
|
|
|
+ logger.info("飞书文字消息发送成功(个人): len=%d", len(text))
|
|
|
+ except Exception as e:
|
|
|
+ failed_targets.append(f"个人 IM: {e}")
|
|
|
+ logger.error("飞书文字消息发送失败(个人): %s", e)
|
|
|
+
|
|
|
+ if to_project_chat and FEISHU_AD_PROJECT_CHAT_ID:
|
|
|
+ try:
|
|
|
+ _feishu.send_message(to=FEISHU_AD_PROJECT_CHAT_ID, text=text)
|
|
|
+ sent_targets.append(f"项目群 ({FEISHU_AD_PROJECT_CHAT_ID[:12]}...)")
|
|
|
+ logger.info("飞书文字消息发送成功(群聊): len=%d", len(text))
|
|
|
+ except Exception as e:
|
|
|
+ failed_targets.append(f"项目群: {e}")
|
|
|
+ logger.error("飞书文字消息发送失败(群聊): %s", e)
|
|
|
+
|
|
|
+ if not sent_targets and not failed_targets:
|
|
|
+ return ToolResult(
|
|
|
+ title="无可用发送目标",
|
|
|
+ output="to_operator/to_project_chat 均未启用或对应 ID 未配置",
|
|
|
+ )
|
|
|
+
|
|
|
+ summary_lines = []
|
|
|
+ if sent_targets:
|
|
|
+ summary_lines.append(f"✅ 已发送 {len(sent_targets)} 个目标:")
|
|
|
+ for t in sent_targets:
|
|
|
+ summary_lines.append(f" - {t}")
|
|
|
+ if failed_targets:
|
|
|
+ summary_lines.append(f"❌ 失败 {len(failed_targets)} 个目标:")
|
|
|
+ for t in failed_targets:
|
|
|
+ summary_lines.append(f" - {t}")
|
|
|
+
|
|
|
+ return ToolResult(
|
|
|
+ title=f"飞书文字消息已发送({len(sent_targets)} 个目标)",
|
|
|
+ output="\n".join(summary_lines),
|
|
|
+ metadata={
|
|
|
+ "sent_count": len(sent_targets),
|
|
|
+ "failed_count": len(failed_targets),
|
|
|
+ "text_length": len(text),
|
|
|
+ },
|
|
|
+ )
|