ForcePilot/backend/package/yuxi/channel/extensions/zalo/session.py
Kris 5946478772 feat(channel): 添加小红书、XMPP、元宝和 Zalo 渠道扩展
新增小红书、XMPP、元宝、Zalo 四个渠道扩展。

小红书渠道扩展主要模块:config, gateway, webhook, outbound, streaming, pairing, security, dedupe, media, status, window

XMPP 渠道扩展主要模块:plugin, config, gateway, outbound, streaming, pairing, security, dedupe, accounts, commands, muc, rate_limiter, stanza_utils, status, monitor

元宝渠道扩展主要模块:plugin, client, config_schema, gateway, outbound(chunk/queue/transport), inbound(dispatcher), streaming, pairing, security, accounts, actions, commands, codec(biz/conn), session, shared, utils

Zalo 渠道扩展主要模块:api, config, gateway, webhook, outbound, pairing, security, session, polling, monitor, status
2026-05-21 12:04:05 +08:00

69 lines
2.2 KiB
Python

from __future__ import annotations
import re
from yuxi.channel.protocols import SessionResolution
_RE_DIGITS = re.compile(r"^\d+$")
class ZaloSession:
@staticmethod
def build_conversation_id(chat_type: str, target_id: str) -> str:
if chat_type == "GROUP":
return f"zalo:group:{target_id}"
return f"zalo:{target_id}"
@staticmethod
def build_session_key(agent_id: str, account_id: str, chat_type: str, target_id: str) -> str:
chat = "direct" if chat_type in ("PRIVATE", "direct") else "group"
return f"agent:{agent_id}:zalo:{chat}:{target_id}"
@staticmethod
def normalize_target(raw: str) -> str:
target = raw.strip()
for prefix in ("zalo:", "zl:"):
if target.lower().startswith(prefix.lower()):
target = target[len(prefix) :]
break
return target
@staticmethod
def parse_target(raw: str) -> tuple[str, str]:
"""Parse a target string into (chat_type, target_id).
Supported formats:
- zalo:{userId} → ("direct", userId)
- zl:{userId} → ("direct", userId)
- zalo:group:{chatId} → ("group", chatId)
- zl:group:{chatId} → ("group", chatId)
- {digits} → ("direct", digits)
"""
target = raw.strip()
for prefix in ("zalo:", "zl:"):
if target.lower().startswith(prefix.lower()):
remainder = target[len(prefix) :]
if remainder.lower().startswith("group:"):
return ("group", remainder[len("group:") :])
return ("direct", remainder)
if _RE_DIGITS.match(target):
return ("direct", target)
return ("direct", target)
@staticmethod
def resolve_dm_session(sender_id: str) -> SessionResolution:
return SessionResolution(
kind="direct",
conversation_id=f"zalo:{sender_id}",
)
@staticmethod
def resolve_group_session(chat_id: str) -> SessionResolution:
return SessionResolution(
kind="group",
conversation_id=f"zalo:group:{chat_id}",
)