落地空运群内协同与锁舱出站。
群里按 BOT 通道激活当前工单、航线报价和成交跟进;没有 TMS 报价编号时用工单字段补齐仍调锁舱,避免被本地规则拦住。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -41,6 +41,47 @@ def _create_time(msgtime: Any) -> int:
|
||||
return n
|
||||
|
||||
|
||||
def _unwrap_text(raw: Any) -> str:
|
||||
"""
|
||||
从字符串或嵌套对象取出正文。
|
||||
|
||||
管家 content 经常是摘要;企微官方结构是 text.content。空、null 不当正文。
|
||||
"""
|
||||
if raw is None:
|
||||
return ""
|
||||
if isinstance(raw, str):
|
||||
text = raw.strip()
|
||||
if not text or text.lower() in {"null", "none"}:
|
||||
return ""
|
||||
if text.startswith("{") or text.startswith("["):
|
||||
try:
|
||||
return _unwrap_text(json.loads(text))
|
||||
except json.JSONDecodeError:
|
||||
return text
|
||||
return text
|
||||
if isinstance(raw, Mapping):
|
||||
for key in ("content", "text", "msg"):
|
||||
got = _unwrap_text(raw.get(key))
|
||||
if got:
|
||||
return got
|
||||
return ""
|
||||
return str(raw).strip()
|
||||
|
||||
|
||||
def _best_text(record: Mapping[str, Any], payload: Mapping[str, Any]) -> str:
|
||||
"""多处正文里取最长的一份,避免摘要盖掉带金额的原文。"""
|
||||
candidates = [
|
||||
_unwrap_text(record.get("content")),
|
||||
_unwrap_text(payload.get("content")),
|
||||
_unwrap_text(payload.get("text")),
|
||||
_unwrap_text(payload.get("msg")),
|
||||
]
|
||||
texts = [x for x in candidates if x]
|
||||
if not texts:
|
||||
return ""
|
||||
return max(texts, key=len)
|
||||
|
||||
|
||||
def _filename(msg_type: str, content: str, payload: Mapping[str, Any]) -> str:
|
||||
for key in ("filename", "file_name", "FileName", "title", "name"):
|
||||
val = str(payload.get(key) or "").strip()
|
||||
@@ -75,7 +116,7 @@ def archive_record_to_inbound(
|
||||
return None
|
||||
|
||||
payload = _as_mapping(record.get("msg_content") or record.get("msgcontent"))
|
||||
content = str(record.get("content") or payload.get("content") or "").strip()
|
||||
content = _best_text(record, payload)
|
||||
msgid = str(record.get("msgid") or record.get("msg_id") or "").strip()
|
||||
seq = record.get("seq")
|
||||
if not msgid:
|
||||
|
||||
@@ -123,6 +123,21 @@ def poll_once(
|
||||
page_size = int(getattr(cfg, "wshoto_page_size", 50) or 50)
|
||||
clock = now or time.time
|
||||
rooms = list_watched_rooms(redis=redis)
|
||||
scan_ids = [
|
||||
x.strip()
|
||||
for x in str(getattr(cfg, "wshoto_scan_user_ids", "") or "").replace(";", ",").split(",")
|
||||
if x.strip()
|
||||
]
|
||||
extra: list[dict[str, Any]] = []
|
||||
if scan_ids:
|
||||
extra = _poll_by_scan_users(
|
||||
api,
|
||||
userids=scan_ids,
|
||||
redis=redis,
|
||||
page_size=page_size,
|
||||
lookback=lookback,
|
||||
clock=clock,
|
||||
)
|
||||
if rooms:
|
||||
# 应用群必须带房间号、不要带存档席 userid,否则管家返回空页。
|
||||
records, mode, last = _poll_by_rooms(
|
||||
@@ -132,6 +147,8 @@ def poll_once(
|
||||
lookback=lookback,
|
||||
clock=clock,
|
||||
)
|
||||
if extra:
|
||||
records = list(records) + extra
|
||||
else:
|
||||
seat = seats[0] if seats else ""
|
||||
last = load_last_seq(f"seat:{seat or 'default'}", redis=redis)
|
||||
@@ -160,6 +177,9 @@ def poll_once(
|
||||
lookback=lookback,
|
||||
clock=clock,
|
||||
)
|
||||
if extra:
|
||||
records = list(records) + extra
|
||||
mode = "rooms"
|
||||
# 按房间拉时各群自己的 last_seq 已经过滤过,这里不要再用席游标误丢。
|
||||
ingest_min = 0 if mode == "rooms" else last
|
||||
enqueued, skipped = ingest_records(
|
||||
@@ -194,6 +214,61 @@ def poll_once(
|
||||
}
|
||||
|
||||
|
||||
def _poll_by_scan_users(
|
||||
api: WshotoOpenClient,
|
||||
*,
|
||||
userids: Sequence[str],
|
||||
redis: Optional[RedisClient],
|
||||
page_size: int,
|
||||
lookback: int,
|
||||
clock: Callable[[], float],
|
||||
) -> list[dict[str, Any]]:
|
||||
"""
|
||||
按人扫新群。空运群是人自建的,存档席没进群时房间名单里没有它。
|
||||
|
||||
副作用:看到带 @询价小助手 的新房间会登记监听。
|
||||
"""
|
||||
from agent.channel.wshoto.rooms import watch_collab_room
|
||||
|
||||
merged: list[dict[str, Any]] = []
|
||||
now = int(clock())
|
||||
for uid in userids:
|
||||
scope = f"scan:{uid}"
|
||||
last = load_last_seq(scope, redis=redis)
|
||||
time_start = 0
|
||||
time_end = 0
|
||||
if last <= 0:
|
||||
time_end = now
|
||||
time_start = time_end - min(lookback, 8 * 3600)
|
||||
try:
|
||||
rows = api.list_group_messages(
|
||||
userid=uid,
|
||||
last_seq=last,
|
||||
page_size=page_size,
|
||||
msg_time_start=time_start,
|
||||
msg_time_end=time_end,
|
||||
)
|
||||
except WshotoError as exc:
|
||||
logger.warning("微盛按人扫群失败 userid=%s code=%s", uid, exc.code)
|
||||
continue
|
||||
max_seq = last
|
||||
for rec in rows:
|
||||
seq = _record_seq(rec)
|
||||
if seq > max_seq:
|
||||
max_seq = seq
|
||||
room = str(rec.get("room_id") or rec.get("roomid") or "").strip()
|
||||
blob = str(rec.get("content") or "")
|
||||
if room and "询价小助手" in blob:
|
||||
try:
|
||||
watch_collab_room(room, redis=redis)
|
||||
except Exception:
|
||||
logger.exception("扫到空运群后登记监听失败 chat=%s", room)
|
||||
if max_seq > last:
|
||||
save_last_seq(scope, max_seq, redis=redis)
|
||||
merged.extend(rows)
|
||||
return merged
|
||||
|
||||
|
||||
def _poll_by_rooms(
|
||||
api: WshotoOpenClient,
|
||||
*,
|
||||
|
||||
Reference in New Issue
Block a user