Files
aiagent/backend/app/services/lingxi_app_service.py
renjianbo 1c83b6284f feat: add 灵犀 Feishu bot + fix agent schedule system + default all tools
- Add 灵犀学习助手 Feishu bot (lingxi_app_service + lingxi_ws_handler)
- Fix agent_schedule_service missing AgentSchedule import (Celery Beat)
- Fix scene_templates default enable_tools=False → True
- Fix workflow_engine LLM node: empty tools list now = all tools (consistent with agent node)
- Add 创建agent.md guide document

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-03 00:20:29 +08:00

106 lines
3.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""灵犀飞书应用 API 服务 — 通过灵犀应用发送消息到用户"""
from __future__ import annotations
import json
import logging
import time
from typing import Optional
import httpx
from app.core.config import settings
logger = logging.getLogger(__name__)
_token_cache: dict = {"token": None, "expires_at": 0}
def _get_tenant_access_token() -> Optional[str]:
now = time.time()
if _token_cache["token"] and now < _token_cache["expires_at"] - 300:
return _token_cache["token"]
app_id = settings.LINGXI_APP_ID
app_secret = settings.LINGXI_APP_SECRET
if not app_id or not app_secret:
logger.warning("灵犀应用未配置LINGXI_APP_ID / LINGXI_APP_SECRET")
return None
try:
with httpx.Client(timeout=10) as client:
resp = client.post(
"https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal",
json={"app_id": app_id, "app_secret": app_secret},
)
result = resp.json()
if resp.is_success and result.get("code") == 0:
token = result["tenant_access_token"]
expire = result.get("expire", 7200)
_token_cache["token"] = token
_token_cache["expires_at"] = now + expire
logger.info("灵犀 tenant_access_token 获取成功")
return token
else:
logger.warning("灵犀 token 获取失败: %s", result)
return None
except Exception as e:
logger.warning("灵犀 token 获取异常: %s", e)
return None
def send_message_to_user(
open_id: str, title: str, content: str,
status: str = "info", detail_link: Optional[str] = None,
) -> bool:
token = _get_tenant_access_token()
if not token:
return False
color_map = {"success": "green", "failed": "red", "info": "blue"}
color = color_map.get(status, "blue")
elements = [{"tag": "markdown", "content": content}]
if detail_link:
elements.append({
"tag": "action",
"actions": [{"tag": "button", "text": {"tag": "plain_text", "content": "查看详情"}, "url": detail_link, "type": "default"}],
})
card = {
"config": {"wide_screen_mode": True},
"header": {"title": {"tag": "plain_text", "content": title}, "template": color},
"elements": elements,
}
try:
with httpx.Client(timeout=10) as client:
resp = client.post(
"https://open.feishu.cn/open-apis/im/v1/messages?receive_id_type=open_id",
headers={"Authorization": f"Bearer {token}"},
json={"receive_id": open_id, "msg_type": "interactive", "content": json.dumps(card, ensure_ascii=False)},
)
result = resp.json()
if resp.is_success and result.get("code") == 0:
logger.info("灵犀消息发送成功: open_id=%s title=%s", open_id[:20], title)
return True
else:
logger.warning("灵犀消息发送失败: code=%s msg=%s", result.get("code"), result.get("msg"))
return False
except Exception as e:
logger.warning("灵犀消息发送异常: %s", e)
return False
def send_plain_text(open_id: str, text: str) -> bool:
token = _get_tenant_access_token()
if not token:
return False
try:
with httpx.Client(timeout=10) as client:
resp = client.post(
"https://open.feishu.cn/open-apis/im/v1/messages?receive_id_type=open_id",
headers={"Authorization": f"Bearer {token}"},
json={"receive_id": open_id, "msg_type": "text", "content": json.dumps({"text": text}, ensure_ascii=False)},
)
result = resp.json()
return resp.is_success and result.get("code") == 0
except Exception as e:
logger.warning("灵犀文本消息发送异常: %s", e)
return False