2026-05-12 00:43:14 +08:00
|
|
|
from __future__ import annotations
|
|
|
|
|
|
2026-05-13 16:06:10 +08:00
|
|
|
from enum import StrEnum
|
|
|
|
|
|
|
|
|
|
CHAT_ID_PREFIX_DM = "dingding:dm:"
|
|
|
|
|
CHAT_ID_PREFIX_GROUP = "dingding:group:"
|
|
|
|
|
CHAT_ID_PREFIX_THREAD = "dingding:thread:"
|
|
|
|
|
CHAT_ID_PREFIX_GROUP_SENDER = "dingding:group_sender:"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class SessionScope(StrEnum):
|
|
|
|
|
DIRECT = "dm"
|
|
|
|
|
GROUP = "group"
|
|
|
|
|
GROUP_SENDER = "group_sender"
|
|
|
|
|
TOPIC = "topic"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class SessionMode(StrEnum):
|
|
|
|
|
RAW = "raw"
|
|
|
|
|
CHAT_RAW = "chat_raw"
|
|
|
|
|
CHAT_RESOLVE = "chat_resolve"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def generate_dingding_chat_id(
|
|
|
|
|
raw: dict,
|
|
|
|
|
chat_type: str,
|
|
|
|
|
*,
|
|
|
|
|
sender_id: str | None = None,
|
|
|
|
|
scope: str = "group",
|
|
|
|
|
) -> str:
|
|
|
|
|
raw_sender_id = sender_id or raw.get("senderId", "")
|
2026-05-12 00:43:14 +08:00
|
|
|
|
|
|
|
|
if chat_type == "direct":
|
2026-05-13 16:06:10 +08:00
|
|
|
return f"dm_{raw_sender_id}"
|
|
|
|
|
|
2026-05-12 00:43:14 +08:00
|
|
|
conversation_id = raw.get("conversationId", "")
|
2026-05-13 16:06:10 +08:00
|
|
|
|
|
|
|
|
if scope == "group_sender" and raw_sender_id:
|
|
|
|
|
return f"{CHAT_ID_PREFIX_GROUP_SENDER}{conversation_id}:{raw_sender_id}"
|
|
|
|
|
|
2026-05-12 00:43:14 +08:00
|
|
|
return f"group_{conversation_id}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def resolve_dingding_chat_type(raw: dict) -> str:
|
|
|
|
|
is_group = raw.get("isGroupChat", False)
|
|
|
|
|
return "group" if is_group else "direct"
|
|
|
|
|
|
|
|
|
|
|
2026-05-13 16:06:10 +08:00
|
|
|
def generate_thread_key(channel_id: str, chat_id: str, *, scope: str = "group") -> str:
|
2026-05-12 00:43:14 +08:00
|
|
|
if chat_id.startswith("dm_"):
|
|
|
|
|
return f"{channel_id}:direct:{chat_id}"
|
2026-05-13 16:06:10 +08:00
|
|
|
if scope == "group_sender":
|
|
|
|
|
return f"{channel_id}:group_sender:{chat_id}"
|
2026-05-12 00:43:14 +08:00
|
|
|
return f"{channel_id}:group:{chat_id}"
|
2026-05-13 16:06:10 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def normalize_chat_id(chat_id: str) -> str:
|
|
|
|
|
if chat_id.startswith("dingding:") or chat_id.startswith("dm_") or chat_id.startswith("group_"):
|
|
|
|
|
return chat_id
|
|
|
|
|
if chat_id.startswith("group_sender_"):
|
|
|
|
|
return f"{CHAT_ID_PREFIX_GROUP_SENDER}{chat_id}"
|
|
|
|
|
return f"{CHAT_ID_PREFIX_GROUP}{chat_id}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def resolve_session_scope(chat_id: str) -> str:
|
|
|
|
|
if chat_id.startswith("dm_"):
|
|
|
|
|
return SessionScope.DIRECT
|
|
|
|
|
if CHAT_ID_PREFIX_GROUP_SENDER in chat_id:
|
|
|
|
|
return SessionScope.GROUP_SENDER
|
|
|
|
|
return SessionScope.GROUP
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def parse_chat_id(chat_id: str) -> dict[str, str]:
|
|
|
|
|
if chat_id.startswith("dm_"):
|
|
|
|
|
return {"type": "direct", "open_id": chat_id[3:]}
|
|
|
|
|
if chat_id.startswith("group_"):
|
|
|
|
|
return {"type": "group", "open_conversation_id": chat_id[6:]}
|
|
|
|
|
if CHAT_ID_PREFIX_GROUP_SENDER in chat_id:
|
|
|
|
|
parts = chat_id.split(":")
|
|
|
|
|
if len(parts) >= 3:
|
|
|
|
|
return {"type": "group_sender", "group_id": parts[-2], "sender_id": parts[-1]}
|
|
|
|
|
return {"type": "unknown", "raw": chat_id}
|