from __future__ import annotations import re import time import uuid from typing import Any from yuxi.channels.models import ( ChannelIdentity, ChannelMessage, ChannelType, ChatType, EventType, MentionsInfo, MessageType, ) def _parse_irc_time_tag(time_tag: str) -> int: """解析 IRCv3 server-time 标签为毫秒时间戳""" import datetime try: ts_str = time_tag.replace("Z", "+00:00") dt = datetime.datetime.fromisoformat(ts_str) return int(dt.timestamp() * 1000) except (ValueError, TypeError): return int(time.time() * 1000) def normalize_inbound( raw: dict[str, Any], channel_id: str, channel_type: ChannelType, nick: str, ) -> ChannelMessage | None: sender_nick = raw.get("sender_nick", "unknown") target = raw.get("target", "") text = raw.get("text", "") if not text or not text.strip(): return None is_channel = target.startswith("#") or target.startswith("&") chat_id = target if is_channel else f"dm_{sender_nick}" chat_type = ChatType.GROUP if is_channel else ChatType.DIRECT mentioned_user_ids: list[str] = [] is_bot_mentioned = _check_bot_mention(text, nick) if is_channel: for word in text.split(): if word.startswith("@"): mentioned_user_ids.append(word[1:]) server_time = raw.get("time") ts = _parse_irc_time_tag(server_time) if server_time else int(time.time() * 1000) return ChannelMessage( identity=ChannelIdentity( channel_id=channel_id, channel_type=channel_type, channel_user_id=sender_nick, channel_chat_id=chat_id, channel_message_id=f"{sender_nick}:{ts}:{uuid.uuid4().hex[:8]}", ), event_type=EventType.MESSAGE_RECEIVED, message_type=MessageType.TEXT, chat_type=chat_type, content=text, mentions=MentionsInfo( mentioned_user_ids=mentioned_user_ids, is_bot_mentioned=is_bot_mentioned, ), metadata={ "irc_target": target, "is_channel": is_channel, "account": raw.get("account"), }, ) def resolve_chat_id(target: str, sender_nick: str) -> str: from .session import resolve_chat_id as _resolve return _resolve(target, sender_nick) def _check_bot_mention(text: str, nick: str) -> bool: """Check if bot nick is mentioned with word boundary awareness.""" pattern = rf"\b{re.escape(nick)}\b[:,]?" return bool(re.search(pattern, text))