本提交新增了全渠道SDK核心模块: 1. 异步锁、目标解析、动作调度等基础工具 2. 消息动作注册与统一调度系统 3. 测试套件与契约测试框架 4. 完整的目标解析流水线与工具函数 5. 资源依赖注入与生命周期管理
79 lines
3.0 KiB
Python
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", ""))) |