130 lines
4.0 KiB
Python
130 lines
4.0 KiB
Python
"""Row/response serialization helpers for the kommunikation plugin."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
import re
|
|
from typing import Any
|
|
|
|
from app.plugins.builtins.kommunikation.models import (
|
|
CommConversation,
|
|
CommMessage,
|
|
CommMessageAttachment,
|
|
CommMessageBlock,
|
|
CommMessageReaction,
|
|
CommParticipant,
|
|
)
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
MAX_TRIGGER_DEPTH = 3
|
|
|
|
|
|
# ─── serializers ───
|
|
|
|
# ─── Mention Parsing ───
|
|
|
|
MENTION_PATTERN = re.compile(r"@(\w+)")
|
|
|
|
|
|
def parse_mentions(content: str) -> list[str]:
|
|
"""Parse @mentions from message content. Returns list of mentioned types/names."""
|
|
return MENTION_PATTERN.findall(content)
|
|
|
|
|
|
# ─── Conversation Helpers ───
|
|
|
|
|
|
def conversation_to_response(
|
|
conv: CommConversation,
|
|
participants: list[CommParticipant],
|
|
unread_count: int = 0,
|
|
is_pinned_by_user: bool = False,
|
|
) -> dict[str, Any]:
|
|
"""Convert a CommConversation to a response dict."""
|
|
return {
|
|
"id": str(conv.id),
|
|
"title": conv.title,
|
|
"is_locked": conv.is_locked,
|
|
"locked_by": conv.locked_by,
|
|
"is_direct": conv.is_direct,
|
|
"is_archived": conv.is_archived,
|
|
"is_pinned": is_pinned_by_user,
|
|
"created_by": str(conv.created_by) if conv.created_by else None,
|
|
"created_by_type": conv.created_by_type,
|
|
"last_msg_at": conv.last_msg_at.isoformat() if conv.last_msg_at else None,
|
|
"last_msg_preview": conv.last_msg_preview,
|
|
"last_msg_sender_type": conv.last_msg_sender_type,
|
|
"participants": [participant_to_response(p) for p in participants],
|
|
"unread_count": unread_count,
|
|
"metadata": conv.metadata_ or {},
|
|
}
|
|
|
|
|
|
def participant_to_response(p: CommParticipant) -> dict[str, Any]:
|
|
"""Convert a CommParticipant to a response dict."""
|
|
return {
|
|
"id": str(p.id),
|
|
"conversation_id": str(p.conversation_id),
|
|
"participant_id": str(p.participant_id) if p.participant_id else None,
|
|
"participant_type": p.participant_type,
|
|
"display_name": p.display_name,
|
|
"role": p.role,
|
|
"joined_at": p.joined_at.isoformat() if p.joined_at else None,
|
|
}
|
|
|
|
|
|
def message_to_response(
|
|
msg: CommMessage,
|
|
blocks: list[CommMessageBlock] | None = None,
|
|
attachments: list[CommMessageAttachment] | None = None,
|
|
reactions: list[CommMessageReaction] | None = None,
|
|
) -> dict[str, Any]:
|
|
"""Convert a CommMessage to a response dict."""
|
|
return {
|
|
"id": str(msg.id),
|
|
"conversation_id": str(msg.conversation_id),
|
|
"sender_id": str(msg.sender_id) if msg.sender_id else None,
|
|
"sender_type": msg.sender_type,
|
|
"content": msg.content,
|
|
"content_format": msg.content_format,
|
|
"metadata": msg.metadata_ or {},
|
|
"reply_to_id": str(msg.reply_to_id) if msg.reply_to_id else None,
|
|
"is_pinned": msg.is_pinned,
|
|
"created_at": msg.created_at.isoformat() if msg.created_at else None,
|
|
"edited_at": msg.edited_at.isoformat() if msg.edited_at else None,
|
|
"blocks": [
|
|
{
|
|
"id": str(b.id),
|
|
"block_type": b.block_type,
|
|
"block_data": b.block_data,
|
|
"sort_order": b.sort_order,
|
|
}
|
|
for b in (blocks or [])
|
|
],
|
|
"attachments": [
|
|
{
|
|
"id": str(a.id),
|
|
"file_id": str(a.file_id) if a.file_id else None,
|
|
"file_source": a.file_source,
|
|
"file_name": a.file_name,
|
|
"file_type": a.file_type,
|
|
"file_size": a.file_size,
|
|
"thumbnail_path": a.thumbnail_path,
|
|
}
|
|
for a in (attachments or [])
|
|
],
|
|
"reactions": [
|
|
{
|
|
"id": str(r.id),
|
|
"message_id": str(r.message_id),
|
|
"user_id": str(r.user_id),
|
|
"emoji": r.emoji,
|
|
}
|
|
for r in (reactions or [])
|
|
],
|
|
}
|
|
|
|
|
|
# ─── Conversation CRUD ───
|