Usage

Basic Usage

The easiest way to initialize a notifier is via the get_notifier() helper:

>>> import notifiers
>>> pushover = notifiers.get_notifier('pushover')

Or import it directly:

>>> from notifiers.providers.pushover import Pushover
>>> pushover = Pushover()

To send a notification invoke the notify() method:

>>> pushover.notify(apikey='FOO', user='BAR', message='BAZ')

The notifiers.core.Provider.notify() method takes key word arguments based on the provider’s schema. The message key word is used in all notifiers.

Note

You can also send a notification without getting a provider object via the notifiers.core.notify() method:

>>> from notifiers import notify
>>> notify('pushover', apikey='FOO', user='BAR', message='BAZ').

The first argument of the notify() method is the requested provider name. If such does not exist a NoSuchNotifierError exception will be raised.

If there’s a problem with sent key words, a NotifierException will be thrown:

>>> import notifiers
>>> pushover = notifiers.get_notifier('pushover')
>>> pushover.notify(message='FOO')
Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "/Users/liiight/PycharmProjects/notifiers/notifiers/core.py", line 215, in notify
    self._validate_data(kwargs, validator)
  File "/Users/liiight/PycharmProjects/notifiers/notifiers/core.py", line 193, in _validate_data
    raise BadArguments(validation_error=msg, provider=self.name, data=data)
notifiers.exceptions.BadArguments: <NotificationError: Error with sent data: 'user' is a required property>

In this case, a BadArguments exception was thrown since not all required key words were sent.

Provider schema

Notifier’s schema is constructed with JSON Schema. Some understanding of it is needed in order to correctly construct the notification correctly. To see provider schema, use the schema property:

>>> pushover.schema
{'type': 'object', 'properties': {'user': {'oneOf': [{'type': 'array', 'items': {'type': 'string', 'title': 'the user/group key (not e-mail address) of your user (or you)'}, 'minItems': 1, 'uniqueItems': True}, {'type': 'string', 'title': 'the user/group key (not e-mail address) of your user (or you)'}]}, 'message': {'type': 'string', 'title': 'your message'}, 'title': {'type': 'string', 'title': "your message's title, otherwise your app's name is used"}, 'token': {'type': 'string', 'title': "your application's API token"}, 'device': {'oneOf': [{'type': 'array', 'items': {'type': 'string', 'title': "your user's device name to send the message directly to that device"}, 'minItems': 1, 'uniqueItems': True}, {'type': 'string', 'title': "your user's device name to send the message directly to that device"}]}, 'priority': {'type': 'number', 'minimum': -2, 'maximum': 2, 'title': 'notification priority'}, 'url': {'type': 'string', 'format': 'uri', 'title': 'a supplementary URL to show with your message'}, 'url_title': {'type': 'string', 'title': 'a title for your supplementary URL, otherwise just the URL is shown'}, 'sound': {'type': 'string', 'title': "the name of one of the sounds supported by device clients to override the user's default sound choice", 'enum': ['pushover', 'bike', 'bugle', 'cashregister', 'classical', 'cosmic', 'falling', 'gamelan', 'incoming', 'intermission', 'magic', 'mechanical', 'pianobar', 'siren', 'spacealarm', 'tugboat', 'alien', 'climb', 'persistent', 'echo', 'updown', 'none']}, 'timestamp': {'type': 'integer', 'minimum': 0, 'title': "a Unix timestamp of your message's date and time to display to the user, rather than the time your message is received by our API"}, 'retry': {'type': 'integer', 'minimum': 30, 'title': 'how often (in seconds) the Pushover servers will send the same notification to the user. priority must be set to 2'}, 'expire': {'type': 'integer', 'maximum': 86400, 'title': 'how many seconds your notification will continue to be retried for. priority must be set to 2'}, 'callback': {'type': 'string', 'format': 'uri', 'title': 'a publicly-accessible URL that our servers will send a request to when the user has acknowledged your notification. priority must be set to 2'}, 'html': {'type': 'integer', 'minimum': 0, 'maximum': 1, 'title': 'enable HTML formatting'}}, 'additionalProperties': False, 'required': ['user', 'message', 'token']}

To see the required schema use the required property:

>>> pushover.required
{'required': ['user', 'message', 'token']}

The reply is always a dict which represent the validation of the schema. In this case it’s pretty straightforward, but it can be more complex at times:

