from typing import Dict, Optional, Any import logging import requests # type: ignore import io import os from enum import IntEnum import requests from rest_framework.serializers import ValidationError from lib import syndicate from notifications.plugin import ( BaseNotificationPlugin, FailureAlertContext, PrinterNotificationContext, TestMessageContext, ) LOGGER = logging.getLogger(__name__) class PushoverException(Exception): pass class PushoverPriority(IntEnum): # See https://pushover.net/api#priority for more details on pushover notification priority LOWEST = -2 # Don't show any notification LOW = -1 # Show a quiet (non-alerting) notification NORMAL = 0 # Show a normal notification that respects a user's quiet hours HIGH = 1 # Show a high-priority notification that bypasses a user's quiet hours EMERGENCY = 2 # Show a high-priority notification that requires user confirmation class PushOverNotificationPlugin(BaseNotificationPlugin): def validate_config(self, data: Dict) -> Dict: if 'user_key' in data: user_key = data['user_key'].strip() return {'user_key': user_key} raise ValidationError('user_key is missing from config') def env_vars(self) -> Dict: return { 'PUSHOVER_APP_TOKEN': { 'is_required': True, 'is_set': 'PUSHOVER_APP_TOKEN' in os.environ, }, } def get_user_key_from_config(self, config: Dict) -> str: if config and 'user_key' in config: return config['user_key'] return '' def i(self, s: str) -> str: return f"{s}" def b(self, s: str): return f"{s}" def u(self, s: str): return f"{s}" def call_pushover( self, token: str, user_key: str, title: str, message: str, priority: Optional[PushoverPriority] = None, file_content: Optional[bytes] = None, timeout: float = 5.0, ) -> None: API_URL = "https://api.pushover.net/1/messages.json" RETRY = 60 # seconds EXPIRE = 120 # seconds if len(message) > 1024: raise PushoverException("Maximum message size of 1024 characters exceeded!") if title and len(title) > 250: raise PushoverException("Maximum title size of 250 characters exceeded!") payload: Dict[str, Any] = { "token": token, "user": user_key, "message": message, "html": 1, } if title: payload["title"] = title if priority: payload["priority"] = int(priority) if priority == PushoverPriority.EMERGENCY: # retry every RETRY seconds for EXPIRE seconds total payload["retry"] = RETRY payload["expire"] = EXPIRE files = None if file_content: files = { "attachment": io.BytesIO(file_content) } req = requests.post(API_URL, data=payload, files=files, timeout=timeout) req.raise_for_status() def send_failure_alert(self, context: FailureAlertContext) -> None: if not os.environ.get('PUSHOVER_APP_TOKEN'): return user_key = self.get_user_key_from_config(context.config) if not user_key: return link = syndicate.build_full_url_for_syndicate('/', context.user.syndicate_name), title = self.get_failure_alert_title(context=context, link=link) text = self.get_failure_alert_text(context=context, link=link) if not title or not text: return try: file_content = requests.get(context.img_url).content except: file_content = None self.call_pushover( token=os.environ.get('PUSHOVER_APP_TOKEN', ''), user_key=user_key, priority=PushoverPriority.HIGH, title=title, message=text, file_content=file_content, ) def send_printer_notification(self, context: PrinterNotificationContext) -> None: if not os.environ.get('PUSHOVER_APP_TOKEN'): return user_key = self.get_user_key_from_config(context.config) if not user_key: return title = self.get_printer_notification_title(context=context) text = self.get_printer_notification_text(context=context) if not title or not text: return try: file_content = requests.get(context.img_url).content except: file_content = None self.call_pushover( token=os.environ.get('PUSHOVER_APP_TOKEN', ''), user_key=user_key, title=title, message=text, file_content=file_content, ) def send_test_message(self, context: TestMessageContext) -> None: user_key = self.get_user_key_from_config(context.config) self.call_pushover( token=os.environ.get('PUSHOVER_APP_TOKEN') or '', user_key=user_key, title='Test Notification', message='It works!', ) def __load_plugin__(): return PushOverNotificationPlugin()