summaryrefslogtreecommitdiffhomepage
path: root/libs/apprise
diff options
context:
space:
mode:
authorLouis Vézina <[email protected]>2020-03-30 20:18:11 -0400
committerLouis Vézina <[email protected]>2020-03-30 20:18:11 -0400
commit70b4a6c469e1f4b098d7c93df86d30691a487c42 (patch)
tree94b2803a460b660f8cd7da5c2e762ef88df99086 /libs/apprise
parent299af384865adc3e22f1c5f02ee59deb5b56bde0 (diff)
downloadbazarr-70b4a6c469e1f4b098d7c93df86d30691a487c42.tar.gz
bazarr-70b4a6c469e1f4b098d7c93df86d30691a487c42.zip
Fix for #883
Diffstat (limited to 'libs/apprise')
-rw-r--r--libs/apprise/__init__.py4
-rw-r--r--libs/apprise/i18n/apprise.pot7
-rw-r--r--libs/apprise/plugins/NotifyEmail.py25
-rw-r--r--libs/apprise/plugins/NotifyJoin.py26
-rw-r--r--libs/apprise/plugins/NotifySlack.py6
-rw-r--r--libs/apprise/plugins/NotifyTelegram.py41
-rw-r--r--libs/apprise/plugins/NotifyXMPP/SleekXmppAdapter.py208
-rw-r--r--libs/apprise/plugins/NotifyXMPP/__init__.py (renamed from libs/apprise/plugins/NotifyXMPP.py)128
-rw-r--r--libs/apprise/plugins/__init__.py4
9 files changed, 298 insertions, 151 deletions
diff --git a/libs/apprise/__init__.py b/libs/apprise/__init__.py
index cf080be1b..63da23f8c 100644
--- a/libs/apprise/__init__.py
+++ b/libs/apprise/__init__.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
+# Copyright (C) 2020 Chris Caron <[email protected]>
# All rights reserved.
#
# This code is licensed under the MIT License.
@@ -24,7 +24,7 @@
# THE SOFTWARE.
__title__ = 'apprise'
-__version__ = '0.8.4'
+__version__ = '0.8.5'
__author__ = 'Chris Caron'
__license__ = 'MIT'
__copywrite__ = 'Copyright (C) 2020 Chris Caron <[email protected]>'
diff --git a/libs/apprise/i18n/apprise.pot b/libs/apprise/i18n/apprise.pot
index ffd9b700a..ea3fdfad1 100644
--- a/libs/apprise/i18n/apprise.pot
+++ b/libs/apprise/i18n/apprise.pot
@@ -6,9 +6,9 @@
#, fuzzy
msgid ""
msgstr ""
-"Project-Id-Version: apprise 0.8.4\n"
+"Project-Id-Version: apprise 0.8.5\n"
"Report-Msgid-Bugs-To: [email protected]\n"
-"POT-Creation-Date: 2020-02-01 12:59-0500\n"
+"POT-Creation-Date: 2020-03-30 16:00-0400\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <[email protected]>\n"
@@ -98,6 +98,9 @@ msgstr ""
msgid "Device ID"
msgstr ""
+msgid "Device Name"
+msgstr ""
+
msgid "Display Footer"
msgstr ""
diff --git a/libs/apprise/plugins/NotifyEmail.py b/libs/apprise/plugins/NotifyEmail.py
index 222e32e48..de686c8b3 100644
--- a/libs/apprise/plugins/NotifyEmail.py
+++ b/libs/apprise/plugins/NotifyEmail.py
@@ -269,6 +269,14 @@ class NotifyEmail(NotifyBase):
# Define object templates
templates = (
+ '{schema}://{host}',
+ '{schema}://{host}:{port}',
+ '{schema}://{host}/{targets}',
+ '{schema}://{host}:{port}/{targets}',
+ '{schema}://{user}@{host}',
+ '{schema}://{user}@{host}:{port}',
+ '{schema}://{user}@{host}/{targets}',
+ '{schema}://{user}@{host}:{port}/{targets}',
'{schema}://{user}:{password}@{host}',
'{schema}://{user}:{password}@{host}:{port}',
'{schema}://{user}:{password}@{host}/{targets}',
@@ -280,13 +288,11 @@ class NotifyEmail(NotifyBase):
'user': {
'name': _('User Name'),
'type': 'string',
- 'required': True,
},
'password': {
'name': _('Password'),
'type': 'string',
'private': True,
- 'required': True,
},
'host': {
'name': _('Domain'),
@@ -388,7 +394,7 @@ class NotifyEmail(NotifyBase):
self.from_name = from_name
self.from_addr = from_addr
- if not self.from_addr:
+ if self.user and not self.from_addr:
# detect our email address
self.from_addr = '{}@{}'.format(
re.split(r'[\s@]+', self.user)[0],
@@ -446,6 +452,10 @@ class NotifyEmail(NotifyBase):
# Apply any defaults based on certain known configurations
self.NotifyEmailDefaults()
+ # if there is still no smtp_host then we fall back to the hostname
+ if not self.smtp_host:
+ self.smtp_host = self.host
+
return
def NotifyEmailDefaults(self):
@@ -454,10 +464,11 @@ class NotifyEmail(NotifyBase):
it was provided.
"""
- if self.smtp_host:
+ if self.smtp_host or not self.user:
# SMTP Server was explicitly specified, therefore it is assumed
# the caller knows what he's doing and is intentionally
- # over-riding any smarts to be applied
+ # over-riding any smarts to be applied. We also can not apply
+ # any default if there was no user specified.
return
# detect our email address using our user/host combo
@@ -683,7 +694,7 @@ class NotifyEmail(NotifyBase):
args['bcc'] = ','.join(self.bcc)
# pull email suffix from username (if present)
- user = self.user.split('@')[0]
+ user = None if not self.user else self.user.split('@')[0]
# Determine Authentication
auth = ''
@@ -693,7 +704,7 @@ class NotifyEmail(NotifyBase):
password=self.pprint(
self.password, privacy, mode=PrivacyMode.Secret, safe=''),
)
- else:
+ elif user:
# user url
auth = '{user}@'.format(
user=NotifyEmail.quote(user, safe=''),
diff --git a/libs/apprise/plugins/NotifyJoin.py b/libs/apprise/plugins/NotifyJoin.py
index 76011d984..278ddaef8 100644
--- a/libs/apprise/plugins/NotifyJoin.py
+++ b/libs/apprise/plugins/NotifyJoin.py
@@ -130,6 +130,11 @@ class NotifyJoin(NotifyBase):
'regex': (r'^[a-z0-9]{32}$', 'i'),
'map_to': 'targets',
},
+ 'device_name': {
+ 'name': _('Device Name'),
+ 'type': 'string',
+ 'map_to': 'targets',
+ },
'group': {
'name': _('Group'),
'type': 'choice:string',
@@ -210,18 +215,7 @@ class NotifyJoin(NotifyBase):
'group.{}'.format(group_re.group('name').lower()))
continue
- elif IS_DEVICE_RE.match(target):
- self.targets.append(target)
- continue
-
- self.logger.warning(
- 'Ignoring invalid Join device/group "{}"'.format(target)
- )
-
- if not self.targets:
- msg = 'No Join targets to notify.'
- self.logger.warning(msg)
- raise TypeError(msg)
+ self.targets.append(target)
return
@@ -247,12 +241,18 @@ class NotifyJoin(NotifyBase):
url_args = {
'apikey': self.apikey,
- 'deviceId': target,
'priority': str(self.priority),
'title': title,
'text': body,
}
+ if IS_GROUP_RE.match(target) or IS_DEVICE_RE.match(target):
+ url_args['deviceId'] = target
+
+ else:
+ # Support Device Names
+ url_args['deviceNames'] = target
+
# prepare our image for display if configured to do so
image_url = None if not self.include_image \
else self.image_url(notify_type)
diff --git a/libs/apprise/plugins/NotifySlack.py b/libs/apprise/plugins/NotifySlack.py
index b17ecd858..d4e4f6112 100644
--- a/libs/apprise/plugins/NotifySlack.py
+++ b/libs/apprise/plugins/NotifySlack.py
@@ -176,7 +176,7 @@ class NotifySlack(NotifyBase):
'type': 'string',
'private': True,
'required': True,
- 'regex': (r'^[A-Z0-9]{9}$', 'i'),
+ 'regex': (r'^[A-Z0-9]+$', 'i'),
},
# Token required as part of the Webhook request
# /........./BBBBBBBBB/........................
@@ -185,7 +185,7 @@ class NotifySlack(NotifyBase):
'type': 'string',
'private': True,
'required': True,
- 'regex': (r'^[A-Z0-9]{9}$', 'i'),
+ 'regex': (r'^[A-Z0-9]+$', 'i'),
},
# Token required as part of the Webhook request
# /........./........./CCCCCCCCCCCCCCCCCCCCCCCC
@@ -194,7 +194,7 @@ class NotifySlack(NotifyBase):
'type': 'string',
'private': True,
'required': True,
- 'regex': (r'^[A-Za-z0-9]{24}$', 'i'),
+ 'regex': (r'^[A-Za-z0-9]+$', 'i'),
},
'target_encoded_id': {
'name': _('Target Encoded ID'),
diff --git a/libs/apprise/plugins/NotifyTelegram.py b/libs/apprise/plugins/NotifyTelegram.py
index 73bdf6585..0b6a2343f 100644
--- a/libs/apprise/plugins/NotifyTelegram.py
+++ b/libs/apprise/plugins/NotifyTelegram.py
@@ -477,6 +477,9 @@ class NotifyTelegram(NotifyBase):
# Return our detected userid
return _id
+ self.logger.warning(
+ 'Failed to detect a Telegram user; '
+ 'try sending your bot a message first.')
return 0
def send(self, body, title='', notify_type=NotifyType.INFO, attach=None,
@@ -505,8 +508,12 @@ class NotifyTelegram(NotifyBase):
if self.notify_format == NotifyFormat.MARKDOWN:
payload['parse_mode'] = 'MARKDOWN'
- else:
- # Either TEXT or HTML; if TEXT we'll make it HTML
+ payload['text'] = '{}{}'.format(
+ '{}\r\n'.format(title) if title else '',
+ body,
+ )
+
+ elif self.notify_format == NotifyFormat.HTML:
payload['parse_mode'] = 'HTML'
# HTML Spaces (&nbsp;) and tabs (&emsp;) aren't supported
@@ -524,31 +531,23 @@ class NotifyTelegram(NotifyBase):
# Tabs become 3 spaces
title = re.sub('&emsp;?', ' ', title, re.I)
- # HTML
- title = NotifyTelegram.escape_html(title, whitespace=False)
-
- # HTML
- body = NotifyTelegram.escape_html(body, whitespace=False)
-
- if title and self.notify_format == NotifyFormat.TEXT:
- # Text HTML Formatting
- payload['text'] = '<b>%s</b>\r\n%s' % (
- title,
+ payload['text'] = '{}{}'.format(
+ '<b>{}</b>\r\n'.format(title) if title else '',
body,
)
- elif title:
- # Already HTML; trust developer has wrapped
- # the title appropriately
- payload['text'] = '%s\r\n%s' % (
- title,
+ else: # TEXT
+ payload['parse_mode'] = 'HTML'
+
+ # Escape content
+ title = NotifyTelegram.escape_html(title, whitespace=False)
+ body = NotifyTelegram.escape_html(body, whitespace=False)
+
+ payload['text'] = '{}{}'.format(
+ '<b>{}</b>\r\n'.format(title) if title else '',
body,
)
- else:
- # Assign the body
- payload['text'] = body
-
# Create a copy of the chat_ids list
targets = list(self.targets)
while len(targets):
diff --git a/libs/apprise/plugins/NotifyXMPP/SleekXmppAdapter.py b/libs/apprise/plugins/NotifyXMPP/SleekXmppAdapter.py
new file mode 100644
index 000000000..a28e9ce54
--- /dev/null
+++ b/libs/apprise/plugins/NotifyXMPP/SleekXmppAdapter.py
@@ -0,0 +1,208 @@
+# -*- coding: utf-8 -*-
+
+import ssl
+from os.path import isfile
+import logging
+
+
+# Default our global support flag
+SLEEKXMPP_SUPPORT_AVAILABLE = False
+
+try:
+ # Import sleekxmpp if available
+ import sleekxmpp
+
+ SLEEKXMPP_SUPPORT_AVAILABLE = True
+
+except ImportError:
+ # No problem; we just simply can't support this plugin because we're
+ # either using Linux, or simply do not have sleekxmpp installed.
+ pass
+
+
+class SleekXmppAdapter(object):
+ """
+ Wrapper to sleekxmpp
+
+ """
+
+ # Reference to XMPP client.
+ xmpp = None
+
+ # Whether everything succeeded
+ success = False
+
+ # The default protocol
+ protocol = 'xmpp'
+
+ # The default secure protocol
+ secure_protocol = 'xmpps'
+
+ # The default XMPP port
+ default_unsecure_port = 5222
+
+ # The default XMPP secure port
+ default_secure_port = 5223
+
+ # Taken from https://golang.org/src/crypto/x509/root_linux.go
+ CA_CERTIFICATE_FILE_LOCATIONS = [
+ # Debian/Ubuntu/Gentoo etc.
+ "/etc/ssl/certs/ca-certificates.crt",
+ # Fedora/RHEL 6
+ "/etc/pki/tls/certs/ca-bundle.crt",
+ # OpenSUSE
+ "/etc/ssl/ca-bundle.pem",
+ # OpenELEC
+ "/etc/pki/tls/cacert.pem",
+ # CentOS/RHEL 7
+ "/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem",
+ ]
+
+ # This entry is a bit hacky, but it allows us to unit-test this library
+ # in an environment that simply doesn't have the sleekxmpp package
+ # available to us.
+ #
+ # If anyone is seeing this had knows a better way of testing this
+ # outside of what is defined in test/test_xmpp_plugin.py, please
+ # let me know! :)
+ _enabled = SLEEKXMPP_SUPPORT_AVAILABLE
+
+ def __init__(self, host=None, port=None, secure=False,
+ verify_certificate=True, xep=None, jid=None, password=None,
+ body=None, targets=None, before_message=None, logger=None):
+ """
+ Initialize our SleekXmppAdapter object
+ """
+
+ self.host = host
+ self.port = port
+ self.secure = secure
+ self.verify_certificate = verify_certificate
+
+ self.xep = xep
+ self.jid = jid
+ self.password = password
+
+ self.body = body
+ self.targets = targets
+ self.before_message = before_message
+
+ self.logger = logger or logging.getLogger(__name__)
+
+ # Use the Apprise log handlers for configuring the sleekxmpp logger.
+ apprise_logger = logging.getLogger('apprise')
+ sleek_logger = logging.getLogger('sleekxmpp')
+ for handler in apprise_logger.handlers:
+ sleek_logger.addHandler(handler)
+ sleek_logger.setLevel(apprise_logger.level)
+
+ if not self.load():
+ raise ValueError("Invalid XMPP Configuration")
+
+ def load(self):
+
+ # Prepare our object
+ self.xmpp = sleekxmpp.ClientXMPP(self.jid, self.password)
+
+ # Register our session
+ self.xmpp.add_event_handler("session_start", self.session_start)
+
+ for xep in self.xep:
+ # Load xep entries
+ try:
+ self.xmpp.register_plugin('xep_{0:04d}'.format(xep))
+
+ except sleekxmpp.plugins.base.PluginNotFound:
+ self.logger.warning(
+ 'Could not register plugin {}'.format(
+ 'xep_{0:04d}'.format(xep)))
+ return False
+
+ if self.secure:
+ # Don't even try to use the outdated ssl.PROTOCOL_SSLx
+ self.xmpp.ssl_version = ssl.PROTOCOL_TLSv1
+
+ # If the python version supports it, use highest TLS version
+ # automatically
+ if hasattr(ssl, "PROTOCOL_TLS"):
+ # Use the best version of TLS available to us
+ self.xmpp.ssl_version = ssl.PROTOCOL_TLS
+
+ self.xmpp.ca_certs = None
+ if self.verify_certificate:
+ # Set the ca_certs variable for certificate verification
+ self.xmpp.ca_certs = next(
+ (cert for cert in self.CA_CERTIFICATE_FILE_LOCATIONS
+ if isfile(cert)), None)
+
+ if self.xmpp.ca_certs is None:
+ self.logger.warning(
+ 'XMPP Secure comunication can not be verified; '
+ 'no local CA certificate file')
+ return False
+
+ # We're good
+ return True
+
+ def process(self):
+ """
+ Thread that handles the server/client i/o
+
+ """
+
+ # Establish connection to XMPP server.
+ # To speed up sending messages, don't use the "reattempt" feature,
+ # it will add a nasty delay even before connecting to XMPP server.
+ if not self.xmpp.connect((self.host, self.port),
+ use_ssl=self.secure, reattempt=False):
+
+ default_port = self.default_secure_port \
+ if self.secure else self.default_unsecure_port
+
+ default_schema = self.secure_protocol \
+ if self.secure else self.protocol
+
+ # Log connection issue
+ self.logger.warning(
+ 'Failed to authenticate {jid} with: {schema}://{host}{port}'
+ .format(
+ jid=self.jid,
+ schema=default_schema,
+ host=self.host,
+ port='' if not self.port or self.port == default_port
+ else ':{}'.format(self.port),
+ ))
+ return False
+
+ # Process XMPP communication.
+ self.xmpp.process(block=True)
+
+ return self.success
+
+ def session_start(self, *args, **kwargs):
+ """
+ Session Manager
+ """
+
+ targets = list(self.targets)
+ if not targets:
+ # We always default to notifying ourselves
+ targets.append(self.jid)
+
+ while len(targets) > 0:
+
+ # Get next target (via JID)
+ target = targets.pop(0)
+
+ # Invoke "before_message" event hook.
+ self.before_message()
+
+ # The message we wish to send, and the JID that will receive it.
+ self.xmpp.send_message(mto=target, mbody=self.body, mtype='chat')
+
+ # Using wait=True ensures that the send queue will be
+ # emptied before ending the session.
+ self.xmpp.disconnect(wait=True)
+
+ # Toggle our success flag
+ self.success = True
diff --git a/libs/apprise/plugins/NotifyXMPP.py b/libs/apprise/plugins/NotifyXMPP/__init__.py
index 82623cb45..a1cd0073a 100644
--- a/libs/apprise/plugins/NotifyXMPP.py
+++ b/libs/apprise/plugins/NotifyXMPP/__init__.py
@@ -24,46 +24,17 @@
# THE SOFTWARE.
import re
-import ssl
-from os.path import isfile
-from .NotifyBase import NotifyBase
-from ..URLBase import PrivacyMode
-from ..common import NotifyType
-from ..utils import parse_list
-from ..AppriseLocale import gettext_lazy as _
+from ..NotifyBase import NotifyBase
+from ...URLBase import PrivacyMode
+from ...common import NotifyType
+from ...utils import parse_list
+from ...AppriseLocale import gettext_lazy as _
+from .SleekXmppAdapter import SleekXmppAdapter
# xep string parser
XEP_PARSE_RE = re.compile('^[^1-9]*(?P<xep>[1-9][0-9]{0,3})$')
-# Default our global support flag
-NOTIFY_XMPP_SUPPORT_ENABLED = False
-
-# Taken from https://golang.org/src/crypto/x509/root_linux.go
-CA_CERTIFICATE_FILE_LOCATIONS = [
- # Debian/Ubuntu/Gentoo etc.
- "/etc/ssl/certs/ca-certificates.crt",
- # Fedora/RHEL 6
- "/etc/pki/tls/certs/ca-bundle.crt",
- # OpenSUSE
- "/etc/ssl/ca-bundle.pem",
- # OpenELEC
- "/etc/pki/tls/cacert.pem",
- # CentOS/RHEL 7
- "/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem",
-]
-
-try:
- # Import sleekxmpp if available
- import sleekxmpp
-
- NOTIFY_XMPP_SUPPORT_ENABLED = True
-
-except ImportError:
- # No problem; we just simply can't support this plugin because we're
- # either using Linux, or simply do not have sleekxmpp installed.
- pass
-
class NotifyXMPP(NotifyBase):
"""
@@ -82,6 +53,9 @@ class NotifyXMPP(NotifyBase):
# A URL that takes you to the setup/help of the specific protocol
setup_url = 'https://github.com/caronc/apprise/wiki/Notify_xmpp'
+ # Lower throttle rate for XMPP
+ request_rate_per_sec = 0.5
+
# The default XMPP port
default_unsecure_port = 5222
@@ -98,7 +72,7 @@ class NotifyXMPP(NotifyBase):
# If anyone is seeing this had knows a better way of testing this
# outside of what is defined in test/test_xmpp_plugin.py, please
# let me know! :)
- _enabled = NOTIFY_XMPP_SUPPORT_ENABLED
+ _enabled = SleekXmppAdapter._enabled
# Define object templates
templates = (
@@ -231,10 +205,11 @@ class NotifyXMPP(NotifyBase):
result = XEP_PARSE_RE.match(xep)
if result is not None:
self.xep.append(int(result.group('xep')))
+ self.logger.debug('Loaded XMPP {}'.format(xep))
else:
self.logger.warning(
- "Could not load XMPP xep {}".format(xep))
+ "Could not load XMPP {}".format(xep))
# By default we send ourselves a message
if targets:
@@ -267,34 +242,7 @@ class NotifyXMPP(NotifyBase):
jid = self.host
password = self.password if self.password else self.user
- # Prepare our object
- xmpp = sleekxmpp.ClientXMPP(jid, password)
-
- for xep in self.xep:
- # Load xep entries
- xmpp.register_plugin('xep_{0:04d}'.format(xep))
-
- if self.secure:
- xmpp.ssl_version = ssl.PROTOCOL_TLSv1
- # If the python version supports it, use highest TLS version
- # automatically
- if hasattr(ssl, "PROTOCOL_TLS"):
- # Use the best version of TLS available to us
- xmpp.ssl_version = ssl.PROTOCOL_TLS
-
- xmpp.ca_certs = None
- if self.verify_certificate:
- # Set the ca_certs variable for certificate verification
- xmpp.ca_certs = next(
- (cert for cert in CA_CERTIFICATE_FILE_LOCATIONS
- if isfile(cert)), None)
-
- if xmpp.ca_certs is None:
- self.logger.warning(
- 'XMPP Secure comunication can not be verified; '
- 'no CA certificate found')
-
- # Acquire our port number
+ # Compute port number
if not self.port:
port = self.default_secure_port \
if self.secure else self.default_unsecure_port
@@ -302,48 +250,22 @@ class NotifyXMPP(NotifyBase):
else:
port = self.port
- # Establish our connection
- if not xmpp.connect((self.host, port)):
- return False
-
- xmpp.send_presence()
-
try:
- xmpp.get_roster()
-
- except sleekxmpp.exceptions.IqError as e:
- self.logger.warning('There was an error getting the XMPP roster.')
- self.logger.debug(e.iq['error']['condition'])
- xmpp.disconnect()
+ # Communicate with XMPP.
+ xmpp_adapter = SleekXmppAdapter(
+ host=self.host, port=port, secure=self.secure,
+ verify_certificate=self.verify_certificate, xep=self.xep,
+ jid=jid, password=password, body=body, targets=self.targets,
+ before_message=self.throttle, logger=self.logger)
+
+ except ValueError:
+ # We failed
return False
- except sleekxmpp.exceptions.IqTimeout:
- self.logger.warning('XMPP Server is taking too long to respond.')
- xmpp.disconnect()
- return False
-
- targets = list(self.targets)
- if not targets:
- # We always default to notifying ourselves
- targets.append(jid)
-
- while len(targets) > 0:
-
- # Get next target (via JID)
- target = targets.pop(0)
-
- # Always call throttle before any remote server i/o is made
- self.throttle()
-
- # The message we wish to send, and the JID that
- # will receive it.
- xmpp.send_message(mto=target, mbody=body, mtype='chat')
-
- # Using wait=True ensures that the send queue will be
- # emptied before ending the session.
- xmpp.disconnect(wait=True)
+ # Initialize XMPP machinery and begin processing the XML stream.
+ outcome = xmpp_adapter.process()
- return True
+ return outcome
def url(self, privacy=False, *args, **kwargs):
"""
diff --git a/libs/apprise/plugins/__init__.py b/libs/apprise/plugins/__init__.py
index 21ff47fcd..fd41cb7fd 100644
--- a/libs/apprise/plugins/__init__.py
+++ b/libs/apprise/plugins/__init__.py
@@ -34,6 +34,7 @@ from os.path import abspath
# Used for testing
from . import NotifyEmail as NotifyEmailBase
from .NotifyGrowl import gntp
+from .NotifyXMPP import SleekXmppAdapter
# NotifyBase object is passed in as a module not class
from . import NotifyBase
@@ -63,6 +64,9 @@ __all__ = [
# gntp (used for NotifyGrowl Testing)
'gntp',
+
+ # sleekxmpp access points (used for NotifyXMPP Testing)
+ 'SleekXmppAdapter',
]
# we mirror our base purely for the ability to reset everything; this