>>> hipchat = notifiers.get_notifier('hipchat')
>>> hipchat.required
{'allOf': [{'required': ['message', 'id', 'token']}, {'oneOf': [{'required': ['room']}, {'required': ['user']}], 'error_oneOf': "Only one of 'room' or 'user' is allowed"}, {'oneOf': [{'required': ['group']}, {'required': ['team_server']}], 'error_oneOf': "Only one 'group' or 'team_server' is allowed"}]}

Hipchat’s validation requires message, id and token are sent, exactly one of of room or user and exactly one of group or team_server.

To get all of the schema properties, which correlates to the key words it can handle, use arguments:

>>> pushover.arguments
{'user': {'oneOf': [{'type': 'array', 'items': {'type': 'string', 'title': 'the user/group key (not e-mail address) of your user (or you)'}, 'minItems': 1, 'uniqueItems': True}, {'type': 'string', 'title': 'the user/group key (not e-mail address) of your user (or you)'}]}, 'message': {'type': 'string', 'title': 'your message'}, 'title': {'type': 'string', 'title': "your message's title, otherwise your app's name is used"}, 'token': {'type': 'string', 'title': "your application's API token"}, 'device': {'oneOf': [{'type': 'array', 'items': {'type': 'string', 'title': "your user's device name to send the message directly to that device"}, 'minItems': 1, 'uniqueItems': True}, {'type': 'string', 'title': "your user's device name to send the message directly to that device"}]}, 'priority': {'type': 'number', 'minimum': -2, 'maximum': 2, 'title': 'notification priority'}, 'url': {'type': 'string', 'format': 'uri', 'title': 'a supplementary URL to show with your message'}, 'url_title': {'type': 'string', 'title': 'a title for your supplementary URL, otherwise just the URL is shown'}, 'sound': {'type': 'string', 'title': "the name of one of the sounds supported by device clients to override the user's default sound choice", 'enum': ['pushover', 'bike', 'bugle', 'cashregister', 'classical', 'cosmic', 'falling', 'gamelan', 'incoming', 'intermission', 'magic', 'mechanical', 'pianobar', 'siren', 'spacealarm', 'tugboat', 'alien', 'climb', 'persistent', 'echo', 'updown', 'none']}, 'timestamp': {'type': 'integer', 'minimum': 0, 'title': "a Unix timestamp of your message's date and time to display to the user, rather than the time your message is received by our API"}, 'retry': {'type': 'integer', 'minimum': 30, 'title': 'how often (in seconds) the Pushover servers will send the same notification to the user. priority must be set to 2'}, 'expire': {'type': 'integer', 'maximum': 86400, 'title': 'how many seconds your notification will continue to be retried for. priority must be set to 2'}, 'callback': {'type': 'string', 'format': 'uri', 'title': 'a publicly-accessible URL that our servers will send a request to when the user has acknowledged your notification. priority must be set to 2'}, 'html': {'type': 'integer', 'minimum': 0, 'maximum': 1, 'title': 'enable HTML formatting'}}

Environment variables

You can set environment variable to replace any argument that the notifier can use. The default syntax to follow is NOTIFIERS_[PROVIDER_NAME]_[ARGUMENT_NAME]:

$ export NOTIFIERS_PUSHOVER_TOKEN=FOO
$ export NOTIFIERS_PUSHOVER_USER=BAR

Then you could just use:

>>> p.notify(message='message')

Note that you can also set MESSAGE in an environment variable. You can also change the default prefix of NOTIFIERS_ by pass the env_prefix argument on notify:

>>> p.notify(message='test', env_prefix='MY_OWN_PREFIX_')

Provider resources

Some provider have helper method to enable fetching relevant resources (like rooms, users etc.) To get a list of provider resources use the notifiers.core.Provider.resources() property:

>>> telegram.resources
['updates']

Resource share almost all of their functionality with the Provider class, as they have a schema as well:

>>> telegram.updates
<ProviderResource,provider=telegram,resource=updates>
>>> telegram.updates.schema
{'type': 'object', 'properties': {'token': {'type': 'string', 'title': 'Bot token'}}, 'additionalProperties': False, 'required': ['token']}

To invoke the resource, just call it:

>>> telegram.updates()
Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "/Users/orcarmi/PycharmProjects/notifiers/notifiers/core.py", line 278, in __call__
    data = self._process_data(**kwargs)
  File "/Users/orcarmi/PycharmProjects/notifiers/notifiers/core.py", line 204, in _process_data
    self._validate_data(data, validator)
  File "/Users/orcarmi/PycharmProjects/notifiers/notifiers/core.py", line 175, in _validate_data
    raise BadArguments(validation_error=msg, provider=self.name, data=data)
