From 3cb270cee8d1e36e4fb21e15de4b1588ee3ee50d Mon Sep 17 00:00:00 2001
From: Chris Caron
Date: Sat, 24 Aug 2024 19:12:23 -0400
Subject: [PATCH] Documentation and CLI Tidying (#1187)
---
README.md | 42 +++++++-------
apprise/cli.py | 55 ++++++++++++------
apprise/decorators/base.py | 3 +
apprise/plugins/base.py | 2 +-
packaging/man/apprise.1 | 90 +++++++++++++++++++++++++-----
packaging/man/apprise.1.html | 102 ++++++++++++++++++++++++++++------
packaging/man/apprise.md | 96 ++++++++++++++++++++++++++------
test/test_apprise_cli.py | 35 +++++++++++-
test/test_persistent_store.py | 5 ++
9 files changed, 342 insertions(+), 88 deletions(-)
diff --git a/README.md b/README.md
index 6ffebaec..164296d8 100644
--- a/README.md
+++ b/README.md
@@ -540,15 +540,17 @@ You can read more about creating your own custom notifications and/or hooks [her
Persistent storage allows Apprise to cache re-occurring actions optionaly to disk. This can greatly reduce the overhead used to send a notification.
-There are 3 options Apprise can operate using this:
-1. `AUTO`: Flush any gathered data for persistent storage on demand. This option is incredibly light weight. This is the default behavior for all CLI usage. Content can be manually flushed to disk using this option as well should a developer choose to do so. The CLI uses this option by default and only writes anything accumulated to disk after all of it's notifications have completed.
-1. `FLUSH`: Flushes any gathered data for persistent storage as often as it is acquired.
-1. `MEMORY`: Only store information in memory, never write to disk. This is the option one would set if they simply wish to disable Persistent Storage entirely. By default this is the mode used by the API and is at the developers discretion to enable one of the other options.
+There are 3 Persistent Storage operational states Apprise can operate using:
+1. `auto`: Flush gathered cache information to the filesystem on demand. This option is incredibly light weight. This is the default behavior for all CLI usage.
+ * Developers who choose to use this operational mode can also force cached information manually if they choose.
+ * The CLI will use this operational mode by default.
+1. `flush`: Flushes any cache information to the filesystem during every transaction.
+1. `memory`: Effectively disable Persistent Storage. Any caching of data required by each plugin used is done in memory. Apprise effectively operates as it always did before peristent storage was available. This setting ensures no content is every written to disk.
+ * By default this is the mode Apprise will operate under for those developing with it unless they configure it to otherwise operate as `auto` or `flush`. This is done through the `AppriseAsset()` object and is explained further on in this documentation.
## CLI Persistent Storage Commands
-Persistent storage is set to `AUTO` mode by default.
-Specifying the keyword `storage` will assume that all subseqent calls are related to the storage subsection of Apprise.
+You can provide the keyword `storage` on your CLI call to see the persistent storage options available to you.
```bash
# List all of the occupied space used by Apprise's Persistent Storage:
apprise storage list
@@ -579,12 +581,12 @@ You can also filter your results by adding tags and/or URL Identifiers. When yo
3. abcd123 12.00B stale
```
-The states are:
+The (persistent storage) cache states are:
- `unused`: This plugin has not commited anything to disk for reuse/cache purposes
- `active`: This plugin has written content to disk. Or at the very least, it has prepared a persistent storage location it can write into.
- `stale`: The system detected a location where a URL may have possibly written to in the past, but there is nothing linking to it using the URLs provided. It is likely wasting space or is no longer of any use.
-You can use this information to filter your results by specifying _URL ID_ values after your command. For example:
+You can use this information to filter your results by specifying _URL ID_ (UID) values after your command. For example:
```bash
# The below commands continue with the example already identified above
# the following would match abcd123 (even though just ab was provided)
@@ -603,13 +605,14 @@ apprise storage list --tag=team
# The followin would actually match the URL ID's of 1. and .2 above
apprise storage list f 0
```
+When using the CLI, Persistent storage is set to the operational mode of `auto` by default, you can change this by providing `--storage-mode=` (`-SM`) during your calls. If you want to ensure it's always set to a value of your choice.
For more information on persistent storage, [visit here](https://github.com/caronc/apprise/wiki/persistent_storage).
-
## API Persistent Storage Commands
-By default, no persistent storage is set to be in `MEMORY` mode for those building from within the Apprise API.
-It's at the developers discretion to enable it. But should you choose to do so, it's as easy as including the information in the `AppriseAsset()` object prior to the initialization of your `Apprise()` instance.
+For developers, persistent storage is set in the operational mode of `memory` by default.
+
+It's at the developers discretion to enable it (by switching it to either `auto` or `flush`). Should you choose to do so: it's as easy as including the information in the `AppriseAsset()` object prior to the initialization of your `Apprise()` instance.
For example:
```python
@@ -617,12 +620,13 @@ from apprise import Apprise
from apprise import AppriseAsset
from apprise import PersistentStoreMode
-# Prepare a location the persistent storage can write to
-# This immediately assumes you wish to write in AUTO mode
+# Prepare a location the persistent storage can write it's cached content to.
+# By setting this path, this immediately assumes you wish to operate the
+# persistent storage in the operational 'auto' mode
asset = AppriseAsset(storage_path="/path/to/save/data")
-# If you want to be more explicit and set more options, then
-# you may do the following
+# If you want to be more explicit and set more options, then you may do the
+# following
asset = AppriseAsset(
# Set our storage path directory (minimum requirement to enable it)
storage_path="/path/to/save/data",
@@ -636,10 +640,10 @@ asset = AppriseAsset(
# - write to disk always and often
storage_mode=PersistentStoreMode.FLUSH
- # the URL IDs are by default 8 characters in length, there is
- # really no reason to change this. You can increase/decrease
- # it's value here. Must be > 2; default is 8 if not specified
- storage_idlen=6,
+ # The URL IDs are by default 8 characters in length. You can increase and
+ # decrease it's value here. The value must be > 2. The default value is 8
+ # if not otherwise specified
+ storage_idlen=8,
)
# Now that we've got our asset, we just work with our Apprise object as we
diff --git a/apprise/cli.py b/apprise/cli.py
index 92dca428..2834efe9 100644
--- a/apprise/cli.py
+++ b/apprise/cli.py
@@ -60,6 +60,14 @@ from . import __copywrite__
# files.
DEFAULT_RECURSION_DEPTH = 1
+# Default number of days to prune persistent storage
+DEFAULT_STORAGE_PRUNE_DAYS = \
+ int(os.environ.get('APPRISE_STORAGE_PRUNE_DAYS', 30))
+
+# The default URL ID Length
+DEFAULT_STORAGE_UID_LENGTH = \
+ int(os.environ.get('APPRISE_STORAGE_UID_LENGTH', 8))
+
# Defines our click context settings adding -h to the additional options that
# can be specified to get the help menu to come up
CONTEXT_SETTINGS = dict(help_option_names=['-h', '--help'])
@@ -190,9 +198,9 @@ PERSISTENT_STORAGE_MODES = (
PersistentStorageMode.CLEAR,
)
-if os.environ.get('APPRISE_STORAGE', '').strip():
+if os.environ.get('APPRISE_STORAGE_PATH', '').strip():
# Over-ride Default Storage Path
- DEFAULT_STORAGE_PATH = os.environ.get('APPRISE_STORAGE')
+ DEFAULT_STORAGE_PATH = os.environ.get('APPRISE_STORAGE_PATH')
def print_version_msg():
@@ -210,7 +218,14 @@ def print_version_msg():
class CustomHelpCommand(click.Command):
def format_help(self, ctx, formatter):
+ formatter.write_text('Usage:')
+ formatter.write_text(
+ ' apprise [OPTIONS] [APPRISE_URL [APPRISE_URL2 [APPRISE_URL3]]]')
+ formatter.write_text(
+ ' apprise storage [OPTIONS] [ACTION] [UID1 [UID2 [UID3]]]')
+
# Custom help message
+ formatter.write_text('')
content = (
'Send a notification to all of the specified servers '
'identified by their URLs',
@@ -315,25 +330,27 @@ class CustomHelpCommand(click.Command):
help='Specify the message title. This field is complete '
'optional.')
@click.option('--plugin-path', '-P', default=None, type=str, multiple=True,
- metavar='PLUGIN_PATH',
+ metavar='PATH',
help='Specify one or more plugin paths to scan.')
@click.option('--storage-path', '-S', default=DEFAULT_STORAGE_PATH, type=str,
- metavar='STORAGE_PATH',
+ metavar='PATH',
help='Specify the path to the persistent storage location '
'(default={}).'.format(DEFAULT_STORAGE_PATH))
-@click.option('--storage-prune-days', '-SPD', default=30,
- type=int,
+@click.option('--storage-prune-days', '-SPD',
+ default=DEFAULT_STORAGE_PRUNE_DAYS, type=int,
help='Define the number of days the storage prune '
'should run using. Setting this to zero (0) will eliminate '
- 'all accumulated content. By default this value is 30 (days).')
-@click.option('--storage-uid-length', '-SUL', default=8,
- type=int,
+ 'all accumulated content. By default this value is {} days.'
+ .format(DEFAULT_STORAGE_PRUNE_DAYS))
+@click.option('--storage-uid-length', '-SUL',
+ default=DEFAULT_STORAGE_UID_LENGTH, type=int,
help='Define the number of unique characters to store persistent'
- 'cache in. By default this value is 6 (characters).')
+ 'cache in. By default this value is {} characters.'
+ .format(DEFAULT_STORAGE_UID_LENGTH))
@click.option('--storage-mode', '-SM', default=PERSISTENT_STORE_MODES[0],
type=str, metavar='MODE',
- help='Persistent disk storage write mode (default={}). '
- 'Possible values are "{}", and "{}".'.format(
+ help='Specify the persistent storage operational mode '
+ '(default={}). Possible values are "{}", and "{}".'.format(
PERSISTENT_STORE_MODES[0], '", "'.join(
PERSISTENT_STORE_MODES[:-1]),
PERSISTENT_STORE_MODES[-1]))
@@ -527,6 +544,9 @@ def main(ctx, body, title, config, attach, urls, notification_type, theme, tag,
# Create our Apprise object
a = Apprise(asset=asset, debug=debug, location=ContentLocation.LOCAL)
+ # Track if we are performing a storage action
+ storage_action = True if urls and 'storage'.startswith(urls[0]) else False
+
if details:
# Print details and exit
results = a.details(show_requirements=True, show_disabled=True)
@@ -615,7 +635,7 @@ def main(ctx, body, title, config, attach, urls, notification_type, theme, tag,
# 4. Configuration by environment variable: APPRISE_CONFIG
# 5. Default Configuration File(s) (if found)
#
- elif urls and not 'storage'.startswith(urls[0]):
+ elif urls and not storage_action:
if tag:
# Ignore any tags specified
logger.warning(
@@ -662,11 +682,10 @@ def main(ctx, body, title, config, attach, urls, notification_type, theme, tag,
paths=[f for f in DEFAULT_CONFIG_PATHS if isfile(path_decode(f))],
asset=asset, recursion=recursion_depth))
- if len(a) == 0 and not urls:
+ if not dry_run and not (a or storage_action):
logger.error(
'You must specify at least one server URL or populated '
'configuration file.')
- click.echo(ctx.get_help())
ctx.exit(1)
# each --tag entry comprises of a comma separated 'and' list
@@ -675,7 +694,7 @@ def main(ctx, body, title, config, attach, urls, notification_type, theme, tag,
# Determine if we're dealing with URLs or url_ids based on the first
# entry provided.
- if urls and 'storage'.startswith(urls[0]):
+ if storage_action:
#
# Storage Mode
# - urls are now to be interpreted as best matching namespaces
@@ -694,7 +713,9 @@ def main(ctx, body, title, config, attach, urls, notification_type, theme, tag,
# 5 characters are already reserved for the counter on the left
(columns, _) = shutil.get_terminal_size(fallback=(80, 24))
+ # Pop 'storage' off of the head of our list
filter_uids = urls[1:]
+
action = PERSISTENT_STORAGE_MODES[0]
if filter_uids:
_action = next( # pragma: no branch
@@ -702,7 +723,7 @@ def main(ctx, body, title, config, attach, urls, notification_type, theme, tag,
if a.startswith(filter_uids[0])), None)
if _action:
- # pop top entry
+ # pop 'action' off the head of our list
filter_uids = filter_uids[1:]
action = _action
diff --git a/apprise/decorators/base.py b/apprise/decorators/base.py
index 2661db0a..688fd96d 100644
--- a/apprise/decorators/base.py
+++ b/apprise/decorators/base.py
@@ -58,6 +58,9 @@ class CustomNotifyPlugin(NotifyBase):
# Support Attachments
attachment_support = True
+ # Allow persistent storage support
+ storage_mode = common.PersistentStoreMode.AUTO
+
# Define object templates
templates = (
'{schema}://',
diff --git a/apprise/plugins/base.py b/apprise/plugins/base.py
index 8e142be9..1c490def 100644
--- a/apprise/plugins/base.py
+++ b/apprise/plugins/base.py
@@ -132,7 +132,7 @@ class NotifyBase(URLBase):
# of lines. Setting this to zero disables this feature.
body_max_line_count = 0
- # Persistent storage default html settings
+ # Persistent storage default settings
persistent_storage = True
# Default Notify Format
diff --git a/packaging/man/apprise.1 b/packaging/man/apprise.1
index f17c4617..90479d42 100644
--- a/packaging/man/apprise.1
+++ b/packaging/man/apprise.1
@@ -1,11 +1,13 @@
.\" generated with Ronn-NG/v0.9.1
.\" http://github.com/apjanke/ronn-ng/tree/0.9.1
-.TH "APPRISE" "1" "July 2024" "Chris Caron "
+.TH "APPRISE" "1" "August 2024" "Chris Caron "
.SH "NAME"
\fBapprise\fR \- Push Notifications that work with just about every platform!
.SH "SYNOPSIS"
\fBapprise\fR [\fIoptions\fR\|\.\|\.\|\.] \fIservice\-url\fR\|\.\|\.\|\.
.br
+\fBapprise\fR storage [\fIoptions\fR\|\.\|\.\|\.] [\fIaction\fR] \fIurl\-id\fR\|\.\|\.\|\.
+.br
.SH "DESCRIPTION"
\fBApprise\fR allows you to send a notification to \fIalmost all\fR of the most popular notification services available to us today such as: Discord, Telegram, Pushbullet, Slack, Twitter, etc\.
.IP "\[ci]" 4
@@ -14,38 +16,50 @@ One notification library to rule them all\.
A common and intuitive notification syntax\.
.IP "\[ci]" 4
Supports the handling of images (to the notification services that will accept them)\.
+.IP "\[ci]" 4
+It\'s incredibly lightweight\.
+.IP "\[ci]" 4
+Amazing response times because all messages sent asynchronously\.
.IP "" 0
.SH "OPTIONS"
The Apprise options are as follows:
.P
-\fB\-b\fR, \fB\-\-body=\fR\fITEXT\fR: Specify the message body\. If no body is specified then content is read from \fIstdin\fR\.
+\fB\-b\fR, \fB\-\-body=\fR\fIVALUE\fR: Specify the message body\. If no body is specified then content is read from \fIstdin\fR\.
.P
-\fB\-t\fR, \fB\-\-title=\fR\fITEXT\fR: Specify the message title\. This field is complete optional\.
+\fB\-t\fR, \fB\-\-title=\fR\fIVALUE\fR: Specify the message title\. This field is complete optional\.
.P
\fB\-c\fR, \fB\-\-config=\fR\fICONFIG\-URL\fR: Specify one or more configuration locations\.
.P
\fB\-a\fR, \fB\-\-attach=\fR\fIATTACH\-URL\fR: Specify one or more file attachment locations\.
.P
-\fB\-P\fR, \fB\-\-plugin\-path=\fR\fIPLUGIN\-PATH\fR: Specify a path to scan for custom notification plugin support\. You can create your own notification by simply creating a Python file that contains the \fB@notify("schema")\fR decorator\.
+\fB\-P\fR, \fB\-\-plugin\-path=\fR\fIPATH\fR: Specify a path to scan for custom notification plugin support\. You can create your own notification by simply creating a Python file that contains the \fB@notify("schema")\fR decorator\.
.P
You can optioanly chose to specify more then one \fB\-\-plugin\-path\fR (\fB\-P\fR) to increase the modules included\.
.P
-\fB\-n\fR, \fB\-\-notification\-type=\fR\fITYPE\fR: Specify the message type (default=info)\. Possible values are "info", "success", "failure", and "warning"\.
+\fB\-n\fR, \fB\-\-notification\-type=\fR\fIVALUE\fR: Specify the message type (default=info)\. Possible values are "info", "success", "failure", and "warning"\.
.P
-\fB\-i\fR, \fB\-\-input\-format=\fR\fIFORMAT\fR: Specify the input message format (default=text)\. Possible values are "text", "html", and "markdown"\.
+\fB\-i\fR, \fB\-\-input\-format=\fR\fIVALUE\fR: Specify the input message format (default=text)\. Possible values are "text", "html", and "markdown"\.
.P
-\fB\-T\fR, \fB\-\-theme=\fRTHEME: Specify the default theme\.
+\fB\-T\fR, \fB\-\-theme=\fR\fIVALUE\fR: Specify the default theme\.
.P
-\fB\-g\fR, \fB\-\-tag=\fRTAG: Specify one or more tags to filter which services to notify\. Use multiple \fB\-\-tag\fR (\fB\-g\fR) entries to \fBOR\fR the tags together and comma separated to \fBAND\fR them\. If no tags are specified then all services are notified\.
+\fB\-g\fR, \fB\-\-tag=\fR\fIVALUE\fR: Specify one or more tags to filter which services to notify\. Use multiple \fB\-\-tag\fR (\fB\-g\fR) entries to \fBOR\fR the tags together and comma separated to \fBAND\fR them\. If no tags are specified then all services are notified\.
.P
\fB\-Da\fR, \fB\-\-disable\-async\fR: Send notifications synchronously (one after the other) instead of all at once\.
.P
-\fB\-R\fR, \fB\-\-recursion\-depth\fR: he number of recursive import entries that can be loaded from within Apprise configuration\. By default this is set to 1\. If this is set to zero, then import statements found in any configuration is ignored\.
+\fB\-R\fR, \fB\-\-recursion\-depth\fR\fIINTEGER\fR: he number of recursive import entries that can be loaded from within Apprise configuration\. By default this is set to 1\. If this is set to zero, then import statements found in any configuration is ignored\.
.P
\fB\-e\fR, \fB\-\-interpret\-escapes\fR Enable interpretation of backslash escapes\. For example, this would convert sequences such as \en and \er to their respected ascii new\-line and carriage
.P
\fB\-j\fR, \fB\-\-interpret\-emojis\fR Enable interpretation of emoji strings\. For example, this would convert sequences such as :smile: or :grin: to their respected unicode emoji character\.
.P
+\fB\-S\fR, \fB\-\-storage\-path=\fR\fIPATH\fR: Specify the path to the persistent storage caching location
+.P
+\fB\-SM\fR, \fB\-\-storage\-mode=\fR\fIMODE\fR: Specify the persistent storage operational mode\. Possible values are "auto", "flush", and "memory"\. The default is "auto" not not specified\.
+.P
+\fB\-SPD\fR, \fB\-\-storage\-prune\-days=\fR\fIINTEGER\fR: Define the number of days the storage prune should run using\. Setting this to zero (0) will eliminate all accumulated content\. By default this value is 30 (days)\.
+.P
+\fB\-SUL\fR, \fB\-\-storage\-uid\-length=\fR\fIINTEGER\fR: Define the number of unique characters to store persistent cache in\. By default this value is 8 (characters)\.
+.P
\fB\-d\fR, \fB\-\-dry\-run\fR: Perform a trial run but only prints the notification services to\-be triggered to \fBstdout\fR\. Notifications are never sent using this mode\.
.P
return characters prior to the delivery of the notification\.
@@ -59,6 +73,28 @@ return characters prior to the delivery of the notification\.
\fB\-V\fR, \fB\-\-version\fR: Display the apprise version and exit\.
.P
\fB\-h\fR, \fB\-\-help\fR: Show this message and exit\.
+.SH "PERSISTENT STORAGE"
+Persistent storage by default writes to the following location unless the environment variable \fBAPPRISE_STORAGE_PATH\fR over\-rides it and/or \fB\-\-storage\-path\fR (\fB\-SP\fR) is specified to over\-ride it:
+.IP "" 4
+.nf
+~/\.local/share/apprise/cache
+.fi
+.IP "" 0
+.P
+To utilize the persistent storage \fIhttps://github\.com/caronc/apprise/wiki/persistent_storage\fR element associated with Apprise, simply specify the keyword \fBstorage\fR
+.IP "" 4
+.nf
+$ apprise storage
+.fi
+.IP "" 0
+.P
+The \fBstorage\fR action has the following sub actions:
+.P
+\fBlist\fR: List all of the detected persistent storage elements and their state (\fBstale\fR, \fBactive\fR, or \fBunused\fR)\. This is the default action if nothing further is identified\.
+.P
+\fBprune\fR: Removes all persistent storage that has not been referenced for more then 30 days\. You can optionally set the \fB\-\-storage\-prune\-days\fR to alter this default value\.
+.P
+\fBclean\fR: Removes all persistent storage reguardless of age\.
.SH "EXIT STATUS"
\fBapprise\fR exits with a status of:
.IP "\[ci]" 4
@@ -114,6 +150,27 @@ $ apprise \-vv \-t "School Assignment" \-b "See attached" \e
\-\-attach=Documents/FinalReport\.docx
.fi
.IP "" 0
+.P
+List all of the notifications loaded:
+.IP "" 4
+.nf
+$ apprise \-\-dry\-run \-\-tag=all
+.fi
+.IP "" 0
+.P
+List all of the details around the current persistent storage setup:
+.IP "" 4
+.nf
+$ apprise storage list
+.fi
+.IP "" 0
+.P
+Prune all persistent storage that has not been referenced for at least 10 days or more
+.IP "" 4
+.nf
+$ apprise storage prune \-\-storage\-prune\-days=10
+.fi
+.IP "" 0
.SH "CUSTOM PLUGIN/NOTIFICATIONS"
Apprise can additionally allow you to define your own custom \fBschema://\fR entries that you can trigger on and call services you\'ve defined\.
.P
@@ -134,15 +191,15 @@ Simply create your own python file with the following bare minimum content in it
@notify(on="foobar", name="My Custom Notification")
def my_wrapper(body, title, notify_type, *args, **kwargs):
-
+ print("Define your custom code here")
- # Returning True/False is a way to relay your status back to Apprise\.
- # Returning nothing (None by default) is always interpreted as a Success
+ # Returning True/False will relay your status back through Apprise
+ # Returning nothing (None by default) is always interpreted as True
return True
.fi
.IP "" 0
.SH "CONFIGURATION"
-A configuration file can be in the format of either \fBTEXT\fR or \fBYAML\fR where [TEXT][textconfig] is the easiest and most ideal solution for most users\. However YAML \fIhttps://github\.com/caronc/apprise/wiki/config_yaml\fR configuration files grants the user a bit more leverage and access to some of the internal features of Apprise\. Reguardless of which format you choose, both provide the users the ability to leverage \fBtagging\fR which adds a more rich and powerful notification environment\.
+A configuration file can be in the format of either \fBTEXT\fR or \fBYAML\fR where TEXT \fIhttps://github\.com/caronc/apprise/wiki/config_text\fR is the easiest and most ideal solution for most users\. However YAML \fIhttps://github\.com/caronc/apprise/wiki/config_yaml\fR configuration files grants the user a bit more leverage and access to some of the internal features of Apprise\. Reguardless of which format you choose, both provide the users the ability to leverage \fBtagging\fR which adds a more rich and powerful notification environment\.
.P
Configuration files can be directly referenced via \fBapprise\fR when referencing the \fB\-\-config=\fR (\fB\-c\fR) CLI directive\. You can identify as many as you like on the command line and all of them will be loaded\. You can also point your configuration to a cloud location (by referencing \fBhttp://\fR or \fBhttps://\fR\. By default \fBapprise\fR looks in the following local locations for configuration files and loads them:
.IP "" 4
@@ -176,11 +233,14 @@ $ apprise \-vv \-t "my title" \-b "my notification body"
If you leveraged tagging \fIhttps://github\.com/caronc/apprise/wiki/CLI_Usage#label\-leverage\-tagging\fR, you can define all of Apprise Service URLs in your configuration that you want and only specifically notify a subset of them:
.IP "" 4
.nf
-$ apprise \-vv \-t "Will Be Late" \-b "Go ahead and make dinner without me" \e
- \-\-tag=family
+$ apprise \-vv \-\-title "Will Be Late Getting Home" \e
+ \-\-body "Please go ahead and make dinner without me\." \e
+ \-\-tag=family
.fi
.IP "" 0
.SH "BUGS"
If you find any bugs, please make them known at: \fIhttps://github\.com/caronc/apprise/issues\fR
+.SH "DONATIONS"
+If you found Apprise useful at all, \fIplease consider donating\fR!
.SH "COPYRIGHT"
Apprise is Copyright (C) 2024 Chris Caron \fIlead2gold@gmail\.com\fR
diff --git a/packaging/man/apprise.1.html b/packaging/man/apprise.1.html
index 084bb790..6e7dd455 100644
--- a/packaging/man/apprise.1.html
+++ b/packaging/man/apprise.1.html
@@ -57,12 +57,14 @@
SYNOPSIS
DESCRIPTION
OPTIONS
+ PERSISTENT STORAGE
EXIT STATUS
SERVICE URLS
EXAMPLES
CUSTOM PLUGIN/NOTIFICATIONS
CONFIGURATION
BUGS
+ DONATIONS
COPYRIGHT
@@ -80,7 +82,8 @@
SYNOPSIS
-apprise
[options...] service-url...
+apprise
[options...] service-url...
+apprise
storage [options...] [action] url-id...
DESCRIPTION
@@ -93,17 +96,19 @@ Telegram, Pushbullet, Slack, Twitter, etc.
A common and intuitive notification syntax.
Supports the handling of images (to the notification services that will
accept them).
+ It's incredibly lightweight.
+ Amazing response times because all messages sent asynchronously.
OPTIONS
The Apprise options are as follows:
--b
, --body=
TEXT:
+
-b
, --body=
VALUE:
Specify the message body. If no body is specified then content is read from
stdin.
--t
, --title=
TEXT:
+
-t
, --title=
VALUE:
Specify the message title. This field is complete optional.
-c
, --config=
CONFIG-URL:
@@ -112,7 +117,7 @@ accept them).
-a
, --attach=
ATTACH-URL:
Specify one or more file attachment locations.
--P
, --plugin-path=
PLUGIN-PATH:
+
-P
, --plugin-path=
PATH:
Specify a path to scan for custom notification plugin support.
You can create your own notification by simply creating a Python file
that contains the @notify("schema")
decorator.
@@ -120,18 +125,18 @@ accept them).
You can optioanly chose to specify more then one --plugin-path (-P)
to increase the modules included.
--n
, --notification-type=
TYPE:
+
-n
, --notification-type=
VALUE:
Specify the message type (default=info). Possible values are "info",
"success", "failure", and "warning".
--i
, --input-format=
FORMAT:
+
-i
, --input-format=
VALUE:
Specify the input message format (default=text). Possible values are "text",
"html", and "markdown".
--T
, --theme=
THEME:
+
-T
, --theme=
VALUE:
Specify the default theme.
--g
, --tag=
TAG:
+
-g
, --tag=
VALUE:
Specify one or more tags to filter which services to notify. Use multiple
--tag (-g) entries to OR
the tags together and comma separated
to AND
them. If no tags are specified then all services are notified.
@@ -140,7 +145,7 @@ accept them).
Send notifications synchronously (one after the other) instead of
all at once.
--R
, --recursion-depth
:
+
-R
, --recursion-depth
INTEGER:
he number of recursive import entries that can be loaded from within
Apprise configuration. By default this is set to 1. If this is set to
zero, then import statements found in any configuration is ignored.
@@ -154,6 +159,22 @@ accept them).
sequences such as :smile: or :grin: to their respected unicode emoji
character.
+-S
, --storage-path=
PATH:
+ Specify the path to the persistent storage caching location
+
+-SM
, --storage-mode=
MODE:
+ Specify the persistent storage operational mode. Possible values are "auto",
+ "flush", and "memory". The default is "auto" not not specified.
+
+-SPD
, --storage-prune-days=
INTEGER:
+ Define the number of days the storage prune should run using.
+ Setting this to zero (0) will eliminate all accumulated content. By
+ default this value is 30 (days).
+
+-SUL
, --storage-uid-length=
INTEGER:
+ Define the number of unique characters to store persistent cache in.
+ By default this value is 8 (characters).
+
-d
, --dry-run
:
Perform a trial run but only prints the notification services to-be
triggered to stdout. Notifications are never sent using this mode.
@@ -175,6 +196,34 @@ accept them).
-h
, --help
:
Show this message and exit.
+PERSISTENT STORAGE
+
+Persistent storage by default writes to the following location unless the environment variable APPRISE_STORAGE_PATH
over-rides it and/or --storage-path
(-SP
) is specified to over-ride it:
+
+~/.local/share/apprise/cache
+
+
+To utilize the persistent storage element associated with Apprise, simply
+specify the keyword storage
+
+$ apprise storage
+
+
+The storage action has the following sub actions:
+
+list
:
+ List all of the detected persistent storage elements and their state
+ (stale, active, or unused). This is the default action if
+ nothing further is identified.
+
+prune
:
+ Removes all persistent storage that has not been referenced for more then 30
+ days. You can optionally set the --storage-prune-days
to alter this
+ default value.
+
+clean
:
+ Removes all persistent storage reguardless of age.
+
EXIT STATUS
apprise exits with a status of:
@@ -235,6 +284,21 @@ notification service URLs and only notify the ones tagged as devops.
+List all of the notifications loaded:
+
+$ apprise --dry-run --tag=all
+
+
+List all of the details around the current persistent storage setup:
+
+$ apprise storage list
+
+
+Prune all persistent storage that has not been referenced for at least 10 days or more
+
+$ apprise storage prune --storage-prune-days=10
+
+
CUSTOM PLUGIN/NOTIFICATIONS
Apprise can additionally allow you to define your own custom schema://
entries that you can trigger on and call services you've defined.
@@ -256,17 +320,17 @@ it:
@notify(on="foobar", name="My Custom Notification")
def my_wrapper(body, title, notify_type, *args, **kwargs):
- <define your custom code here>
-
- # Returning True/False is a way to relay your status back to Apprise.
- # Returning nothing (None by default) is always interpreted as a Success
+ print("Define your custom code here")
+
+ # Returning True/False will relay your status back through Apprise
+ # Returning nothing (None by default) is always interpreted as True
return True
CONFIGURATION
A configuration file can be in the format of either TEXT or YAML where
-[TEXT][textconfig] is the easiest and most ideal solution for most users. However
+TEXT is the easiest and most ideal solution for most users. However
YAML configuration files grants the user a bit more leverage and access
to some of the internal features of Apprise. Reguardless of which format you choose,
both provide the users the ability to leverage tagging which adds a more rich and
@@ -307,8 +371,9 @@ tool simplifies to:
If you leveraged tagging, you can define all of Apprise Service URLs in your
configuration that you want and only specifically notify a subset of them:
-$ apprise -vv -t "Will Be Late" -b "Go ahead and make dinner without me" \
- --tag=family
+$ apprise -vv --title "Will Be Late Getting Home" \
+ --body "Please go ahead and make dinner without me." \
+ --tag=family
BUGS
@@ -316,13 +381,16 @@ configuration that you want and only specifically notify a subset of them:
If you find any bugs, please make them known at:
https://github.com/caronc/apprise/issues
+DONATIONS
+If you found Apprise useful at all, please consider donating!
+
COPYRIGHT
Apprise is Copyright (C) 2024 Chris Caron lead2gold@gmail.com
diff --git a/packaging/man/apprise.md b/packaging/man/apprise.md
index 4b12abc1..24140725 100644
--- a/packaging/man/apprise.md
+++ b/packaging/man/apprise.md
@@ -4,6 +4,7 @@ apprise(1) -- Push Notifications that work with just about every platform!
## SYNOPSIS
`apprise` [...] ...
+`apprise` storage [...] [] ...
## DESCRIPTION
@@ -15,16 +16,18 @@ Telegram, Pushbullet, Slack, Twitter, etc.
* A common and intuitive notification syntax.
* Supports the handling of images (to the notification services that will
accept them).
+ * It's incredibly lightweight.
+ * Amazing response times because all messages sent asynchronously.
## OPTIONS
The Apprise options are as follows:
- `-b`, `--body=`:
+ `-b`, `--body=`:
Specify the message body. If no body is specified then content is read from
.
- `-t`, `--title=`:
+ `-t`, `--title=`:
Specify the message title. This field is complete optional.
`-c`, `--config=`:
@@ -33,7 +36,7 @@ The Apprise options are as follows:
`-a`, `--attach=`:
Specify one or more file attachment locations.
- `-P`, `--plugin-path=`:
+ `-P`, `--plugin-path=`:
Specify a path to scan for custom notification plugin support.
You can create your own notification by simply creating a Python file
that contains the `@notify("schema")` decorator.
@@ -41,18 +44,18 @@ The Apprise options are as follows:
You can optioanly chose to specify more then one **--plugin-path** (**-P**)
to increase the modules included.
- `-n`, `--notification-type=`:
+ `-n`, `--notification-type=`:
Specify the message type (default=info). Possible values are "info",
"success", "failure", and "warning".
- `-i`, `--input-format=`:
+ `-i`, `--input-format=`:
Specify the input message format (default=text). Possible values are "text",
"html", and "markdown".
- `-T`, `--theme=`THEME:
+ `-T`, `--theme=`:
Specify the default theme.
- `-g`, `--tag=`TAG:
+ `-g`, `--tag=`:
Specify one or more tags to filter which services to notify. Use multiple
**--tag** (**-g**) entries to `OR` the tags together and comma separated
to `AND` them. If no tags are specified then all services are notified.
@@ -61,7 +64,7 @@ The Apprise options are as follows:
Send notifications synchronously (one after the other) instead of
all at once.
- `-R`, `--recursion-depth`:
+ `-R`, `--recursion-depth`:
he number of recursive import entries that can be loaded from within
Apprise configuration. By default this is set to 1. If this is set to
zero, then import statements found in any configuration is ignored.
@@ -75,6 +78,22 @@ The Apprise options are as follows:
sequences such as :smile: or :grin: to their respected unicode emoji
character.
+ `-S`, `--storage-path=`:
+ Specify the path to the persistent storage caching location
+
+ `-SM`, `--storage-mode=`:
+ Specify the persistent storage operational mode. Possible values are "auto",
+ "flush", and "memory". The default is "auto" not not specified.
+
+ `-SPD`, `--storage-prune-days=`:
+ Define the number of days the storage prune should run using.
+ Setting this to zero (0) will eliminate all accumulated content. By
+ default this value is 30 (days).
+
+ `-SUL`, `--storage-uid-length=`:
+ Define the number of unique characters to store persistent cache in.
+ By default this value is 8 (characters).
+
`-d`, `--dry-run`:
Perform a trial run but only prints the notification services to-be
triggered to **stdout**. Notifications are never sent using this mode.
@@ -96,6 +115,32 @@ The Apprise options are as follows:
`-h`, `--help`:
Show this message and exit.
+## PERSISTENT STORAGE
+
+Persistent storage by default writes to the following location unless the environment variable `APPRISE_STORAGE_PATH` over-rides it and/or `--storage-path` (`-SP`) is specified to over-ride it:
+
+ ~/.local/share/apprise/cache
+
+To utilize the [persistent storage][pstorage] element associated with Apprise, simply
+specify the keyword **storage**
+
+ $ apprise storage
+
+The **storage** action has the following sub actions:
+
+ `list`:
+ List all of the detected persistent storage elements and their state
+ (**stale**, **active**, or **unused**). This is the default action if
+ nothing further is identified.
+
+ `prune`:
+ Removes all persistent storage that has not been referenced for more then 30
+ days. You can optionally set the `--storage-prune-days` to alter this
+ default value.
+
+ `clean`:
+ Removes all persistent storage reguardless of age.
+
## EXIT STATUS
**apprise** exits with a status of:
@@ -147,6 +192,18 @@ Include an attachment:
$ apprise -vv -t "School Assignment" -b "See attached" \
--attach=Documents/FinalReport.docx
+List all of the notifications loaded:
+
+ $ apprise --dry-run --tag=all
+
+List all of the details around the current persistent storage setup:
+
+ $ apprise storage list
+
+Prune all persistent storage that has not been referenced for at least 10 days or more
+
+ $ apprise storage prune --storage-prune-days=10
+
## CUSTOM PLUGIN/NOTIFICATIONS
Apprise can additionally allow you to define your own custom **schema://**
entries that you can trigger on and call services you've defined.
@@ -166,11 +223,11 @@ it:
# references:
@notify(on="foobar", name="My Custom Notification")
def my_wrapper(body, title, notify_type, *args, **kwargs):
-
-
-
- # Returning True/False is a way to relay your status back to Apprise.
- # Returning nothing (None by default) is always interpreted as a Success
+
+ print("Define your custom code here")
+
+ # Returning True/False will relay your status back through Apprise
+ # Returning nothing (None by default) is always interpreted as True
return True
## CONFIGURATION
@@ -215,18 +272,25 @@ tool simplifies to:
If you leveraged [tagging][tagging], you can define all of Apprise Service URLs in your
configuration that you want and only specifically notify a subset of them:
- $ apprise -vv -t "Will Be Late" -b "Go ahead and make dinner without me" \
- --tag=family
+ $ apprise -vv --title "Will Be Late Getting Home" \
+ --body "Please go ahead and make dinner without me." \
+ --tag=family
[yamlconfig]: https://github.com/caronc/apprise/wiki/config_yaml
+[textconfig]: https://github.com/caronc/apprise/wiki/config_text
[tagging]: https://github.com/caronc/apprise/wiki/CLI_Usage#label-leverage-tagging
-
+[pstorage]: https://github.com/caronc/apprise/wiki/persistent_storage
## BUGS
If you find any bugs, please make them known at:
+## DONATIONS
+If you found Apprise useful at all, [please consider donating][donations]!
+
+[donations]: https://github.com/caronc/apprise/wiki/persistent_storage
+
## COPYRIGHT
Apprise is Copyright (C) 2024 Chris Caron
diff --git a/test/test_apprise_cli.py b/test/test_apprise_cli.py
index 52051d5a..d671b8b7 100644
--- a/test/test_apprise_cli.py
+++ b/test/test_apprise_cli.py
@@ -29,7 +29,8 @@
import os
import re
from unittest import mock
-
+import pytest
+import sys
import requests
import json
from inspect import cleandoc
@@ -677,6 +678,8 @@ def test_apprise_cli_modules(tmpdir):
assert result.exit_code == 0
+@pytest.mark.skipif(
+ sys.platform == "win32", reason="Unreliable results to be determined")
def test_apprise_cli_persistent_storage(tmpdir):
"""
CLI: test persistent storage
@@ -768,7 +771,7 @@ def test_apprise_cli_persistent_storage(tmpdir):
# our persist storage has not been created yet
_stdout = result.stdout.strip()
assert re.match(
- r'^1\.\s+[a-z0-9_-]{8}\s+0\.00B\s+unused\s+-\s+test://$', _stdout,
+ r'^1\.\s+[a-z0-9]{8}\s+0\.00B\s+unused\s+-\s+test://\s*', _stdout,
re.MULTILINE)
# An invalid mode specified
@@ -1083,6 +1086,10 @@ def test_apprise_cli_persistent_storage(tmpdir):
# list our entries
assert result.exit_code == 0
+ # Note: An prune/expiry of zero gets everything except for MS Windows
+ # during testing only.
+ # Until this can be resolved, this is the section of the test that
+ # caused us to disable it in MS Windows
_stdout = result.stdout.strip()
assert re.match(
r'^1\.\s+[a-z0-9_-]{8}\s+0\.00B\s+unused\s+-\s+test://$', _stdout,
@@ -1090,7 +1097,7 @@ def test_apprise_cli_persistent_storage(tmpdir):
# New Temporary namespace
new_persistent_base = tmpdir.mkdir('namespace')
- with environ(APPRISE_STORAGE=str(new_persistent_base)):
+ with environ(APPRISE_STORAGE_PATH=str(new_persistent_base)):
# Reload our module
reload(cli)
@@ -1350,6 +1357,28 @@ def test_apprise_cli_details(tmpdir):
N_MGR.unload_modules()
+def test_apprise_cli_print_help():
+ """
+ CLI: --help (-h)
+
+ """
+ runner = CliRunner()
+
+ # Clear our working variables so they don't obstruct the next test
+ # This simulates an actual call from the CLI. Unfortunately through
+ # testing were occupying the same memory space so our singleton's
+ # have already been populated
+ N_MGR._paths_previously_scanned.clear()
+ N_MGR._custom_module_map.clear()
+
+ # Print help and exit
+ result = runner.invoke(cli.main, ['--help'])
+ assert result.exit_code == 0
+
+ result = runner.invoke(cli.main, ['-h'])
+ assert result.exit_code == 0
+
+
@mock.patch('requests.post')
def test_apprise_cli_plugin_loading(mock_post, tmpdir):
"""
diff --git a/test/test_persistent_store.py b/test/test_persistent_store.py
index 20cabb52..bd0b1dd4 100644
--- a/test/test_persistent_store.py
+++ b/test/test_persistent_store.py
@@ -28,6 +28,7 @@
import time
import os
+import sys
import zlib
import pytest
import shutil
@@ -1256,6 +1257,8 @@ def test_persistent_storage_cache_object(tmpdir):
'c': 'datetime'}, verify=False) is None
+@pytest.mark.skipif(
+ sys.platform == "win32", reason="Unreliable results to be determined")
def test_persistent_storage_disk_prune(tmpdir):
"""
General testing of a Persistent Store prune calls
@@ -1291,6 +1294,8 @@ def test_persistent_storage_disk_prune(tmpdir):
assert pc.read() == b'data-t01'
# An expiry of zero gets everything
+ # Note: This test randomly fails in Microsoft Windows for unknown reasons
+ # When this is determined, this test can be opened back up
results = PersistentStore.disk_prune(path=str(tmpdir), expires=0)
# We match everything now
assert isinstance(results, dict)