from django.db.models import Count from rest_framework import status from rest_framework.exceptions import NotFound, ValidationError from rest_framework.generics import RetrieveAPIView from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response from rest_framework.views import APIView from apps.banker.models import ContactGroup from apps.billing.permissions import HasActiveFeature from apps.messaging.models import BulkMessage, MessageRecipient from .serializers import BulkMessageSerializer, SendBulkMessageSerializer from .services import BulkMessageService class SendBulkMessageView(APIView): """POST: می‌سازد و بلافاصله dispatch می‌کند (پیامک/کانال‌های Business-API-دار خودکار، بقیه needs_manual برای صف deep-link سمت اپ).""" permission_classes = [IsAuthenticated, HasActiveFeature] feature_key = "sms_bank" throttle_scope = "messaging_send" def post(self, request): serializer = SendBulkMessageSerializer(data=request.data) serializer.is_valid(raise_exception=True) data = serializer.validated_data if data["group_id"] and not ContactGroup.objects.filter(user=request.user, id=data["group_id"]).exists(): raise ValidationError({"group_id": "گروه یافت نشد."}) bulk = BulkMessageService.create_and_dispatch( user=request.user, channels=data["channels"], group_id=data["group_id"], contact_ids=data["contact_ids"], template_id=data["template_id"], body_override=data["body_override"], ) return Response(BulkMessageSerializer(bulk).data, status=status.HTTP_201_CREATED) class BulkMessageDetailView(RetrieveAPIView): serializer_class = BulkMessageSerializer permission_classes = [IsAuthenticated] def get_queryset(self): return BulkMessage.objects.filter(user=self.request.user) class MessagingStatsView(APIView): """آمار ارسال: چند پیام موفق به گیت‌وی/API فرستاده شده، چند تا ناموفق بوده، چند تا نیاز به تایید دستی داشته/دارد — بر اساس MessageRecipient (نه رسید تحویل واقعی گوشی، چون آن نیاز به webhook از سرویس‌دهنده‌ی پیامک دارد که در این نسخه وصل نشده؛ «ارسال‌شده» یعنی سرور با موفقیت آن را به API/گیت‌وی تحویل داده).""" permission_classes = [IsAuthenticated, HasActiveFeature] feature_key = "sms_bank" def get(self, request): rows = ( MessageRecipient.objects.filter(bulk_message__user=request.user) .values("status") .annotate(count=Count("id")) ) counts = {row["status"]: row["count"] for row in rows} total = sum(counts.values()) return Response( { "total": total, "sent": counts.get(MessageRecipient.Status.SENT, 0), "failed": counts.get(MessageRecipient.Status.FAILED, 0), "needs_manual": counts.get(MessageRecipient.Status.NEEDS_MANUAL, 0), "manual_confirmed": counts.get(MessageRecipient.Status.MANUAL_CONFIRMED, 0), } ) class ConfirmManualSendView(APIView): """اپ بعد از این‌که کاربر دستی در واتساپ/تلگرام/بله/ایتا زد «ارسال»، این را صدا می‌زند.""" permission_classes = [IsAuthenticated, HasActiveFeature] feature_key = "sms_bank" def post(self, request, bulk_id, recipient_id): try: recipient = BulkMessageService.confirm_manual(request.user, bulk_id, recipient_id) except MessageRecipient.DoesNotExist: raise NotFound("گیرنده یافت نشد.") return Response({"success": True, "status": recipient.status})