from __future__ import annotations import logging from datetime import datetime from yuxi.channel.message.models import ( GroupContext, MessageType, PeerInfo, UnifiedMessage, ) from yuxi.channel.routing.models import PeerKind from .constants import PREFIX_GROUP from .types import ( FlockChatType, FlockEvent, FlockEventType, InboundFlockMessage, ) logger = logging.getLogger(__name__) def parse_event_listener(body: dict) -> FlockEvent: name = body.get("name", "") event_data = body.get("event", {}) chat = event_data.get("chat", "") message = event_data.get("message", {}) return FlockEvent( name=name, chat=chat, message=message, event=event_data, ) def parse_outgoing_webhook(body: dict) -> InboundFlockMessage: user_id = body.get("userId", "") channel_id = body.get("channelId", "") text = body.get("text", "") timestamp = int(body.get("timestamp", 0)) chat_type = FlockChatType.GROUP if channel_id.startswith(PREFIX_GROUP) else FlockChatType.DIRECT return InboundFlockMessage( message_id=f"outgoing_{user_id}_{timestamp}", event_type=FlockEventType.OUTGOING_WEBHOOK, from_user=user_id, to=channel_id or user_id, chat_type=chat_type, text=text, timestamp=timestamp, raw_payload=body, ) def flock_event_to_inbound(event: FlockEvent) -> InboundFlockMessage: msg = event.message message_id = msg.get("uid", "") from_user = msg.get("from", "") to = msg.get("to", "") text = msg.get("text", "") flockml = msg.get("flockml", "") timestamp = int(msg.get("timestamp", 0)) thread_id = msg.get("threadId") mentions = msg.get("mentions", []) attachments = msg.get("attachments", []) chat_type = FlockChatType.GROUP if to.startswith(PREFIX_GROUP) else FlockChatType.DIRECT event_type_map = { "chat.receiveMessage": FlockEventType.MESSAGE_RECEIVE, "client.slashCommand": FlockEventType.SLASH_COMMAND, "client.chatAction": FlockEventType.CHAT_ACTION, "app.install": FlockEventType.APP_INSTALL, "app.uninstall": FlockEventType.APP_UNINSTALL, } event_type = event_type_map.get(event.name, FlockEventType.MESSAGE_RECEIVE) return InboundFlockMessage( message_id=message_id, event_type=event_type, from_user=from_user, to=to, chat_type=chat_type, text=text, flockml=flockml, timestamp=timestamp, thread_id=thread_id, mentions=mentions, attachments=attachments, raw_payload={"name": event.name, "event": event.event}, ) def inbound_to_unified(msg: InboundFlockMessage, account_id: str) -> UnifiedMessage: from .utils import strip_flock_prefix display_name = strip_flock_prefix(msg.from_user) sender = PeerInfo( kind=PeerKind.DIRECT if msg.chat_type == FlockChatType.DIRECT else PeerKind.GROUP, id=msg.from_user, display_name=display_name, ) group = None if msg.chat_type == FlockChatType.GROUP: group = GroupContext( id=msg.to, thread_id=msg.thread_id, ) bot_mentioned = False if msg.mentions: bot_mentioned = True message_type = MessageType.TEXT media_urls: list[str] = [] if msg.attachments: message_type = MessageType.FILE for att in msg.attachments: if isinstance(att, dict): src = att.get("src", "") or (att.get("views", {}).get("image", {}).get("original", {}).get("src", "")) if src: media_urls.append(src) ts = datetime.fromtimestamp(msg.timestamp / 1000.0) if msg.timestamp else datetime.now() return UnifiedMessage( msg_id=msg.message_id, channel_type="flock", account_id=account_id, content=msg.text, sender=sender, message_type=message_type, media_urls=media_urls, group=group, timestamp=ts, raw_payload=msg.raw_payload, message_thread_id=msg.thread_id, was_mentioned=len(msg.mentions) > 0, explicitly_mentioned_bot=bot_mentioned, mentioned_user_ids=list(msg.mentions), )