notifiers.exceptions.BadArguments: Error with sent data: 'token' is a required property

Oops, forgot to send token. Let’s try again:

>>> telegram.updates(token='foo')
[{'update_id': REDACTED, 'message': {'message_id': REDACTED, 'from': {'id': REDACTED, 'is_bot': False, 'first_name': 'REDACTED', 'last_name': 'REDACTED', 'username': 'REDACTED', 'language_code': 'en-US'}, 'chat': {'id': REDACTED, 'first_name': 'REDACTED', 'last_name': 'REDACTED', 'username': 'REDACTED', 'type': 'private'}, 'date': 1516178366, 'text': 'Ccc'}}]

As can be expected, each provider resource returns a completely different response that correlates to the underlying API command it wraps. In this example, by invoking the notifiers.providers.telegram.Telegram.updates() method, you get a response that shows you which active chat IDs your telegram bot token can send to.

Handling errors

There are two base types of errors in Notifiers, data (or schema) related errors and notification related errors. The former can present as so:

>>> import notifiers
>>> pushover = notifiers.get_notifier('pushover')
>>> pushover.notify(message='FOO')
Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "/Users/liiight/PycharmProjects/notifiers/notifiers/core.py", line 215, in notify
    self._validate_data(kwargs, validator)
  File "/Users/liiight/PycharmProjects/notifiers/notifiers/core.py", line 193, in _validate_data
    raise BadArguments(validation_error=msg, provider=self.name, data=data)
notifiers.exceptions.BadArguments: <NotificationError: Error with sent data: 'user' is a required property>

Here we see that an BadArguments exception was raised instantly, since not all required values were sent. Another example:

>>> pushover.notify(message='FOO', token='TOKEN', user='USER', attachment='/foo')
Traceback (most recent call last):
  File "/Users/orcarmi/PycharmProjects/notifiers/poc.py", line 50, in <module>
    raise_on_errors=True)
  File "/Users/orcarmi/PycharmProjects/notifiers/notifiers/core.py", line 273, in notify
    data = self._process_data(**kwargs)
  File "/Users/orcarmi/PycharmProjects/notifiers/notifiers/core.py", line 203, in _process_data
    self._validate_data(data)
  File "/Users/orcarmi/PycharmProjects/notifiers/notifiers/core.py", line 176, in _validate_data
    raise BadArguments(validation_error=msg, provider=self.name, data=data)
notifiers.exceptions.BadArguments: Error with sent data: 'foo' is not a 'valid_file'

Some values have both type and format set in their schema, which enforces a specific logic. Here we can see that the schema for pushover’s attachment attribute has format set to valid_file which check that the file is present.

There are also notification based errors:
>>> rsp = pushover.notify(message='FOO', token='BAD TOKEN', user='USER')
>>> rsp.raise_on_errors()
Traceback (most recent call last):
  File "/Users/orcarmi/PycharmProjects/notifiers/poc.py", line 49, in <module>
    raise_on_errors=True)
  File "/Users/orcarmi/PycharmProjects/notifiers/notifiers/core.py", line 276, in notify
    rsp.raise_on_errors()
  File "/Users/orcarmi/PycharmProjects/notifiers/notifiers/core.py", line 48, in raise_on_errors
    raise NotificationError(provider=self.provider, data=self.data, errors=self.errors, response=self.response)
notifiers.exceptions.NotificationError: Notification errors: application token is invalid

Note the default behaviour for Response is not to raise exception on error. You can either use the raise_on_errors() method, or pass raise_on_errors=True to the notification command:

>>> pushover.notify(message='FOO', token='BAD TOKEN', user='USER', raise_on_errors=True)
Traceback (most recent call last):
  File "/Users/orcarmi/PycharmProjects/notifiers/poc.py", line 49, in <module>
    raise_on_errors=True)
  File "/Users/orcarmi/PycharmProjects/notifiers/notifiers/core.py", line 276, in notify
    rsp.raise_on_errors()
  File "/Users/orcarmi/PycharmProjects/notifiers/notifiers/core.py", line 48, in raise_on_errors
    raise NotificationError(provider=self.provider, data=self.data, errors=self.errors, response=self.response)
notifiers.exceptions.NotificationError: Notification errors: application token is invalid

You can also use the ok property:

>>> rsp = pushover.notify(message='FOO', token='BAD TOKEN', user='USER')
>>> rsp.ok
False
>>> rsp.errors
['application token is invalid']