summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authormorpheus65535 <[email protected]>2023-06-07 14:05:42 -0400
committermorpheus65535 <[email protected]>2023-06-07 14:05:42 -0400
commit07f601f407ef5b9e6fe0b0db842f3bec8c9916b0 (patch)
tree9d1fa6b5fe1ec006ff063dcb0cbb296e64efebce
parent0956d401bc11854a2abaeef100ce6e5cf75aeb20 (diff)
downloadbazarr-07f601f407ef5b9e6fe0b0db842f3bec8c9916b0.tar.gz
bazarr-07f601f407ef5b9e6fe0b0db842f3bec8c9916b0.zip
Updated apprise module to improve notification system. #2163
-rw-r--r--libs/apprise/Apprise.py280
-rw-r--r--libs/apprise/AppriseAsset.py43
-rw-r--r--libs/apprise/AppriseAttachment.py54
-rw-r--r--libs/apprise/AppriseConfig.py43
-rw-r--r--libs/apprise/AppriseLocale.py45
-rw-r--r--libs/apprise/URLBase.py77
-rw-r--r--libs/apprise/__init__.py49
-rw-r--r--libs/apprise/attachment/AttachBase.py45
-rw-r--r--libs/apprise/attachment/AttachFile.py45
-rw-r--r--libs/apprise/attachment/AttachHTTP.py47
-rw-r--r--libs/apprise/attachment/__init__.py44
-rw-r--r--libs/apprise/cli.py93
-rw-r--r--libs/apprise/common.py44
-rw-r--r--libs/apprise/common.pyi9
-rw-r--r--libs/apprise/config/ConfigBase.py47
-rw-r--r--libs/apprise/config/ConfigFile.py45
-rw-r--r--libs/apprise/config/ConfigHTTP.py45
-rw-r--r--libs/apprise/config/ConfigMemory.py45
-rw-r--r--libs/apprise/config/__init__.py43
-rw-r--r--libs/apprise/conversion.py45
-rw-r--r--libs/apprise/decorators/CustomNotifyPlugin.py46
-rw-r--r--libs/apprise/decorators/__init__.py44
-rw-r--r--libs/apprise/decorators/notify.py44
-rw-r--r--libs/apprise/logger.py44
-rw-r--r--libs/apprise/plugins/NotifyAppriseAPI.py170
-rw-r--r--libs/apprise/plugins/NotifyBark.py54
-rw-r--r--libs/apprise/plugins/NotifyBase.py135
-rw-r--r--libs/apprise/plugins/NotifyBoxcar.py92
-rw-r--r--libs/apprise/plugins/NotifyBulkSMS.py64
-rw-r--r--libs/apprise/plugins/NotifyClickSend.py60
-rw-r--r--libs/apprise/plugins/NotifyD7Networks.py281
-rw-r--r--libs/apprise/plugins/NotifyDBus.py70
-rw-r--r--libs/apprise/plugins/NotifyDapnet.py60
-rw-r--r--libs/apprise/plugins/NotifyDingTalk.py52
-rw-r--r--libs/apprise/plugins/NotifyDiscord.py56
-rw-r--r--libs/apprise/plugins/NotifyEmail.py400
-rw-r--r--libs/apprise/plugins/NotifyEmby.py48
-rw-r--r--libs/apprise/plugins/NotifyEnigma2.py45
-rw-r--r--libs/apprise/plugins/NotifyFCM/__init__.py51
-rw-r--r--libs/apprise/plugins/NotifyFCM/color.py43
-rw-r--r--libs/apprise/plugins/NotifyFCM/common.py44
-rw-r--r--libs/apprise/plugins/NotifyFCM/oauth.py43
-rw-r--r--libs/apprise/plugins/NotifyFCM/priority.py43
-rw-r--r--libs/apprise/plugins/NotifyFaast.py44
-rw-r--r--libs/apprise/plugins/NotifyFlock.py52
-rw-r--r--libs/apprise/plugins/NotifyForm.py217
-rw-r--r--libs/apprise/plugins/NotifyGitter.py51
-rw-r--r--libs/apprise/plugins/NotifyGnome.py62
-rw-r--r--libs/apprise/plugins/NotifyGoogleChat.py104
-rw-r--r--libs/apprise/plugins/NotifyGotify.py48
-rw-r--r--libs/apprise/plugins/NotifyGrowl.py45
-rw-r--r--libs/apprise/plugins/NotifyGuilded.py43
-rw-r--r--libs/apprise/plugins/NotifyHomeAssistant.py45
-rw-r--r--libs/apprise/plugins/NotifyIFTTT.py52
-rw-r--r--libs/apprise/plugins/NotifyJSON.py137
-rw-r--r--libs/apprise/plugins/NotifyJoin.py51
-rw-r--r--libs/apprise/plugins/NotifyKavenegar.py51
-rw-r--r--libs/apprise/plugins/NotifyKumulos.py45
-rw-r--r--libs/apprise/plugins/NotifyLametric.py45
-rw-r--r--libs/apprise/plugins/NotifyLine.py52
-rw-r--r--libs/apprise/plugins/NotifyMQTT.py111
-rw-r--r--libs/apprise/plugins/NotifyMSG91.py51
-rw-r--r--libs/apprise/plugins/NotifyMSTeams.py45
-rw-r--r--libs/apprise/plugins/NotifyMacOSX.py79
-rw-r--r--libs/apprise/plugins/NotifyMailgun.py127
-rw-r--r--libs/apprise/plugins/NotifyMastodon.py990
-rw-r--r--libs/apprise/plugins/NotifyMatrix.py61
-rw-r--r--libs/apprise/plugins/NotifyMatterMost.py45
-rw-r--r--libs/apprise/plugins/NotifyMessageBird.py52
-rw-r--r--libs/apprise/plugins/NotifyMisskey.py310
-rw-r--r--libs/apprise/plugins/NotifyNextcloud.py50
-rw-r--r--libs/apprise/plugins/NotifyNextcloudTalk.py57
-rw-r--r--libs/apprise/plugins/NotifyNotica.py44
-rw-r--r--libs/apprise/plugins/NotifyNotifico.py45
-rw-r--r--libs/apprise/plugins/NotifyNtfy.py227
-rw-r--r--libs/apprise/plugins/NotifyOffice365.py51
-rw-r--r--libs/apprise/plugins/NotifyOneSignal.py73
-rw-r--r--libs/apprise/plugins/NotifyOpsgenie.py65
-rw-r--r--libs/apprise/plugins/NotifyPagerDuty.py86
-rw-r--r--libs/apprise/plugins/NotifyPagerTree.py424
-rw-r--r--libs/apprise/plugins/NotifyParsePlatform.py45
-rw-r--r--libs/apprise/plugins/NotifyPopcornNotify.py60
-rw-r--r--libs/apprise/plugins/NotifyProwl.py45
-rw-r--r--libs/apprise/plugins/NotifyPushBullet.py51
-rw-r--r--libs/apprise/plugins/NotifyPushSafer.py51
-rw-r--r--libs/apprise/plugins/NotifyPushed.py52
-rw-r--r--libs/apprise/plugins/NotifyPushjet.py45
-rw-r--r--libs/apprise/plugins/NotifyPushover.py53
-rw-r--r--libs/apprise/plugins/NotifyReddit.py52
-rw-r--r--libs/apprise/plugins/NotifyRocketChat.py61
-rw-r--r--libs/apprise/plugins/NotifyRyver.py45
-rw-r--r--libs/apprise/plugins/NotifySES.py52
-rw-r--r--libs/apprise/plugins/NotifySMSEagle.py94
-rw-r--r--libs/apprise/plugins/NotifySMTP2Go.py60
-rw-r--r--libs/apprise/plugins/NotifySNS.py51
-rw-r--r--libs/apprise/plugins/NotifySendGrid.py52
-rw-r--r--libs/apprise/plugins/NotifyServerChan.py45
-rw-r--r--libs/apprise/plugins/NotifySignalAPI.py60
-rw-r--r--libs/apprise/plugins/NotifySimplePush.py46
-rw-r--r--libs/apprise/plugins/NotifySinch.py52
-rw-r--r--libs/apprise/plugins/NotifySlack.py98
-rw-r--r--libs/apprise/plugins/NotifySparkPost.py71
-rw-r--r--libs/apprise/plugins/NotifySpontit.py52
-rw-r--r--libs/apprise/plugins/NotifyStreamlabs.py45
-rw-r--r--libs/apprise/plugins/NotifySyslog.py46
-rw-r--r--libs/apprise/plugins/NotifyTechulusPush.py45
-rw-r--r--libs/apprise/plugins/NotifyTelegram.py82
-rw-r--r--libs/apprise/plugins/NotifyTwilio.py52
-rw-r--r--libs/apprise/plugins/NotifyTwist.py52
-rw-r--r--libs/apprise/plugins/NotifyTwitter.py163
-rw-r--r--libs/apprise/plugins/NotifyVoipms.py379
-rw-r--r--libs/apprise/plugins/NotifyVonage.py52
-rw-r--r--libs/apprise/plugins/NotifyWebexTeams.py56
-rw-r--r--libs/apprise/plugins/NotifyWindows.py47
-rw-r--r--libs/apprise/plugins/NotifyXBMC.py45
-rw-r--r--libs/apprise/plugins/NotifyXML.py145
-rw-r--r--libs/apprise/plugins/NotifyZulip.py51
-rw-r--r--libs/apprise/plugins/__init__.py56
-rw-r--r--libs/apprise/py3compat/__init__.py0
-rw-r--r--libs/apprise/py3compat/asyncio.py140
-rw-r--r--libs/apprise/utils.py85
-rw-r--r--libs/version.txt2
122 files changed, 7159 insertions, 2991 deletions
diff --git a/libs/apprise/Apprise.py b/libs/apprise/Apprise.py
index 39a1ff0aa..8c2cf5330 100644
--- a/libs/apprise/Apprise.py
+++ b/libs/apprise/Apprise.py
@@ -1,28 +1,37 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+import asyncio
+import concurrent.futures as cf
import os
from itertools import chain
from . import common
@@ -43,11 +52,6 @@ from .plugins.NotifyBase import NotifyBase
from . import plugins
from . import __version__
-# Python v3+ support code made importable, so it can remain backwards
-# compatible with Python v2
-# TODO: Review after dropping support for Python 2.
-from . import py3compat
-
class Apprise:
"""
@@ -369,91 +373,83 @@ class Apprise:
such as turning a \n into an actual new line, etc.
"""
- return py3compat.asyncio.tosync(
- self.async_notify(
+ try:
+ # Process arguments and build synchronous and asynchronous calls
+ # (this step can throw internal errors).
+ sequential_calls, parallel_calls = self._create_notify_calls(
body, title,
notify_type=notify_type, body_format=body_format,
tag=tag, match_always=match_always, attach=attach,
- interpret_escapes=interpret_escapes,
- ),
- debug=self.debug
- )
+ interpret_escapes=interpret_escapes
+ )
+
+ except TypeError:
+ # No notifications sent, and there was an internal error.
+ return False
+
+ if not sequential_calls and not parallel_calls:
+ # Nothing to send
+ return None
+
+ sequential_result = Apprise._notify_sequential(*sequential_calls)
+ parallel_result = Apprise._notify_parallel_threadpool(*parallel_calls)
+ return sequential_result and parallel_result
- def async_notify(self, *args, **kwargs):
+ async def async_notify(self, *args, **kwargs):
"""
Send a notification to all the plugins previously loaded, for
- asynchronous callers. This method is an async method that should be
- awaited on, even if it is missing the async keyword in its signature.
- (This is omitted to preserve syntax compatibility with Python 2.)
+ asynchronous callers.
The arguments are identical to those of Apprise.notify().
"""
try:
- coroutines = list(
- self._notifyall(
- Apprise._notifyhandlerasync, *args, **kwargs))
+ # Process arguments and build synchronous and asynchronous calls
+ # (this step can throw internal errors).
+ sequential_calls, parallel_calls = self._create_notify_calls(
+ *args, **kwargs)
except TypeError:
# No notifications sent, and there was an internal error.
- return py3compat.asyncio.toasyncwrapvalue(False)
+ return False
- else:
- if len(coroutines) > 0:
- # All notifications sent, return False if any failed.
- return py3compat.asyncio.notify(coroutines)
+ if not sequential_calls and not parallel_calls:
+ # Nothing to send
+ return None
- else:
- # No notifications sent.
- return py3compat.asyncio.toasyncwrapvalue(None)
+ sequential_result = Apprise._notify_sequential(*sequential_calls)
+ parallel_result = \
+ await Apprise._notify_parallel_asyncio(*parallel_calls)
+ return sequential_result and parallel_result
- @staticmethod
- def _notifyhandler(server, **kwargs):
- """
- The synchronous notification sender. Returns True if the notification
- sent successfully.
+ def _create_notify_calls(self, *args, **kwargs):
"""
+ Creates notifications for all the plugins loaded.
- try:
- # Send notification
- return server.notify(**kwargs)
-
- except TypeError:
- # These our our internally thrown notifications
- return False
-
- except Exception:
- # A catch all so we don't have to abort early
- # just because one of our plugins has a bug in it.
- logger.exception("Unhandled Notification Exception")
- return False
-
- @staticmethod
- def _notifyhandlerasync(server, **kwargs):
- """
- The asynchronous notification sender. Returns a coroutine that yields
- True if the notification sent successfully.
+ Returns a list of (server, notify() kwargs) tuples for plugins with
+ parallelism disabled and another list for plugins with parallelism
+ enabled.
"""
- if server.asset.async_mode:
- return server.async_notify(**kwargs)
+ all_calls = list(self._create_notify_gen(*args, **kwargs))
- else:
- # Send the notification immediately, and wrap the result in a
- # coroutine.
- status = Apprise._notifyhandler(server, **kwargs)
- return py3compat.asyncio.toasyncwrapvalue(status)
+ # Split into sequential and parallel notify() calls.
+ sequential, parallel = [], []
+ for (server, notify_kwargs) in all_calls:
+ if server.asset.async_mode:
+ parallel.append((server, notify_kwargs))
+ else:
+ sequential.append((server, notify_kwargs))
- def _notifyall(self, handler, body, title='',
- notify_type=common.NotifyType.INFO, body_format=None,
- tag=common.MATCH_ALL_TAG, match_always=True, attach=None,
- interpret_escapes=None):
- """
- Creates notifications for all the plugins loaded.
+ return sequential, parallel
- Returns a generator that calls handler for each notification. The first
- and only argument supplied to handler is the server, and the keyword
- arguments are exactly as they would be passed to server.notify().
+ def _create_notify_gen(self, body, title='',
+ notify_type=common.NotifyType.INFO,
+ body_format=None, tag=common.MATCH_ALL_TAG,
+ match_always=True, attach=None,
+ interpret_escapes=None):
+ """
+ Internal generator function for _create_notify_calls().
"""
if len(self) == 0:
@@ -546,14 +542,121 @@ class Apprise:
logger.error(msg)
raise TypeError(msg)
- yield handler(
- server,
+ kwargs = dict(
body=conversion_body_map[server.notify_format],
title=conversion_title_map[server.notify_format],
notify_type=notify_type,
attach=attach,
- body_format=body_format,
+ body_format=body_format
)
+ yield (server, kwargs)
+
+ @staticmethod
+ def _notify_sequential(*servers_kwargs):
+ """
+ Process a list of notify() calls sequentially and synchronously.
+ """
+
+ success = True
+
+ for (server, kwargs) in servers_kwargs:
+ try:
+ # Send notification
+ result = server.notify(**kwargs)
+ success = success and result
+
+ except TypeError:
+ # These are our internally thrown notifications.
+ success = False
+
+ except Exception:
+ # A catch all so we don't have to abort early
+ # just because one of our plugins has a bug in it.
+ logger.exception("Unhandled Notification Exception")
+ success = False
+
+ return success
+
+ @staticmethod
+ def _notify_parallel_threadpool(*servers_kwargs):
+ """
+ Process a list of notify() calls in parallel and synchronously.
+ """
+
+ n_calls = len(servers_kwargs)
+
+ # 0-length case
+ if n_calls == 0:
+ return True
+
+ # There's no need to use a thread pool for just a single notification
+ if n_calls == 1:
+ return Apprise._notify_sequential(servers_kwargs[0])
+
+ # Create log entry
+ logger.info(
+ 'Notifying %d service(s) with threads.', len(servers_kwargs))
+
+ with cf.ThreadPoolExecutor() as executor:
+ success = True
+ futures = [executor.submit(server.notify, **kwargs)
+ for (server, kwargs) in servers_kwargs]
+
+ for future in cf.as_completed(futures):
+ try:
+ result = future.result()
+ success = success and result
+
+ except TypeError:
+ # These are our internally thrown notifications.
+ success = False
+
+ except Exception:
+ # A catch all so we don't have to abort early
+ # just because one of our plugins has a bug in it.
+ logger.exception("Unhandled Notification Exception")
+ success = False
+
+ return success
+
+ @staticmethod
+ async def _notify_parallel_asyncio(*servers_kwargs):
+ """
+ Process a list of async_notify() calls in parallel and asynchronously.
+ """
+
+ n_calls = len(servers_kwargs)
+
+ # 0-length case
+ if n_calls == 0:
+ return True
+
+ # (Unlike with the thread pool, we don't optimize for the single-
+ # notification case because asyncio can do useful work while waiting
+ # for that thread to complete)
+
+ # Create log entry
+ logger.info(
+ 'Notifying %d service(s) asynchronously.', len(servers_kwargs))
+
+ async def do_call(server, kwargs):
+ return await server.async_notify(**kwargs)
+
+ cors = (do_call(server, kwargs) for (server, kwargs) in servers_kwargs)
+ results = await asyncio.gather(*cors, return_exceptions=True)
+
+ if any(isinstance(status, Exception)
+ and not isinstance(status, TypeError) for status in results):
+ # A catch all so we don't have to abort early just because
+ # one of our plugins has a bug in it.
+ logger.exception("Unhandled Notification Exception")
+ return False
+
+ if any(isinstance(status, TypeError) for status in results):
+ # These are our internally thrown notifications.
+ return False
+
+ return all(results)
def details(self, lang=None, show_requirements=False, show_disabled=False):
"""
@@ -581,6 +684,7 @@ class Apprise:
'setup_url': getattr(plugin, 'setup_url', None),
# Placeholder - populated below
'details': None,
+
# Differentiat between what is a custom loaded plugin and
# which is native.
'category': getattr(plugin, 'category', None)
diff --git a/libs/apprise/AppriseAsset.py b/libs/apprise/AppriseAsset.py
index 80bd0656c..34821e278 100644
--- a/libs/apprise/AppriseAsset.py
+++ b/libs/apprise/AppriseAsset.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
import re
from uuid import uuid4
diff --git a/libs/apprise/AppriseAttachment.py b/libs/apprise/AppriseAttachment.py
index b808cfaee..0a3913ed0 100644
--- a/libs/apprise/AppriseAttachment.py
+++ b/libs/apprise/AppriseAttachment.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
from . import attachment
from . import URLBase
@@ -170,6 +177,11 @@ class AppriseAttachment:
return_status = False
continue
+ elif isinstance(_attachment, AppriseAttachment):
+ # We were provided a list of Apprise Attachments
+ # append our content together
+ instance = _attachment.attachments
+
elif not isinstance(_attachment, attachment.AttachBase):
logger.warning(
"An invalid attachment (type={}) was specified.".format(
@@ -196,7 +208,11 @@ class AppriseAttachment:
continue
# Add our initialized plugin to our server listings
- self.attachments.append(instance)
+ if isinstance(instance, list):
+ self.attachments.extend(instance)
+
+ else:
+ self.attachments.append(instance)
# Return our status
return return_status
diff --git a/libs/apprise/AppriseConfig.py b/libs/apprise/AppriseConfig.py
index f92d31d18..8f2857776 100644
--- a/libs/apprise/AppriseConfig.py
+++ b/libs/apprise/AppriseConfig.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
from . import config
from . import ConfigBase
diff --git a/libs/apprise/AppriseLocale.py b/libs/apprise/AppriseLocale.py
index 9da8467b7..ce61d0c9b 100644
--- a/libs/apprise/AppriseLocale.py
+++ b/libs/apprise/AppriseLocale.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
import ctypes
import locale
@@ -67,7 +74,7 @@ class LazyTranslation:
"""
self.text = text
- super(LazyTranslation, self).__init__(*args, **kwargs)
+ super().__init__(*args, **kwargs)
def __str__(self):
return gettext.gettext(self.text)
diff --git a/libs/apprise/URLBase.py b/libs/apprise/URLBase.py
index eb4a379e4..4b33920ea 100644
--- a/libs/apprise/URLBase.py
+++ b/libs/apprise/URLBase.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
import re
from .logger import logger
@@ -194,7 +201,7 @@ class URLBase:
asset if isinstance(asset, AppriseAsset) else AppriseAsset()
# Certificate Verification (for SSL calls); default to being enabled
- self.verify_certificate = kwargs.get('verify', True)
+ self.verify_certificate = parse_bool(kwargs.get('verify', True))
# Secure Mode
self.secure = kwargs.get('secure', False)
@@ -222,24 +229,22 @@ class URLBase:
self.password = URLBase.unquote(self.password)
# Store our Timeout Variables
- if 'socket_read_timeout' in kwargs:
+ if 'rto' in kwargs:
try:
- self.socket_read_timeout = \
- float(kwargs.get('socket_read_timeout'))
+ self.socket_read_timeout = float(kwargs.get('rto'))
except (TypeError, ValueError):
self.logger.warning(
'Invalid socket read timeout (rto) was specified {}'
- .format(kwargs.get('socket_read_timeout')))
+ .format(kwargs.get('rto')))
- if 'socket_connect_timeout' in kwargs:
+ if 'cto' in kwargs:
try:
- self.socket_connect_timeout = \
- float(kwargs.get('socket_connect_timeout'))
+ self.socket_connect_timeout = float(kwargs.get('cto'))
except (TypeError, ValueError):
self.logger.warning(
'Invalid socket connect timeout (cto) was specified {}'
- .format(kwargs.get('socket_connect_timeout')))
+ .format(kwargs.get('cto')))
if 'tag' in kwargs:
# We want to associate some tags with our notification service.
@@ -598,7 +603,7 @@ class URLBase:
}
@staticmethod
- def parse_url(url, verify_host=True):
+ def parse_url(url, verify_host=True, plus_to_space=False):
"""Parses the URL and returns it broken apart into a dictionary.
This is very specific and customized for Apprise.
@@ -618,7 +623,8 @@ class URLBase:
"""
results = parse_url(
- url, default_schema='unknown', verify_host=verify_host)
+ url, default_schema='unknown', verify_host=verify_host,
+ plus_to_space=plus_to_space)
if not results:
# We're done; we failed to parse our url
@@ -646,11 +652,11 @@ class URLBase:
# Store our socket read timeout if specified
if 'rto' in results['qsd']:
- results['socket_read_timeout'] = results['qsd']['rto']
+ results['rto'] = results['qsd']['rto']
# Store our socket connect timeout if specified
if 'cto' in results['qsd']:
- results['socket_connect_timeout'] = results['qsd']['cto']
+ results['cto'] = results['qsd']['cto']
if 'port' in results['qsd']:
results['port'] = results['qsd']['port']
@@ -679,6 +685,15 @@ class URLBase:
return response
+ def __len__(self):
+ """
+ Should be over-ridden and allows the tracking of how many targets
+ are associated with each URLBase object.
+
+ Default is always 1
+ """
+ return 1
+
def schemas(self):
"""A simple function that returns a set of all schemas associated
with this object based on the object.protocol and
diff --git a/libs/apprise/__init__.py b/libs/apprise/__init__.py
index 04ae0982d..3a9136e96 100644
--- a/libs/apprise/__init__.py
+++ b/libs/apprise/__init__.py
@@ -1,33 +1,40 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2022 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
__title__ = 'Apprise'
-__version__ = '1.1.0'
+__version__ = '1.4.0'
__author__ = 'Chris Caron'
-__license__ = 'MIT'
-__copywrite__ = 'Copyright (C) 2022 Chris Caron <[email protected]>'
+__license__ = 'BSD'
+__copywrite__ = 'Copyright (C) 2023 Chris Caron <[email protected]>'
__email__ = '[email protected]'
__status__ = 'Production'
diff --git a/libs/apprise/attachment/AttachBase.py b/libs/apprise/attachment/AttachBase.py
index 16f6c6429..2b05c8497 100644
--- a/libs/apprise/attachment/AttachBase.py
+++ b/libs/apprise/attachment/AttachBase.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
import os
import time
@@ -120,7 +127,7 @@ class AttachBase(URLBase):
should be considered expired.
"""
- super(AttachBase, self).__init__(**kwargs)
+ super().__init__(**kwargs)
if not mimetypes.inited:
# Ensure mimetypes has been initialized
diff --git a/libs/apprise/attachment/AttachFile.py b/libs/apprise/attachment/AttachFile.py
index 20ee15199..f89b915eb 100644
--- a/libs/apprise/attachment/AttachFile.py
+++ b/libs/apprise/attachment/AttachFile.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
import re
import os
@@ -50,7 +57,7 @@ class AttachFile(AttachBase):
Initialize Local File Attachment Object
"""
- super(AttachFile, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# Store path but mark it dirty since we have not performed any
# verification at this point.
diff --git a/libs/apprise/attachment/AttachHTTP.py b/libs/apprise/attachment/AttachHTTP.py
index da1d698e8..d8b46ff28 100644
--- a/libs/apprise/attachment/AttachHTTP.py
+++ b/libs/apprise/attachment/AttachHTTP.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
import re
import os
@@ -61,7 +68,7 @@ class AttachHTTP(AttachBase):
additionally include as part of the server headers to post with
"""
- super(AttachHTTP, self).__init__(**kwargs)
+ super().__init__(**kwargs)
self.schema = 'https' if self.secure else 'http'
@@ -254,7 +261,7 @@ class AttachHTTP(AttachBase):
self._temp_file.close()
self._temp_file = None
- super(AttachHTTP, self).invalidate()
+ super().invalidate()
def url(self, privacy=False, *args, **kwargs):
"""
diff --git a/libs/apprise/attachment/__init__.py b/libs/apprise/attachment/__init__.py
index 7f83769a8..1b0e1bfe6 100644
--- a/libs/apprise/attachment/__init__.py
+++ b/libs/apprise/attachment/__init__.py
@@ -1,30 +1,36 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
import re
-
from os import listdir
from os.path import dirname
from os.path import abspath
diff --git a/libs/apprise/cli.py b/libs/apprise/cli.py
index 0e60e5cd2..a3335bbb5 100644
--- a/libs/apprise/cli.py
+++ b/libs/apprise/cli.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
-
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# BSD 3-Clause License
+#
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# This code is licensed under the MIT License.
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
import click
import logging
@@ -73,28 +80,64 @@ DEFAULT_CONFIG_PATHS = (
'~/.apprise/apprise.yml',
'~/.config/apprise/apprise',
'~/.config/apprise/apprise.yml',
+
+ # Global Configuration Support
+ '/etc/apprise',
+ '/etc/apprise.yml',
+ '/etc/apprise/apprise',
+ '/etc/apprise/apprise.yml',
)
# Define our paths to search for plugins
DEFAULT_PLUGIN_PATHS = (
'~/.apprise/plugins',
'~/.config/apprise/plugins',
+
+ # Global Plugin Support
+ '/var/lib/apprise/plugins',
)
# Detect Windows
if platform.system() == 'Windows':
# Default Config Search Path for Windows Users
DEFAULT_CONFIG_PATHS = (
- expandvars('%APPDATA%/Apprise/apprise'),
- expandvars('%APPDATA%/Apprise/apprise.yml'),
- expandvars('%LOCALAPPDATA%/Apprise/apprise'),
- expandvars('%LOCALAPPDATA%/Apprise/apprise.yml'),
+ expandvars('%APPDATA%\\Apprise\\apprise'),
+ expandvars('%APPDATA%\\Apprise\\apprise.yml'),
+ expandvars('%LOCALAPPDATA%\\Apprise\\apprise'),
+ expandvars('%LOCALAPPDATA%\\Apprise\\apprise.yml'),
+
+ #
+ # Global Support
+ #
+
+ # C:\ProgramData\Apprise\
+ expandvars('%ALLUSERSPROFILE%\\Apprise\\apprise'),
+ expandvars('%ALLUSERSPROFILE%\\Apprise\\apprise.yml'),
+
+ # C:\Program Files\Apprise
+ expandvars('%PROGRAMFILES%\\Apprise\\apprise'),
+ expandvars('%PROGRAMFILES%\\Apprise\\apprise.yml'),
+
+ # C:\Program Files\Common Files
+ expandvars('%COMMONPROGRAMFILES%\\Apprise\\apprise'),
+ expandvars('%COMMONPROGRAMFILES%\\Apprise\\apprise.yml'),
)
# Default Plugin Search Path for Windows Users
DEFAULT_PLUGIN_PATHS = (
- expandvars('%APPDATA%/Apprise/plugins'),
- expandvars('%LOCALAPPDATA%/Apprise/plugins'),
+ expandvars('%APPDATA%\\Apprise\\plugins'),
+ expandvars('%LOCALAPPDATA%\\Apprise\\plugins'),
+
+ #
+ # Global Support
+ #
+
+ # C:\ProgramData\Apprise\plugins
+ expandvars('%ALLUSERSPROFILE%\\Apprise\\plugins'),
+ # C:\Program Files\Apprise\plugins
+ expandvars('%PROGRAMFILES%\\Apprise\\plugins'),
+ # C:\Program Files\Common Files
+ expandvars('%COMMONPROGRAMFILES%\\Apprise\\plugins'),
)
diff --git a/libs/apprise/common.py b/libs/apprise/common.py
index 958bd22ee..8380c405e 100644
--- a/libs/apprise/common.py
+++ b/libs/apprise/common.py
@@ -1,28 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
# we mirror our base purely for the ability to reset everything; this
# is generally only used in testing and should not be used by developers
diff --git a/libs/apprise/common.pyi b/libs/apprise/common.pyi
index 769573185..862fc4f27 100644
--- a/libs/apprise/common.pyi
+++ b/libs/apprise/common.pyi
@@ -1,3 +1,7 @@
+import types
+import typing as t
+
+
class NotifyType:
INFO: NotifyType
SUCCESS: NotifyType
@@ -12,4 +16,7 @@ class NotifyFormat:
class ContentLocation:
LOCAL: ContentLocation
HOSTED: ContentLocation
- INACCESSIBLE: ContentLocation \ No newline at end of file
+ INACCESSIBLE: ContentLocation
+
+
+NOTIFY_MODULE_MAP: t.Dict[str, t.Dict[str, t.Union[t.Type["NotifyBase"], types.ModuleType]]]
diff --git a/libs/apprise/config/ConfigBase.py b/libs/apprise/config/ConfigBase.py
index d504a98dd..5eb73ebcb 100644
--- a/libs/apprise/config/ConfigBase.py
+++ b/libs/apprise/config/ConfigBase.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2020 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
import os
import re
@@ -113,7 +120,7 @@ class ConfigBase(URLBase):
these 'include' entries to be honored, this value must be set to True.
"""
- super(ConfigBase, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# Tracks the time the content was last retrieved on. This place a role
# for cases where we are not caching our response and are required to
@@ -548,7 +555,7 @@ class ConfigBase(URLBase):
# Define what a valid line should look like
valid_line_re = re.compile(
r'^\s*(?P<line>([;#]+(?P<comment>.*))|'
- r'(\s*(?P<tags>[^=]+)=|=)?\s*'
+ r'(\s*(?P<tags>[a-z0-9, \t_-]+)\s*=|=)?\s*'
r'(?P<url>[a-z0-9]{2,9}://.*)|'
r'include\s+(?P<config>.+))?\s*$', re.I)
diff --git a/libs/apprise/config/ConfigFile.py b/libs/apprise/config/ConfigFile.py
index 10f0a463c..b2c211766 100644
--- a/libs/apprise/config/ConfigFile.py
+++ b/libs/apprise/config/ConfigFile.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
import re
import os
@@ -53,7 +60,7 @@ class ConfigFile(ConfigBase):
additionally include as part of the server headers to post with
"""
- super(ConfigFile, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# Store our file path as it was set
self.path = os.path.abspath(os.path.expanduser(path))
diff --git a/libs/apprise/config/ConfigHTTP.py b/libs/apprise/config/ConfigHTTP.py
index 795c6fac8..5813b90a9 100644
--- a/libs/apprise/config/ConfigHTTP.py
+++ b/libs/apprise/config/ConfigHTTP.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
import re
import requests
@@ -75,7 +82,7 @@ class ConfigHTTP(ConfigBase):
additionally include as part of the server headers to post with
"""
- super(ConfigHTTP, self).__init__(**kwargs)
+ super().__init__(**kwargs)
self.schema = 'https' if self.secure else 'http'
diff --git a/libs/apprise/config/ConfigMemory.py b/libs/apprise/config/ConfigMemory.py
index c8d49a141..ec44e9b4f 100644
--- a/libs/apprise/config/ConfigMemory.py
+++ b/libs/apprise/config/ConfigMemory.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2020 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
from .ConfigBase import ConfigBase
from ..AppriseLocale import gettext_lazy as _
@@ -46,7 +53,7 @@ class ConfigMemory(ConfigBase):
Memory objects just store the raw configuration in memory. There is
no external reference point. It's always considered cached.
"""
- super(ConfigMemory, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# Store our raw config into memory
self.content = content
diff --git a/libs/apprise/config/__init__.py b/libs/apprise/config/__init__.py
index 783118903..7d03a34a8 100644
--- a/libs/apprise/config/__init__.py
+++ b/libs/apprise/config/__init__.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
import re
from os import listdir
diff --git a/libs/apprise/conversion.py b/libs/apprise/conversion.py
index 3b692aa60..77c9aa5e5 100644
--- a/libs/apprise/conversion.py
+++ b/libs/apprise/conversion.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2022 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
import re
from markdown import markdown
@@ -99,7 +106,7 @@ class HTMLConverter(HTMLParser, object):
BLOCK_END = {}
def __init__(self, **kwargs):
- super(HTMLConverter, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# Shoudl we store the text content or not?
self._do_store = True
diff --git a/libs/apprise/decorators/CustomNotifyPlugin.py b/libs/apprise/decorators/CustomNotifyPlugin.py
index 39fb51a9e..9c8e7cb1d 100644
--- a/libs/apprise/decorators/CustomNotifyPlugin.py
+++ b/libs/apprise/decorators/CustomNotifyPlugin.py
@@ -1,27 +1,35 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2022 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
from ..plugins.NotifyBase import NotifyBase
from ..utils import URL_DETAILS_RE
from ..utils import parse_url
@@ -134,7 +142,7 @@ class CustomNotifyPlugin(NotifyBase):
"""
# init parent
- super(CustomNotifyPluginWrapper, self).__init__(**kwargs)
+ super().__init__(**kwargs)
self._default_args = {}
diff --git a/libs/apprise/decorators/__init__.py b/libs/apprise/decorators/__init__.py
index a6ef9662a..699fd0da4 100644
--- a/libs/apprise/decorators/__init__.py
+++ b/libs/apprise/decorators/__init__.py
@@ -1,27 +1,35 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2022 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
from .notify import notify
diff --git a/libs/apprise/decorators/notify.py b/libs/apprise/decorators/notify.py
index 3705e8708..36842b419 100644
--- a/libs/apprise/decorators/notify.py
+++ b/libs/apprise/decorators/notify.py
@@ -1,27 +1,35 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2022 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
from .CustomNotifyPlugin import CustomNotifyPlugin
diff --git a/libs/apprise/logger.py b/libs/apprise/logger.py
index 4510e1b60..005a3e0d7 100644
--- a/libs/apprise/logger.py
+++ b/libs/apprise/logger.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
import os
import logging
@@ -181,6 +188,7 @@ class LogCapture:
if self.__path:
# Close our file pointer
self.__buffer_ptr.close()
+ self.__handler.close()
if self.__delete:
try:
# Always remove file afterwards
diff --git a/libs/apprise/plugins/NotifyAppriseAPI.py b/libs/apprise/plugins/NotifyAppriseAPI.py
index 10d52d5ba..b8765496f 100644
--- a/libs/apprise/plugins/NotifyAppriseAPI.py
+++ b/libs/apprise/plugins/NotifyAppriseAPI.py
@@ -1,31 +1,39 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2021 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
import re
import requests
from json import dumps
+import base64
from .NotifyBase import NotifyBase
from ..URLBase import PrivacyMode
@@ -35,6 +43,20 @@ from ..utils import validate_regex
from ..AppriseLocale import gettext_lazy as _
+class AppriseAPIMethod:
+ """
+ Defines the method to post data tot he remote server
+ """
+ JSON = 'json'
+ FORM = 'form'
+
+
+APPRISE_API_METHODS = (
+ AppriseAPIMethod.FORM,
+ AppriseAPIMethod.JSON,
+)
+
+
class NotifyAppriseAPI(NotifyBase):
"""
A wrapper for Apprise (Persistent) API Notifications
@@ -57,7 +79,7 @@ class NotifyAppriseAPI(NotifyBase):
# Depending on the number of transactions/notifications taking place, this
# could take a while. 30 seconds should be enough to perform the task
- socket_connect_timeout = 30.0
+ socket_read_timeout = 30.0
# Disable throttle rate for Apprise API requests since they are normally
# local anyway
@@ -112,6 +134,12 @@ class NotifyAppriseAPI(NotifyBase):
'name': _('Tags'),
'type': 'string',
},
+ 'method': {
+ 'name': _('Query Method'),
+ 'type': 'choice:string',
+ 'values': APPRISE_API_METHODS,
+ 'default': APPRISE_API_METHODS[0],
+ },
'to': {
'alias_of': 'token',
},
@@ -125,7 +153,8 @@ class NotifyAppriseAPI(NotifyBase):
},
}
- def __init__(self, token=None, tags=None, headers=None, **kwargs):
+ def __init__(self, token=None, tags=None, method=None, headers=None,
+ **kwargs):
"""
Initialize Apprise API Object
@@ -133,7 +162,7 @@ class NotifyAppriseAPI(NotifyBase):
additionally include as part of the server headers to post with
"""
- super(NotifyAppriseAPI, self).__init__(**kwargs)
+ super().__init__(**kwargs)
self.fullpath = kwargs.get('fullpath')
if not isinstance(self.fullpath, str):
@@ -147,6 +176,14 @@ class NotifyAppriseAPI(NotifyBase):
self.logger.warning(msg)
raise TypeError(msg)
+ self.method = self.template_args['method']['default'] \
+ if not isinstance(method, str) else method.lower()
+
+ if self.method not in APPRISE_API_METHODS:
+ msg = 'The method specified ({}) is invalid.'.format(method)
+ self.logger.warning(msg)
+ raise TypeError(msg)
+
# Build list of tags
self.__tags = parse_list(tags)
@@ -162,8 +199,13 @@ class NotifyAppriseAPI(NotifyBase):
Returns the URL built dynamically based on specified arguments.
"""
- # Our URL parameters
- params = self.url_parameters(privacy=privacy, *args, **kwargs)
+ # Define any URL parameters
+ params = {
+ 'method': self.method,
+ }
+
+ # Extend our parameters
+ params.update(self.url_parameters(privacy=privacy, *args, **kwargs))
# Append our headers into our parameters
params.update({'+{}'.format(k): v for k, v in self.headers.items()})
@@ -202,15 +244,61 @@ class NotifyAppriseAPI(NotifyBase):
token=self.pprint(self.token, privacy, safe=''),
params=NotifyAppriseAPI.urlencode(params))
- def send(self, body, title='', notify_type=NotifyType.INFO, **kwargs):
+ def send(self, body, title='', notify_type=NotifyType.INFO, attach=None,
+ **kwargs):
"""
Perform Apprise API Notification
"""
- headers = {}
+ # Prepare HTTP Headers
+ headers = {
+ 'User-Agent': self.app_id,
+ }
+
# Apply any/all header over-rides defined
headers.update(self.headers)
+ attachments = []
+ files = []
+ if attach:
+ for no, attachment in enumerate(attach, start=1):
+ # Perform some simple error checking
+ if not attachment:
+ # We could not access the attachment
+ self.logger.error(
+ 'Could not access attachment {}.'.format(
+ attachment.url(privacy=True)))
+ return False
+
+ try:
+ if self.method == AppriseAPIMethod.JSON:
+ with open(attachment.path, 'rb') as f:
+ # Output must be in a DataURL format (that's what
+ # PushSafer calls it):
+ attachments.append({
+ 'filename': attachment.name,
+ 'base64': base64.b64encode(f.read())
+ .decode('utf-8'),
+ 'mimetype': attachment.mimetype,
+ })
+
+ else: # AppriseAPIMethod.FORM
+ files.append((
+ 'file{:02d}'.format(no),
+ (
+ attachment.name,
+ open(attachment.path, 'rb'),
+ attachment.mimetype,
+ )
+ ))
+
+ except (OSError, IOError) as e:
+ self.logger.warning(
+ 'An I/O error occurred while reading {}.'.format(
+ attachment.name if attachment else 'attachment'))
+ self.logger.debug('I/O Exception: %s' % str(e))
+ return False
+
# prepare Apprise API Object
payload = {
# Apprise API Payload
@@ -220,6 +308,11 @@ class NotifyAppriseAPI(NotifyBase):
'format': self.notify_format,
}
+ if self.method == AppriseAPIMethod.JSON:
+ headers['Content-Type'] = 'application/json'
+ payload['attachments'] = attachments
+ payload = dumps(payload)
+
if self.__tags:
payload['tag'] = self.__tags
@@ -240,8 +333,8 @@ class NotifyAppriseAPI(NotifyBase):
# Some entries can not be over-ridden
headers.update({
- 'User-Agent': self.app_id,
- 'Content-Type': 'application/json',
+ # Our response to be in JSON format always
+ 'Accept': 'application/json',
# Pass our Source UUID4 Identifier
'X-Apprise-ID': self.asset._uid,
# Pass our current recursion count to our upstream server
@@ -259,9 +352,10 @@ class NotifyAppriseAPI(NotifyBase):
try:
r = requests.post(
url,
- data=dumps(payload),
+ data=payload,
headers=headers,
auth=auth,
+ files=files if files else None,
verify=self.verify_certificate,
timeout=self.request_timeout,
)
@@ -283,7 +377,8 @@ class NotifyAppriseAPI(NotifyBase):
return False
else:
- self.logger.info('Sent Apprise API notification.')
+ self.logger.info(
+ 'Sent Apprise API notification; method=%s.', self.method)
except requests.RequestException as e:
self.logger.warning(
@@ -294,6 +389,18 @@ class NotifyAppriseAPI(NotifyBase):
# Return; we're done
return False
+ except (OSError, IOError) as e:
+ self.logger.warning(
+ 'An I/O error occurred while reading one of the '
+ 'attached files.')
+ self.logger.debug('I/O Exception: %s' % str(e))
+ return False
+
+ finally:
+ for file in files:
+ # Ensure all files are closed
+ file[1][1].close()
+
return True
@staticmethod
@@ -370,4 +477,9 @@ class NotifyAppriseAPI(NotifyBase):
# re-assemble our full path
results['fullpath'] = '/'.join(entries)
+ # Set method if specified
+ if 'method' in results['qsd'] and len(results['qsd']['method']):
+ results['method'] = \
+ NotifyAppriseAPI.unquote(results['qsd']['method'])
+
return results
diff --git a/libs/apprise/plugins/NotifyBark.py b/libs/apprise/plugins/NotifyBark.py
index d6283c022..f1c6d7bf9 100644
--- a/libs/apprise/plugins/NotifyBark.py
+++ b/libs/apprise/plugins/NotifyBark.py
@@ -1,27 +1,35 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2022 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
#
# API: https://github.com/Finb/bark-server/blob/master/docs/API_V2.md#python
#
@@ -204,7 +212,7 @@ class NotifyBark(NotifyBase):
"""
Initialize Notify Bark Object
"""
- super(NotifyBark, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# Prepare our URL
self.notify_url = '%s://%s%s/push' % (
@@ -272,7 +280,7 @@ class NotifyBark(NotifyBase):
# error tracking (used for function return)
has_error = False
- if not len(self.targets):
+ if not self.targets:
# We have nothing to notify; we're done
self.logger.warning('There are no Bark devices to notify')
return False
@@ -448,6 +456,12 @@ class NotifyBark(NotifyBase):
params=NotifyBark.urlencode(params),
)
+ def __len__(self):
+ """
+ Returns the number of targets associated with this notification
+ """
+ return len(self.targets)
+
@staticmethod
def parse_url(url):
"""
diff --git a/libs/apprise/plugins/NotifyBase.py b/libs/apprise/plugins/NotifyBase.py
index 4bb937795..1b07baa71 100644
--- a/libs/apprise/plugins/NotifyBase.py
+++ b/libs/apprise/plugins/NotifyBase.py
@@ -1,29 +1,38 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+import asyncio
import re
+from functools import partial
from ..URLBase import URLBase
from ..common import NotifyType
@@ -36,12 +45,7 @@ from ..AppriseLocale import gettext_lazy as _
from ..AppriseAttachment import AppriseAttachment
-# Wrap our base with the asyncio wrapper
-from ..py3compat.asyncio import AsyncNotifyBase
-BASE_OBJECT = AsyncNotifyBase
-
-
-class NotifyBase(BASE_OBJECT):
+class NotifyBase(URLBase):
"""
This is the base class for all notification services
"""
@@ -180,7 +184,7 @@ class NotifyBase(BASE_OBJECT):
"""
- super(NotifyBase, self).__init__(**kwargs)
+ super().__init__(**kwargs)
if 'format' in kwargs:
# Store the specified format if specified
@@ -267,19 +271,64 @@ class NotifyBase(BASE_OBJECT):
color_type=color_type,
)
- def notify(self, body, title=None, notify_type=NotifyType.INFO,
- overflow=None, attach=None, body_format=None, **kwargs):
+ def notify(self, *args, **kwargs):
"""
Performs notification
+ """
+ try:
+ # Build a list of dictionaries that can be used to call send().
+ send_calls = list(self._build_send_calls(*args, **kwargs))
+
+ except TypeError:
+ # Internal error
+ return False
+
+ else:
+ # Loop through each call, one at a time. (Use a list rather than a
+ # generator to call all the partials, even in case of a failure.)
+ the_calls = [self.send(**kwargs2) for kwargs2 in send_calls]
+ return all(the_calls)
+
+ async def async_notify(self, *args, **kwargs):
+ """
+ Performs notification for asynchronous callers
+ """
+ try:
+ # Build a list of dictionaries that can be used to call send().
+ send_calls = list(self._build_send_calls(*args, **kwargs))
+
+ except TypeError:
+ # Internal error
+ return False
+
+ else:
+ loop = asyncio.get_event_loop()
+
+ # Wrap each call in a coroutine that uses the default executor.
+ # TODO: In the future, allow plugins to supply a native
+ # async_send() method.
+ async def do_send(**kwargs2):
+ send = partial(self.send, **kwargs2)
+ result = await loop.run_in_executor(None, send)
+ return result
+ # gather() all calls in parallel.
+ the_cors = (do_send(**kwargs2) for kwargs2 in send_calls)
+ return all(await asyncio.gather(*the_cors))
+
+ def _build_send_calls(self, body, title=None,
+ notify_type=NotifyType.INFO, overflow=None,
+ attach=None, body_format=None, **kwargs):
+ """
+ Get a list of dictionaries that can be used to call send() or
+ (in the future) async_send().
"""
if not self.enabled:
# Deny notifications issued to services that are disabled
- self.logger.warning(
- "{} is currently disabled on this system.".format(
- self.service_name))
- return False
+ msg = f"{self.service_name} is currently disabled on this system."
+ self.logger.warning(msg)
+ raise TypeError(msg)
# Prepare attachments if required
if attach is not None and not isinstance(attach, AppriseAttachment):
@@ -288,7 +337,7 @@ class NotifyBase(BASE_OBJECT):
except TypeError:
# bad attachments
- return False
+ raise
# Handle situations where the title is None
title = '' if not title else title
@@ -299,14 +348,11 @@ class NotifyBase(BASE_OBJECT):
body_format=body_format):
# Send notification
- if not self.send(body=chunk['body'], title=chunk['title'],
- notify_type=notify_type, attach=attach,
- body_format=body_format):
-
- # Toggle our return status flag
- return False
-
- return True
+ yield dict(
+ body=chunk['body'], title=chunk['title'],
+ notify_type=notify_type, attach=attach,
+ body_format=body_format
+ )
def _apply_overflow(self, body, title=None, overflow=None,
body_format=None):
@@ -423,13 +469,13 @@ class NotifyBase(BASE_OBJECT):
'overflow': self.overflow_mode,
}
- params.update(super(NotifyBase, self).url_parameters(*args, **kwargs))
+ params.update(super().url_parameters(*args, **kwargs))
# return default parameters
return params
@staticmethod
- def parse_url(url, verify_host=True):
+ def parse_url(url, verify_host=True, plus_to_space=False):
"""Parses the URL and returns it broken apart into a dictionary.
This is very specific and customized for Apprise.
@@ -447,7 +493,8 @@ class NotifyBase(BASE_OBJECT):
A dictionary is returned containing the URL fully parsed if
successful, otherwise None is returned.
"""
- results = URLBase.parse_url(url, verify_host=verify_host)
+ results = URLBase.parse_url(
+ url, verify_host=verify_host, plus_to_space=plus_to_space)
if not results:
# We're done; we failed to parse our url
diff --git a/libs/apprise/plugins/NotifyBoxcar.py b/libs/apprise/plugins/NotifyBoxcar.py
index b40b71cd9..8e7045c7b 100644
--- a/libs/apprise/plugins/NotifyBoxcar.py
+++ b/libs/apprise/plugins/NotifyBoxcar.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
import re
import requests
@@ -39,6 +46,7 @@ except ImportError:
from .NotifyBase import NotifyBase
from ..URLBase import PrivacyMode
from ..utils import parse_bool
+from ..utils import parse_list
from ..utils import validate_regex
from ..common import NotifyType
from ..common import NotifyImageSize
@@ -51,7 +59,7 @@ DEFAULT_TAG = '@all'
# list of tagged devices that the notification need to be send to, and a
# boolean operator (‘and’ / ‘or’) that defines the criteria to match devices
# against those tags.
-IS_TAG = re.compile(r'^[@](?P<name>[A-Z0-9]{1,63})$', re.I)
+IS_TAG = re.compile(r'^[@]?(?P<name>[A-Z0-9]{1,63})$', re.I)
# Device tokens are only referenced when developing.
# It's not likely you'll send a message directly to a device, but if you do;
@@ -150,10 +158,10 @@ class NotifyBoxcar(NotifyBase):
"""
Initialize Boxcar Object
"""
- super(NotifyBoxcar, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# Initialize tag list
- self.tags = list()
+ self._tags = list()
# Initialize device_token list
self.device_tokens = list()
@@ -177,29 +185,27 @@ class NotifyBoxcar(NotifyBase):
raise TypeError(msg)
if not targets:
- self.tags.append(DEFAULT_TAG)
+ self._tags.append(DEFAULT_TAG)
targets = []
- elif isinstance(targets, str):
- targets = [x for x in filter(bool, TAGS_LIST_DELIM.split(
- targets,
- ))]
-
# Validate targets and drop bad ones:
- for target in targets:
- if IS_TAG.match(target):
+ for target in parse_list(targets):
+ result = IS_TAG.match(target)
+ if result:
# store valid tag/alias
- self.tags.append(IS_TAG.match(target).group('name'))
+ self._tags.append(result.group('name'))
+ continue
- elif IS_DEVICETOKEN.match(target):
+ result = IS_DEVICETOKEN.match(target)
+ if result:
# store valid device
self.device_tokens.append(target)
+ continue
- else:
- self.logger.warning(
- 'Dropped invalid tag/alias/device_token '
- '({}) specified.'.format(target),
- )
+ self.logger.warning(
+ 'Dropped invalid tag/alias/device_token '
+ '({}) specified.'.format(target),
+ )
# Track whether or not we want to send an image with our notification
# or not.
@@ -231,8 +237,8 @@ class NotifyBoxcar(NotifyBase):
if body:
payload['aps']['alert'] = body
- if self.tags:
- payload['tags'] = {'or': self.tags}
+ if self._tags:
+ payload['tags'] = {'or': self._tags}
if self.device_tokens:
payload['device_tokens'] = self.device_tokens
@@ -334,10 +340,18 @@ class NotifyBoxcar(NotifyBase):
self.secret, privacy, mode=PrivacyMode.Secret, safe=''),
targets='/'.join([
NotifyBoxcar.quote(x, safe='') for x in chain(
- self.tags, self.device_tokens) if x != DEFAULT_TAG]),
+ self._tags, self.device_tokens) if x != DEFAULT_TAG]),
params=NotifyBoxcar.urlencode(params),
)
+ def __len__(self):
+ """
+ Returns the number of targets associated with this notification
+ """
+ targets = len(self._tags) + len(self.device_tokens)
+ # DEFAULT_TAG is set if no tokens/tags are otherwise set
+ return targets if targets > 0 else 1
+
@staticmethod
def parse_url(url):
"""
diff --git a/libs/apprise/plugins/NotifyBulkSMS.py b/libs/apprise/plugins/NotifyBulkSMS.py
index 8fa546421..814badaef 100644
--- a/libs/apprise/plugins/NotifyBulkSMS.py
+++ b/libs/apprise/plugins/NotifyBulkSMS.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2022 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
# To use this service you will need a BulkSMS account
# You will need credits (new accounts start with a few)
@@ -30,7 +37,6 @@
# API is documented here:
# - https://www.bulksms.com/developer/json/v1/#tag/Message
import re
-import six
import requests
import json
from itertools import chain
@@ -192,7 +198,7 @@ class NotifyBulkSMS(NotifyBase):
# Setup our route
self.route = self.template_args['route']['default'] \
- if not isinstance(route, six.string_types) else route.upper()
+ if not isinstance(route, str) else route.upper()
if self.route not in BULKSMS_ROUTING_GROUPS:
msg = 'The route specified ({}) is invalid.'.format(route)
self.logger.warning(msg)
@@ -408,6 +414,24 @@ class NotifyBulkSMS(NotifyBase):
for x in self.groups])),
params=NotifyBulkSMS.urlencode(params))
+ def __len__(self):
+ """
+ Returns the number of targets associated with this notification
+ """
+
+ #
+ # Factor batch into calculation
+ #
+ # Note: Groups always require a separate request (and can not be
+ # included in batch calculations)
+ batch_size = 1 if not self.batch else self.default_batch_size
+ targets = len(self.targets)
+ if batch_size > 1:
+ targets = int(targets / batch_size) + \
+ (1 if targets % batch_size else 0)
+
+ return targets + len(self.groups)
+
@staticmethod
def parse_url(url):
"""
diff --git a/libs/apprise/plugins/NotifyClickSend.py b/libs/apprise/plugins/NotifyClickSend.py
index 9054c6f01..ed6e462fc 100644
--- a/libs/apprise/plugins/NotifyClickSend.py
+++ b/libs/apprise/plugins/NotifyClickSend.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
# To use this plugin, simply signup with clicksend:
# https://www.clicksend.com/
@@ -132,7 +139,7 @@ class NotifyClickSend(NotifyBase):
"""
Initialize ClickSend Object
"""
- super(NotifyClickSend, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# Prepare Batch Mode Flag
self.batch = batch
@@ -281,6 +288,21 @@ class NotifyClickSend(NotifyBase):
params=NotifyClickSend.urlencode(params),
)
+ def __len__(self):
+ """
+ Returns the number of targets associated with this notification
+ """
+ #
+ # Factor batch into calculation
+ #
+ batch_size = 1 if not self.batch else self.default_batch_size
+ targets = len(self.targets)
+ if batch_size > 1:
+ targets = int(targets / batch_size) + \
+ (1 if targets % batch_size else 0)
+
+ return targets
+
@staticmethod
def parse_url(url):
"""
diff --git a/libs/apprise/plugins/NotifyD7Networks.py b/libs/apprise/plugins/NotifyD7Networks.py
index b7575d92e..3d0ee8aa4 100644
--- a/libs/apprise/plugins/NotifyD7Networks.py
+++ b/libs/apprise/plugins/NotifyD7Networks.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
# To use this service you will need a D7 Networks account from their website
# at https://d7networks.com/
@@ -29,17 +36,18 @@
# After you've established your account you can get your api login credentials
# (both user and password) from the API Details section from within your
# account profile area: https://d7networks.com/accounts/profile/
+#
+# API Reference: https://d7networks.com/docs/Messages/Send_Message/
import requests
-import base64
from json import dumps
from json import loads
from .NotifyBase import NotifyBase
-from ..URLBase import PrivacyMode
from ..common import NotifyType
from ..utils import is_phone_no
from ..utils import parse_phone_no
+from ..utils import validate_regex
from ..utils import parse_bool
from ..AppriseLocale import gettext_lazy as _
@@ -52,25 +60,6 @@ D7NETWORKS_HTTP_ERROR_MAP = {
}
-# Priorities
-class D7SMSPriority:
- """
- D7 Networks SMS Message Priority
- """
- LOW = 0
- MODERATE = 1
- NORMAL = 2
- HIGH = 3
-
-
-D7NETWORK_SMS_PRIORITIES = (
- D7SMSPriority.LOW,
- D7SMSPriority.MODERATE,
- D7SMSPriority.NORMAL,
- D7SMSPriority.HIGH,
-)
-
-
class NotifyD7Networks(NotifyBase):
"""
A wrapper for D7 Networks Notifications
@@ -92,11 +81,8 @@ class NotifyD7Networks(NotifyBase):
# A URL that takes you to the setup/help of the specific protocol
setup_url = 'https://github.com/caronc/apprise/wiki/Notify_d7networks'
- # D7 Networks batch notification URL
- notify_batch_url = 'http://rest-api.d7networks.com/secure/sendbatch'
-
# D7 Networks single notification URL
- notify_url = 'http://rest-api.d7networks.com/secure/send'
+ notify_url = 'https://api.d7networks.com/messages/v1/send'
# The maximum length of the body
body_maxlen = 160
@@ -107,21 +93,16 @@ class NotifyD7Networks(NotifyBase):
# Define object templates
templates = (
- '{schema}://{user}:{password}@{targets}',
+ '{schema}://{token}@{targets}',
)
# Define our template tokens
template_tokens = dict(NotifyBase.template_tokens, **{
- 'user': {
- 'name': _('Username'),
+ 'token': {
+ 'name': _('API Access Token'),
'type': 'string',
'required': True,
- },
- 'password': {
- 'name': _('Password'),
- 'type': 'string',
'private': True,
- 'required': True,
},
'target_phone': {
'name': _('Target Phone No'),
@@ -138,16 +119,11 @@ class NotifyD7Networks(NotifyBase):
# Define our template arguments
template_args = dict(NotifyBase.template_args, **{
- 'priority': {
- 'name': _('Priority'),
- 'type': 'choice:int',
- 'min': D7SMSPriority.LOW,
- 'max': D7SMSPriority.HIGH,
- 'values': D7NETWORK_SMS_PRIORITIES,
-
- # The website identifies that the default priority is low; so
- # this plugin will honor that same default
- 'default': D7SMSPriority.LOW,
+ 'unicode': {
+ # Unicode characters (default is 'auto')
+ 'name': _('Unicode Characters'),
+ 'type': 'bool',
+ 'default': False,
},
'batch': {
'name': _('Batch Mode'),
@@ -172,19 +148,12 @@ class NotifyD7Networks(NotifyBase):
},
})
- def __init__(self, targets=None, priority=None, source=None, batch=False,
- **kwargs):
+ def __init__(self, token=None, targets=None, source=None,
+ batch=False, unicode=None, **kwargs):
"""
Initialize D7 Networks Object
"""
- super(NotifyD7Networks, self).__init__(**kwargs)
-
- # The Priority of the message
- if priority not in D7NETWORK_SMS_PRIORITIES:
- self.priority = self.template_args['priority']['default']
-
- else:
- self.priority = priority
+ super().__init__(**kwargs)
# Prepare Batch Mode Flag
self.batch = batch
@@ -193,8 +162,15 @@ class NotifyD7Networks(NotifyBase):
self.source = None \
if not isinstance(source, str) else source.strip()
- if not (self.user and self.password):
- msg = 'A D7 Networks user/pass was not provided.'
+ # Define whether or not we should set the unicode flag
+ self.unicode = self.template_args['unicode']['default'] \
+ if unicode is None else bool(unicode)
+
+ # The token associated with the account
+ self.token = validate_regex(token)
+ if not self.token:
+ msg = 'The D7 Networks token specified ({}) is invalid.'\
+ .format(token)
self.logger.warning(msg)
raise TypeError(msg)
@@ -229,40 +205,41 @@ class NotifyD7Networks(NotifyBase):
# error tracking (used for function return)
has_error = False
- auth = '{user}:{password}'.format(
- user=self.user, password=self.password)
-
- # Python 3's versio of b64encode() expects a byte array and not
- # a string. To accommodate this, we encode the content here
- auth = auth.encode('utf-8')
-
# Prepare our headers
headers = {
'User-Agent': self.app_id,
+ 'Content-Type': 'application/json',
'Accept': 'application/json',
- 'Authorization': 'Basic {}'.format(base64.b64encode(auth))
+ 'Authorization': f'Bearer {self.token}',
}
- # Our URL varies depending if we're doing a batch mode or not
- url = self.notify_batch_url if self.batch else self.notify_url
+ payload = {
+ 'message_globals': {
+ 'channel': 'sms',
+ },
+ 'messages': [{
+ # Populated later on
+ 'recipients': None,
+ 'content': body,
+ 'data_coding':
+ # auto is a better substitute over 'text' as text is easier to
+ # detect from a post than `unicode` is.
+ 'auto' if not self.unicode else 'unicode',
+ }],
+ }
# use the list directly
targets = list(self.targets)
+ if self.source:
+ payload['message_globals']['originator'] = self.source
+
+ target = None
while len(targets):
if self.batch:
# Prepare our payload
- payload = {
- 'globals': {
- 'priority': self.priority,
- 'from': self.source if self.source else self.app_id,
- },
- 'messages': [{
- 'to': self.targets,
- 'content': body,
- }],
- }
+ payload['messages'][0]['recipients'] = self.targets
# Reset our targets so we don't keep going. This is required
# because we're in batch mode; we only need to loop once.
@@ -274,24 +251,19 @@ class NotifyD7Networks(NotifyBase):
target = targets.pop(0)
# Prepare our payload
- payload = {
- 'priority': self.priority,
- 'content': body,
- 'to': target,
- 'from': self.source if self.source else self.app_id,
- }
+ payload['messages'][0]['recipients'] = [target]
# Some Debug Logging
self.logger.debug(
'D7 Networks POST URL: {} (cert_verify={})'.format(
- url, self.verify_certificate))
+ self.notify_url, self.verify_certificate))
self.logger.debug('D7 Networks Payload: {}' .format(payload))
# Always call throttle before any remote server i/o is made
self.throttle()
try:
r = requests.post(
- url,
+ self.notify_url,
data=dumps(payload),
headers=headers,
verify=self.verify_certificate,
@@ -337,29 +309,9 @@ class NotifyD7Networks(NotifyBase):
else:
if self.batch:
- count = len(self.targets)
- try:
- # Get our message delivery count if we can
- json_response = loads(r.content)
- count = int(json_response.get(
- 'data', {}).get('messageCount', -1))
-
- except (AttributeError, TypeError, ValueError):
- # ValueError = r.content is Unparsable
- # TypeError = r.content is None
- # AttributeError = r is None
-
- # We could not parse JSON response. Assume that
- # our delivery is okay for now.
- pass
-
- if count != len(self.targets):
- has_error = True
-
self.logger.info(
'Sent D7 Networks batch SMS notification to '
- '{} of {} target(s).'.format(
- count, len(self.targets)))
+ '{} target(s).'.format(len(self.targets)))
else:
self.logger.info(
@@ -389,26 +341,31 @@ class NotifyD7Networks(NotifyBase):
# Define any URL parameters
params = {
'batch': 'yes' if self.batch else 'no',
+ 'unicode': 'yes' if self.unicode else 'no',
}
- # Extend our parameters
- params.update(self.url_parameters(privacy=privacy, *args, **kwargs))
-
- if self.priority != self.template_args['priority']['default']:
- params['priority'] = str(self.priority)
-
if self.source:
params['from'] = self.source
- return '{schema}://{user}:{password}@{targets}/?{params}'.format(
+ # Extend our parameters
+ params.update(self.url_parameters(privacy=privacy, *args, **kwargs))
+
+ return '{schema}://{token}@{targets}/?{params}'.format(
schema=self.secure_protocol,
- user=NotifyD7Networks.quote(self.user, safe=''),
- password=self.pprint(
- self.password, privacy, mode=PrivacyMode.Secret, safe=''),
+ token=self.pprint(self.token, privacy, safe=''),
targets='/'.join(
[NotifyD7Networks.quote(x, safe='') for x in self.targets]),
params=NotifyD7Networks.urlencode(params))
+ def __len__(self):
+ """
+ Returns the number of targets associated with this notification
+ """
+ #
+ # Factor batch into calculation
+ #
+ return len(self.targets) if not self.batch else 1
+
@staticmethod
def parse_url(url):
"""
@@ -421,6 +378,23 @@ class NotifyD7Networks(NotifyBase):
# We're done early as we couldn't load the results
return results
+ if 'token' in results['qsd'] and len(results['qsd']['token']):
+ results['token'] = \
+ NotifyD7Networks.unquote(results['qsd']['token'])
+
+ elif results['user']:
+ results['token'] = NotifyD7Networks.unquote(results['user'])
+
+ if results['password']:
+ # Support token containing a colon (:)
+ results['token'] += \
+ ':' + NotifyD7Networks.unquote(results['password'])
+
+ elif results['password']:
+ # Support token starting with a colon (:)
+ results['token'] = \
+ ':' + NotifyD7Networks.unquote(results['password'])
+
# Initialize our targets
results['targets'] = list()
@@ -432,44 +406,27 @@ class NotifyD7Networks(NotifyBase):
results['targets'].extend(
NotifyD7Networks.split_path(results['fullpath']))
- # Set our priority
- if 'priority' in results['qsd'] and len(results['qsd']['priority']):
- _map = {
- 'l': D7SMSPriority.LOW,
- '0': D7SMSPriority.LOW,
- 'm': D7SMSPriority.MODERATE,
- '1': D7SMSPriority.MODERATE,
- 'n': D7SMSPriority.NORMAL,
- '2': D7SMSPriority.NORMAL,
- 'h': D7SMSPriority.HIGH,
- '3': D7SMSPriority.HIGH,
- }
- try:
- results['priority'] = \
- _map[results['qsd']['priority'][0].lower()]
-
- except KeyError:
- # No priority was set
- pass
-
- # Support the 'from' and 'source' variable so that we can support
- # targets this way too.
- # The 'from' makes it easier to use yaml configuration
- if 'from' in results['qsd'] and len(results['qsd']['from']):
- results['source'] = \
- NotifyD7Networks.unquote(results['qsd']['from'])
- if 'source' in results['qsd'] and len(results['qsd']['source']):
- results['source'] = \
- NotifyD7Networks.unquote(results['qsd']['source'])
-
# Get Batch Mode Flag
results['batch'] = \
parse_bool(results['qsd'].get('batch', False))
+ # Get Unicode Flag
+ results['unicode'] = \
+ parse_bool(results['qsd'].get('unicode', False))
+
# Support the 'to' variable so that we can support targets this way too
# The 'to' makes it easier to use yaml configuration
if 'to' in results['qsd'] and len(results['qsd']['to']):
results['targets'] += \
NotifyD7Networks.parse_phone_no(results['qsd']['to'])
+ # Support the 'from' and source variable
+ if 'from' in results['qsd'] and len(results['qsd']['from']):
+ results['source'] = \
+ NotifyD7Networks.unquote(results['qsd']['from'])
+
+ elif 'source' in results['qsd'] and len(results['qsd']['source']):
+ results['source'] = \
+ NotifyD7Networks.unquote(results['qsd']['source'])
+
return results
diff --git a/libs/apprise/plugins/NotifyDBus.py b/libs/apprise/plugins/NotifyDBus.py
index b568dfe73..62a1093c8 100644
--- a/libs/apprise/plugins/NotifyDBus.py
+++ b/libs/apprise/plugins/NotifyDBus.py
@@ -1,31 +1,39 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
from __future__ import absolute_import
from __future__ import print_function
+import sys
from .NotifyBase import NotifyBase
from ..common import NotifyImageSize
from ..common import NotifyType
@@ -77,6 +85,13 @@ try:
NOTIFY_DBUS_SUPPORT_ENABLED = (
LOOP_GLIB is not None or LOOP_QT is not None)
+ # ImportError: When using gi.repository you must not import static modules
+ # like "gobject". Please change all occurrences of "import gobject" to
+ # "from gi.repository import GObject".
+ # See: https://bugzilla.gnome.org/show_bug.cgi?id=709183
+ if "gobject" in sys.modules: # pragma: no cover
+ del sys.modules["gobject"]
+
try:
# The following is required for Image/Icon loading only
import gi
@@ -233,7 +248,7 @@ class NotifyDBus(NotifyBase):
Initialize DBus Object
"""
- super(NotifyDBus, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# Track our notifications
self.registry = {}
@@ -272,12 +287,9 @@ class NotifyDBus(NotifyBase):
self.x_axis = None
self.y_axis = None
- # Track whether or not we want to send an image with our notification
- # or not.
+ # Track whether we want to add an image to the notification.
self.include_image = include_image
- return
-
def send(self, body, title='', notify_type=NotifyType.INFO, **kwargs):
"""
Perform DBus Notification
@@ -286,10 +298,10 @@ class NotifyDBus(NotifyBase):
try:
session = SessionBus(mainloop=MAINLOOP_MAP[self.schema])
- except DBusException:
+ except DBusException as e:
# Handle exception
self.logger.warning('Failed to send DBus notification.')
- self.logger.exception('DBus Exception')
+ self.logger.debug(f'DBus Exception: {e}')
return False
# If there is no title, but there is a body, swap the two to get rid
@@ -342,8 +354,8 @@ class NotifyDBus(NotifyBase):
except Exception as e:
self.logger.warning(
- "Could not load Gnome notification icon ({}): {}"
- .format(icon_path, e))
+ "Could not load notification icon (%s).", icon_path)
+ self.logger.debug(f'DBus Exception: {e}')
try:
# Always call throttle() before any remote execution is made
@@ -370,9 +382,9 @@ class NotifyDBus(NotifyBase):
self.logger.info('Sent DBus notification.')
- except Exception:
+ except Exception as e:
self.logger.warning('Failed to send DBus notification.')
- self.logger.exception('DBus Exception')
+ self.logger.debug(f'DBus Exception: {e}')
return False
return True
diff --git a/libs/apprise/plugins/NotifyDapnet.py b/libs/apprise/plugins/NotifyDapnet.py
index 27ea65cd3..1b718286a 100644
--- a/libs/apprise/plugins/NotifyDapnet.py
+++ b/libs/apprise/plugins/NotifyDapnet.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
# To use this plugin, sign up with Hampager (you need to be a licensed
# ham radio operator
@@ -179,7 +186,7 @@ class NotifyDapnet(NotifyBase):
"""
Initialize Dapnet Object
"""
- super(NotifyDapnet, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# Parse our targets
self.targets = list()
@@ -343,6 +350,21 @@ class NotifyDapnet(NotifyBase):
params=NotifyDapnet.urlencode(params),
)
+ def __len__(self):
+ """
+ Returns the number of targets associated with this notification
+ """
+ #
+ # Factor batch into calculation
+ #
+ batch_size = 1 if not self.batch else self.default_batch_size
+ targets = len(self.targets)
+ if batch_size > 1:
+ targets = int(targets / batch_size) + \
+ (1 if targets % batch_size else 0)
+
+ return targets
+
@staticmethod
def parse_url(url):
"""
diff --git a/libs/apprise/plugins/NotifyDingTalk.py b/libs/apprise/plugins/NotifyDingTalk.py
index 68c069479..ae2a9b499 100644
--- a/libs/apprise/plugins/NotifyDingTalk.py
+++ b/libs/apprise/plugins/NotifyDingTalk.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2020 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
import re
import time
@@ -124,7 +131,7 @@ class NotifyDingTalk(NotifyBase):
"""
Initialize DingTalk Object
"""
- super(NotifyDingTalk, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# Secret Key (associated with project)
self.token = validate_regex(
@@ -302,6 +309,13 @@ class NotifyDingTalk(NotifyBase):
[NotifyDingTalk.quote(x, safe='') for x in self.targets]),
args=NotifyDingTalk.urlencode(args))
+ def __len__(self):
+ """
+ Returns the number of targets associated with this notification
+ """
+ targets = len(self.targets)
+ return targets if targets > 0 else 1
+
@staticmethod
def parse_url(url):
"""
diff --git a/libs/apprise/plugins/NotifyDiscord.py b/libs/apprise/plugins/NotifyDiscord.py
index 48e2c2836..fff76eef2 100644
--- a/libs/apprise/plugins/NotifyDiscord.py
+++ b/libs/apprise/plugins/NotifyDiscord.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
# For this to work correctly you need to create a webhook. To do this just
# click on the little gear icon next to the channel you're part of. From
@@ -164,7 +171,7 @@ class NotifyDiscord(NotifyBase):
Initialize Discord Object
"""
- super(NotifyDiscord, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# Webhook ID (associated with project)
self.webhook_id = validate_regex(webhook_id)
@@ -283,9 +290,6 @@ class NotifyDiscord(NotifyBase):
payload['content'] = \
body if not title else "{}\r\n{}".format(title, body)
- if self.thread_id:
- payload['thread_id'] = self.thread_id
-
if self.avatar and (image_url or self.avatar_url):
payload['avatar_url'] = \
self.avatar_url if self.avatar_url else image_url
@@ -294,7 +298,8 @@ class NotifyDiscord(NotifyBase):
# Optionally override the default username of the webhook
payload['username'] = self.user
- if not self._send(payload):
+ params = {'thread_id': self.thread_id} if self.thread_id else None
+ if not self._send(payload, params=params):
# We failed to post our message
return False
@@ -338,7 +343,7 @@ class NotifyDiscord(NotifyBase):
# Otherwise return
return True
- def _send(self, payload, attach=None, **kwargs):
+ def _send(self, payload, attach=None, params=None, **kwargs):
"""
Wrapper to the requests (post) object
"""
@@ -389,6 +394,7 @@ class NotifyDiscord(NotifyBase):
r = requests.post(
notify_url,
+ params=params,
data=payload if files else dumps(payload),
headers=headers,
files=files,
@@ -580,7 +586,7 @@ class NotifyDiscord(NotifyBase):
if description:
# Strip description from our string since it has been handled
# now.
- markdown = re.sub(description, '', markdown, count=1)
+ markdown = re.sub(re.escape(description), '', markdown, count=1)
regex = re.compile(
r'\s*#[# \t\v]*(?P<name>[^\n]+)(\n|\s*$)'
diff --git a/libs/apprise/plugins/NotifyEmail.py b/libs/apprise/plugins/NotifyEmail.py
index 5858f0906..e55de7314 100644
--- a/libs/apprise/plugins/NotifyEmail.py
+++ b/libs/apprise/plugins/NotifyEmail.py
@@ -1,30 +1,39 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+import dataclasses
import re
import smtplib
+import typing as t
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication
from email.mime.multipart import MIMEMultipart
@@ -41,6 +50,7 @@ from ..common import NotifyFormat, NotifyType
from ..conversion import convert_between
from ..utils import is_email, parse_emails
from ..AppriseLocale import gettext_lazy as _
+from ..logger import logger
# Globally Default encoding mode set to Quoted Printable.
charset.add_charset('utf-8', charset.QP, charset.QP, 'utf-8')
@@ -60,15 +70,23 @@ class WebBaseLogin:
# Secure Email Modes
class SecureMailMode:
+ INSECURE = "insecure"
SSL = "ssl"
STARTTLS = "starttls"
# Define all of the secure modes (used during validation)
-SECURE_MODES = (
- SecureMailMode.SSL,
- SecureMailMode.STARTTLS,
-)
+SECURE_MODES = {
+ SecureMailMode.STARTTLS: {
+ 'default_port': 587,
+ },
+ SecureMailMode.SSL: {
+ 'default_port': 465,
+ },
+ SecureMailMode.INSECURE: {
+ 'default_port': 25,
+ },
+}
# To attempt to make this script stupid proof, if we detect an email address
# that is part of the this table, we can pre-use a lot more defaults if they
@@ -109,7 +127,7 @@ EMAIL_TEMPLATES = (
'Microsoft Hotmail',
re.compile(
r'^((?P<label>[^+]+)\+)?(?P<id>[^@]+)@'
- r'(?P<domain>(outlook|hotmail|live)\.com(\.au)?)$', re.I),
+ r'(?P<domain>(hotmail|live)\.com(\.au)?)$', re.I),
{
'port': 587,
'smtp_host': 'smtp-mail.outlook.com',
@@ -119,6 +137,21 @@ EMAIL_TEMPLATES = (
},
),
+ # Microsoft Outlook
+ (
+ 'Microsoft Outlook',
+ re.compile(
+ r'^((?P<label>[^+]+)\+)?(?P<id>[^@]+)@'
+ r'(?P<domain>(smtp\.)?outlook\.com(\.au)?)$', re.I),
+ {
+ 'port': 587,
+ 'smtp_host': 'smtp.outlook.com',
+ 'secure': True,
+ 'secure_mode': SecureMailMode.STARTTLS,
+ 'login_type': (WebBaseLogin.EMAIL, )
+ },
+ ),
+
# Microsoft Office 365 (Email Server)
# You must specify an authenticated sender address in the from= settings
# and a valid email in the to= to deliver your emails to
@@ -282,6 +315,13 @@ EMAIL_TEMPLATES = (
)
+class EmailMessage:
+ recipient: str
+ to_addrs: t.List[str]
+ body: str
+
+
class NotifyEmail(NotifyBase):
"""
A wrapper to Email Notifications
@@ -303,15 +343,6 @@ class NotifyEmail(NotifyBase):
# Default Notify Format
notify_format = NotifyFormat.HTML
- # Default Non-Encryption Port
- default_port = 25
-
- # Default Secure Port
- default_secure_port = 587
-
- # Default Secure Mode
- default_secure_mode = SecureMailMode.STARTTLS
-
# Default SMTP Timeout (in seconds)
socket_connect_timeout = 15
@@ -373,7 +404,7 @@ class NotifyEmail(NotifyBase):
'name': {
'name': _('From Name'),
'type': 'string',
- 'map_to': 'from_name',
+ 'map_to': 'from_addr',
},
'cc': {
'name': _('Carbon Copy'),
@@ -410,24 +441,16 @@ class NotifyEmail(NotifyBase):
},
}
- def __init__(self, smtp_host=None, from_name=None,
- from_addr=None, secure_mode=None, targets=None, cc=None,
- bcc=None, reply_to=None, headers=None, **kwargs):
+ def __init__(self, smtp_host=None, from_addr=None, secure_mode=None,
+ targets=None, cc=None, bcc=None, reply_to=None, headers=None,
+ **kwargs):
"""
Initialize Email Object
The smtp_host and secure_mode can be automatically detected depending
on how the URL was built
"""
- super(NotifyEmail, self).__init__(**kwargs)
-
- # Handle SMTP vs SMTPS (Secure vs UnSecure)
- if not self.port:
- if self.secure:
- self.port = self.default_secure_port
-
- else:
- self.port = self.default_port
+ super().__init__(**kwargs)
# Acquire Email 'To'
self.targets = list()
@@ -451,40 +474,49 @@ class NotifyEmail(NotifyBase):
# Now we want to construct the To and From email
# addresses from the URL provided
- self.from_addr = from_addr
+ self.from_addr = [False, '']
- if self.user and not self.from_addr:
- # detect our email address
- self.from_addr = '{}@{}'.format(
+ if self.user and self.host:
+ # Prepare the bases of our email
+ self.from_addr = [self.app_id, '{}@{}'.format(
re.split(r'[\s@]+', self.user)[0],
self.host,
- )
+ )]
+
+ if from_addr:
+ result = is_email(from_addr)
+ if result:
+ self.from_addr = (
+ result['name'] if result['name'] else False,
+ result['full_email'])
+ else:
+ self.from_addr[0] = from_addr
- result = is_email(self.from_addr)
+ result = is_email(self.from_addr[1])
if not result:
# Parse Source domain based on from_addr
- msg = 'Invalid ~From~ email specified: {}'.format(self.from_addr)
+ msg = 'Invalid ~From~ email specified: {}'.format(
+ '{} <{}>'.format(self.from_addr[0], self.from_addr[1])
+ if self.from_addr[0] else '{}'.format(self.from_addr[1]))
self.logger.warning(msg)
raise TypeError(msg)
- # Store our email address
- self.from_addr = result['full_email']
-
- # Set our from name
- self.from_name = from_name if from_name else result['name']
-
# Store our lookup
- self.names[self.from_addr] = \
- self.from_name if self.from_name else False
+ self.names[self.from_addr[1]] = self.from_addr[0]
# Now detect the SMTP Server
self.smtp_host = \
smtp_host if isinstance(smtp_host, str) else ''
# Now detect secure mode
- self.secure_mode = self.default_secure_mode \
- if not isinstance(secure_mode, str) \
- else secure_mode.lower()
+ if secure_mode:
+ self.secure_mode = None \
+ if not isinstance(secure_mode, str) \
+ else secure_mode.lower()
+ else:
+ self.secure_mode = SecureMailMode.INSECURE \
+ if not self.secure else self.template_args['mode']['default']
+
if self.secure_mode not in SECURE_MODES:
msg = 'The secure mode specified ({}) is invalid.'\
.format(secure_mode)
@@ -508,8 +540,7 @@ class NotifyEmail(NotifyBase):
else:
# If our target email list is empty we want to add ourselves to it
- self.targets.append(
- (self.from_name if self.from_name else False, self.from_addr))
+ self.targets.append((False, self.from_addr[1]))
# Validate recipients (cc:) and drop bad ones:
for recipient in parse_emails(cc):
@@ -562,6 +593,15 @@ class NotifyEmail(NotifyBase):
# Apply any defaults based on certain known configurations
self.NotifyEmailDefaults(secure_mode=secure_mode, **kwargs)
+ if not self.secure and self.secure_mode != SecureMailMode.INSECURE:
+ # Enable Secure mode if not otherwise set
+ self.secure = True
+
+ if not self.port:
+ # Assign our port based on our secure_mode if not otherwise
+ # detected
+ self.port = SECURE_MODES[self.secure_mode]['default_port']
+
# if there is still no smtp_host then we fall back to the hostname
if not self.smtp_host:
self.smtp_host = self.host
@@ -625,11 +665,11 @@ class NotifyEmail(NotifyBase):
if login_type:
# only apply additional logic to our user if a login_type
# was specified.
- if is_email(self.user) and \
- WebBaseLogin.EMAIL not in login_type:
- # Email specified but login type
- # not supported; switch it to user id
- self.user = match.group('id')
+ if is_email(self.user):
+ if WebBaseLogin.EMAIL not in login_type:
+ # Email specified but login type
+ # not supported; switch it to user id
+ self.user = match.group('id')
elif WebBaseLogin.USERID not in login_type:
# user specified but login type
@@ -656,18 +696,14 @@ class NotifyEmail(NotifyBase):
Perform Email Notification
"""
- # Initialize our default from name
- from_name = self.from_name if self.from_name else self.app_desc
-
- # error tracking (used for function return)
- has_error = False
-
if not self.targets:
# There is no one to email; we're done
self.logger.warning(
'There are no Email recipients to notify')
return False
+ messages: t.List[EmailMessage] = []
+
# Create a copy of the targets list
emails = list(self.targets)
while len(emails):
@@ -700,7 +736,9 @@ class NotifyEmail(NotifyBase):
for addr in reply_to]
self.logger.debug(
- 'Email From: {} <{}>'.format(from_name, self.from_addr))
+ 'Email From: {}'.format(
+ formataddr(self.from_addr, charset='utf-8')))
+
self.logger.debug('Email To: {}'.format(to_addr))
if cc:
self.logger.debug('Email Cc: {}'.format(', '.join(cc)))
@@ -763,9 +801,7 @@ class NotifyEmail(NotifyBase):
base[k] = Header(v, self._get_charset(v))
base['Subject'] = Header(title, self._get_charset(title))
- base['From'] = formataddr(
- (from_name if from_name else False, self.from_addr),
- charset='utf-8')
+ base['From'] = formataddr(self.from_addr, charset='utf-8')
base['To'] = formataddr((to_name, to_addr), charset='utf-8')
base['Message-ID'] = make_msgid(domain=self.smtp_host)
base['Date'] = \
@@ -778,58 +814,79 @@ class NotifyEmail(NotifyBase):
if reply_to:
base['Reply-To'] = ','.join(reply_to)
- # bind the socket variable to the current namespace
- socket = None
+ message = EmailMessage(
+ recipient=to_addr,
+ to_addrs=[to_addr] + list(cc) + list(bcc),
+ body=base.as_string())
+ messages.append(message)
- # Always call throttle before any remote server i/o is made
- self.throttle()
+ return self.submit(messages)
- try:
- self.logger.debug('Connecting to remote SMTP server...')
- socket_func = smtplib.SMTP
- if self.secure and self.secure_mode == SecureMailMode.SSL:
- self.logger.debug('Securing connection with SSL...')
- socket_func = smtplib.SMTP_SSL
+ def submit(self, messages: t.List[EmailMessage]):
- socket = socket_func(
- self.smtp_host,
- self.port,
- None,
- timeout=self.socket_connect_timeout,
- )
+ # error tracking (used for function return)
+ has_error = False
- if self.secure and self.secure_mode == SecureMailMode.STARTTLS:
- # Handle Secure Connections
- self.logger.debug('Securing connection with STARTTLS...')
- socket.starttls()
+ # bind the socket variable to the current namespace
+ socket = None
- if self.user and self.password:
- # Apply Login credetials
- self.logger.debug('Applying user credentials...')
- socket.login(self.user, self.password)
+ # Always call throttle before any remote server i/o is made
+ self.throttle()
- # Send the email
- socket.sendmail(
- self.from_addr,
- [to_addr] + list(cc) + list(bcc),
- base.as_string())
+ try:
+ self.logger.debug('Connecting to remote SMTP server...')
+ socket_func = smtplib.SMTP
+ if self.secure_mode == SecureMailMode.SSL:
+ self.logger.debug('Securing connection with SSL...')
+ socket_func = smtplib.SMTP_SSL
- self.logger.info(
- 'Sent Email notification to "{}".'.format(to_addr))
+ socket = socket_func(
+ self.smtp_host,
+ self.port,
+ None,
+ timeout=self.socket_connect_timeout,
+ )
- except (SocketError, smtplib.SMTPException, RuntimeError) as e:
- self.logger.warning(
- 'A Connection error occurred sending Email '
- 'notification to {}.'.format(self.smtp_host))
- self.logger.debug('Socket Exception: %s' % str(e))
+ if self.secure_mode == SecureMailMode.STARTTLS:
+ # Handle Secure Connections
+ self.logger.debug('Securing connection with STARTTLS...')
+ socket.starttls()
+
+ if self.user and self.password:
+ # Apply Login credetials
+ self.logger.debug('Applying user credentials...')
+ socket.login(self.user, self.password)
+
+ # Send the emails
+ for message in messages:
+ try:
+ socket.sendmail(
+ self.from_addr[1],
+ message.to_addrs,
+ message.body)
+
+ self.logger.info(
+ f'Sent Email notification to "{message.recipient}".')
+ except (SocketError, smtplib.SMTPException, RuntimeError) as e:
+ self.logger.warning(
+ f'Sending email to "{message.recipient}" failed. '
+ f'Reason: {e}')
+
+ # Mark as failure
+ has_error = True
+
+ except (SocketError, smtplib.SMTPException, RuntimeError) as e:
+ self.logger.warning(
+ f'Connection error while submitting email to {self.smtp_host}.'
+ f' Reason: {e}')
- # Mark our failure
- has_error = True
+ # Mark as failure
+ has_error = True
- finally:
- # Gracefully terminate the connection with the server
- if socket is not None: # pragma: no branch
- socket.quit()
+ finally:
+ # Gracefully terminate the connection with the server
+ if socket is not None: # pragma: no branch
+ socket.quit()
return not has_error
@@ -839,12 +896,7 @@ class NotifyEmail(NotifyBase):
"""
# Define an URL parameters
- params = {
- 'from': self.from_addr,
- 'mode': self.secure_mode,
- 'smtp': self.smtp_host,
- 'user': self.user,
- }
+ params = {}
# Append our headers into our parameters
params.update({'+{}'.format(k): v for k, v in self.headers.items()})
@@ -852,30 +904,60 @@ class NotifyEmail(NotifyBase):
# Extend our parameters
params.update(self.url_parameters(privacy=privacy, *args, **kwargs))
- if self.from_name:
- params['name'] = self.from_name
+ from_addr = None
+ if len(self.targets) == 1 and self.targets[0][1] != self.from_addr[1]:
+ # A custom email was provided
+ from_addr = self.from_addr[1]
+
+ if self.smtp_host != self.host:
+ # Apply our SMTP Host only if it differs from the provided hostname
+ params['smtp'] = self.smtp_host
+
+ if self.secure:
+ # Mode is only requried if we're dealing with a secure connection
+ params['mode'] = self.secure_mode
+
+ if self.from_addr[0] and self.from_addr[0] != self.app_id:
+ # A custom name was provided
+ params['from'] = self.from_addr[0] if not from_addr else \
+ formataddr((self.from_addr[0], from_addr), charset='utf-8')
+
+ elif from_addr:
+ params['from'] = formataddr((False, from_addr), charset='utf-8')
+
+ elif not self.user:
+ params['from'] = \
+ formataddr((False, self.from_addr[1]), charset='utf-8')
if len(self.cc) > 0:
# Handle our Carbon Copy Addresses
- params['cc'] = ','.join(
- ['{}{}'.format(
- '' if not e not in self.names
- else '{}:'.format(self.names[e]), e) for e in self.cc])
+ params['cc'] = ','.join([
+ formataddr(
+ (self.names[e] if e in self.names else False, e),
+ # Swap comma for it's escaped url code (if detected) since
+ # we're using that as a delimiter
+ charset='utf-8').replace(',', '%2C')
+ for e in self.cc])
if len(self.bcc) > 0:
# Handle our Blind Carbon Copy Addresses
- params['bcc'] = ','.join(
- ['{}{}'.format(
- '' if not e not in self.names
- else '{}:'.format(self.names[e]), e) for e in self.bcc])
+ params['bcc'] = ','.join([
+ formataddr(
+ (self.names[e] if e in self.names else False, e),
+ # Swap comma for it's escaped url code (if detected) since
+ # we're using that as a delimiter
+ charset='utf-8').replace(',', '%2C')
+ for e in self.bcc])
if self.reply_to:
# Handle our Reply-To Addresses
- params['reply'] = ','.join(
- ['{}{}'.format(
- '' if not e not in self.names
- else '{}:'.format(self.names[e]), e)
- for e in self.reply_to])
+ params['reply'] = ','.join([
+ formataddr(
+ (self.names[e] if e in self.names else False, e),
+ # Swap comma for it's escaped url code (if detected) since
+ # we're using that as a delimiter
+ charset='utf-8').replace(',', '%2C')
+ for e in self.reply_to])
# pull email suffix from username (if present)
user = None if not self.user else self.user.split('@')[0]
@@ -895,14 +977,13 @@ class NotifyEmail(NotifyBase):
)
# Default Port setup
- default_port = \
- self.default_secure_port if self.secure else self.default_port
+ default_port = SECURE_MODES[self.secure_mode]['default_port']
# a simple boolean check as to whether we display our target emails
# or not
has_targets = \
not (len(self.targets) == 1
- and self.targets[0][1] == self.from_addr)
+ and self.targets[0][1] == self.from_addr[1])
return '{schema}://{auth}{hostname}{port}/{targets}?{params}'.format(
schema=self.secure_protocol if self.secure else self.protocol,
@@ -918,6 +999,13 @@ class NotifyEmail(NotifyBase):
params=NotifyEmail.urlencode(params),
)
+ def __len__(self):
+ """
+ Returns the number of targets associated with this notification
+ """
+ targets = len(self.targets)
+ return targets if targets > 0 else 1
+
@staticmethod
def parse_url(url):
"""
@@ -946,14 +1034,24 @@ class NotifyEmail(NotifyBase):
if 'from' in results['qsd'] and len(results['qsd']['from']):
from_addr = NotifyEmail.unquote(results['qsd']['from'])
+ if 'name' in results['qsd'] and len(results['qsd']['name']):
+ # Depricate use of both `from=` and `name=` in the same url as
+ # they will be synomomus of one another in the future.
+ from_addr = formataddr(
+ (NotifyEmail.unquote(results['qsd']['name']), from_addr),
+ charset='utf-8')
+ logger.warning(
+ 'Email name= and from= are synonymous; '
+ 'use one or the other.')
+
+ elif 'name' in results['qsd'] and len(results['qsd']['name']):
+ # Extract from name to associate with from address
+ from_addr = NotifyEmail.unquote(results['qsd']['name'])
+
# Attempt to detect 'to' email address
if 'to' in results['qsd'] and len(results['qsd']['to']):
results['targets'].append(results['qsd']['to'])
- if 'name' in results['qsd'] and len(results['qsd']['name']):
- # Extract from name to associate with from address
- results['from_name'] = NotifyEmail.unquote(results['qsd']['name'])
-
# Store SMTP Host if specified
if 'smtp' in results['qsd'] and len(results['qsd']['smtp']):
# Extract the smtp server
diff --git a/libs/apprise/plugins/NotifyEmby.py b/libs/apprise/plugins/NotifyEmby.py
index d609149a9..23d4c6114 100644
--- a/libs/apprise/plugins/NotifyEmby.py
+++ b/libs/apprise/plugins/NotifyEmby.py
@@ -1,30 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-
-# For this plugin to work correct, the Emby server must be set up to allow
-# for remote connections.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
# Emby Docker configuration: https://hub.docker.com/r/emby/embyserver/
# Authentication: https://github.com/MediaBrowser/Emby/wiki/Authentication
@@ -117,7 +121,7 @@ class NotifyEmby(NotifyBase):
Initialize Emby Object
"""
- super(NotifyEmby, self).__init__(**kwargs)
+ super().__init__(**kwargs)
if self.secure:
self.schema = 'https'
diff --git a/libs/apprise/plugins/NotifyEnigma2.py b/libs/apprise/plugins/NotifyEnigma2.py
index f47b67180..10d581792 100644
--- a/libs/apprise/plugins/NotifyEnigma2.py
+++ b/libs/apprise/plugins/NotifyEnigma2.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
# Sources
# - https://dreambox.de/en/
@@ -155,7 +162,7 @@ class NotifyEnigma2(NotifyBase):
headers can be a dictionary of key/value pairs that you want to
additionally include as part of the server headers to post with
"""
- super(NotifyEnigma2, self).__init__(**kwargs)
+ super().__init__(**kwargs)
try:
self.timeout = int(timeout)
diff --git a/libs/apprise/plugins/NotifyFCM/__init__.py b/libs/apprise/plugins/NotifyFCM/__init__.py
index eb21028ea..d8857d340 100644
--- a/libs/apprise/plugins/NotifyFCM/__init__.py
+++ b/libs/apprise/plugins/NotifyFCM/__init__.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2021 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
# For this plugin to work correct, the FCM server must be set up to allow
# for remote connections.
@@ -218,7 +225,7 @@ class NotifyFCM(NotifyBase):
Initialize Firebase Cloud Messaging
"""
- super(NotifyFCM, self).__init__(**kwargs)
+ super().__init__(**kwargs)
if mode is None:
# Detect our mode
@@ -548,6 +555,12 @@ class NotifyFCM(NotifyBase):
params=NotifyFCM.urlencode(params),
)
+ def __len__(self):
+ """
+ Returns the number of targets associated with this notification
+ """
+ return len(self.targets)
+
@staticmethod
def parse_url(url):
"""
diff --git a/libs/apprise/plugins/NotifyFCM/color.py b/libs/apprise/plugins/NotifyFCM/color.py
index 1f096ab03..46d0f2a71 100644
--- a/libs/apprise/plugins/NotifyFCM/color.py
+++ b/libs/apprise/plugins/NotifyFCM/color.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2021 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
# New priorities are defined here:
# - https://firebase.google.com/docs/reference/fcm/rest/v1/\
diff --git a/libs/apprise/plugins/NotifyFCM/common.py b/libs/apprise/plugins/NotifyFCM/common.py
index 91bc2a0c5..0ec10eec6 100644
--- a/libs/apprise/plugins/NotifyFCM/common.py
+++ b/libs/apprise/plugins/NotifyFCM/common.py
@@ -1,27 +1,35 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2021 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
class FCMMode:
"""
Define the Firebase Cloud Messaging Modes
diff --git a/libs/apprise/plugins/NotifyFCM/oauth.py b/libs/apprise/plugins/NotifyFCM/oauth.py
index a948d2fe0..a76bc6987 100644
--- a/libs/apprise/plugins/NotifyFCM/oauth.py
+++ b/libs/apprise/plugins/NotifyFCM/oauth.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2021 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
#
# To generate a private key file for your service account:
#
diff --git a/libs/apprise/plugins/NotifyFCM/priority.py b/libs/apprise/plugins/NotifyFCM/priority.py
index b6652f278..81976cb63 100644
--- a/libs/apprise/plugins/NotifyFCM/priority.py
+++ b/libs/apprise/plugins/NotifyFCM/priority.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2021 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
# New priorities are defined here:
# - https://firebase.google.com/docs/reference/fcm/rest/v1/\
diff --git a/libs/apprise/plugins/NotifyFaast.py b/libs/apprise/plugins/NotifyFaast.py
index d34b4800d..3e55e1200 100644
--- a/libs/apprise/plugins/NotifyFaast.py
+++ b/libs/apprise/plugins/NotifyFaast.py
@@ -1,26 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CON
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
import requests
@@ -84,7 +92,7 @@ class NotifyFaast(NotifyBase):
"""
Initialize Faast Object
"""
- super(NotifyFaast, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# Store the Authentication Token
self.authtoken = validate_regex(authtoken)
diff --git a/libs/apprise/plugins/NotifyFlock.py b/libs/apprise/plugins/NotifyFlock.py
index 2c68cc1c6..60b337e82 100644
--- a/libs/apprise/plugins/NotifyFlock.py
+++ b/libs/apprise/plugins/NotifyFlock.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
# To use this plugin, you need to first access https://dev.flock.com/webhooks
# Specifically https://dev.flock.com/webhooks/incoming
@@ -145,7 +152,7 @@ class NotifyFlock(NotifyBase):
"""
Initialize Flock Object
"""
- super(NotifyFlock, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# Build ourselves a target list
self.targets = list()
@@ -327,6 +334,13 @@ class NotifyFlock(NotifyBase):
params=NotifyFlock.urlencode(params),
)
+ def __len__(self):
+ """
+ Returns the number of targets associated with this notification
+ """
+ targets = len(self.targets)
+ return targets if targets > 0 else 1
+
@staticmethod
def parse_url(url):
"""
diff --git a/libs/apprise/plugins/NotifyForm.py b/libs/apprise/plugins/NotifyForm.py
index 7ea4202c1..3ef8d21b4 100644
--- a/libs/apprise/plugins/NotifyForm.py
+++ b/libs/apprise/plugins/NotifyForm.py
@@ -1,28 +1,36 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2022 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+import re
import requests
from .NotifyBase import NotifyBase
@@ -32,13 +40,24 @@ from ..common import NotifyType
from ..AppriseLocale import gettext_lazy as _
+class FORMPayloadField:
+ """
+ Identifies the fields available in the FORM Payload
+ """
+ VERSION = 'version'
+ TITLE = 'title'
+ MESSAGE = 'message'
+ MESSAGETYPE = 'type'
+
+
# Defines the method to send the notification
METHODS = (
'POST',
'GET',
'DELETE',
'PUT',
- 'HEAD'
+ 'HEAD',
+ 'PATCH'
)
@@ -47,6 +66,27 @@ class NotifyForm(NotifyBase):
A wrapper for Form Notifications
"""
+ # Support
+ # - file*
+ # - file?
+ # - file*name
+ # - file?name
+ # - ?file
+ # - *file
+ # - file
+ # The code will convert the ? or * to the digit increments
+ __attach_as_re = re.compile(
+ r'((?P<match1>(?P<id1a>[a-z0-9_-]+)?'
+ r'(?P<wc1>[*?+$:.%]+)(?P<id1b>[a-z0-9_-]+))'
+ r'|(?P<match2>(?P<id2>[a-z0-9_-]+)(?P<wc2>[*?+$:.%]?)))',
+ re.IGNORECASE)
+
+ # Our count
+ attach_as_count = '{:02d}'
+
+ # the default attach_as value
+ attach_as_default = f'file{attach_as_count}'
+
# The default descriptive name associated with the Notification
service_name = 'Form'
@@ -66,6 +106,12 @@ class NotifyForm(NotifyBase):
# local anyway
request_rate_per_sec = 0
+ # Define the FORM version to place in all payloads
+ # Version: Major.Minor, Major is only updated if the entire schema is
+ # changed. If just adding new items (or removing old ones, only increment
+ # the Minor!
+ form_version = '1.0'
+
# Define object templates
templates = (
'{schema}://{host}',
@@ -111,6 +157,12 @@ class NotifyForm(NotifyBase):
'values': METHODS,
'default': METHODS[0],
},
+ 'attach-as': {
+ 'name': _('Attach File As'),
+ 'type': 'string',
+ 'default': 'file*',
+ 'map_to': 'attach_as',
+ },
})
# Define any kwargs we're using
@@ -123,9 +175,14 @@ class NotifyForm(NotifyBase):
'name': _('Payload Extras'),
'prefix': ':',
},
+ 'params': {
+ 'name': _('GET Params'),
+ 'prefix': '-',
+ },
}
- def __init__(self, headers=None, method=None, payload=None, **kwargs):
+ def __init__(self, headers=None, method=None, payload=None, params=None,
+ attach_as=None, **kwargs):
"""
Initialize Form Object
@@ -133,7 +190,7 @@ class NotifyForm(NotifyBase):
additionally include as part of the server headers to post with
"""
- super(NotifyForm, self).__init__(**kwargs)
+ super().__init__(**kwargs)
self.fullpath = kwargs.get('fullpath')
if not isinstance(self.fullpath, str):
@@ -147,15 +204,72 @@ class NotifyForm(NotifyBase):
self.logger.warning(msg)
raise TypeError(msg)
+ # Custom File Attachment Over-Ride Support
+ if not isinstance(attach_as, str):
+ # Default value
+ self.attach_as = self.attach_as_default
+ self.attach_multi_support = True
+
+ else:
+ result = self.__attach_as_re.match(attach_as.strip())
+ if not result:
+ msg = 'The attach-as specified ({}) is invalid.'.format(
+ attach_as)
+ self.logger.warning(msg)
+ raise TypeError(msg)
+
+ self.attach_as = ''
+ self.attach_multi_support = False
+ if result.group('match1'):
+ if result.group('id1a'):
+ self.attach_as += result.group('id1a')
+
+ self.attach_as += self.attach_as_count
+ self.attach_multi_support = True
+ self.attach_as += result.group('id1b')
+
+ else: # result.group('match2'):
+ self.attach_as += result.group('id2')
+ if result.group('wc2'):
+ self.attach_as += self.attach_as_count
+ self.attach_multi_support = True
+
+ # A payload map allows users to over-ride the default mapping if
+ # they're detected with the :overide=value. Normally this would
+ # create a new key and assign it the value specified. However
+ # if the key you specify is actually an internally mapped one,
+ # then a re-mapping takes place using the value
+ self.payload_map = {
+ FORMPayloadField.VERSION: FORMPayloadField.VERSION,
+ FORMPayloadField.TITLE: FORMPayloadField.TITLE,
+ FORMPayloadField.MESSAGE: FORMPayloadField.MESSAGE,
+ FORMPayloadField.MESSAGETYPE: FORMPayloadField.MESSAGETYPE,
+ }
+
+ self.params = {}
+ if params:
+ # Store our extra headers
+ self.params.update(params)
+
self.headers = {}
if headers:
# Store our extra headers
self.headers.update(headers)
+ self.payload_overrides = {}
self.payload_extras = {}
if payload:
# Store our extra payload entries
self.payload_extras.update(payload)
+ for key in list(self.payload_extras.keys()):
+ # Any values set in the payload to alter a system related one
+ # alters the system key. Hence :message=msg maps the 'message'
+ # variable that otherwise already contains the payload to be
+ # 'msg' instead (containing the payload)
+ if key in self.payload_map:
+ self.payload_map[key] = self.payload_extras[key]
+ self.payload_overrides[key] = self.payload_extras[key]
+ del self.payload_extras[key]
return
@@ -175,9 +289,18 @@ class NotifyForm(NotifyBase):
# Append our headers into our parameters
params.update({'+{}'.format(k): v for k, v in self.headers.items()})
+ # Append our GET params into our parameters
+ params.update({'-{}'.format(k): v for k, v in self.params.items()})
+
# Append our payload extra's into our parameters
params.update(
{':{}'.format(k): v for k, v in self.payload_extras.items()})
+ params.update(
+ {':{}'.format(k): v for k, v in self.payload_overrides.items()})
+
+ if self.attach_as != self.attach_as_default:
+ # Provide Attach-As extension details
+ params['attach-as'] = self.attach_as
# Determine Authentication
auth = ''
@@ -212,6 +335,7 @@ class NotifyForm(NotifyBase):
Perform Form Notification
"""
+ # Prepare HTTP Headers
headers = {
'User-Agent': self.app_id,
}
@@ -233,7 +357,8 @@ class NotifyForm(NotifyBase):
try:
files.append((
- 'file{:02d}'.format(no), (
+ self.attach_as.format(no)
+ if self.attach_multi_support else self.attach_as, (
attachment.name,
open(attachment.path, 'rb'),
attachment.mimetype)
@@ -246,22 +371,24 @@ class NotifyForm(NotifyBase):
self.logger.debug('I/O Exception: %s' % str(e))
return False
- finally:
- for file in files:
- # Ensure all files are closed
- if file[1][1]:
- file[1][1].close()
+ if not self.attach_multi_support and no > 1:
+ self.logger.warning(
+ 'Multiple attachments provided while '
+ 'form:// Multi-Attachment Support not enabled')
# prepare Form Object
- payload = {
- # Version: Major.Minor, Major is only updated if the entire
- # schema is changed. If just adding new items (or removing
- # old ones, only increment the Minor!
- 'version': '1.0',
- 'title': title,
- 'message': body,
- 'type': notify_type,
- }
+ payload = {}
+
+ for key, value in (
+ (FORMPayloadField.VERSION, self.form_version),
+ (FORMPayloadField.TITLE, title),
+ (FORMPayloadField.MESSAGE, body),
+ (FORMPayloadField.MESSAGETYPE, notify_type)):
+
+ if not self.payload_map[key]:
+ # Do not store element in payload response
+ continue
+ payload[self.payload_map[key]] = value
# Apply any/all payload over-rides defined
payload.update(self.payload_extras)
@@ -289,10 +416,14 @@ class NotifyForm(NotifyBase):
if self.method == 'GET':
method = requests.get
+ payload.update(self.params)
elif self.method == 'PUT':
method = requests.put
+ elif self.method == 'PATCH':
+ method = requests.patch
+
elif self.method == 'DELETE':
method = requests.delete
@@ -307,7 +438,7 @@ class NotifyForm(NotifyBase):
url,
files=None if not files else files,
data=payload if self.method != 'GET' else None,
- params=payload if self.method == 'GET' else None,
+ params=payload if self.method == 'GET' else self.params,
headers=headers,
auth=auth,
verify=self.verify_certificate,
@@ -377,6 +508,16 @@ class NotifyForm(NotifyBase):
results['headers'] = {NotifyForm.unquote(x): NotifyForm.unquote(y)
for x, y in results['qsd+'].items()}
+ # Add our GET paramters in the event the user wants to pass these along
+ results['params'] = {NotifyForm.unquote(x): NotifyForm.unquote(y)
+ for x, y in results['qsd-'].items()}
+
+ # Allow Attach-As Support which over-rides the name of the filename
+ # posted with the form://
+ # the default is file01, file02, file03, etc
+ if 'attach-as' in results['qsd'] and len(results['qsd']['attach-as']):
+ results['attach_as'] = results['qsd']['attach-as']
+
# Set method if not otherwise set
if 'method' in results['qsd'] and len(results['qsd']['method']):
results['method'] = NotifyForm.unquote(results['qsd']['method'])
diff --git a/libs/apprise/plugins/NotifyGitter.py b/libs/apprise/plugins/NotifyGitter.py
index 577959836..805d69c8c 100644
--- a/libs/apprise/plugins/NotifyGitter.py
+++ b/libs/apprise/plugins/NotifyGitter.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
# Once you visit: https://developer.gitter.im/apps you'll get a personal
# access token that will look something like this:
@@ -137,7 +144,7 @@ class NotifyGitter(NotifyBase):
"""
Initialize Gitter Object
"""
- super(NotifyGitter, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# Secret Key (associated with project)
self.token = validate_regex(
@@ -382,6 +389,12 @@ class NotifyGitter(NotifyBase):
[NotifyGitter.quote(x, safe='') for x in self.targets]),
params=NotifyGitter.urlencode(params))
+ def __len__(self):
+ """
+ Returns the number of targets associated with this notification
+ """
+ return len(self.targets)
+
@staticmethod
def parse_url(url):
"""
diff --git a/libs/apprise/plugins/NotifyGnome.py b/libs/apprise/plugins/NotifyGnome.py
index b7bffd2c1..9476c78a3 100644
--- a/libs/apprise/plugins/NotifyGnome.py
+++ b/libs/apprise/plugins/NotifyGnome.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
from __future__ import absolute_import
from __future__ import print_function
@@ -54,8 +61,8 @@ except (ImportError, ValueError, AttributeError):
# be in microsoft windows, or we just don't have the python-gobject
# library available to us (or maybe one we don't support)?
- # Alternativey A ValueError will get thrown upon calling
- # gi.require_version() if the requested Notify namespace isn't available
+ # Alternatively, a `ValueError` will get raised upon calling
+ # gi.require_version() if the requested Notify namespace isn't available.
pass
@@ -164,7 +171,7 @@ class NotifyGnome(NotifyBase):
Initialize Gnome Object
"""
- super(NotifyGnome, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# The urgency of the message
self.urgency = int(
@@ -175,12 +182,9 @@ class NotifyGnome(NotifyBase):
if str(urgency).lower().startswith(k)),
NotifyGnome.template_args['urgency']['default']))
- # Track whether or not we want to send an image with our notification
- # or not.
+ # Track whether we want to add an image to the notification.
self.include_image = include_image
- return
-
def send(self, body, title='', notify_type=NotifyType.INFO, **kwargs):
"""
Perform Gnome Notification
@@ -214,15 +218,15 @@ class NotifyGnome(NotifyBase):
except Exception as e:
self.logger.warning(
- "Could not load Gnome notification icon ({}): {}"
- .format(icon_path, e))
+ "Could not load notification icon (%s).", icon_path)
+ self.logger.debug(f'Gnome Exception: {e}')
notification.show()
self.logger.info('Sent Gnome notification.')
- except Exception:
+ except Exception as e:
self.logger.warning('Failed to send Gnome notification.')
- self.logger.exception('Gnome Exception')
+ self.logger.debug(f'Gnome Exception: {e}')
return False
return True
diff --git a/libs/apprise/plugins/NotifyGoogleChat.py b/libs/apprise/plugins/NotifyGoogleChat.py
index 2cba98405..f65b6541e 100644
--- a/libs/apprise/plugins/NotifyGoogleChat.py
+++ b/libs/apprise/plugins/NotifyGoogleChat.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2021 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
# For this to work correctly you need to create a webhook. You'll also
# need a GSuite account (there are free trials if you don't have one)
@@ -80,8 +87,7 @@ class NotifyGoogleChat(NotifyBase):
setup_url = 'https://github.com/caronc/apprise/wiki/Notify_googlechat'
# Google Chat Webhook
- notify_url = 'https://chat.googleapis.com/v1/spaces/{workspace}/messages' \
- '?key={key}&token={token}'
+ notify_url = 'https://chat.googleapis.com/v1/spaces/{workspace}/messages'
# Default Notify Format
notify_format = NotifyFormat.MARKDOWN
@@ -96,6 +102,7 @@ class NotifyGoogleChat(NotifyBase):
# Define object templates
templates = (
'{schema}://{workspace}/{webhook_key}/{webhook_token}',
+ '{schema}://{workspace}/{webhook_key}/{webhook_token}/{thread_key}',
)
# Define our template tokens
@@ -118,6 +125,11 @@ class NotifyGoogleChat(NotifyBase):
'private': True,
'required': True,
},
+ 'thread_key': {
+ 'name': _('Thread Key'),
+ 'type': 'string',
+ 'private': True,
+ },
})
# Define our template arguments
@@ -131,14 +143,18 @@ class NotifyGoogleChat(NotifyBase):
'token': {
'alias_of': 'webhook_token',
},
+ 'thread': {
+ 'alias_of': 'thread_key',
+ },
})
- def __init__(self, workspace, webhook_key, webhook_token, **kwargs):
+ def __init__(self, workspace, webhook_key, webhook_token,
+ thread_key=None, **kwargs):
"""
Initialize Google Chat Object
"""
- super(NotifyGoogleChat, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# Workspace (associated with project)
self.workspace = validate_regex(workspace)
@@ -164,6 +180,16 @@ class NotifyGoogleChat(NotifyBase):
self.logger.warning(msg)
raise TypeError(msg)
+ if thread_key:
+ self.thread_key = validate_regex(thread_key)
+ if not self.thread_key:
+ msg = 'An invalid Google Chat Thread Key ' \
+ '({}) was specified.'.format(thread_key)
+ self.logger.warning(msg)
+ raise TypeError(msg)
+ else:
+ self.thread_key = None
+
return
def send(self, body, title='', notify_type=NotifyType.INFO, **kwargs):
@@ -185,13 +211,21 @@ class NotifyGoogleChat(NotifyBase):
# Construct Notify URL
notify_url = self.notify_url.format(
workspace=self.workspace,
- key=self.webhook_key,
- token=self.webhook_token,
)
+ params = {
+ # Prepare our URL Parameters
+ 'token': self.webhook_token,
+ 'key': self.webhook_key,
+ }
+
+ if self.thread_key:
+ params['threadKey'] = self.thread_key
+
self.logger.debug('Google Chat POST URL: %s (cert_verify=%r)' % (
notify_url, self.verify_certificate,
))
+ self.logger.debug('Google Chat Parameters: %s' % str(params))
self.logger.debug('Google Chat Payload: %s' % str(payload))
# Always call throttle before any remote server i/o is made
@@ -199,6 +233,7 @@ class NotifyGoogleChat(NotifyBase):
try:
r = requests.post(
notify_url,
+ params=params,
data=dumps(payload),
headers=headers,
verify=self.verify_certificate,
@@ -242,11 +277,13 @@ class NotifyGoogleChat(NotifyBase):
# Set our parameters
params = self.url_parameters(privacy=privacy, *args, **kwargs)
- return '{schema}://{workspace}/{key}/{token}/?{params}'.format(
+ return '{schema}://{workspace}/{key}/{token}/{thread}?{params}'.format(
schema=self.secure_protocol,
workspace=self.pprint(self.workspace, privacy, safe=''),
key=self.pprint(self.webhook_key, privacy, safe=''),
token=self.pprint(self.webhook_token, privacy, safe=''),
+ thread='' if not self.thread_key
+ else self.pprint(self.thread_key, privacy, safe=''),
params=NotifyGoogleChat.urlencode(params),
)
@@ -258,6 +295,7 @@ class NotifyGoogleChat(NotifyBase):
Syntax:
gchat://workspace/webhook_key/webhook_token
+ gchat://workspace/webhook_key/webhook_token/thread_key
"""
results = NotifyBase.parse_url(url, verify_host=False)
@@ -277,6 +315,9 @@ class NotifyGoogleChat(NotifyBase):
# Store our Webhook Token
results['webhook_token'] = tokens.pop(0) if tokens else None
+ # Store our Thread Key
+ results['thread_key'] = tokens.pop(0) if tokens else None
+
# Support arguments as overrides (if specified)
if 'workspace' in results['qsd']:
results['workspace'] = \
@@ -290,6 +331,17 @@ class NotifyGoogleChat(NotifyBase):
results['webhook_token'] = \
NotifyGoogleChat.unquote(results['qsd']['token'])
+ if 'thread' in results['qsd']:
+ results['thread_key'] = \
+ NotifyGoogleChat.unquote(results['qsd']['thread'])
+
+ elif 'threadkey' in results['qsd']:
+ # Support Google Chat's Thread Key (if set)
+ # keys are always made lowercase; so check above is attually
+ # testing threadKey successfully as well
+ results['thread_key'] = \
+ NotifyGoogleChat.unquote(results['qsd']['threadkey'])
+
return results
@staticmethod
@@ -298,6 +350,8 @@ class NotifyGoogleChat(NotifyBase):
Support
https://chat.googleapis.com/v1/spaces/{workspace}/messages
'?key={key}&token={token}
+ https://chat.googleapis.com/v1/spaces/{workspace}/messages
+ '?key={key}&token={token}&threadKey={thread}
"""
result = re.match(
diff --git a/libs/apprise/plugins/NotifyGotify.py b/libs/apprise/plugins/NotifyGotify.py
index 077289230..379225681 100644
--- a/libs/apprise/plugins/NotifyGotify.py
+++ b/libs/apprise/plugins/NotifyGotify.py
@@ -1,30 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-
-# For this plugin to work correct, the Gotify server must be set up to allow
-# for remote connections.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
# Gotify Docker configuration: https://hub.docker.com/r/gotify/server
# Example: https://github.com/gotify/server/blob/\
@@ -155,7 +159,7 @@ class NotifyGotify(NotifyBase):
Initialize Gotify Object
"""
- super(NotifyGotify, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# Token (associated with project)
self.token = validate_regex(token)
diff --git a/libs/apprise/plugins/NotifyGrowl.py b/libs/apprise/plugins/NotifyGrowl.py
index 921c48529..9240d62c5 100644
--- a/libs/apprise/plugins/NotifyGrowl.py
+++ b/libs/apprise/plugins/NotifyGrowl.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2020 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
from .NotifyBase import NotifyBase
from ..URLBase import PrivacyMode
@@ -187,7 +194,7 @@ class NotifyGrowl(NotifyBase):
"""
Initialize Growl Object
"""
- super(NotifyGrowl, self).__init__(**kwargs)
+ super().__init__(**kwargs)
if not self.port:
self.port = self.default_port
diff --git a/libs/apprise/plugins/NotifyGuilded.py b/libs/apprise/plugins/NotifyGuilded.py
index 603bd9fea..8bb9aeeaa 100644
--- a/libs/apprise/plugins/NotifyGuilded.py
+++ b/libs/apprise/plugins/NotifyGuilded.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
# For this to work correctly you need to create a webhook. To do this just
# click on the little gear icon next to the channel you're part of. From
diff --git a/libs/apprise/plugins/NotifyHomeAssistant.py b/libs/apprise/plugins/NotifyHomeAssistant.py
index bb49462fc..a403356ab 100644
--- a/libs/apprise/plugins/NotifyHomeAssistant.py
+++ b/libs/apprise/plugins/NotifyHomeAssistant.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2021 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
# You must generate a "Long-Lived Access Token". This can be done from your
# Home Assistant Profile page.
@@ -115,7 +122,7 @@ class NotifyHomeAssistant(NotifyBase):
"""
Initialize Home Assistant Object
"""
- super(NotifyHomeAssistant, self).__init__(**kwargs)
+ super().__init__(**kwargs)
self.fullpath = kwargs.get('fullpath', '')
diff --git a/libs/apprise/plugins/NotifyIFTTT.py b/libs/apprise/plugins/NotifyIFTTT.py
index b735a4d07..04c6911ef 100644
--- a/libs/apprise/plugins/NotifyIFTTT.py
+++ b/libs/apprise/plugins/NotifyIFTTT.py
@@ -1,29 +1,35 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# IFTTT (If-This-Then-That)
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# This code is licensed under the MIT License.
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
#
# For this plugin to work, you need to add the Maker applet to your profile
# Simply visit https://ifttt.com/search and search for 'Webhooks'
@@ -147,7 +153,7 @@ class NotifyIFTTT(NotifyBase):
reference to Value1, Value2, and/or Value3
"""
- super(NotifyIFTTT, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# Webhook ID (associated with project)
self.webhook_id = validate_regex(webhook_id)
@@ -306,6 +312,12 @@ class NotifyIFTTT(NotifyBase):
params=NotifyIFTTT.urlencode(params),
)
+ def __len__(self):
+ """
+ Returns the number of targets associated with this notification
+ """
+ return len(self.events)
+
@staticmethod
def parse_url(url):
"""
diff --git a/libs/apprise/plugins/NotifyJSON.py b/libs/apprise/plugins/NotifyJSON.py
index c9ce1edc1..f1a9cc04e 100644
--- a/libs/apprise/plugins/NotifyJSON.py
+++ b/libs/apprise/plugins/NotifyJSON.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
import requests
import base64
@@ -34,13 +41,25 @@ from ..common import NotifyType
from ..AppriseLocale import gettext_lazy as _
+class JSONPayloadField:
+ """
+ Identifies the fields available in the JSON Payload
+ """
+ VERSION = 'version'
+ TITLE = 'title'
+ MESSAGE = 'message'
+ ATTACHMENTS = 'attachments'
+ MESSAGETYPE = 'type'
+
+
# Defines the method to send the notification
METHODS = (
'POST',
'GET',
'DELETE',
'PUT',
- 'HEAD'
+ 'HEAD',
+ 'PATCH'
)
@@ -68,6 +87,12 @@ class NotifyJSON(NotifyBase):
# local anyway
request_rate_per_sec = 0
+ # Define the JSON version to place in all payloads
+ # Version: Major.Minor, Major is only updated if the entire schema is
+ # changed. If just adding new items (or removing old ones, only increment
+ # the Minor!
+ json_version = '1.0'
+
# Define object templates
templates = (
'{schema}://{host}',
@@ -125,9 +150,14 @@ class NotifyJSON(NotifyBase):
'name': _('Payload Extras'),
'prefix': ':',
},
+ 'params': {
+ 'name': _('GET Params'),
+ 'prefix': '-',
+ },
}
- def __init__(self, headers=None, method=None, payload=None, **kwargs):
+ def __init__(self, headers=None, method=None, payload=None, params=None,
+ **kwargs):
"""
Initialize JSON Object
@@ -135,7 +165,7 @@ class NotifyJSON(NotifyBase):
additionally include as part of the server headers to post with
"""
- super(NotifyJSON, self).__init__(**kwargs)
+ super().__init__(**kwargs)
self.fullpath = kwargs.get('fullpath')
if not isinstance(self.fullpath, str):
@@ -149,15 +179,44 @@ class NotifyJSON(NotifyBase):
self.logger.warning(msg)
raise TypeError(msg)
+ # A payload map allows users to over-ride the default mapping if
+ # they're detected with the :overide=value. Normally this would
+ # create a new key and assign it the value specified. However
+ # if the key you specify is actually an internally mapped one,
+ # then a re-mapping takes place using the value
+ self.payload_map = {
+ JSONPayloadField.VERSION: JSONPayloadField.VERSION,
+ JSONPayloadField.TITLE: JSONPayloadField.TITLE,
+ JSONPayloadField.MESSAGE: JSONPayloadField.MESSAGE,
+ JSONPayloadField.ATTACHMENTS: JSONPayloadField.ATTACHMENTS,
+ JSONPayloadField.MESSAGETYPE: JSONPayloadField.MESSAGETYPE,
+ }
+
+ self.params = {}
+ if params:
+ # Store our extra headers
+ self.params.update(params)
+
self.headers = {}
if headers:
# Store our extra headers
self.headers.update(headers)
+ self.payload_overrides = {}
self.payload_extras = {}
if payload:
# Store our extra payload entries
self.payload_extras.update(payload)
+ for key in list(self.payload_extras.keys()):
+ # Any values set in the payload to alter a system related one
+ # alters the system key. Hence :message=msg maps the 'message'
+ # variable that otherwise already contains the payload to be
+ # 'msg' instead (containing the payload)
+ if key in self.payload_map:
+ self.payload_map[key] = self.payload_extras[key].strip()
+ self.payload_overrides[key] = \
+ self.payload_extras[key].strip()
+ del self.payload_extras[key]
return
@@ -177,9 +236,14 @@ class NotifyJSON(NotifyBase):
# Append our headers into our parameters
params.update({'+{}'.format(k): v for k, v in self.headers.items()})
+ # Append our GET params into our parameters
+ params.update({'-{}'.format(k): v for k, v in self.params.items()})
+
# Append our payload extra's into our parameters
params.update(
{':{}'.format(k): v for k, v in self.payload_extras.items()})
+ params.update(
+ {':{}'.format(k): v for k, v in self.payload_overrides.items()})
# Determine Authentication
auth = ''
@@ -214,6 +278,7 @@ class NotifyJSON(NotifyBase):
Perform JSON Notification
"""
+ # Prepare HTTP Headers
headers = {
'User-Agent': self.app_id,
'Content-Type': 'application/json'
@@ -253,16 +318,18 @@ class NotifyJSON(NotifyBase):
return False
# prepare JSON Object
- payload = {
- # Version: Major.Minor, Major is only updated if the entire
- # schema is changed. If just adding new items (or removing
- # old ones, only increment the Minor!
- 'version': '1.0',
- 'title': title,
- 'message': body,
- 'attachments': attachments,
- 'type': notify_type,
- }
+ payload = {}
+ for key, value in (
+ (JSONPayloadField.VERSION, self.json_version),
+ (JSONPayloadField.TITLE, title),
+ (JSONPayloadField.MESSAGE, body),
+ (JSONPayloadField.ATTACHMENTS, attachments),
+ (JSONPayloadField.MESSAGETYPE, notify_type)):
+
+ if not self.payload_map[key]:
+ # Do not store element in payload response
+ continue
+ payload[self.payload_map[key]] = value
# Apply any/all payload over-rides defined
payload.update(self.payload_extras)
@@ -294,6 +361,9 @@ class NotifyJSON(NotifyBase):
elif self.method == 'PUT':
method = requests.put
+ elif self.method == 'PATCH':
+ method = requests.patch
+
elif self.method == 'DELETE':
method = requests.delete
@@ -307,6 +377,7 @@ class NotifyJSON(NotifyBase):
r = method(
url,
data=dumps(payload),
+ params=self.params,
headers=headers,
auth=auth,
verify=self.verify_certificate,
@@ -364,6 +435,10 @@ class NotifyJSON(NotifyBase):
results['headers'] = {NotifyJSON.unquote(x): NotifyJSON.unquote(y)
for x, y in results['qsd+'].items()}
+ # Add our GET paramters in the event the user wants to pass these along
+ results['params'] = {NotifyJSON.unquote(x): NotifyJSON.unquote(y)
+ for x, y in results['qsd-'].items()}
+
# Set method if not otherwise set
if 'method' in results['qsd'] and len(results['qsd']['method']):
results['method'] = NotifyJSON.unquote(results['qsd']['method'])
diff --git a/libs/apprise/plugins/NotifyJoin.py b/libs/apprise/plugins/NotifyJoin.py
index 9ac2f244b..e6210a5f3 100644
--- a/libs/apprise/plugins/NotifyJoin.py
+++ b/libs/apprise/plugins/NotifyJoin.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
# Join URL: http://joaoapps.com/join/
# To use this plugin, you need to first access (make sure your browser allows
@@ -194,7 +201,7 @@ class NotifyJoin(NotifyBase):
"""
Initialize Join Object
"""
- super(NotifyJoin, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# Track whether or not we want to send an image with our notification
# or not.
@@ -366,6 +373,12 @@ class NotifyJoin(NotifyBase):
for x in self.targets]),
params=NotifyJoin.urlencode(params))
+ def __len__(self):
+ """
+ Returns the number of targets associated with this notification
+ """
+ return len(self.targets)
+
@staticmethod
def parse_url(url):
"""
diff --git a/libs/apprise/plugins/NotifyKavenegar.py b/libs/apprise/plugins/NotifyKavenegar.py
index 97c69366e..8905e2431 100644
--- a/libs/apprise/plugins/NotifyKavenegar.py
+++ b/libs/apprise/plugins/NotifyKavenegar.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2020 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
# To use this service you will need a Kavenegar account from their website
# at https://kavenegar.com/
@@ -149,7 +156,7 @@ class NotifyKavenegar(NotifyBase):
"""
Initialize Kavenegar Object
"""
- super(NotifyKavenegar, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# API Key (associated with project)
self.apikey = validate_regex(
@@ -317,6 +324,12 @@ class NotifyKavenegar(NotifyBase):
[NotifyKavenegar.quote(x, safe='') for x in self.targets]),
params=NotifyKavenegar.urlencode(params))
+ def __len__(self):
+ """
+ Returns the number of targets associated with this notification
+ """
+ return len(self.targets)
+
@staticmethod
def parse_url(url):
"""
diff --git a/libs/apprise/plugins/NotifyKumulos.py b/libs/apprise/plugins/NotifyKumulos.py
index 8506aef3d..27e0995c9 100644
--- a/libs/apprise/plugins/NotifyKumulos.py
+++ b/libs/apprise/plugins/NotifyKumulos.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
# To use this plugin, you must have a Kumulos account set up. Add a client
# and link it with your phone using the phone app (using your Companion App
@@ -104,7 +111,7 @@ class NotifyKumulos(NotifyBase):
"""
Initialize Kumulos Object
"""
- super(NotifyKumulos, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# API Key (associated with project)
self.apikey = validate_regex(
diff --git a/libs/apprise/plugins/NotifyLametric.py b/libs/apprise/plugins/NotifyLametric.py
index 1c4eaa040..1b98b6946 100644
--- a/libs/apprise/plugins/NotifyLametric.py
+++ b/libs/apprise/plugins/NotifyLametric.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2020 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
# For LaMetric to work, you need to first setup a custom application on their
# website. it can be done as follows:
@@ -467,7 +474,7 @@ class NotifyLametric(NotifyBase):
"""
Initialize LaMetric Object
"""
- super(NotifyLametric, self).__init__(**kwargs)
+ super().__init__(**kwargs)
self.mode = mode.strip().lower() \
if isinstance(mode, str) \
diff --git a/libs/apprise/plugins/NotifyLine.py b/libs/apprise/plugins/NotifyLine.py
index 7cb660978..817a998c8 100644
--- a/libs/apprise/plugins/NotifyLine.py
+++ b/libs/apprise/plugins/NotifyLine.py
@@ -1,27 +1,35 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2022 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
#
# API Docs: https://developers.line.biz/en/reference/messaging-api/
@@ -114,7 +122,7 @@ class NotifyLine(NotifyBase):
"""
Initialize Line Object
"""
- super(NotifyLine, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# Long-Lived Access token (generated from User Profile)
self.token = validate_regex(token)
@@ -259,6 +267,12 @@ class NotifyLine(NotifyBase):
params=NotifyLine.urlencode(params),
)
+ def __len__(self):
+ """
+ Returns the number of targets associated with this notification
+ """
+ return len(self.targets)
+
@staticmethod
def parse_url(url):
"""
diff --git a/libs/apprise/plugins/NotifyMQTT.py b/libs/apprise/plugins/NotifyMQTT.py
index 9ea1e44b2..c8ee7cbce 100644
--- a/libs/apprise/plugins/NotifyMQTT.py
+++ b/libs/apprise/plugins/NotifyMQTT.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2021 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
# PAHO MQTT Documentation:
# https://www.eclipse.org/paho/index.php?page=clients/python/docs/index.php
@@ -132,20 +139,6 @@ class NotifyMQTT(NotifyBase):
# through their network flow at once.
mqtt_inflight_messages = 200
- # 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",
- ]
-
# Define object templates
templates = (
'{schema}://{user}@{host}/{topic}',
@@ -223,7 +216,7 @@ class NotifyMQTT(NotifyBase):
Initialize MQTT Object
"""
- super(NotifyMQTT, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# Initialize topics
self.topics = parse_list(targets)
@@ -264,6 +257,9 @@ class NotifyMQTT(NotifyBase):
self.ca_certs = None
if self.secure:
# verify SSL key or abort
+ # TODO: There is no error reporting or aborting here?
+ # It could be useful to inform the user _where_ Apprise
+ # tried to find the root CA certificates file.
self.ca_certs = next(
(cert for cert in self.CA_CERTIFICATE_FILE_LOCATIONS
if isfile(cert)), None)
@@ -316,9 +312,9 @@ class NotifyMQTT(NotifyBase):
if self.secure:
if self.ca_certs is None:
- self.logger.warning(
- 'MQTT Secure comunication can not be verified; '
- 'no local CA certificate file')
+ self.logger.error(
+ 'MQTT secure communication can not be verified, '
+ 'CA certificates file missing')
return False
self.client.tls_set(
@@ -328,7 +324,7 @@ class NotifyMQTT(NotifyBase):
ciphers=None)
# Set our TLS Verify Flag
- self.client.tls_insecure_set(self.verify_certificate)
+ self.client.tls_insecure_set(not self.verify_certificate)
# Establish our connection
if self.client.connect(
@@ -480,6 +476,12 @@ class NotifyMQTT(NotifyBase):
params=NotifyMQTT.urlencode(params),
)
+ def __len__(self):
+ """
+ Returns the number of targets associated with this notification
+ """
+ return len(self.topics)
+
@staticmethod
def parse_url(url):
"""
@@ -528,3 +530,38 @@ class NotifyMQTT(NotifyBase):
# return results
return results
+
+ @property
+ def CA_CERTIFICATE_FILE_LOCATIONS(self):
+ """
+ Return possible locations to root certificate authority (CA) bundles.
+
+ Taken from https://golang.org/src/crypto/x509/root_linux.go
+ TODO: Maybe refactor to a general utility function?
+ """
+ candidates = [
+ # 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",
+ # macOS Homebrew; brew install ca-certificates
+ "/usr/local/etc/ca-certificates/cert.pem",
+ ]
+
+ # Certifi provides Mozilla’s carefully curated collection of Root
+ # Certificates for validating the trustworthiness of SSL certificates
+ # while verifying the identity of TLS hosts. It has been extracted from
+ # the Requests project.
+ try:
+ import certifi
+ candidates.append(certifi.where())
+ except ImportError: # pragma: no cover
+ pass
+
+ return candidates
diff --git a/libs/apprise/plugins/NotifyMSG91.py b/libs/apprise/plugins/NotifyMSG91.py
index acbfa8d42..75834c399 100644
--- a/libs/apprise/plugins/NotifyMSG91.py
+++ b/libs/apprise/plugins/NotifyMSG91.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
# Create an account https://msg91.com/ if you don't already have one
#
@@ -156,7 +163,7 @@ class NotifyMSG91(NotifyBase):
"""
Initialize MSG91 Object
"""
- super(NotifyMSG91, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# Authentication Key (associated with project)
self.authkey = validate_regex(
@@ -322,6 +329,12 @@ class NotifyMSG91(NotifyBase):
[NotifyMSG91.quote(x, safe='') for x in self.targets]),
params=NotifyMSG91.urlencode(params))
+ def __len__(self):
+ """
+ Returns the number of targets associated with this notification
+ """
+ return len(self.targets)
+
@staticmethod
def parse_url(url):
"""
diff --git a/libs/apprise/plugins/NotifyMSTeams.py b/libs/apprise/plugins/NotifyMSTeams.py
index 65631037c..19f9fe34f 100644
--- a/libs/apprise/plugins/NotifyMSTeams.py
+++ b/libs/apprise/plugins/NotifyMSTeams.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2020 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
# To use this plugin, you need to create a webhook; you can read more about
# this here:
@@ -214,7 +221,7 @@ class NotifyMSTeams(NotifyBase):
template arguments that can not be over-ridden are:
`body`, `title`, and `type`.
"""
- super(NotifyMSTeams, self).__init__(**kwargs)
+ super().__init__(**kwargs)
try:
self.version = int(version)
diff --git a/libs/apprise/plugins/NotifyMacOSX.py b/libs/apprise/plugins/NotifyMacOSX.py
index dfd8080f6..59c0620a3 100644
--- a/libs/apprise/plugins/NotifyMacOSX.py
+++ b/libs/apprise/plugins/NotifyMacOSX.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2020 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
from __future__ import absolute_import
from __future__ import print_function
@@ -39,13 +46,15 @@ from ..AppriseLocale import gettext_lazy as _
# Default our global support flag
NOTIFY_MACOSX_SUPPORT_ENABLED = False
+
+# TODO: The module will be easier to test without module-level code.
if platform.system() == 'Darwin':
# Check this is Mac OS X 10.8, or higher
major, minor = platform.mac_ver()[0].split('.')[:2]
- # Toggle our enabled flag if verion is correct and executable
+ # Toggle our enabled flag, if version is correct and executable
# found. This is done in such a way to provide verbosity to the
- # end user so they know why it may or may not work for them.
+ # end user, so they know why it may or may not work for them.
NOTIFY_MACOSX_SUPPORT_ENABLED = \
(int(major) > 10 or (int(major) == 10 and int(minor) >= 8))
@@ -95,6 +104,8 @@ class NotifyMacOSX(NotifyBase):
notify_paths = (
'/opt/homebrew/bin/terminal-notifier',
'/usr/local/bin/terminal-notifier',
+ '/usr/bin/terminal-notifier',
+ '/bin/terminal-notifier',
)
# Define object templates
@@ -117,26 +128,32 @@ class NotifyMacOSX(NotifyBase):
'name': _('Sound'),
'type': 'string',
},
+ 'click': {
+ 'name': _('Open/Click URL'),
+ 'type': 'string',
+ },
})
- def __init__(self, sound=None, include_image=True, **kwargs):
+ def __init__(self, sound=None, include_image=True, click=None, **kwargs):
"""
Initialize MacOSX Object
"""
- super(NotifyMacOSX, self).__init__(**kwargs)
+ super().__init__(**kwargs)
- # Track whether or not we want to send an image with our notification
- # or not.
+ # Track whether we want to add an image to the notification.
self.include_image = include_image
- # Acquire the notify path
+ # Acquire the path to the `terminal-notifier` program.
self.notify_path = next( # pragma: no branch
(p for p in self.notify_paths if os.access(p, os.X_OK)), None)
+ # Click URL
+ # Allow user to provide the `--open` argument on the notify wrapper
+ self.click = click
+
# Set sound object (no q/a for now)
self.sound = sound
- return
def send(self, body, title='', notify_type=NotifyType.INFO, **kwargs):
"""
@@ -160,6 +177,9 @@ class NotifyMacOSX(NotifyBase):
if title:
cmd.extend(['-title', title])
+ if self.click:
+ cmd.extend(['-open', self.click])
+
# The sound to play
if self.sound:
cmd.extend(['-sound', self.sound])
@@ -201,6 +221,9 @@ class NotifyMacOSX(NotifyBase):
'image': 'yes' if self.include_image else 'no',
}
+ if self.click:
+ params['click'] = self.click
+
# Extend our parameters
params.update(self.url_parameters(privacy=privacy, *args, **kwargs))
@@ -228,6 +251,10 @@ class NotifyMacOSX(NotifyBase):
results['include_image'] = \
parse_bool(results['qsd'].get('image', True))
+ # Support 'click'
+ if 'click' in results['qsd'] and len(results['qsd']['click']):
+ results['click'] = NotifyMacOSX.unquote(results['qsd']['click'])
+
# Support 'sound'
if 'sound' in results['qsd'] and len(results['qsd']['sound']):
results['sound'] = NotifyMacOSX.unquote(results['qsd']['sound'])
diff --git a/libs/apprise/plugins/NotifyMailgun.py b/libs/apprise/plugins/NotifyMailgun.py
index 855d65e1c..3139e3416 100644
--- a/libs/apprise/plugins/NotifyMailgun.py
+++ b/libs/apprise/plugins/NotifyMailgun.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
# Signup @ https://www.mailgun.com/
#
@@ -60,6 +67,7 @@ from ..utils import parse_emails
from ..utils import parse_bool
from ..utils import is_email
from ..utils import validate_regex
+from ..logger import logger
from ..AppriseLocale import gettext_lazy as _
# Provide some known codes Mailgun uses and what they translate to:
@@ -116,9 +124,6 @@ class NotifyMailgun(NotifyBase):
# Default Notify Format
notify_format = NotifyFormat.HTML
- # The default region to use if one isn't otherwise specified
- mailgun_default_region = MailgunRegion.US
-
# The maximum amount of emails that can reside within a single
# batch transfer
default_batch_size = 2000
@@ -158,7 +163,10 @@ class NotifyMailgun(NotifyBase):
'name': {
'name': _('From Name'),
'type': 'string',
- 'map_to': 'from_name',
+ 'map_to': 'from_addr',
+ },
+ 'from': {
+ 'alias_of': 'name',
},
'region': {
'name': _('Region Name'),
@@ -197,13 +205,13 @@ class NotifyMailgun(NotifyBase):
},
}
- def __init__(self, apikey, targets, cc=None, bcc=None, from_name=None,
+ def __init__(self, apikey, targets, cc=None, bcc=None, from_addr=None,
region_name=None, headers=None, tokens=None, batch=False,
**kwargs):
"""
Initialize Mailgun Object
"""
- super(NotifyMailgun, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# API Key (associated with project)
self.apikey = validate_regex(apikey)
@@ -246,7 +254,8 @@ class NotifyMailgun(NotifyBase):
# Store our region
try:
- self.region_name = self.mailgun_default_region \
+ self.region_name = \
+ NotifyMailgun.template_args['region']['default'] \
if region_name is None else region_name.lower()
if self.region_name not in MAILGUN_REGIONS:
@@ -260,12 +269,20 @@ class NotifyMailgun(NotifyBase):
raise TypeError(msg)
# Get our From username (if specified)
- self.from_name = from_name
-
- # Get our from email address
- self.from_addr = '{user}@{host}'.format(user=self.user, host=self.host)
-
- if not is_email(self.from_addr):
+ self.from_addr = [
+ self.app_id, '{user}@{host}'.format(
+ user=self.user, host=self.host)]
+
+ if from_addr:
+ result = is_email(from_addr)
+ if result:
+ self.from_addr = (
+ result['name'] if result['name'] else False,
+ result['full_email'])
+ else:
+ self.from_addr[0] = from_addr
+
+ if not is_email(self.from_addr[1]):
# Parse Source domain based on from_addr
msg = 'Invalid ~From~ email format: {}'.format(self.from_addr)
self.logger.warning(msg)
@@ -288,8 +305,7 @@ class NotifyMailgun(NotifyBase):
else:
# If our target email list is empty we want to add ourselves to it
- self.targets.append(
- (self.from_name if self.from_name else False, self.from_addr))
+ self.targets.append((False, self.from_addr[1]))
# Validate recipients (cc:) and drop bad ones:
for recipient in parse_emails(cc):
@@ -383,9 +399,7 @@ class NotifyMailgun(NotifyBase):
return False
- reply_to = formataddr(
- (self.from_name if self.from_name else False,
- self.from_addr), charset='utf-8')
+ reply_to = formataddr(self.from_addr, charset='utf-8')
# Prepare our payload
payload = {
@@ -581,9 +595,9 @@ class NotifyMailgun(NotifyBase):
# Extend our parameters
params.update(self.url_parameters(privacy=privacy, *args, **kwargs))
- if self.from_name is not None:
- # from_name specified; pass it back on the url
- params['name'] = self.from_name
+ if self.from_addr[0]:
+ # from_addr specified; pass it back on the url
+ params['name'] = self.from_addr[0]
if self.cc:
# Handle our Carbon Copy Addresses
@@ -613,6 +627,20 @@ class NotifyMailgun(NotifyBase):
safe='') for e in self.targets]),
params=NotifyMailgun.urlencode(params))
+ def __len__(self):
+ """
+ Returns the number of targets associated with this notification
+ """
+ #
+ # Factor batch into calculation
+ #
+ batch_size = 1 if not self.batch else self.default_batch_size
+ targets = len(self.targets)
+ if batch_size > 1:
+ targets = int(targets / batch_size) + \
+ (1 if targets % batch_size else 0)
+ return targets if targets > 0 else 1
+
@staticmethod
def parse_url(url):
"""
@@ -637,13 +665,28 @@ class NotifyMailgun(NotifyBase):
# We're done - no API Key found
results['apikey'] = None
- if 'name' in results['qsd'] and len(results['qsd']['name']):
+ # Attempt to detect 'from' email address
+ if 'from' in results['qsd'] and len(results['qsd']['from']):
+ results['from_addr'] = \
+ NotifyMailgun.unquote(results['qsd']['from'])
+
+ if 'name' in results['qsd'] and len(results['qsd']['name']):
+ # Depricate use of both `from=` and `name=` in the same url as
+ # they will be synomomus of one another in the future.
+ results['from_addr'] = formataddr(
+ (NotifyMailgun.unquote(results['qsd']['name']),
+ results['from_addr']), charset='utf-8')
+ logger.warning(
+ 'Mailgun name= and from= are synonymous; '
+ 'use one or the other.')
+
+ elif 'name' in results['qsd'] and len(results['qsd']['name']):
# Extract from name to associate with from address
- results['from_name'] = \
+ results['from_addr'] = \
NotifyMailgun.unquote(results['qsd']['name'])
if 'region' in results['qsd'] and len(results['qsd']['region']):
- # Extract from name to associate with from address
+ # Acquire region if defined
results['region_name'] = \
NotifyMailgun.unquote(results['qsd']['region'])
diff --git a/libs/apprise/plugins/NotifyMastodon.py b/libs/apprise/plugins/NotifyMastodon.py
new file mode 100644
index 000000000..74d13952a
--- /dev/null
+++ b/libs/apprise/plugins/NotifyMastodon.py
@@ -0,0 +1,990 @@
+# -*- coding: utf-8 -*-
+# BSD 3-Clause License
+#
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+#
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+import re
+import requests
+from copy import deepcopy
+from json import dumps, loads
+from datetime import datetime
+
+from .NotifyBase import NotifyBase
+from ..URLBase import PrivacyMode
+from ..common import NotifyImageSize
+from ..common import NotifyFormat
+from ..common import NotifyType
+from ..utils import parse_list
+from ..utils import parse_bool
+from ..utils import validate_regex
+from ..AppriseLocale import gettext_lazy as _
+from ..attachment.AttachBase import AttachBase
+
+# Accept:
+# - @username
+# - username
+IS_USER = re.compile(
+ r'^\s*@?(?P<user>[A-Z0-9_]+(?:@(?P<host>[A-Z0-9_.-]+))?)$', re.I)
+
+USER_DETECTION_RE = re.compile(
+ r'(@[A-Z0-9_]+(?:@[A-Z0-9_.-]+)?)(?=$|[\s,.&()\[\]]+)', re.I)
+
+
+class MastodonMessageVisibility:
+ """
+ The visibility of any status message made
+ """
+ # post visibility defaults to the accounts default-visibilty setting
+ DEFAULT = 'default'
+
+ # post will be visible only to mentioned users
+ # similar to a Twitter DM
+ DIRECT = 'direct'
+
+ # post will be visible only to followers
+ PRIVATE = 'private'
+
+ # post will be public but not appear on the public timeline
+ UNLISTED = 'unlisted'
+
+ # post will be public
+ PUBLIC = 'public'
+
+
+# Define the types in a list for validation purposes
+MASTODON_MESSAGE_VISIBILITIES = (
+ MastodonMessageVisibility.DEFAULT,
+ MastodonMessageVisibility.DIRECT,
+ MastodonMessageVisibility.PRIVATE,
+ MastodonMessageVisibility.UNLISTED,
+ MastodonMessageVisibility.PUBLIC,
+)
+
+
+class NotifyMastodon(NotifyBase):
+ """
+ A wrapper for Notify Mastodon Notifications
+ """
+
+ # The default descriptive name associated with the Notification
+ service_name = 'Mastodon'
+
+ # The services URL
+ service_url = 'https://joinmastodon.org'
+
+ # The default protocol
+ protocol = ('mastodon', 'toot')
+
+ # The default secure protocol
+ secure_protocol = ('mastodons', 'toots')
+
+ # A URL that takes you to the setup/help of the specific protocol
+ setup_url = 'https://github.com/caronc/apprise/wiki/Notify_mastodon'
+
+ # Allows the user to specify the NotifyImageSize object; this is supported
+ # through the webhook
+ image_size = NotifyImageSize.XY_128
+
+ # it is documented on the site that the maximum images per toot
+ # is 4 (unless it's a GIF, then it's only 1)
+ __toot_non_gif_images_batch = 4
+
+ # Mastodon API Reference To Acquire Current Users Information
+ # See: https://docs.joinmastodon.org/methods/accounts/
+ # Requires Scope Element: read:accounts
+ mastodon_whoami = '/api/v1/accounts/verify_credentials'
+
+ # URL for posting media files
+ mastodon_media = '/api/v1/media'
+
+ # URL for posting status messages
+ mastodon_toot = '/api/v1/statuses'
+
+ # URL for posting direct messages
+ mastodon_dm = '/api/v1/dm'
+
+ # The title is not used
+ title_maxlen = 0
+
+ # The maximum size of the message
+ body_maxlen = 500
+
+ # Default to text
+ notify_format = NotifyFormat.TEXT
+
+ # Mastodon is kind enough to return how many more requests we're allowed to
+ # continue to make within it's header response as:
+ # X-Rate-Limit-Reset: The epoc time (in seconds) we can expect our
+ # rate-limit to be reset.
+ # X-Rate-Limit-Remaining: an integer identifying how many requests we're
+ # still allow to make.
+ request_rate_per_sec = 0
+
+ # For Tracking Purposes
+ ratelimit_reset = datetime.utcnow()
+
+ # Default to 1000; users can send up to 1000 DM's and 2400 toot a day
+ # This value only get's adjusted if the server sets it that way
+ ratelimit_remaining = 1
+
+ # Define object templates
+ templates = (
+ '{schema}://{token}@{host}',
+ '{schema}://{token}@{host}:{port}',
+ '{schema}://{token}@{host}/{targets}',
+ '{schema}://{token}@{host}:{port}/{targets}',
+ )
+
+ # Define our template arguments
+ template_tokens = dict(NotifyBase.template_tokens, **{
+ 'host': {
+ 'name': _('Hostname'),
+ 'type': 'string',
+ 'required': True,
+ },
+ 'token': {
+ 'name': _('Access Token'),
+ 'type': 'string',
+ 'required': True,
+ },
+ 'port': {
+ 'name': _('Port'),
+ 'type': 'int',
+ 'min': 1,
+ 'max': 65535,
+ },
+ 'target_user': {
+ 'name': _('Target User'),
+ 'type': 'string',
+ 'prefix': '@',
+ 'map_to': 'targets',
+ },
+ 'targets': {
+ 'name': _('Targets'),
+ 'type': 'list:string',
+ },
+ })
+
+ # Define our template arguments
+ template_args = dict(NotifyBase.template_args, **{
+ 'token': {
+ 'alias_of': 'token',
+ },
+ 'visibility': {
+ 'name': _('Visibility'),
+ 'type': 'choice:string',
+ 'values': MASTODON_MESSAGE_VISIBILITIES,
+ 'default': MastodonMessageVisibility.DEFAULT,
+ },
+ 'cache': {
+ 'name': _('Cache Results'),
+ 'type': 'bool',
+ 'default': True,
+ },
+ 'batch': {
+ 'name': _('Batch Mode'),
+ 'type': 'bool',
+ 'default': True,
+ },
+ 'sensitive': {
+ 'name': _('Sensitive Attachments'),
+ 'type': 'bool',
+ 'default': False,
+ },
+ 'spoiler': {
+ 'name': _('Spoiler Text'),
+ 'type': 'string',
+ },
+ 'key': {
+ 'name': _('Idempotency-Key'),
+ 'type': 'string',
+ },
+ 'language': {
+ 'name': _('Language Code'),
+ 'type': 'string',
+ },
+ 'to': {
+ 'alias_of': 'targets',
+ },
+ })
+
+ def __init__(self, token=None, targets=None, batch=True,
+ sensitive=None, spoiler=None, visibility=None, cache=True,
+ key=None, language=None, **kwargs):
+ """
+ Initialize Notify Mastodon Object
+ """
+ super().__init__(**kwargs)
+
+ # Set our schema
+ self.schema = 'https' if self.secure else 'http'
+
+ # Initialize our cache value
+ self._whoami_cache = None
+
+ self.token = validate_regex(token)
+ if not self.token:
+ msg = 'An invalid Mastodon Access Token was specified.'
+ self.logger.warning(msg)
+ raise TypeError(msg)
+
+ if visibility:
+ # Input is a string; attempt to get the lookup from our
+ # sound mapping
+ vis = 'invalid' if not isinstance(visibility, str) \
+ else visibility.lower().strip()
+
+ # This little bit of black magic allows us to match against
+ # against multiple versions of the same string
+ # ... etc
+ self.visibility = \
+ next((v for v in MASTODON_MESSAGE_VISIBILITIES
+ if v.startswith(vis)), None)
+
+ if self.visibility not in MASTODON_MESSAGE_VISIBILITIES:
+ msg = 'The Mastodon visibility specified ({}) is invalid.' \
+ .format(visibility)
+ self.logger.warning(msg)
+ raise TypeError(msg)
+
+ else:
+ self.visibility = \
+ self.template_args['visibility']['default']
+
+ # Prepare our URL
+ self.api_url = '%s://%s' % (self.schema, self.host)
+
+ if isinstance(self.port, int):
+ self.api_url += ':%d' % self.port
+
+ # Set Cache Flag
+ self.cache = cache
+
+ # Prepare Image Batch Mode Flag
+ self.batch = self.template_args['batch']['default'] \
+ if batch is None else batch
+
+ # Images to be marked sensitive
+ self.sensitive = self.template_args['sensitive']['default'] \
+ if sensitive is None else sensitive
+
+ # Text marked as being a spoiler
+ self.spoiler = spoiler if isinstance(spoiler, str) else None
+
+ # Idempotency Key
+ self.idempotency_key = key if isinstance(key, str) else None
+
+ # Over-ride default language (ISO 639) (e.g: en, fr, es, etc)
+ self.language = language if isinstance(language, str) else None
+
+ # Our target users
+ self.targets = []
+
+ # Track any errors
+ has_error = False
+
+ # Identify our targets
+ for target in parse_list(targets):
+ match = IS_USER.match(target)
+ if match and match.group('user'):
+ self.targets.append('@' + match.group('user'))
+ continue
+
+ has_error = True
+ self.logger.warning(
+ 'Dropped invalid Mastodon user ({}) specified.'.format(target),
+ )
+
+ if has_error and not self.targets:
+ # We have specified that we want to notify one or more individual
+ # and we failed to load any of them. Since it's also valid to
+ # notify no one at all (which means we notify ourselves), it's
+ # important we don't switch from the users original intentions
+ msg = 'No Mastodon targets to notify.'
+ self.logger.warning(msg)
+ raise TypeError(msg)
+
+ return
+
+ def url(self, privacy=False, *args, **kwargs):
+ """
+ Returns the URL built dynamically based on specified arguments.
+ """
+
+ # Define any URL parameters
+ params = {
+ 'visibility': self.visibility,
+ 'batch': 'yes' if self.batch else 'no',
+ 'sensitive': 'yes' if self.sensitive else 'no',
+ }
+
+ # Extend our parameters
+ params.update(self.url_parameters(privacy=privacy, *args, **kwargs))
+
+ if self.spoiler:
+ # Our Spoiler if one was specified
+ params['spoiler'] = self.spoiler
+
+ if self.idempotency_key:
+ # Our Idempotency Key
+ params['key'] = self.idempotency_key
+
+ if self.language:
+ # Override Language
+ params['language'] = self.language
+
+ default_port = 443 if self.secure else 80
+
+ return '{schema}://{token}@{host}{port}/{targets}?{params}'.format(
+ schema=self.secure_protocol[0]
+ if self.secure else self.protocol[0],
+ token=self.pprint(
+ self.token, privacy, mode=PrivacyMode.Secret, safe=''),
+ # never encode hostname since we're expecting it to be a valid one
+ host=self.host,
+ port='' if self.port is None or self.port == default_port
+ else ':{}'.format(self.port),
+ targets='/'.join(
+ [NotifyMastodon.quote(x, safe='@') for x in self.targets]),
+ params=NotifyMastodon.urlencode(params),
+ )
+
+ def __len__(self):
+ """
+ Returns the number of targets associated with this notification
+ """
+ targets = len(self.targets)
+ return targets if targets > 0 else 1
+
+ def send(self, body, title='', notify_type=NotifyType.INFO, attach=None,
+ **kwargs):
+ """
+ wrapper to _send since we can alert more then one channel
+ """
+
+ # Build a list of our attachments
+ attachments = []
+
+ # Smart Target Detection for Direct Messages; this prevents us from
+ # adding @user entries that were already placed in the message body
+ users = set(USER_DETECTION_RE.findall(body))
+ targets = users - set(self.targets.copy())
+ if not self.targets and self.visibility == \
+ MastodonMessageVisibility.DIRECT:
+
+ result = self._whoami()
+ if not result:
+ # Could not access our status
+ return False
+
+ myself = '@' + next(iter(result.keys()))
+ if myself in users:
+ targets.remove(myself)
+
+ else:
+ targets.add(myself)
+
+ if attach:
+ # We need to upload our payload first so that we can source it
+ # in remaining messages
+ for attachment in attach:
+
+ # Perform some simple error checking
+ if not attachment:
+ # We could not access the attachment
+ self.logger.error(
+ 'Could not access attachment {}.'.format(
+ attachment.url(privacy=True)))
+ return False
+
+ #
+ # Images (PNG, JPG, GIF) up to 8MB.
+ # - Images will be downscaled to 1.6 megapixels (enough for a
+ # 1280x1280 image).
+ # - Up to 4 images can be attached.
+ # - Animated GIFs are converted to soundless MP4s like on
+ # Imgur/Gfycat (GIFV).
+ # - You can also upload soundless MP4 and WebM, which will
+ # be handled the same way.
+ # Videos (MP4, M4V, MOV, WebM) up to 40MB.
+ # - Video will be transcoded to H.264 MP4 with a maximum
+ # bitrate of 1300kbps and framerate of 60fps.
+ # Audio (MP3, OGG, WAV, FLAC, OPUS, AAC, M4A, 3GP) up to 40MB.
+ # - Audio will be transcoded to MP3 using V2 VBR (roughly
+ # 192kbps).
+ if not re.match(r'^(image|video|audio)/.*',
+ attachment.mimetype, re.I):
+ # Only support images at this time
+ self.logger.warning(
+ 'Ignoring unsupported Mastodon attachment {}.'.format(
+ attachment.url(privacy=True)))
+ continue
+
+ self.logger.debug(
+ 'Preparing Mastodon attachment {}'.format(
+ attachment.url(privacy=True)))
+
+ # Upload our image and get our id associated with it
+ postokay, response = self._request(
+ self.mastodon_media,
+ payload=attachment,
+ )
+
+ if not postokay:
+ # We can't post our attachment
+ if response and 'authorized scopes' \
+ in response.get('error', ''):
+ self.logger.warning(
+ 'Failed to Send Attachment to Mastodon: '
+ 'missing scope: write:media')
+
+ # All other failures should cause us to abort
+ return False
+
+ if not (isinstance(response, dict)
+ and response.get('id')):
+ self.logger.debug(
+ 'Could not attach the file to Mastodon: %s (mime=%s)',
+ attachment.name, attachment.mimetype)
+ continue
+
+ # If we get here, our output will look something like this:
+ # {
+ # 'id': '12345',
+ # 'type': 'image',
+ # 'url': 'https://.../6dad4663a.jpeg',
+ # 'preview_url': 'https://.../adde6dad4663a.jpeg',
+ # 'remote_url': None,
+ # 'preview_remote_url': None,
+ # 'text_url': None,
+ # 'meta': {
+ # 'original': {
+ # 'width': 640,
+ # 'height': 640,
+ # 'size': '640x640',
+ # 'aspect': 1.0
+ # },
+ # 'small': {
+ # 'width': 400,
+ # 'height': 400,
+ # 'size': '400x400',
+ # 'aspect': 1.0
+ # }
+ # },
+ # 'description': None,
+ # 'blurhash': 'UmIsdJnT^mX4V@XQofnQ~Ebq%4o3ofnQjZbt'
+ # }
+ response.update({
+ # Update our response to additionally include the
+ # attachment details
+ 'file_name': attachment.name,
+ 'file_mime': attachment.mimetype,
+ 'file_path': attachment.path,
+ })
+
+ # Save our pre-prepared payload for attachment posting
+ attachments.append(response)
+
+ payload = {
+ 'status': '{} {}'.format(' '.join(targets), body)
+ if targets else body,
+ 'sensitive': self.sensitive,
+ }
+
+ # Handle Visibility Flag
+ if self.visibility != MastodonMessageVisibility.DEFAULT:
+ payload['visibility'] = self.visibility
+
+ # Set Spoiler text (if set)
+ if self.spoiler:
+ payload['spoiler_text'] = self.spoiler
+
+ # Set Idempotency-Key (if set)
+ if self.idempotency_key:
+ payload['Idempotency-Key'] = self.idempotency_key
+
+ # Set Language
+ if self.language:
+ payload['language'] = self.language
+
+ payloads = []
+ if not attachments:
+ payloads.append(payload)
+
+ else:
+ # Group our images if batch is set to do so
+ batch_size = 1 if not self.batch \
+ else self.__toot_non_gif_images_batch
+
+ # Track our batch control in our message generation
+ batches = []
+ batch = []
+ for attachment in attachments:
+ batch.append(attachment['id'])
+ # Mastodon supports batching images together. This allows
+ # the batching of multiple images together. Mastodon also
+ # makes it clear that you can't batch `gif` files; they need
+ # to be separate. So the below preserves the ordering that
+ # a user passed their attachments in. if 4-non-gif images
+ # are passed, they are all part of a single message.
+ #
+ # however, if they pass in image, gif, image, gif. The
+ # gif's inbetween break apart the batches so this would
+ # produce 4 separate toots.
+ #
+ # If you passed in, image, image, gif, image. <- This would
+ # produce 3 images (as the first 2 images could be lumped
+ # together as a batch)
+ if not re.match(
+ r'^image/(png|jpe?g)', attachment['file_mime'], re.I) \
+ or len(batch) >= batch_size:
+ batches.append(batch)
+ batch = []
+
+ if batch:
+ batches.append(batch)
+
+ for no, media_ids in enumerate(batches):
+ _payload = deepcopy(payload)
+ _payload['media_ids'] = media_ids
+
+ if no:
+ # strip text and replace it with the image representation
+ _payload['status'] = \
+ '{:02d}/{:02d}'.format(no + 1, len(batches))
+ # No longer sensitive information
+ _payload['sensitive'] = False
+ if self.idempotency_key:
+ # Support multiposts while a Idempotency Key has been
+ # defined
+ _payload['Idempotency-Key'] = '{}-part{:02d}'.format(
+ self.idempotency_key, no)
+ payloads.append(_payload)
+
+ # Error Tracking
+ has_error = False
+
+ for no, payload in enumerate(payloads, start=1):
+ postokay, response = self._request(self.mastodon_toot, payload)
+ if not postokay:
+ # Track our error
+ has_error = True
+
+ # We can't post our attachment
+ if response and 'authorized scopes' \
+ in response.get('error', ''):
+ self.logger.warning(
+ 'Failed to Send Status to Mastodon: '
+ 'missing scope: write:statuses')
+
+ continue
+
+ # Example Attachment Output:
+ # {
+ # "id":"109315796435904505",
+ # "created_at":"2022-11-09T20:44:39.017Z",
+ # "in_reply_to_id":null,
+ # "in_reply_to_account_id":null,
+ # "sensitive":false,
+ # "spoiler_text":"",
+ # "visibility":"public",
+ # "language":"en",
+ # "uri":"https://host/users/caronc/statuses/109315796435904505",
+ # "url":"https://host/@caronc/109315796435904505",
+ # "replies_count":0,
+ # "reblogs_count":0,
+ # "favourites_count":0,
+ # "edited_at":null,
+ # "favourited":false,
+ # "reblogged":false,
+ # "muted":false,
+ # "bookmarked":false,
+ # "pinned":false,
+ # "content":"<p>test</p>",
+ # "reblog":null,
+ # "application":{
+ # "name":"Apprise Notifications",
+ # "website":"https://github.com/caronc/apprise"
+ # },
+ # "account":{
+ # "id":"109310334138718878",
+ # "username":"caronc",
+ # "acct":"caronc",
+ # "display_name":"Chris",
+ # "locked":false,
+ # "bot":false,
+ # "discoverable":false,
+ # "group":false,
+ # "created_at":"2022-11-08T00:00:00.000Z",
+ # "note":"content",
+ # "url":"https://host/@caronc",
+ # "avatar":"https://host/path/file.png",
+ # "avatar_static":"https://host/path/file.png",
+ # "header":"https://host/headers/original/missing.png",
+ # "header_static":"https://host/path/missing.png",
+ # "followers_count":0,
+ # "following_count":0,
+ # "statuses_count":15,
+ # "last_status_at":"2022-11-09",
+ # "emojis":[
+ #
+ # ],
+ # "fields":[
+ #
+ # ]
+ # },
+ # "media_attachments":[
+ # {
+ # "id":"109315796405707501",
+ # "type":"image",
+ # "url":"https://host/path/file.jpeg",
+ # "preview_url":"https://host/path/file.jpeg",
+ # "remote_url":null,
+ # "preview_remote_url":null,
+ # "text_url":null,
+ # "meta":{
+ # "original":{
+ # "width":640,
+ # "height":640,
+ # "size":"640x640",
+ # "aspect":1.0
+ # },
+ # "small":{
+ # "width":400,
+ # "height":400,
+ # "size":"400x400",
+ # "aspect":1.0
+ # }
+ # },
+ # "description":null,
+ # "blurhash":"UmIsdJnT^mX4V@XQofnQ~Ebq%4o3ofnQjZbt"
+ # }
+ # ],
+ # "mentions":[
+ #
+ # ],
+ # "tags":[
+ #
+ # ],
+ # "emojis":[
+ #
+ # ],
+ # "card":null,
+ # "poll":null
+ # }
+
+ try:
+ url = '{}/web/@{}'.format(
+ self.api_url,
+ response['account']['username'])
+
+ except (KeyError, TypeError):
+ url = 'unknown'
+
+ self.logger.debug(
+ 'Mastodon [%.2d/%.2d] (%d attached) delivered to %s',
+ no, len(payloads), len(payload.get('media_ids', [])), url)
+
+ self.logger.info(
+ 'Sent [%.2d/%.2d] Mastodon notification as public toot.',
+ no, len(payloads))
+
+ return not has_error
+
+ def _whoami(self, lazy=True):
+ """
+ Looks details of current authenticated user
+
+ """
+
+ if lazy and self._whoami_cache is not None:
+ # Use cached response
+ return self._whoami_cache
+
+ # Send Mastodon Whoami request
+ postokay, response = self._request(
+ self.mastodon_whoami,
+ method='GET',
+ )
+
+ if postokay:
+ # Sample Response:
+ # {
+ # 'id': '12345',
+ # 'username': 'caronc',
+ # 'acct': 'caronc',
+ # 'display_name': 'Chris',
+ # 'locked': False,
+ # 'bot': False,
+ # 'discoverable': False,
+ # 'group': False,
+ # 'created_at': '2022-11-08T00:00:00.000Z',
+ # 'note': 'details',
+ # 'url': 'https://noc.social/@caronc',
+ # 'avatar': 'https://host/path/image.png',
+ # 'avatar_static': 'https://host/path/image.png',
+ # 'header': 'https://host/path/missing.png',
+ # 'header_static': 'https://host/path/missing.png',
+ # 'followers_count': 0,
+ # 'following_count': 0,
+ # 'statuses_count': 2,
+ # 'last_status_at': '2022-11-09',
+ # 'source': {
+ # 'privacy': 'public',
+ # 'sensitive': False,
+ # 'language': None,
+ # 'note': 'details',
+ # 'fields': [],
+ # 'follow_requests_count': 0
+ # },
+ # 'emojis': [],
+ # 'fields': []
+ # }
+ try:
+ # Cache our response for future references
+ self._whoami_cache = {
+ response['username']: response['id']}
+
+ except (TypeError, KeyError):
+ pass
+
+ elif response and 'authorized scopes' in response.get('error', ''):
+ self.logger.warning(
+ 'Failed to lookup Mastodon Auth details; '
+ 'missing scope: read:accounts')
+
+ return self._whoami_cache if postokay else {}
+
+ def _request(self, path, payload=None, method='POST'):
+ """
+ Wrapper to Mastodon API requests object
+ """
+
+ headers = {
+ 'User-Agent': self.app_id,
+ 'Authorization': f'Bearer {self.token}',
+ }
+
+ data = None
+ files = None
+
+ # Prepare our message
+ url = '{}{}'.format(self.api_url, path)
+
+ # Some Debug Logging
+ self.logger.debug('Mastodon {} URL: {} (cert_verify={})'.format(
+ method, url, self.verify_certificate))
+
+ # Open our attachment path if required:
+ if isinstance(payload, AttachBase):
+ # prepare payload
+ files = {
+ 'file': (payload.name, open(payload.path, 'rb'),
+ 'application/octet-stream')}
+
+ # Provide a description
+ data = {
+ 'description': payload.name,
+ }
+
+ else:
+ headers['Content-Type'] = 'application/json'
+ data = dumps(payload)
+ self.logger.debug('Mastodon Payload: %s' % str(payload))
+
+ # Default content response object
+ content = {}
+
+ # By default set wait to None
+ wait = None
+
+ if self.ratelimit_remaining == 0:
+ # Determine how long we should wait for or if we should wait at
+ # all. This isn't fool-proof because we can't be sure the client
+ # time (calling this script) is completely synced up with the
+ # Mastodon server. One would hope we're on NTP and our clocks are
+ # the same allowing this to role smoothly:
+
+ now = datetime.utcnow()
+ if now < self.ratelimit_reset:
+ # We need to throttle for the difference in seconds
+ # We add 0.5 seconds to the end just to allow a grace
+ # period.
+ wait = (self.ratelimit_reset - now).total_seconds() + 0.5
+
+ # Always call throttle before any remote server i/o is made;
+ self.throttle(wait=wait)
+
+ # acquire our request mode
+ fn = requests.post if method == 'POST' else requests.get
+
+ try:
+ r = fn(
+ url,
+ data=data,
+ files=files,
+ headers=headers,
+ verify=self.verify_certificate,
+ timeout=self.request_timeout,
+ )
+
+ try:
+ content = loads(r.content)
+
+ except (AttributeError, TypeError, ValueError):
+ # ValueError = r.content is Unparsable
+ # TypeError = r.content is None
+ # AttributeError = r is None
+ content = {}
+
+ if r.status_code not in (
+ requests.codes.ok, requests.codes.created,
+ requests.codes.accepted):
+
+ # We had a problem
+ status_str = \
+ NotifyMastodon.http_response_code_lookup(r.status_code)
+
+ self.logger.warning(
+ 'Failed to send Mastodon {} to {}: '
+ '{}error={}.'.format(
+ method,
+ url,
+ ', ' if status_str else '',
+ r.status_code))
+
+ self.logger.debug(
+ 'Response Details:\r\n{}'.format(r.content))
+
+ # Mark our failure
+ return (False, content)
+
+ try:
+ # Capture rate limiting if possible
+ self.ratelimit_remaining = \
+ int(r.headers.get('X-RateLimit-Remaining'))
+ self.ratelimit_reset = datetime.utcfromtimestamp(
+ int(r.headers.get('X-RateLimit-Limit')))
+
+ except (TypeError, ValueError):
+ # This is returned if we could not retrieve this information
+ # gracefully accept this state and move on
+ pass
+
+ except requests.RequestException as e:
+ self.logger.warning(
+ 'Exception received when sending Mastodon {} to {}: '.
+ format(method, url))
+ self.logger.debug('Socket Exception: %s' % str(e))
+
+ # Mark our failure
+ return (False, content)
+
+ except (OSError, IOError) as e:
+ self.logger.warning(
+ 'An I/O error occurred while handling {}.'.format(
+ payload.name if isinstance(payload, AttachBase)
+ else payload))
+ self.logger.debug('I/O Exception: %s' % str(e))
+ return (False, content)
+
+ finally:
+ # Close our file (if it's open) stored in the second element
+ # of our files tuple (index 1)
+ if files:
+ files['file'][1].close()
+
+ return (True, content)
+
+ @staticmethod
+ def parse_url(url):
+ """
+ Parses the URL and returns enough arguments that can allow
+ us to re-instantiate this object.
+
+ """
+ results = NotifyBase.parse_url(url)
+ if not results:
+ # We're done early as we couldn't load the results
+ return results
+
+ if 'token' in results['qsd'] and len(results['qsd']['token']):
+ results['token'] = NotifyMastodon.unquote(results['qsd']['token'])
+
+ elif not results['password'] and results['user']:
+ results['token'] = NotifyMastodon.unquote(results['user'])
+
+ # Apply our targets
+ results['targets'] = NotifyMastodon.split_path(results['fullpath'])
+
+ # The defined Mastodon visibility
+ if 'visibility' in results['qsd'] and \
+ len(results['qsd']['visibility']):
+ # Simplified version
+ results['visibility'] = \
+ NotifyMastodon.unquote(results['qsd']['visibility'])
+
+ elif results['schema'].startswith('toot'):
+ results['visibility'] = MastodonMessageVisibility.PUBLIC
+
+ # Get Idempotency Key (if specified)
+ if 'key' in results['qsd'] and len(results['qsd']['key']):
+ results['key'] = \
+ NotifyMastodon.unquote(results['qsd']['key'])
+
+ # Get Spoiler Text
+ if 'spoiler' in results['qsd'] and len(results['qsd']['spoiler']):
+ results['spoiler'] = \
+ NotifyMastodon.unquote(results['qsd']['spoiler'])
+
+ # Get Language (if specified)
+ if 'language' in results['qsd'] and len(results['qsd']['language']):
+ results['language'] = \
+ NotifyMastodon.unquote(results['qsd']['language'])
+
+ # Get Sensitive Flag (for Attachments)
+ results['sensitive'] = \
+ parse_bool(results['qsd'].get(
+ 'sensitive',
+ NotifyMastodon.template_args['sensitive']['default']))
+
+ # Get Batch Mode Flag
+ results['batch'] = \
+ parse_bool(results['qsd'].get(
+ 'batch', NotifyMastodon.template_args['batch']['default']))
+
+ # The 'to' makes it easier to use yaml configuration
+ if 'to' in results['qsd'] and len(results['qsd']['to']):
+ results['targets'] += \
+ NotifyMastodon.parse_list(results['qsd']['to'])
+
+ return results
diff --git a/libs/apprise/plugins/NotifyMatrix.py b/libs/apprise/plugins/NotifyMatrix.py
index f62b16e0a..c0b524a0d 100644
--- a/libs/apprise/plugins/NotifyMatrix.py
+++ b/libs/apprise/plugins/NotifyMatrix.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
# Great sources
# - https://github.com/matrix-org/matrix-python-sdk
@@ -128,7 +135,14 @@ class NotifyMatrix(NotifyBase):
image_size = NotifyImageSize.XY_32
# The maximum allowable characters allowed in the body per message
- body_maxlen = 1000
+ # https://spec.matrix.org/v1.6/client-server-api/#size-limits
+ # The complete event MUST NOT be larger than 65536 bytes, when formatted
+ # with the federation event format, including any signatures, and encoded
+ # as Canonical JSON.
+ #
+ # To gracefully allow for some overhead' we'll define a max body length
+ # of just slighty lower then the limit of the full message itself.
+ body_maxlen = 65000
# Throttle a wee-bit to avoid thrashing
request_rate_per_sec = 0.5
@@ -239,7 +253,7 @@ class NotifyMatrix(NotifyBase):
"""
Initialize Matrix Object
"""
- super(NotifyMatrix, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# Prepare a list of rooms to connect and notify
self.rooms = parse_list(targets)
@@ -1182,6 +1196,13 @@ class NotifyMatrix(NotifyBase):
params=NotifyMatrix.urlencode(params),
)
+ def __len__(self):
+ """
+ Returns the number of targets associated with this notification
+ """
+ targets = len(self.rooms)
+ return targets if targets > 0 else 1
+
@staticmethod
def parse_url(url):
"""
diff --git a/libs/apprise/plugins/NotifyMatterMost.py b/libs/apprise/plugins/NotifyMatterMost.py
index 9c776b512..e62f653c4 100644
--- a/libs/apprise/plugins/NotifyMatterMost.py
+++ b/libs/apprise/plugins/NotifyMatterMost.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
# Create an incoming webhook; the website will provide you with something like:
# http://localhost:8065/hooks/yobjmukpaw3r3urc5h6i369yima
@@ -145,7 +152,7 @@ class NotifyMattermost(NotifyBase):
"""
Initialize Mattermost Object
"""
- super(NotifyMattermost, self).__init__(**kwargs)
+ super().__init__(**kwargs)
if self.secure:
self.schema = 'https'
diff --git a/libs/apprise/plugins/NotifyMessageBird.py b/libs/apprise/plugins/NotifyMessageBird.py
index 4b1da524e..f477df489 100644
--- a/libs/apprise/plugins/NotifyMessageBird.py
+++ b/libs/apprise/plugins/NotifyMessageBird.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
# Create an account https://messagebird.com if you don't already have one
#
@@ -115,7 +122,7 @@ class NotifyMessageBird(NotifyBase):
"""
Initialize MessageBird Object
"""
- super(NotifyMessageBird, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# API Key (associated with project)
self.apikey = validate_regex(
@@ -304,6 +311,13 @@ class NotifyMessageBird(NotifyBase):
[NotifyMessageBird.quote(x, safe='') for x in self.targets]),
params=NotifyMessageBird.urlencode(params))
+ def __len__(self):
+ """
+ Returns the number of targets associated with this notification
+ """
+ targets = len(self.targets)
+ return targets if targets > 0 else 1
+
@staticmethod
def parse_url(url):
"""
diff --git a/libs/apprise/plugins/NotifyMisskey.py b/libs/apprise/plugins/NotifyMisskey.py
new file mode 100644
index 000000000..54c4e628a
--- /dev/null
+++ b/libs/apprise/plugins/NotifyMisskey.py
@@ -0,0 +1,310 @@
+# -*- coding: utf-8 -*-
+# BSD 3-Clause License
+#
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+#
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+# 1. visit https://misskey-hub.net/ and see what it's all about if you want.
+# Choose a service you want to create an account on from here:
+# https://misskey-hub.net/en/instances.html
+#
+# - For this plugin, I tested using https://misskey.sda1.net and created an
+# account.
+#
+# 2. Generate an API Key:
+# - Settings > API > Generate Key
+# - Name it whatever you want
+# - Assign it 'AT LEAST':
+# a. Compose or delete chat messages
+# b. Compose or delete notes
+#
+#
+# This plugin also supports taking the URL (as identified above) directly
+# as well.
+
+import requests
+from json import dumps
+
+from .NotifyBase import NotifyBase
+from ..common import NotifyType
+from ..utils import validate_regex
+from ..AppriseLocale import gettext_lazy as _
+
+
+class MisskeyVisibility:
+ """
+ The visibility of any note created
+ """
+ # post will be public
+ PUBLIC = 'public'
+
+ HOME = 'home'
+
+ FOLLOWERS = 'followers'
+
+ PRIVATE = 'private'
+
+ SPECIFIED = 'specified'
+
+
+# Define the types in a list for validation purposes
+MISSKEY_VISIBILITIES = (
+ MisskeyVisibility.PUBLIC,
+ MisskeyVisibility.HOME,
+ MisskeyVisibility.FOLLOWERS,
+ MisskeyVisibility.PRIVATE,
+ MisskeyVisibility.SPECIFIED,
+)
+
+
+class NotifyMisskey(NotifyBase):
+ """
+ A wrapper for Misskey Notifications
+ """
+
+ # The default descriptive name associated with the Notification
+ service_name = 'Misskey'
+
+ # The services URL
+ service_url = 'https://misskey-hub.net/'
+
+ # The default protocol
+ protocol = 'misskey'
+
+ # The default secure protocol
+ secure_protocol = 'misskeys'
+
+ # A URL that takes you to the setup/help of the specific protocol
+ setup_url = 'https://github.com/caronc/apprise/wiki/Notify_misskey'
+
+ # The title is not used
+ title_maxlen = 0
+
+ # The maximum allowable characters allowed in the body per message
+ body_maxlen = 512
+
+ # Define object templates
+ templates = (
+ '{schema}://{project_id}/{msghook}',
+ )
+
+ # Define object templates
+ templates = (
+ '{schema}://{token}@{host}',
+ '{schema}://{token}@{host}:{port}',
+ )
+
+ # Define our template arguments
+ # Define our template arguments
+ template_tokens = dict(NotifyBase.template_tokens, **{
+ 'host': {
+ 'name': _('Hostname'),
+ 'type': 'string',
+ 'required': True,
+ },
+ 'token': {
+ 'name': _('Access Token'),
+ 'type': 'string',
+ 'required': True,
+ },
+ 'port': {
+ 'name': _('Port'),
+ 'type': 'int',
+ 'min': 1,
+ 'max': 65535,
+ },
+ })
+
+ # Define our template arguments
+ template_args = dict(NotifyBase.template_args, **{
+ 'token': {
+ 'alias_of': 'token',
+ },
+ 'visibility': {
+ 'name': _('Visibility'),
+ 'type': 'choice:string',
+ 'values': MISSKEY_VISIBILITIES,
+ 'default': MisskeyVisibility.PUBLIC,
+ },
+ })
+
+ def __init__(self, token=None, visibility=None, **kwargs):
+ """
+ Initialize Misskey Object
+ """
+ super().__init__(**kwargs)
+
+ self.token = validate_regex(token)
+ if not self.token:
+ msg = 'An invalid Misskey Access Token was specified.'
+ self.logger.warning(msg)
+ raise TypeError(msg)
+
+ if visibility:
+ # Input is a string; attempt to get the lookup from our
+ # sound mapping
+ vis = 'invalid' if not isinstance(visibility, str) \
+ else visibility.lower().strip()
+
+ # This little bit of black magic allows us to match against
+ # against multiple versions of the same string ... etc
+ self.visibility = \
+ next((v for v in MISSKEY_VISIBILITIES
+ if v.startswith(vis)), None)
+
+ if self.visibility not in MISSKEY_VISIBILITIES:
+ msg = 'The Misskey visibility specified ({}) is invalid.' \
+ .format(visibility)
+ self.logger.warning(msg)
+ raise TypeError(msg)
+ else:
+ self.visibility = self.template_args['visibility']['default']
+
+ # Prepare our URL
+ self.schema = 'https' if self.secure else 'http'
+ self.api_url = '%s://%s' % (self.schema, self.host)
+
+ if isinstance(self.port, int):
+ self.api_url += ':%d' % self.port
+
+ return
+
+ def url(self, privacy=False, *args, **kwargs):
+ """
+ Returns the URL built dynamically based on specified arguments.
+ """
+
+ params = {
+ 'visibility': self.visibility,
+ }
+
+ # Extend our parameters
+ params.update(self.url_parameters(privacy=privacy, *args, **kwargs))
+
+ host = self.host
+ if isinstance(self.port, int):
+ host += ':%d' % self.port
+
+ return '{schema}://{token}@{host}/?{params}'.format(
+ schema=self.secure_protocol if self.secure else self.protocol,
+ host=host,
+ token=self.pprint(self.token, privacy, safe=''),
+ params=NotifyMisskey.urlencode(params),
+ )
+
+ def send(self, body, title='', notify_type=NotifyType.INFO, **kwargs):
+ """
+ wrapper to _send since we can alert more then one channel
+ """
+
+ # prepare our headers
+ headers = {
+ 'User-Agent': self.app_id,
+ 'Content-Type': 'application/json',
+ }
+
+ # Prepare our payload
+ payload = {
+ 'i': self.token,
+ 'text': body,
+ 'visibility': self.visibility,
+ }
+
+ api_url = f'{self.api_url}/api/notes/create'
+ self.logger.debug('Misskey GET URL: %s (cert_verify=%r)' % (
+ api_url, self.verify_certificate))
+ self.logger.debug('Misskey Payload: %s' % str(payload))
+
+ # Always call throttle before any remote server i/o is made
+ self.throttle()
+
+ try:
+ r = requests.post(
+ api_url,
+ headers=headers,
+ data=dumps(payload),
+ verify=self.verify_certificate,
+ timeout=self.request_timeout,
+ )
+ if r.status_code != requests.codes.ok:
+ # We had a problem
+ status_str = \
+ NotifyMisskey.http_response_code_lookup(r.status_code)
+
+ self.logger.warning(
+ 'Failed to send Misskey notification: '
+ '{}{}error={}.'.format(
+ status_str,
+ ', ' if status_str else '',
+ r.status_code))
+
+ self.logger.debug('Response Details:\r\n{}'.format(r.content))
+
+ # Return; we're done
+ return False
+
+ else:
+ self.logger.info('Sent Misskey notification.')
+
+ except requests.RequestException as e:
+ self.logger.warning(
+ 'A Connection error occurred sending Misskey '
+ 'notification.')
+ self.logger.debug('Socket Exception: %s' % str(e))
+
+ # Return; we're done
+ return False
+
+ return True
+
+ @staticmethod
+ def parse_url(url):
+ """
+ Parses the URL and returns enough arguments that can allow
+ us to re-instantiate this object.
+
+ """
+
+ results = NotifyBase.parse_url(url)
+ if not results:
+ # We're done early as we couldn't load the results
+ return results
+
+ if 'token' in results['qsd'] and len(results['qsd']['token']):
+ results['token'] = NotifyMisskey.unquote(results['qsd']['token'])
+
+ elif not results['password'] and results['user']:
+ results['token'] = NotifyMisskey.unquote(results['user'])
+
+ # Capture visibility if specified
+ if 'visibility' in results['qsd'] and \
+ len(results['qsd']['visibility']):
+ results['visibility'] = \
+ NotifyMisskey.unquote(results['qsd']['visibility'])
+
+ return results
diff --git a/libs/apprise/plugins/NotifyNextcloud.py b/libs/apprise/plugins/NotifyNextcloud.py
index 6db7f2743..6bb79a7ef 100644
--- a/libs/apprise/plugins/NotifyNextcloud.py
+++ b/libs/apprise/plugins/NotifyNextcloud.py
@@ -1,26 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CON
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
import requests
@@ -125,7 +133,7 @@ class NotifyNextcloud(NotifyBase):
"""
Initialize Nextcloud Object
"""
- super(NotifyNextcloud, self).__init__(**kwargs)
+ super().__init__(**kwargs)
self.targets = parse_list(targets)
if len(self.targets) == 0:
@@ -304,6 +312,12 @@ class NotifyNextcloud(NotifyBase):
params=NotifyNextcloud.urlencode(params),
)
+ def __len__(self):
+ """
+ Returns the number of targets associated with this notification
+ """
+ return len(self.targets)
+
@staticmethod
def parse_url(url):
"""
diff --git a/libs/apprise/plugins/NotifyNextcloudTalk.py b/libs/apprise/plugins/NotifyNextcloudTalk.py
index a1853795c..8a1dc4294 100644
--- a/libs/apprise/plugins/NotifyNextcloudTalk.py
+++ b/libs/apprise/plugins/NotifyNextcloudTalk.py
@@ -1,29 +1,38 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CON
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
import requests
+from json import dumps
from .NotifyBase import NotifyBase
from ..URLBase import PrivacyMode
from ..common import NotifyType
@@ -106,7 +115,7 @@ class NotifyNextcloudTalk(NotifyBase):
"""
Initialize Nextcloud Talk Object
"""
- super(NotifyNextcloudTalk, self).__init__(**kwargs)
+ super().__init__(**kwargs)
if self.user is None or self.password is None:
msg = 'User and password have to be specified.'
@@ -134,7 +143,9 @@ class NotifyNextcloudTalk(NotifyBase):
# Prepare our Header
headers = {
'User-Agent': self.app_id,
- 'OCS-APIREQUEST': 'true',
+ 'OCS-APIRequest': 'true',
+ 'Accept': 'application/json',
+ 'Content-Type': 'application/json',
}
# Apply any/all header over-rides defined
@@ -183,7 +194,7 @@ class NotifyNextcloudTalk(NotifyBase):
try:
r = requests.post(
notify_url,
- data=payload,
+ data=dumps(payload),
headers=headers,
auth=(self.user, self.password),
verify=self.verify_certificate,
@@ -252,6 +263,12 @@ class NotifyNextcloudTalk(NotifyBase):
for x in self.targets]),
)
+ def __len__(self):
+ """
+ Returns the number of targets associated with this notification
+ """
+ return len(self.targets)
+
@staticmethod
def parse_url(url):
"""
diff --git a/libs/apprise/plugins/NotifyNotica.py b/libs/apprise/plugins/NotifyNotica.py
index 36cd77cb7..90bf7ef1c 100644
--- a/libs/apprise/plugins/NotifyNotica.py
+++ b/libs/apprise/plugins/NotifyNotica.py
@@ -1,26 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CON
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
# 1. Simply visit https://notica.us
# 2. You'll be provided a new variation of the website which will look
@@ -160,7 +168,7 @@ class NotifyNotica(NotifyBase):
"""
Initialize Notica Object
"""
- super(NotifyNotica, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# Token (associated with project)
self.token = validate_regex(token)
diff --git a/libs/apprise/plugins/NotifyNotifico.py b/libs/apprise/plugins/NotifyNotifico.py
index 987661740..9b1661bf6 100644
--- a/libs/apprise/plugins/NotifyNotifico.py
+++ b/libs/apprise/plugins/NotifyNotifico.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
# Notifico allows you to relay notifications into IRC channels.
#
@@ -160,7 +167,7 @@ class NotifyNotifico(NotifyBase):
"""
Initialize Notifico Object
"""
- super(NotifyNotifico, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# Assign our message hook
self.project_id = validate_regex(
diff --git a/libs/apprise/plugins/NotifyNtfy.py b/libs/apprise/plugins/NotifyNtfy.py
index c7b2475df..87587c041 100644
--- a/libs/apprise/plugins/NotifyNtfy.py
+++ b/libs/apprise/plugins/NotifyNtfy.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2022 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
# Great sources
# - https://github.com/matrix-org/matrix-python-sdk
@@ -40,8 +47,10 @@ from os.path import basename
from .NotifyBase import NotifyBase
from ..common import NotifyType
+from ..common import NotifyImageSize
from ..AppriseLocale import gettext_lazy as _
from ..utils import parse_list
+from ..utils import parse_bool
from ..utils import is_hostname
from ..utils import is_ipaddr
from ..utils import validate_regex
@@ -65,6 +74,27 @@ NTFY_MODES = (
NtfyMode.PRIVATE,
)
+# A Simple regular expression used to auto detect Auth mode if it isn't
+# otherwise specified:
+NTFY_AUTH_DETECT_RE = re.compile('tk_[^ \t]+', re.IGNORECASE)
+
+
+class NtfyAuth:
+ """
+ Define ntfy Authentication Modes
+ """
+ # Basic auth (user and password provided)
+ BASIC = "basic"
+
+ # Auth Token based
+ TOKEN = "token"
+
+
+NTFY_AUTH = (
+ NtfyAuth.BASIC,
+ NtfyAuth.TOKEN,
+)
+
class NtfyPriority:
"""
@@ -142,6 +172,9 @@ class NotifyNtfy(NotifyBase):
# Default upstream/cloud host if none is defined
cloud_notify_url = 'https://ntfy.sh'
+ # Allows the user to specify the NotifyImageSize object
+ image_size = NotifyImageSize.XY_256
+
# Message time to live (if remote client isn't around to receive it)
time_to_live = 2419200
@@ -158,6 +191,8 @@ class NotifyNtfy(NotifyBase):
'{schema}://{user}@{host}:{port}/{targets}',
'{schema}://{user}:{password}@{host}/{targets}',
'{schema}://{user}:{password}@{host}:{port}/{targets}',
+ '{schema}://{token}@{host}/{targets}',
+ '{schema}://{token}@{host}:{port}/{targets}',
)
# Define our template tokens
@@ -181,6 +216,11 @@ class NotifyNtfy(NotifyBase):
'type': 'string',
'private': True,
},
+ 'token': {
+ 'name': _('Token'),
+ 'type': 'string',
+ 'private': True,
+ },
'topic': {
'name': _('Topic'),
'type': 'string',
@@ -199,6 +239,16 @@ class NotifyNtfy(NotifyBase):
'name': _('Attach'),
'type': 'string',
},
+ 'image': {
+ 'name': _('Include Image'),
+ 'type': 'bool',
+ 'default': True,
+ 'map_to': 'include_image',
+ },
+ 'avatar_url': {
+ 'name': _('Avatar URL'),
+ 'type': 'string',
+ },
'filename': {
'name': _('Attach Filename'),
'type': 'string',
@@ -231,6 +281,15 @@ class NotifyNtfy(NotifyBase):
'values': NTFY_MODES,
'default': NtfyMode.PRIVATE,
},
+ 'token': {
+ 'alias_of': 'token',
+ },
+ 'auth': {
+ 'name': _('Authentication Type'),
+ 'type': 'choice:string',
+ 'values': NTFY_AUTH,
+ 'default': NtfyAuth.BASIC,
+ },
'to': {
'alias_of': 'targets',
},
@@ -238,11 +297,12 @@ class NotifyNtfy(NotifyBase):
def __init__(self, targets=None, attach=None, filename=None, click=None,
delay=None, email=None, priority=None, tags=None, mode=None,
+ include_image=True, avatar_url=None, auth=None, token=None,
**kwargs):
"""
Initialize ntfy Object
"""
- super(NotifyNtfy, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# Prepare our mode
self.mode = mode.strip().lower() \
@@ -254,6 +314,20 @@ class NotifyNtfy(NotifyBase):
self.logger.warning(msg)
raise TypeError(msg)
+ # Show image associated with notification
+ self.include_image = include_image
+
+ # Prepare our authentication type
+ self.auth = auth.strip().lower() \
+ if isinstance(auth, str) \
+ else self.template_args['auth']['default']
+
+ if self.auth not in NTFY_AUTH:
+ msg = 'An invalid ntfy Authentication type ({}) was specified.' \
+ .format(auth)
+ self.logger.warning(msg)
+ raise TypeError(msg)
+
# Attach a file (URL supported)
self.attach = attach
@@ -269,6 +343,9 @@ class NotifyNtfy(NotifyBase):
# An email to forward notifications to
self.email = email
+ # Save our token
+ self.token = token
+
# The Priority of the message
self.priority = NotifyNtfy.template_args['priority']['default'] \
if not priority else \
@@ -280,6 +357,11 @@ class NotifyNtfy(NotifyBase):
# Any optional tags to attach to the notification
self.__tags = parse_list(tags)
+ # Avatar URL
+ # This allows a user to provide an over-ride to the otherwise
+ # dynamically generated avatar url images
+ self.avatar_url = avatar_url
+
# Build list of topics
topics = parse_list(targets)
self.topics = []
@@ -308,6 +390,15 @@ class NotifyNtfy(NotifyBase):
self.logger.warning('There are no ntfy topics to notify')
return False
+ # Acquire image_url
+ image_url = self.image_url(notify_type)
+
+ if self.include_image and (image_url or self.avatar_url):
+ image_url = \
+ self.avatar_url if self.avatar_url else image_url
+ else:
+ image_url = None
+
# Create a copy of the topics
topics = list(self.topics)
while len(topics) > 0:
@@ -336,20 +427,23 @@ class NotifyNtfy(NotifyBase):
attachment.url(privacy=True)))
okay, response = self._send(
- topic, body=_body, title=_title, attach=attachment)
+ topic, body=_body, title=_title, image_url=image_url,
+ attach=attachment)
if not okay:
# We can't post our attachment; abort immediately
return False
else:
# Send our Notification Message
- okay, response = self._send(topic, body=body, title=title)
+ okay, response = self._send(
+ topic, body=body, title=title, image_url=image_url)
if not okay:
# Mark our failure, but contiue to move on
has_error = True
return not has_error
- def _send(self, topic, body=None, title=None, attach=None, **kwargs):
+ def _send(self, topic, body=None, title=None, attach=None, image_url=None,
+ **kwargs):
"""
Wrapper to the requests (post) object
"""
@@ -376,9 +470,17 @@ class NotifyNtfy(NotifyBase):
else: # NotifyNtfy.PRVATE
# Allow more settings to be applied now
- if self.user:
+ if self.auth == NtfyAuth.BASIC and self.user:
auth = (self.user, self.password)
+ elif self.auth == NtfyAuth.TOKEN:
+ if not self.token:
+ self.logger.warning('No Ntfy Token was specified')
+ return False, None
+
+ # Set Token
+ headers['Authorization'] = f'Bearer {self.token}'
+
# Prepare our ntfy Template URL
schema = 'https' if self.secure else 'http'
@@ -397,6 +499,9 @@ class NotifyNtfy(NotifyBase):
virt_payload = params
notify_url += '/{topic}'.format(topic=topic)
+ if image_url:
+ headers['X-Icon'] = image_url
+
if title:
virt_payload['title'] = title
@@ -529,8 +634,13 @@ class NotifyNtfy(NotifyBase):
params = {
'priority': self.priority,
'mode': self.mode,
+ 'image': 'yes' if self.include_image else 'no',
+ 'auth': self.auth,
}
+ if self.avatar_url:
+ params['avatar_url'] = self.avatar_url
+
if self.attach is not None:
params['attach'] = self.attach
@@ -550,15 +660,22 @@ class NotifyNtfy(NotifyBase):
# Determine Authentication
auth = ''
- if self.user and self.password:
- auth = '{user}:{password}@'.format(
- user=NotifyNtfy.quote(self.user, safe=''),
- password=self.pprint(
- self.password, privacy, mode=PrivacyMode.Secret, safe=''),
- )
- elif self.user:
- auth = '{user}@'.format(
- user=NotifyNtfy.quote(self.user, safe=''),
+ if self.auth == NtfyAuth.BASIC:
+ if self.user and self.password:
+ auth = '{user}:{password}@'.format(
+ user=NotifyNtfy.quote(self.user, safe=''),
+ password=self.pprint(
+ self.password, privacy, mode=PrivacyMode.Secret,
+ safe=''),
+ )
+ elif self.user:
+ auth = '{user}@'.format(
+ user=NotifyNtfy.quote(self.user, safe=''),
+ )
+
+ elif self.token: # NtfyAuth.TOKEN also
+ auth = '{token}@'.format(
+ token=self.pprint(self.token, privacy, safe=''),
)
if self.mode == NtfyMode.PRIVATE:
@@ -581,6 +698,12 @@ class NotifyNtfy(NotifyBase):
params=NotifyNtfy.urlencode(params)
)
+ def __len__(self):
+ """
+ Returns the number of targets associated with this notification
+ """
+ return len(self.topics)
+
@staticmethod
def parse_url(url):
"""
@@ -623,6 +746,15 @@ class NotifyNtfy(NotifyBase):
results['tags'] = \
parse_list(NotifyNtfy.unquote(results['qsd']['tags']))
+ # Boolean to include an image or not
+ results['include_image'] = parse_bool(results['qsd'].get(
+ 'image', NotifyNtfy.template_args['image']['default']))
+
+ # Extract avatar url if it was specified
+ if 'avatar_url' in results['qsd']:
+ results['avatar_url'] = \
+ NotifyNtfy.unquote(results['qsd']['avatar_url'])
+
# Acquire our targets/topics
results['targets'] = NotifyNtfy.split_path(results['fullpath'])
@@ -631,6 +763,37 @@ class NotifyNtfy(NotifyBase):
results['targets'] += \
NotifyNtfy.parse_list(results['qsd']['to'])
+ # Token Specified
+ if 'token' in results['qsd'] and len(results['qsd']['token']):
+ # Token presumed to be the one in use
+ results['auth'] = NtfyAuth.TOKEN
+ results['token'] = NotifyNtfy.unquote(results['qsd']['token'])
+
+ # Auth override
+ if 'auth' in results['qsd'] and results['qsd']['auth']:
+ results['auth'] = NotifyNtfy.unquote(
+ results['qsd']['auth'].strip().lower())
+
+ if not results.get('auth') and results['user'] \
+ and not results['password']:
+ # We can try to detect the authentication type on the formatting of
+ # the username. Look for tk_.*
+ #
+ # This isn't a surfire way to do things though; it's best to
+ # specify the auth= flag
+ results['auth'] = NtfyAuth.TOKEN \
+ if NTFY_AUTH_DETECT_RE.match(results['user']) \
+ else NtfyAuth.BASIC
+
+ if results.get('auth') == NtfyAuth.TOKEN and not results.get('token'):
+ if results['user'] and not results['password']:
+ # Make sure we properly set our token
+ results['token'] = NotifyNtfy.unquote(results['user'])
+
+ elif results['password']:
+ # Make sure we properly set our token
+ results['token'] = NotifyNtfy.unquote(results['password'])
+
# Mode override
if 'mode' in results['qsd'] and results['qsd']['mode']:
results['mode'] = NotifyNtfy.unquote(
diff --git a/libs/apprise/plugins/NotifyOffice365.py b/libs/apprise/plugins/NotifyOffice365.py
index 5c8ea934e..658a21526 100644
--- a/libs/apprise/plugins/NotifyOffice365.py
+++ b/libs/apprise/plugins/NotifyOffice365.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2020 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
# API Details:
# https://docs.microsoft.com/en-us/previous-versions/office/\
@@ -173,7 +180,7 @@ class NotifyOffice365(NotifyBase):
"""
Initialize Office 365 Object
"""
- super(NotifyOffice365, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# Tenant identifier
self.tenant = validate_regex(
@@ -589,6 +596,12 @@ class NotifyOffice365(NotifyBase):
safe='') for e in self.targets]),
params=NotifyOffice365.urlencode(params))
+ def __len__(self):
+ """
+ Returns the number of targets associated with this notification
+ """
+ return len(self.targets)
+
@staticmethod
def parse_url(url):
"""
diff --git a/libs/apprise/plugins/NotifyOneSignal.py b/libs/apprise/plugins/NotifyOneSignal.py
index 3d936f5be..ce56bbdd9 100644
--- a/libs/apprise/plugins/NotifyOneSignal.py
+++ b/libs/apprise/plugins/NotifyOneSignal.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2020 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
# One Signal requires that you've signed up with the service and
# generated yourself an API Key and APP ID.
@@ -44,7 +51,7 @@ from ..utils import is_email
from ..AppriseLocale import gettext_lazy as _
-class OneSignalCategory(NotifyBase):
+class OneSignalCategory:
"""
We define the different category types that we can notify via OneSignal
"""
@@ -85,7 +92,7 @@ class NotifyOneSignal(NotifyBase):
image_size = NotifyImageSize.XY_72
# The maximum allowable batch sizes per message
- maximum_batch_size = 2000
+ default_batch_size = 2000
# Define object templates
templates = (
@@ -114,7 +121,7 @@ class NotifyOneSignal(NotifyBase):
'private': True,
'required': True,
},
- 'target_device': {
+ 'target_player': {
'name': _('Target Player ID'),
'type': 'string',
'map_to': 'targets',
@@ -178,7 +185,7 @@ class NotifyOneSignal(NotifyBase):
Initialize OneSignal
"""
- super(NotifyOneSignal, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# The apikey associated with the account
self.apikey = validate_regex(apikey)
@@ -197,7 +204,7 @@ class NotifyOneSignal(NotifyBase):
raise TypeError(msg)
# Prepare Batch Mode Flag
- self.batch_size = self.maximum_batch_size if batch else 1
+ self.batch_size = self.default_batch_size if batch else 1
# Place a thumbnail image inline with the message body
self.include_image = include_image
@@ -425,6 +432,26 @@ class NotifyOneSignal(NotifyBase):
params=NotifyOneSignal.urlencode(params),
)
+ def __len__(self):
+ """
+ Returns the number of targets associated with this notification
+ """
+ #
+ # Factor batch into calculation
+ #
+ if self.batch_size > 1:
+ # Batches can only be sent by group (you can't combine groups into
+ # a single batch)
+ total_targets = 0
+ for k, m in self.targets.items():
+ targets = len(m)
+ total_targets += int(targets / self.batch_size) + \
+ (1 if targets % self.batch_size else 0)
+ return total_targets
+
+ # Normal batch count; just count the targets
+ return sum([len(m) for _, m in self.targets.items()])
+
@staticmethod
def parse_url(url):
"""
diff --git a/libs/apprise/plugins/NotifyOpsgenie.py b/libs/apprise/plugins/NotifyOpsgenie.py
index 47e580993..0639c1ed2 100644
--- a/libs/apprise/plugins/NotifyOpsgenie.py
+++ b/libs/apprise/plugins/NotifyOpsgenie.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2020 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
# Signup @ https://www.opsgenie.com
#
@@ -165,7 +172,7 @@ class NotifyOpsgenie(NotifyBase):
opsgenie_default_region = OpsgenieRegion.US
# The maximum allowable targets within a notification
- maximum_batch_size = 50
+ default_batch_size = 50
# Define object templates
templates = (
@@ -262,7 +269,7 @@ class NotifyOpsgenie(NotifyBase):
"""
Initialize Opsgenie Object
"""
- super(NotifyOpsgenie, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# API Key (associated with project)
self.apikey = validate_regex(apikey)
@@ -301,7 +308,7 @@ class NotifyOpsgenie(NotifyBase):
self.details.update(details)
# Prepare Batch Mode Flag
- self.batch_size = self.maximum_batch_size if batch else 1
+ self.batch_size = self.default_batch_size if batch else 1
# Assign our tags (if defined)
self.__tags = parse_list(tags)
@@ -382,7 +389,7 @@ class NotifyOpsgenie(NotifyBase):
has_error = False
# Use body if title not set
- title_body = body if not title else body
+ title_body = body if not title else title
# Create a copy ouf our details object
details = self.details.copy()
@@ -529,6 +536,20 @@ class NotifyOpsgenie(NotifyBase):
for x in self.targets]),
params=NotifyOpsgenie.urlencode(params))
+ def __len__(self):
+ """
+ Returns the number of targets associated with this notification
+ """
+ #
+ # Factor batch into calculation
+ #
+ targets = len(self.targets)
+ if self.batch_size > 1:
+ targets = int(targets / self.batch_size) + \
+ (1 if targets % self.batch_size else 0)
+
+ return targets if targets > 0 else 1
+
@staticmethod
def parse_url(url):
"""
diff --git a/libs/apprise/plugins/NotifyPagerDuty.py b/libs/apprise/plugins/NotifyPagerDuty.py
index deff363d5..a2417275b 100644
--- a/libs/apprise/plugins/NotifyPagerDuty.py
+++ b/libs/apprise/plugins/NotifyPagerDuty.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2022 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
# API Refererence:
# - https://developer.pagerduty.com/api-reference/\
@@ -61,6 +68,13 @@ PAGERDUTY_SEVERITY_MAP = {
NotifyType.FAILURE: PagerDutySeverity.CRITICAL,
}
+PAGERDUTY_SEVERITIES = (
+ PagerDutySeverity.INFO,
+ PagerDutySeverity.WARNING,
+ PagerDutySeverity.CRITICAL,
+ PagerDutySeverity.ERROR,
+)
+
# Priorities
class PagerDutyRegion:
@@ -169,6 +183,14 @@ class NotifyPagerDuty(NotifyBase):
'default': PagerDutyRegion.US,
'map_to': 'region_name',
},
+ # The severity is automatically determined, however you can optionally
+ # over-ride its value and force it to be what you want
+ 'severity': {
+ 'name': _('Severity'),
+ 'type': 'choice:string',
+ 'values': PAGERDUTY_SEVERITIES,
+ 'map_to': 'severity',
+ },
'image': {
'name': _('Include Image'),
'type': 'bool',
@@ -188,11 +210,11 @@ class NotifyPagerDuty(NotifyBase):
def __init__(self, apikey, integrationkey=None, source=None,
component=None, group=None, class_id=None,
include_image=True, click=None, details=None,
- region_name=None, **kwargs):
+ region_name=None, severity=None, **kwargs):
"""
Initialize Pager Duty Object
"""
- super(NotifyPagerDuty, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# Long-Lived Access token (generated from User Profile)
self.apikey = validate_regex(apikey)
@@ -248,6 +270,19 @@ class NotifyPagerDuty(NotifyBase):
self.logger.warning(msg)
raise TypeError(msg)
+ # The severity (if specified)
+ self.severity = \
+ None if severity is None else next((
+ s for s in PAGERDUTY_SEVERITIES
+ if str(s).lower().startswith(severity)), False)
+
+ if self.severity is False:
+ # Invalid severity specified
+ msg = 'The PagerDuty severity specified ({}) is invalid.' \
+ .format(severity)
+ self.logger.warning(msg)
+ raise TypeError(msg)
+
# A clickthrough option for notifications
self.click = click
@@ -289,8 +324,8 @@ class NotifyPagerDuty(NotifyBase):
'summary': body,
# Set our severity
- 'severity': PAGERDUTY_SEVERITY_MAP[notify_type],
-
+ 'severity': PAGERDUTY_SEVERITY_MAP[notify_type]
+ if not self.severity else self.severity,
# Our Alerting Source/Component
'source': self.source,
@@ -400,6 +435,9 @@ class NotifyPagerDuty(NotifyBase):
if self.click is not None:
params['click'] = self.click
+ if self.severity:
+ params['severity'] = self.severity
+
# Append our custom entries our parameters
params.update({'+{}'.format(k): v for k, v in self.details.items()})
@@ -464,6 +502,10 @@ class NotifyPagerDuty(NotifyBase):
results['class_id'] = \
NotifyPagerDuty.unquote(results['qsd']['class'])
+ if 'severity' in results['qsd'] and len(results['qsd']['severity']):
+ results['severity'] = \
+ NotifyPagerDuty.unquote(results['qsd']['severity'])
+
# Acquire our full path
fullpath = NotifyPagerDuty.split_path(results['fullpath'])
diff --git a/libs/apprise/plugins/NotifyPagerTree.py b/libs/apprise/plugins/NotifyPagerTree.py
new file mode 100644
index 000000000..65a19f613
--- /dev/null
+++ b/libs/apprise/plugins/NotifyPagerTree.py
@@ -0,0 +1,424 @@
+# -*- coding: utf-8 -*-
+# BSD 3-Clause License
+#
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+#
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+import requests
+from json import dumps
+
+from uuid import uuid4
+
+from .NotifyBase import NotifyBase
+from ..common import NotifyType
+from ..utils import parse_list
+from ..utils import validate_regex
+from ..AppriseLocale import gettext_lazy as _
+
+
+# Actions
+class PagerTreeAction:
+ CREATE = 'create'
+ ACKNOWLEDGE = 'acknowledge'
+ RESOLVE = 'resolve'
+
+
+# Urgencies
+class PagerTreeUrgency:
+ SILENT = "silent"
+ LOW = "low"
+ MEDIUM = "medium"
+ HIGH = "high"
+ CRITICAL = "critical"
+
+
+PAGERTREE_ACTIONS = {
+ PagerTreeAction.CREATE: 'create',
+ PagerTreeAction.ACKNOWLEDGE: 'acknowledge',
+ PagerTreeAction.RESOLVE: 'resolve',
+}
+
+PAGERTREE_URGENCIES = {
+ # Note: This also acts as a reverse lookup mapping
+ PagerTreeUrgency.SILENT: 'silent',
+ PagerTreeUrgency.LOW: 'low',
+ PagerTreeUrgency.MEDIUM: 'medium',
+ PagerTreeUrgency.HIGH: 'high',
+ PagerTreeUrgency.CRITICAL: 'critical',
+}
+# Extend HTTP Error Messages
+PAGERTREE_HTTP_ERROR_MAP = {
+ 402: 'Payment Required - Please subscribe or upgrade',
+ 403: 'Forbidden - Blocked',
+ 404: 'Not Found - Invalid Integration ID',
+ 405: 'Method Not Allowed - Integration Disabled',
+ 429: 'Too Many Requests - Rate Limit Exceeded',
+}
+
+
+class NotifyPagerTree(NotifyBase):
+ """
+ A wrapper for PagerTree Notifications
+ """
+
+ # The default descriptive name associated with the Notification
+ service_name = 'PagerTree'
+
+ # The services URL
+ service_url = 'https://pagertree.com/'
+
+ # All PagerTree requests are secure
+ secure_protocol = 'pagertree'
+
+ # A URL that takes you to the setup/help of the specific protocol
+ setup_url = 'https://github.com/caronc/apprise/wiki/Notify_pagertree'
+
+ # PagerTree uses the http protocol with JSON requests
+ notify_url = 'https://api.pagertree.com/integration/{}'
+
+ # Define object templates
+ templates = (
+ '{schema}://{integration}',
+ )
+
+ # Define our template tokens
+ template_tokens = dict(NotifyBase.template_tokens, **{
+ 'integration': {
+ 'name': _('Integration ID'),
+ 'type': 'string',
+ 'private': True,
+ 'required': True,
+ }
+ })
+
+ # Define our template arguments
+ template_args = dict(NotifyBase.template_args, **{
+ 'action': {
+ 'name': _('Action'),
+ 'type': 'choice:string',
+ 'values': PAGERTREE_ACTIONS,
+ 'default': PagerTreeAction.CREATE,
+ },
+ 'thirdparty': {
+ 'name': _('Third Party ID'),
+ 'type': 'string',
+ },
+ 'urgency': {
+ 'name': _('Urgency'),
+ 'type': 'choice:string',
+ 'values': PAGERTREE_URGENCIES,
+ },
+ 'tags': {
+ 'name': _('Tags'),
+ 'type': 'string',
+ },
+ })
+
+ # Define any kwargs we're using
+ template_kwargs = {
+ 'headers': {
+ 'name': _('HTTP Header'),
+ 'prefix': '+',
+ },
+ 'payload_extras': {
+ 'name': _('Payload Extras'),
+ 'prefix': ':',
+ },
+ 'meta_extras': {
+ 'name': _('Meta Extras'),
+ 'prefix': '-',
+ },
+ }
+
+ def __init__(self, integration, action=None, thirdparty=None,
+ urgency=None, tags=None, headers=None,
+ payload_extras=None, meta_extras=None, **kwargs):
+ """
+ Initialize PagerTree Object
+ """
+ super().__init__(**kwargs)
+
+ # Integration ID (associated with account)
+ self.integration = \
+ validate_regex(integration, r'^int_[a-zA-Z0-9\-_]{7,14}$')
+ if not self.integration:
+ msg = 'An invalid PagerTree Integration ID ' \
+ '({}) was specified.'.format(integration)
+ self.logger.warning(msg)
+ raise TypeError(msg)
+
+ # thirdparty (optional, in case they want to pass the
+ # acknowledge or resolve action)
+ self.thirdparty = None
+ if thirdparty:
+ # An id was specified, we want to validate it
+ self.thirdparty = validate_regex(thirdparty)
+ if not self.thirdparty:
+ msg = 'An invalid PagerTree third party ID ' \
+ '({}) was specified.'.format(thirdparty)
+ self.logger.warning(msg)
+ raise TypeError(msg)
+
+ self.headers = {}
+ if headers:
+ # Store our extra headers
+ self.headers.update(headers)
+
+ self.payload_extras = {}
+ if payload_extras:
+ # Store our extra payload entries
+ self.payload_extras.update(payload_extras)
+
+ self.meta_extras = {}
+ if meta_extras:
+ # Store our extra payload entries
+ self.meta_extras.update(meta_extras)
+
+ # Setup our action
+ self.action = NotifyPagerTree.template_args['action']['default'] \
+ if action not in PAGERTREE_ACTIONS else \
+ PAGERTREE_ACTIONS[action]
+
+ # Setup our urgency
+ self.urgency = \
+ None if urgency not in PAGERTREE_URGENCIES else \
+ PAGERTREE_URGENCIES[urgency]
+
+ # Any optional tags to attach to the notification
+ self.__tags = parse_list(tags)
+
+ return
+
+ def send(self, body, title='', notify_type=NotifyType.INFO, **kwargs):
+ """
+ Perform PagerTree Notification
+ """
+
+ # Prepare our headers
+ headers = {
+ 'User-Agent': self.app_id,
+ 'Content-Type': 'application/json',
+ }
+
+ # Apply any/all header over-rides defined
+ # For things like PagerTree Token
+ headers.update(self.headers)
+
+ # prepare JSON Object
+ payload = {
+ # Generate an ID (unless one was explicitly forced to be used)
+ 'id': self.thirdparty if self.thirdparty else str(uuid4()),
+ 'event_type': self.action,
+ }
+
+ if self.action == PagerTreeAction.CREATE:
+ payload['title'] = title if title else self.app_desc
+ payload['description'] = body
+
+ payload['meta'] = self.meta_extras
+ payload['tags'] = self.__tags
+
+ if self.urgency is not None:
+ payload['urgency'] = self.urgency
+
+ # Apply any/all payload over-rides defined
+ payload.update(self.payload_extras)
+
+ # Prepare our URL based on integration
+ notify_url = self.notify_url.format(self.integration)
+
+ self.logger.debug('PagerTree POST URL: %s (cert_verify=%r)' % (
+ notify_url, self.verify_certificate,
+ ))
+ self.logger.debug('PagerTree Payload: %s' % str(payload))
+
+ # Always call throttle before any remote server i/o is made
+ self.throttle()
+
+ try:
+ r = requests.post(
+ notify_url,
+ data=dumps(payload),
+ headers=headers,
+ verify=self.verify_certificate,
+ timeout=self.request_timeout,
+ )
+ if r.status_code not in (
+ requests.codes.ok, requests.codes.created,
+ requests.codes.accepted):
+ # We had a problem
+ status_str = \
+ NotifyPagerTree.http_response_code_lookup(
+ r.status_code)
+
+ self.logger.warning(
+ 'Failed to send PagerTree notification: '
+ '{}{}error={}.'.format(
+ status_str,
+ ', ' if status_str else '',
+ r.status_code))
+
+ self.logger.debug('Response Details:\r\n{}'.format(r.content))
+
+ # Return; we're done
+ return False
+
+ else:
+ self.logger.info('Sent PagerTree notification.')
+
+ except requests.RequestException as e:
+ self.logger.warning(
+ 'A Connection error occurred sending PagerTree '
+ 'notification to %s.' % self.host)
+ self.logger.debug('Socket Exception: %s' % str(e))
+
+ # Return; we're done
+ return False
+
+ return True
+
+ def url(self, privacy=False, *args, **kwargs):
+ """
+ Returns the URL built dynamically based on specified arguments.
+ """
+
+ # Define any URL parameters
+ params = {
+ 'action': self.action,
+ }
+
+ if self.thirdparty:
+ params['tid'] = self.thirdparty
+
+ if self.urgency:
+ params['urgency'] = self.urgency
+
+ if self.__tags:
+ params['tags'] = ','.join([x for x in self.__tags])
+
+ # Extend our parameters
+ params.update(self.url_parameters(privacy=privacy, *args, **kwargs))
+
+ # Headers prefixed with a '+' sign
+ # Append our headers into our parameters
+ params.update({'+{}'.format(k): v for k, v in self.headers.items()})
+
+ # Meta: {} prefixed with a '-' sign
+ # Append our meta extras into our parameters
+ params.update(
+ {'-{}'.format(k): v for k, v in self.meta_extras.items()})
+
+ # Payload body extras prefixed with a ':' sign
+ # Append our payload extras into our parameters
+ params.update(
+ {':{}'.format(k): v for k, v in self.payload_extras.items()})
+
+ return '{schema}://{integration}?{params}'.format(
+ schema=self.secure_protocol,
+ # never encode hostname since we're expecting it to be a valid one
+ integration=self.pprint(self.integration, privacy, safe=''),
+ params=NotifyPagerTree.urlencode(params),
+ )
+
+ @staticmethod
+ def parse_url(url):
+ """
+ Parses the URL and returns enough arguments that can allow
+ us to re-instantiate this object.
+
+ """
+ results = NotifyBase.parse_url(url, verify_host=False)
+
+ if not results:
+ # We're done early as we couldn't load the results
+ return results
+
+ # Add our headers that the user can potentially over-ride if they wish
+ # to to our returned result set and tidy entries by unquoting them
+ results['headers'] = {
+ NotifyPagerTree.unquote(x): NotifyPagerTree.unquote(y)
+ for x, y in results['qsd+'].items()
+ }
+
+ # store any additional payload extra's defined
+ results['payload_extras'] = {
+ NotifyPagerTree.unquote(x): NotifyPagerTree.unquote(y)
+ for x, y in results['qsd:'].items()
+ }
+
+ # store any additional meta extra's defined
+ results['meta_extras'] = {
+ NotifyPagerTree.unquote(x): NotifyPagerTree.unquote(y)
+ for x, y in results['qsd-'].items()
+ }
+
+ # Integration ID
+ if 'id' in results['qsd'] and len(results['qsd']['id']):
+ # Shortened version of integration id
+ results['integration'] = \
+ NotifyPagerTree.unquote(results['qsd']['id'])
+
+ elif 'integration' in results['qsd'] and \
+ len(results['qsd']['integration']):
+ results['integration'] = \
+ NotifyPagerTree.unquote(results['qsd']['integration'])
+
+ else:
+ results['integration'] = \
+ NotifyPagerTree.unquote(results['host'])
+
+ # Set our thirdparty
+
+ if 'tid' in results['qsd'] and len(results['qsd']['tid']):
+ # Shortened version of thirdparty
+ results['thirdparty'] = \
+ NotifyPagerTree.unquote(results['qsd']['tid'])
+
+ elif 'thirdparty' in results['qsd'] and \
+ len(results['qsd']['thirdparty']):
+ results['thirdparty'] = \
+ NotifyPagerTree.unquote(results['qsd']['thirdparty'])
+
+ # Set our urgency
+ if 'action' in results['qsd'] and \
+ len(results['qsd']['action']):
+ results['action'] = \
+ NotifyPagerTree.unquote(results['qsd']['action'])
+
+ # Set our urgency
+ if 'urgency' in results['qsd'] and len(results['qsd']['urgency']):
+ results['urgency'] = \
+ NotifyPagerTree.unquote(results['qsd']['urgency'])
+
+ # Set our tags
+ if 'tags' in results['qsd'] and len(results['qsd']['tags']):
+ results['tags'] = \
+ parse_list(NotifyPagerTree.unquote(results['qsd']['tags']))
+
+ return results
diff --git a/libs/apprise/plugins/NotifyParsePlatform.py b/libs/apprise/plugins/NotifyParsePlatform.py
index 40582a8dc..69efb61c7 100644
--- a/libs/apprise/plugins/NotifyParsePlatform.py
+++ b/libs/apprise/plugins/NotifyParsePlatform.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2020 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
# Official API reference: https://developer.gitter.im/docs/user-resource
@@ -130,7 +137,7 @@ class NotifyParsePlatform(NotifyBase):
"""
Initialize Parse Platform Object
"""
- super(NotifyParsePlatform, self).__init__(**kwargs)
+ super().__init__(**kwargs)
self.fullpath = kwargs.get('fullpath')
if not isinstance(self.fullpath, str):
diff --git a/libs/apprise/plugins/NotifyPopcornNotify.py b/libs/apprise/plugins/NotifyPopcornNotify.py
index 7352c6676..a36aed9f9 100644
--- a/libs/apprise/plugins/NotifyPopcornNotify.py
+++ b/libs/apprise/plugins/NotifyPopcornNotify.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2020 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
import requests
@@ -105,7 +112,7 @@ class NotifyPopcornNotify(NotifyBase):
"""
Initialize PopcornNotify Object
"""
- super(NotifyPopcornNotify, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# Access Token (associated with project)
self.apikey = validate_regex(
@@ -258,6 +265,21 @@ class NotifyPopcornNotify(NotifyBase):
[NotifyPopcornNotify.quote(x, safe='') for x in self.targets]),
params=NotifyPopcornNotify.urlencode(params))
+ def __len__(self):
+ """
+ Returns the number of targets associated with this notification
+ """
+ #
+ # Factor batch into calculation
+ #
+ batch_size = 1 if not self.batch else self.default_batch_size
+ targets = len(self.targets)
+ if batch_size > 1:
+ targets = int(targets / batch_size) + \
+ (1 if targets % batch_size else 0)
+
+ return targets
+
@staticmethod
def parse_url(url):
"""
diff --git a/libs/apprise/plugins/NotifyProwl.py b/libs/apprise/plugins/NotifyProwl.py
index b41d7566a..cebe07010 100644
--- a/libs/apprise/plugins/NotifyProwl.py
+++ b/libs/apprise/plugins/NotifyProwl.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
import requests
@@ -143,7 +150,7 @@ class NotifyProwl(NotifyBase):
"""
Initialize Prowl Object
"""
- super(NotifyProwl, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# The Priority of the message
self.priority = NotifyProwl.template_args['priority']['default'] \
diff --git a/libs/apprise/plugins/NotifyPushBullet.py b/libs/apprise/plugins/NotifyPushBullet.py
index 53240d2e7..07b2a43a0 100644
--- a/libs/apprise/plugins/NotifyPushBullet.py
+++ b/libs/apprise/plugins/NotifyPushBullet.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
import requests
from json import dumps
@@ -115,7 +122,7 @@ class NotifyPushBullet(NotifyBase):
"""
Initialize PushBullet Object
"""
- super(NotifyPushBullet, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# Access Token (associated with project)
self.accesstoken = validate_regex(accesstoken)
@@ -399,6 +406,12 @@ class NotifyPushBullet(NotifyBase):
targets=targets,
params=NotifyPushBullet.urlencode(params))
+ def __len__(self):
+ """
+ Returns the number of targets associated with this notification
+ """
+ return len(self.targets)
+
@staticmethod
def parse_url(url):
"""
diff --git a/libs/apprise/plugins/NotifyPushSafer.py b/libs/apprise/plugins/NotifyPushSafer.py
index 3f495702a..19bff2bd0 100644
--- a/libs/apprise/plugins/NotifyPushSafer.py
+++ b/libs/apprise/plugins/NotifyPushSafer.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
import base64
import requests
@@ -400,7 +407,7 @@ class NotifyPushSafer(NotifyBase):
"""
Initialize PushSafer Object
"""
- super(NotifyPushSafer, self).__init__(**kwargs)
+ super().__init__(**kwargs)
#
# Priority
@@ -787,6 +794,12 @@ class NotifyPushSafer(NotifyBase):
targets=targets,
params=NotifyPushSafer.urlencode(params))
+ def __len__(self):
+ """
+ Returns the number of targets associated with this notification
+ """
+ return len(self.targets)
+
@staticmethod
def parse_url(url):
"""
diff --git a/libs/apprise/plugins/NotifyPushed.py b/libs/apprise/plugins/NotifyPushed.py
index c6dfe6ad1..b5ec3f6de 100644
--- a/libs/apprise/plugins/NotifyPushed.py
+++ b/libs/apprise/plugins/NotifyPushed.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
import re
import requests
@@ -120,7 +127,7 @@ class NotifyPushed(NotifyBase):
Initialize Pushed Object
"""
- super(NotifyPushed, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# Application Key (associated with project)
self.app_key = validate_regex(app_key)
@@ -322,6 +329,13 @@ class NotifyPushed(NotifyBase):
)]),
params=NotifyPushed.urlencode(params))
+ def __len__(self):
+ """
+ Returns the number of targets associated with this notification
+ """
+ targets = len(self.channels) + len(self.users)
+ return targets if targets > 0 else 1
+
@staticmethod
def parse_url(url):
"""
diff --git a/libs/apprise/plugins/NotifyPushjet.py b/libs/apprise/plugins/NotifyPushjet.py
index 49e6596e6..c6e36a393 100644
--- a/libs/apprise/plugins/NotifyPushjet.py
+++ b/libs/apprise/plugins/NotifyPushjet.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
import requests
from json import dumps
@@ -102,7 +109,7 @@ class NotifyPushjet(NotifyBase):
"""
Initialize Pushjet Object
"""
- super(NotifyPushjet, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# Secret Key (associated with project)
self.secret_key = validate_regex(secret_key)
diff --git a/libs/apprise/plugins/NotifyPushover.py b/libs/apprise/plugins/NotifyPushover.py
index 1dfb5787b..64b94774c 100644
--- a/libs/apprise/plugins/NotifyPushover.py
+++ b/libs/apprise/plugins/NotifyPushover.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
import re
import requests
@@ -158,7 +165,7 @@ class NotifyPushover(NotifyBase):
notify_url = 'https://api.pushover.net/1/messages.json'
# The maximum allowable characters allowed in the body per message
- body_maxlen = 512
+ body_maxlen = 1024
# Default Pushover sound
default_pushover_sound = PushoverSound.PUSHOVER
@@ -251,7 +258,7 @@ class NotifyPushover(NotifyBase):
"""
Initialize Pushover Object
"""
- super(NotifyPushover, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# Access Token (associated with project)
self.token = validate_regex(token)
@@ -570,6 +577,12 @@ class NotifyPushover(NotifyBase):
devices=devices,
params=NotifyPushover.urlencode(params))
+ def __len__(self):
+ """
+ Returns the number of targets associated with this notification
+ """
+ return len(self.targets)
+
@staticmethod
def parse_url(url):
"""
diff --git a/libs/apprise/plugins/NotifyReddit.py b/libs/apprise/plugins/NotifyReddit.py
index e5709bf89..5cb22a726 100644
--- a/libs/apprise/plugins/NotifyReddit.py
+++ b/libs/apprise/plugins/NotifyReddit.py
@@ -1,27 +1,35 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2021 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
#
# 1. Visit https://www.reddit.com/prefs/apps and scroll to the bottom
# 2. Click on the button that reads 'are you a developer? create an app...'
@@ -240,7 +248,7 @@ class NotifyReddit(NotifyBase):
"""
Initialize Notify Reddit Object
"""
- super(NotifyReddit, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# Initialize subreddit list
self.subreddits = set()
@@ -359,6 +367,12 @@ class NotifyReddit(NotifyBase):
params=NotifyReddit.urlencode(params),
)
+ def __len__(self):
+ """
+ Returns the number of targets associated with this notification
+ """
+ return len(self.subreddits)
+
def login(self):
"""
A simple wrapper to authenticate with the Reddit Server
diff --git a/libs/apprise/plugins/NotifyRocketChat.py b/libs/apprise/plugins/NotifyRocketChat.py
index b8337ff59..ca6b5cd83 100644
--- a/libs/apprise/plugins/NotifyRocketChat.py
+++ b/libs/apprise/plugins/NotifyRocketChat.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
import re
import requests
@@ -48,10 +55,6 @@ RC_HTTP_ERROR_MAP = {
401: 'Authentication tokens provided is invalid or missing.',
}
-# Used to break apart list of potential tags by their delimiter
-# into a usable list.
-LIST_DELIM = re.compile(r'[ \t\r\n,\\/]+')
-
class RocketChatAuthMode:
"""
@@ -188,7 +191,7 @@ class NotifyRocketChat(NotifyBase):
"""
Initialize Notify Rocket.Chat Object
"""
- super(NotifyRocketChat, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# Set our schema
self.schema = 'https' if self.secure else 'http'
@@ -320,7 +323,8 @@ class NotifyRocketChat(NotifyBase):
auth = '{user}{webhook}@'.format(
user='{}:'.format(NotifyRocketChat.quote(self.user, safe=''))
if self.user else '',
- webhook=self.pprint(self.webhook, privacy, safe=''),
+ webhook=self.pprint(self.webhook, privacy,
+ mode=PrivacyMode.Secret, safe=''),
)
default_port = 443 if self.secure else 80
@@ -333,7 +337,7 @@ class NotifyRocketChat(NotifyBase):
port='' if self.port is None or self.port == default_port
else ':{}'.format(self.port),
targets='/'.join(
- [NotifyRocketChat.quote(x, safe='') for x in chain(
+ [NotifyRocketChat.quote(x, safe='@#') for x in chain(
# Channels are prefixed with a pound/hashtag symbol
['#{}'.format(x) for x in self.channels],
# Rooms are as is
@@ -344,6 +348,13 @@ class NotifyRocketChat(NotifyBase):
params=NotifyRocketChat.urlencode(params),
)
+ def __len__(self):
+ """
+ Returns the number of targets associated with this notification
+ """
+ targets = len(self.channels) + len(self.rooms) + len(self.users)
+ return targets if targets > 0 else 1
+
def send(self, body, title='', notify_type=NotifyType.INFO, **kwargs):
"""
wrapper to _send since we can alert more then one channel
diff --git a/libs/apprise/plugins/NotifyRyver.py b/libs/apprise/plugins/NotifyRyver.py
index e186a84bb..b8b34a3c4 100644
--- a/libs/apprise/plugins/NotifyRyver.py
+++ b/libs/apprise/plugins/NotifyRyver.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
# To use this plugin, you need to first generate a webhook.
@@ -129,7 +136,7 @@ class NotifyRyver(NotifyBase):
"""
Initialize Ryver Object
"""
- super(NotifyRyver, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# API Token (associated with project)
self.token = validate_regex(
diff --git a/libs/apprise/plugins/NotifySES.py b/libs/apprise/plugins/NotifySES.py
index 1c14d34a6..fb0017036 100644
--- a/libs/apprise/plugins/NotifySES.py
+++ b/libs/apprise/plugins/NotifySES.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2021 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
# API Information:
# - https://docs.aws.amazon.com/ses/latest/APIReference/API_SendRawEmail.html
@@ -217,7 +224,7 @@ class NotifySES(NotifyBase):
"""
Initialize Notify AWS SES Object
"""
- super(NotifySES, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# Store our AWS API Access Key
self.aws_access_key_id = validate_regex(access_key_id)
@@ -809,6 +816,13 @@ class NotifySES(NotifyBase):
params=NotifySES.urlencode(params),
)
+ def __len__(self):
+ """
+ Returns the number of targets associated with this notification
+ """
+ targets = len(self.targets)
+ return targets if targets > 0 else 1
+
@staticmethod
def parse_url(url):
"""
diff --git a/libs/apprise/plugins/NotifySMSEagle.py b/libs/apprise/plugins/NotifySMSEagle.py
index 40e562691..747831e10 100644
--- a/libs/apprise/plugins/NotifySMSEagle.py
+++ b/libs/apprise/plugins/NotifySMSEagle.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2022 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
import re
import requests
@@ -66,6 +73,22 @@ SMSEAGLE_PRIORITY_MAP = {
}
+class SMSEagleCategory:
+ """
+ We define the different category types that we can notify via SMS Eagle
+ """
+ PHONE = 'phone'
+ GROUP = 'group'
+ CONTACT = 'contact'
+
+
+SMSEAGLE_CATEGORIES = (
+ SMSEagleCategory.PHONE,
+ SMSEagleCategory.GROUP,
+ SMSEagleCategory.CONTACT,
+)
+
+
class NotifySMSEagle(NotifyBase):
"""
A wrapper for SMSEagle Notifications
@@ -191,7 +214,7 @@ class NotifySMSEagle(NotifyBase):
"""
Initialize SMSEagle Object
"""
- super(NotifySMSEagle, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# Prepare Flash Mode Flag
self.flash = flash
@@ -396,15 +419,15 @@ class NotifySMSEagle(NotifyBase):
batch_size = 1 if not self.batch else self.default_batch_size
notify_by = {
- 'phone': {
+ SMSEagleCategory.PHONE: {
"method": "sms.send_sms",
'target': 'to',
},
- 'group': {
+ SMSEagleCategory.GROUP: {
"method": "sms.send_togroup",
'target': 'groupname',
},
- 'contact': {
+ SMSEagleCategory.CONTACT: {
"method": "sms.send_tocontact",
'target': 'contactname',
},
@@ -413,7 +436,7 @@ class NotifySMSEagle(NotifyBase):
# categories separated into a tuple since notify_by.keys()
# returns an unpredicable list in Python 2.7 which causes
# tests to fail every so often
- for category in ('phone', 'group', 'contact'):
+ for category in SMSEAGLE_CATEGORIES:
# Create a copy of our template
payload = {
'method': notify_by[category]['method'],
@@ -583,10 +606,34 @@ class NotifySMSEagle(NotifyBase):
['@{}'.format(x) for x in self.target_contacts],
# Groups
['#{}'.format(x) for x in self.target_groups],
+ # Pass along the same invalid entries as were provided
+ self.invalid_targets,
)]),
params=NotifySMSEagle.urlencode(params),
)
+ def __len__(self):
+ """
+ Returns the number of targets associated with this notification
+ """
+ #
+ # Factor batch into calculation
+ #
+ batch_size = 1 if not self.batch else self.default_batch_size
+ if batch_size > 1:
+ # Batches can only be sent by group (you can't combine groups into
+ # a single batch)
+ total_targets = 0
+ for c in SMSEAGLE_CATEGORIES:
+ targets = len(getattr(self, f'target_{c}s'))
+ total_targets += int(targets / batch_size) + \
+ (1 if targets % batch_size else 0)
+ return total_targets
+
+ # Normal batch count; just count the targets
+ return len(self.target_phones) + len(self.target_contacts) + \
+ len(self.target_groups)
+
@staticmethod
def parse_url(url):
"""
@@ -633,6 +680,7 @@ class NotifySMSEagle(NotifyBase):
results['status'] = \
parse_bool(results['qsd'].get('status', False))
+ # Get priority
if 'priority' in results['qsd'] and len(results['qsd']['priority']):
results['priority'] = \
NotifySMSEagle.unquote(results['qsd']['priority'])
diff --git a/libs/apprise/plugins/NotifySMTP2Go.py b/libs/apprise/plugins/NotifySMTP2Go.py
index 952604a7b..3634ba6a8 100644
--- a/libs/apprise/plugins/NotifySMTP2Go.py
+++ b/libs/apprise/plugins/NotifySMTP2Go.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2021 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
# Signup @ https://smtp2go.com (free accounts available)
#
@@ -159,7 +166,7 @@ class NotifySMTP2Go(NotifyBase):
"""
Initialize SMTP2Go Object
"""
- super(NotifySMTP2Go, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# API Key (associated with project)
self.apikey = validate_regex(apikey)
@@ -506,6 +513,21 @@ class NotifySMTP2Go(NotifyBase):
safe='') for e in self.targets]),
params=NotifySMTP2Go.urlencode(params))
+ def __len__(self):
+ """
+ Returns the number of targets associated with this notification
+ """
+ #
+ # Factor batch into calculation
+ #
+ batch_size = 1 if not self.batch else self.default_batch_size
+ targets = len(self.targets)
+ if batch_size > 1:
+ targets = int(targets / batch_size) + \
+ (1 if targets % batch_size else 0)
+
+ return targets if targets > 0 else 1
+
@staticmethod
def parse_url(url):
"""
diff --git a/libs/apprise/plugins/NotifySNS.py b/libs/apprise/plugins/NotifySNS.py
index 8af0847a2..c1d2ed932 100644
--- a/libs/apprise/plugins/NotifySNS.py
+++ b/libs/apprise/plugins/NotifySNS.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
import re
import hmac
@@ -159,7 +166,7 @@ class NotifySNS(NotifyBase):
"""
Initialize Notify AWS SNS Object
"""
- super(NotifySNS, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# Store our AWS API Access Key
self.aws_access_key_id = validate_regex(access_key_id)
@@ -593,6 +600,12 @@ class NotifySNS(NotifyBase):
params=NotifySNS.urlencode(params),
)
+ def __len__(self):
+ """
+ Returns the number of targets associated with this notification
+ """
+ return len(self.phone) + len(self.topics)
+
@staticmethod
def parse_url(url):
"""
diff --git a/libs/apprise/plugins/NotifySendGrid.py b/libs/apprise/plugins/NotifySendGrid.py
index 12f829fb3..d1ae8a4d4 100644
--- a/libs/apprise/plugins/NotifySendGrid.py
+++ b/libs/apprise/plugins/NotifySendGrid.py
@@ -1,27 +1,35 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
#
# You will need an API Key for this plugin to work.
# From the Settings -> API Keys you can click "Create API Key" if you don't
@@ -159,7 +167,7 @@ class NotifySendGrid(NotifyBase):
"""
Initialize Notify SendGrid Object
"""
- super(NotifySendGrid, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# API Key (associated with project)
self.apikey = validate_regex(
@@ -279,6 +287,12 @@ class NotifySendGrid(NotifyBase):
params=NotifySendGrid.urlencode(params),
)
+ def __len__(self):
+ """
+ Returns the number of targets associated with this notification
+ """
+ return len(self.targets)
+
def send(self, body, title='', notify_type=NotifyType.INFO, **kwargs):
"""
Perform SendGrid Notification
diff --git a/libs/apprise/plugins/NotifyServerChan.py b/libs/apprise/plugins/NotifyServerChan.py
index 2af40d73f..6fa8c5570 100644
--- a/libs/apprise/plugins/NotifyServerChan.py
+++ b/libs/apprise/plugins/NotifyServerChan.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2020 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
import re
import requests
@@ -79,7 +86,7 @@ class NotifyServerChan(NotifyBase):
"""
Initialize ServerChan Object
"""
- super(NotifyServerChan, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# Token (associated with project)
self.token = validate_regex(
diff --git a/libs/apprise/plugins/NotifySignalAPI.py b/libs/apprise/plugins/NotifySignalAPI.py
index a24425093..589499f8d 100644
--- a/libs/apprise/plugins/NotifySignalAPI.py
+++ b/libs/apprise/plugins/NotifySignalAPI.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2022 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
import re
import requests
@@ -157,7 +164,7 @@ class NotifySignalAPI(NotifyBase):
"""
Initialize SignalAPI Object
"""
- super(NotifySignalAPI, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# Prepare Batch Mode Flag
self.batch = batch
@@ -424,6 +431,21 @@ class NotifySignalAPI(NotifyBase):
params=NotifySignalAPI.urlencode(params),
)
+ def __len__(self):
+ """
+ Returns the number of targets associated with this notification
+ """
+ #
+ # Factor batch into calculation
+ #
+ batch_size = 1 if not self.batch else self.default_batch_size
+ targets = len(self.targets)
+ if batch_size > 1:
+ targets = int(targets / batch_size) + \
+ (1 if targets % batch_size else 0)
+
+ return targets
+
@staticmethod
def parse_url(url):
"""
diff --git a/libs/apprise/plugins/NotifySimplePush.py b/libs/apprise/plugins/NotifySimplePush.py
index 400216e72..25066067c 100644
--- a/libs/apprise/plugins/NotifySimplePush.py
+++ b/libs/apprise/plugins/NotifySimplePush.py
@@ -1,27 +1,35 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
from os import urandom
from json import loads
import requests
@@ -125,7 +133,7 @@ class NotifySimplePush(NotifyBase):
"""
Initialize SimplePush Object
"""
- super(NotifySimplePush, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# API Key (associated with project)
self.apikey = validate_regex(apikey)
diff --git a/libs/apprise/plugins/NotifySinch.py b/libs/apprise/plugins/NotifySinch.py
index f911cdb71..0756f76b3 100644
--- a/libs/apprise/plugins/NotifySinch.py
+++ b/libs/apprise/plugins/NotifySinch.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
# To use this service you will need a Sinch account to which you can get your
# API_TOKEN and SERVICE_PLAN_ID right from your console/dashboard at:
@@ -169,7 +176,7 @@ class NotifySinch(NotifyBase):
"""
Initialize Sinch Object
"""
- super(NotifySinch, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# The Account SID associated with the account
self.service_plan_id = validate_regex(
@@ -401,6 +408,13 @@ class NotifySinch(NotifyBase):
[NotifySinch.quote(x, safe='') for x in self.targets]),
params=NotifySinch.urlencode(params))
+ def __len__(self):
+ """
+ Returns the number of targets associated with this notification
+ """
+ targets = len(self.targets)
+ return targets if targets > 0 else 1
+
@staticmethod
def parse_url(url):
"""
diff --git a/libs/apprise/plugins/NotifySlack.py b/libs/apprise/plugins/NotifySlack.py
index 3b0c733a3..0d85d25fe 100644
--- a/libs/apprise/plugins/NotifySlack.py
+++ b/libs/apprise/plugins/NotifySlack.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
# There are 2 ways to use this plugin...
# Method 1: Via Webhook:
@@ -278,7 +285,7 @@ class NotifySlack(NotifyBase):
"""
Initialize Slack Object
"""
- super(NotifySlack, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# Setup our mode
self.mode = SlackMode.BOT if access_token else SlackMode.WEBHOOK
@@ -347,6 +354,22 @@ class NotifySlack(NotifyBase):
r'>': '&gt;',
}
+ # To notify a channel, one uses <!channel|channel>
+ self._re_channel_support = re.compile(
+ r'(?P<match>(?:<|\&lt;)?[ \t]*'
+ r'!(?P<channel>[^| \n]+)'
+ r'(?:[ \t]*\|[ \t]*(?:(?P<val>[^\n]+?)[ \t]*)?(?:>|\&gt;)'
+ r'|(?:>|\&gt;)))', re.IGNORECASE)
+
+ # The markdown in slack isn't [desc](url), it's <url|desc>
+ #
+ # To accomodate this, we need to ensure we don't escape URLs that match
+ self._re_url_support = re.compile(
+ r'(?P<match>(?:<|\&lt;)?[ \t]*'
+ r'(?P<url>(?:https?|mailto)://[^| \n]+)'
+ r'(?:[ \t]*\|[ \t]*(?:(?P<val>[^\n]+?)[ \t]*)?(?:>|\&gt;)'
+ r'|(?:>|\&gt;)))', re.IGNORECASE)
+
# Iterate over above list and store content accordingly
self._re_formatting_rules = re.compile(
r'(' + '|'.join(self._re_formatting_map.keys()) + r')',
@@ -439,6 +462,35 @@ class NotifySlack(NotifyBase):
lambda x: self._re_formatting_map[x.group()], body,
)
+ # Support <!channel|desc>, <!channel> entries
+ for match in self._re_channel_support.findall(body):
+ # Swap back any ampersands previously updaated
+ channel = match[1].strip()
+ desc = match[2].strip()
+
+ # Update our string
+ body = re.sub(
+ re.escape(match[0]),
+ '<!{channel}|{desc}>'.format(
+ channel=channel, desc=desc)
+ if desc else '<!{channel}>'.format(channel=channel),
+ body,
+ re.IGNORECASE)
+
+ # Support <url|desc>, <url> entries
+ for match in self._re_url_support.findall(body):
+ # Swap back any ampersands previously updaated
+ url = match[1].replace('&amp;', '&')
+ desc = match[2].strip()
+
+ # Update our string
+ body = re.sub(
+ re.escape(match[0]),
+ '<{url}|{desc}>'.format(url=url, desc=desc)
+ if desc else '<{url}>'.format(url=url),
+ body,
+ re.IGNORECASE)
+
# Perform Formatting on title here; this is not needed for block
# mode above
title = self._re_formatting_rules.sub( # pragma: no branch
@@ -803,7 +855,7 @@ class NotifySlack(NotifyBase):
# The text 'ok' is returned if this is a Webhook request
# So the below captures that as well.
status_okay = (response and response.get('ok', False)) \
- if self.mode is SlackMode.BOT else r.text == 'ok'
+ if self.mode is SlackMode.BOT else r.content == b'ok'
if r.status_code != requests.codes.ok or not status_okay:
# We had a problem
@@ -963,6 +1015,12 @@ class NotifySlack(NotifyBase):
params=NotifySlack.urlencode(params),
)
+ def __len__(self):
+ """
+ Returns the number of targets associated with this notification
+ """
+ return len(self.channels)
+
@staticmethod
def parse_url(url):
"""
diff --git a/libs/apprise/plugins/NotifySparkPost.py b/libs/apprise/plugins/NotifySparkPost.py
index 93ce84d52..25024bc5f 100644
--- a/libs/apprise/plugins/NotifySparkPost.py
+++ b/libs/apprise/plugins/NotifySparkPost.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2020 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
# Signup @ https://www.sparkpost.com
#
@@ -79,8 +86,10 @@ SPARKPOST_HTTP_ERROR_MAP = {
}
-# Priorities
class SparkPostRegion:
+ """
+ Regions
+ """
US = 'us'
EU = 'eu'
@@ -141,9 +150,6 @@ class NotifySparkPost(NotifyBase):
# Default Notify Format
notify_format = NotifyFormat.HTML
- # The default region to use if one isn't otherwise specified
- sparkpost_default_region = SparkPostRegion.US
-
# Define object templates
templates = (
'{schema}://{user}@{host}:{apikey}/',
@@ -224,7 +230,7 @@ class NotifySparkPost(NotifyBase):
"""
Initialize SparkPost Object
"""
- super(NotifySparkPost, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# API Key (associated with project)
self.apikey = validate_regex(apikey)
@@ -254,7 +260,7 @@ class NotifySparkPost(NotifyBase):
# Store our region
try:
- self.region_name = self.sparkpost_default_region \
+ self.region_name = self.template_args['region']['default'] \
if region_name is None else region_name.lower()
if self.region_name not in SPARKPOST_REGIONS:
@@ -716,6 +722,21 @@ class NotifySparkPost(NotifyBase):
safe='') for e in self.targets]),
params=NotifySparkPost.urlencode(params))
+ def __len__(self):
+ """
+ Returns the number of targets associated with this notification
+ """
+ #
+ # Factor batch into calculation
+ #
+ batch_size = 1 if not self.batch else self.default_batch_size
+ targets = len(self.targets)
+ if batch_size > 1:
+ targets = int(targets / batch_size) + \
+ (1 if targets % batch_size else 0)
+
+ return targets if targets > 0 else 1
+
@staticmethod
def parse_url(url):
"""
@@ -746,7 +767,7 @@ class NotifySparkPost(NotifyBase):
NotifySparkPost.unquote(results['qsd']['name'])
if 'region' in results['qsd'] and len(results['qsd']['region']):
- # Extract from name to associate with from address
+ # Extract region
results['region_name'] = \
NotifySparkPost.unquote(results['qsd']['region'])
diff --git a/libs/apprise/plugins/NotifySpontit.py b/libs/apprise/plugins/NotifySpontit.py
index 59c708bde..01d4e1980 100644
--- a/libs/apprise/plugins/NotifySpontit.py
+++ b/libs/apprise/plugins/NotifySpontit.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2020 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
# To use this service you will need a Spontit account from their website
# at https://spontit.com/
@@ -148,7 +155,7 @@ class NotifySpontit(NotifyBase):
"""
Initialize Spontit Object
"""
- super(NotifySpontit, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# User ID (associated with project)
user = validate_regex(
@@ -343,6 +350,13 @@ class NotifySpontit(NotifyBase):
[NotifySpontit.quote(x, safe='') for x in self.targets]),
params=NotifySpontit.urlencode(params))
+ def __len__(self):
+ """
+ Returns the number of targets associated with this notification
+ """
+ targets = len(self.targets)
+ return targets if targets > 0 else 1
+
@staticmethod
def parse_url(url):
"""
diff --git a/libs/apprise/plugins/NotifyStreamlabs.py b/libs/apprise/plugins/NotifyStreamlabs.py
index e4217151f..3489519a5 100644
--- a/libs/apprise/plugins/NotifyStreamlabs.py
+++ b/libs/apprise/plugins/NotifyStreamlabs.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2021 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
# For this to work correctly you need to register an app
# and generate an access token
@@ -183,7 +190,7 @@ class NotifyStreamlabs(NotifyBase):
Initialize Streamlabs Object
"""
- super(NotifyStreamlabs, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# access token is generated by user
# using https://streamlabs.com/api/v1.0/token
diff --git a/libs/apprise/plugins/NotifySyslog.py b/libs/apprise/plugins/NotifySyslog.py
index 20d20b192..433aab9c5 100644
--- a/libs/apprise/plugins/NotifySyslog.py
+++ b/libs/apprise/plugins/NotifySyslog.py
@@ -1,27 +1,35 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
import os
import syslog
import socket
@@ -199,7 +207,7 @@ class NotifySyslog(NotifyBase):
"""
Initialize Syslog Object
"""
- super(NotifySyslog, self).__init__(**kwargs)
+ super().__init__(**kwargs)
if facility:
try:
diff --git a/libs/apprise/plugins/NotifyTechulusPush.py b/libs/apprise/plugins/NotifyTechulusPush.py
index 5dcb33e5f..0f3e79e53 100644
--- a/libs/apprise/plugins/NotifyTechulusPush.py
+++ b/libs/apprise/plugins/NotifyTechulusPush.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
# To use this plugin, you need to download the app
# - Apple: https://itunes.apple.com/us/app/\
@@ -104,7 +111,7 @@ class NotifyTechulusPush(NotifyBase):
"""
Initialize Techulus Push Object
"""
- super(NotifyTechulusPush, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# The apikey associated with the account
self.apikey = validate_regex(
diff --git a/libs/apprise/plugins/NotifyTelegram.py b/libs/apprise/plugins/NotifyTelegram.py
index 1317d7ca0..d5a52be60 100644
--- a/libs/apprise/plugins/NotifyTelegram.py
+++ b/libs/apprise/plugins/NotifyTelegram.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
# To use this plugin, you need to first access https://api.telegram.org
# You need to create a bot and acquire it's Token Identifier (bot_token)
@@ -305,17 +312,22 @@ class NotifyTelegram(NotifyBase):
'type': 'bool',
'default': False,
},
+ 'topic': {
+ 'name': _('Topic Thread ID'),
+ 'type': 'int',
+ },
'to': {
'alias_of': 'targets',
},
})
def __init__(self, bot_token, targets, detect_owner=True,
- include_image=False, silent=None, preview=None, **kwargs):
+ include_image=False, silent=None, preview=None, topic=None,
+ **kwargs):
"""
Initialize Telegram Object
"""
- super(NotifyTelegram, self).__init__(**kwargs)
+ super().__init__(**kwargs)
self.bot_token = validate_regex(
bot_token, *self.template_tokens['bot_token']['regex'],
@@ -337,6 +349,20 @@ class NotifyTelegram(NotifyBase):
self.preview = self.template_args['preview']['default'] \
if preview is None else bool(preview)
+ if topic:
+ try:
+ self.topic = int(topic)
+
+ except (TypeError, ValueError):
+ # Not a valid integer; ignore entry
+ err = 'The Telegram Topic ID specified ({}) is invalid.'\
+ .format(topic)
+ self.logger.warning(err)
+ raise TypeError(err)
+ else:
+ # No Topic Thread
+ self.topic = None
+
# if detect_owner is set to True, we will attempt to determine who
# the bot owner is based on the first person who messaged it. This
# is not a fool proof way of doing things as over time Telegram removes
@@ -628,6 +654,9 @@ class NotifyTelegram(NotifyBase):
'disable_web_page_preview': not self.preview,
}
+ if self.topic:
+ payload['message_thread_id'] = self.topic
+
# Prepare Message Body
if self.notify_format == NotifyFormat.MARKDOWN:
payload['parse_mode'] = 'MARKDOWN'
@@ -775,6 +804,9 @@ class NotifyTelegram(NotifyBase):
'preview': 'yes' if self.preview else 'no',
}
+ if self.topic:
+ params['topic'] = self.topic
+
# Extend our parameters
params.update(self.url_parameters(privacy=privacy, *args, **kwargs))
@@ -787,6 +819,12 @@ class NotifyTelegram(NotifyBase):
[NotifyTelegram.quote('@{}'.format(x)) for x in self.targets]),
params=NotifyTelegram.urlencode(params))
+ def __len__(self):
+ """
+ Returns the number of targets associated with this notification
+ """
+ return len(self.targets)
+
@staticmethod
def parse_url(url):
"""
@@ -856,6 +894,10 @@ class NotifyTelegram(NotifyBase):
# Store our bot token
results['bot_token'] = bot_token
+ # Support Thread Topic
+ if 'topic' in results['qsd'] and len(results['qsd']['topic']):
+ results['topic'] = results['qsd']['topic']
+
# Silent (Sends the message Silently); users will receive
# notification with no sound.
results['silent'] = \
diff --git a/libs/apprise/plugins/NotifyTwilio.py b/libs/apprise/plugins/NotifyTwilio.py
index 883cc50ba..08a3b2917 100644
--- a/libs/apprise/plugins/NotifyTwilio.py
+++ b/libs/apprise/plugins/NotifyTwilio.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
# To use this service you will need a Twilio account to which you can get your
# AUTH_TOKEN and ACCOUNT SID right from your console/dashboard at:
@@ -163,7 +170,7 @@ class NotifyTwilio(NotifyBase):
"""
Initialize Twilio Object
"""
- super(NotifyTwilio, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# The Account SID associated with the account
self.account_sid = validate_regex(
@@ -378,6 +385,13 @@ class NotifyTwilio(NotifyBase):
[NotifyTwilio.quote(x, safe='') for x in self.targets]),
params=NotifyTwilio.urlencode(params))
+ def __len__(self):
+ """
+ Returns the number of targets associated with this notification
+ """
+ targets = len(self.targets)
+ return targets if targets > 0 else 1
+
@staticmethod
def parse_url(url):
"""
diff --git a/libs/apprise/plugins/NotifyTwist.py b/libs/apprise/plugins/NotifyTwist.py
index d11208680..ea7b19760 100644
--- a/libs/apprise/plugins/NotifyTwist.py
+++ b/libs/apprise/plugins/NotifyTwist.py
@@ -1,27 +1,35 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
#
# All of the documentation needed to work with the Twist API can be found
# here: https://developer.twist.com/v3/
@@ -131,7 +139,7 @@ class NotifyTwist(NotifyBase):
"""
Initialize Notify Twist Object
"""
- super(NotifyTwist, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# Initialize channels list
self.channels = set()
@@ -248,6 +256,12 @@ class NotifyTwist(NotifyBase):
params=NotifyTwist.urlencode(params),
)
+ def __len__(self):
+ """
+ Returns the number of targets associated with this notification
+ """
+ return len(self.channels) + len(self.channel_ids)
+
def login(self):
"""
A simple wrapper to authenticate with the Twist Server
diff --git a/libs/apprise/plugins/NotifyTwitter.py b/libs/apprise/plugins/NotifyTwitter.py
index f26d30fa4..7862d0042 100644
--- a/libs/apprise/plugins/NotifyTwitter.py
+++ b/libs/apprise/plugins/NotifyTwitter.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
# See https://developer.twitter.com/en/docs/direct-messages/\
# sending-and-receiving/api-reference/new-event.html
@@ -75,7 +82,7 @@ class NotifyTwitter(NotifyBase):
service_url = 'https://twitter.com/'
# The default secure protocol is twitter.
- secure_protocol = 'twitter'
+ secure_protocol = ('twitter', 'tweet')
# A URL that takes you to the setup/help of the specific protocol
setup_url = 'https://github.com/caronc/apprise/wiki/Notify_twitter'
@@ -194,7 +201,7 @@ class NotifyTwitter(NotifyBase):
Initialize Twitter Object
"""
- super(NotifyTwitter, self).__init__(**kwargs)
+ super().__init__(**kwargs)
self.ckey = validate_regex(ckey)
if not self.ckey:
@@ -221,21 +228,21 @@ class NotifyTwitter(NotifyBase):
raise TypeError(msg)
# Store our webhook mode
- self.mode = None \
+ self.mode = self.template_args['mode']['default'] \
if not isinstance(mode, str) else mode.lower()
- # Set Cache Flag
- self.cache = cache
-
- # Prepare Image Batch Mode Flag
- self.batch = batch
-
if self.mode not in TWITTER_MESSAGE_MODES:
msg = 'The Twitter message mode specified ({}) is invalid.' \
.format(mode)
self.logger.warning(msg)
raise TypeError(msg)
+ # Set Cache Flag
+ self.cache = cache
+
+ # Prepare Image Batch Mode Flag
+ self.batch = batch
+
# Track any errors
has_error = False
@@ -249,7 +256,7 @@ class NotifyTwitter(NotifyBase):
has_error = True
self.logger.warning(
- 'Dropped invalid user ({}) specified.'.format(target),
+ 'Dropped invalid Twitter user ({}) specified.'.format(target),
)
if has_error and not self.targets:
@@ -261,6 +268,10 @@ class NotifyTwitter(NotifyBase):
self.logger.warning(msg)
raise TypeError(msg)
+ # Initialize our cache values
+ self._whoami_cache = None
+ self._user_cache = {}
+
return
def send(self, body, title='', notify_type=NotifyType.INFO, attach=None,
@@ -293,7 +304,7 @@ class NotifyTwitter(NotifyBase):
continue
self.logger.debug(
- 'Preparing Twiter attachment {}'.format(
+ 'Preparing Twitter attachment {}'.format(
attachment.url(privacy=True)))
# Upload our image and get our id associated with it
@@ -536,16 +547,9 @@ class NotifyTwitter(NotifyBase):
"""
- # Prepare a whoami key; this is to prevent conflict with other
- # NotifyTwitter declarations that may or may not use a different
- # set of authentication keys
- whoami_key = '{}{}{}{}'.format(
- self.ckey, self.csecret, self.akey, self.asecret)
-
- if lazy and hasattr(NotifyTwitter, '_whoami_cache') \
- and whoami_key in getattr(NotifyTwitter, '_whoami_cache'):
+ if lazy and self._whoami_cache is not None:
# Use cached response
- return getattr(NotifyTwitter, '_whoami_cache')[whoami_key]
+ return self._whoami_cache
# Contains a mapping of screen_name to id
results = {}
@@ -560,22 +564,11 @@ class NotifyTwitter(NotifyBase):
if postokay:
try:
results[response['screen_name']] = response['id']
+ self._whoami_cache = {
+ response['screen_name']: response['id'],
+ }
- if lazy:
- # Cache our response for future references
- if not hasattr(NotifyTwitter, '_whoami_cache'):
- setattr(
- NotifyTwitter, '_whoami_cache',
- {whoami_key: results})
- else:
- getattr(NotifyTwitter, '_whoami_cache')\
- .update({whoami_key: results})
-
- # Update our user cache as well
- if not hasattr(NotifyTwitter, '_user_cache'):
- setattr(NotifyTwitter, '_user_cache', results)
- else:
- getattr(NotifyTwitter, '_user_cache').update(results)
+ self._user_cache.update(results)
except (TypeError, KeyError):
pass
@@ -595,10 +588,10 @@ class NotifyTwitter(NotifyBase):
# Build a unique set of names
names = parse_list(screen_name)
- if lazy and hasattr(NotifyTwitter, '_user_cache'):
+ if lazy and self._user_cache:
# Use cached response
- results = {k: v for k, v in getattr(
- NotifyTwitter, '_user_cache').items() if k in names}
+ results = {
+ k: v for k, v in self._user_cache.items() if k in names}
# limit our names if they already exist in our cache
names = [name for name in names if name not in results]
@@ -612,7 +605,7 @@ class NotifyTwitter(NotifyBase):
# https://developer.twitter.com/en/docs/accounts-and-users/\
# follow-search-get-users/api-reference/get-users-lookup
for i in range(0, len(names), 100):
- # Send Twitter DM
+ # Look up our names by their screen_name
postokay, response = self._fetch(
self.twitter_lookup,
payload={
@@ -635,11 +628,7 @@ class NotifyTwitter(NotifyBase):
# Cache our response for future use; this saves on un-nessisary extra
# hits against the Twitter API when we already know the answer
- if lazy:
- if not hasattr(NotifyTwitter, '_user_cache'):
- setattr(NotifyTwitter, '_user_cache', results)
- else:
- getattr(NotifyTwitter, '_user_cache').update(results)
+ self._user_cache.update(results)
return results
@@ -686,7 +675,7 @@ class NotifyTwitter(NotifyBase):
# Determine how long we should wait for or if we should wait at
# all. This isn't fool-proof because we can't be sure the client
# time (calling this script) is completely synced up with the
- # Gitter server. One would hope we're on NTP and our clocks are
+ # Twitter server. One would hope we're on NTP and our clocks are
# the same allowing this to role smoothly:
now = datetime.utcnow()
@@ -804,13 +793,9 @@ class NotifyTwitter(NotifyBase):
# Extend our parameters
params.update(self.url_parameters(privacy=privacy, *args, **kwargs))
- if len(self.targets) > 0:
- params['to'] = ','.join(
- [NotifyTwitter.quote(x, safe='') for x in self.targets])
-
return '{schema}://{ckey}/{csecret}/{akey}/{asecret}' \
'/{targets}/?{params}'.format(
- schema=self.secure_protocol,
+ schema=self.secure_protocol[0],
ckey=self.pprint(self.ckey, privacy, safe=''),
csecret=self.pprint(
self.csecret, privacy, mode=PrivacyMode.Secret, safe=''),
@@ -818,10 +803,17 @@ class NotifyTwitter(NotifyBase):
asecret=self.pprint(
self.asecret, privacy, mode=PrivacyMode.Secret, safe=''),
targets='/'.join(
- [NotifyTwitter.quote('@{}'.format(target), safe='')
+ [NotifyTwitter.quote('@{}'.format(target), safe='@')
for target in self.targets]),
params=NotifyTwitter.urlencode(params))
+ def __len__(self):
+ """
+ Returns the number of targets associated with this notification
+ """
+ targets = len(self.targets)
+ return targets if targets > 0 else 1
+
@staticmethod
def parse_url(url):
"""
@@ -834,34 +826,31 @@ class NotifyTwitter(NotifyBase):
# We're done early as we couldn't load the results
return results
- # The first token is stored in the hostname
- consumer_key = NotifyTwitter.unquote(results['host'])
-
# Acquire remaining tokens
tokens = NotifyTwitter.split_path(results['fullpath'])
- # Now fetch the remaining tokens
- try:
- consumer_secret, access_token_key, access_token_secret = \
- tokens[0:3]
+ # The consumer token is stored in the hostname
+ results['ckey'] = NotifyTwitter.unquote(results['host'])
- except (ValueError, AttributeError, IndexError):
- # Force some bad values that will get caught
- # in parsing later
- consumer_secret = None
- access_token_key = None
- access_token_secret = None
+ #
+ # Now fetch the remaining tokens
+ #
- results['ckey'] = consumer_key
- results['csecret'] = consumer_secret
- results['akey'] = access_token_key
- results['asecret'] = access_token_secret
+ # Consumer Secret
+ results['csecret'] = tokens.pop(0) if tokens else None
+ # Access Token Key
+ results['akey'] = tokens.pop(0) if tokens else None
+ # Access Token Secret
+ results['asecret'] = tokens.pop(0) if tokens else None
# The defined twitter mode
if 'mode' in results['qsd'] and len(results['qsd']['mode']):
results['mode'] = \
NotifyTwitter.unquote(results['qsd']['mode'])
+ elif results['schema'].startswith('tweet'):
+ results['mode'] = TwitterMessageMode.TWEET
+
results['targets'] = []
# if a user has been defined, add it to the list of targets
@@ -869,7 +858,7 @@ class NotifyTwitter(NotifyBase):
results['targets'].append(results.get('user'))
# Store any remaining items as potential targets
- results['targets'].extend(tokens[3:])
+ results['targets'].extend(tokens)
# Get Cache Flag (reduces lookup hits)
if 'cache' in results['qsd'] and len(results['qsd']['cache']):
diff --git a/libs/apprise/plugins/NotifyVoipms.py b/libs/apprise/plugins/NotifyVoipms.py
new file mode 100644
index 000000000..a4ec5ae1b
--- /dev/null
+++ b/libs/apprise/plugins/NotifyVoipms.py
@@ -0,0 +1,379 @@
+# -*- coding: utf-8 -*-
+# BSD 3-Clause License
+#
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+#
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+# Create an account https://voip.ms/ if you don't already have one
+#
+# Enable API and set an API password here:
+# - https://voip.ms/m/api.php
+#
+# Read more about VoIP.ms API here:
+# - https://voip.ms/m/apidocs.php
+
+import requests
+from json import loads
+
+from .NotifyBase import NotifyBase
+from ..common import NotifyType
+from ..utils import is_phone_no
+from ..utils import is_email
+from ..utils import parse_phone_no
+from ..AppriseLocale import gettext_lazy as _
+
+
+class NotifyVoipms(NotifyBase):
+ """
+ A wrapper for Voipms Notifications
+ """
+
+ # The default descriptive name associated with the Notification
+ service_name = 'VoIPms'
+
+ # The services URL
+ service_url = 'https://voip.ms'
+
+ # The default protocol
+ secure_protocol = 'voipms'
+
+ # A URL that takes you to the setup/help of the specific protocol
+ setup_url = 'https://github.com/caronc/apprise/wiki/Notify_voipms'
+
+ # Voipms uses the http protocol with JSON requests
+ notify_url = 'https://voip.ms/api/v1/rest.php'
+
+ # The maximum length of the body
+ body_maxlen = 160
+
+ # A title can not be used for SMS Messages. Setting this to zero will
+ # cause any title (if defined) to get placed into the message body.
+ title_maxlen = 0
+
+ # Define object templates
+ templates = (
+ '{schema}://{password}:{email}',
+ '{schema}://{password}:{email}/{from_phone}/{targets}',
+ )
+
+ # Define our template tokens
+ template_tokens = dict(NotifyBase.template_tokens, **{
+ 'email': {
+ 'name': _('User Email'),
+ 'type': 'string',
+ 'required': True,
+ },
+ 'password': {
+ 'name': _('Password'),
+ 'type': 'string',
+ 'private': True,
+ 'required': True,
+ },
+ 'from_phone': {
+ 'name': _('From Phone No'),
+ 'type': 'string',
+ 'regex': (r'^\+?[0-9\s)(+-]+$', 'i'),
+ 'map_to': 'source',
+ },
+ 'target_phone': {
+ 'name': _('Target Phone No'),
+ 'type': 'string',
+ 'prefix': '+',
+ 'regex': (r'^[0-9\s)(+-]+$', 'i'),
+ 'map_to': 'targets',
+ },
+ 'targets': {
+ 'name': _('Targets'),
+ 'type': 'list:string',
+ },
+ })
+
+ # Define our template arguments
+ template_args = dict(NotifyBase.template_args, **{
+ 'to': {
+ 'alias_of': 'targets',
+ },
+ 'from': {
+ 'alias_of': 'from_phone',
+ },
+ })
+
+ def __init__(self, email, source=None, targets=None, **kwargs):
+ """
+ Initialize Voipms Object
+ """
+ super().__init__(**kwargs)
+
+ # Validate our params here.
+
+ if self.password is None:
+ msg = 'Password has to be specified.'
+ self.logger.warning(msg)
+ raise TypeError(msg)
+
+ # User is the email associated with the account
+ result = is_email(email)
+ if not result:
+ msg = 'An invalid Voipms user email: ' \
+ '({}) was specified.'.format(email)
+ self.logger.warning(msg)
+ raise TypeError(msg)
+ self.email = result['full_email']
+
+ # Validate our source Phone #
+ result = is_phone_no(source)
+ if not result:
+ msg = 'An invalid Voipms source phone # ' \
+ '({}) was specified.'.format(source)
+ self.logger.warning(msg)
+ raise TypeError(msg)
+
+ # Source Phone # only supports +1 country code
+ # Allow 7 digit phones (presume they're local with +1 country code)
+ if result['country'] and result['country'] != '1':
+ msg = 'Voipms only supports +1 country code ' \
+ '({}) was specified.'.format(source)
+ self.logger.warning(msg)
+ raise TypeError(msg)
+
+ # Store our source phone number (without country code)
+ self.source = result['area'] + result['line']
+
+ # Parse our targets
+ self.targets = list()
+
+ if targets:
+ for target in parse_phone_no(targets):
+ # Validate targets and drop bad ones:
+ result = is_phone_no(target)
+
+ # Target Phone # only supports +1 country code
+ if result['country'] != '1':
+ self.logger.warning(
+ 'Dropped invalid phone # '
+ '({}) specified.'.format(target),
+ )
+ continue
+
+ # store valid phone number
+ self.targets.append(result['area'] + result['line'])
+
+ else:
+ # Send a message to ourselves
+ self.targets.append(self.source)
+
+ return
+
+ def send(self, body, title='', notify_type=NotifyType.INFO, **kwargs):
+ """
+ Perform Voipms Notification
+ """
+
+ if len(self.targets) == 0:
+ # There were no services to notify
+ self.logger.warning('There were no Voipms targets to notify.')
+ return False
+
+ # error tracking (used for function return)
+ has_error = False
+
+ # Prepare our headers
+ headers = {
+ 'User-Agent': self.app_id,
+ 'Content-Type': 'application/x-www-form-urlencoded',
+ }
+
+ # Prepare our payload
+ payload = {
+ 'api_username': self.email,
+ 'api_password': self.password,
+ 'did': self.source,
+ 'message': body,
+ 'method': 'sendSMS',
+
+ # Gets filled in the loop below
+ 'dst': None
+ }
+
+ # Create a copy of the targets list
+ targets = list(self.targets)
+
+ while len(targets):
+ # Get our target to notify
+ target = targets.pop(0)
+
+ # Add target Phone #
+ payload['dst'] = target
+
+ # Some Debug Logging
+ self.logger.debug('Voipms GET URL: {} (cert_verify={})'.format(
+ self.notify_url, self.verify_certificate))
+ self.logger.debug('Voipms Payload: {}' .format(payload))
+
+ # Always call throttle before any remote server i/o is made
+ self.throttle()
+
+ response = {'status': 'unknown', 'message': ''}
+
+ try:
+ r = requests.get(
+ self.notify_url,
+ params=payload,
+ headers=headers,
+ verify=self.verify_certificate,
+ timeout=self.request_timeout,
+ )
+
+ try:
+ response = loads(r.content)
+
+ except (AttributeError, TypeError, ValueError):
+ # ValueError = r.content is Unparsable
+ # TypeError = r.content is None
+ # AttributeError = r is None
+ pass
+
+ if r.status_code != requests.codes.ok:
+ # We had a problem
+ status_str = \
+ NotifyVoipms.http_response_code_lookup(
+ r.status_code)
+
+ self.logger.warning(
+ 'Failed to send Voipms notification to {}: '
+ '{}{}error={}.'.format(
+ target,
+ status_str,
+ ', ' if status_str else '',
+ r.status_code))
+
+ self.logger.debug(
+ 'Response Details:\r\n{}'.format(r.content))
+
+ # Mark our failure
+ has_error = True
+ continue
+
+ # Voipms sends 200 OK even if there is an error
+ # check if status in response and if it is not success
+
+ if response is not None and response['status'] != 'success':
+ self.logger.warning(
+ 'Failed to send Voipms notification to {}: '
+ 'status: {}, message: {}'.format(
+ target, response['status'], response['message'])
+ )
+
+ # Mark our failure
+ has_error = True
+ continue
+ else:
+ self.logger.info(
+ 'Sent Voipms notification to %s' % target)
+
+ except requests.RequestException as e:
+ self.logger.warning(
+ 'A Connection error occurred sending Voipms:%s '
+ 'notification.' % target
+ )
+ self.logger.debug('Socket Exception: %s' % str(e))
+
+ # Mark our failure
+ has_error = True
+ continue
+
+ return not has_error
+
+ def url(self, privacy=False, *args, **kwargs):
+ """
+ Returns the URL built dynamically based on specified arguments.
+ """
+
+ # Define any URL parameters
+ params = self.url_parameters(privacy=privacy, *args, **kwargs)
+
+ schemaStr = \
+ '{schema}://{password}:{email}/{from_phone}/{targets}/?{params}'
+ return schemaStr.format(
+ schema=self.secure_protocol,
+ email=self.email,
+ password=self.pprint(self.password, privacy, safe=''),
+ from_phone='1' + self.pprint(self.source, privacy, safe=''),
+ targets='/'.join(
+ ['1' + NotifyVoipms.quote(x, safe='') for x in self.targets]),
+ params=NotifyVoipms.urlencode(params))
+
+ def __len__(self):
+ """
+ Returns the number of targets associated with this notification
+ """
+ targets = len(self.targets)
+ return targets if targets > 0 else 1
+
+ @staticmethod
+ def parse_url(url):
+ """
+ Parses the URL and returns enough arguments that can allow
+ us to re-instantiate this object.
+
+ """
+
+ results = NotifyBase.parse_url(url, verify_host=False)
+ if not results:
+ # We're done early as we couldn't load the results
+ return results
+
+ results['targets'] = \
+ NotifyVoipms.split_path(results['fullpath'])
+
+ if 'from' in results['qsd'] and len(results['qsd']['from']):
+ results['source'] = \
+ NotifyVoipms.unquote(results['qsd']['from'])
+
+ elif results['targets']:
+ # The from phone no is the first entry in the list otherwise
+ results['source'] = results['targets'].pop(0)
+
+ # Swap user for pass since our input is: password:email
+ # where email is user@hostname (or user@domain)
+ user = results['password']
+ password = results['user']
+ results['password'] = password
+ results['user'] = user
+
+ results['email'] = '{}@{}'.format(
+ NotifyVoipms.unquote(user),
+ NotifyVoipms.unquote(results['host']),
+ )
+
+ if 'to' in results['qsd'] and len(results['qsd']['to']):
+ results['targets'] += \
+ NotifyVoipms.parse_phone_no(results['qsd']['to'])
+
+ return results
diff --git a/libs/apprise/plugins/NotifyVonage.py b/libs/apprise/plugins/NotifyVonage.py
index 1dea3157d..bc3ab0647 100644
--- a/libs/apprise/plugins/NotifyVonage.py
+++ b/libs/apprise/plugins/NotifyVonage.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
# Sign-up with https://dashboard.nexmo.com/
#
@@ -142,7 +149,7 @@ class NotifyVonage(NotifyBase):
"""
Initialize Vonage Object
"""
- super(NotifyVonage, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# API Key (associated with project)
self.apikey = validate_regex(
@@ -327,6 +334,13 @@ class NotifyVonage(NotifyBase):
[NotifyVonage.quote(x, safe='') for x in self.targets]),
params=NotifyVonage.urlencode(params))
+ def __len__(self):
+ """
+ Returns the number of targets associated with this notification
+ """
+ targets = len(self.targets)
+ return targets if targets > 0 else 1
+
@staticmethod
def parse_url(url):
"""
diff --git a/libs/apprise/plugins/NotifyWebexTeams.py b/libs/apprise/plugins/NotifyWebexTeams.py
index 5e8021330..6b953b711 100644
--- a/libs/apprise/plugins/NotifyWebexTeams.py
+++ b/libs/apprise/plugins/NotifyWebexTeams.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
# At the time I created this plugin, their website had lots of issues with the
# Firefox Browser. I fell back to Chrome and had no problems.
@@ -88,7 +95,7 @@ class NotifyWebexTeams(NotifyBase):
service_url = 'https://webex.teams.com/'
# The default secure protocol
- secure_protocol = 'wxteams'
+ secure_protocol = ('wxteams', 'webex')
# A URL that takes you to the setup/help of the specific protocol
setup_url = 'https://github.com/caronc/apprise/wiki/Notify_wxteams'
@@ -117,7 +124,7 @@ class NotifyWebexTeams(NotifyBase):
'type': 'string',
'private': True,
'required': True,
- 'regex': (r'^[a-z0-9]{80}$', 'i'),
+ 'regex': (r'^[a-z0-9]{80,160}$', 'i'),
},
})
@@ -125,7 +132,7 @@ class NotifyWebexTeams(NotifyBase):
"""
Initialize Webex Teams Object
"""
- super(NotifyWebexTeams, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# The token associated with the account
self.token = validate_regex(
@@ -213,7 +220,7 @@ class NotifyWebexTeams(NotifyBase):
params = self.url_parameters(privacy=privacy, *args, **kwargs)
return '{schema}://{token}/?{params}'.format(
- schema=self.secure_protocol,
+ schema=self.secure_protocol[0],
token=self.pprint(self.token, privacy, safe=''),
params=NotifyWebexTeams.urlencode(params),
)
@@ -242,14 +249,15 @@ class NotifyWebexTeams(NotifyBase):
"""
result = re.match(
- r'^https?://api\.ciscospark\.com/v[1-9][0-9]*/webhooks/incoming/'
+ r'^https?://(api\.ciscospark\.com|webexapis\.com)'
+ r'/v[1-9][0-9]*/webhooks/incoming/'
r'(?P<webhook_token>[A-Z0-9_-]+)/?'
r'(?P<params>\?.+)?$', url, re.I)
if result:
return NotifyWebexTeams.parse_url(
'{schema}://{webhook_token}/{params}'.format(
- schema=NotifyWebexTeams.secure_protocol,
+ schema=NotifyWebexTeams.secure_protocol[0],
webhook_token=result.group('webhook_token'),
params='' if not result.group('params')
else result.group('params')))
diff --git a/libs/apprise/plugins/NotifyWindows.py b/libs/apprise/plugins/NotifyWindows.py
index 1c599e14b..70f438894 100644
--- a/libs/apprise/plugins/NotifyWindows.py
+++ b/libs/apprise/plugins/NotifyWindows.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
from __future__ import absolute_import
from __future__ import print_function
@@ -113,7 +120,7 @@ class NotifyWindows(NotifyBase):
Initialize Windows Object
"""
- super(NotifyWindows, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# Number of seconds to display notification for
self.duration = self.default_popup_duration_sec \
@@ -135,7 +142,7 @@ class NotifyWindows(NotifyBase):
win32gui.Shell_NotifyIcon(win32gui.NIM_DELETE, nid)
win32api.PostQuitMessage(0)
- return None
+ return 0
def send(self, body, title='', notify_type=NotifyType.INFO, **kwargs):
"""
diff --git a/libs/apprise/plugins/NotifyXBMC.py b/libs/apprise/plugins/NotifyXBMC.py
index 22f4219c0..963a74d88 100644
--- a/libs/apprise/plugins/NotifyXBMC.py
+++ b/libs/apprise/plugins/NotifyXBMC.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
import requests
from json import dumps
@@ -131,7 +138,7 @@ class NotifyXBMC(NotifyBase):
"""
Initialize XBMC/KODI Object
"""
- super(NotifyXBMC, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# Number of seconds to display notification for
self.duration = self.template_args['duration']['default'] \
diff --git a/libs/apprise/plugins/NotifyXML.py b/libs/apprise/plugins/NotifyXML.py
index 9fea49da1..04cdac10e 100644
--- a/libs/apprise/plugins/NotifyXML.py
+++ b/libs/apprise/plugins/NotifyXML.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
import re
import requests
@@ -34,13 +41,24 @@ from ..common import NotifyType
from ..AppriseLocale import gettext_lazy as _
+class XMLPayloadField:
+ """
+ Identifies the fields available in the JSON Payload
+ """
+ VERSION = 'Version'
+ TITLE = 'Subject'
+ MESSAGE = 'Message'
+ MESSAGETYPE = 'MessageType'
+
+
# Defines the method to send the notification
METHODS = (
'POST',
'GET',
'DELETE',
'PUT',
- 'HEAD'
+ 'HEAD',
+ 'PATCH'
)
@@ -70,7 +88,8 @@ class NotifyXML(NotifyBase):
# XSD Information
xsd_ver = '1.1'
- xsd_url = 'https://raw.githubusercontent.com/caronc/apprise/master' \
+ xsd_default_url = \
+ 'https://raw.githubusercontent.com/caronc/apprise/master' \
'/apprise/assets/NotifyXML-{version}.xsd'
# Define object templates
@@ -130,9 +149,14 @@ class NotifyXML(NotifyBase):
'name': _('Payload Extras'),
'prefix': ':',
},
+ 'params': {
+ 'name': _('GET Params'),
+ 'prefix': '-',
+ },
}
- def __init__(self, headers=None, method=None, payload=None, **kwargs):
+ def __init__(self, headers=None, method=None, payload=None, params=None,
+ **kwargs):
"""
Initialize XML Object
@@ -140,7 +164,7 @@ class NotifyXML(NotifyBase):
additionally include as part of the server headers to post with
"""
- super(NotifyXML, self).__init__(**kwargs)
+ super().__init__(**kwargs)
self.payload = """<?xml version='1.0' encoding='utf-8'?>
<soapenv:Envelope
@@ -148,7 +172,7 @@ class NotifyXML(NotifyBase):
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
- <Notification xmlns:xsi="{{XSD_URL}}">
+ <Notification{{XSD_URL}}>
{{CORE}}
{{ATTACHMENTS}}
</Notification>
@@ -167,11 +191,32 @@ class NotifyXML(NotifyBase):
self.logger.warning(msg)
raise TypeError(msg)
+ # A payload map allows users to over-ride the default mapping if
+ # they're detected with the :overide=value. Normally this would
+ # create a new key and assign it the value specified. However
+ # if the key you specify is actually an internally mapped one,
+ # then a re-mapping takes place using the value
+ self.payload_map = {
+ XMLPayloadField.VERSION: XMLPayloadField.VERSION,
+ XMLPayloadField.TITLE: XMLPayloadField.TITLE,
+ XMLPayloadField.MESSAGE: XMLPayloadField.MESSAGE,
+ XMLPayloadField.MESSAGETYPE: XMLPayloadField.MESSAGETYPE,
+ }
+
+ self.params = {}
+ if params:
+ # Store our extra headers
+ self.params.update(params)
+
self.headers = {}
if headers:
# Store our extra headers
self.headers.update(headers)
+ # Set our xsd url
+ self.xsd_url = self.xsd_default_url.format(version=self.xsd_ver)
+
+ self.payload_overrides = {}
self.payload_extras = {}
if payload:
# Store our extra payload entries (but tidy them up since they will
@@ -183,8 +228,20 @@ class NotifyXML(NotifyBase):
'Ignoring invalid XML Stanza element name({})'
.format(k))
continue
- self.payload_extras[key] = v
+ # Any values set in the payload to alter a system related one
+ # alters the system key. Hence :message=msg maps the 'message'
+ # variable that otherwise already contains the payload to be
+ # 'msg' instead (containing the payload)
+ if key in self.payload_map:
+ self.payload_map[key] = v
+ self.payload_overrides[key] = v
+
+ # Over-ride XSD URL as data is no longer known
+ self.xsd_url = None
+
+ else:
+ self.payload_extras[key] = v
return
def url(self, privacy=False, *args, **kwargs):
@@ -203,9 +260,14 @@ class NotifyXML(NotifyBase):
# Append our headers into our parameters
params.update({'+{}'.format(k): v for k, v in self.headers.items()})
+ # Append our GET params into our parameters
+ params.update({'-{}'.format(k): v for k, v in self.params.items()})
+
# Append our payload extra's into our parameters
params.update(
{':{}'.format(k): v for k, v in self.payload_extras.items()})
+ params.update(
+ {':{}'.format(k): v for k, v in self.payload_overrides.items()})
# Determine Authentication
auth = ''
@@ -240,7 +302,7 @@ class NotifyXML(NotifyBase):
Perform XML Notification
"""
- # prepare XML Object
+ # Prepare HTTP Headers
headers = {
'User-Agent': self.app_id,
'Content-Type': 'application/xml'
@@ -252,14 +314,21 @@ class NotifyXML(NotifyBase):
# Our XML Attachmement subsitution
xml_attachments = ''
- # Our Payload Base
- payload_base = {
- 'Version': self.xsd_ver,
- 'Subject': NotifyXML.escape_html(title, whitespace=False),
- 'MessageType': NotifyXML.escape_html(
- notify_type, whitespace=False),
- 'Message': NotifyXML.escape_html(body, whitespace=False),
- }
+ payload_base = {}
+
+ for key, value in (
+ (XMLPayloadField.VERSION, self.xsd_ver),
+ (XMLPayloadField.TITLE, NotifyXML.escape_html(
+ title, whitespace=False)),
+ (XMLPayloadField.MESSAGE, NotifyXML.escape_html(
+ body, whitespace=False)),
+ (XMLPayloadField.MESSAGETYPE, NotifyXML.escape_html(
+ notify_type, whitespace=False))):
+
+ if not self.payload_map[key]:
+ # Do not store element in payload response
+ continue
+ payload_base[self.payload_map[key]] = value
# Apply our payload extras
payload_base.update(
@@ -307,7 +376,8 @@ class NotifyXML(NotifyBase):
''.join(attachments) + '</Attachments>'
re_map = {
- '{{XSD_URL}}': self.xsd_url.format(version=self.xsd_ver),
+ '{{XSD_URL}}':
+ f' xmlns:xsi="{self.xsd_url}"' if self.xsd_url else '',
'{{ATTACHMENTS}}': xml_attachments,
'{{CORE}}': xml_base,
}
@@ -347,6 +417,9 @@ class NotifyXML(NotifyBase):
elif self.method == 'PUT':
method = requests.put
+ elif self.method == 'PATCH':
+ method = requests.patch
+
elif self.method == 'DELETE':
method = requests.delete
@@ -417,6 +490,10 @@ class NotifyXML(NotifyBase):
results['headers'] = {NotifyXML.unquote(x): NotifyXML.unquote(y)
for x, y in results['qsd+'].items()}
+ # Add our GET paramters in the event the user wants to pass these along
+ results['params'] = {NotifyXML.unquote(x): NotifyXML.unquote(y)
+ for x, y in results['qsd-'].items()}
+
# Set method if not otherwise set
if 'method' in results['qsd'] and len(results['qsd']['method']):
results['method'] = NotifyXML.unquote(results['qsd']['method'])
diff --git a/libs/apprise/plugins/NotifyZulip.py b/libs/apprise/plugins/NotifyZulip.py
index 662771379..f9521ae19 100644
--- a/libs/apprise/plugins/NotifyZulip.py
+++ b/libs/apprise/plugins/NotifyZulip.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
# To use this plugin, you must have a ZulipChat bot defined; See here:
# https://zulipchat.com/help/add-a-bot-or-integration
@@ -172,7 +179,7 @@ class NotifyZulip(NotifyBase):
"""
Initialize Zulip Object
"""
- super(NotifyZulip, self).__init__(**kwargs)
+ super().__init__(**kwargs)
# our default hostname
self.hostname = self.default_hostname
@@ -352,6 +359,12 @@ class NotifyZulip(NotifyBase):
params=NotifyZulip.urlencode(params),
)
+ def __len__(self):
+ """
+ Returns the number of targets associated with this notification
+ """
+ return len(self.targets)
+
@staticmethod
def parse_url(url):
"""
diff --git a/libs/apprise/plugins/__init__.py b/libs/apprise/plugins/__init__.py
index 9abc3e061..5560568b7 100644
--- a/libs/apprise/plugins/__init__.py
+++ b/libs/apprise/plugins/__init__.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
import os
import re
@@ -31,10 +38,7 @@ from os.path import dirname
from os.path import abspath
# Used for testing
-from . import NotifyEmail as NotifyEmailBase
-
-# NotifyBase object is passed in as a module not class
-from . import NotifyBase
+from .NotifyBase import NotifyBase
from ..common import NotifyImageSize
from ..common import NOTIFY_IMAGE_SIZES
@@ -53,9 +57,6 @@ __all__ = [
'NotifyImageSize', 'NOTIFY_IMAGE_SIZES', 'NotifyType', 'NOTIFY_TYPES',
'NotifyBase',
- # NotifyEmail Base Module (used for NotifyEmail testing)
- 'NotifyEmailBase',
-
# Tokenizer
'url_to_dict',
]
@@ -115,9 +116,6 @@ def __load_matrix(path=abspath(dirname(__file__)), name='apprise.plugins'):
# Add our module name to our __all__
__all__.append(plugin_name)
- # Load our module into memory so it's accessible to all
- globals()[plugin_name] = plugin
-
fn = getattr(plugin, 'schemas', None)
schemas = set([]) if not callable(fn) else fn(plugin)
@@ -147,8 +145,6 @@ def __reset_matrix():
# Iterate over our module map so we can clear out our __all__ and globals
for plugin_name in common.NOTIFY_MODULE_MAP.keys():
- # Clear out globals
- del globals()[plugin_name]
# Remove element from plugins
__all__.remove(plugin_name)
diff --git a/libs/apprise/py3compat/__init__.py b/libs/apprise/py3compat/__init__.py
deleted file mode 100644
index e69de29bb..000000000
--- a/libs/apprise/py3compat/__init__.py
+++ /dev/null
diff --git a/libs/apprise/py3compat/asyncio.py b/libs/apprise/py3compat/asyncio.py
deleted file mode 100644
index a53139062..000000000
--- a/libs/apprise/py3compat/asyncio.py
+++ /dev/null
@@ -1,140 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# Copyright (C) 2020 Chris Caron <[email protected]>
-# All rights reserved.
-#
-# This code is licensed under the MIT License.
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-
-import sys
-import asyncio
-from functools import partial
-from ..URLBase import URLBase
-from ..logger import logger
-
-
-# A global flag that tracks if we are Python v3.7 or higher
-ASYNCIO_RUN_SUPPORT = \
- sys.version_info.major > 3 or \
- (sys.version_info.major == 3 and sys.version_info.minor >= 7)
-
-
-async def notify(coroutines):
- """
- An async wrapper to the AsyncNotifyBase.async_notify() calls allowing us
- to call gather() and collect the responses
- """
-
- # Create log entry
- logger.info(
- 'Notifying {} service(s) asynchronously.'.format(len(coroutines)))
-
- results = await asyncio.gather(*coroutines, return_exceptions=True)
-
- # Returns True if all notifications succeeded, otherwise False is
- # returned.
- failed = any(not status or isinstance(status, Exception)
- for status in results)
- return not failed
-
-
-def tosync(cor, debug=False):
- """
- Await a coroutine from non-async code.
- """
-
- if ASYNCIO_RUN_SUPPORT:
- try:
- loop = asyncio.get_running_loop()
-
- except RuntimeError:
- # There is no existing event loop, so we can start our own.
- return asyncio.run(cor, debug=debug)
-
- else:
- # Enable debug mode
- loop.set_debug(debug)
-
- # Run the coroutine and wait for the result.
- task = loop.create_task(cor)
- return asyncio.ensure_future(task, loop=loop)
-
- else:
- # The Deprecated Way (<= Python v3.6)
- try:
- # acquire access to our event loop
- loop = asyncio.get_event_loop()
-
- except RuntimeError:
- # This happens if we're inside a thread of another application
- # where there is no running event_loop(). Pythong v3.7 and
- # higher automatically take care of this case for us. But for
- # the lower versions we need to do the following:
- loop = asyncio.new_event_loop()
- asyncio.set_event_loop(loop)
-
- # Enable debug mode
- loop.set_debug(debug)
-
- return loop.run_until_complete(cor)
-
-
-async def toasyncwrapvalue(v):
- """
- Create a coroutine that, when run, returns the provided value.
- """
-
- return v
-
-
-async def toasyncwrap(fn):
- """
- Create a coroutine that, when run, executes the provided function.
- """
-
- return fn()
-
-
-class AsyncNotifyBase(URLBase):
- """
- asyncio wrapper for the NotifyBase object
- """
-
- async def async_notify(self, *args, **kwargs):
- """
- Async Notification Wrapper
- """
-
- loop = asyncio.get_event_loop()
-
- try:
- return await loop.run_in_executor(
- None, partial(self.notify, *args, **kwargs))
-
- except TypeError:
- # These are our internally thrown notifications
- pass
-
- except Exception:
- # A catch-all so we don't have to abort early
- # just because one of our plugins has a bug in it.
- logger.exception("Notification Exception")
-
- return False
diff --git a/libs/apprise/utils.py b/libs/apprise/utils.py
index 334711de9..561a5a232 100644
--- a/libs/apprise/utils.py
+++ b/libs/apprise/utils.py
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
+# BSD 3-Clause License
#
-# Copyright (C) 2019 Chris Caron <[email protected]>
-# All rights reserved.
+# Apprise - Push Notification Library.
+# Copyright (c) 2023, Chris Caron <[email protected]>
#
-# This code is licensed under the MIT License.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files(the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions :
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
import re
import sys
@@ -47,10 +54,6 @@ def import_module(path, name):
"""
Load our module based on path
"""
- # if path.endswith('test_module_detection0/a/hook.py'):
- # import pdb
- # pdb.set_trace()
-
spec = importlib.util.spec_from_file_location(name, path)
try:
module = importlib.util.module_from_spec(spec)
@@ -60,7 +63,13 @@ def import_module(path, name):
except Exception as e:
# module isn't loadable
- del sys.modules[name]
+ try:
+ del sys.modules[name]
+
+ except KeyError:
+ # nothing to clean up
+ pass
+
module = None
logger.debug(
@@ -153,10 +162,10 @@ URL_DETAILS_RE = re.compile(
GET_EMAIL_RE = re.compile(
- r'(([\s"\']+)?(?P<name>[^:<"\']+)?[:<\s"\']+)?'
+ r'(([\s"\']+)?(?P<name>[^:<\'"]+)?[:<\s\'"]+)?'
r'(?P<full_email>((?P<label>[^+]+)\+)?'
- r'(?P<email>(?P<userid>[a-z0-9$%=_~-]+'
- r'(?:\.[a-z0-9$%+=_~-]+)'
+ r'(?P<email>(?P<userid>[a-z0-9_!#$%&*/=?%`{|}~^-]+'
+ r'(?:\.[a-z0-9_!#$%&\'*/=?%`{|}~^-]+)'
r'*)@(?P<domain>('
r'(?:[a-z0-9](?:[a-z0-9_-]*[a-z0-9])?\.)+'
r'[a-z0-9](?:[a-z0-9_-]*[a-z0-9]))|'
@@ -188,7 +197,7 @@ URL_DETECTION_RE = re.compile(
EMAIL_DETECTION_RE = re.compile(
r'[\s,]*([^@]+@.*?)(?=$|[\s,]+'
- + r'(?:[^:<]+?[:<\s]+?)?'
+ r'(?:[^:<]+?[:<\s]+?)?'
r'[^@\s,]+@[^\s,]+)',
re.IGNORECASE)
@@ -197,6 +206,9 @@ UUID4_RE = re.compile(
r'[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}',
re.IGNORECASE)
+# Validate if we're a loadable Python file or not
+VALID_PYTHON_FILE_RE = re.compile(r'.+\.py(o|c)?$', re.IGNORECASE)
+
# validate_regex() utilizes this mapping to track and re-use pre-complied
# regular expressions
REGEX_VALIDATE_LOOKUP = {}
@@ -519,7 +531,7 @@ def tidy_path(path):
return path
-def parse_qsd(qs, simple=False):
+def parse_qsd(qs, simple=False, plus_to_space=False):
"""
Query String Dictionary Builder
@@ -541,6 +553,11 @@ def parse_qsd(qs, simple=False):
if simple is set to true, then a ONE dictionary is returned and is not
sub-parsed for additional elements
+
+ plus_to_space will cause all `+` references to become a space as
+ per normal URL Encoded defininition. Normal URL parsing applies
+ this, but `+` is very actively used character with passwords,
+ api keys, tokens, etc. So Apprise does not do this by default.
"""
# Our return result set:
@@ -575,7 +592,7 @@ def parse_qsd(qs, simple=False):
key = unquote(key)
key = '' if not key else key
- val = nv[1].replace('+', ' ')
+ val = nv[1].replace('+', ' ') if plus_to_space else nv[1]
val = unquote(val)
val = '' if not val else val.strip()
@@ -609,7 +626,7 @@ def parse_qsd(qs, simple=False):
def parse_url(url, default_schema='http', verify_host=True, strict_port=False,
- simple=False):
+ simple=False, plus_to_space=False):
"""A function that greatly simplifies the parsing of a url
specified by the end user.
@@ -722,7 +739,8 @@ def parse_url(url, default_schema='http', verify_host=True, strict_port=False,
# Parse Query Arugments ?val=key&key=val
# while ensuring that all keys are lowercase
if qsdata:
- result.update(parse_qsd(qsdata, simple=simple))
+ result.update(parse_qsd(
+ qsdata, simple=simple, plus_to_space=plus_to_space))
# Now do a proper extraction of data; http:// is just substitued in place
# to allow urlparse() to function as expected, we'll swap this back to the
@@ -1556,6 +1574,11 @@ def module_detection(paths, cache=True):
# Since our plugin name can conflict (as a module) with another
# we want to generate random strings to avoid steping on
# another's namespace
+ if not (path and VALID_PYTHON_FILE_RE.match(path)):
+ # Ignore file/module type
+ logger.trace('Plugin Scan: Skipping %s', path)
+ return None
+
module_name = hashlib.sha1(path.encode('utf-8')).hexdigest()
module_pyname = "{prefix}.{name}".format(
prefix='apprise.custom.module', name=module_name)
diff --git a/libs/version.txt b/libs/version.txt
index ad74cae19..878cf53d2 100644
--- a/libs/version.txt
+++ b/libs/version.txt
@@ -1,7 +1,7 @@
# Bazarr dependencies
aniso8601==9.0.1
argparse==1.4.0
-apprise==1.1.0
+apprise==1.4.0
apscheduler==3.9.1
attrs==22.1.0
charamel==1.0.0