from django.conf import settings from django.db import models from django.utils.translation import gettext_lazy as _ from apps.banker.models import Contact, ContactGroup, MessageTemplate # این اپ فقط از مسیرهای رسمی هر پلتفرم برای ارسال استفاده می‌کند: پیامک از # طریق درگاه بالک کاوه‌نگار (سمت سرور، خودکار)، واتساپ از طریق WhatsApp # Business Cloud API رسمی، تلگرام/بله/ایتا از طریق Bot API رسمی هرکدام — # فقط برای مخاطبینی که قبلا چت با بات را شروع کرده‌اند (opt-in). برای بقیه‌ی # موارد، MessageRecipient با status=needs_manual برمی‌گردد تا اپ یک صف # deep-link یک‌لمسی به کاربر نشان دهد؛ هیچ اتوماسیون/شبیه‌سازی رفتار انسانی # برای دور زدن ضدهرزنامه‌ی این پلتفرم‌ها در این اپ وجود ندارد. class Channel(models.TextChoices): SMS = "sms", _("پیامک") WHATSAPP = "whatsapp", _("واتساپ") TELEGRAM = "telegram", _("تلگرام") BALE = "bale", _("بله") EITAA = "eitaa", _("ایتا") class BulkMessage(models.Model): class Status(models.TextChoices): DISPATCHED = "dispatched", _("ارسال‌شده") PARTIALLY_MANUAL = "partially_manual", _("بخشی نیاز به تایید دستی") user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="bulk_messages") # null = همه‌ی مخاطبین کاربر group = models.ForeignKey(ContactGroup, on_delete=models.SET_NULL, null=True, blank=True, related_name="+") template = models.ForeignKey( MessageTemplate, on_delete=models.SET_NULL, null=True, blank=True, related_name="+" ) body_override = models.TextField(blank=True, verbose_name=_("متن آزاد (اگر قالب انتخاب نشده)")) channels = models.JSONField(default=list, verbose_name=_("کانال‌های انتخاب‌شده")) status = models.CharField(max_length=20, choices=Status.choices, default=Status.DISPATCHED) created_at = models.DateTimeField(auto_now_add=True) class Meta: verbose_name = _("ارسال گروهی") verbose_name_plural = _("ارسال‌های گروهی") ordering = ["-created_at"] def __str__(self): return f"#{self.pk} — {self.user.phone} — {', '.join(self.channels)}" class MessageRecipient(models.Model): class Status(models.TextChoices): SENT = "sent", _("ارسال‌شده") FAILED = "failed", _("ناموفق") NEEDS_MANUAL = "needs_manual", _("نیاز به تایید دستی") MANUAL_CONFIRMED = "manual_confirmed", _("تایید دستی شد") bulk_message = models.ForeignKey(BulkMessage, on_delete=models.CASCADE, related_name="recipients") contact = models.ForeignKey(Contact, on_delete=models.CASCADE, related_name="+") channel = models.CharField(max_length=10, choices=Channel.choices) status = models.CharField(max_length=20, choices=Status.choices) rendered_body = models.TextField() error = models.CharField(max_length=255, blank=True) updated_at = models.DateTimeField(auto_now=True) class Meta: verbose_name = _("گیرنده‌ی پیام") verbose_name_plural = _("گیرنده‌های پیام") indexes = [models.Index(fields=["bulk_message", "status"])] def __str__(self): return f"{self.contact.name} — {self.channel} — {self.status}"