massive refactoring; pep8 enhancments refs #1
@ -1,18 +1,43 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# Apprise Core
|
||||||
|
#
|
||||||
|
# Copyright (C) 2017 Chris Caron <lead2gold@gmail.com>
|
||||||
|
#
|
||||||
|
# This file is part of apprise.
|
||||||
|
#
|
||||||
|
# apprise is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# apprise is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with apprise. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import re
|
import re
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from .common import NotifyType
|
||||||
|
from .common import NOTIFY_TYPES
|
||||||
|
from .utils import parse_list
|
||||||
|
|
||||||
|
from .AppriseAsset import AppriseAsset
|
||||||
|
|
||||||
from . import plugins
|
from . import plugins
|
||||||
from .Utils import parse_url
|
|
||||||
from .Utils import parse_list
|
|
||||||
from .Utils import parse_bool
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
# Build a list of supported plugins
|
# Build a list of supported plugins
|
||||||
SCHEMA_MAP = {}
|
SCHEMA_MAP = {}
|
||||||
|
|
||||||
|
# Used for attempting to acquire the schema if the URL can't be parsed.
|
||||||
|
GET_SCHEMA_RE = re.compile('\s*(?P<schema>[a-z0-9]+)://.*$', re.I)
|
||||||
|
|
||||||
|
|
||||||
# Load our Lookup Matrix
|
# Load our Lookup Matrix
|
||||||
def __load_matrix():
|
def __load_matrix():
|
||||||
@ -23,21 +48,34 @@ def __load_matrix():
|
|||||||
"""
|
"""
|
||||||
# to add it's mapping to our hash table
|
# to add it's mapping to our hash table
|
||||||
for entry in dir(plugins):
|
for entry in dir(plugins):
|
||||||
|
|
||||||
# Get our plugin
|
# Get our plugin
|
||||||
plugin = getattr(plugins, entry)
|
plugin = getattr(plugins, entry)
|
||||||
|
|
||||||
proto = getattr(plugin, 'PROTOCOL', None)
|
# Load protocol(s) if defined
|
||||||
protos = getattr(plugin, 'SECURE_PROTOCOL', None)
|
proto = getattr(plugin, 'protocol', None)
|
||||||
if not proto:
|
if isinstance(proto, basestring):
|
||||||
# Must have at least PROTOCOL defined
|
|
||||||
continue
|
|
||||||
|
|
||||||
if proto not in SCHEMA_MAP:
|
if proto not in SCHEMA_MAP:
|
||||||
SCHEMA_MAP[proto] = plugin
|
SCHEMA_MAP[proto] = plugin
|
||||||
|
|
||||||
if protos and protos not in SCHEMA_MAP:
|
elif isinstance(proto, (set, list, tuple)):
|
||||||
|
# Support iterables list types
|
||||||
|
for p in proto:
|
||||||
|
if p not in SCHEMA_MAP:
|
||||||
|
SCHEMA_MAP[p] = plugin
|
||||||
|
|
||||||
|
# Load secure protocol(s) if defined
|
||||||
|
protos = getattr(plugin, 'secure_protocol', None)
|
||||||
|
if isinstance(protos, basestring):
|
||||||
|
if protos not in SCHEMA_MAP:
|
||||||
SCHEMA_MAP[protos] = plugin
|
SCHEMA_MAP[protos] = plugin
|
||||||
|
|
||||||
|
if isinstance(protos, (set, list, tuple)):
|
||||||
|
# Support iterables list types
|
||||||
|
for p in protos:
|
||||||
|
if p not in SCHEMA_MAP:
|
||||||
|
SCHEMA_MAP[p] = plugin
|
||||||
|
|
||||||
|
|
||||||
# Dynamically build our module
|
# Dynamically build our module
|
||||||
__load_matrix()
|
__load_matrix()
|
||||||
@ -48,25 +86,40 @@ class Apprise(object):
|
|||||||
Our Notification Manager
|
Our Notification Manager
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def __init__(self, servers=None):
|
def __init__(self, servers=None, asset=None):
|
||||||
"""
|
"""
|
||||||
Loads a set of server urls
|
Loads a set of server urls while applying the Asset() module to each
|
||||||
|
if specified.
|
||||||
|
|
||||||
|
If no asset is provided, then the default asset is used.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Initialize a server list of URLs
|
# Initialize a server list of URLs
|
||||||
self.servers = list()
|
self.servers = list()
|
||||||
|
|
||||||
|
# Assigns an central asset object that will be later passed into each
|
||||||
|
# notification plugin. Assets contain information such as the local
|
||||||
|
# directory images can be found in. It can also identify remote
|
||||||
|
# URL paths that contain the images you want to present to the end
|
||||||
|
# user. If no asset is specified, then the default one is used.
|
||||||
|
self.asset = asset
|
||||||
|
if asset is None:
|
||||||
|
# Load our default configuration
|
||||||
|
self.asset = AppriseAsset()
|
||||||
|
|
||||||
if servers:
|
if servers:
|
||||||
self.add(servers)
|
self.add(servers)
|
||||||
|
|
||||||
def add(self, servers, include_image=True, image_url=None,
|
def add(self, servers, asset=None):
|
||||||
image_path=None):
|
|
||||||
"""
|
"""
|
||||||
Adds one or more server URLs into our list.
|
Adds one or more server URLs into our list.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# Initialize our return status
|
||||||
|
return_status = True
|
||||||
|
|
||||||
servers = parse_list(servers)
|
servers = parse_list(servers)
|
||||||
for _server in servers:
|
for _server in servers:
|
||||||
|
|
||||||
@ -75,87 +128,58 @@ class Apprise(object):
|
|||||||
# pushbullet)
|
# pushbullet)
|
||||||
_server = _server.replace('/#', '/%23')
|
_server = _server.replace('/#', '/%23')
|
||||||
|
|
||||||
|
# Attempt to acquire the schema at the very least to allow
|
||||||
|
# our plugins to determine if they can make a better
|
||||||
|
# interpretation of a URL geared for them anyway.
|
||||||
|
schema = GET_SCHEMA_RE.match(_server)
|
||||||
|
if schema is None:
|
||||||
|
logger.error(
|
||||||
|
'%s is an unparseable server url.' % _server,
|
||||||
|
)
|
||||||
|
return_status = False
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Update the schema
|
||||||
|
schema = schema.group('schema').lower()
|
||||||
|
|
||||||
|
# Some basic validation
|
||||||
|
if schema not in SCHEMA_MAP:
|
||||||
|
logger.error(
|
||||||
|
'%s is not a supported server type.' % schema,
|
||||||
|
)
|
||||||
|
return_status = False
|
||||||
|
continue
|
||||||
|
|
||||||
# Parse our url details
|
# Parse our url details
|
||||||
# the server object is a dictionary containing all of the
|
# the server object is a dictionary containing all of the
|
||||||
# information parsed from our URL
|
# information parsed from our URL
|
||||||
server = parse_url(_server, default_schema='unknown')
|
results = SCHEMA_MAP[schema].parse_url(_server)
|
||||||
|
|
||||||
# Initialize our return status
|
if not results:
|
||||||
return_status = True
|
# Failed to parse the server URL
|
||||||
|
logger.error('Could not parse URL: %s' % _server)
|
||||||
if not server:
|
|
||||||
# This is a dirty hack; but it's the only work around to
|
|
||||||
# tgram:// messages since the bot_token has a colon in it.
|
|
||||||
# It invalidates an normal URL.
|
|
||||||
|
|
||||||
# This hack searches for this bogus URL and corrects it
|
|
||||||
# so we can properly load it further down. The other
|
|
||||||
# alternative is to ask users to actually change the colon
|
|
||||||
# into a slash (which will work too), but it's more likely
|
|
||||||
# to cause confusion... So this is the next best thing
|
|
||||||
tgram = re.match(
|
|
||||||
r'(?P<protocol>%s://)(bot)?(?P<prefix>([a-z0-9_-]+)'
|
|
||||||
r'(:[a-z0-9_-]+)?@)?(?P<btoken_a>[0-9]+):+'
|
|
||||||
r'(?P<remaining>.*)$' % 'tgram',
|
|
||||||
_server, re.I)
|
|
||||||
|
|
||||||
if tgram:
|
|
||||||
if tgram.group('prefix'):
|
|
||||||
server = self.parse_url('%s%s%s/%s' % (
|
|
||||||
tgram.group('protocol'),
|
|
||||||
tgram.group('prefix'),
|
|
||||||
tgram.group('btoken_a'),
|
|
||||||
tgram.group('remaining'),
|
|
||||||
),
|
|
||||||
default_schema='unknown',
|
|
||||||
)
|
|
||||||
|
|
||||||
else:
|
|
||||||
server = self.parse_url('%s%s/%s' % (
|
|
||||||
tgram.group('protocol'),
|
|
||||||
tgram.group('btoken_a'),
|
|
||||||
tgram.group('remaining'),
|
|
||||||
),
|
|
||||||
default_schema='unknown',
|
|
||||||
)
|
|
||||||
|
|
||||||
if not server:
|
|
||||||
# Failed to parse te server
|
|
||||||
self.logger.error('Could not parse URL: %s' % server)
|
|
||||||
return_status = False
|
return_status = False
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Some basic validation
|
try:
|
||||||
if server['schema'] not in SCHEMA_MAP:
|
# Attempt to create an instance of our plugin using the parsed
|
||||||
self.logger.error(
|
# URL information
|
||||||
'%s is not a supported server type.' %
|
plugin = SCHEMA_MAP[results['schema']](**results)
|
||||||
server['schema'].upper(),
|
|
||||||
)
|
|
||||||
return_status = False
|
|
||||||
continue
|
|
||||||
|
|
||||||
notify_args = server.copy().items() + {
|
except:
|
||||||
# Logger Details
|
|
||||||
'logger': self.logger,
|
|
||||||
# Base
|
|
||||||
'include_image': include_image,
|
|
||||||
'secure': (server['schema'][-1] == 's'),
|
|
||||||
# Support SSL Certificate 'verify' keyword
|
|
||||||
# Default to being enabled (True)
|
|
||||||
'verify': parse_bool(server['qsd'].get('verify', True)),
|
|
||||||
# Overrides
|
|
||||||
'override_image_url': image_url,
|
|
||||||
'override_image_path': image_path,
|
|
||||||
}.items()
|
|
||||||
|
|
||||||
# Grant our plugin access to manipulate the dictionary
|
|
||||||
if not SCHEMA_MAP[server['schema']].pre_parse(notify_args):
|
|
||||||
# the arguments are invalid or can not be used.
|
# the arguments are invalid or can not be used.
|
||||||
return_status = False
|
return_status = False
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Add our entry to our list as it can be actioned at this point
|
# Save our asset
|
||||||
self.servers.add(notify_args)
|
if asset:
|
||||||
|
plugin.asset = asset
|
||||||
|
|
||||||
|
else:
|
||||||
|
plugin.asset = self.asset
|
||||||
|
|
||||||
|
# Add our initialized plugin to our server listings
|
||||||
|
self.servers.append(plugin)
|
||||||
|
|
||||||
# Return our status
|
# Return our status
|
||||||
return return_status
|
return return_status
|
||||||
@ -167,9 +191,38 @@ class Apprise(object):
|
|||||||
"""
|
"""
|
||||||
self.servers.clear()
|
self.servers.clear()
|
||||||
|
|
||||||
def notify(self, title='', body=''):
|
def notify(self, title, body, notify_type=NotifyType.SUCCESS, **kwargs):
|
||||||
|
"""
|
||||||
|
This should be over-rided by the class that inherits this one.
|
||||||
"""
|
"""
|
||||||
Notifies all loaded servers using the content provided.
|
|
||||||
|
|
||||||
"""
|
# Initialize our return result
|
||||||
# TODO: iterate over server entries and execute notification
|
status = len(self.servers) > 0
|
||||||
|
|
||||||
|
if notify_type and notify_type not in NOTIFY_TYPES:
|
||||||
|
self.warning(
|
||||||
|
'An invalid notification type (%s) was specified.' % (
|
||||||
|
notify_type))
|
||||||
|
|
||||||
|
if not isinstance(body, basestring):
|
||||||
|
body = ''
|
||||||
|
|
||||||
|
if not isinstance(title, basestring):
|
||||||
|
title = ''
|
||||||
|
|
||||||
|
# Iterate over our loaded plugins
|
||||||
|
for server in self.servers:
|
||||||
|
try:
|
||||||
|
# Send notification
|
||||||
|
if not server.notify(title=title, body=body):
|
||||||
|
|
||||||
|
# Toggle our return status flag
|
||||||
|
status = False
|
||||||
|
|
||||||
|
except:
|
||||||
|
# A catch all so we don't have to abort early
|
||||||
|
# just because one of our plugins has a bug in it.
|
||||||
|
# TODO: print backtrace
|
||||||
|
status = False
|
||||||
|
|
||||||
|
return status
|
||||||
|
146
apprise/AppriseAsset.py
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# Apprise Asset
|
||||||
|
#
|
||||||
|
# Copyright (C) 2017 Chris Caron <lead2gold@gmail.com>
|
||||||
|
#
|
||||||
|
# This file is part of apprise.
|
||||||
|
#
|
||||||
|
# apprise is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# apprise is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with apprise. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
|
from os.path import join
|
||||||
|
from os.path import dirname
|
||||||
|
from os.path import isfile
|
||||||
|
from os.path import abspath
|
||||||
|
from .common import NotifyType
|
||||||
|
|
||||||
|
|
||||||
|
class AppriseAsset(object):
|
||||||
|
"""
|
||||||
|
Provides a supplimentary class that can be used to provide extra
|
||||||
|
information and details that can be used by Apprise such as providing
|
||||||
|
an alternate location to where images/icons can be found and the
|
||||||
|
URL masks.
|
||||||
|
|
||||||
|
"""
|
||||||
|
# A Simple Mapping of Colors; For every NOTIFY_TYPE identified,
|
||||||
|
# there should be a mapping to it's color here:
|
||||||
|
html_notify_map = {
|
||||||
|
NotifyType.INFO: '#3AA3E3',
|
||||||
|
NotifyType.SUCCESS: '#3AA337',
|
||||||
|
NotifyType.FAILURE: '#A32037',
|
||||||
|
NotifyType.WARNING: '#CACF29',
|
||||||
|
}
|
||||||
|
|
||||||
|
# The default theme
|
||||||
|
theme = 'default'
|
||||||
|
|
||||||
|
# Image URL Mask
|
||||||
|
image_url_mask = \
|
||||||
|
'http://nuxref.com/apprise/themes/{THEME}/apprise-{TYPE}-{XY}.png'
|
||||||
|
|
||||||
|
# Image Path Mask
|
||||||
|
image_path_mask = abspath(join(
|
||||||
|
dirname(__file__),
|
||||||
|
'assets',
|
||||||
|
'themes',
|
||||||
|
'{THEME}',
|
||||||
|
'apprise-{TYPE}-{XY}.png',
|
||||||
|
))
|
||||||
|
|
||||||
|
def __init__(self, image_path_mask=None, image_url_mask=None, theme=None):
|
||||||
|
"""
|
||||||
|
Asset Initialization
|
||||||
|
|
||||||
|
"""
|
||||||
|
if theme:
|
||||||
|
self.theme = theme
|
||||||
|
|
||||||
|
if image_path_mask:
|
||||||
|
self.image_path_mask = image_path_mask
|
||||||
|
|
||||||
|
if image_url_mask:
|
||||||
|
self.image_url_mask = image_url_mask
|
||||||
|
|
||||||
|
def html_color(self, notify_type):
|
||||||
|
"""
|
||||||
|
Returns an HTML mapped color based on passed in notify type
|
||||||
|
"""
|
||||||
|
# Attempt to get the type, otherwise return a default grey
|
||||||
|
# if we couldn't look up the entry
|
||||||
|
return self.html_notify_map.get(notify_type, '#888888')
|
||||||
|
|
||||||
|
def image_url(self, notify_type, image_size):
|
||||||
|
"""
|
||||||
|
Apply our mask to our image URL
|
||||||
|
|
||||||
|
"""
|
||||||
|
re_map = {
|
||||||
|
'{THEME}': self.theme,
|
||||||
|
'{TYPE}': notify_type,
|
||||||
|
'{XY}': image_size,
|
||||||
|
}
|
||||||
|
|
||||||
|
# Iterate over above list and store content accordingly
|
||||||
|
re_table = re.compile(
|
||||||
|
r'(' + '|'.join(re_map.keys()) + r')',
|
||||||
|
re.IGNORECASE,
|
||||||
|
)
|
||||||
|
|
||||||
|
return re_table.sub(lambda x: re_map[x.group()], self.image_url_mask)
|
||||||
|
|
||||||
|
def image_path(self, notify_type, image_size, must_exist=True):
|
||||||
|
"""
|
||||||
|
Apply our mask to our image file path
|
||||||
|
|
||||||
|
"""
|
||||||
|
re_map = {
|
||||||
|
'{THEME}': self.theme,
|
||||||
|
'{TYPE}': notify_type,
|
||||||
|
'{XY}': image_size,
|
||||||
|
}
|
||||||
|
|
||||||
|
# Iterate over above list and store content accordingly
|
||||||
|
re_table = re.compile(
|
||||||
|
r'(' + '|'.join(re_map.keys()) + r')',
|
||||||
|
re.IGNORECASE,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Acquire our path
|
||||||
|
path = re_table.sub(lambda x: re_map[x.group()], self.image_path_mask)
|
||||||
|
if must_exist and not isfile(path):
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Return what we parsed
|
||||||
|
return path
|
||||||
|
|
||||||
|
def image_raw(self, notify_type, image_size):
|
||||||
|
"""
|
||||||
|
Returns the raw image if it can (otherwise the function returns None)
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
path = self.image_path(notify_type=notify_type, image_size=image_size)
|
||||||
|
if path:
|
||||||
|
try:
|
||||||
|
with open(path, 'rb') as fd:
|
||||||
|
return fd.read()
|
||||||
|
|
||||||
|
except (OSError, IOError):
|
||||||
|
# We can't access the file
|
||||||
|
pass
|
||||||
|
|
||||||
|
return None
|
@ -1,8 +1,8 @@
|
|||||||
# -*- encoding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Supported Push Notifications Libraries
|
# base class for easier library inclusion
|
||||||
#
|
#
|
||||||
# Copyright (C) 2014-2017 Chris Caron <lead2gold@gmail.com>
|
# Copyright (C) 2017 Chris Caron <lead2gold@gmail.com>
|
||||||
#
|
#
|
||||||
# This file is part of apprise.
|
# This file is part of apprise.
|
||||||
#
|
#
|
||||||
@ -19,12 +19,23 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with apprise. If not, see <http://www.gnu.org/licenses/>.
|
# along with apprise. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
from .common import NotifyType
|
||||||
|
from .common import NOTIFY_TYPES
|
||||||
|
from .common import NOTIFY_IMAGE_SIZES
|
||||||
|
from .common import NotifyImageSize
|
||||||
|
from .plugins.NotifyBase import NotifyFormat
|
||||||
|
|
||||||
from .Apprise import Apprise
|
from .Apprise import Apprise
|
||||||
|
from .AppriseAsset import AppriseAsset
|
||||||
|
|
||||||
__version__ = '0.0.1'
|
__version__ = '0.0.1'
|
||||||
__author__ = 'Chris Caron <lead2gold@gmail.com>'
|
__author__ = 'Chris Caron <lead2gold@gmail.com>'
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
# Core
|
# Core
|
||||||
'Apprise',
|
'Apprise', 'AppriseAsset',
|
||||||
|
|
||||||
|
# Reference
|
||||||
|
'NotifyType', 'NotifyImageSize', 'NotifyFormat', 'NOTIFY_TYPES',
|
||||||
|
'NOTIFY_IMAGE_SIZES',
|
||||||
]
|
]
|
||||||
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 41 KiB |
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 7.5 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 7.7 KiB After Width: | Height: | Size: 7.7 KiB |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 7.8 KiB After Width: | Height: | Size: 7.8 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 7.7 KiB After Width: | Height: | Size: 7.7 KiB |
56
apprise/common.py
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# Base Notify Wrapper
|
||||||
|
#
|
||||||
|
# Copyright (C) 2017 Chris Caron <lead2gold@gmail.com>
|
||||||
|
#
|
||||||
|
# This file is part of apprise.
|
||||||
|
#
|
||||||
|
# apprise is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# apprise is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with apprise. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
class NotifyType(object):
|
||||||
|
"""
|
||||||
|
A simple mapping of notification types most commonly used with
|
||||||
|
all types of logging and notification services.
|
||||||
|
"""
|
||||||
|
INFO = 'info'
|
||||||
|
SUCCESS = 'success'
|
||||||
|
FAILURE = 'failure'
|
||||||
|
WARNING = 'warning'
|
||||||
|
|
||||||
|
|
||||||
|
NOTIFY_TYPES = (
|
||||||
|
NotifyType.INFO,
|
||||||
|
NotifyType.SUCCESS,
|
||||||
|
NotifyType.FAILURE,
|
||||||
|
NotifyType.WARNING,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class NotifyImageSize(object):
|
||||||
|
"""
|
||||||
|
A list of pre-defined image sizes to make it easier to work with defined
|
||||||
|
plugins.
|
||||||
|
"""
|
||||||
|
XY_72 = '72x72'
|
||||||
|
XY_128 = '128x128'
|
||||||
|
XY_256 = '256x256'
|
||||||
|
|
||||||
|
|
||||||
|
NOTIFY_IMAGE_SIZES = (
|
||||||
|
NotifyImageSize.XY_72,
|
||||||
|
NotifyImageSize.XY_128,
|
||||||
|
NotifyImageSize.XY_256,
|
||||||
|
)
|
@ -1,8 +1,8 @@
|
|||||||
# -*- encoding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Base Notify Wrapper
|
# Base Notify Wrapper
|
||||||
#
|
#
|
||||||
# Copyright (C) 2014-2017 Chris Caron <lead2gold@gmail.com>
|
# Copyright (C) 2017 Chris Caron <lead2gold@gmail.com>
|
||||||
#
|
#
|
||||||
# This file is part of apprise.
|
# This file is part of apprise.
|
||||||
#
|
#
|
||||||
@ -19,20 +19,22 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with apprise. If not, see <http://www.gnu.org/licenses/>.
|
# along with apprise. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from time import sleep
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
import markdown
|
import markdown
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
from time import sleep
|
||||||
from os.path import join
|
from urllib import unquote as _unquote
|
||||||
from os.path import dirname
|
|
||||||
from os.path import abspath
|
|
||||||
|
|
||||||
# For conversion
|
# For conversion
|
||||||
from chardet import detect as chardet_detect
|
from chardet import detect as chardet_detect
|
||||||
|
|
||||||
|
from ..utils import parse_url
|
||||||
|
from ..utils import parse_bool
|
||||||
|
from ..common import NOTIFY_IMAGE_SIZES
|
||||||
|
from ..common import NOTIFY_TYPES
|
||||||
|
|
||||||
|
from ..AppriseAsset import AppriseAsset
|
||||||
|
|
||||||
# Define a general HTML Escaping
|
# Define a general HTML Escaping
|
||||||
try:
|
try:
|
||||||
# use sax first because it's faster
|
# use sax first because it's faster
|
||||||
@ -55,46 +57,6 @@ except ImportError:
|
|||||||
return cgi_escape(text, quote=True)
|
return cgi_escape(text, quote=True)
|
||||||
|
|
||||||
|
|
||||||
class NotifyType(object):
|
|
||||||
INFO = 'info'
|
|
||||||
SUCCESS = 'success'
|
|
||||||
FAILURE = 'failure'
|
|
||||||
WARNING = 'warning'
|
|
||||||
|
|
||||||
|
|
||||||
# Most Servers do not like more then 1 request per 5 seconds,
|
|
||||||
# so 5.5 gives us a safe play range...
|
|
||||||
NOTIFY_THROTTLE_SEC = 5.5
|
|
||||||
|
|
||||||
NOTIFY_TYPES = (
|
|
||||||
NotifyType.INFO,
|
|
||||||
NotifyType.SUCCESS,
|
|
||||||
NotifyType.FAILURE,
|
|
||||||
NotifyType.WARNING,
|
|
||||||
)
|
|
||||||
|
|
||||||
# A Simple Mapping of Colors; For every NOTIFY_TYPE identified,
|
|
||||||
# there should be a mapping to it's color here:
|
|
||||||
HTML_NOTIFY_MAP = {
|
|
||||||
NotifyType.INFO: '#3AA3E3',
|
|
||||||
NotifyType.SUCCESS: '#3AA337',
|
|
||||||
NotifyType.FAILURE: '#A32037',
|
|
||||||
NotifyType.WARNING: '#CACF29',
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class NotifyImageSize(object):
|
|
||||||
XY_72 = '72x72'
|
|
||||||
XY_128 = '128x128'
|
|
||||||
XY_256 = '256x256'
|
|
||||||
|
|
||||||
|
|
||||||
NOTIFY_IMAGE_SIZES = (
|
|
||||||
NotifyImageSize.XY_72,
|
|
||||||
NotifyImageSize.XY_128,
|
|
||||||
NotifyImageSize.XY_256,
|
|
||||||
)
|
|
||||||
|
|
||||||
HTTP_ERROR_MAP = {
|
HTTP_ERROR_MAP = {
|
||||||
400: 'Bad Request - Unsupported Parameters.',
|
400: 'Bad Request - Unsupported Parameters.',
|
||||||
401: 'Verification Failed.',
|
401: 'Verification Failed.',
|
||||||
@ -104,32 +66,23 @@ HTTP_ERROR_MAP = {
|
|||||||
503: 'Servers are overloaded.',
|
503: 'Servers are overloaded.',
|
||||||
}
|
}
|
||||||
|
|
||||||
# Application Identifier
|
|
||||||
NOTIFY_APPLICATION_ID = 'apprise'
|
|
||||||
NOTIFY_APPLICATION_DESC = 'Apprise Notifications'
|
|
||||||
|
|
||||||
# Image Control
|
|
||||||
NOTIFY_IMAGE_URL = \
|
|
||||||
'http://nuxref.com/apprise/apprise-{TYPE}-{XY}.png'
|
|
||||||
|
|
||||||
NOTIFY_IMAGE_FILE = abspath(join(
|
|
||||||
dirname(__file__),
|
|
||||||
'var',
|
|
||||||
'apprise-{TYPE}-{XY}.png',
|
|
||||||
))
|
|
||||||
|
|
||||||
# HTML New Line Delimiter
|
# HTML New Line Delimiter
|
||||||
NOTIFY_NEWLINE = '\r\n'
|
NOTIFY_NEWLINE = '\n'
|
||||||
|
|
||||||
|
# Used to break a path list into parts
|
||||||
|
PATHSPLIT_LIST_DELIM = re.compile(r'[ \t\r\n,\\/]+')
|
||||||
|
|
||||||
|
|
||||||
class NotifyFormat(object):
|
class NotifyFormat(object):
|
||||||
TEXT = 'text'
|
TEXT = 'text'
|
||||||
HTML = 'html'
|
HTML = 'html'
|
||||||
|
MARKDOWN = 'markdown'
|
||||||
|
|
||||||
|
|
||||||
NOTIFY_FORMATS = (
|
NOTIFY_FORMATS = (
|
||||||
NotifyFormat.TEXT,
|
NotifyFormat.TEXT,
|
||||||
NotifyFormat.HTML,
|
NotifyFormat.HTML,
|
||||||
|
NotifyFormat.MARKDOWN,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Regular expression retrieved from:
|
# Regular expression retrieved from:
|
||||||
@ -152,26 +105,36 @@ class NotifyBase(object):
|
|||||||
# The default simple (insecure) protocol
|
# The default simple (insecure) protocol
|
||||||
# all inheriting entries must provide their protocol lookup
|
# all inheriting entries must provide their protocol lookup
|
||||||
# protocol:// (in this example they would specify 'protocol')
|
# protocol:// (in this example they would specify 'protocol')
|
||||||
PROTOCOL = ''
|
protocol = ''
|
||||||
|
|
||||||
# The default secure protocol
|
# The default secure protocol
|
||||||
# all inheriting entries must provide their protocol lookup
|
# all inheriting entries must provide their protocol lookup
|
||||||
# protocols:// (in this example they would specify 'protocols')
|
# protocols:// (in this example they would specify 'protocols')
|
||||||
# This value can be the same as the defined PROTOCOL.
|
# This value can be the same as the defined protocol.
|
||||||
SECURE_PROTOCOL = ''
|
secure_protocol = ''
|
||||||
|
|
||||||
|
# our Application identifier
|
||||||
|
app_id = 'Apprise'
|
||||||
|
|
||||||
|
# our Application description
|
||||||
|
app_desc = 'Apprise Notifications'
|
||||||
|
|
||||||
|
# Most Servers do not like more then 1 request per 5 seconds, so 5.5 gives
|
||||||
|
# us a safe play range...
|
||||||
|
throttle_attempt = 5.5
|
||||||
|
|
||||||
|
# Logging
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
def __init__(self, title_maxlen=100, body_maxlen=512,
|
def __init__(self, title_maxlen=100, body_maxlen=512,
|
||||||
notify_format=NotifyFormat.TEXT, image_size=None,
|
notify_format=NotifyFormat.TEXT, image_size=None,
|
||||||
include_image=False, override_image_path=None,
|
include_image=False, secure=False, throttle=None, **kwargs):
|
||||||
secure=False, **kwargs):
|
|
||||||
"""
|
|
||||||
Initialize some general logging and common server arguments
|
|
||||||
that will keep things consistent when working with the
|
|
||||||
notifiers that will inherit this class
|
|
||||||
"""
|
"""
|
||||||
|
Initialize some general logging and common server arguments that will
|
||||||
|
keep things consistent when working with the notifiers that will
|
||||||
|
inherit this class.
|
||||||
|
|
||||||
# Logging
|
"""
|
||||||
self.logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
if notify_format.lower() not in NOTIFY_FORMATS:
|
if notify_format.lower() not in NOTIFY_FORMATS:
|
||||||
self.logger.error(
|
self.logger.error(
|
||||||
@ -189,8 +152,8 @@ class NotifyBase(object):
|
|||||||
'Invalid image size %s' % image_size,
|
'Invalid image size %s' % image_size,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.app_id = NOTIFY_APPLICATION_ID
|
# Prepare our Assets
|
||||||
self.app_desc = NOTIFY_APPLICATION_DESC
|
self.asset = AppriseAsset()
|
||||||
|
|
||||||
self.notify_format = notify_format.lower()
|
self.notify_format = notify_format.lower()
|
||||||
self.title_maxlen = title_maxlen
|
self.title_maxlen = title_maxlen
|
||||||
@ -199,6 +162,10 @@ class NotifyBase(object):
|
|||||||
self.include_image = include_image
|
self.include_image = include_image
|
||||||
self.secure = secure
|
self.secure = secure
|
||||||
|
|
||||||
|
if throttle:
|
||||||
|
# Custom throttle override
|
||||||
|
self.throttle_attempt = throttle
|
||||||
|
|
||||||
# Certificate Verification (for SSL calls); default to being enabled
|
# Certificate Verification (for SSL calls); default to being enabled
|
||||||
self.verify_certificate = kwargs.get('verify', True)
|
self.verify_certificate = kwargs.get('verify', True)
|
||||||
|
|
||||||
@ -213,16 +180,19 @@ class NotifyBase(object):
|
|||||||
self.user = kwargs.get('user')
|
self.user = kwargs.get('user')
|
||||||
self.password = kwargs.get('password')
|
self.password = kwargs.get('password')
|
||||||
|
|
||||||
# Over-rides
|
def throttle(self, throttle_time=None):
|
||||||
self.override_image_url = kwargs.get('override_image_url')
|
|
||||||
self.override_image_path = kwargs.get('override_image_path')
|
|
||||||
|
|
||||||
def throttle(self, throttle_time=NOTIFY_THROTTLE_SEC):
|
|
||||||
"""
|
"""
|
||||||
A common throttle control
|
A common throttle control
|
||||||
"""
|
"""
|
||||||
self.logger.debug('Throttling...')
|
self.logger.debug('Throttling...')
|
||||||
|
|
||||||
|
throttle_time = throttle_time \
|
||||||
|
if throttle_time is not None else self.throttle_attempt
|
||||||
|
|
||||||
|
# Perform throttle
|
||||||
|
if throttle_time > 0:
|
||||||
sleep(throttle_time)
|
sleep(throttle_time)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def image_url(self, notify_type):
|
def image_url(self, notify_type):
|
||||||
@ -230,74 +200,47 @@ class NotifyBase(object):
|
|||||||
Returns Image URL if possible
|
Returns Image URL if possible
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if self.override_image_url:
|
|
||||||
# Over-ride
|
|
||||||
return self.override_image_url
|
|
||||||
|
|
||||||
if not self.image_size:
|
if not self.image_size:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if notify_type not in NOTIFY_TYPES:
|
if notify_type not in NOTIFY_TYPES:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
re_map = {
|
return self.asset.image_url(
|
||||||
'{TYPE}': notify_type,
|
notify_type=notify_type,
|
||||||
'{XY}': self.image_size,
|
image_size=self.image_size,
|
||||||
}
|
|
||||||
|
|
||||||
# Iterate over above list and store content accordingly
|
|
||||||
re_table = re.compile(
|
|
||||||
r'(' + '|'.join(re_map.keys()) + r')',
|
|
||||||
re.IGNORECASE,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return re_table.sub(lambda x: re_map[x.group()], NOTIFY_IMAGE_URL)
|
def image_path(self, notify_type):
|
||||||
|
"""
|
||||||
|
Returns the path of the image if it can
|
||||||
|
"""
|
||||||
|
if not self.image_size:
|
||||||
|
return None
|
||||||
|
|
||||||
|
if notify_type not in NOTIFY_TYPES:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return self.asset.image_path(
|
||||||
|
notify_type=notify_type,
|
||||||
|
image_size=self.image_size,
|
||||||
|
)
|
||||||
|
|
||||||
def image_raw(self, notify_type):
|
def image_raw(self, notify_type):
|
||||||
"""
|
"""
|
||||||
Returns the raw image if it can
|
Returns the raw image if it can
|
||||||
"""
|
"""
|
||||||
if not self.override_image_path:
|
|
||||||
if not self.image_size:
|
if not self.image_size:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if notify_type not in NOTIFY_TYPES:
|
if notify_type not in NOTIFY_TYPES:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
re_map = {
|
return self.asset.image_raw(
|
||||||
'{TYPE}': notify_type,
|
notify_type=notify_type,
|
||||||
'{XY}': self.image_size,
|
image_size=self.image_size,
|
||||||
}
|
|
||||||
|
|
||||||
# Iterate over above list and store content accordingly
|
|
||||||
re_table = re.compile(
|
|
||||||
r'(' + '|'.join(re_map.keys()) + r')',
|
|
||||||
re.IGNORECASE,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Now we open and return the file
|
|
||||||
_file = re_table.sub(
|
|
||||||
lambda x: re_map[x.group()], NOTIFY_IMAGE_FILE)
|
|
||||||
|
|
||||||
else:
|
|
||||||
# Override Path Specified
|
|
||||||
_file = self.override_image_path
|
|
||||||
|
|
||||||
try:
|
|
||||||
fd = open(_file, 'rb')
|
|
||||||
|
|
||||||
except:
|
|
||||||
return None
|
|
||||||
|
|
||||||
try:
|
|
||||||
return fd.read()
|
|
||||||
|
|
||||||
except:
|
|
||||||
return None
|
|
||||||
|
|
||||||
finally:
|
|
||||||
fd.close()
|
|
||||||
|
|
||||||
def escape_html(self, html, convert_new_lines=False):
|
def escape_html(self, html, convert_new_lines=False):
|
||||||
"""
|
"""
|
||||||
Takes html text as input and escapes it so that it won't
|
Takes html text as input and escapes it so that it won't
|
||||||
@ -379,67 +322,55 @@ class NotifyBase(object):
|
|||||||
# we always return a list
|
# we always return a list
|
||||||
return [html, ]
|
return [html, ]
|
||||||
|
|
||||||
def notify(self, title, body, notify_type=NotifyType.SUCCESS,
|
@staticmethod
|
||||||
**kwargs):
|
def split_path(path, unquote=True):
|
||||||
"""
|
"""
|
||||||
This should be over-rided by the class that
|
Splits a URL up into a list object.
|
||||||
inherits this one.
|
|
||||||
"""
|
|
||||||
if notify_type and notify_type not in NOTIFY_TYPES:
|
|
||||||
self.warning(
|
|
||||||
'An invalid notification type (%s) was specified.' % (
|
|
||||||
notify_type))
|
|
||||||
|
|
||||||
if not isinstance(body, basestring):
|
|
||||||
body = ''
|
|
||||||
|
|
||||||
if not isinstance(title, basestring):
|
|
||||||
title = ''
|
|
||||||
|
|
||||||
# Ensure we're set up as UTF-8
|
|
||||||
title = self.to_utf8(title)
|
|
||||||
body = self.to_utf8(body)
|
|
||||||
|
|
||||||
if title:
|
|
||||||
title = title[0:self.title_maxlen]
|
|
||||||
|
|
||||||
if self.notify_format == NotifyFormat.HTML:
|
|
||||||
bodies = self.to_html(body=body)
|
|
||||||
|
|
||||||
elif self.notify_format == NotifyFormat.TEXT:
|
|
||||||
# TODO: this should split the content into
|
|
||||||
# multiple messages
|
|
||||||
bodies = [body[0:self.body_maxlen], ]
|
|
||||||
|
|
||||||
while len(bodies):
|
|
||||||
b = bodies.pop(0)
|
|
||||||
# Send Message(s)
|
|
||||||
if not self._notify(
|
|
||||||
title=title, body=b,
|
|
||||||
notify_type=notify_type,
|
|
||||||
**kwargs):
|
|
||||||
return False
|
|
||||||
|
|
||||||
# If we got here, we sent part of the notification
|
|
||||||
# if there are any left, we should throttle so we
|
|
||||||
# don't overload the server with requests (they
|
|
||||||
# might not be happy with us otherwise)
|
|
||||||
if len(bodies):
|
|
||||||
self.throttle()
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
def pre_parse(self, url, server_settings):
|
|
||||||
"""
|
|
||||||
grants the ability to manipulate or additionally parse the content
|
|
||||||
provided in the server_settings variable.
|
|
||||||
|
|
||||||
Return True if you're satisfied with them (and may have additionally
|
|
||||||
changed them) and False if the settings are not acceptable or useable
|
|
||||||
|
|
||||||
Since this is the base class, plugins are not requird to overload it
|
|
||||||
but have the option to. By default the configuration is always
|
|
||||||
accepted.
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return True
|
if unquote:
|
||||||
|
return PATHSPLIT_LIST_DELIM.split(_unquote(path).lstrip('/'))
|
||||||
|
return PATHSPLIT_LIST_DELIM.split(path.lstrip('/'))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def is_email(address):
|
||||||
|
"""
|
||||||
|
Returns True if specified entry is an email address
|
||||||
|
|
||||||
|
"""
|
||||||
|
return IS_EMAIL_RE.match(address) is not None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def parse_url(url):
|
||||||
|
"""
|
||||||
|
Parses the URL and returns it broken apart into a dictionary.
|
||||||
|
|
||||||
|
"""
|
||||||
|
results = parse_url(url, default_schema='unknown')
|
||||||
|
|
||||||
|
if not results:
|
||||||
|
# We're done; we failed to parse our url
|
||||||
|
return results
|
||||||
|
|
||||||
|
# if our URL ends with an 's', then assueme our secure flag is set.
|
||||||
|
results['secure'] = (results['schema'][-1] == 's')
|
||||||
|
|
||||||
|
# Our default notification format
|
||||||
|
results['notify_format'] = NotifyFormat.TEXT
|
||||||
|
|
||||||
|
# Support SSL Certificate 'verify' keyword. Default to being enabled
|
||||||
|
results['verify'] = True
|
||||||
|
|
||||||
|
if 'qsd' in results:
|
||||||
|
if 'verify' in results['qsd']:
|
||||||
|
parse_bool(results['qsd'].get('verify', True))
|
||||||
|
|
||||||
|
# Password overrides
|
||||||
|
if 'pass' in results['qsd']:
|
||||||
|
results['password'] = results['qsd']['pass']
|
||||||
|
|
||||||
|
# User overrides
|
||||||
|
if 'user' in results['qsd']:
|
||||||
|
results['user'] = results['qsd']['user']
|
||||||
|
|
||||||
|
return results
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
# -*- encoding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Boxcar Notify Wrapper
|
# Boxcar Notify Wrapper
|
||||||
#
|
#
|
||||||
# Copyright (C) 2014-2017 Chris Caron <lead2gold@gmail.com>
|
# Copyright (C) 2017 Chris Caron <lead2gold@gmail.com>
|
||||||
#
|
#
|
||||||
# This file is part of apprise.
|
# This file is part of apprise.
|
||||||
#
|
#
|
||||||
@ -20,15 +20,11 @@
|
|||||||
# along with apprise. If not, see <http://www.gnu.org/licenses/>.
|
# along with apprise. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from json import dumps
|
from json import dumps
|
||||||
|
from urllib import unquote
|
||||||
import requests
|
import requests
|
||||||
import re
|
import re
|
||||||
|
|
||||||
# Used to break apart list of potential tags by their delimiter
|
|
||||||
# into a usable list.
|
|
||||||
TAGS_LIST_DELIM = re.compile(r'[ \t\r\n,\\/]+')
|
|
||||||
|
|
||||||
from .NotifyBase import NotifyBase
|
from .NotifyBase import NotifyBase
|
||||||
from .NotifyBase import NotifyFormat
|
|
||||||
from .NotifyBase import HTTP_ERROR_MAP
|
from .NotifyBase import HTTP_ERROR_MAP
|
||||||
|
|
||||||
# Used to validate Tags, Aliases and Devices
|
# Used to validate Tags, Aliases and Devices
|
||||||
@ -36,6 +32,10 @@ IS_TAG = re.compile(r'^[A-Za-z0-9]{1,63}$')
|
|||||||
IS_ALIAS = re.compile(r'^[@]?[A-Za-z0-9]+$')
|
IS_ALIAS = re.compile(r'^[@]?[A-Za-z0-9]+$')
|
||||||
IS_DEVICETOKEN = re.compile(r'^[A-Za-z0-9]{64}$')
|
IS_DEVICETOKEN = re.compile(r'^[A-Za-z0-9]{64}$')
|
||||||
|
|
||||||
|
# Used to break apart list of potential tags by their delimiter
|
||||||
|
# into a usable list.
|
||||||
|
TAGS_LIST_DELIM = re.compile(r'[ \t\r\n,\\/]+')
|
||||||
|
|
||||||
|
|
||||||
class NotifyBoxcar(NotifyBase):
|
class NotifyBoxcar(NotifyBase):
|
||||||
"""
|
"""
|
||||||
@ -43,29 +43,30 @@ class NotifyBoxcar(NotifyBase):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# The default simple (insecure) protocol
|
# The default simple (insecure) protocol
|
||||||
PROTOCOL = 'boxcar'
|
protocol = 'boxcar'
|
||||||
|
|
||||||
# The default secure protocol
|
# The default secure protocol
|
||||||
SECURE_PROTOCOL = 'boxcars'
|
secure_protocol = 'boxcars'
|
||||||
|
|
||||||
def __init__(self, recipients=None, **kwargs):
|
def __init__(self, recipients=None, **kwargs):
|
||||||
"""
|
"""
|
||||||
Initialize Boxcar Object
|
Initialize Boxcar Object
|
||||||
"""
|
"""
|
||||||
super(NotifyBoxcar, self).__init__(
|
super(NotifyBoxcar, self).__init__(
|
||||||
title_maxlen=250, body_maxlen=10000,
|
title_maxlen=250, body_maxlen=10000, **kwargs)
|
||||||
notify_format=NotifyFormat.TEXT,
|
|
||||||
**kwargs)
|
|
||||||
|
|
||||||
if self.secure:
|
if self.secure:
|
||||||
self.schema = 'https'
|
self.schema = 'https'
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.schema = 'http'
|
self.schema = 'http'
|
||||||
|
|
||||||
# Initialize tag list
|
# Initialize tag list
|
||||||
self.tags = list()
|
self.tags = list()
|
||||||
|
|
||||||
# Initialize alias list
|
# Initialize alias list
|
||||||
self.aliases = list()
|
self.aliases = list()
|
||||||
|
|
||||||
# Initialize device_token list
|
# Initialize device_token list
|
||||||
self.device_tokens = list()
|
self.device_tokens = list()
|
||||||
|
|
||||||
@ -101,7 +102,7 @@ class NotifyBoxcar(NotifyBase):
|
|||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
def _notify(self, title, body, notify_type, **kwargs):
|
def notify(self, title, body, notify_type, **kwargs):
|
||||||
"""
|
"""
|
||||||
Perform Boxcar Notification
|
Perform Boxcar Notification
|
||||||
"""
|
"""
|
||||||
@ -176,3 +177,28 @@ class NotifyBoxcar(NotifyBase):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def parse_url(url):
|
||||||
|
"""
|
||||||
|
Parses the URL and returns it broken apart into a dictionary.
|
||||||
|
|
||||||
|
"""
|
||||||
|
results = NotifyBase.parse_url(url)
|
||||||
|
|
||||||
|
if not results:
|
||||||
|
# We're done early
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Acquire our recipients and include them in the response
|
||||||
|
try:
|
||||||
|
recipients = unquote(results['fullpath'])
|
||||||
|
|
||||||
|
except (AttributeError, KeyError):
|
||||||
|
# no recipients detected
|
||||||
|
recipients = ''
|
||||||
|
|
||||||
|
# Store our recipients
|
||||||
|
results['recipients'] = recipients
|
||||||
|
|
||||||
|
return results
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
# -*- encoding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Email Notify Wrapper
|
# Email Notify Wrapper
|
||||||
#
|
#
|
||||||
# Copyright (C) 2014-2017 Chris Caron <lead2gold@gmail.com>
|
# Copyright (C) 2017 Chris Caron <lead2gold@gmail.com>
|
||||||
#
|
#
|
||||||
# This file is part of apprise.
|
# This file is part of apprise.
|
||||||
#
|
#
|
||||||
@ -26,20 +26,12 @@ from smtplib import SMTP
|
|||||||
from smtplib import SMTPException
|
from smtplib import SMTPException
|
||||||
from socket import error as SocketError
|
from socket import error as SocketError
|
||||||
|
|
||||||
|
from urllib import unquote as unquote
|
||||||
|
|
||||||
from email.mime.text import MIMEText
|
from email.mime.text import MIMEText
|
||||||
|
|
||||||
from .NotifyBase import NotifyBase
|
from .NotifyBase import NotifyBase
|
||||||
from .NotifyBase import NotifyFormat
|
from .NotifyBase import NotifyFormat
|
||||||
from .NotifyBase import IS_EMAIL_RE
|
|
||||||
|
|
||||||
# Default Non-Encryption Port
|
|
||||||
EMAIL_SMTP_PORT = 25
|
|
||||||
|
|
||||||
# Default Secure Port
|
|
||||||
EMAIL_SMTPS_PORT = 587
|
|
||||||
|
|
||||||
# Default SMTP Timeout (in seconds)
|
|
||||||
SMTP_SERVER_TIMEOUT = 30
|
|
||||||
|
|
||||||
|
|
||||||
class WebBaseLogin(object):
|
class WebBaseLogin(object):
|
||||||
@ -49,15 +41,14 @@ class WebBaseLogin(object):
|
|||||||
"""
|
"""
|
||||||
# User Login must be Email Based
|
# User Login must be Email Based
|
||||||
EMAIL = 'Email'
|
EMAIL = 'Email'
|
||||||
|
|
||||||
# User Login must UserID Based
|
# User Login must UserID Based
|
||||||
USERID = 'UserID'
|
USERID = 'UserID'
|
||||||
|
|
||||||
|
|
||||||
# To attempt to make this script stupid proof,
|
# To attempt to make this script stupid proof, if we detect an email address
|
||||||
# if we detect an email address that is part of the
|
# that is part of the this table, we can pre-use a lot more defaults if they
|
||||||
# this table, we can pre-use a lot more defaults if
|
# aren't otherwise specified on the users input.
|
||||||
# they aren't otherwise specified on the users
|
|
||||||
# input
|
|
||||||
WEBBASE_LOOKUP_TABLE = (
|
WEBBASE_LOOKUP_TABLE = (
|
||||||
# Google GMail
|
# Google GMail
|
||||||
(
|
(
|
||||||
@ -121,11 +112,6 @@ WEBBASE_LOOKUP_TABLE = (
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
# Mail Prefix Servers (TODO)
|
|
||||||
MAIL_SERVER_PREFIXES = (
|
|
||||||
'smtp', 'mail', 'smtps', 'outgoing'
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class NotifyEmail(NotifyBase):
|
class NotifyEmail(NotifyBase):
|
||||||
"""
|
"""
|
||||||
@ -134,10 +120,19 @@ class NotifyEmail(NotifyBase):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# The default simple (insecure) protocol
|
# The default simple (insecure) protocol
|
||||||
PROTOCOL = 'mailto'
|
protocol = 'mailto'
|
||||||
|
|
||||||
# The default secure protocol
|
# The default secure protocol
|
||||||
SECURE_PROTOCOL = 'mailtos'
|
secure_protocol = 'mailtos'
|
||||||
|
|
||||||
|
# Default Non-Encryption Port
|
||||||
|
default_port = 25
|
||||||
|
|
||||||
|
# Default Secure Port
|
||||||
|
default_secure_port = 587
|
||||||
|
|
||||||
|
# Default SMTP Timeout (in seconds)
|
||||||
|
connect_timeout = 15
|
||||||
|
|
||||||
def __init__(self, to, notify_format, **kwargs):
|
def __init__(self, to, notify_format, **kwargs):
|
||||||
"""
|
"""
|
||||||
@ -154,15 +149,17 @@ class NotifyEmail(NotifyBase):
|
|||||||
# Handle SMTP vs SMTPS (Secure vs UnSecure)
|
# Handle SMTP vs SMTPS (Secure vs UnSecure)
|
||||||
if not self.port:
|
if not self.port:
|
||||||
if self.secure:
|
if self.secure:
|
||||||
self.port = EMAIL_SMTPS_PORT
|
self.port = self.default_secure_port
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.port = EMAIL_SMTP_PORT
|
self.port = self.default_port
|
||||||
|
|
||||||
# Email SMTP Server Timeout
|
# Email SMTP Server Timeout
|
||||||
try:
|
try:
|
||||||
self.timeout = int(kwargs.get('timeout', SMTP_SERVER_TIMEOUT))
|
self.timeout = int(kwargs.get('timeout', self.connect_timeout))
|
||||||
|
|
||||||
except (ValueError, TypeError):
|
except (ValueError, TypeError):
|
||||||
self.timeout = SMTP_SERVER_TIMEOUT
|
self.timeout = self.connect_timeout
|
||||||
|
|
||||||
# Now we want to construct the To and From email
|
# Now we want to construct the To and From email
|
||||||
# addresses from the URL provided
|
# addresses from the URL provided
|
||||||
@ -175,13 +172,13 @@ class NotifyEmail(NotifyBase):
|
|||||||
if not isinstance(self.to_addr, basestring):
|
if not isinstance(self.to_addr, basestring):
|
||||||
raise TypeError('No valid ~To~ email address specified.')
|
raise TypeError('No valid ~To~ email address specified.')
|
||||||
|
|
||||||
if not IS_EMAIL_RE.match(self.to_addr):
|
if not NotifyBase.is_email(self.to_addr):
|
||||||
raise TypeError('Invalid ~To~ email format: %s' % self.to_addr)
|
raise TypeError('Invalid ~To~ email format: %s' % self.to_addr)
|
||||||
|
|
||||||
if not isinstance(self.from_addr, basestring):
|
if not isinstance(self.from_addr, basestring):
|
||||||
raise TypeError('No valid ~From~ email address specified.')
|
raise TypeError('No valid ~From~ email address specified.')
|
||||||
|
|
||||||
match = IS_EMAIL_RE.match(self.from_addr)
|
match = NotifyBase.is_email(self.from_addr)
|
||||||
if not match:
|
if not match:
|
||||||
# Parse Source domain based on from_addr
|
# Parse Source domain based on from_addr
|
||||||
raise TypeError('Invalid ~From~ email format: %s' % self.to_addr)
|
raise TypeError('Invalid ~From~ email format: %s' % self.to_addr)
|
||||||
@ -202,10 +199,9 @@ class NotifyEmail(NotifyBase):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
if self.smtp_host:
|
if self.smtp_host:
|
||||||
# SMTP Server was explicitly specified, therefore it
|
# SMTP Server was explicitly specified, therefore it is assumed
|
||||||
# is assumed the caller knows what he's doing and
|
# the caller knows what he's doing and is intentionally
|
||||||
# is intentionally over-riding any smarts to be
|
# over-riding any smarts to be applied
|
||||||
# applied
|
|
||||||
return
|
return
|
||||||
|
|
||||||
for i in range(len(WEBBASE_LOOKUP_TABLE)):
|
for i in range(len(WEBBASE_LOOKUP_TABLE)):
|
||||||
@ -235,7 +231,7 @@ class NotifyEmail(NotifyBase):
|
|||||||
login_type = WEBBASE_LOOKUP_TABLE[i][2]\
|
login_type = WEBBASE_LOOKUP_TABLE[i][2]\
|
||||||
.get('login_type', [])
|
.get('login_type', [])
|
||||||
|
|
||||||
if IS_EMAIL_RE.match(self.user) and \
|
if NotifyBase.is_email(self.user) and \
|
||||||
WebBaseLogin.EMAIL not in login_type:
|
WebBaseLogin.EMAIL not in login_type:
|
||||||
# Email specified but login type
|
# Email specified but login type
|
||||||
# not supported; switch it to user id
|
# not supported; switch it to user id
|
||||||
@ -248,7 +244,7 @@ class NotifyEmail(NotifyBase):
|
|||||||
|
|
||||||
break
|
break
|
||||||
|
|
||||||
def _notify(self, title, body, **kwargs):
|
def notify(self, title, body, **kwargs):
|
||||||
"""
|
"""
|
||||||
Perform Email Notification
|
Perform Email Notification
|
||||||
"""
|
"""
|
||||||
@ -263,6 +259,7 @@ class NotifyEmail(NotifyBase):
|
|||||||
if self.notify_format == NotifyFormat.HTML:
|
if self.notify_format == NotifyFormat.HTML:
|
||||||
email = MIMEText(body, 'html')
|
email = MIMEText(body, 'html')
|
||||||
email['Content-Type'] = 'text/html'
|
email['Content-Type'] = 'text/html'
|
||||||
|
|
||||||
else:
|
else:
|
||||||
email = MIMEText(body, 'text')
|
email = MIMEText(body, 'text')
|
||||||
email['Content-Type'] = 'text/plain'
|
email['Content-Type'] = 'text/plain'
|
||||||
@ -310,8 +307,120 @@ class NotifyEmail(NotifyBase):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
socket.quit()
|
socket.quit()
|
||||||
|
|
||||||
except:
|
except:
|
||||||
# no problem
|
# no problem
|
||||||
pass
|
pass
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def parse_url(url):
|
||||||
|
"""
|
||||||
|
Parses the URL and returns enough arguments that can allow
|
||||||
|
us to substantiate this object.
|
||||||
|
|
||||||
|
"""
|
||||||
|
results = NotifyBase.parse_url(url)
|
||||||
|
|
||||||
|
if not results:
|
||||||
|
# We're done early as we couldn't load the results
|
||||||
|
return results
|
||||||
|
|
||||||
|
# Apply our settings now
|
||||||
|
|
||||||
|
# Default Format is HTML
|
||||||
|
results['notify_format'] = NotifyFormat.HTML
|
||||||
|
|
||||||
|
to_addr = ''
|
||||||
|
from_addr = ''
|
||||||
|
smtp_host = ''
|
||||||
|
|
||||||
|
if 'format' in results['qsd'] and len(results['qsd']['format']):
|
||||||
|
# Extract email format (Text/Html)
|
||||||
|
try:
|
||||||
|
format = unquote(results['qsd']['format']).lower()
|
||||||
|
if len(format) > 0 and format[0] == 't':
|
||||||
|
results['notify_format'] = NotifyFormat.TEXT
|
||||||
|
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# get 'To' email address
|
||||||
|
try:
|
||||||
|
to_addr = filter(bool, NotifyBase.split_path(results['host']))[0]
|
||||||
|
|
||||||
|
except (AttributeError, IndexError):
|
||||||
|
# No problem, we have other ways of getting
|
||||||
|
# the To address
|
||||||
|
pass
|
||||||
|
|
||||||
|
if not NotifyBase.is_email(to_addr):
|
||||||
|
if results['user']:
|
||||||
|
# Try to be clever and build a potential
|
||||||
|
# email address based on what we've been provided
|
||||||
|
to_addr = '%s@%s' % (
|
||||||
|
re.split('[\s@]+', results['user'])[0],
|
||||||
|
re.split('[\s@]+', to_addr)[-1],
|
||||||
|
)
|
||||||
|
|
||||||
|
if not NotifyBase.is_email(to_addr):
|
||||||
|
NotifyBase.logger.error(
|
||||||
|
'%s does not contain a recipient email.' %
|
||||||
|
unquote(results['url'].lstrip('/')),
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Attempt to detect 'from' email address
|
||||||
|
from_addr = to_addr
|
||||||
|
try:
|
||||||
|
if 'from' in results['qsd'] and len(results['qsd']['from']):
|
||||||
|
from_addr = results['qsd']['from']
|
||||||
|
if not NotifyBase.is_email(results['qsd']['from']):
|
||||||
|
# Lets be clever and attempt to make the from
|
||||||
|
# address email
|
||||||
|
from_addr = '%s@%s' % (
|
||||||
|
re.split('[\s@]+', from_addr)[0],
|
||||||
|
re.split('[\s@]+', to_addr)[-1],
|
||||||
|
)
|
||||||
|
|
||||||
|
if not NotifyBase.is_email(from_addr):
|
||||||
|
NotifyBase.logger.error(
|
||||||
|
'%s does not contain a from address.' %
|
||||||
|
unquote(results['url'].lstrip('/')),
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
if 'name' in results['qsd'] and len(results['qsd']['name']):
|
||||||
|
# Extract from name to associate with from address
|
||||||
|
results['name'] = unquote(results['qsd']['name'])
|
||||||
|
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
if 'timeout' in results['qsd'] and len(results['qsd']['timeout']):
|
||||||
|
# Extract the timeout to associate with smtp server
|
||||||
|
results['timeout'] = unquote(results['qsd']['timeout'])
|
||||||
|
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Store SMTP Host if specified
|
||||||
|
try:
|
||||||
|
# Extract from password to associate with smtp server
|
||||||
|
if 'smtp' in results['qsd'] and len(results['qsd']['smtp']):
|
||||||
|
smtp_host = unquote(results['qsd']['smtp'])
|
||||||
|
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
results['to'] = to_addr
|
||||||
|
results['from'] = from_addr
|
||||||
|
results['smtp_host'] = smtp_host
|
||||||
|
|
||||||
|
return results
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# -*- encoding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Faast Notify Wrapper
|
# Faast Notify Wrapper
|
||||||
#
|
#
|
||||||
@ -22,12 +22,8 @@
|
|||||||
import requests
|
import requests
|
||||||
|
|
||||||
from .NotifyBase import NotifyBase
|
from .NotifyBase import NotifyBase
|
||||||
from .NotifyBase import NotifyFormat
|
|
||||||
from .NotifyBase import NotifyImageSize
|
|
||||||
from .NotifyBase import HTTP_ERROR_MAP
|
from .NotifyBase import HTTP_ERROR_MAP
|
||||||
|
from ..common import NotifyImageSize
|
||||||
# Faast uses the http protocol with JSON requests
|
|
||||||
FAAST_URL = 'https://www.appnotifications.com/account/notifications.json'
|
|
||||||
|
|
||||||
# Image Support (72x72)
|
# Image Support (72x72)
|
||||||
FAAST_IMAGE_XY = NotifyImageSize.XY_72
|
FAAST_IMAGE_XY = NotifyImageSize.XY_72
|
||||||
@ -39,24 +35,22 @@ class NotifyFaast(NotifyBase):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# The default protocol (this is secure for faast)
|
# The default protocol (this is secure for faast)
|
||||||
PROTOCOL = 'faast'
|
protocol = 'faast'
|
||||||
|
|
||||||
# The default secure protocol
|
# Faast uses the http protocol with JSON requests
|
||||||
SECURE_PROTOCOL = 'faast'
|
notify_url = 'https://www.appnotifications.com/account/notifications.json'
|
||||||
|
|
||||||
def __init__(self, authtoken, **kwargs):
|
def __init__(self, authtoken, **kwargs):
|
||||||
"""
|
"""
|
||||||
Initialize Faast Object
|
Initialize Faast Object
|
||||||
"""
|
"""
|
||||||
super(NotifyFaast, self).__init__(
|
super(NotifyFaast, self).__init__(
|
||||||
title_maxlen=250, body_maxlen=32768,
|
title_maxlen=250, body_maxlen=32768, image_size=FAAST_IMAGE_XY,
|
||||||
image_size=FAAST_IMAGE_XY,
|
|
||||||
notify_format=NotifyFormat.TEXT,
|
|
||||||
**kwargs)
|
**kwargs)
|
||||||
|
|
||||||
self.authtoken = authtoken
|
self.authtoken = authtoken
|
||||||
|
|
||||||
def _notify(self, title, body, notify_type, **kwargs):
|
def notify(self, title, body, notify_type, **kwargs):
|
||||||
"""
|
"""
|
||||||
Perform Faast Notification
|
Perform Faast Notification
|
||||||
"""
|
"""
|
||||||
@ -81,12 +75,12 @@ class NotifyFaast(NotifyBase):
|
|||||||
payload['icon_url'] = image_url
|
payload['icon_url'] = image_url
|
||||||
|
|
||||||
self.logger.debug('Faast POST URL: %s (cert_verify=%r)' % (
|
self.logger.debug('Faast POST URL: %s (cert_verify=%r)' % (
|
||||||
FAAST_URL, self.verify_certificate,
|
self.notify_url, self.verify_certificate,
|
||||||
))
|
))
|
||||||
self.logger.debug('Faast Payload: %s' % str(payload))
|
self.logger.debug('Faast Payload: %s' % str(payload))
|
||||||
try:
|
try:
|
||||||
r = requests.post(
|
r = requests.post(
|
||||||
FAAST_URL,
|
self.notify_url,
|
||||||
data=payload,
|
data=payload,
|
||||||
headers=headers,
|
headers=headers,
|
||||||
verify=self.verify_certificate,
|
verify=self.verify_certificate,
|
||||||
@ -121,3 +115,23 @@ class NotifyFaast(NotifyBase):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def parse_url(url):
|
||||||
|
"""
|
||||||
|
Parses the URL and returns enough arguments that can allow
|
||||||
|
us to substantiate this object.
|
||||||
|
|
||||||
|
"""
|
||||||
|
results = NotifyBase.parse_url(url)
|
||||||
|
|
||||||
|
if not results:
|
||||||
|
# We're done early as we couldn't load the results
|
||||||
|
return results
|
||||||
|
|
||||||
|
# Apply our settings now
|
||||||
|
|
||||||
|
# Store our authtoken using the host
|
||||||
|
results['authtoken'] = results['host']
|
||||||
|
|
||||||
|
return results
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
# -*- encoding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Growl Notify Wrapper
|
# Growl Notify Wrapper
|
||||||
#
|
#
|
||||||
# Copyright (C) 2014-2017 Chris Caron <lead2gold@gmail.com>
|
# Copyright (C) 2017 Chris Caron <lead2gold@gmail.com>
|
||||||
#
|
#
|
||||||
# This file is part of apprise.
|
# This file is part of apprise.
|
||||||
#
|
#
|
||||||
@ -18,17 +18,15 @@
|
|||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with apprise. If not, see <http://www.gnu.org/licenses/>.
|
# along with apprise. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
import re
|
||||||
from ..NotifyBase import NotifyBase
|
from urllib import unquote
|
||||||
from ..NotifyBase import NotifyFormat
|
|
||||||
from ..NotifyBase import NotifyImageSize
|
|
||||||
|
|
||||||
from .gntp.notifier import GrowlNotifier
|
from .gntp.notifier import GrowlNotifier
|
||||||
from .gntp.errors import NetworkError as GrowlNetworkError
|
from .gntp.errors import NetworkError as GrowlNetworkError
|
||||||
from .gntp.errors import AuthError as GrowlAuthenticationError
|
from .gntp.errors import AuthError as GrowlAuthenticationError
|
||||||
|
|
||||||
# Default Growl Port
|
from ..NotifyBase import NotifyBase
|
||||||
GROWL_UDP_PORT = 23053
|
from ...common import NotifyImageSize
|
||||||
|
|
||||||
# Image Support (72x72)
|
# Image Support (72x72)
|
||||||
GROWL_IMAGE_XY = NotifyImageSize.XY_72
|
GROWL_IMAGE_XY = NotifyImageSize.XY_72
|
||||||
@ -61,10 +59,10 @@ class NotifyGrowl(NotifyBase):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# The default protocol
|
# The default protocol
|
||||||
PROTOCOL = 'growl'
|
protocol = 'growl'
|
||||||
|
|
||||||
# The default secure protocol
|
# Default Growl Port
|
||||||
SECURE_PROTOCOL = 'growl'
|
default_port = 23053
|
||||||
|
|
||||||
def __init__(self, priority=GrowlPriority.NORMAL, version=2, **kwargs):
|
def __init__(self, priority=GrowlPriority.NORMAL, version=2, **kwargs):
|
||||||
"""
|
"""
|
||||||
@ -72,19 +70,18 @@ class NotifyGrowl(NotifyBase):
|
|||||||
"""
|
"""
|
||||||
super(NotifyGrowl, self).__init__(
|
super(NotifyGrowl, self).__init__(
|
||||||
title_maxlen=250, body_maxlen=32768,
|
title_maxlen=250, body_maxlen=32768,
|
||||||
image_size=GROWL_IMAGE_XY,
|
image_size=GROWL_IMAGE_XY, **kwargs)
|
||||||
notify_format=NotifyFormat.TEXT,
|
|
||||||
**kwargs)
|
|
||||||
|
|
||||||
# A Global flag that tracks registration
|
# A Global flag that tracks registration
|
||||||
self.is_registered = False
|
self.is_registered = False
|
||||||
|
|
||||||
if not self.port:
|
if not self.port:
|
||||||
self.port = GROWL_UDP_PORT
|
self.port = self.default_port
|
||||||
|
|
||||||
# The Priority of the message
|
# The Priority of the message
|
||||||
if priority not in GROWL_PRIORITIES:
|
if priority not in GROWL_PRIORITIES:
|
||||||
self.priority = GrowlPriority.NORMAL
|
self.priority = GrowlPriority.NORMAL
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.priority = priority
|
self.priority = priority
|
||||||
|
|
||||||
@ -130,7 +127,7 @@ class NotifyGrowl(NotifyBase):
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def _notify(self, title, body, notify_type, **kwargs):
|
def notify(self, title, body, notify_type, **kwargs):
|
||||||
"""
|
"""
|
||||||
Perform Growl Notification
|
Perform Growl Notification
|
||||||
"""
|
"""
|
||||||
@ -139,6 +136,12 @@ class NotifyGrowl(NotifyBase):
|
|||||||
# We can't do anything
|
# We can't do anything
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
# Limit results to just the first 2 line otherwise there is just to
|
||||||
|
# much content to display
|
||||||
|
body = re.split('[\r\n]+', body)
|
||||||
|
body[0] = body[0].strip('#').strip()
|
||||||
|
body = '\r\n'.join(body[0:2])
|
||||||
|
|
||||||
icon = None
|
icon = None
|
||||||
if self.include_image:
|
if self.include_image:
|
||||||
if self.version >= 2:
|
if self.version >= 2:
|
||||||
@ -175,13 +178,13 @@ class NotifyGrowl(NotifyBase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
except GrowlNetworkError as e:
|
except GrowlNetworkError as e:
|
||||||
# Since Growl servers listen for UDP broadcasts,
|
# Since Growl servers listen for UDP broadcasts, it's possible
|
||||||
# it's possible that you will never get to this part
|
# that you will never get to this part of the code since there is
|
||||||
# of the code since there is no acknowledgement as to
|
# no acknowledgement as to whether it accepted what was sent to it
|
||||||
# whether it accepted what was sent to it or not.
|
# or not.
|
||||||
|
|
||||||
# however, if the host/server is unavailable, you will
|
# However, if the host/server is unavailable, you will get to this
|
||||||
# get to this point of the code.
|
# point of the code.
|
||||||
self.logger.warning(
|
self.logger.warning(
|
||||||
'A Connection error occured sending Growl '
|
'A Connection error occured sending Growl '
|
||||||
'notification to %s.' % self.host)
|
'notification to %s.' % self.host)
|
||||||
@ -191,3 +194,43 @@ class NotifyGrowl(NotifyBase):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def parse_url(url):
|
||||||
|
"""
|
||||||
|
Parses the URL and returns enough arguments that can allow
|
||||||
|
us to substantiate this object.
|
||||||
|
|
||||||
|
"""
|
||||||
|
results = NotifyBase.parse_url(url)
|
||||||
|
|
||||||
|
if not results:
|
||||||
|
# We're done early as we couldn't load the results
|
||||||
|
return results
|
||||||
|
|
||||||
|
# Apply our settings now
|
||||||
|
version = None
|
||||||
|
if 'version' in results['qsd'] and len(results['qsd']['version']):
|
||||||
|
# Allow the user to specify the version of the protocol to use.
|
||||||
|
try:
|
||||||
|
version = int(
|
||||||
|
unquote(results['qsd']['version']).strip().split('.')[0])
|
||||||
|
|
||||||
|
except (AttributeError, IndexError, TypeError, ValueError):
|
||||||
|
NotifyBase.logger.warning(
|
||||||
|
'An invalid Growl version of "%s" was specified and will '
|
||||||
|
'be ignored.' % results['qsd']['version']
|
||||||
|
)
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Because of the URL formatting, the password is actually where the
|
||||||
|
# username field is. For this reason, we just preform this small hack
|
||||||
|
# to make it (the URL) conform correctly. The following strips out the
|
||||||
|
# existing password entry (if exists) so that it can be swapped with
|
||||||
|
# the new one we specify.
|
||||||
|
results['user'] = None
|
||||||
|
results['password'] = results.get('user', None)
|
||||||
|
if version:
|
||||||
|
results['version'] = version
|
||||||
|
|
||||||
|
return results
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# -*- encoding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from . import NotifyGrowl
|
from . import NotifyGrowl
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
|
@ -10,8 +10,8 @@ programs using gntp
|
|||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from . import gntp.notifier
|
from .gntp import notifier
|
||||||
from . import gntp.shim
|
from .gntp import shim
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'mini',
|
'mini',
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# -*- encoding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# JSON Notify Wrapper
|
# JSON Notify Wrapper
|
||||||
#
|
#
|
||||||
@ -19,13 +19,12 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with apprise. If not, see <http://www.gnu.org/licenses/>.
|
# along with apprise. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from json import dumps
|
|
||||||
import requests
|
import requests
|
||||||
|
from json import dumps
|
||||||
|
|
||||||
from .NotifyBase import NotifyBase
|
from .NotifyBase import NotifyBase
|
||||||
from .NotifyBase import NotifyFormat
|
|
||||||
from .NotifyBase import NotifyImageSize
|
|
||||||
from .NotifyBase import HTTP_ERROR_MAP
|
from .NotifyBase import HTTP_ERROR_MAP
|
||||||
|
from ..common import NotifyImageSize
|
||||||
|
|
||||||
# Image Support (128x128)
|
# Image Support (128x128)
|
||||||
JSON_IMAGE_XY = NotifyImageSize.XY_128
|
JSON_IMAGE_XY = NotifyImageSize.XY_128
|
||||||
@ -37,19 +36,17 @@ class NotifyJSON(NotifyBase):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# The default protocol
|
# The default protocol
|
||||||
PROTOCOL = 'json'
|
protocol = 'json'
|
||||||
|
|
||||||
# The default secure protocol
|
# The default secure protocol
|
||||||
SECURE_PROTOCOL = 'jsons'
|
secure_protocol = 'jsons'
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
"""
|
"""
|
||||||
Initialize JSON Object
|
Initialize JSON Object
|
||||||
"""
|
"""
|
||||||
super(NotifyJSON, self).__init__(
|
super(NotifyJSON, self).__init__(
|
||||||
title_maxlen=250, body_maxlen=32768,
|
title_maxlen=250, body_maxlen=32768, image_size=JSON_IMAGE_XY,
|
||||||
image_size=JSON_IMAGE_XY,
|
|
||||||
notify_format=NotifyFormat.TEXT,
|
|
||||||
**kwargs)
|
**kwargs)
|
||||||
|
|
||||||
if self.secure:
|
if self.secure:
|
||||||
@ -64,7 +61,7 @@ class NotifyJSON(NotifyBase):
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def _notify(self, title, body, notify_type, **kwargs):
|
def notify(self, title, body, notify_type, **kwargs):
|
||||||
"""
|
"""
|
||||||
Perform JSON Notification
|
Perform JSON Notification
|
||||||
"""
|
"""
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
# -*- encoding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Join Notify Wrapper
|
# Join Notify Wrapper
|
||||||
#
|
#
|
||||||
# Copyright (C) 2014-2017 Chris Caron <lead2gold@gmail.com>
|
# Copyright (C) 2017 Chris Caron <lead2gold@gmail.com>
|
||||||
#
|
#
|
||||||
# This file is part of apprise.
|
# This file is part of apprise.
|
||||||
#
|
#
|
||||||
@ -29,25 +29,17 @@
|
|||||||
# You can download the app for your phone here:
|
# You can download the app for your phone here:
|
||||||
# https://play.google.com/store/apps/details?id=com.joaomgcd.join
|
# https://play.google.com/store/apps/details?id=com.joaomgcd.join
|
||||||
|
|
||||||
import requests
|
|
||||||
import re
|
import re
|
||||||
|
import requests
|
||||||
from urllib import urlencode
|
from urllib import urlencode
|
||||||
|
|
||||||
from .NotifyBase import NotifyBase
|
from .NotifyBase import NotifyBase
|
||||||
from .NotifyBase import NotifyFormat
|
|
||||||
from .NotifyBase import HTTP_ERROR_MAP
|
from .NotifyBase import HTTP_ERROR_MAP
|
||||||
from .NotifyBase import NotifyImageSize
|
from ..common import NotifyImageSize
|
||||||
|
|
||||||
# Join uses the http protocol with JSON requests
|
|
||||||
JOIN_URL = 'https://joinjoaomgcd.appspot.com/_ah/api/messaging/v1/sendPush'
|
|
||||||
|
|
||||||
# Token required as part of the API request
|
# Token required as part of the API request
|
||||||
VALIDATE_APIKEY = re.compile(r'[A-Za-z0-9]{32}')
|
VALIDATE_APIKEY = re.compile(r'[A-Za-z0-9]{32}')
|
||||||
|
|
||||||
# Default User
|
|
||||||
JOIN_DEFAULT_USER = 'apprise'
|
|
||||||
|
|
||||||
# Extend HTTP Error Messages
|
# Extend HTTP Error Messages
|
||||||
JOIN_HTTP_ERROR_MAP = dict(HTTP_ERROR_MAP.items() + {
|
JOIN_HTTP_ERROR_MAP = dict(HTTP_ERROR_MAP.items() + {
|
||||||
401: 'Unauthorized - Invalid Token.',
|
401: 'Unauthorized - Invalid Token.',
|
||||||
@ -75,25 +67,25 @@ class NotifyJoin(NotifyBase):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# The default protocol
|
# The default protocol
|
||||||
PROTOCOL = 'join'
|
protocol = 'join'
|
||||||
|
|
||||||
# The default secure protocol
|
# Join uses the http protocol with JSON requests
|
||||||
SECURE_PROTOCOL = 'join'
|
notify_url = \
|
||||||
|
'https://joinjoaomgcd.appspot.com/_ah/api/messaging/v1/sendPush'
|
||||||
|
|
||||||
def __init__(self, apikey, devices, **kwargs):
|
def __init__(self, apikey, devices, **kwargs):
|
||||||
"""
|
"""
|
||||||
Initialize Join Object
|
Initialize Join Object
|
||||||
"""
|
"""
|
||||||
super(NotifyJoin, self).__init__(
|
super(NotifyJoin, self).__init__(
|
||||||
title_maxlen=250, body_maxlen=1000,
|
title_maxlen=250, body_maxlen=1000, image_size=JOIN_IMAGE_XY,
|
||||||
image_size=JOIN_IMAGE_XY,
|
|
||||||
notify_format=NotifyFormat.TEXT,
|
|
||||||
**kwargs)
|
**kwargs)
|
||||||
|
|
||||||
if not VALIDATE_APIKEY.match(apikey.strip()):
|
if not VALIDATE_APIKEY.match(apikey.strip()):
|
||||||
self.logger.warning(
|
self.logger.warning(
|
||||||
'The first API Token specified (%s) is invalid.' % apikey,
|
'The first API Token specified (%s) is invalid.' % apikey,
|
||||||
)
|
)
|
||||||
|
|
||||||
raise TypeError(
|
raise TypeError(
|
||||||
'The first API Token specified (%s) is invalid.' % apikey,
|
'The first API Token specified (%s) is invalid.' % apikey,
|
||||||
)
|
)
|
||||||
@ -105,8 +97,10 @@ class NotifyJoin(NotifyBase):
|
|||||||
self.devices = filter(bool, DEVICE_LIST_DELIM.split(
|
self.devices = filter(bool, DEVICE_LIST_DELIM.split(
|
||||||
devices,
|
devices,
|
||||||
))
|
))
|
||||||
|
|
||||||
elif isinstance(devices, (tuple, list)):
|
elif isinstance(devices, (tuple, list)):
|
||||||
self.devices = devices
|
self.devices = devices
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.devices = list()
|
self.devices = list()
|
||||||
|
|
||||||
@ -114,11 +108,17 @@ class NotifyJoin(NotifyBase):
|
|||||||
self.logger.warning('No device(s) were specified.')
|
self.logger.warning('No device(s) were specified.')
|
||||||
raise TypeError('No device(s) were specified.')
|
raise TypeError('No device(s) were specified.')
|
||||||
|
|
||||||
def _notify(self, title, body, notify_type, **kwargs):
|
def notify(self, title, body, notify_type, **kwargs):
|
||||||
"""
|
"""
|
||||||
Perform Join Notification
|
Perform Join Notification
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# Limit results to just the first 2 line otherwise
|
||||||
|
# there is just to much content to display
|
||||||
|
body = re.split('[\r\n]+', body)
|
||||||
|
body[0] = body[0].strip('#').strip()
|
||||||
|
body = '\r\n'.join(body[0:2])
|
||||||
|
|
||||||
headers = {
|
headers = {
|
||||||
'User-Agent': self.app_id,
|
'User-Agent': self.app_id,
|
||||||
'Content-Type': 'application/x-www-form-urlencoded',
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
@ -158,11 +158,10 @@ class NotifyJoin(NotifyBase):
|
|||||||
url_args['icon'] = image_url
|
url_args['icon'] = image_url
|
||||||
|
|
||||||
# prepare payload
|
# prepare payload
|
||||||
payload = {
|
payload = {}
|
||||||
}
|
|
||||||
|
|
||||||
# Prepare the URL
|
# Prepare the URL
|
||||||
url = '%s?%s' % (JOIN_URL, urlencode(url_args))
|
url = '%s?%s' % (self.notify_url, urlencode(url_args))
|
||||||
|
|
||||||
self.logger.debug('Join POST URL: %s (cert_verify=%r)' % (
|
self.logger.debug('Join POST URL: %s (cert_verify=%r)' % (
|
||||||
url, self.verify_certificate,
|
url, self.verify_certificate,
|
||||||
@ -194,6 +193,7 @@ class NotifyJoin(NotifyBase):
|
|||||||
r.status_code))
|
r.status_code))
|
||||||
|
|
||||||
# self.logger.debug('Response Details: %s' % r.raw.read())
|
# self.logger.debug('Response Details: %s' % r.raw.read())
|
||||||
|
|
||||||
# Return; we're done
|
# Return; we're done
|
||||||
has_error = True
|
has_error = True
|
||||||
|
|
||||||
@ -210,3 +210,31 @@ class NotifyJoin(NotifyBase):
|
|||||||
self.throttle()
|
self.throttle()
|
||||||
|
|
||||||
return has_error
|
return has_error
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def parse_url(url):
|
||||||
|
"""
|
||||||
|
Parses the URL and returns enough arguments that can allow
|
||||||
|
us to substantiate this object.
|
||||||
|
|
||||||
|
"""
|
||||||
|
results = NotifyBase.parse_url(url)
|
||||||
|
|
||||||
|
if not results:
|
||||||
|
# We're done early as we couldn't load the results
|
||||||
|
return results
|
||||||
|
|
||||||
|
# Apply our settings now
|
||||||
|
try:
|
||||||
|
devices = ' '.join(
|
||||||
|
filter(bool, NotifyBase.split_path(results['fullpath'])))
|
||||||
|
|
||||||
|
except (AttributeError, IndexError):
|
||||||
|
# Force some bad values that will get caught
|
||||||
|
# in parsing later
|
||||||
|
devices = None
|
||||||
|
|
||||||
|
results['apikey'] = results['host']
|
||||||
|
results['devices'] = devices
|
||||||
|
|
||||||
|
return results
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# -*- encoding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# MatterMost Notify Wrapper
|
# MatterMost Notify Wrapper
|
||||||
#
|
#
|
||||||
@ -19,15 +19,14 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with apprise. If not, see <http://www.gnu.org/licenses/>.
|
# along with apprise. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from json import dumps
|
import re
|
||||||
import requests
|
import requests
|
||||||
|
from json import dumps
|
||||||
|
from urllib import unquote as unquote
|
||||||
|
|
||||||
from .NotifyBase import NotifyBase
|
from .NotifyBase import NotifyBase
|
||||||
from .NotifyBase import NotifyFormat
|
|
||||||
from .NotifyBase import NotifyImageSize
|
|
||||||
from .NotifyBase import HTTP_ERROR_MAP
|
from .NotifyBase import HTTP_ERROR_MAP
|
||||||
from .NotifyBase import NOTIFY_APPLICATION_ID
|
from ..common import NotifyImageSize
|
||||||
import re
|
|
||||||
|
|
||||||
# Some Reference Locations:
|
# Some Reference Locations:
|
||||||
# - https://docs.mattermost.com/developer/webhooks-incoming.html
|
# - https://docs.mattermost.com/developer/webhooks-incoming.html
|
||||||
@ -39,9 +38,6 @@ VALIDATE_AUTHTOKEN = re.compile(r'[A-Za-z0-9]{24,32}')
|
|||||||
# Image Support (72x72)
|
# Image Support (72x72)
|
||||||
MATTERMOST_IMAGE_XY = NotifyImageSize.XY_72
|
MATTERMOST_IMAGE_XY = NotifyImageSize.XY_72
|
||||||
|
|
||||||
# MATTERMOST uses the http protocol with JSON requests
|
|
||||||
MATTERMOST_PORT = 8065
|
|
||||||
|
|
||||||
|
|
||||||
class NotifyMatterMost(NotifyBase):
|
class NotifyMatterMost(NotifyBase):
|
||||||
"""
|
"""
|
||||||
@ -49,23 +45,25 @@ class NotifyMatterMost(NotifyBase):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# The default protocol
|
# The default protocol
|
||||||
PROTOCOL = 'mmost'
|
protocol = 'mmost'
|
||||||
|
|
||||||
# The default secure protocol
|
# The default secure protocol
|
||||||
SECURE_PROTOCOL = 'mmosts'
|
secure_protocol = 'mmosts'
|
||||||
|
|
||||||
|
# The default Mattermost port
|
||||||
|
default_port = 8065
|
||||||
|
|
||||||
def __init__(self, authtoken, channel=None, **kwargs):
|
def __init__(self, authtoken, channel=None, **kwargs):
|
||||||
"""
|
"""
|
||||||
Initialize MatterMost Object
|
Initialize MatterMost Object
|
||||||
"""
|
"""
|
||||||
super(NotifyMatterMost, self).__init__(
|
super(NotifyMatterMost, self).__init__(
|
||||||
title_maxlen=250, body_maxlen=4000,
|
title_maxlen=250, body_maxlen=4000, image_size=MATTERMOST_IMAGE_XY,
|
||||||
image_size=MATTERMOST_IMAGE_XY,
|
|
||||||
notify_format=NotifyFormat.TEXT,
|
|
||||||
**kwargs)
|
**kwargs)
|
||||||
|
|
||||||
if self.secure:
|
if self.secure:
|
||||||
self.schema = 'https'
|
self.schema = 'https'
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.schema = 'http'
|
self.schema = 'http'
|
||||||
|
|
||||||
@ -93,11 +91,11 @@ class NotifyMatterMost(NotifyBase):
|
|||||||
self.channel = channel
|
self.channel = channel
|
||||||
|
|
||||||
if not self.port:
|
if not self.port:
|
||||||
self.port = MATTERMOST_PORT
|
self.port = self.default_port
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def _notify(self, title, body, notify_type, **kwargs):
|
def notify(self, title, body, notify_type, **kwargs):
|
||||||
"""
|
"""
|
||||||
Perform MatterMost Notification
|
Perform MatterMost Notification
|
||||||
"""
|
"""
|
||||||
@ -117,7 +115,7 @@ class NotifyMatterMost(NotifyBase):
|
|||||||
payload['username'] = self.user
|
payload['username'] = self.user
|
||||||
|
|
||||||
else:
|
else:
|
||||||
payload['username'] = NOTIFY_APPLICATION_ID
|
payload['username'] = self.app_id
|
||||||
|
|
||||||
if self.channel:
|
if self.channel:
|
||||||
payload['channel'] = self.channel
|
payload['channel'] = self.channel
|
||||||
@ -170,3 +168,44 @@ class NotifyMatterMost(NotifyBase):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def parse_url(url):
|
||||||
|
"""
|
||||||
|
Parses the URL and returns enough arguments that can allow
|
||||||
|
us to substantiate this object.
|
||||||
|
|
||||||
|
"""
|
||||||
|
results = NotifyBase.parse_url(url)
|
||||||
|
|
||||||
|
if not results:
|
||||||
|
# We're done early as we couldn't load the results
|
||||||
|
return results
|
||||||
|
|
||||||
|
# Apply our settings now
|
||||||
|
try:
|
||||||
|
authtoken = filter(
|
||||||
|
bool, NotifyBase.split_path(results['fullpath']))[0]
|
||||||
|
|
||||||
|
except (AttributeError, IndexError):
|
||||||
|
# Force some bad values that will get caught
|
||||||
|
# in parsing later
|
||||||
|
authtoken = None
|
||||||
|
|
||||||
|
channel = None
|
||||||
|
if 'channel' in results['qsd'] and len(results['qsd']['channel']):
|
||||||
|
# Allow the user to specify the channel to post to
|
||||||
|
try:
|
||||||
|
channel = unquote(results['qsd']['channel']).strip()
|
||||||
|
|
||||||
|
except (AttributeError, TypeError, ValueError):
|
||||||
|
NotifyBase.logger.warning(
|
||||||
|
'An invalid MatterMost channel of "%s" was specified and '
|
||||||
|
'will be ignored.' % results['qsd']['channel']
|
||||||
|
)
|
||||||
|
pass
|
||||||
|
|
||||||
|
results['authtoken'] = authtoken
|
||||||
|
results['channel'] = channel
|
||||||
|
|
||||||
|
return results
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
# -*- encoding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Notify My Android (NMA) Notify Wrapper
|
# Notify My Android (NMA) Notify Wrapper
|
||||||
#
|
#
|
||||||
# Copyright (C) 2014-2017 Chris Caron <lead2gold@gmail.com>
|
# Copyright (C) 2017 Chris Caron <lead2gold@gmail.com>
|
||||||
#
|
#
|
||||||
# This file is part of apprise.
|
# This file is part of apprise.
|
||||||
#
|
#
|
||||||
@ -19,16 +19,14 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with apprise. If not, see <http://www.gnu.org/licenses/>.
|
# along with apprise. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import requests
|
|
||||||
import re
|
import re
|
||||||
|
import requests
|
||||||
|
from urllib import unquote
|
||||||
|
|
||||||
from .NotifyBase import NotifyBase
|
from .NotifyBase import NotifyBase
|
||||||
from .NotifyBase import NotifyFormat
|
from .NotifyBase import NotifyFormat
|
||||||
from .NotifyBase import HTTP_ERROR_MAP
|
from .NotifyBase import HTTP_ERROR_MAP
|
||||||
|
|
||||||
# Notify My Android uses the http protocol with JSON requests
|
|
||||||
NMA_URL = 'https://www.notifymyandroid.com/publicapi/notify'
|
|
||||||
|
|
||||||
# Extend HTTP Error Messages
|
# Extend HTTP Error Messages
|
||||||
NMA_HTTP_ERROR_MAP = dict(HTTP_ERROR_MAP.items() + {
|
NMA_HTTP_ERROR_MAP = dict(HTTP_ERROR_MAP.items() + {
|
||||||
400: 'Data is wrong format, invalid length or null.',
|
400: 'Data is wrong format, invalid length or null.',
|
||||||
@ -64,10 +62,10 @@ class NotifyMyAndroid(NotifyBase):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# The default protocol
|
# The default protocol
|
||||||
PROTOCOL = 'nma'
|
protocol = 'nma'
|
||||||
|
|
||||||
# The default secure protocol
|
# Notify My Android uses the http protocol with JSON requests
|
||||||
SECURE_PROTOCOL = 'nma'
|
notify_url = 'https://www.notifymyandroid.com/publicapi/notify'
|
||||||
|
|
||||||
def __init__(self, apikey, priority=NotifyMyAndroidPriority.NORMAL,
|
def __init__(self, apikey, priority=NotifyMyAndroidPriority.NORMAL,
|
||||||
devapikey=None, **kwargs):
|
devapikey=None, **kwargs):
|
||||||
@ -75,13 +73,12 @@ class NotifyMyAndroid(NotifyBase):
|
|||||||
Initialize Notify My Android Object
|
Initialize Notify My Android Object
|
||||||
"""
|
"""
|
||||||
super(NotifyMyAndroid, self).__init__(
|
super(NotifyMyAndroid, self).__init__(
|
||||||
title_maxlen=1000, body_maxlen=10000,
|
title_maxlen=1000, body_maxlen=10000, **kwargs)
|
||||||
notify_format=NotifyFormat.HTML,
|
|
||||||
**kwargs)
|
|
||||||
|
|
||||||
# The Priority of the message
|
# The Priority of the message
|
||||||
if priority not in NMA_PRIORITIES:
|
if priority not in NMA_PRIORITIES:
|
||||||
self.priority = NotifyMyAndroidPriority.NORMAL
|
self.priority = NotifyMyAndroidPriority.NORMAL
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.priority = priority
|
self.priority = priority
|
||||||
|
|
||||||
@ -106,7 +103,7 @@ class NotifyMyAndroid(NotifyBase):
|
|||||||
)
|
)
|
||||||
self.devapikey = devapikey
|
self.devapikey = devapikey
|
||||||
|
|
||||||
def _notify(self, title, body, notify_type, **kwargs):
|
def notify(self, title, body, notify_type, **kwargs):
|
||||||
"""
|
"""
|
||||||
Perform Notify My Android Notification
|
Perform Notify My Android Notification
|
||||||
"""
|
"""
|
||||||
@ -131,12 +128,12 @@ class NotifyMyAndroid(NotifyBase):
|
|||||||
payload['developerkey'] = self.devapikey
|
payload['developerkey'] = self.devapikey
|
||||||
|
|
||||||
self.logger.debug('NMA POST URL: %s (cert_verify=%r)' % (
|
self.logger.debug('NMA POST URL: %s (cert_verify=%r)' % (
|
||||||
NMA_URL, self.verify_certificate,
|
self.notify_url, self.verify_certificate,
|
||||||
))
|
))
|
||||||
self.logger.debug('NMA Payload: %s' % str(payload))
|
self.logger.debug('NMA Payload: %s' % str(payload))
|
||||||
try:
|
try:
|
||||||
r = requests.post(
|
r = requests.post(
|
||||||
NMA_URL,
|
self.notify_url,
|
||||||
data=payload,
|
data=payload,
|
||||||
headers=headers,
|
headers=headers,
|
||||||
verify=self.verify_certificate,
|
verify=self.verify_certificate,
|
||||||
@ -171,3 +168,33 @@ class NotifyMyAndroid(NotifyBase):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def parse_url(url):
|
||||||
|
"""
|
||||||
|
Parses the URL and returns enough arguments that can allow
|
||||||
|
us to substantiate this object.
|
||||||
|
|
||||||
|
"""
|
||||||
|
results = NotifyBase.parse_url(url)
|
||||||
|
|
||||||
|
if not results:
|
||||||
|
# We're done early as we couldn't load the results
|
||||||
|
return results
|
||||||
|
|
||||||
|
# Apply our settings now
|
||||||
|
results['notify_format'] = NotifyFormat.HTML
|
||||||
|
|
||||||
|
if 'format' in results['qsd'] and len(results['qsd']['format']):
|
||||||
|
# Extract email format (Text/Html)
|
||||||
|
try:
|
||||||
|
format = unquote(results['qsd']['format']).lower()
|
||||||
|
if len(format) > 0 and format[0] == 't':
|
||||||
|
results['notify_format'] = NotifyFormat.TEXT
|
||||||
|
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
results['apikey'] = results['host']
|
||||||
|
|
||||||
|
return results
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
# -*- encoding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Prowl Notify Wrapper
|
# Prowl Notify Wrapper
|
||||||
#
|
#
|
||||||
# Copyright (C) 2014-2017 Chris Caron <lead2gold@gmail.com>
|
# Copyright (C) 2017 Chris Caron <lead2gold@gmail.com>
|
||||||
#
|
#
|
||||||
# This file is part of apprise.
|
# This file is part of apprise.
|
||||||
#
|
#
|
||||||
@ -19,16 +19,12 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with apprise. If not, see <http://www.gnu.org/licenses/>.
|
# along with apprise. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import requests
|
|
||||||
import re
|
import re
|
||||||
|
import requests
|
||||||
|
|
||||||
from .NotifyBase import NotifyBase
|
from .NotifyBase import NotifyBase
|
||||||
from .NotifyBase import NotifyFormat
|
|
||||||
from .NotifyBase import HTTP_ERROR_MAP
|
from .NotifyBase import HTTP_ERROR_MAP
|
||||||
|
|
||||||
# Prowl uses the http protocol with JSON requests
|
|
||||||
PROWL_URL = 'https://api.prowlapp.com/publicapi/add'
|
|
||||||
|
|
||||||
# Used to validate API Key
|
# Used to validate API Key
|
||||||
VALIDATE_APIKEY = re.compile(r'[A-Za-z0-9]{40}')
|
VALIDATE_APIKEY = re.compile(r'[A-Za-z0-9]{40}')
|
||||||
|
|
||||||
@ -65,25 +61,23 @@ class NotifyProwl(NotifyBase):
|
|||||||
A wrapper for Prowl Notifications
|
A wrapper for Prowl Notifications
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# The default protocol
|
|
||||||
PROTOCOL = 'prowl'
|
|
||||||
|
|
||||||
# The default secure protocol
|
# The default secure protocol
|
||||||
SECURE_PROTOCOL = 'prowl'
|
secure_protocol = 'prowl'
|
||||||
|
|
||||||
def __init__(self, apikey, providerkey=None,
|
# Prowl uses the http protocol with JSON requests
|
||||||
priority=ProwlPriority.NORMAL,
|
notify_url = 'https://api.prowlapp.com/publicapi/add'
|
||||||
|
|
||||||
|
def __init__(self, apikey, providerkey=None, priority=ProwlPriority.NORMAL,
|
||||||
**kwargs):
|
**kwargs):
|
||||||
"""
|
"""
|
||||||
Initialize Prowl Object
|
Initialize Prowl Object
|
||||||
"""
|
"""
|
||||||
super(NotifyProwl, self).__init__(
|
super(NotifyProwl, self).__init__(
|
||||||
title_maxlen=1024, body_maxlen=10000,
|
title_maxlen=1024, body_maxlen=10000, **kwargs)
|
||||||
notify_format=NotifyFormat.TEXT,
|
|
||||||
**kwargs)
|
|
||||||
|
|
||||||
if priority not in PROWL_PRIORITIES:
|
if priority not in PROWL_PRIORITIES:
|
||||||
self.priority = ProwlPriority.NORMAL
|
self.priority = ProwlPriority.NORMAL
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.priority = priority
|
self.priority = priority
|
||||||
|
|
||||||
@ -112,7 +106,7 @@ class NotifyProwl(NotifyBase):
|
|||||||
# Store the Provider Key
|
# Store the Provider Key
|
||||||
self.providerkey = providerkey
|
self.providerkey = providerkey
|
||||||
|
|
||||||
def _notify(self, title, body, **kwargs):
|
def notify(self, title, body, **kwargs):
|
||||||
"""
|
"""
|
||||||
Perform Prowl Notification
|
Perform Prowl Notification
|
||||||
"""
|
"""
|
||||||
@ -135,12 +129,12 @@ class NotifyProwl(NotifyBase):
|
|||||||
payload['providerkey'] = self.providerkey
|
payload['providerkey'] = self.providerkey
|
||||||
|
|
||||||
self.logger.debug('Prowl POST URL: %s (cert_verify=%r)' % (
|
self.logger.debug('Prowl POST URL: %s (cert_verify=%r)' % (
|
||||||
PROWL_URL, self.verify_certificate,
|
self.notify_url, self.verify_certificate,
|
||||||
))
|
))
|
||||||
self.logger.debug('Prowl Payload: %s' % str(payload))
|
self.logger.debug('Prowl Payload: %s' % str(payload))
|
||||||
try:
|
try:
|
||||||
r = requests.post(
|
r = requests.post(
|
||||||
PROWL_URL,
|
self.notify_url,
|
||||||
data=payload,
|
data=payload,
|
||||||
headers=headers,
|
headers=headers,
|
||||||
verify=self.verify_certificate,
|
verify=self.verify_certificate,
|
||||||
@ -161,6 +155,7 @@ class NotifyProwl(NotifyBase):
|
|||||||
r.status_code))
|
r.status_code))
|
||||||
|
|
||||||
self.logger.debug('Response Details: %s' % r.raw.read())
|
self.logger.debug('Response Details: %s' % r.raw.read())
|
||||||
|
|
||||||
# Return; we're done
|
# Return; we're done
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
@ -175,3 +170,34 @@ class NotifyProwl(NotifyBase):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def parse_url(url):
|
||||||
|
"""
|
||||||
|
Parses the URL and returns enough arguments that can allow
|
||||||
|
us to substantiate this object.
|
||||||
|
|
||||||
|
"""
|
||||||
|
results = NotifyBase.parse_url(url)
|
||||||
|
|
||||||
|
if not results:
|
||||||
|
# We're done early as we couldn't load the results
|
||||||
|
return results
|
||||||
|
|
||||||
|
# Apply our settings now
|
||||||
|
|
||||||
|
# optionally find the provider key
|
||||||
|
try:
|
||||||
|
providerkey = filter(
|
||||||
|
bool, NotifyBase.split_path(results['fullpath']))[0]
|
||||||
|
|
||||||
|
if not providerkey:
|
||||||
|
providerkey = None
|
||||||
|
|
||||||
|
except (AttributeError, IndexError):
|
||||||
|
providerkey = None
|
||||||
|
|
||||||
|
results['apikey'] = results['host']
|
||||||
|
results['providerkey'] = providerkey
|
||||||
|
|
||||||
|
return results
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
# -*- encoding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# PushBullet Notify Wrapper
|
# PushBullet Notify Wrapper
|
||||||
#
|
#
|
||||||
# Copyright (C) 2014-2017 Chris Caron <lead2gold@gmail.com>
|
# Copyright (C) 2017 Chris Caron <lead2gold@gmail.com>
|
||||||
#
|
#
|
||||||
# This file is part of apprise.
|
# This file is part of apprise.
|
||||||
#
|
#
|
||||||
@ -19,21 +19,18 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with apprise. If not, see <http://www.gnu.org/licenses/>.
|
# along with apprise. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from json import dumps
|
|
||||||
import requests
|
|
||||||
import re
|
import re
|
||||||
|
import requests
|
||||||
|
from json import dumps
|
||||||
|
from urllib import unquote
|
||||||
|
|
||||||
from .NotifyBase import NotifyBase
|
from .NotifyBase import NotifyBase
|
||||||
from .NotifyBase import NotifyFormat
|
|
||||||
from .NotifyBase import HTTP_ERROR_MAP
|
from .NotifyBase import HTTP_ERROR_MAP
|
||||||
from .NotifyBase import IS_EMAIL_RE
|
from .NotifyBase import IS_EMAIL_RE
|
||||||
|
|
||||||
# Flag used as a placeholder to sending to all devices
|
# Flag used as a placeholder to sending to all devices
|
||||||
PUSHBULLET_SEND_TO_ALL = 'ALL_DEVICES'
|
PUSHBULLET_SEND_TO_ALL = 'ALL_DEVICES'
|
||||||
|
|
||||||
# PushBullet uses the http protocol with JSON requests
|
|
||||||
PUSHBULLET_URL = 'https://api.pushbullet.com/v2/pushes'
|
|
||||||
|
|
||||||
# Used to break apart list of potential recipients by their delimiter
|
# Used to break apart list of potential recipients by their delimiter
|
||||||
# into a usable list.
|
# into a usable list.
|
||||||
RECIPIENTS_LIST_DELIM = re.compile(r'[ \t\r\n,\\/]+')
|
RECIPIENTS_LIST_DELIM = re.compile(r'[ \t\r\n,\\/]+')
|
||||||
@ -50,34 +47,37 @@ class NotifyPushBullet(NotifyBase):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# The default protocol
|
# The default protocol
|
||||||
PROTOCOL = 'pbul'
|
protocol = 'pbul'
|
||||||
|
|
||||||
# The default secure protocol
|
# The default secure protocol
|
||||||
SECURE_PROTOCOL = 'pbul'
|
secure_protocol = 'pbul'
|
||||||
|
|
||||||
|
# PushBullet uses the http protocol with JSON requests
|
||||||
|
notify_url = 'https://api.pushbullet.com/v2/pushes'
|
||||||
|
|
||||||
def __init__(self, accesstoken, recipients=None, **kwargs):
|
def __init__(self, accesstoken, recipients=None, **kwargs):
|
||||||
"""
|
"""
|
||||||
Initialize PushBullet Object
|
Initialize PushBullet Object
|
||||||
"""
|
"""
|
||||||
super(NotifyPushBullet, self).__init__(
|
super(NotifyPushBullet, self).__init__(
|
||||||
title_maxlen=250, body_maxlen=32768,
|
title_maxlen=250, body_maxlen=32768, **kwargs)
|
||||||
notify_format=NotifyFormat.TEXT,
|
|
||||||
**kwargs)
|
|
||||||
|
|
||||||
self.accesstoken = accesstoken
|
self.accesstoken = accesstoken
|
||||||
if isinstance(recipients, basestring):
|
if isinstance(recipients, basestring):
|
||||||
self.recipients = filter(bool, RECIPIENTS_LIST_DELIM.split(
|
self.recipients = filter(bool, RECIPIENTS_LIST_DELIM.split(
|
||||||
recipients,
|
recipients,
|
||||||
))
|
))
|
||||||
|
|
||||||
elif isinstance(recipients, (tuple, list)):
|
elif isinstance(recipients, (tuple, list)):
|
||||||
self.recipients = recipients
|
self.recipients = recipients
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.recipients = list()
|
self.recipients = list()
|
||||||
|
|
||||||
if len(self.recipients) == 0:
|
if len(self.recipients) == 0:
|
||||||
self.recipients = (PUSHBULLET_SEND_TO_ALL, )
|
self.recipients = (PUSHBULLET_SEND_TO_ALL, )
|
||||||
|
|
||||||
def _notify(self, title, body, **kwargs):
|
def notify(self, title, body, **kwargs):
|
||||||
"""
|
"""
|
||||||
Perform PushBullet Notification
|
Perform PushBullet Notification
|
||||||
"""
|
"""
|
||||||
@ -122,12 +122,12 @@ class NotifyPushBullet(NotifyBase):
|
|||||||
"Recipient '%s' is a device" % recipient)
|
"Recipient '%s' is a device" % recipient)
|
||||||
|
|
||||||
self.logger.debug('PushBullet POST URL: %s (cert_verify=%r)' % (
|
self.logger.debug('PushBullet POST URL: %s (cert_verify=%r)' % (
|
||||||
PUSHBULLET_URL, self.verify_certificate,
|
self.notify_url, self.verify_certificate,
|
||||||
))
|
))
|
||||||
self.logger.debug('PushBullet Payload: %s' % str(payload))
|
self.logger.debug('PushBullet Payload: %s' % str(payload))
|
||||||
try:
|
try:
|
||||||
r = requests.post(
|
r = requests.post(
|
||||||
PUSHBULLET_URL,
|
self.notify_url,
|
||||||
data=dumps(payload),
|
data=dumps(payload),
|
||||||
headers=headers,
|
headers=headers,
|
||||||
auth=auth,
|
auth=auth,
|
||||||
@ -165,3 +165,28 @@ class NotifyPushBullet(NotifyBase):
|
|||||||
self.throttle()
|
self.throttle()
|
||||||
|
|
||||||
return not has_error
|
return not has_error
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def parse_url(url):
|
||||||
|
"""
|
||||||
|
Parses the URL and returns enough arguments that can allow
|
||||||
|
us to substantiate this object.
|
||||||
|
|
||||||
|
"""
|
||||||
|
results = NotifyBase.parse_url(url)
|
||||||
|
|
||||||
|
if not results:
|
||||||
|
# We're done early as we couldn't load the results
|
||||||
|
return results
|
||||||
|
|
||||||
|
# Apply our settings now
|
||||||
|
try:
|
||||||
|
recipients = unquote(results['fullpath'])
|
||||||
|
|
||||||
|
except AttributeError:
|
||||||
|
recipients = ''
|
||||||
|
|
||||||
|
results['accesstoken'] = results['host']
|
||||||
|
results['recipients'] = recipients
|
||||||
|
|
||||||
|
return results
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
# -*- encoding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Pushalot Notify Wrapper
|
# Pushalot Notify Wrapper
|
||||||
#
|
#
|
||||||
# Copyright (C) 2014-2017 Chris Caron <lead2gold@gmail.com>
|
# Copyright (C) 2017 Chris Caron <lead2gold@gmail.com>
|
||||||
#
|
#
|
||||||
# This file is part of apprise.
|
# This file is part of apprise.
|
||||||
#
|
#
|
||||||
@ -19,17 +19,13 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with apprise. If not, see <http://www.gnu.org/licenses/>.
|
# along with apprise. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from json import dumps
|
|
||||||
import requests
|
|
||||||
import re
|
import re
|
||||||
|
import requests
|
||||||
|
from json import dumps
|
||||||
|
|
||||||
from .NotifyBase import NotifyBase
|
from .NotifyBase import NotifyBase
|
||||||
from .NotifyBase import NotifyFormat
|
|
||||||
from .NotifyBase import NotifyImageSize
|
|
||||||
from .NotifyBase import HTTP_ERROR_MAP
|
from .NotifyBase import HTTP_ERROR_MAP
|
||||||
|
from ..common import NotifyImageSize
|
||||||
# Pushalot uses the http protocol with JSON requests
|
|
||||||
PUSHALOT_URL = 'https://pushalot.com/api/sendmessage'
|
|
||||||
|
|
||||||
# Image Support (72x72)
|
# Image Support (72x72)
|
||||||
PUSHALOT_IMAGE_XY = NotifyImageSize.XY_72
|
PUSHALOT_IMAGE_XY = NotifyImageSize.XY_72
|
||||||
@ -50,10 +46,13 @@ class NotifyPushalot(NotifyBase):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# The default protocol
|
# The default protocol
|
||||||
PROTOCOL = 'palot'
|
protocol = 'palot'
|
||||||
|
|
||||||
# The default secure protocol
|
# The default secure protocol
|
||||||
SECURE_PROTOCOL = 'palot'
|
secure_protocol = 'palot'
|
||||||
|
|
||||||
|
# Pushalot uses the http protocol with JSON requests
|
||||||
|
notify_url = 'https://pushalot.com/api/sendmessage'
|
||||||
|
|
||||||
def __init__(self, authtoken, is_important=False, **kwargs):
|
def __init__(self, authtoken, is_important=False, **kwargs):
|
||||||
"""
|
"""
|
||||||
@ -61,9 +60,7 @@ class NotifyPushalot(NotifyBase):
|
|||||||
"""
|
"""
|
||||||
super(NotifyPushalot, self).__init__(
|
super(NotifyPushalot, self).__init__(
|
||||||
title_maxlen=250, body_maxlen=32768,
|
title_maxlen=250, body_maxlen=32768,
|
||||||
image_size=PUSHALOT_IMAGE_XY,
|
image_size=PUSHALOT_IMAGE_XY, **kwargs)
|
||||||
notify_format=NotifyFormat.TEXT,
|
|
||||||
**kwargs)
|
|
||||||
|
|
||||||
# Is Important Flag
|
# Is Important Flag
|
||||||
self.is_important = is_important
|
self.is_important = is_important
|
||||||
@ -78,7 +75,7 @@ class NotifyPushalot(NotifyBase):
|
|||||||
'Invalid Pushalot Authorization Token Specified.'
|
'Invalid Pushalot Authorization Token Specified.'
|
||||||
)
|
)
|
||||||
|
|
||||||
def _notify(self, title, body, notify_type, **kwargs):
|
def notify(self, title, body, notify_type, **kwargs):
|
||||||
"""
|
"""
|
||||||
Perform Pushalot Notification
|
Perform Pushalot Notification
|
||||||
"""
|
"""
|
||||||
@ -105,16 +102,17 @@ class NotifyPushalot(NotifyBase):
|
|||||||
payload['Image'] = image_url
|
payload['Image'] = image_url
|
||||||
|
|
||||||
self.logger.debug('Pushalot POST URL: %s (cert_verify=%r)' % (
|
self.logger.debug('Pushalot POST URL: %s (cert_verify=%r)' % (
|
||||||
PUSHALOT_URL, self.verify_certificate,
|
self.notify_url, self.verify_certificate,
|
||||||
))
|
))
|
||||||
self.logger.debug('Pushalot Payload: %s' % str(payload))
|
self.logger.debug('Pushalot Payload: %s' % str(payload))
|
||||||
try:
|
try:
|
||||||
r = requests.post(
|
r = requests.post(
|
||||||
PUSHALOT_URL,
|
self.notify_url,
|
||||||
data=dumps(payload),
|
data=dumps(payload),
|
||||||
headers=headers,
|
headers=headers,
|
||||||
verify=self.verify_certificate,
|
verify=self.verify_certificate,
|
||||||
)
|
)
|
||||||
|
|
||||||
if r.status_code != requests.codes.ok:
|
if r.status_code != requests.codes.ok:
|
||||||
# We had a problem
|
# We had a problem
|
||||||
try:
|
try:
|
||||||
@ -144,3 +142,21 @@ class NotifyPushalot(NotifyBase):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def parse_url(url):
|
||||||
|
"""
|
||||||
|
Parses the URL and returns enough arguments that can allow
|
||||||
|
us to substantiate this object.
|
||||||
|
|
||||||
|
"""
|
||||||
|
results = NotifyBase.parse_url(url)
|
||||||
|
|
||||||
|
if not results:
|
||||||
|
# We're done early as we couldn't load the results
|
||||||
|
return results
|
||||||
|
|
||||||
|
# Apply our settings now
|
||||||
|
results['authtoken'] = results['host']
|
||||||
|
|
||||||
|
return results
|
||||||
|
@ -1,76 +0,0 @@
|
|||||||
# -*- encoding: utf-8 -*-
|
|
||||||
#
|
|
||||||
# Pushjet Notify Wrapper
|
|
||||||
#
|
|
||||||
# Copyright (C) 2014-2017 Chris Caron <lead2gold@gmail.com>
|
|
||||||
#
|
|
||||||
# This file is part of apprise.
|
|
||||||
#
|
|
||||||
# apprise is free software: you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation, either version 2 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# apprise is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with apprise. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
from pushjet import errors
|
|
||||||
from pushjet import pushjet
|
|
||||||
|
|
||||||
from .NotifyBase import NotifyBase
|
|
||||||
from .NotifyBase import NotifyFormat
|
|
||||||
|
|
||||||
|
|
||||||
class NotifyPushjet(NotifyBase):
|
|
||||||
"""
|
|
||||||
A wrapper for Pushjet Notifications
|
|
||||||
"""
|
|
||||||
|
|
||||||
# The default protocol
|
|
||||||
PROTOCOL = 'pjet'
|
|
||||||
|
|
||||||
# The default secure protocol
|
|
||||||
SECURE_PROTOCOL = 'pjets'
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
"""
|
|
||||||
Initialize Pushjet Object
|
|
||||||
"""
|
|
||||||
super(NotifyPushjet, self).__init__(
|
|
||||||
title_maxlen=250, body_maxlen=32768,
|
|
||||||
notify_format=NotifyFormat.TEXT,
|
|
||||||
**kwargs)
|
|
||||||
|
|
||||||
def _notify(self, title, body, notify_type):
|
|
||||||
"""
|
|
||||||
Perform Pushjet Notification
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
if self.user and self.host:
|
|
||||||
server = "http://"
|
|
||||||
if self.secure:
|
|
||||||
server = "https://"
|
|
||||||
|
|
||||||
server += self.host
|
|
||||||
if self.port:
|
|
||||||
server += ":" + str(self.port)
|
|
||||||
|
|
||||||
api = pushjet.Api(server)
|
|
||||||
service = api.Service(secret_key=self.user)
|
|
||||||
else:
|
|
||||||
api = pushjet.Api(pushjet.DEFAULT_API_URL)
|
|
||||||
service = api.Service(secret_key=self.host)
|
|
||||||
|
|
||||||
service.send(body, title)
|
|
||||||
|
|
||||||
except (errors.PushjetError, ValueError) as e:
|
|
||||||
self.logger.warning('Failed to send Pushjet notification.')
|
|
||||||
self.logger.debug('Pushjet Exception: %s' % str(e))
|
|
||||||
return False
|
|
||||||
|
|
||||||
return True
|
|
@ -1,8 +1,8 @@
|
|||||||
# -*- encoding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Pushjet Notify Wrapper
|
# Pushjet Notify Wrapper
|
||||||
#
|
#
|
||||||
# Copyright (C) 2014-2017 Chris Caron <lead2gold@gmail.com>
|
# Copyright (C) 2017 Chris Caron <lead2gold@gmail.com>
|
||||||
#
|
#
|
||||||
# This file is part of apprise.
|
# This file is part of apprise.
|
||||||
#
|
#
|
||||||
@ -23,7 +23,6 @@ from .pushjet import errors
|
|||||||
from .pushjet import pushjet
|
from .pushjet import pushjet
|
||||||
|
|
||||||
from ..NotifyBase import NotifyBase
|
from ..NotifyBase import NotifyBase
|
||||||
from ..NotifyBase import NotifyFormat
|
|
||||||
|
|
||||||
|
|
||||||
class NotifyPushjet(NotifyBase):
|
class NotifyPushjet(NotifyBase):
|
||||||
@ -32,21 +31,19 @@ class NotifyPushjet(NotifyBase):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# The default protocol
|
# The default protocol
|
||||||
PROTOCOL = 'pjet'
|
protocol = 'pjet'
|
||||||
|
|
||||||
# The default secure protocol
|
# The default secure protocol
|
||||||
SECURE_PROTOCOL = 'pjets'
|
secure_protocol = 'pjets'
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
"""
|
"""
|
||||||
Initialize Pushjet Object
|
Initialize Pushjet Object
|
||||||
"""
|
"""
|
||||||
super(NotifyPushjet, self).__init__(
|
super(NotifyPushjet, self).__init__(
|
||||||
title_maxlen=250, body_maxlen=32768,
|
title_maxlen=250, body_maxlen=32768, **kwargs)
|
||||||
notify_format=NotifyFormat.TEXT,
|
|
||||||
**kwargs)
|
|
||||||
|
|
||||||
def _notify(self, title, body, notify_type):
|
def notify(self, title, body, notify_type):
|
||||||
"""
|
"""
|
||||||
Perform Pushjet Notification
|
Perform Pushjet Notification
|
||||||
"""
|
"""
|
||||||
@ -62,6 +59,7 @@ class NotifyPushjet(NotifyBase):
|
|||||||
|
|
||||||
api = pushjet.Api(server)
|
api = pushjet.Api(server)
|
||||||
service = api.Service(secret_key=self.user)
|
service = api.Service(secret_key=self.user)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
api = pushjet.Api(pushjet.DEFAULT_API_URL)
|
api = pushjet.Api(pushjet.DEFAULT_API_URL)
|
||||||
service = api.Service(secret_key=self.host)
|
service = api.Service(secret_key=self.host)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# -*- encoding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from . import NotifyPushjet
|
from . import NotifyPushjet
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
# -*- encoding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Pushover Notify Wrapper
|
# Pushover Notify Wrapper
|
||||||
#
|
#
|
||||||
# Copyright (C) 2014-2017 Chris Caron <lead2gold@gmail.com>
|
# Copyright (C) 2017 Chris Caron <lead2gold@gmail.com>
|
||||||
#
|
#
|
||||||
# This file is part of apprise.
|
# This file is part of apprise.
|
||||||
#
|
#
|
||||||
@ -19,19 +19,16 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with apprise. If not, see <http://www.gnu.org/licenses/>.
|
# along with apprise. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import requests
|
|
||||||
import re
|
import re
|
||||||
|
import requests
|
||||||
|
from urllib import unquote
|
||||||
|
|
||||||
from .NotifyBase import NotifyBase
|
from .NotifyBase import NotifyBase
|
||||||
from .NotifyBase import NotifyFormat
|
|
||||||
from .NotifyBase import HTTP_ERROR_MAP
|
from .NotifyBase import HTTP_ERROR_MAP
|
||||||
|
|
||||||
# Flag used as a placeholder to sending to all devices
|
# Flag used as a placeholder to sending to all devices
|
||||||
PUSHOVER_SEND_TO_ALL = 'ALL_DEVICES'
|
PUSHOVER_SEND_TO_ALL = 'ALL_DEVICES'
|
||||||
|
|
||||||
# Pushover uses the http protocol with JSON requests
|
|
||||||
PUSHOVER_URL = 'https://api.pushover.net/1/messages.json'
|
|
||||||
|
|
||||||
# Used to validate API Key
|
# Used to validate API Key
|
||||||
VALIDATE_TOKEN = re.compile(r'[A-Za-z0-9]{30}')
|
VALIDATE_TOKEN = re.compile(r'[A-Za-z0-9]{30}')
|
||||||
|
|
||||||
@ -74,21 +71,21 @@ class NotifyPushover(NotifyBase):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# The default protocol
|
# The default protocol
|
||||||
PROTOCOL = 'pover'
|
protocol = 'pover'
|
||||||
|
|
||||||
# The default secure protocol
|
# The default secure protocol
|
||||||
SECURE_PROTOCOL = 'pover'
|
secure_protocol = 'pover'
|
||||||
|
|
||||||
|
# Pushover uses the http protocol with JSON requests
|
||||||
|
notify_url = 'https://api.pushover.net/1/messages.json'
|
||||||
|
|
||||||
def __init__(self, token, devices=None,
|
def __init__(self, token, devices=None,
|
||||||
priority=PushoverPriority.NORMAL,
|
priority=PushoverPriority.NORMAL, **kwargs):
|
||||||
**kwargs):
|
|
||||||
"""
|
"""
|
||||||
Initialize Pushover Object
|
Initialize Pushover Object
|
||||||
"""
|
"""
|
||||||
super(NotifyPushover, self).__init__(
|
super(NotifyPushover, self).__init__(
|
||||||
title_maxlen=250, body_maxlen=512,
|
title_maxlen=250, body_maxlen=512, **kwargs)
|
||||||
notify_format=NotifyFormat.TEXT,
|
|
||||||
**kwargs)
|
|
||||||
|
|
||||||
if not VALIDATE_TOKEN.match(token.strip()):
|
if not VALIDATE_TOKEN.match(token.strip()):
|
||||||
self.logger.warning(
|
self.logger.warning(
|
||||||
@ -135,7 +132,7 @@ class NotifyPushover(NotifyBase):
|
|||||||
'The user/group specified (%s) is invalid.' % self.user,
|
'The user/group specified (%s) is invalid.' % self.user,
|
||||||
)
|
)
|
||||||
|
|
||||||
def _notify(self, title, body, **kwargs):
|
def notify(self, title, body, **kwargs):
|
||||||
"""
|
"""
|
||||||
Perform Pushover Notification
|
Perform Pushover Notification
|
||||||
"""
|
"""
|
||||||
@ -174,12 +171,12 @@ class NotifyPushover(NotifyBase):
|
|||||||
payload['device'] = device
|
payload['device'] = device
|
||||||
|
|
||||||
self.logger.debug('Pushover POST URL: %s (cert_verify=%r)' % (
|
self.logger.debug('Pushover POST URL: %s (cert_verify=%r)' % (
|
||||||
PUSHOVER_URL, self.verify_certificate,
|
self.notify_url, self.verify_certificate,
|
||||||
))
|
))
|
||||||
self.logger.debug('Pushover Payload: %s' % str(payload))
|
self.logger.debug('Pushover Payload: %s' % str(payload))
|
||||||
try:
|
try:
|
||||||
r = requests.post(
|
r = requests.post(
|
||||||
PUSHOVER_URL,
|
self.notify_url,
|
||||||
data=payload,
|
data=payload,
|
||||||
headers=headers,
|
headers=headers,
|
||||||
auth=auth,
|
auth=auth,
|
||||||
@ -220,3 +217,28 @@ class NotifyPushover(NotifyBase):
|
|||||||
self.throttle()
|
self.throttle()
|
||||||
|
|
||||||
return has_error
|
return has_error
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def parse_url(url):
|
||||||
|
"""
|
||||||
|
Parses the URL and returns enough arguments that can allow
|
||||||
|
us to substantiate this object.
|
||||||
|
|
||||||
|
"""
|
||||||
|
results = NotifyBase.parse_url(url)
|
||||||
|
|
||||||
|
if not results:
|
||||||
|
# We're done early as we couldn't load the results
|
||||||
|
return results
|
||||||
|
|
||||||
|
# Apply our settings now
|
||||||
|
try:
|
||||||
|
devices = unquote(results['fullpath'])
|
||||||
|
|
||||||
|
except AttributeError:
|
||||||
|
devices = ''
|
||||||
|
|
||||||
|
results['token'] = results['host']
|
||||||
|
results['devices'] = devices
|
||||||
|
|
||||||
|
return results
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# -*- encoding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Notify Rocket.Chat Notify Wrapper
|
# Notify Rocket.Chat Notify Wrapper
|
||||||
#
|
#
|
||||||
@ -19,12 +19,12 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with apprise. If not, see <http://www.gnu.org/licenses/>.
|
# along with apprise. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import requests
|
|
||||||
import json
|
|
||||||
import re
|
import re
|
||||||
|
import requests
|
||||||
|
from json import loads
|
||||||
|
from urllib import unquote
|
||||||
|
|
||||||
from .NotifyBase import NotifyBase
|
from .NotifyBase import NotifyBase
|
||||||
from .NotifyBase import NotifyFormat
|
|
||||||
from .NotifyBase import HTTP_ERROR_MAP
|
from .NotifyBase import HTTP_ERROR_MAP
|
||||||
|
|
||||||
IS_CHANNEL = re.compile(r'^#(?P<name>[A-Za-z0-9]+)$')
|
IS_CHANNEL = re.compile(r'^#(?P<name>[A-Za-z0-9]+)$')
|
||||||
@ -47,19 +47,17 @@ class NotifyRocketChat(NotifyBase):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# The default protocol
|
# The default protocol
|
||||||
PROTOCOL = 'rocket'
|
protocol = 'rocket'
|
||||||
|
|
||||||
# The default secure protocol
|
# The default secure protocol
|
||||||
SECURE_PROTOCOL = 'rockets'
|
secure_protocol = 'rockets'
|
||||||
|
|
||||||
def __init__(self, recipients=None, **kwargs):
|
def __init__(self, recipients=None, **kwargs):
|
||||||
"""
|
"""
|
||||||
Initialize Notify Rocket.Chat Object
|
Initialize Notify Rocket.Chat Object
|
||||||
"""
|
"""
|
||||||
super(NotifyRocketChat, self).__init__(
|
super(NotifyRocketChat, self).__init__(
|
||||||
title_maxlen=200, body_maxlen=32768,
|
title_maxlen=200, body_maxlen=32768, **kwargs)
|
||||||
notify_format=NotifyFormat.TEXT,
|
|
||||||
**kwargs)
|
|
||||||
|
|
||||||
if self.secure:
|
if self.secure:
|
||||||
self.schema = 'https'
|
self.schema = 'https'
|
||||||
@ -127,7 +125,7 @@ class NotifyRocketChat(NotifyBase):
|
|||||||
'Authentication to Rocket.Chat server failed.'
|
'Authentication to Rocket.Chat server failed.'
|
||||||
)
|
)
|
||||||
|
|
||||||
def _notify(self, title, body, notify_type, **kwargs):
|
def notify(self, title, body, notify_type, **kwargs):
|
||||||
"""
|
"""
|
||||||
wrapper to send_notification since we can alert more then one channel
|
wrapper to send_notification since we can alert more then one channel
|
||||||
"""
|
"""
|
||||||
@ -235,7 +233,7 @@ class NotifyRocketChat(NotifyBase):
|
|||||||
|
|
||||||
else:
|
else:
|
||||||
self.logger.debug('Rocket.Chat authentication successful')
|
self.logger.debug('Rocket.Chat authentication successful')
|
||||||
response = json.loads(r.text)
|
response = loads(r.text)
|
||||||
if response.get('status') != "success":
|
if response.get('status') != "success":
|
||||||
self.logger.warning(
|
self.logger.warning(
|
||||||
'Could not authenticate with Rocket.Chat server.')
|
'Could not authenticate with Rocket.Chat server.')
|
||||||
@ -305,3 +303,25 @@ class NotifyRocketChat(NotifyBase):
|
|||||||
# We're no longer authenticated now
|
# We're no longer authenticated now
|
||||||
self.authenticated = False
|
self.authenticated = False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def parse_url(url):
|
||||||
|
"""
|
||||||
|
Parses the URL and returns enough arguments that can allow
|
||||||
|
us to substantiate this object.
|
||||||
|
|
||||||
|
"""
|
||||||
|
results = NotifyBase.parse_url(url)
|
||||||
|
|
||||||
|
if not results:
|
||||||
|
# We're done early as we couldn't load the results
|
||||||
|
return results
|
||||||
|
|
||||||
|
# Apply our settings now
|
||||||
|
try:
|
||||||
|
results['recipients'] = unquote(results['fullpath'])
|
||||||
|
|
||||||
|
except AttributeError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return results
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
# -*- encoding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Slack Notify Wrapper
|
# Slack Notify Wrapper
|
||||||
#
|
#
|
||||||
# Copyright (C) 2014-2017 Chris Caron <lead2gold@gmail.com>
|
# Copyright (C) 2017 Chris Caron <lead2gold@gmail.com>
|
||||||
#
|
#
|
||||||
# This file is part of apprise.
|
# This file is part of apprise.
|
||||||
#
|
#
|
||||||
@ -31,20 +31,14 @@
|
|||||||
# These are important <--------------^---------^---------------^
|
# These are important <--------------^---------^---------------^
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
import requests
|
|
||||||
import re
|
import re
|
||||||
|
import requests
|
||||||
from json import dumps
|
from json import dumps
|
||||||
from time import time
|
from time import time
|
||||||
|
|
||||||
from .NotifyBase import NotifyBase
|
from .NotifyBase import NotifyBase
|
||||||
from .NotifyBase import NotifyFormat
|
|
||||||
from .NotifyBase import HTTP_ERROR_MAP
|
from .NotifyBase import HTTP_ERROR_MAP
|
||||||
from .NotifyBase import HTML_NOTIFY_MAP
|
from ..common import NotifyImageSize
|
||||||
from .NotifyBase import NotifyImageSize
|
|
||||||
|
|
||||||
# Slack uses the http protocol with JSON requests
|
|
||||||
SLACK_URL = 'https://hooks.slack.com/services'
|
|
||||||
|
|
||||||
# Token required as part of the API request
|
# Token required as part of the API request
|
||||||
# /AAAAAAAAA/........./........................
|
# /AAAAAAAAA/........./........................
|
||||||
@ -81,11 +75,11 @@ class NotifySlack(NotifyBase):
|
|||||||
A wrapper for Slack Notifications
|
A wrapper for Slack Notifications
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# The default protocol
|
|
||||||
PROTOCOL = 'slack'
|
|
||||||
|
|
||||||
# The default secure protocol
|
# The default secure protocol
|
||||||
SECURE_PROTOCOL = 'slack'
|
secure_protocol = 'slack'
|
||||||
|
|
||||||
|
# Slack uses the http protocol with JSON requests
|
||||||
|
notify_url = 'https://hooks.slack.com/services'
|
||||||
|
|
||||||
def __init__(self, token_a, token_b, token_c, channels, **kwargs):
|
def __init__(self, token_a, token_b, token_c, channels, **kwargs):
|
||||||
"""
|
"""
|
||||||
@ -93,9 +87,7 @@ class NotifySlack(NotifyBase):
|
|||||||
"""
|
"""
|
||||||
super(NotifySlack, self).__init__(
|
super(NotifySlack, self).__init__(
|
||||||
title_maxlen=250, body_maxlen=1000,
|
title_maxlen=250, body_maxlen=1000,
|
||||||
image_size=SLACK_IMAGE_XY,
|
image_size=SLACK_IMAGE_XY, **kwargs)
|
||||||
notify_format=NotifyFormat.TEXT,
|
|
||||||
**kwargs)
|
|
||||||
|
|
||||||
if not VALIDATE_TOKEN_A.match(token_a.strip()):
|
if not VALIDATE_TOKEN_A.match(token_a.strip()):
|
||||||
self.logger.warning(
|
self.logger.warning(
|
||||||
@ -165,7 +157,7 @@ class NotifySlack(NotifyBase):
|
|||||||
re.IGNORECASE,
|
re.IGNORECASE,
|
||||||
)
|
)
|
||||||
|
|
||||||
def _notify(self, title, body, notify_type, **kwargs):
|
def notify(self, title, body, notify_type, **kwargs):
|
||||||
"""
|
"""
|
||||||
Perform Slack Notification
|
Perform Slack Notification
|
||||||
"""
|
"""
|
||||||
@ -186,7 +178,7 @@ class NotifySlack(NotifyBase):
|
|||||||
lambda x: self._re_formatting_map[x.group()], body,
|
lambda x: self._re_formatting_map[x.group()], body,
|
||||||
)
|
)
|
||||||
url = '%s/%s/%s/%s' % (
|
url = '%s/%s/%s/%s' % (
|
||||||
SLACK_URL,
|
self.notify_url,
|
||||||
self.token_a,
|
self.token_a,
|
||||||
self.token_b,
|
self.token_b,
|
||||||
self.token_c,
|
self.token_c,
|
||||||
@ -229,7 +221,7 @@ class NotifySlack(NotifyBase):
|
|||||||
'attachments': [{
|
'attachments': [{
|
||||||
'title': title,
|
'title': title,
|
||||||
'text': body,
|
'text': body,
|
||||||
'color': HTML_NOTIFY_MAP[notify_type],
|
'color': self.asset.html_color[notify_type],
|
||||||
# Time
|
# Time
|
||||||
'ts': time(),
|
'ts': time(),
|
||||||
'footer': self.app_id,
|
'footer': self.app_id,
|
||||||
@ -285,3 +277,48 @@ class NotifySlack(NotifyBase):
|
|||||||
self.throttle()
|
self.throttle()
|
||||||
|
|
||||||
return has_error
|
return has_error
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def parse_url(url):
|
||||||
|
"""
|
||||||
|
Parses the URL and returns enough arguments that can allow
|
||||||
|
us to substantiate this object.
|
||||||
|
|
||||||
|
"""
|
||||||
|
results = NotifyBase.parse_url(url)
|
||||||
|
|
||||||
|
if not results:
|
||||||
|
# We're done early as we couldn't load the results
|
||||||
|
return results
|
||||||
|
|
||||||
|
# Apply our settings now
|
||||||
|
|
||||||
|
# The first token is stored in the hostnamee
|
||||||
|
token_a = results['host']
|
||||||
|
|
||||||
|
# Now fetch the remaining tokens
|
||||||
|
try:
|
||||||
|
token_b, token_c = filter(
|
||||||
|
bool, NotifyBase.split_path(results['fullpath']))[0:2]
|
||||||
|
|
||||||
|
except (AttributeError, IndexError):
|
||||||
|
# Force some bad values that will get caught
|
||||||
|
# in parsing later
|
||||||
|
token_b = None
|
||||||
|
token_c = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
channels = '#'.join(filter(
|
||||||
|
bool, NotifyBase.split_path(results['fullpath']))[2:])
|
||||||
|
|
||||||
|
except (AttributeError, IndexError):
|
||||||
|
# Force some bad values that will get caught
|
||||||
|
# in parsing later
|
||||||
|
channels = None
|
||||||
|
|
||||||
|
results['token_a'] = token_a
|
||||||
|
results['token_b'] = token_b
|
||||||
|
results['token_c'] = token_c
|
||||||
|
results['channels'] = channels
|
||||||
|
|
||||||
|
return results
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
# -*- encoding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Telegram Notify Wrapper
|
# Telegram Notify Wrapper
|
||||||
#
|
#
|
||||||
# Copyright (C) 2016-2017 Chris Caron <lead2gold@gmail.com>
|
# Copyright (C) 2017 Chris Caron <lead2gold@gmail.com>
|
||||||
#
|
#
|
||||||
# This file is part of apprise.
|
# This file is part of apprise.
|
||||||
#
|
#
|
||||||
@ -53,9 +53,6 @@ from .NotifyBase import NotifyBase
|
|||||||
from .NotifyBase import NotifyFormat
|
from .NotifyBase import NotifyFormat
|
||||||
from .NotifyBase import HTTP_ERROR_MAP
|
from .NotifyBase import HTTP_ERROR_MAP
|
||||||
|
|
||||||
# Telegram uses the http protocol with JSON requests
|
|
||||||
TELEGRAM_BOT_URL = 'https://api.telegram.org/bot'
|
|
||||||
|
|
||||||
# Token required as part of the API request
|
# Token required as part of the API request
|
||||||
# allow the word 'bot' infront
|
# allow the word 'bot' infront
|
||||||
VALIDATE_BOT_TOKEN = re.compile(
|
VALIDATE_BOT_TOKEN = re.compile(
|
||||||
@ -86,11 +83,11 @@ class NotifyTelegram(NotifyBase):
|
|||||||
A wrapper for Telegram Notifications
|
A wrapper for Telegram Notifications
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# The default protocol
|
|
||||||
PROTOCOL = 'tgram'
|
|
||||||
|
|
||||||
# The default secure protocol
|
# The default secure protocol
|
||||||
SECURE_PROTOCOL = 'tgram'
|
secure_protocol = 'tgram'
|
||||||
|
|
||||||
|
# Telegram uses the http protocol with JSON requests
|
||||||
|
notify_url = 'https://api.telegram.org/bot'
|
||||||
|
|
||||||
def __init__(self, bot_token, chat_ids, **kwargs):
|
def __init__(self, bot_token, chat_ids, **kwargs):
|
||||||
"""
|
"""
|
||||||
@ -98,9 +95,7 @@ class NotifyTelegram(NotifyBase):
|
|||||||
"""
|
"""
|
||||||
super(NotifyTelegram, self).__init__(
|
super(NotifyTelegram, self).__init__(
|
||||||
title_maxlen=250, body_maxlen=4096,
|
title_maxlen=250, body_maxlen=4096,
|
||||||
image_size=TELEGRAM_IMAGE_XY,
|
image_size=TELEGRAM_IMAGE_XY, **kwargs)
|
||||||
notify_format=NotifyFormat.TEXT,
|
|
||||||
**kwargs)
|
|
||||||
|
|
||||||
if bot_token is None:
|
if bot_token is None:
|
||||||
raise TypeError(
|
raise TypeError(
|
||||||
@ -157,7 +152,7 @@ class NotifyTelegram(NotifyBase):
|
|||||||
}
|
}
|
||||||
|
|
||||||
url = '%s%s/%s' % (
|
url = '%s%s/%s' % (
|
||||||
TELEGRAM_BOT_URL,
|
self.notify_url,
|
||||||
self.bot_token,
|
self.bot_token,
|
||||||
'getMe'
|
'getMe'
|
||||||
)
|
)
|
||||||
@ -209,7 +204,7 @@ class NotifyTelegram(NotifyBase):
|
|||||||
|
|
||||||
return chat_id
|
return chat_id
|
||||||
|
|
||||||
def _notify(self, title, body, notify_type, **kwargs):
|
def notify(self, title, body, notify_type, **kwargs):
|
||||||
"""
|
"""
|
||||||
Perform Telegram Notification
|
Perform Telegram Notification
|
||||||
"""
|
"""
|
||||||
@ -224,13 +219,11 @@ class NotifyTelegram(NotifyBase):
|
|||||||
|
|
||||||
image_url = None
|
image_url = None
|
||||||
if self.include_image:
|
if self.include_image:
|
||||||
image_content = self.image_raw(
|
image_content = self.image_raw(notify_type)
|
||||||
notify_type,
|
|
||||||
)
|
|
||||||
if image_content is not None:
|
if image_content is not None:
|
||||||
# prepare our eimage URL
|
# prepare our image URL
|
||||||
image_url = '%s%s/%s' % (
|
image_url = '%s%s/%s' % (
|
||||||
TELEGRAM_BOT_URL,
|
self.notify_url,
|
||||||
self.bot_token,
|
self.bot_token,
|
||||||
'sendPhoto'
|
'sendPhoto'
|
||||||
)
|
)
|
||||||
@ -239,7 +232,7 @@ class NotifyTelegram(NotifyBase):
|
|||||||
files = {'photo': ('%s.png' % notify_type, image_content)}
|
files = {'photo': ('%s.png' % notify_type, image_content)}
|
||||||
|
|
||||||
url = '%s%s/%s' % (
|
url = '%s%s/%s' % (
|
||||||
TELEGRAM_BOT_URL,
|
self.notify_url,
|
||||||
self.bot_token,
|
self.bot_token,
|
||||||
'sendMessage'
|
'sendMessage'
|
||||||
)
|
)
|
||||||
@ -410,3 +403,87 @@ class NotifyTelegram(NotifyBase):
|
|||||||
self.throttle()
|
self.throttle()
|
||||||
|
|
||||||
return has_error
|
return has_error
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def parse_url(url):
|
||||||
|
"""
|
||||||
|
Parses the URL and returns enough arguments that can allow
|
||||||
|
us to substantiate this object.
|
||||||
|
|
||||||
|
"""
|
||||||
|
# super() is formatted slightly different when dealing with
|
||||||
|
# static method inheritance
|
||||||
|
results = NotifyBase.parse_url(url)
|
||||||
|
|
||||||
|
if results:
|
||||||
|
# We're done early
|
||||||
|
return results
|
||||||
|
|
||||||
|
# This is a dirty hack; but it's the only work around to
|
||||||
|
# tgram:// messages since the bot_token has a colon in it.
|
||||||
|
# It invalidates an normal URL.
|
||||||
|
|
||||||
|
# This hack searches for this bogus URL and corrects it
|
||||||
|
# so we can properly load it further down. The other
|
||||||
|
# alternative is to ask users to actually change the colon
|
||||||
|
# into a slash (which will work too), but it's more likely
|
||||||
|
# to cause confusion... So this is the next best thing
|
||||||
|
tgram = re.match(
|
||||||
|
r'(?P<protocol>%s://)(bot)?(?P<prefix>([a-z0-9_-]+)'
|
||||||
|
r'(:[a-z0-9_-]+)?@)?(?P<btoken_a>[0-9]+):+'
|
||||||
|
r'(?P<remaining>.*)$' % 'tgram',
|
||||||
|
url, re.I)
|
||||||
|
|
||||||
|
if not tgram:
|
||||||
|
# Content is simply not parseable
|
||||||
|
return None
|
||||||
|
|
||||||
|
if tgram.group('prefix'):
|
||||||
|
# Try again
|
||||||
|
result = NotifyBase.parse_url(
|
||||||
|
'%s%s%s/%s' % (
|
||||||
|
tgram.group('protocol'),
|
||||||
|
tgram.group('prefix'),
|
||||||
|
tgram.group('btoken_a'),
|
||||||
|
tgram.group('remaining'),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
# Try again
|
||||||
|
result = NotifyBase.parse_url(
|
||||||
|
'%s%s/%s' % (
|
||||||
|
tgram.group('protocol'),
|
||||||
|
tgram.group('btoken_a'),
|
||||||
|
tgram.group('remaining'),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
# The first token is stored in the hostnamee
|
||||||
|
bot_token_a = result['host']
|
||||||
|
|
||||||
|
# Now fetch the remaining tokens
|
||||||
|
try:
|
||||||
|
bot_token_b = filter(
|
||||||
|
bool, NotifyBase.split_path(result['fullpath']))[0]
|
||||||
|
|
||||||
|
bot_token = '%s:%s' % (bot_token_a, bot_token_b)
|
||||||
|
|
||||||
|
except (AttributeError, IndexError):
|
||||||
|
# Force a bad value that will get caught in parsing later
|
||||||
|
bot_token = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
chat_ids = ','.join(
|
||||||
|
filter(bool, NotifyBase.split_path(result['fullpath']))[1:])
|
||||||
|
|
||||||
|
except (AttributeError, IndexError):
|
||||||
|
# Force some bad values that will get caught
|
||||||
|
# in parsing later
|
||||||
|
chat_ids = None
|
||||||
|
|
||||||
|
# Return our results
|
||||||
|
return result + {
|
||||||
|
'bot_token': bot_token,
|
||||||
|
'chat_ids': chat_ids,
|
||||||
|
}.items()
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
# -*- encoding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# (Super) Toasty Notify Wrapper
|
# (Super) Toasty Notify Wrapper
|
||||||
#
|
#
|
||||||
# Copyright (C) 2014-2017 Chris Caron <lead2gold@gmail.com>
|
# Copyright (C) 2017 Chris Caron <lead2gold@gmail.com>
|
||||||
#
|
#
|
||||||
# This file is part of apprise.
|
# This file is part of apprise.
|
||||||
#
|
#
|
||||||
@ -19,17 +19,14 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with apprise. If not, see <http://www.gnu.org/licenses/>.
|
# along with apprise. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from urllib import quote
|
|
||||||
import requests
|
|
||||||
import re
|
import re
|
||||||
|
import requests
|
||||||
|
from urllib import quote
|
||||||
|
from urllib import unquote
|
||||||
|
|
||||||
from .NotifyBase import NotifyBase
|
from .NotifyBase import NotifyBase
|
||||||
from .NotifyBase import NotifyFormat
|
|
||||||
from .NotifyBase import NotifyImageSize
|
|
||||||
from .NotifyBase import HTTP_ERROR_MAP
|
from .NotifyBase import HTTP_ERROR_MAP
|
||||||
|
from ..common import NotifyImageSize
|
||||||
# Toasty uses the http protocol with JSON requests
|
|
||||||
TOASTY_URL = 'http://api.supertoasty.com/notify/'
|
|
||||||
|
|
||||||
# Image Support (128x128)
|
# Image Support (128x128)
|
||||||
TOASTY_IMAGE_XY = NotifyImageSize.XY_128
|
TOASTY_IMAGE_XY = NotifyImageSize.XY_128
|
||||||
@ -45,34 +42,34 @@ class NotifyToasty(NotifyBase):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# The default protocol
|
# The default protocol
|
||||||
PROTOCOL = 'toasty'
|
protocol = 'toasty'
|
||||||
|
|
||||||
# The default secure protocol
|
# Toasty uses the http protocol with JSON requests
|
||||||
SECURE_PROTOCOL = 'toasty'
|
notify_url = 'http://api.supertoasty.com/notify/'
|
||||||
|
|
||||||
def __init__(self, devices, **kwargs):
|
def __init__(self, devices, **kwargs):
|
||||||
"""
|
"""
|
||||||
Initialize Toasty Object
|
Initialize Toasty Object
|
||||||
"""
|
"""
|
||||||
super(NotifyToasty, self).__init__(
|
super(NotifyToasty, self).__init__(
|
||||||
title_maxlen=250, body_maxlen=32768,
|
title_maxlen=250, body_maxlen=32768, image_size=TOASTY_IMAGE_XY,
|
||||||
image_size=TOASTY_IMAGE_XY,
|
|
||||||
notify_format=NotifyFormat.TEXT,
|
|
||||||
**kwargs)
|
**kwargs)
|
||||||
|
|
||||||
if isinstance(devices, basestring):
|
if isinstance(devices, basestring):
|
||||||
self.devices = filter(bool, DEVICES_LIST_DELIM.split(
|
self.devices = filter(bool, DEVICES_LIST_DELIM.split(
|
||||||
devices,
|
devices,
|
||||||
))
|
))
|
||||||
|
|
||||||
elif isinstance(devices, (tuple, list)):
|
elif isinstance(devices, (tuple, list)):
|
||||||
self.devices = devices
|
self.devices = devices
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise TypeError('You must specify at least 1 device.')
|
raise TypeError('You must specify at least 1 device.')
|
||||||
|
|
||||||
if not self.user:
|
if not self.user:
|
||||||
raise TypeError('You must specify a username.')
|
raise TypeError('You must specify a username.')
|
||||||
|
|
||||||
def _notify(self, title, body, notify_type, **kwargs):
|
def notify(self, title, body, notify_type, **kwargs):
|
||||||
"""
|
"""
|
||||||
Perform Toasty Notification
|
Perform Toasty Notification
|
||||||
"""
|
"""
|
||||||
@ -105,7 +102,7 @@ class NotifyToasty(NotifyBase):
|
|||||||
payload['image'] = image_url
|
payload['image'] = image_url
|
||||||
|
|
||||||
# URL to transmit content via
|
# URL to transmit content via
|
||||||
url = '%s%s' % (TOASTY_URL, device)
|
url = '%s%s' % (self.notify_url, device)
|
||||||
|
|
||||||
self.logger.debug('Toasty POST URL: %s (cert_verify=%r)' % (
|
self.logger.debug('Toasty POST URL: %s (cert_verify=%r)' % (
|
||||||
url, self.verify_certificate,
|
url, self.verify_certificate,
|
||||||
@ -153,3 +150,28 @@ class NotifyToasty(NotifyBase):
|
|||||||
self.throttle()
|
self.throttle()
|
||||||
|
|
||||||
return has_error
|
return has_error
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def parse_url(url):
|
||||||
|
"""
|
||||||
|
Parses the URL and returns enough arguments that can allow
|
||||||
|
us to substantiate this object.
|
||||||
|
|
||||||
|
"""
|
||||||
|
results = NotifyBase.parse_url(url)
|
||||||
|
|
||||||
|
if not results:
|
||||||
|
# We're done early as we couldn't load the results
|
||||||
|
return results
|
||||||
|
|
||||||
|
# Apply our settings now
|
||||||
|
try:
|
||||||
|
devices = unquote(results['fullpath'])
|
||||||
|
|
||||||
|
except AttributeError:
|
||||||
|
devices = ''
|
||||||
|
|
||||||
|
# Store our devices
|
||||||
|
results['devices'] = '%s/%s' % (results['host'], devices)
|
||||||
|
|
||||||
|
return results
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
# -*- encoding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Twitter Notify Wrapper
|
# Twitter Notify Wrapper
|
||||||
#
|
#
|
||||||
# Copyright (C) 2014-2017 Chris Caron <lead2gold@gmail.com>
|
# Copyright (C) 2017 Chris Caron <lead2gold@gmail.com>
|
||||||
#
|
#
|
||||||
# This file is part of apprise.
|
# This file is part of apprise.
|
||||||
#
|
#
|
||||||
@ -21,7 +21,6 @@
|
|||||||
|
|
||||||
from . import tweepy
|
from . import tweepy
|
||||||
from ..NotifyBase import NotifyBase
|
from ..NotifyBase import NotifyBase
|
||||||
from ..NotifyBase import NotifyFormat
|
|
||||||
|
|
||||||
# Direct Messages have not image support
|
# Direct Messages have not image support
|
||||||
TWITTER_IMAGE_XY = None
|
TWITTER_IMAGE_XY = None
|
||||||
@ -33,11 +32,8 @@ class NotifyTwitter(NotifyBase):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# The default protocol
|
|
||||||
PROTOCOL = 'tweet'
|
|
||||||
|
|
||||||
# The default secure protocol
|
# The default secure protocol
|
||||||
SECURE_PROTOCOL = 'tweet'
|
secure_protocol = 'tweet'
|
||||||
|
|
||||||
def __init__(self, ckey, csecret, akey, asecret, **kwargs):
|
def __init__(self, ckey, csecret, akey, asecret, **kwargs):
|
||||||
"""
|
"""
|
||||||
@ -48,9 +44,7 @@ class NotifyTwitter(NotifyBase):
|
|||||||
"""
|
"""
|
||||||
super(NotifyTwitter, self).__init__(
|
super(NotifyTwitter, self).__init__(
|
||||||
title_maxlen=250, body_maxlen=4096,
|
title_maxlen=250, body_maxlen=4096,
|
||||||
image_size=TWITTER_IMAGE_XY,
|
image_size=TWITTER_IMAGE_XY, **kwargs)
|
||||||
notify_format=NotifyFormat.TEXT,
|
|
||||||
**kwargs)
|
|
||||||
|
|
||||||
if not ckey:
|
if not ckey:
|
||||||
raise TypeError(
|
raise TypeError(
|
||||||
@ -80,6 +74,7 @@ class NotifyTwitter(NotifyBase):
|
|||||||
try:
|
try:
|
||||||
# Attempt to Establish a connection to Twitter
|
# Attempt to Establish a connection to Twitter
|
||||||
self.auth = tweepy.OAuthHandler(ckey, csecret)
|
self.auth = tweepy.OAuthHandler(ckey, csecret)
|
||||||
|
|
||||||
# Apply our Access Tokens
|
# Apply our Access Tokens
|
||||||
self.auth.set_access_token(akey, asecret)
|
self.auth.set_access_token(akey, asecret)
|
||||||
|
|
||||||
@ -91,7 +86,7 @@ class NotifyTwitter(NotifyBase):
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def _notify(self, title, body, notify_type, **kwargs):
|
def notify(self, title, body, notify_type, **kwargs):
|
||||||
"""
|
"""
|
||||||
Perform Twitter Notification
|
Perform Twitter Notification
|
||||||
"""
|
"""
|
||||||
@ -114,3 +109,40 @@ class NotifyTwitter(NotifyBase):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def parse_url(url):
|
||||||
|
"""
|
||||||
|
Parses the URL and returns enough arguments that can allow
|
||||||
|
us to substantiate this object.
|
||||||
|
|
||||||
|
"""
|
||||||
|
results = NotifyBase.parse_url(url)
|
||||||
|
|
||||||
|
if not results:
|
||||||
|
# We're done early as we couldn't load the results
|
||||||
|
return results
|
||||||
|
|
||||||
|
# Apply our settings now
|
||||||
|
|
||||||
|
# The first token is stored in the hostnamee
|
||||||
|
consumer_key = results['host']
|
||||||
|
|
||||||
|
# Now fetch the remaining tokens
|
||||||
|
try:
|
||||||
|
consumer_secret, access_token_key, access_token_secret = \
|
||||||
|
filter(bool, NotifyBase.split_path(results['fullpath']))[0:3]
|
||||||
|
|
||||||
|
except (AttributeError, IndexError):
|
||||||
|
# Force some bad values that will get caught
|
||||||
|
# in parsing later
|
||||||
|
consumer_secret = None
|
||||||
|
access_token_key = None
|
||||||
|
access_token_secret = None
|
||||||
|
|
||||||
|
results['ckey'] = consumer_key,
|
||||||
|
results['csecret'] = consumer_secret,
|
||||||
|
results['akey'] = access_token_key,
|
||||||
|
results['asecret'] = access_token_secret,
|
||||||
|
|
||||||
|
return results
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# -*- encoding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from . import NotifyTwitter
|
from . import NotifyTwitter
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
# -*- encoding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# XBMC Notify Wrapper
|
# XBMC Notify Wrapper
|
||||||
#
|
#
|
||||||
# Copyright (C) 2014-2017 Chris Caron <lead2gold@gmail.com>
|
# Copyright (C) 2017 Chris Caron <lead2gold@gmail.com>
|
||||||
#
|
#
|
||||||
# This file is part of apprise.
|
# This file is part of apprise.
|
||||||
#
|
#
|
||||||
@ -19,22 +19,22 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with apprise. If not, see <http://www.gnu.org/licenses/>.
|
# along with apprise. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from json import dumps
|
import re
|
||||||
import requests
|
import requests
|
||||||
|
from json import dumps
|
||||||
|
|
||||||
from .NotifyBase import NotifyBase
|
from .NotifyBase import NotifyBase
|
||||||
from .NotifyBase import NotifyFormat
|
|
||||||
from .NotifyBase import NotifyType
|
|
||||||
from .NotifyBase import NotifyImageSize
|
|
||||||
from .NotifyBase import HTTP_ERROR_MAP
|
from .NotifyBase import HTTP_ERROR_MAP
|
||||||
|
from ..common import NotifyType
|
||||||
|
from ..common import NotifyImageSize
|
||||||
|
|
||||||
# Image Support (128x128)
|
# Image Support (128x128)
|
||||||
XBMC_IMAGE_XY = NotifyImageSize.XY_128
|
XBMC_IMAGE_XY = NotifyImageSize.XY_128
|
||||||
|
|
||||||
# XBMC uses the http protocol with JSON requests
|
# XBMC uses v2
|
||||||
XBMC_PORT = 8080
|
|
||||||
|
|
||||||
XBMC_PROTOCOL_V2 = 2
|
XBMC_PROTOCOL_V2 = 2
|
||||||
|
|
||||||
|
# Kodi uses v6
|
||||||
XBMC_PROTOCOL_V6 = 6
|
XBMC_PROTOCOL_V6 = 6
|
||||||
|
|
||||||
SUPPORTED_XBMC_PROTOCOLS = (
|
SUPPORTED_XBMC_PROTOCOLS = (
|
||||||
@ -48,11 +48,14 @@ class NotifyXBMC(NotifyBase):
|
|||||||
A wrapper for XBMC/KODI Notifications
|
A wrapper for XBMC/KODI Notifications
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# The default protocol
|
# The default protocols
|
||||||
PROTOCOL = ('xbmc', 'kodi')
|
protocol = ('xbmc', 'kodi')
|
||||||
|
|
||||||
# The default secure protocol
|
# The default secure protocols
|
||||||
SECURE_PROTOCOL = ('xbmc', 'kodis')
|
secure_protocol = ('xbmc', 'kodis')
|
||||||
|
|
||||||
|
# XBMC uses the http protocol with JSON requests
|
||||||
|
default_port = 8080
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
"""
|
"""
|
||||||
@ -60,17 +63,16 @@ class NotifyXBMC(NotifyBase):
|
|||||||
"""
|
"""
|
||||||
super(NotifyXBMC, self).__init__(
|
super(NotifyXBMC, self).__init__(
|
||||||
title_maxlen=250, body_maxlen=32768,
|
title_maxlen=250, body_maxlen=32768,
|
||||||
image_size=XBMC_IMAGE_XY,
|
image_size=XBMC_IMAGE_XY, **kwargs)
|
||||||
notify_format=NotifyFormat.TEXT,
|
|
||||||
**kwargs)
|
|
||||||
|
|
||||||
if self.secure:
|
if self.secure:
|
||||||
self.schema = 'https'
|
self.schema = 'https'
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.schema = 'http'
|
self.schema = 'http'
|
||||||
|
|
||||||
if not self.port:
|
if not self.port:
|
||||||
self.port = XBMC_PORT
|
self.port = self.default_port
|
||||||
|
|
||||||
self.protocol = kwargs.get('protocol', XBMC_PROTOCOL_V2)
|
self.protocol = kwargs.get('protocol', XBMC_PROTOCOL_V2)
|
||||||
if self.protocol not in SUPPORTED_XBMC_PROTOCOLS:
|
if self.protocol not in SUPPORTED_XBMC_PROTOCOLS:
|
||||||
@ -152,11 +154,17 @@ class NotifyXBMC(NotifyBase):
|
|||||||
|
|
||||||
return (headers, dumps(payload))
|
return (headers, dumps(payload))
|
||||||
|
|
||||||
def _notify(self, title, body, notify_type, **kwargs):
|
def notify(self, title, body, notify_type, **kwargs):
|
||||||
"""
|
"""
|
||||||
Perform XBMC Notification
|
Perform XBMC Notification
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# Limit results to just the first 2 line otherwise
|
||||||
|
# there is just to much content to display
|
||||||
|
body = re.split('[\r\n]+', body)
|
||||||
|
body[0] = body[0].strip('#').strip()
|
||||||
|
body = '\r\n'.join(body[0:2])
|
||||||
|
|
||||||
if self.protocol == XBMC_PROTOCOL_V2:
|
if self.protocol == XBMC_PROTOCOL_V2:
|
||||||
# XBMC v2.0
|
# XBMC v2.0
|
||||||
(headers, payload) = self._payload_20(
|
(headers, payload) = self._payload_20(
|
||||||
@ -205,6 +213,7 @@ class NotifyXBMC(NotifyBase):
|
|||||||
|
|
||||||
# Return; we're done
|
# Return; we're done
|
||||||
return False
|
return False
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.logger.info('Sent XBMC/KODI notification.')
|
self.logger.info('Sent XBMC/KODI notification.')
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
# -*- encoding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# XML Notify Wrapper
|
# XML Notify Wrapper
|
||||||
#
|
#
|
||||||
# Copyright (C) 2014-2017 Chris Caron <lead2gold@gmail.com>
|
# Copyright (C) 2017 Chris Caron <lead2gold@gmail.com>
|
||||||
#
|
#
|
||||||
# This file is part of apprise.
|
# This file is part of apprise.
|
||||||
#
|
#
|
||||||
@ -19,14 +19,13 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with apprise. If not, see <http://www.gnu.org/licenses/>.
|
# along with apprise. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from urllib import quote
|
|
||||||
import requests
|
|
||||||
import re
|
import re
|
||||||
|
import requests
|
||||||
|
from urllib import quote
|
||||||
|
|
||||||
from .NotifyBase import NotifyBase
|
from .NotifyBase import NotifyBase
|
||||||
from .NotifyBase import NotifyFormat
|
|
||||||
from .NotifyBase import NotifyImageSize
|
|
||||||
from .NotifyBase import HTTP_ERROR_MAP
|
from .NotifyBase import HTTP_ERROR_MAP
|
||||||
|
from ..common import NotifyImageSize
|
||||||
|
|
||||||
# Image Support (128x128)
|
# Image Support (128x128)
|
||||||
XML_IMAGE_XY = NotifyImageSize.XY_128
|
XML_IMAGE_XY = NotifyImageSize.XY_128
|
||||||
@ -38,10 +37,10 @@ class NotifyXML(NotifyBase):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# The default protocol
|
# The default protocol
|
||||||
PROTOCOL = 'xml'
|
protocol = 'xml'
|
||||||
|
|
||||||
# The default secure protocol
|
# The default secure protocol
|
||||||
SECURE_PROTOCOL = 'xmls'
|
secure_protocol = 'xmls'
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
"""
|
"""
|
||||||
@ -49,9 +48,7 @@ class NotifyXML(NotifyBase):
|
|||||||
"""
|
"""
|
||||||
super(NotifyXML, self).__init__(
|
super(NotifyXML, self).__init__(
|
||||||
title_maxlen=250, body_maxlen=32768,
|
title_maxlen=250, body_maxlen=32768,
|
||||||
image_size=XML_IMAGE_XY,
|
image_size=XML_IMAGE_XY, **kwargs)
|
||||||
notify_format=NotifyFormat.TEXT,
|
|
||||||
**kwargs)
|
|
||||||
|
|
||||||
self.payload = """<?xml version='1.0' encoding='utf-8'?>
|
self.payload = """<?xml version='1.0' encoding='utf-8'?>
|
||||||
<soapenv:Envelope
|
<soapenv:Envelope
|
||||||
@ -80,7 +77,7 @@ class NotifyXML(NotifyBase):
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def _notify(self, title, body, notify_type, **kwargs):
|
def notify(self, title, body, notify_type, **kwargs):
|
||||||
"""
|
"""
|
||||||
Perform XML Notification
|
Perform XML Notification
|
||||||
"""
|
"""
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
# -*- encoding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Our service wrappers
|
# Our service wrappers
|
||||||
#
|
#
|
||||||
# Copyright (C) 2014-2017 Chris Caron <lead2gold@gmail.com>
|
# Copyright (C) 2017 Chris Caron <lead2gold@gmail.com>
|
||||||
#
|
#
|
||||||
# This file is part of apprise.
|
# This file is part of apprise.
|
||||||
#
|
#
|
||||||
@ -40,11 +40,19 @@ from .NotifyTelegram import NotifyTelegram
|
|||||||
from .NotifyMatterMost import NotifyMatterMost
|
from .NotifyMatterMost import NotifyMatterMost
|
||||||
from .NotifyPushjet import NotifyPushjet
|
from .NotifyPushjet import NotifyPushjet
|
||||||
|
|
||||||
|
from ..common import NotifyImageSize
|
||||||
|
from ..common import NOTIFY_IMAGE_SIZES
|
||||||
|
from ..common import NotifyType
|
||||||
|
from ..common import NOTIFY_TYPES
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
# Notification Services
|
# Notification Services
|
||||||
'NotifyBoxcar', 'NotifyEmail', 'NotifyFaast', 'NotifyGrowl', 'NotifyJSON',
|
'NotifyBoxcar', 'NotifyEmail', 'NotifyFaast', 'NotifyGrowl', 'NotifyJSON',
|
||||||
'NotifyMyAndroid', 'NotifyProwl', 'NotifyPushalot', 'NotifyPushBullet',
|
'NotifyMyAndroid', 'NotifyProwl', 'NotifyPushalot', 'NotifyPushBullet',
|
||||||
'NotifyPushover', 'NotifyRocketChat', 'NotifyToasty', 'NotifyTwitter',
|
'NotifyPushover', 'NotifyRocketChat', 'NotifyToasty', 'NotifyTwitter',
|
||||||
'NotifyXBMC', 'NotifyXML', 'NotifySlack', 'NotifyJoin', 'NotifyTelegram',
|
'NotifyXBMC', 'NotifyXML', 'NotifySlack', 'NotifyJoin', 'NotifyTelegram',
|
||||||
'NotifyMatterMost', 'NotifyPushjet'
|
'NotifyMatterMost', 'NotifyPushjet',
|
||||||
|
|
||||||
|
# Reference
|
||||||
|
'NotifyImageSize', 'NOTIFY_IMAGE_SIZES', 'NotifyType', 'NOTIFY_TYPES',
|
||||||
]
|
]
|
||||||
|
@ -1,26 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
|
|
||||||
def _main():
|
|
||||||
"""\
|
|
||||||
Usage: apprise [options] [URL ...]
|
|
||||||
|
|
||||||
Send notifications to a variety of different supported services.
|
|
||||||
See also https://github.com/caronc/apprise
|
|
||||||
|
|
||||||
URL The notification service URL
|
|
||||||
|
|
||||||
Options:
|
|
||||||
|
|
||||||
-h, --help show this message
|
|
||||||
-t TITLE, --title TITLE Specify a notification title.
|
|
||||||
-b BODY, --body BODY Specify a notification body.
|
|
||||||
-i IMGURL, --image IMGURL Specify an image to send with the notification.
|
|
||||||
The image should be in the format of a URL
|
|
||||||
string such as file:///local/path/to/file.png or
|
|
||||||
a remote site like: http://my.host/my.image.png.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
_main()
|
|
75
cli/notify.py
Executable file
@ -0,0 +1,75 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import click
|
||||||
|
import logging
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from apprise import Apprise
|
||||||
|
|
||||||
|
# Logging
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
logger.setLevel(logging.INFO)
|
||||||
|
|
||||||
|
ch = logging.StreamHandler(sys.stdout)
|
||||||
|
ch.setLevel(logging.INFO)
|
||||||
|
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
|
||||||
|
ch.setFormatter(formatter)
|
||||||
|
logger.addHandler(ch)
|
||||||
|
|
||||||
|
|
||||||
|
@click.command()
|
||||||
|
@click.option('--title', '-t', default=None, type=str,
|
||||||
|
help='Specify the message title.')
|
||||||
|
@click.option('--body', '-b', default=None, type=str,
|
||||||
|
help='Specify the message body.')
|
||||||
|
@click.option('--theme', '-T', default='default', type=str,
|
||||||
|
help='Specify the default theme.')
|
||||||
|
@click.option('--image-url', '-i', default=None, type=str,
|
||||||
|
help='Specify the image URL.')
|
||||||
|
@click.argument('urls', nargs=-1)
|
||||||
|
def _main(title, body, urls, theme, image_url):
|
||||||
|
"""
|
||||||
|
Notify all specified servers
|
||||||
|
|
||||||
|
"""
|
||||||
|
if not (title and body):
|
||||||
|
logger.error('Neither a message body or title was specified.')
|
||||||
|
return 1
|
||||||
|
|
||||||
|
if not urls:
|
||||||
|
logger.error('You must specify at least one server URL')
|
||||||
|
return 1
|
||||||
|
|
||||||
|
# Create our object
|
||||||
|
apprise = Apprise()
|
||||||
|
|
||||||
|
# Load our inventory up
|
||||||
|
for url in urls:
|
||||||
|
apprise.add(url)
|
||||||
|
|
||||||
|
# now print it out
|
||||||
|
apprise.notify(title=title, body=body)
|
||||||
|
|
||||||
|
return 0
|
||||||
|
# """\
|
||||||
|
# Usage: apprise [options] [URL ...]
|
||||||
|
#
|
||||||
|
# Send notifications to a variety of different supported services.
|
||||||
|
# See also https://github.com/caronc/apprise
|
||||||
|
#
|
||||||
|
# URL The notification service URL
|
||||||
|
#
|
||||||
|
# Options:
|
||||||
|
#
|
||||||
|
# -h, --help show this message
|
||||||
|
# -t TITLE, --title TITLE Specify a notification title.
|
||||||
|
# -b BODY, --body BODY Specify a notification body.
|
||||||
|
# -i IMGURL, --image IMGURL Specify an image to send with the notification.
|
||||||
|
# The image should be in the format of a URL
|
||||||
|
# string such as file:///local/path/to/file.png or
|
||||||
|
# a remote site like: http://my.host/my.image.png.
|
||||||
|
# """
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
exit(_main())
|
@ -6,4 +6,4 @@ requests-oauthlib
|
|||||||
oauthlib
|
oauthlib
|
||||||
urllib3
|
urllib3
|
||||||
six
|
six
|
||||||
click
|
click >= 5.0
|
||||||
|
10
setup.cfg
@ -2,3 +2,13 @@
|
|||||||
tag_build =
|
tag_build =
|
||||||
tag_date = 0
|
tag_date = 0
|
||||||
tag_svn_revision = 0
|
tag_svn_revision = 0
|
||||||
|
|
||||||
|
[pycodestyle]
|
||||||
|
# We exclude packages we don't maintain
|
||||||
|
exclude = gntp,tweepy,pushjet
|
||||||
|
ignore = E722
|
||||||
|
statistics = True
|
||||||
|
|
||||||
|
[coverage:run]
|
||||||
|
source=apprise
|
||||||
|
omit=*/gntp/*,*/tweepy/*,*/pushjet/*
|
||||||
|
11
setup.py
@ -19,6 +19,7 @@
|
|||||||
import os
|
import os
|
||||||
try:
|
try:
|
||||||
from setuptools import setup
|
from setuptools import setup
|
||||||
|
|
||||||
except ImportError:
|
except ImportError:
|
||||||
from distutils.core import setup
|
from distutils.core import setup
|
||||||
|
|
||||||
@ -44,11 +45,11 @@ setup(
|
|||||||
author='Chris Caron',
|
author='Chris Caron',
|
||||||
author_email='lead2gold@gmail.com',
|
author_email='lead2gold@gmail.com',
|
||||||
packages=find_packages(),
|
packages=find_packages(),
|
||||||
package_data={
|
|
||||||
'apprise': ['var/*'],
|
|
||||||
},
|
|
||||||
include_package_data=True,
|
include_package_data=True,
|
||||||
scripts=['bin/apprise.py', ],
|
package_data={
|
||||||
|
'apprise': ['assets'],
|
||||||
|
},
|
||||||
|
scripts=['cli/notify.py', ],
|
||||||
install_requires=open('requirements.txt').readlines(),
|
install_requires=open('requirements.txt').readlines(),
|
||||||
classifiers=(
|
classifiers=(
|
||||||
'Development Status :: 4 - Beta',
|
'Development Status :: 4 - Beta',
|
||||||
@ -61,4 +62,6 @@ setup(
|
|||||||
),
|
),
|
||||||
entry_points={'console_scripts': console_scripts},
|
entry_points={'console_scripts': console_scripts},
|
||||||
python_requires='>=2.7, <3',
|
python_requires='>=2.7, <3',
|
||||||
|
test_suite='nose.collector',
|
||||||
|
tests_require=['nose', 'coverage', 'pycodestyle'],
|
||||||
)
|
)
|
||||||
|
230
test/test_utils.py
Normal file
@ -0,0 +1,230 @@
|
|||||||
|
"""API properties.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import print_function
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
from urllib import unquote
|
||||||
|
from apprise import utils
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_url():
|
||||||
|
"utils: parse_url() testing """
|
||||||
|
|
||||||
|
result = utils.parse_url('http://hostname')
|
||||||
|
assert(result['schema'] == 'http')
|
||||||
|
assert(result['host'] == 'hostname')
|
||||||
|
assert(result['port'] is None)
|
||||||
|
assert(result['user'] is None)
|
||||||
|
assert(result['password'] is None)
|
||||||
|
assert(result['fullpath'] is None)
|
||||||
|
assert(result['path'] is None)
|
||||||
|
assert(result['query'] is None)
|
||||||
|
assert(result['url'] == 'http://hostname')
|
||||||
|
assert(result['qsd'] == {})
|
||||||
|
|
||||||
|
result = utils.parse_url('http://hostname/')
|
||||||
|
assert(result['schema'] == 'http')
|
||||||
|
assert(result['host'] == 'hostname')
|
||||||
|
assert(result['port'] is None)
|
||||||
|
assert(result['user'] is None)
|
||||||
|
assert(result['password'] is None)
|
||||||
|
assert(result['fullpath'] == '/')
|
||||||
|
assert(result['path'] == '/')
|
||||||
|
assert(result['query'] is None)
|
||||||
|
assert(result['url'] == 'http://hostname/')
|
||||||
|
assert(result['qsd'] == {})
|
||||||
|
|
||||||
|
result = utils.parse_url('hostname')
|
||||||
|
assert(result['schema'] == 'http')
|
||||||
|
assert(result['host'] == 'hostname')
|
||||||
|
assert(result['port'] is None)
|
||||||
|
assert(result['user'] is None)
|
||||||
|
assert(result['password'] is None)
|
||||||
|
assert(result['fullpath'] is None)
|
||||||
|
assert(result['path'] is None)
|
||||||
|
assert(result['query'] is None)
|
||||||
|
assert(result['url'] == 'http://hostname')
|
||||||
|
assert(result['qsd'] == {})
|
||||||
|
|
||||||
|
result = utils.parse_url('http://hostname////')
|
||||||
|
assert(result['schema'] == 'http')
|
||||||
|
assert(result['host'] == 'hostname')
|
||||||
|
assert(result['port'] is None)
|
||||||
|
assert(result['user'] is None)
|
||||||
|
assert(result['password'] is None)
|
||||||
|
assert(result['fullpath'] == '/')
|
||||||
|
assert(result['path'] == '/')
|
||||||
|
assert(result['query'] is None)
|
||||||
|
assert(result['url'] == 'http://hostname/')
|
||||||
|
assert(result['qsd'] == {})
|
||||||
|
|
||||||
|
result = utils.parse_url('http://hostname:40////')
|
||||||
|
assert(result['schema'] == 'http')
|
||||||
|
assert(result['host'] == 'hostname')
|
||||||
|
assert(result['port'] == 40)
|
||||||
|
assert(result['user'] is None)
|
||||||
|
assert(result['password'] is None)
|
||||||
|
assert(result['fullpath'] == '/')
|
||||||
|
assert(result['path'] == '/')
|
||||||
|
assert(result['query'] is None)
|
||||||
|
assert(result['url'] == 'http://hostname:40/')
|
||||||
|
assert(result['qsd'] == {})
|
||||||
|
|
||||||
|
result = utils.parse_url('HTTP://HoStNaMe:40/test.php')
|
||||||
|
assert(result['schema'] == 'http')
|
||||||
|
assert(result['host'] == 'HoStNaMe')
|
||||||
|
assert(result['port'] == 40)
|
||||||
|
assert(result['user'] is None)
|
||||||
|
assert(result['password'] is None)
|
||||||
|
assert(result['fullpath'] == '/test.php')
|
||||||
|
assert(result['path'] == '/')
|
||||||
|
assert(result['query'] == 'test.php')
|
||||||
|
assert(result['url'] == 'http://HoStNaMe:40/test.php')
|
||||||
|
assert(result['qsd'] == {})
|
||||||
|
|
||||||
|
result = utils.parse_url('HTTPS://user@hostname/test.py')
|
||||||
|
assert(result['schema'] == 'https')
|
||||||
|
assert(result['host'] == 'hostname')
|
||||||
|
assert(result['port'] is None)
|
||||||
|
assert(result['user'] == 'user')
|
||||||
|
assert(result['password'] is None)
|
||||||
|
assert(result['fullpath'] == '/test.py')
|
||||||
|
assert(result['path'] == '/')
|
||||||
|
assert(result['query'] == 'test.py')
|
||||||
|
assert(result['url'] == 'https://user@hostname/test.py')
|
||||||
|
assert(result['qsd'] == {})
|
||||||
|
|
||||||
|
result = utils.parse_url(' HTTPS://///user@@@hostname///test.py ')
|
||||||
|
assert(result['schema'] == 'https')
|
||||||
|
assert(result['host'] == 'hostname')
|
||||||
|
assert(result['port'] is None)
|
||||||
|
assert(result['user'] == 'user')
|
||||||
|
assert(result['password'] is None)
|
||||||
|
assert(result['fullpath'] == '/test.py')
|
||||||
|
assert(result['path'] == '/')
|
||||||
|
assert(result['query'] == 'test.py')
|
||||||
|
assert(result['url'] == 'https://user@hostname/test.py')
|
||||||
|
assert(result['qsd'] == {})
|
||||||
|
|
||||||
|
result = utils.parse_url(
|
||||||
|
'HTTPS://user:password@otherHost/full///path/name/',
|
||||||
|
)
|
||||||
|
assert(result['schema'] == 'https')
|
||||||
|
assert(result['host'] == 'otherHost')
|
||||||
|
assert(result['port'] is None)
|
||||||
|
assert(result['user'] == 'user')
|
||||||
|
assert(result['password'] == 'password')
|
||||||
|
assert(result['fullpath'] == '/full/path/name/')
|
||||||
|
assert(result['path'] == '/full/path/name/')
|
||||||
|
assert(result['query'] is None)
|
||||||
|
assert(result['url'] == 'https://user:password@otherHost/full/path/name/')
|
||||||
|
assert(result['qsd'] == {})
|
||||||
|
|
||||||
|
# Handle garbage
|
||||||
|
assert(utils.parse_url(None) is None)
|
||||||
|
|
||||||
|
result = utils.parse_url(
|
||||||
|
'mailto://user:password@otherHost/lead2gold@gmail.com' +
|
||||||
|
'?from=test@test.com&name=Chris%20Caron&format=text'
|
||||||
|
)
|
||||||
|
assert(result['schema'] == 'mailto')
|
||||||
|
assert(result['host'] == 'otherHost')
|
||||||
|
assert(result['port'] is None)
|
||||||
|
assert(result['user'] == 'user')
|
||||||
|
assert(result['password'] == 'password')
|
||||||
|
assert(unquote(result['fullpath']) == '/lead2gold@gmail.com')
|
||||||
|
assert(result['path'] == '/')
|
||||||
|
assert(unquote(result['query']) == 'lead2gold@gmail.com')
|
||||||
|
assert(unquote(
|
||||||
|
result['url']) ==
|
||||||
|
'mailto://user:password@otherHost/lead2gold@gmail.com')
|
||||||
|
assert(len(result['qsd']) == 3)
|
||||||
|
assert('name' in result['qsd'])
|
||||||
|
assert(unquote(result['qsd']['name']) == 'Chris Caron')
|
||||||
|
assert('from' in result['qsd'])
|
||||||
|
assert(unquote(result['qsd']['from']) == 'test@test.com')
|
||||||
|
assert('format' in result['qsd'])
|
||||||
|
assert(unquote(result['qsd']['format']) == 'text')
|
||||||
|
|
||||||
|
# Test Passwords with question marks ?; not supported
|
||||||
|
result = utils.parse_url(
|
||||||
|
'http://user:pass.with.?question@host'
|
||||||
|
)
|
||||||
|
assert(result is None)
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_bool():
|
||||||
|
"utils: parse_bool() testing """
|
||||||
|
|
||||||
|
assert(utils.parse_bool('Enabled', None) is True)
|
||||||
|
assert(utils.parse_bool('Disabled', None) is False)
|
||||||
|
assert(utils.parse_bool('Allow', None) is True)
|
||||||
|
assert(utils.parse_bool('Deny', None) is False)
|
||||||
|
assert(utils.parse_bool('Yes', None) is True)
|
||||||
|
assert(utils.parse_bool('YES', None) is True)
|
||||||
|
assert(utils.parse_bool('Always', None) is True)
|
||||||
|
assert(utils.parse_bool('No', None) is False)
|
||||||
|
assert(utils.parse_bool('NO', None) is False)
|
||||||
|
assert(utils.parse_bool('NEVER', None) is False)
|
||||||
|
assert(utils.parse_bool('TrUE', None) is True)
|
||||||
|
assert(utils.parse_bool('tRUe', None) is True)
|
||||||
|
assert(utils.parse_bool('FAlse', None) is False)
|
||||||
|
assert(utils.parse_bool('F', None) is False)
|
||||||
|
assert(utils.parse_bool('T', None) is True)
|
||||||
|
assert(utils.parse_bool('0', None) is False)
|
||||||
|
assert(utils.parse_bool('1', None) is True)
|
||||||
|
assert(utils.parse_bool('True', None) is True)
|
||||||
|
assert(utils.parse_bool('Yes', None) is True)
|
||||||
|
assert(utils.parse_bool(1, None) is True)
|
||||||
|
assert(utils.parse_bool(0, None) is False)
|
||||||
|
assert(utils.parse_bool(True, None) is True)
|
||||||
|
assert(utils.parse_bool(False, None) is False)
|
||||||
|
|
||||||
|
# only the int of 0 will return False since the function
|
||||||
|
# casts this to a boolean
|
||||||
|
assert(utils.parse_bool(2, None) is True)
|
||||||
|
# An empty list is still false
|
||||||
|
assert(utils.parse_bool([], None) is False)
|
||||||
|
# But a list that contains something is True
|
||||||
|
assert(utils.parse_bool(['value', ], None) is True)
|
||||||
|
|
||||||
|
# Use Default (which is False)
|
||||||
|
assert(utils.parse_bool('OhYeah') is False)
|
||||||
|
# Adjust Default and get a different result
|
||||||
|
assert(utils.parse_bool('OhYeah', True) is True)
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_list():
|
||||||
|
"utils: parse_list() testing """
|
||||||
|
|
||||||
|
# A simple single array entry (As str)
|
||||||
|
results = utils.parse_list(
|
||||||
|
'.mkv,.avi,.divx,.xvid,.mov,.wmv,.mp4,.mpg,.mpeg,.vob,.iso')
|
||||||
|
|
||||||
|
assert(results == [
|
||||||
|
'.divx', '.iso', '.mkv', '.mov', '.mpg', '.avi', '.mpeg', '.vob',
|
||||||
|
'.xvid', '.wmv', '.mp4',
|
||||||
|
])
|
||||||
|
|
||||||
|
# Now 2 lists with lots of duplicates and other delimiters
|
||||||
|
results = utils.parse_list(
|
||||||
|
'.mkv,.avi,.divx,.xvid,.mov,.wmv,.mp4,.mpg .mpeg,.vob,,; ;',
|
||||||
|
'.mkv,.avi,.divx,.xvid,.mov .wmv,.mp4;.mpg,.mpeg,'
|
||||||
|
'.vob,.iso')
|
||||||
|
|
||||||
|
assert(results == [
|
||||||
|
'.divx', '.iso', '.mkv', '.mov', '.mpg', '.avi', '.mpeg', '.vob',
|
||||||
|
'.xvid', '.wmv', '.mp4',
|
||||||
|
])
|
||||||
|
|
||||||
|
# Now a list with extras we want to add as strings
|
||||||
|
# empty entries are removed
|
||||||
|
results = utils.parse_list([
|
||||||
|
'.divx', '.iso', '.mkv', '.mov', '', ' ', '.avi', '.mpeg', '.vob',
|
||||||
|
'.xvid', '.mp4'], '.mov,.wmv,.mp4,.mpg')
|
||||||
|
|
||||||
|
assert(results == [
|
||||||
|
'.divx', '.wmv', '.iso', '.mkv', '.mov', '.mpg', '.avi', '.vob',
|
||||||
|
'.xvid', '.mpeg', '.mp4',
|
||||||
|
])
|