1# -*- coding: utf-8 -*- 2# 3# Copyright (C) 2019 Chris Caron <lead2gold@gmail.com> 4# All rights reserved. 5# 6# This code is licensed under the MIT License. 7# 8# Permission is hereby granted, free of charge, to any person obtaining a copy 9# of this software and associated documentation files(the "Software"), to deal 10# in the Software without restriction, including without limitation the rights 11# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell 12# copies of the Software, and to permit persons to whom the Software is 13# furnished to do so, subject to the following conditions : 14# 15# The above copyright notice and this permission notice shall be included in 16# all copies or substantial portions of the Software. 17# 18# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 21# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23# OUT OF OR IN CON 24 25import requests 26 27from .NotifyBase import NotifyBase 28from ..common import NotifyImageSize 29from ..common import NotifyType 30from ..utils import parse_bool 31from ..AppriseLocale import gettext_lazy as _ 32from ..utils import validate_regex 33 34 35class NotifyFaast(NotifyBase): 36 """ 37 A wrapper for Faast Notifications 38 """ 39 40 # The default descriptive name associated with the Notification 41 service_name = 'Faast' 42 43 # The services URL 44 service_url = 'http://www.faast.io/' 45 46 # The default protocol (this is secure for faast) 47 protocol = 'faast' 48 49 # A URL that takes you to the setup/help of the specific protocol 50 setup_url = 'https://github.com/caronc/apprise/wiki/Notify_faast' 51 52 # Faast uses the http protocol with JSON requests 53 notify_url = 'https://www.appnotifications.com/account/notifications.json' 54 55 # Allows the user to specify the NotifyImageSize object 56 image_size = NotifyImageSize.XY_72 57 58 # Define object templates 59 templates = ( 60 '{schema}://{authtoken}', 61 ) 62 63 # Define our template tokens 64 template_tokens = dict(NotifyBase.template_tokens, **{ 65 'authtoken': { 66 'name': _('Authorization Token'), 67 'type': 'string', 68 'private': True, 69 'required': True, 70 }, 71 }) 72 73 # Define our template arguments 74 template_args = dict(NotifyBase.template_args, **{ 75 'image': { 76 'name': _('Include Image'), 77 'type': 'bool', 78 'default': True, 79 'map_to': 'include_image', 80 }, 81 }) 82 83 def __init__(self, authtoken, include_image=True, **kwargs): 84 """ 85 Initialize Faast Object 86 """ 87 super(NotifyFaast, self).__init__(**kwargs) 88 89 # Store the Authentication Token 90 self.authtoken = validate_regex(authtoken) 91 if not self.authtoken: 92 msg = 'An invalid Faast Authentication Token ' \ 93 '({}) was specified.'.format(authtoken) 94 self.logger.warning(msg) 95 raise TypeError(msg) 96 97 # Associate an image with our post 98 self.include_image = include_image 99 100 return 101 102 def send(self, body, title='', notify_type=NotifyType.INFO, **kwargs): 103 """ 104 Perform Faast Notification 105 """ 106 107 headers = { 108 'User-Agent': self.app_id, 109 'Content-Type': 'multipart/form-data' 110 } 111 112 # prepare JSON Object 113 payload = { 114 'user_credentials': self.authtoken, 115 'title': title, 116 'message': body, 117 } 118 119 # Acquire our image if we're configured to do so 120 image_url = None if not self.include_image \ 121 else self.image_url(notify_type) 122 123 if image_url: 124 payload['icon_url'] = image_url 125 126 self.logger.debug('Faast POST URL: %s (cert_verify=%r)' % ( 127 self.notify_url, self.verify_certificate, 128 )) 129 self.logger.debug('Faast Payload: %s' % str(payload)) 130 131 # Always call throttle before any remote server i/o is made 132 self.throttle() 133 134 try: 135 r = requests.post( 136 self.notify_url, 137 data=payload, 138 headers=headers, 139 verify=self.verify_certificate, 140 timeout=self.request_timeout, 141 ) 142 if r.status_code != requests.codes.ok: 143 # We had a problem 144 status_str = \ 145 NotifyFaast.http_response_code_lookup(r.status_code) 146 147 self.logger.warning( 148 'Failed to send Faast notification:' 149 '{}{}error={}.'.format( 150 status_str, 151 ', ' if status_str else '', 152 r.status_code)) 153 154 self.logger.debug('Response Details:\r\n{}'.format(r.content)) 155 156 # Return; we're done 157 return False 158 159 else: 160 self.logger.info('Sent Faast notification.') 161 162 except requests.RequestException as e: 163 self.logger.warning( 164 'A Connection error occurred sending Faast notification.', 165 ) 166 self.logger.debug('Socket Exception: %s' % str(e)) 167 168 # Return; we're done 169 return False 170 171 return True 172 173 def url(self, privacy=False, *args, **kwargs): 174 """ 175 Returns the URL built dynamically based on specified arguments. 176 """ 177 178 # Define any URL parameters 179 params = { 180 'image': 'yes' if self.include_image else 'no', 181 } 182 183 # Extend our parameters 184 params.update(self.url_parameters(privacy=privacy, *args, **kwargs)) 185 186 return '{schema}://{authtoken}/?{params}'.format( 187 schema=self.protocol, 188 authtoken=self.pprint(self.authtoken, privacy, safe=''), 189 params=NotifyFaast.urlencode(params), 190 ) 191 192 @staticmethod 193 def parse_url(url): 194 """ 195 Parses the URL and returns enough arguments that can allow 196 us to re-instantiate this object. 197 198 """ 199 results = NotifyBase.parse_url(url, verify_host=False) 200 if not results: 201 # We're done early as we couldn't load the results 202 return results 203 204 # Store our authtoken using the host 205 results['authtoken'] = NotifyFaast.unquote(results['host']) 206 207 # Include image with our post 208 results['include_image'] = \ 209 parse_bool(results['qsd'].get('image', True)) 210 211 return results 212