from dataclasses import dataclass import requests from django.conf import settings from django.core.exceptions import ImproperlyConfigured @dataclass class NotificationResult: success: bool provider_id: str = "" error: str = "" class ConsoleBaleProvider: def send_message(self, chat_id, text): if settings.DEBUG: print(f"[DEV BALE] {chat_id or 'dashboard'}: {text}") return NotificationResult(True, "console") class BaleBotProvider: def send_photo(self, chat_id, photo, caption='', *, token=None, filename='receipt.jpg', mime_type='image/jpeg'): """Upload private evidence bytes; do not expose a media URL or log a bot token.""" token = settings.BALE_BOT_TOKEN if token is None else token if not token or not chat_id: return NotificationResult(False, error='توکن یا گروه بله تنظیم نشده است.') field, method = ('photo', 'sendPhoto') if mime_type in ('image/jpeg', 'image/png') else ('document', 'sendDocument') try: response = requests.post(f"{settings.BALE_API_BASE.rstrip('/')}/bot{token}/{method}", data={'chat_id': chat_id, 'caption': caption}, files={field: (filename, photo, mime_type)}, timeout=settings.INTEGRATION_HTTP_TIMEOUT_SECONDS) if not response.ok: return NotificationResult(False, error=f'خطای HTTP بله: {response.status_code}') data = response.json() result = data.get('result') if isinstance(data, dict) else None if data.get('ok') is not True or not isinstance(result, dict) or not result.get('message_id'): return NotificationResult(False, error='بله ارسال تصویر را تأیید نکرد.') return NotificationResult(True, str(result['message_id'])) except (requests.RequestException, ValueError, AttributeError) as error: return NotificationResult(False, error=f'خطا در ارتباط با بله ({type(error).__name__})') def send_message(self, chat_id, text): if not chat_id: return NotificationResult(False, error="Bale chat id is not configured") response = requests.post(f"{settings.BALE_API_BASE.rstrip('/')}/bot{settings.BALE_BOT_TOKEN}/sendMessage", json={"chat_id": chat_id, "text": text, "disable_web_page_preview": True}, timeout=settings.INTEGRATION_HTTP_TIMEOUT_SECONDS) response.raise_for_status(); data = response.json() if not data.get("ok", True): return NotificationResult(False, error=str(data.get("description", "Bale rejected message"))) return NotificationResult(True, str((data.get("result") or {}).get("message_id", "bale"))) def get_bale_provider(): if settings.BALE_BOT_TOKEN: return BaleBotProvider() if settings.DEBUG: return ConsoleBaleProvider() raise ImproperlyConfigured("BALE_BOT_TOKEN is required to deliver Bale notifications in production.")