from __future__ import annotations from enum import StrEnum CHAT_ID_PREFIX_DM = "feishu:dm:" CHAT_ID_PREFIX_GROUP = "feishu:group:" CHAT_ID_PREFIX_THREAD = "feishu:thread:" CHAT_ID_PREFIX_GROUP_SENDER = "feishu:group_sender:" CHAT_ID_PREFIX_GROUP_TOPIC = "feishu:group_topic:" CHAT_ID_PREFIX_GROUP_TOPIC_SENDER = "feishu:group_topic_sender:" GroupSessionScope = str # "group" | "group_sender" | "group_topic" | "group_topic_sender" class SessionMode(StrEnum): RAW = "raw" THREAD = "thread" CHAT_RAW = "chat_raw" CHAT_RESOLVE = "chat_resolve" def generate_feishu_chat_id( raw_chat_id: str, chat_type: str, *, root_id: str | None = None, sender_id: str | None = None, mode: SessionMode = SessionMode.CHAT_RESOLVE, scope: GroupSessionScope = "group", ) -> str: if mode == SessionMode.RAW: return raw_chat_id if mode == SessionMode.CHAT_RAW: prefix = f"feishu:{chat_type}:" return f"{prefix}{raw_chat_id}" if mode == SessionMode.THREAD and root_id: return f"{CHAT_ID_PREFIX_THREAD}{root_id}" if chat_type in ("group", "thread") and root_id: if scope == "group_topic_sender" and sender_id: return f"{CHAT_ID_PREFIX_GROUP_TOPIC_SENDER}{root_id}:{raw_chat_id}:{sender_id}" if scope == "group_topic": return f"{CHAT_ID_PREFIX_GROUP_TOPIC}{root_id}:{raw_chat_id}" if scope == "group_sender" and sender_id: return f"{CHAT_ID_PREFIX_GROUP_SENDER}{raw_chat_id}:{sender_id}" return f"{CHAT_ID_PREFIX_GROUP}{root_id}:{raw_chat_id}" if chat_type == "group" or chat_type == "direct" or chat_type == "p2p": if scope == "group_sender" and sender_id: prefix = CHAT_ID_PREFIX_DM if (chat_type == "direct" or chat_type == "p2p") else CHAT_ID_PREFIX_GROUP_SENDER return f"{prefix}{raw_chat_id}:{sender_id}" prefix = CHAT_ID_PREFIX_DM if (chat_type == "direct" or chat_type == "p2p") else CHAT_ID_PREFIX_GROUP return f"{prefix}{raw_chat_id}" return f"{CHAT_ID_PREFIX_DM}{raw_chat_id}" def resolve_feishu_chat_type(raw_chat_id: str) -> str: return "direct" if raw_chat_id.startswith("ou_") else "group" def resolve_feishu_command_conversation( chat_id: str, user_id: str, *, scope: GroupSessionScope = "group", ) -> str: if user_id.startswith("ou_"): return f"{CHAT_ID_PREFIX_DM}{user_id}" return generate_feishu_chat_id(chat_id, "group", scope=scope) def normalize_feishu_acp_conversation_id(conv_id: str) -> str: if conv_id.startswith("feishu:"): return conv_id if conv_id.startswith("ou_"): return f"{CHAT_ID_PREFIX_DM}{conv_id}" if conv_id.startswith("oc_"): return f"{CHAT_ID_PREFIX_GROUP}{conv_id}" return conv_id def match_feishu_acp_conversation(conv_id: str, pattern: str) -> bool: import fnmatch normalized = normalize_feishu_acp_conversation_id(conv_id) return fnmatch.fnmatch(normalized, pattern) _FEISHU_ID_PREFIXES = ("ou_", "oc_", "on_", "om_", "od_", "og_", "tg_") def looks_like_feishu_id(value: str) -> bool: if not value or not isinstance(value, str): return False return any(value.startswith(p) for p in _FEISHU_ID_PREFIXES) def normalize_feishu_target(target: str) -> str: if not target or not isinstance(target, str): return target target = target.strip() if target.startswith("feishu:"): return target if target.startswith("lark:"): return "feishu:" + target[5:] if target.startswith("feishu://"): return "feishu:" + target[9:] for prefix in _FEISHU_ID_PREFIXES: if target.startswith(prefix): if prefix in ("ou_", "on_"): return f"{CHAT_ID_PREFIX_DM}{target}" if prefix in ("oc_", "om_", "od_", "og_"): return f"{CHAT_ID_PREFIX_GROUP}{target}" return target