ForcePilot/backend/package/yuxi/channels/adapters/whatsapp/poll.py
Kris e9b57546ea feat(whatsapp): 新增WhatsApp适配器完整功能模块
新增大量WhatsApp适配器相关代码,包括账号管理、会话处理、消息收发、验证授权、媒体处理、互动命令、审批流程、健康检测等完整功能模块,搭建基础的Baileys协议WhatsApp接入能力
2026-05-12 00:51:58 +08:00

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]