import rules from asgiref.sync import async_to_sync from channels.layers import get_channel_layer from communications.models import Message from communications.serialization import serialize_message from communications.utils import user_can_access_chat class ChatQuickReplyError(Exception): """A native push reply cannot be sent to the requested chat.""" class ChatQuickReplyService: """Create and broadcast a plain-text reply received from native push UI.""" MAX_CONTENT_LENGTH = 4000 def send(self, *, chat, user, reply_to, content: str) -> Message: normalized_content = content.strip() if not normalized_content: raise ChatQuickReplyError("empty reply") if len(normalized_content) > self.MAX_CONTENT_LENGTH: raise ChatQuickReplyError("reply too long") if not chat.can_reply: raise ChatQuickReplyError("replies are disabled") if not user_can_access_chat(chat, user) or not rules.test_rule( "communications.send_message", user, chat, ): raise ChatQuickReplyError("sending is not allowed") message = Message.objects.create( chat=chat, author=user, content=normalized_content, reply_to=reply_to, ) self._broadcast(message) return message @staticmethod def _broadcast(message: Message) -> None: payload = serialize_message(message) async_to_sync(get_channel_layer().group_send)( f"chat_{message.chat_id}", { "type": "chat.message", "chat_id": message.chat_id, "payload": payload, }, )