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