summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authormorpheus65535 <[email protected]>2024-03-10 09:44:50 -0400
committermorpheus65535 <[email protected]>2024-03-10 09:44:50 -0400
commitaedf2d4d894b1ccbafbf6e418e9753865170edd5 (patch)
treee61f3c96405999bdc6438f8894aa12b768e84897
parent35eabb6a8345774628445f97b7a941b3da755c7c (diff)
downloadbazarr-aedf2d4d894b1ccbafbf6e418e9753865170edd5.tar.gz
bazarr-aedf2d4d894b1ccbafbf6e418e9753865170edd5.zip
Updated apprise to latest version to prevent deadlocks issue in 1.7.3.v1.4.3-beta.10
-rw-r--r--libs/apprise-1.7.4.dist-info/INSTALLER (renamed from libs/apprise-1.7.3.dist-info/INSTALLER)0
-rw-r--r--libs/apprise-1.7.4.dist-info/LICENSE (renamed from libs/apprise-1.7.3.dist-info/LICENSE)0
-rw-r--r--libs/apprise-1.7.4.dist-info/METADATA (renamed from libs/apprise-1.7.3.dist-info/METADATA)37
-rw-r--r--libs/apprise-1.7.4.dist-info/RECORD (renamed from libs/apprise-1.7.3.dist-info/RECORD)23
-rw-r--r--libs/apprise-1.7.4.dist-info/REQUESTED (renamed from libs/apprise-1.7.3.dist-info/REQUESTED)0
-rw-r--r--libs/apprise-1.7.4.dist-info/WHEEL (renamed from libs/apprise-1.7.3.dist-info/WHEEL)0
-rw-r--r--libs/apprise-1.7.4.dist-info/entry_points.txt (renamed from libs/apprise-1.7.3.dist-info/entry_points.txt)0
-rw-r--r--libs/apprise-1.7.4.dist-info/top_level.txt (renamed from libs/apprise-1.7.3.dist-info/top_level.txt)0
-rw-r--r--libs/apprise/__init__.py2
-rw-r--r--libs/apprise/cli.py12
-rw-r--r--libs/apprise/manager.py103
-rw-r--r--libs/apprise/plugins/NotifyLunaSea.py440
-rw-r--r--libs/version.txt2
13 files changed, 531 insertions, 88 deletions
diff --git a/libs/apprise-1.7.3.dist-info/INSTALLER b/libs/apprise-1.7.4.dist-info/INSTALLER
index a1b589e38..a1b589e38 100644
--- a/libs/apprise-1.7.3.dist-info/INSTALLER
+++ b/libs/apprise-1.7.4.dist-info/INSTALLER
diff --git a/libs/apprise-1.7.3.dist-info/LICENSE b/libs/apprise-1.7.4.dist-info/LICENSE
index f9154fefe..f9154fefe 100644
--- a/libs/apprise-1.7.3.dist-info/LICENSE
+++ b/libs/apprise-1.7.4.dist-info/LICENSE
diff --git a/libs/apprise-1.7.3.dist-info/METADATA b/libs/apprise-1.7.4.dist-info/METADATA
index ca24a401d..1fbaf2a08 100644
--- a/libs/apprise-1.7.3.dist-info/METADATA
+++ b/libs/apprise-1.7.4.dist-info/METADATA
@@ -1,12 +1,12 @@
Metadata-Version: 2.1
Name: apprise
-Version: 1.7.3
+Version: 1.7.4
Summary: Push Notifications that work with just about every platform!
Home-page: https://github.com/caronc/apprise
Author: Chris Caron
Author-email: [email protected]
License: BSD
-Keywords: Alerts Apprise API Automated Packet Reporting System AWS Boxcar BulkSMS BulkVS Burst SMS Chat CLI ClickSend D7Networks Dapnet DBus DingTalk Discord Email Emby Enigma2 Faast FCM Flock Form Gnome Google Chat Gotify Growl Guilded Home Assistant httpSMS IFTTT Join JSON Kavenegar KODI Kumulos LaMetric Line MacOSX Mailgun Mastodon Matrix Mattermost MessageBird Microsoft Misskey MQTT MSG91 MSTeams Nextcloud NextcloudTalk Notica Notifiarr Notifico Ntfy Office365 OneSignal Opsgenie PagerDuty PagerTree ParsePlatform PopcornNotify Prowl PushBullet Pushed Pushjet PushMe Push Notifications Pushover PushSafer Pushy PushDeer Reddit Revolt Rocket.Chat RSyslog Ryver SendGrid ServerChan SES Signal SimplePush Sinch Slack SMSEagle SMS Manager SMTP2Go SNS SparkPost Streamlabs Stride Synology Chat Syslog Techulus Telegram Threema Gateway Twilio Twist Twitter Voipms Vonage Webex WeCom Bot WhatsApp Windows XBMC XML Zulip
+Keywords: Alerts Apprise API Automated Packet Reporting System AWS Boxcar BulkSMS BulkVS Burst SMS Chat CLI ClickSend D7Networks Dapnet DBus DingTalk Discord Email Emby Enigma2 Faast FCM Flock Form Gnome Google Chat Gotify Growl Guilded Home Assistant httpSMS IFTTT Join JSON Kavenegar KODI Kumulos LaMetric Line LunaSea MacOSX Mailgun Mastodon Matrix Mattermost MessageBird Microsoft Misskey MQTT MSG91 MSTeams Nextcloud NextcloudTalk Notica Notifiarr Notifico Ntfy Office365 OneSignal Opsgenie PagerDuty PagerTree ParsePlatform PopcornNotify Prowl PushBullet Pushed Pushjet PushMe Push Notifications Pushover PushSafer Pushy PushDeer Reddit Revolt Rocket.Chat RSyslog Ryver SendGrid ServerChan SES Signal SimplePush Sinch Slack SMSEagle SMS Manager SMTP2Go SNS SparkPost Streamlabs Stride Synology Chat Syslog Techulus Telegram Threema Gateway Twilio Twist Twitter Voipms Vonage Webex WeCom Bot WhatsApp Windows XBMC XML Zulip
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: System Administrators
@@ -115,6 +115,7 @@ The table below identifies the services this tool supports and some example serv
| [Kumulos](https://github.com/caronc/apprise/wiki/Notify_kumulos) | kumulos:// | (TCP) 443 | kumulos://apikey/serverkey
| [LaMetric Time](https://github.com/caronc/apprise/wiki/Notify_lametric) | lametric:// | (TCP) 443 | lametric://apikey@device_ipaddr<br/>lametric://apikey@hostname:port<br/>lametric://client_id@client_secret
| [Line](https://github.com/caronc/apprise/wiki/Notify_line) | line:// | (TCP) 443 | line://Token@User<br/>line://Token/User1/User2/UserN
+| [LunaSea](https://github.com/caronc/apprise/wiki/Notify_lunasea) | lunasea:// | (TCP) 80 or 443 | lunasea://user:pass@+FireBaseDevice/<br/>lunasea://user:pass@FireBaseUser/<br/>lunasea://user:pass@hostname/+FireBaseDevice/<br/>lunasea://user:pass@hostname/@FireBaseUser/
| [Mailgun](https://github.com/caronc/apprise/wiki/Notify_mailgun) | mailgun:// | (TCP) 443 | mailgun://user@hostname/apikey<br />mailgun://user@hostname/apikey/email<br />mailgun://user@hostname/apikey/email1/email2/emailN<br />mailgun://user@hostname/apikey/?name="From%20User"
| [Mastodon](https://github.com/caronc/apprise/wiki/Notify_mastodon) | mastodon:// or mastodons://| (TCP) 80 or 443 | mastodon://access_key@hostname<br />mastodon://access_key@hostname/@user<br />mastodon://access_key@hostname/@user1/@user2/@userN
| [Matrix](https://github.com/caronc/apprise/wiki/Notify_matrix) | matrix:// or matrixs:// | (TCP) 80 or 443 | matrix://hostname<br />matrix://user@hostname<br />matrixs://user:pass@hostname:port/#room_alias<br />matrixs://user:pass@hostname:port/!room_id<br />matrixs://user:pass@hostname:port/#room_alias/!room_id/#room2<br />matrixs://token@hostname:port/?webhook=matrix<br />matrix://user:token@hostname/?webhook=slack&format=markdown
@@ -270,43 +271,35 @@ No one wants to put their credentials out for everyone to see on the command lin
# By default if no url or configuration is specified apprise will attempt to load
# configuration files (if present) from:
# ~/.apprise
-# ~/.apprise.yml
# ~/.apprise.yaml
-# ~/.config/apprise
-# ~/.config/apprise.yml
+# ~/.config/apprise.conf
# ~/.config/apprise.yaml
-# /etc/apprise
-# /etc/apprise.yml
+# /etc/apprise.conf
# /etc/apprise.yaml
# Also a subdirectory handling allows you to leverage plugins
# ~/.apprise/apprise
-# ~/.apprise/apprise.yml
# ~/.apprise/apprise.yaml
-# ~/.config/apprise/apprise
-# ~/.config/apprise/apprise.yml
+# ~/.config/apprise/apprise.conf
# ~/.config/apprise/apprise.yaml
-# /etc/apprise/apprise
-# /etc/apprise/apprise.yml
# /etc/apprise/apprise.yaml
+# /etc/apprise/apprise.conf
# Windows users can store their default configuration files here:
-# %APPDATA%/Apprise/apprise
-# %APPDATA%/Apprise/apprise.yml
+# %APPDATA%/Apprise/apprise.conf
# %APPDATA%/Apprise/apprise.yaml
-# %LOCALAPPDATA%/Apprise/apprise
-# %LOCALAPPDATA%/Apprise/apprise.yml
+# %LOCALAPPDATA%/Apprise/apprise.conf
# %LOCALAPPDATA%/Apprise/apprise.yaml
-# %ALLUSERSPROFILE%\Apprise\apprise
-# %ALLUSERSPROFILE%\Apprise\apprise.yml
+# %ALLUSERSPROFILE%\Apprise\apprise.conf
# %ALLUSERSPROFILE%\Apprise\apprise.yaml
-# %PROGRAMFILES%\Apprise\apprise
-# %PROGRAMFILES%\Apprise\apprise.yml
+# %PROGRAMFILES%\Apprise\apprise.conf
# %PROGRAMFILES%\Apprise\apprise.yaml
-# %COMMONPROGRAMFILES%\Apprise\apprise
-# %COMMONPROGRAMFILES%\Apprise\apprise.yml
+# %COMMONPROGRAMFILES%\Apprise\apprise.conf
# %COMMONPROGRAMFILES%\Apprise\apprise.yaml
+# The configuration files specified above can also be identified with a `.yml`
+# extension or even just entirely removing the `.conf` extension altogether.
+
# If you loaded one of those files, your command line gets really easy:
apprise -vv -t 'my title' -b 'my notification body'
diff --git a/libs/apprise-1.7.3.dist-info/RECORD b/libs/apprise-1.7.4.dist-info/RECORD
index de47ab4a7..ca5dcd003 100644
--- a/libs/apprise-1.7.3.dist-info/RECORD
+++ b/libs/apprise-1.7.4.dist-info/RECORD
@@ -1,12 +1,12 @@
../../bin/apprise,sha256=ZJ-e4qqxNLtdW_DAvpuPPX5iROIiQd8I6nvg7vtAv-g,233
-apprise-1.7.3.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
-apprise-1.7.3.dist-info/LICENSE,sha256=gt7qKBxRhVcdmXCYVtrWP6DtYjD0DzONet600dkU994,1343
-apprise-1.7.3.dist-info/METADATA,sha256=1IS6O2IzRJcduJO9wK9tJhz1jDhZXcTTXfudj3-yy-Q,44360
-apprise-1.7.3.dist-info/RECORD,,
-apprise-1.7.3.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
-apprise-1.7.3.dist-info/WHEEL,sha256=Xo9-1PvkuimrydujYJAjF7pCkriuXBpUPEjma1nZyJ0,92
-apprise-1.7.3.dist-info/entry_points.txt,sha256=71YypBuNdjAKiaLsiMG40HEfLHxkU4Mi7o_S0s0d8wI,45
-apprise-1.7.3.dist-info/top_level.txt,sha256=JrCRn-_rXw5LMKXkIgMSE4E0t1Ks9TYrBH54Pflwjkk,8
+apprise-1.7.4.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+apprise-1.7.4.dist-info/LICENSE,sha256=gt7qKBxRhVcdmXCYVtrWP6DtYjD0DzONet600dkU994,1343
+apprise-1.7.4.dist-info/METADATA,sha256=Lc66iPsSCFv0zmoQX8NFuc_V5CqFYN5Yrx_gqeN8OF8,44502
+apprise-1.7.4.dist-info/RECORD,,
+apprise-1.7.4.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+apprise-1.7.4.dist-info/WHEEL,sha256=Xo9-1PvkuimrydujYJAjF7pCkriuXBpUPEjma1nZyJ0,92
+apprise-1.7.4.dist-info/entry_points.txt,sha256=71YypBuNdjAKiaLsiMG40HEfLHxkU4Mi7o_S0s0d8wI,45
+apprise-1.7.4.dist-info/top_level.txt,sha256=JrCRn-_rXw5LMKXkIgMSE4E0t1Ks9TYrBH54Pflwjkk,8
apprise/Apprise.py,sha256=Stm2NhJprWRaMwQfTiIQG_nR1bLpHi_zcdwEcsCpa-A,32865
apprise/Apprise.pyi,sha256=_4TBKvT-QVj3s6PuTh3YX-BbQMeJTdBGdVpubLMY4_k,2203
apprise/AppriseAsset.py,sha256=jRW8Y1EcAvjVA9h_mINmsjO4DM3S0aDl6INIFVMcUCs,11647
@@ -21,7 +21,7 @@ apprise/ConfigurationManager.py,sha256=MUmGajxjgnr6FGN7xb3q0nD0VVgdTdvapBBR7CsI-
apprise/NotificationManager.py,sha256=ZJgkiCgcJ7Bz_6bwQ47flrcxvLMbA4Vbw0HG_yTsGdE,2041
apprise/URLBase.py,sha256=ZWjHz69790EfVNDIBzWzRZzjw-gwC3db_t3_3an6cWI,28388
apprise/URLBase.pyi,sha256=WLaRREH7FzZ5x3-qkDkupojWGFC4uFwJ1EDt02lVs8c,520
-apprise/__init__.py,sha256=hqhBy0IX4xGRicwbKBMX_OVy1tgOo7hBrH_hG0n0XP4,3368
+apprise/__init__.py,sha256=oBHq9Zbcwz9DTkurqnEhbu9Q79a0TdVAZrWFIhlk__8,3368
apprise/assets/NotifyXML-1.0.xsd,sha256=292qQ_IUl5EWDhPyzm9UTT0C2rVvJkyGar8jiODkJs8,986
apprise/assets/NotifyXML-1.1.xsd,sha256=bjR3CGG4AEXoJjYkGCbDttKHSkPP1FlIWO02E7G59g4,1758
apprise/assets/themes/default/apprise-failure-128x128.ico,sha256=Mt0ptfHJaN3Wsv5UCNDn9_3lyEDHxVDv1JdaDEI_xCA,67646
@@ -50,7 +50,7 @@ apprise/attachment/AttachBase.pyi,sha256=w0XG_QKauiMLJ7eQ4S57IiLIURZHm_Snw7l6-ih
apprise/attachment/AttachFile.py,sha256=MbHY_av0GeM_AIBKV02Hq7SHiZ9eCr1yTfvDMUgi2I4,4765
apprise/attachment/AttachHTTP.py,sha256=dyDy3U47cI28ENhaw1r5nQlGh8FWHZlHI8n9__k8wcY,11995
apprise/attachment/__init__.py,sha256=xabgXpvV05X-YRuqIt3uGYMXwYNXjHyF6Dwd8HfZCFE,1658
-apprise/cli.py,sha256=Xl69ZR6dd9SkKqYErAiq2sSK89mXPwWr-QzHaJmK0Ic,20228
+apprise/cli.py,sha256=h-pWSQPqQficH6J-OEp3MTGydWyt6vMYnDZvHCeAt4Y,20697
apprise/common.py,sha256=I6wfrndggCL7l7KAl7Cm4uwAX9n0l3SN4-BVvTE0L0M,5593
apprise/common.pyi,sha256=luF3QRiClDCk8Z23rI6FCGYsVmodOt_JYfYyzGogdNM,447
apprise/config/ConfigBase.py,sha256=A4p_N9vSxOK37x9kuYeZFzHhAeEt-TCe2oweNi2KGg4,53062
@@ -67,7 +67,7 @@ apprise/emojis.py,sha256=ONF0t8dY9f2XlEkLUG79-ybKVAj2GqbPj2-Be97vAoI,87738
apprise/i18n/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
apprise/i18n/en/LC_MESSAGES/apprise.mo,sha256=oUTuHREmLEYN07oqYqRMJ_kU71-o5o37NsF4RXlC5AU,3959
apprise/logger.py,sha256=131hqhed8cUj9x_mfXDEvwA2YbcYDFAYiWVK1HgxRVY,6921
-apprise/manager.py,sha256=1KQVMAzq-wyZlzDBObKawQySah5F_Cq7LFdkmDctqDU,27086
+apprise/manager.py,sha256=R9w8jxQRNy6Z_XDcobkt4JYbrC4jtj2OwRw9Zrib3CA,26857
apprise/plugins/NotifyAppriseAPI.py,sha256=ISBE0brD3eQdyw3XrGXd4Uc4kSYvIuI3SSUVCt-bkdo,16654
apprise/plugins/NotifyAprs.py,sha256=IS1uxIl391L3i2LOK6x8xmlOG1W58k4o793Oq2W5Wao,24220
apprise/plugins/NotifyBark.py,sha256=bsDvKooRy4k1Gg7tvBjv3DIx7-WZiV_mbTrkTwMtd9Q,15698
@@ -108,6 +108,7 @@ apprise/plugins/NotifyKavenegar.py,sha256=F5xTUdebM1lK6yGFbZJQB9Zgw2LTI0angeA-3N
apprise/plugins/NotifyKumulos.py,sha256=eCEW2ZverZqETOLHVWMC4E8Ll6rEhhEWOSD73RD80SM,8214
apprise/plugins/NotifyLametric.py,sha256=h8vZoX-Ll5NBZRprBlxTO2H9w0lOiMxglGvUgJtK4_8,37534
apprise/plugins/NotifyLine.py,sha256=OVI0ozMJcq_-dI8dodVX52dzUzgENlAbOik-Kw4l-rI,10676
+apprise/plugins/NotifyLunaSea.py,sha256=woN8XdkwAjhgxAXp7Zj4XsWLybNL80l4W3Dx5BvobZg,14459
apprise/plugins/NotifyMQTT.py,sha256=PFLwESgR8dMZvVFHxmOZ8xfy-YqyX5b2kl_e8Z1lo-0,19537
apprise/plugins/NotifyMSG91.py,sha256=P7JPyT1xmucnaEeCZPf_6aJfe1gS_STYYwEM7hJ7QBw,12677
apprise/plugins/NotifyMSTeams.py,sha256=dFH575hoLL3zRddbBKfozlYjxvPJGbj3BKvfJSIkvD0,22976
diff --git a/libs/apprise-1.7.3.dist-info/REQUESTED b/libs/apprise-1.7.4.dist-info/REQUESTED
index e69de29bb..e69de29bb 100644
--- a/libs/apprise-1.7.3.dist-info/REQUESTED
+++ b/libs/apprise-1.7.4.dist-info/REQUESTED
diff --git a/libs/apprise-1.7.3.dist-info/WHEEL b/libs/apprise-1.7.4.dist-info/WHEEL
index ba48cbcf9..ba48cbcf9 100644
--- a/libs/apprise-1.7.3.dist-info/WHEEL
+++ b/libs/apprise-1.7.4.dist-info/WHEEL
diff --git a/libs/apprise-1.7.3.dist-info/entry_points.txt b/libs/apprise-1.7.4.dist-info/entry_points.txt
index 7f20ac9a3..7f20ac9a3 100644
--- a/libs/apprise-1.7.3.dist-info/entry_points.txt
+++ b/libs/apprise-1.7.4.dist-info/entry_points.txt
diff --git a/libs/apprise-1.7.3.dist-info/top_level.txt b/libs/apprise-1.7.4.dist-info/top_level.txt
index 9f8c12a76..9f8c12a76 100644
--- a/libs/apprise-1.7.3.dist-info/top_level.txt
+++ b/libs/apprise-1.7.4.dist-info/top_level.txt
diff --git a/libs/apprise/__init__.py b/libs/apprise/__init__.py
index c07d769ae..bb18eaec8 100644
--- a/libs/apprise/__init__.py
+++ b/libs/apprise/__init__.py
@@ -27,7 +27,7 @@
# POSSIBILITY OF SUCH DAMAGE.
__title__ = 'Apprise'
-__version__ = '1.7.3'
+__version__ = '1.7.4'
__author__ = 'Chris Caron'
__license__ = 'BSD'
__copywrite__ = 'Copyright (C) 2024 Chris Caron <[email protected]>'
diff --git a/libs/apprise/cli.py b/libs/apprise/cli.py
index 59a644272..11a6cbc2b 100644
--- a/libs/apprise/cli.py
+++ b/libs/apprise/cli.py
@@ -67,25 +67,30 @@ CONTEXT_SETTINGS = dict(help_option_names=['-h', '--help'])
DEFAULT_CONFIG_PATHS = (
# Legacy Path Support
'~/.apprise',
+ '~/.apprise.conf',
'~/.apprise.yml',
'~/.apprise.yaml',
'~/.config/apprise',
+ '~/.config/apprise.conf',
'~/.config/apprise.yml',
'~/.config/apprise.yaml',
# Plugin Support Extended Directory Search Paths
'~/.apprise/apprise',
+ '~/.apprise/apprise.conf',
'~/.apprise/apprise.yml',
'~/.apprise/apprise.yaml',
'~/.config/apprise/apprise',
+ '~/.config/apprise/apprise.conf',
'~/.config/apprise/apprise.yml',
'~/.config/apprise/apprise.yaml',
- # Global Configuration Support
+ # Global Configuration File Support
'/etc/apprise',
'/etc/apprise.yml',
'/etc/apprise.yaml',
'/etc/apprise/apprise',
+ '/etc/apprise/apprise.conf',
'/etc/apprise/apprise.yml',
'/etc/apprise/apprise.yaml',
)
@@ -104,9 +109,11 @@ if platform.system() == 'Windows':
# Default Config Search Path for Windows Users
DEFAULT_CONFIG_PATHS = (
expandvars('%APPDATA%\\Apprise\\apprise'),
+ expandvars('%APPDATA%\\Apprise\\apprise.conf'),
expandvars('%APPDATA%\\Apprise\\apprise.yml'),
expandvars('%APPDATA%\\Apprise\\apprise.yaml'),
expandvars('%LOCALAPPDATA%\\Apprise\\apprise'),
+ expandvars('%LOCALAPPDATA%\\Apprise\\apprise.conf'),
expandvars('%LOCALAPPDATA%\\Apprise\\apprise.yml'),
expandvars('%LOCALAPPDATA%\\Apprise\\apprise.yaml'),
@@ -116,16 +123,19 @@ if platform.system() == 'Windows':
# C:\ProgramData\Apprise\
expandvars('%ALLUSERSPROFILE%\\Apprise\\apprise'),
+ expandvars('%ALLUSERSPROFILE%\\Apprise\\apprise.conf'),
expandvars('%ALLUSERSPROFILE%\\Apprise\\apprise.yml'),
expandvars('%ALLUSERSPROFILE%\\Apprise\\apprise.yaml'),
# C:\Program Files\Apprise
expandvars('%PROGRAMFILES%\\Apprise\\apprise'),
+ expandvars('%PROGRAMFILES%\\Apprise\\apprise.conf'),
expandvars('%PROGRAMFILES%\\Apprise\\apprise.yml'),
expandvars('%PROGRAMFILES%\\Apprise\\apprise.yaml'),
# C:\Program Files\Common Files
expandvars('%COMMONPROGRAMFILES%\\Apprise\\apprise'),
+ expandvars('%COMMONPROGRAMFILES%\\Apprise\\apprise.conf'),
expandvars('%COMMONPROGRAMFILES%\\Apprise\\apprise.yml'),
expandvars('%COMMONPROGRAMFILES%\\Apprise\\apprise.yaml'),
)
diff --git a/libs/apprise/manager.py b/libs/apprise/manager.py
index d649afab7..c2b715d4f 100644
--- a/libs/apprise/manager.py
+++ b/libs/apprise/manager.py
@@ -365,67 +365,66 @@ class PluginManager(metaclass=Singleton):
# end of _import_module()
return
- with self._lock:
- for _path in paths:
- path = os.path.abspath(os.path.expanduser(_path))
- if (cache and path in self._paths_previously_scanned) \
- or not os.path.exists(path):
- # We're done as we've already scanned this
- continue
+ for _path in paths:
+ path = os.path.abspath(os.path.expanduser(_path))
+ if (cache and path in self._paths_previously_scanned) \
+ or not os.path.exists(path):
+ # We're done as we've already scanned this
+ continue
- # Store our path as a way of hashing it has been handled
- self._paths_previously_scanned.add(path)
+ # Store our path as a way of hashing it has been handled
+ self._paths_previously_scanned.add(path)
- if os.path.isdir(path) and not \
- os.path.isfile(os.path.join(path, '__init__.py')):
+ if os.path.isdir(path) and not \
+ os.path.isfile(os.path.join(path, '__init__.py')):
- logger.debug('Scanning for custom plugins in: %s', path)
- for entry in os.listdir(path):
- re_match = module_re.match(entry)
- if not re_match:
- # keep going
- logger.trace('Plugin Scan: Ignoring %s', entry)
- continue
+ logger.debug('Scanning for custom plugins in: %s', path)
+ for entry in os.listdir(path):
+ re_match = module_re.match(entry)
+ if not re_match:
+ # keep going
+ logger.trace('Plugin Scan: Ignoring %s', entry)
+ continue
- new_path = os.path.join(path, entry)
- if os.path.isdir(new_path):
- # Update our path
- new_path = os.path.join(path, entry, '__init__.py')
- if not os.path.isfile(new_path):
- logger.trace(
- 'Plugin Scan: Ignoring %s',
- os.path.join(path, entry))
- continue
-
- if not cache or \
- (cache and new_path not in
- self._paths_previously_scanned):
- # Load our module
- _import_module(new_path)
-
- # Add our subdir path
- self._paths_previously_scanned.add(new_path)
- else:
- if os.path.isdir(path):
- # This logic is safe to apply because we already
- # validated the directories state above; update our
- # path
- path = os.path.join(path, '__init__.py')
- if cache and path in self._paths_previously_scanned:
+ new_path = os.path.join(path, entry)
+ if os.path.isdir(new_path):
+ # Update our path
+ new_path = os.path.join(path, entry, '__init__.py')
+ if not os.path.isfile(new_path):
+ logger.trace(
+ 'Plugin Scan: Ignoring %s',
+ os.path.join(path, entry))
continue
- self._paths_previously_scanned.add(path)
+ if not cache or \
+ (cache and new_path not in
+ self._paths_previously_scanned):
+ # Load our module
+ _import_module(new_path)
- # directly load as is
- re_match = module_re.match(os.path.basename(path))
- # must be a match and must have a .py extension
- if not re_match or not re_match.group(1):
- # keep going
- logger.trace('Plugin Scan: Ignoring %s', path)
+ # Add our subdir path
+ self._paths_previously_scanned.add(new_path)
+ else:
+ if os.path.isdir(path):
+ # This logic is safe to apply because we already
+ # validated the directories state above; update our
+ # path
+ path = os.path.join(path, '__init__.py')
+ if cache and path in self._paths_previously_scanned:
continue
- # Load our module
- _import_module(path)
+ self._paths_previously_scanned.add(path)
+
+ # directly load as is
+ re_match = module_re.match(os.path.basename(path))
+ # must be a match and must have a .py extension
+ if not re_match or not re_match.group(1):
+ # keep going
+ logger.trace('Plugin Scan: Ignoring %s', path)
+ continue
+
+ # Load our module
+ _import_module(path)
return None
diff --git a/libs/apprise/plugins/NotifyLunaSea.py b/libs/apprise/plugins/NotifyLunaSea.py
new file mode 100644
index 000000000..51d820915
--- /dev/null
+++ b/libs/apprise/plugins/NotifyLunaSea.py
@@ -0,0 +1,440 @@
+# -*- coding: utf-8 -*-
+# BSD 2-Clause License
+#
+# Apprise - Push Notification Library.
+# Copyright (c) 2024, 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.
+#
+# 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://docs.lunasea.app/lunasea/notifications/custom-notifications
+#
+import re
+import requests
+from json import dumps
+
+from .NotifyBase import NotifyBase
+from ..common import NotifyType
+from ..common import NotifyImageSize
+from ..utils import parse_list
+from ..utils import is_hostname
+from ..utils import is_ipaddr
+from ..utils import parse_bool
+from ..AppriseLocale import gettext_lazy as _
+from ..URLBase import PrivacyMode
+
+
+class LunaSeaMode:
+ """
+ Define LunaSea Notification Modes
+ """
+ # App posts upstream to the developer API on LunaSea's website
+ CLOUD = "cloud"
+
+ # Running a dedicated private ntfy Server
+ PRIVATE = "private"
+
+
+LUNASEA_MODES = (
+ LunaSeaMode.CLOUD,
+ LunaSeaMode.PRIVATE,
+)
+
+
+class NotifyLunaSea(NotifyBase):
+ """
+ A wrapper for LunaSea Notifications
+ """
+
+ # The default descriptive name associated with the Notification
+ service_name = 'LunaSea'
+
+ # The services URL
+ service_url = 'https://luasea.app'
+
+ # The default insecure protocol
+ protocol = ('lunasea', 'lsea')
+
+ # The default secure protocol
+ secure_protocol = ('lunaseas', 'lseas')
+
+ # A URL that takes you to the setup/help of the specific protocol
+ setup_url = 'https://github.com/caronc/apprise/wiki/Notify_lunasea'
+
+ # Allows the user to specify the NotifyImageSize object
+ image_size = NotifyImageSize.XY_256
+
+ # LunaSea Notification Details
+ cloud_notify_url = 'https://notify.lunasea.app'
+ notify_user_path = '/v1/custom/user/{}'
+ notify_device_path = '/v1/custom/device/{}'
+
+ # if our hostname matches the following we automatically enforce
+ # cloud mode
+ __auto_cloud_host = re.compile(r'(notify\.)?lunasea\.app', re.IGNORECASE)
+
+ # Define object templates
+ templates = (
+ '{schema}://{targets}',
+ '{schema}://{host}/{targets}',
+ '{schema}://{host}:{port}/{targets}',
+ '{schema}://{user}@{host}/{targets}',
+ '{schema}://{user}@{host}:{port}/{targets}',
+ '{schema}://{user}:{password}@{host}/{targets}',
+ '{schema}://{user}:{password}@{host}:{port}/{targets}',
+ )
+
+ # Define our template tokens
+ template_tokens = dict(NotifyBase.template_tokens, **{
+ 'host': {
+ 'name': _('Hostname'),
+ 'type': 'string',
+ },
+ 'port': {
+ 'name': _('Port'),
+ 'type': 'int',
+ 'min': 1,
+ 'max': 65535,
+ },
+ 'user': {
+ 'name': _('Username'),
+ 'type': 'string',
+ },
+ 'password': {
+ 'name': _('Password'),
+ 'type': 'string',
+ 'private': True,
+ },
+ 'token': {
+ 'name': _('Token'),
+ 'type': 'string',
+ 'private': True,
+ },
+ 'target_user': {
+ 'name': _('Target User'),
+ 'type': 'string',
+ 'prefix': '@',
+ 'map_to': 'targets',
+ },
+ 'target_device': {
+ 'name': _('Target Device'),
+ 'type': 'string',
+ 'prefix': '+',
+ 'map_to': 'targets',
+ },
+ 'targets': {
+ 'name': _('Targets'),
+ 'type': 'list:string',
+ 'required': True,
+ },
+ })
+
+ # Define our template arguments
+ template_args = dict(NotifyBase.template_args, **{
+ 'to': {
+ 'alias_of': 'targets',
+ },
+ 'image': {
+ 'name': _('Include Image'),
+ 'type': 'bool',
+ 'default': False,
+ 'map_to': 'include_image',
+ },
+ 'mode': {
+ 'name': _('Mode'),
+ 'type': 'choice:string',
+ 'values': LUNASEA_MODES,
+ 'default': LunaSeaMode.PRIVATE,
+ },
+ })
+
+ def __init__(self, targets=None, mode=None, token=None,
+ include_image=False, **kwargs):
+ """
+ Initialize LunaSea Object
+ """
+ super().__init__(**kwargs)
+
+ # Show image associated with notification
+ self.include_image = \
+ self.template_args['image']['default'] \
+ if include_image is None else include_image
+
+ # Prepare our mode
+ self.mode = mode.strip().lower() \
+ if isinstance(mode, str) \
+ else self.template_args['mode']['default']
+
+ if self.mode not in LUNASEA_MODES:
+ msg = 'An invalid LunaSea mode ({}) was specified.'.format(mode)
+ self.logger.warning(msg)
+ raise TypeError(msg)
+
+ self.targets = []
+ for target in parse_list(targets):
+ if len(target) < 4:
+ self.logger.warning(
+ 'A specified target ({}) is invalid and will be '
+ 'ignored'.format(target))
+ continue
+
+ if target[0] == '+':
+ # Device
+ self.targets.append(('+', target[1:]))
+
+ elif target[0] == '@':
+ # User
+ self.targets.append(('@', target[1:]))
+
+ else:
+ # User
+ self.targets.append(('@', target))
+
+ return
+
+ def send(self, body, title='', notify_type=NotifyType.INFO, **kwargs):
+ """
+ Perform LunaSea Notification
+ """
+
+ # error tracking (used for function return)
+ has_error = False
+
+ if not len(self.targets):
+ # We have nothing to notify; we're done
+ self.logger.warning('There are no LunaSea targets to notify')
+ return False
+
+ # Prepare our headers
+ headers = {
+ 'User-Agent': self.app_id,
+ 'Content-Type': 'application/json',
+ 'Accept': 'application/json',
+ }
+
+ # prepare payload
+ payload = {
+ 'title': title if title else self.app_desc,
+ 'body': body,
+ }
+
+ # Acquire image_url
+ image_url = None if not self.include_image \
+ else self.image_url(notify_type)
+
+ if image_url:
+ payload['image'] = image_url
+
+ # Prepare our Authentication (if defined)
+ if self.user and self.password:
+ auth = (self.user, self.password)
+
+ else:
+ # No Auth
+ auth = None
+
+ if self.mode == LunaSeaMode.CLOUD:
+ # Cloud Service
+ notify_url = self.cloud_notify_url
+
+ else:
+ # Local Hosting
+ schema = 'https' if self.secure else 'http'
+
+ notify_url = '%s://%s' % (schema, self.host)
+ if isinstance(self.port, int):
+ notify_url += ':%d' % self.port
+
+ # Create a copy of the targets list
+ targets = list(self.targets)
+ while len(targets):
+ target = targets.pop(0)
+
+ if target[0] == '+':
+ url = notify_url + self.notify_device_path.format(target[1])
+
+ else:
+ url = notify_url + self.notify_user_path.format(target[1])
+
+ self.logger.debug('LunaSea POST URL: %s (cert_verify=%r)' % (
+ url, self.verify_certificate,
+ ))
+ self.logger.debug('LunaSea Payload: %s' % str(payload))
+
+ # Always call throttle before any remote server i/o is made
+ self.throttle()
+
+ try:
+ r = requests.post(
+ url,
+ data=dumps(payload),
+ headers=headers,
+ auth=auth,
+ verify=self.verify_certificate,
+ timeout=self.request_timeout,
+ )
+
+ if r.status_code not in (
+ requests.codes.ok, requests.codes.no_content):
+ # We had a problem
+ status_str = \
+ NotifyLunaSea.http_response_code_lookup(r.status_code)
+
+ self.logger.warning(
+ 'Failed to deliver payload to LunaSea:'
+ '{}{}error={}.'.format(
+ status_str,
+ ', ' if status_str else '',
+ r.status_code))
+
+ self.logger.debug(
+ 'Response Details:\r\n{}'.format(r.content))
+
+ has_error = True
+
+ # otherwise we were successful
+ continue
+
+ except requests.RequestException as e:
+ self.logger.warning(
+ 'A Connection error occurred communicating with LunaSea.')
+ self.logger.debug('Socket Exception: %s' % str(e))
+
+ has_error = True
+
+ return not has_error
+
+ def url(self, privacy=False, *args, **kwargs):
+ """
+ Returns the URL built dynamically based on specified arguments.
+ """
+
+ params = {
+ 'mode': self.mode,
+ 'image': 'yes' if self.include_image else 'no',
+ }
+
+ # Our URL parameters
+ params.update(self.url_parameters(privacy=privacy, *args, **kwargs))
+
+ auth = ''
+ if self.user and self.password:
+ auth = '{user}:{password}@'.format(
+ user=NotifyLunaSea.quote(self.user, safe=''),
+ password=self.pprint(
+ self.password, privacy, mode=PrivacyMode.Secret,
+ safe=''),
+ )
+ elif self.user:
+ auth = '{user}@'.format(
+ user=NotifyLunaSea.quote(self.user, safe=''),
+ )
+
+ if self.mode == LunaSeaMode.PRIVATE:
+ default_port = 443 if self.secure else 80
+ return '{schema}://{auth}{host}{port}/{targets}?{params}'.format(
+ schema=self.secure_protocol[0]
+ if self.secure else self.protocol[0],
+ auth=auth,
+ host=self.host,
+ port='' if self.port is None or self.port == default_port
+ else ':{}'.format(self.port),
+ targets='/'.join(
+ [NotifyLunaSea.quote(x[0] + x[1], safe='@+')
+ for x in self.targets]),
+ params=NotifyLunaSea.urlencode(params)
+ )
+
+ else: # Cloud mode
+ return '{schema}://{auth}{targets}?{params}'.format(
+ schema=self.protocol[0],
+ auth=auth,
+ targets='/'.join(
+ [NotifyLunaSea.quote(x[0] + x[1], safe='@+')
+ for x in self.targets]),
+ params=NotifyLunaSea.urlencode(params)
+ )
+
+ def __len__(self):
+ """
+ Returns the number of targets associated with this notification
+ """
+ # always return 1
+ return 1 if not self.targets else len(self.targets)
+
+ @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
+
+ # Fetch our targets
+ results['targets'] = NotifyLunaSea.split_path(results['fullpath'])
+
+ # Boolean to include an image or not
+ results['include_image'] = parse_bool(results['qsd'].get(
+ 'image', NotifyLunaSea.template_args['image']['default']))
+
+ # The 'to' makes it easier to use yaml configuration
+ if 'to' in results['qsd'] and len(results['qsd']['to']):
+ results['targets'] += \
+ NotifyLunaSea.parse_list(results['qsd']['to'])
+
+ # Mode override
+ if 'mode' in results['qsd'] and results['qsd']['mode']:
+ results['mode'] = NotifyLunaSea.unquote(
+ results['qsd']['mode'].strip().lower())
+
+ else:
+ # We can try to detect the mode based on the validity of the
+ # hostname.
+ #
+ # This isn't a surfire way to do things though; it's best to
+ # specify the mode= flag
+ results['mode'] = LunaSeaMode.PRIVATE \
+ if ((is_hostname(results['host'])
+ or is_ipaddr(results['host'])) and results['targets']) \
+ else LunaSeaMode.CLOUD
+
+ if results['mode'] == LunaSeaMode.CLOUD:
+ # Store first entry as it can be a topic too in this case
+ # But only if we also rule it out not being the words
+ # lunasea.app itself, something that starts wiht an non-alpha
+ # numeric character:
+ if not NotifyLunaSea.__auto_cloud_host.search(results['host']):
+ # Add it to the front of the list for consistency
+ results['targets'].insert(0, results['host'])
+
+ elif results['mode'] == LunaSeaMode.PRIVATE and \
+ not (is_hostname(results['host'] or
+ is_ipaddr(results['host']))):
+ # Invalid Host for LunaSeaMode.PRIVATE
+ return None
+
+ return results
diff --git a/libs/version.txt b/libs/version.txt
index 0e1dd8c78..bfd019444 100644
--- a/libs/version.txt
+++ b/libs/version.txt
@@ -2,7 +2,7 @@
alembic==1.13.1
aniso8601==9.0.1
argparse==1.4.0
-apprise==1.7.3
+apprise==1.7.4
apscheduler<=3.10.4
attrs==23.2.0
blinker==1.7.0