落地空运单聊询价字段与线路选择。
必填核对后再建单;多条线路走 H5 点选;费用只展示该线路 TMS 回包,不套用整单空运费。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -56,6 +56,9 @@ def normalize_aibot_payload(body: dict[str, Any]) -> AibotInboundBody:
|
||||
raw.setdefault("source", "aibot")
|
||||
if data.get("response_url"):
|
||||
raw.setdefault("response_url", data.get("response_url"))
|
||||
req_id = str(data.get("aibot_req_id") or raw.get("aibot_req_id") or "").strip()
|
||||
if req_id:
|
||||
raw["aibot_req_id"] = req_id
|
||||
return AibotInboundBody(
|
||||
sender_id=sender,
|
||||
message_id=mid,
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
"""
|
||||
空运群 BOT 回话。
|
||||
|
||||
本文件职责:有即时 `response_url` 就流式回;没有(发文件走存档)就打本机桥 `/send-group`,
|
||||
由长连接 `aibot_send_msg` 主动往群里说。
|
||||
禁止:走海运 appchat/send;在回调线程等 LLM。
|
||||
本文件职责:打本机桥 `/send`(骨架 `/send-group`)。
|
||||
有这次 @ 的 `aibot_req_id` 时,桥走 `aibot_respond_msg`,群里才会引用原消息;
|
||||
没有 req_id(典型:会话存档听到附件)才走 `aibot_send_msg` 主动发言。
|
||||
禁止:走海运 appchat/send;用企微 stream 回复(会带「内容由AI生成」和赞踩);在回调线程等 LLM。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import uuid
|
||||
from typing import Any
|
||||
|
||||
import httpx
|
||||
@@ -23,13 +23,14 @@ class AibotReplyClient:
|
||||
"""
|
||||
空运群出站客户端。
|
||||
|
||||
@ 带了 response_url:流式只收口这次 @;正文走桥主动发言,和发模板同一条通道。
|
||||
存档听到附件时没有 url:直接走桥。该群须先被 @ 过,企微才允许主动说。
|
||||
空运群出站只走桥。带上这次 @ 的 req_id,桥才能引用原消息。
|
||||
禁止 POST 企微 stream(群里会显示「内容由AI生成」和赞踩)。
|
||||
"""
|
||||
|
||||
def __init__(self, response_url: str = "", timeout_sec: float = 8.0) -> None:
|
||||
def __init__(self, response_url: str = "", timeout_sec: float = 8.0, callback_req_id: str = "") -> None:
|
||||
self._url = (response_url or "").strip()
|
||||
self._timeout = timeout_sec
|
||||
self._callback_req_id = str(callback_req_id or "").strip()
|
||||
self.sent: list[dict[str, Any]] = []
|
||||
|
||||
def send_group(
|
||||
@@ -45,41 +46,15 @@ class AibotReplyClient:
|
||||
if not text:
|
||||
return {"ok": False, "error": "empty"}
|
||||
room = str(chat_id or "").strip()
|
||||
# @ 当场流式回话群里经常看不见长摘要,模板却走主动发言,就会只剩一张表。
|
||||
# 工单信息必须再走桥,和模板同一条通道;流式只收口这次 @。
|
||||
if self._url:
|
||||
self._send_stream(room, text.split("\n", 1)[0][:80])
|
||||
# 企微 stream 会自带「内容由AI生成」和赞踩,空运群业务话术不要这条通道。
|
||||
# 工单正文只走桥主动发言(markdown),和发模板同一条通道。
|
||||
if room:
|
||||
out = self._send_proactive(room, text)
|
||||
if out.get("ok") or not self._url:
|
||||
return out
|
||||
if self._url:
|
||||
return self._send_stream(room, text)
|
||||
return self._send_proactive(room, text)
|
||||
return {"ok": False, "error": "no_chat_id"}
|
||||
|
||||
def _send_stream(self, chat_id: str, text: str) -> dict[str, Any]:
|
||||
"""@ 当场回话:POST 企微给的 response_url。"""
|
||||
body = {
|
||||
"msgtype": "stream",
|
||||
"stream": {
|
||||
"id": uuid.uuid4().hex,
|
||||
"finish": True,
|
||||
"content": text,
|
||||
},
|
||||
}
|
||||
try:
|
||||
resp = httpx.post(self._url, json=body, timeout=self._timeout)
|
||||
ok = 200 <= resp.status_code < 300
|
||||
if not ok:
|
||||
logger.warning("aibot 回话 HTTP=%s chat=%s", resp.status_code, chat_id)
|
||||
return {"ok": ok, "status": resp.status_code}
|
||||
except Exception:
|
||||
logger.exception("aibot 回话网络失败 chat=%s", chat_id)
|
||||
return {"ok": False, "error": "network"}
|
||||
|
||||
def _send_proactive(self, chat_id: str, text: str) -> dict[str, Any]:
|
||||
"""
|
||||
没有 response_url(典型:会话存档听到附件)时,让桥主动往群里说。
|
||||
把正文交给桥。有 callback_req_id 时桥引用这次 @;没有则主动发言。
|
||||
|
||||
副作用:回环 HTTP 打 8913,不直连企微。桥未连上或该群从未 @ 过,企微不会投递。
|
||||
"""
|
||||
@@ -100,14 +75,24 @@ class AibotReplyClient:
|
||||
# 现网官方桥是 POST /send;本仓库骨架桥是 /send-group。两个都试,先打官方口径。
|
||||
resp = httpx.post(
|
||||
f"{base}/send",
|
||||
json={"chatid": chat_id, "operation": "text", "content": text},
|
||||
json={
|
||||
"chatid": chat_id,
|
||||
"operation": "text",
|
||||
"content": text,
|
||||
"aibot_req_id": self._callback_req_id,
|
||||
},
|
||||
headers=headers,
|
||||
timeout=self._timeout,
|
||||
)
|
||||
if resp.status_code == 404:
|
||||
resp = httpx.post(
|
||||
f"{base}/send-group",
|
||||
json={"chat_id": chat_id, "content": text, "chat_type": 2},
|
||||
json={
|
||||
"chat_id": chat_id,
|
||||
"content": text,
|
||||
"chat_type": 2,
|
||||
"aibot_req_id": self._callback_req_id,
|
||||
},
|
||||
headers=headers,
|
||||
timeout=self._timeout,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user