mirror of
https://github.com/hansjone/oclaw.git
synced 2026-10-11 00:43:18 +08:00
重构仓库目录为统一的 runtime 分层并清理历史 openclaw 残留。
本次迁移将网关/通道/工具/技能/脚本与协议资源集中到新结构,统一路径常量与脚本转发机制,减少顶层噪音并保证运行与测试行为一致。 Made-with: Cursor
This commit is contained in:
parent
ba3836f00f
commit
4a23b715a2
498 changed files with 2760 additions and 2200 deletions
127
interfaces/channels/wecom/normalize.py
Normal file
127
interfaces/channels/wecom/normalize.py
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
|
||||
def _first_non_empty(*values: Any) -> str:
|
||||
for v in values:
|
||||
s = str(v or "").strip()
|
||||
if s:
|
||||
return s
|
||||
return ""
|
||||
|
||||
|
||||
def _pick(d: dict[str, Any], *keys: str) -> Any:
|
||||
for k in keys:
|
||||
if k in d:
|
||||
return d.get(k)
|
||||
return None
|
||||
|
||||
|
||||
def _extract_text(raw: dict[str, Any]) -> str:
|
||||
direct_text = _pick(raw, "text")
|
||||
if isinstance(direct_text, str) and direct_text.strip():
|
||||
return direct_text.strip()
|
||||
direct = _first_non_empty(
|
||||
_pick(raw, "content", "Content"),
|
||||
)
|
||||
if direct:
|
||||
return direct
|
||||
text_obj = raw.get("text")
|
||||
if isinstance(text_obj, dict):
|
||||
return _first_non_empty(_pick(text_obj, "content", "Content"))
|
||||
text_raw_obj = raw.get("text_raw")
|
||||
if isinstance(text_raw_obj, dict):
|
||||
return _first_non_empty(_pick(text_raw_obj, "content", "Content", "text"))
|
||||
content_obj = raw.get("content")
|
||||
if isinstance(content_obj, dict):
|
||||
return _first_non_empty(_pick(content_obj, "text", "content", "Content"))
|
||||
if isinstance(raw.get("msg"), dict):
|
||||
return _first_non_empty(_pick(raw.get("msg", {}), "text", "content", "Content"))
|
||||
msg_obj = raw.get("message")
|
||||
if isinstance(msg_obj, dict):
|
||||
return _first_non_empty(
|
||||
_pick(msg_obj, "text", "content", "Content"),
|
||||
_pick(msg_obj.get("text", {}), "content") if isinstance(msg_obj.get("text"), dict) else None,
|
||||
)
|
||||
payload = raw.get("payload")
|
||||
if isinstance(payload, dict):
|
||||
return _first_non_empty(
|
||||
_pick(payload, "text", "content", "Content"),
|
||||
_pick(payload.get("text", {}), "content") if isinstance(payload.get("text"), dict) else None,
|
||||
)
|
||||
return ""
|
||||
|
||||
|
||||
def normalize_wecom_event(raw: dict[str, Any]) -> dict[str, Any]:
|
||||
"""Convert WeCom-like raw events into normalized gateway payload."""
|
||||
chat_id = _first_non_empty(
|
||||
_pick(raw, "chat_id", "conversation_id", "conversationId", "chatid", "roomid", "RoomId"),
|
||||
_pick(raw.get("message", {}), "chat_id", "conversation_id", "chatid")
|
||||
if isinstance(raw.get("message"), dict)
|
||||
else None,
|
||||
_pick(raw.get("chat", {}), "id", "chat_id", "chatid", "roomid")
|
||||
if isinstance(raw.get("chat"), dict)
|
||||
else None,
|
||||
_pick(raw.get("conversation", {}), "id", "chat_id", "chatid")
|
||||
if isinstance(raw.get("conversation"), dict)
|
||||
else None,
|
||||
_pick(raw.get("room", {}), "id", "roomid", "chatid")
|
||||
if isinstance(raw.get("room"), dict)
|
||||
else None,
|
||||
)
|
||||
from_obj = raw.get("from")
|
||||
user_id = _first_non_empty(
|
||||
_pick(raw, "user_id", "from_user_id", "fromUserId", "FromUserName", "userid", "external_userid"),
|
||||
from_obj if isinstance(from_obj, str) else None,
|
||||
_pick(from_obj, "userid", "user_id", "id", "from_user_id", "UserId", "userid64", "uid")
|
||||
if isinstance(from_obj, dict)
|
||||
else None,
|
||||
_pick(raw.get("sender", {}), "userid", "user_id", "id") if isinstance(raw.get("sender"), dict) else None,
|
||||
_pick(raw.get("message", {}), "from_user_id", "fromUserId") if isinstance(raw.get("message"), dict) else None,
|
||||
chat_id,
|
||||
)
|
||||
if not chat_id:
|
||||
chat_id = user_id
|
||||
text = _extract_text(raw)
|
||||
msgid = _first_non_empty(_pick(raw, "msgid", "msg_id", "id"), _pick(raw.get("message", {}), "msgid", "id"))
|
||||
agentid = _first_non_empty(_pick(raw, "agentid", "agent_id"), _pick(raw.get("message", {}), "agentid"))
|
||||
|
||||
chat_type = _first_non_empty(_pick(raw, "chat_type", "conversation_type"))
|
||||
is_group = bool(raw.get("is_group")) or chat_type in ("group", "room")
|
||||
if not is_group and chat_id:
|
||||
is_group = chat_id.endswith("@chatroom")
|
||||
|
||||
# If group but room id was missing, we previously fell back chat_id=user_id — same key as private 1:1.
|
||||
if is_group and chat_id == user_id:
|
||||
chat_id = f"group:unknown:{user_id}"
|
||||
|
||||
return {
|
||||
"channel": "wecom",
|
||||
"user_id": user_id,
|
||||
"chat_id": chat_id or user_id,
|
||||
"text": text,
|
||||
"is_group": is_group,
|
||||
"metadata": {
|
||||
"agentid": agentid,
|
||||
"msgid": msgid,
|
||||
"source": "wecom_longconn",
|
||||
"raw": raw,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def normalize_wecom_event_batch(obj: Any) -> list[dict[str, Any]]:
|
||||
"""Extract event list from common envelopes returned by pull APIs."""
|
||||
if isinstance(obj, list):
|
||||
return [x for x in obj if isinstance(x, dict)]
|
||||
if not isinstance(obj, dict):
|
||||
return []
|
||||
for key in ("events", "messages", "items", "data"):
|
||||
arr = obj.get(key)
|
||||
if isinstance(arr, list):
|
||||
return [x for x in arr if isinstance(x, dict)]
|
||||
return [obj]
|
||||
|
||||
|
||||
__all__ = ["normalize_wecom_event", "normalize_wecom_event_batch"]
|
||||
Loading…
Add table
Add a link
Reference in a new issue