ForcePilot/backend/package/yuxi/channel/sdk/targets/extractors.py
Kris b438af3ba8 feat(channel-sdk): 新增完整的渠道SDK工具链
本提交新增了全渠道SDK核心模块:
1.  异步锁、目标解析、动作调度等基础工具
2.  消息动作注册与统一调度系统
3.  测试套件与契约测试框架
4.  完整的目标解析流水线与工具函数
5.  资源依赖注入与生命周期管理
2026-05-21 10:29:12 +08:00

79 lines
3.0 KiB
Python

from __future__ import annotations
from typing import Protocol
from yuxi.channel.message.models import GroupContext, MessageType, PeerInfo
from yuxi.channel.routing.models import PeerKind
class TargetFieldExtractor(Protocol):
def extract_sender(self, raw_message: dict) -> PeerInfo: ...
def extract_content(self, raw_message: dict) -> str: ...
def extract_media_urls(self, raw_message: dict) -> list[str]: ...
def extract_group(self, raw_message: dict) -> GroupContext | None: ...
def detect_message_type(self, raw_message: dict) -> MessageType: ...
def extract_message_id(self, raw_message: dict) -> str: ...
class TelegramTargetExtractor:
@staticmethod
def _msg(raw_message: dict) -> dict:
return raw_message.get("message", raw_message)
def extract_sender(self, raw_message: dict) -> PeerInfo:
msg = self._msg(raw_message)
sender_data = msg.get("from", {})
return PeerInfo(
kind=PeerKind.DIRECT,
id=str(sender_data.get("id", "")),
display_name=sender_data.get("first_name", sender_data.get("name")),
username=sender_data.get("username"),
is_bot=sender_data.get("is_bot", False),
)
def extract_content(self, raw_message: dict) -> str:
msg = self._msg(raw_message)
return str(msg.get("text", msg.get("body", msg.get("caption", ""))) or "")
def extract_media_urls(self, raw_message: dict) -> list[str]:
msg = self._msg(raw_message)
urls: list[str] = []
for field in ("photo", "document", "video", "audio", "image"):
if field in msg:
info = msg[field]
if isinstance(info, list) and info:
last = info[-1]
if isinstance(last, dict):
urls.append(str(last.get("file_id", last.get("url", ""))))
elif isinstance(info, dict):
urls.append(str(info.get("file_id", info.get("url", ""))))
return urls
def extract_group(self, raw_message: dict) -> GroupContext | None:
msg = self._msg(raw_message)
chat = msg.get("chat", {})
if chat.get("type") in ("group", "supergroup", "channel"):
return GroupContext(
id=str(chat.get("id", "")),
name=chat.get("title"),
thread_id=str(msg.get("message_thread_id")) if msg.get("is_topic_message") else None,
)
return None
def detect_message_type(self, raw_message: dict) -> MessageType:
msg = self._msg(raw_message)
if any(k in msg for k in ("photo", "document", "video", "audio", "image", "voice")):
return MessageType.FILE
if msg.get("text") or msg.get("body") or msg.get("caption"):
return MessageType.TEXT
return MessageType.EVENT
def extract_message_id(self, raw_message: dict) -> str:
msg = self._msg(raw_message)
return str(msg.get("message_id", msg.get("id", "")))