import uuid from django.conf import settings from django.db import models from django.db.models import Q class ChannelEndpoint(models.Model): public_id = models.UUIDField(default=uuid.uuid4, unique=True, editable=False) name = models.CharField(max_length=120, unique=True) adapter_type = models.CharField(max_length=40, default="generic_webhook") outbound_url = models.URLField(max_length=500, blank=True) webhook_secret_encrypted = models.TextField(blank=True) outbound_token_encrypted = models.TextField(blank=True) enabled = models.BooleanField(default=True) created_by = models.ForeignKey( settings.AUTH_USER_MODEL, null=True, blank=True, on_delete=models.SET_NULL, related_name="created_channel_endpoints", ) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: ordering = ["name"] class ChannelConversation(models.Model): public_id = models.UUIDField(default=uuid.uuid4, unique=True, editable=False) endpoint = models.ForeignKey( ChannelEndpoint, on_delete=models.PROTECT, related_name="conversations", ) external_conversation_id = models.CharField(max_length=200) title = models.CharField(max_length=200, blank=True) participants = models.JSONField(default=list, blank=True) ticket = models.ForeignKey( "tickets.Ticket", null=True, blank=True, on_delete=models.SET_NULL, related_name="channel_conversations", ) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: ordering = ["-updated_at"] constraints = [ models.UniqueConstraint( fields=["endpoint", "external_conversation_id"], name="unique_channel_external_conversation", ) ] class ChannelMessage(models.Model): public_id = models.UUIDField(default=uuid.uuid4, unique=True, editable=False) endpoint = models.ForeignKey( ChannelEndpoint, on_delete=models.PROTECT, related_name="messages", ) conversation = models.ForeignKey( ChannelConversation, null=True, blank=True, on_delete=models.SET_NULL, related_name="messages", ) ticket = models.ForeignKey( "tickets.Ticket", null=True, blank=True, on_delete=models.SET_NULL, related_name="channel_messages", ) customer_reply_task = models.ForeignKey( "tickets.CustomerReplyTask", null=True, blank=True, on_delete=models.PROTECT, related_name="channel_messages", ) reply_revision = models.PositiveIntegerField(null=True, blank=True) direction = models.CharField(max_length=16) external_message_id = models.CharField(max_length=200, blank=True) idempotency_key = models.CharField(max_length=200, blank=True) sender = models.CharField(max_length=160, blank=True) message_type = models.CharField(max_length=24, default="text") text = models.TextField(blank=True) payload = models.JSONField(default=dict, blank=True) status = models.CharField(max_length=24) error_message = models.TextField(blank=True) created_by = models.ForeignKey( settings.AUTH_USER_MODEL, null=True, blank=True, on_delete=models.SET_NULL, related_name="created_channel_messages", ) occurred_at = models.DateTimeField(null=True, blank=True) sent_at = models.DateTimeField(null=True, blank=True) delivered_at = models.DateTimeField(null=True, blank=True) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: ordering = ["-created_at"] constraints = [ models.UniqueConstraint( fields=["endpoint", "direction", "external_message_id"], condition=Q(external_message_id__gt=""), name="unique_channel_external_message", ), models.UniqueConstraint( fields=["endpoint", "direction", "idempotency_key"], condition=Q(idempotency_key__gt=""), name="unique_channel_message_idempotency", ), models.UniqueConstraint( fields=["customer_reply_task", "reply_revision"], condition=Q( direction="outbound", customer_reply_task__isnull=False, ), name="unique_channel_reply_revision", ), ] indexes = [ models.Index(fields=["endpoint", "direction", "created_at"]), models.Index(fields=["status", "created_at"]), ] class ChannelReceipt(models.Model): message = models.ForeignKey( ChannelMessage, on_delete=models.CASCADE, related_name="receipts", ) receipt_type = models.CharField(max_length=24) external_receipt_id = models.CharField(max_length=200) payload = models.JSONField(default=dict, blank=True) received_at = models.DateTimeField(auto_now_add=True) class Meta: ordering = ["received_at"] constraints = [ models.UniqueConstraint( fields=["message", "receipt_type", "external_receipt_id"], name="unique_channel_message_receipt", ) ]