新增大量WhatsApp适配器相关代码,包括账号管理、会话处理、消息收发、验证授权、媒体处理、互动命令、审批流程、健康检测等完整功能模块,搭建基础的Baileys协议WhatsApp接入能力
114 lines
3.5 KiB
Python
114 lines
3.5 KiB
Python
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
from yuxi.channels.models import (
|
|
Attachment,
|
|
ChannelIdentity,
|
|
ChannelMessage,
|
|
ChannelType,
|
|
ChatType,
|
|
EventType,
|
|
MessageType,
|
|
)
|
|
from yuxi.utils.logging_config import logger
|
|
|
|
|
|
def normalize_poll_input(poll: dict[str, Any], max_options: int = 12) -> dict[str, Any] | None:
|
|
name = poll.get("name", "").strip()
|
|
if not name:
|
|
logger.warning("Poll normalization: empty name")
|
|
return None
|
|
|
|
options = poll.get("options", [])
|
|
if isinstance(options, str):
|
|
options = [o.strip() for o in options.split(",") if o.strip()]
|
|
|
|
options = [o.strip() for o in options if o.strip()]
|
|
if len(options) < 2:
|
|
logger.warning("Poll normalization: less than 2 valid options")
|
|
return None
|
|
if len(options) > max_options:
|
|
logger.warning(f"Poll normalization: {len(options)} options exceeds max {max_options}, truncating")
|
|
options = options[:max_options]
|
|
|
|
selectable_count = poll.get("selectableCount", poll.get("selectable_count", 1))
|
|
try:
|
|
selectable_count = min(max(int(selectable_count), 1), len(options))
|
|
except (TypeError, ValueError):
|
|
selectable_count = 1
|
|
|
|
return {
|
|
"name": name,
|
|
"options": options,
|
|
"selectable_count": selectable_count,
|
|
}
|
|
|
|
|
|
def parse_poll_vote(raw_payload: dict[str, Any], channel_id: str) -> ChannelMessage | None:
|
|
key = raw_payload.get("key", {})
|
|
msg = raw_payload.get("message", {})
|
|
|
|
poll_update = msg.get("pollUpdateMessage", {})
|
|
if not poll_update:
|
|
return None
|
|
|
|
poll_creation_key = poll_update.get("pollCreationMessageKey", {})
|
|
vote_info = poll_update.get("vote", {})
|
|
|
|
remote_jid = key.get("remoteJid", "unknown")
|
|
msg_id = key.get("id")
|
|
|
|
selected_options = []
|
|
if isinstance(vote_info, dict):
|
|
selected = vote_info.get("selectedOptions", [])
|
|
if isinstance(selected, list):
|
|
selected_options = selected
|
|
elif isinstance(vote_info, list):
|
|
selected_options = vote_info
|
|
|
|
option_names = [o.get("name", str(o)) if isinstance(o, dict) else str(o) for o in selected_options]
|
|
vote_text = f"[Poll Vote] {' | '.join(option_names)}" if option_names else "[Poll Vote]"
|
|
|
|
return ChannelMessage(
|
|
identity=ChannelIdentity(
|
|
channel_id=channel_id,
|
|
channel_type=ChannelType.WHATSAPP,
|
|
channel_user_id=_extract_sender_number(remote_jid),
|
|
channel_chat_id=remote_jid,
|
|
channel_message_id=msg_id,
|
|
),
|
|
event_type=EventType.MESSAGE_RECEIVED,
|
|
message_type=MessageType.TEXT,
|
|
chat_type=_jid_to_chat_type(remote_jid),
|
|
content=vote_text,
|
|
metadata={
|
|
"raw_type": "poll_vote",
|
|
"remote_jid": remote_jid,
|
|
"poll_msg_id": poll_creation_key.get("id"),
|
|
"poll_remote_jid": poll_creation_key.get("remoteJid"),
|
|
"selected_options": selected_options,
|
|
},
|
|
attachments=[
|
|
Attachment(
|
|
type="poll_vote",
|
|
file_id=msg_id,
|
|
metadata={
|
|
"poll_msg_id": poll_creation_key.get("id"),
|
|
"poll_remote_jid": poll_creation_key.get("remoteJid"),
|
|
"selected": option_names,
|
|
},
|
|
)
|
|
],
|
|
)
|
|
|
|
|
|
def _jid_to_chat_type(jid: str) -> ChatType:
|
|
if "@g.us" in jid:
|
|
return ChatType.GROUP
|
|
return ChatType.DIRECT
|
|
|
|
|
|
def _extract_sender_number(jid: str) -> str:
|
|
return jid.split("@")[0]
|