# retoor import logging from datetime import datetime, timezone from typing import Any, Optional from devplacepy.attachments import get_attachments, link_attachments from devplacepy.database import get_table, get_blocked_uids from devplacepy.templating import clear_messages_cache from devplacepy.utils import ( create_mention_notifications, create_notification, generate_uid, time_ago, track_action, ) from devplacepy.services.audit import record as audit from devplacepy.services.correction import schedule_correction from devplacepy.services.ai_modifier import schedule_modification logger = logging.getLogger("messaging.persist") MAX_CONTENT_LENGTH = 2000 def _slim_attachment(attachment: dict[str, Any]) -> dict[str, Any]: return { "uid": attachment["uid"], "url": attachment["url"], "thumbnail_url": attachment.get("thumbnail_url"), "is_image": bool(attachment.get("is_image")), "is_video": bool(attachment.get("is_video")), "original_filename": attachment.get("original_filename", ""), "file_size": attachment.get("file_size", 0), "mime_type": attachment.get("mime_type", ""), } def message_frame( message: dict[str, Any], sender_username: str, client_id: Optional[str] = None, sender_role: Optional[str] = None, ) -> dict[str, Any]: attachments = get_attachments("message", message["uid"]) return { "type": "message", "uid": message["uid"], "sender_uid": message["sender_uid"], "sender_username": sender_username, "sender_role": sender_role, "receiver_uid": message["receiver_uid"], "content": message["content"], "created_at": message["created_at"], "time_ago": time_ago(message["created_at"]), "client_id": client_id, "attachments": [_slim_attachment(a) for a in attachments], } def persist_message( sender: dict[str, Any], receiver_uid: str, content: str, attachment_uids: Optional[list[str]] = None, *, request: Any = None, origin: str = "web", ) -> Optional[dict[str, Any]]: content = (content or "").strip()[:MAX_CONTENT_LENGTH] attachment_uids = attachment_uids or [] if not content and not attachment_uids: return None receiver = get_table("users").find_one(uid=receiver_uid) if not receiver: return None if sender["uid"] in get_blocked_uids(receiver_uid): return None sender_uid = sender["uid"] sender_username = sender.get("username", "") messages_table = get_table("messages") msg_uid = generate_uid() created_at = datetime.now(timezone.utc).isoformat() messages_table.insert( { "uid": msg_uid, "sender_uid": sender_uid, "receiver_uid": receiver_uid, "content": content, "read": False, "created_at": created_at, } ) link_attachments(attachment_uids, "message", msg_uid) schedule_correction(sender, "messages", msg_uid, request) schedule_modification(sender, "messages", msg_uid, request) if sender_uid != receiver_uid: create_notification( receiver_uid, "message", f"{sender_username} sent you a message", sender_uid, f"/messages?with_uid={sender_uid}", ) clear_messages_cache(receiver_uid) create_mention_notifications( content, sender_uid, f"/messages?with_uid={receiver_uid}" ) track_action(sender_uid, "message") logger.info( "Message %s sent from %s to %s via %s", msg_uid, sender_username, receiver_uid, origin, ) logger.debug("message %s content length=%d", msg_uid, len(content)) summary = ( f"{sender_username} sent a message to " f"{receiver.get('username') or receiver_uid}: {content}" ) links = [ audit.target("message", msg_uid), audit.recipient(receiver_uid, receiver.get("username")), ] if request is not None: audit.record( request, "message.send", user=sender, target_type="message", target_uid=msg_uid, summary=summary, metadata={"origin": origin}, links=links, ) else: audit.record_system( "message.send", actor_kind="user", actor_uid=sender_uid, actor_username=sender_username, actor_role="admin" if sender.get("role") == "Admin" else "member", origin=origin, target_type="message", target_uid=msg_uid, summary=summary, metadata={"origin": origin}, links=links, ) return { "uid": msg_uid, "sender_uid": sender_uid, "receiver_uid": receiver_uid, "content": content, "read": False, "created_at": created_at, }