mirror of
https://github.com/caronc/apprise.git
synced 2024-11-25 01:24:03 +01:00
Added always
special tag (will always notify) (#561)
This commit is contained in:
parent
b28cd4cdff
commit
5d14259227
@ -28,6 +28,7 @@ import six
|
||||
from itertools import chain
|
||||
from .common import NotifyType
|
||||
from .common import MATCH_ALL_TAG
|
||||
from .common import MATCH_ALWAYS_TAG
|
||||
from .conversion import convert_between
|
||||
from .utils import is_exclusive_match
|
||||
from .utils import parse_list
|
||||
@ -303,7 +304,7 @@ class Apprise(object):
|
||||
"""
|
||||
self.servers[:] = []
|
||||
|
||||
def find(self, tag=MATCH_ALL_TAG):
|
||||
def find(self, tag=MATCH_ALL_TAG, match_always=True):
|
||||
"""
|
||||
Returns an list of all servers matching against the tag specified.
|
||||
|
||||
@ -319,6 +320,10 @@ class Apprise(object):
|
||||
# tag=[('tagA', 'tagC'), 'tagB'] = (tagA and tagC) or tagB
|
||||
# tag=[('tagB', 'tagC')] = tagB and tagC
|
||||
|
||||
# A match_always flag allows us to pick up on our 'any' keyword
|
||||
# and notify these services under all circumstances
|
||||
match_always = MATCH_ALWAYS_TAG if match_always else None
|
||||
|
||||
# Iterate over our loaded plugins
|
||||
for entry in self.servers:
|
||||
|
||||
@ -332,13 +337,14 @@ class Apprise(object):
|
||||
for server in servers:
|
||||
# Apply our tag matching based on our defined logic
|
||||
if is_exclusive_match(
|
||||
logic=tag, data=server.tags, match_all=MATCH_ALL_TAG):
|
||||
logic=tag, data=server.tags, match_all=MATCH_ALL_TAG,
|
||||
match_always=match_always):
|
||||
yield server
|
||||
return
|
||||
|
||||
def notify(self, body, title='', notify_type=NotifyType.INFO,
|
||||
body_format=None, tag=MATCH_ALL_TAG, attach=None,
|
||||
interpret_escapes=None):
|
||||
body_format=None, tag=MATCH_ALL_TAG, match_always=True,
|
||||
attach=None, interpret_escapes=None):
|
||||
"""
|
||||
Send a notification to all of the plugins previously loaded.
|
||||
|
||||
@ -368,7 +374,7 @@ class Apprise(object):
|
||||
self.async_notify(
|
||||
body, title,
|
||||
notify_type=notify_type, body_format=body_format,
|
||||
tag=tag, attach=attach,
|
||||
tag=tag, match_always=match_always, attach=attach,
|
||||
interpret_escapes=interpret_escapes,
|
||||
),
|
||||
debug=self.debug
|
||||
@ -466,8 +472,8 @@ class Apprise(object):
|
||||
return py3compat.asyncio.toasyncwrap(status)
|
||||
|
||||
def _notifyall(self, handler, body, title='', notify_type=NotifyType.INFO,
|
||||
body_format=None, tag=MATCH_ALL_TAG, attach=None,
|
||||
interpret_escapes=None):
|
||||
body_format=None, tag=MATCH_ALL_TAG, match_always=True,
|
||||
attach=None, interpret_escapes=None):
|
||||
"""
|
||||
Creates notifications for all of the plugins loaded.
|
||||
|
||||
@ -509,7 +515,7 @@ class Apprise(object):
|
||||
if interpret_escapes is None else interpret_escapes
|
||||
|
||||
# Iterate over our loaded plugins
|
||||
for server in self.find(tag):
|
||||
for server in self.find(tag, match_always=match_always):
|
||||
# If our code reaches here, we either did not define a tag (it
|
||||
# was set to None), or we did define a tag and the logic above
|
||||
# determined we need to notify the service it's associated with
|
||||
|
@ -32,6 +32,7 @@ from . import URLBase
|
||||
from .AppriseAsset import AppriseAsset
|
||||
|
||||
from .common import MATCH_ALL_TAG
|
||||
from .common import MATCH_ALWAYS_TAG
|
||||
from .utils import GET_SCHEMA_RE
|
||||
from .utils import parse_list
|
||||
from .utils import is_exclusive_match
|
||||
@ -266,7 +267,7 @@ class AppriseConfig(object):
|
||||
# Return our status
|
||||
return True
|
||||
|
||||
def servers(self, tag=MATCH_ALL_TAG, *args, **kwargs):
|
||||
def servers(self, tag=MATCH_ALL_TAG, match_always=True, *args, **kwargs):
|
||||
"""
|
||||
Returns all of our servers dynamically build based on parsed
|
||||
configuration.
|
||||
@ -277,7 +278,15 @@ class AppriseConfig(object):
|
||||
This is for filtering the configuration files polled for
|
||||
results.
|
||||
|
||||
If the anytag is set, then any notification that is found
|
||||
set with that tag are included in the response.
|
||||
|
||||
"""
|
||||
|
||||
# A match_always flag allows us to pick up on our 'any' keyword
|
||||
# and notify these services under all circumstances
|
||||
match_always = MATCH_ALWAYS_TAG if match_always else None
|
||||
|
||||
# Build our tag setup
|
||||
# - top level entries are treated as an 'or'
|
||||
# - second level (or more) entries are treated as 'and'
|
||||
@ -294,7 +303,8 @@ class AppriseConfig(object):
|
||||
|
||||
# Apply our tag matching based on our defined logic
|
||||
if is_exclusive_match(
|
||||
logic=tag, data=entry.tags, match_all=MATCH_ALL_TAG):
|
||||
logic=tag, data=entry.tags, match_all=MATCH_ALL_TAG,
|
||||
match_always=match_always):
|
||||
# Build ourselves a list of services dynamically and return the
|
||||
# as a list
|
||||
response.extend(entry.servers())
|
||||
|
@ -187,3 +187,7 @@ CONTENT_LOCATIONS = (
|
||||
# This is a reserved tag that is automatically assigned to every
|
||||
# Notification Plugin
|
||||
MATCH_ALL_TAG = 'all'
|
||||
|
||||
# Will cause notification to trigger under any circumstance even if an
|
||||
# exclusive tagging was provided.
|
||||
MATCH_ALWAYS_TAG = 'always'
|
||||
|
@ -28,8 +28,11 @@ import six
|
||||
import json
|
||||
import contextlib
|
||||
import os
|
||||
from itertools import chain
|
||||
from os.path import expanduser
|
||||
from functools import reduce
|
||||
from .common import MATCH_ALL_TAG
|
||||
from .common import MATCH_ALWAYS_TAG
|
||||
|
||||
try:
|
||||
# Python 2.7
|
||||
@ -995,7 +998,8 @@ def parse_list(*args):
|
||||
return sorted([x for x in filter(bool, list(set(result)))])
|
||||
|
||||
|
||||
def is_exclusive_match(logic, data, match_all='all'):
|
||||
def is_exclusive_match(logic, data, match_all=MATCH_ALL_TAG,
|
||||
match_always=MATCH_ALWAYS_TAG):
|
||||
"""
|
||||
|
||||
The data variable should always be a set of strings that the logic can be
|
||||
@ -1011,6 +1015,9 @@ def is_exclusive_match(logic, data, match_all='all'):
|
||||
logic=['tagA', 'tagB'] = tagA or tagB
|
||||
logic=[('tagA', 'tagC'), 'tagB'] = (tagA and tagC) or tagB
|
||||
logic=[('tagB', 'tagC')] = tagB and tagC
|
||||
|
||||
If `match_always` is not set to None, then its value is added as an 'or'
|
||||
to all specified logic searches.
|
||||
"""
|
||||
|
||||
if isinstance(logic, six.string_types):
|
||||
@ -1026,6 +1033,10 @@ def is_exclusive_match(logic, data, match_all='all'):
|
||||
# garbage input
|
||||
return False
|
||||
|
||||
if match_always:
|
||||
# Add our match_always to our logic searching if secified
|
||||
logic = chain(logic, [match_always])
|
||||
|
||||
# Track what we match against; but by default we do not match
|
||||
# against anything
|
||||
matched = False
|
||||
|
@ -416,6 +416,33 @@ def test_apprise_config_tagging(tmpdir):
|
||||
# all matches everything
|
||||
assert len(ac.servers(tag='all')) == 3
|
||||
|
||||
# Test cases using the `always` keyword
|
||||
# Create ourselves a config object
|
||||
ac = AppriseConfig()
|
||||
|
||||
# Add an item associated with tag a
|
||||
assert ac.add(configs=str(t), asset=AppriseAsset(), tag='a,always') is True
|
||||
# Add an item associated with tag b
|
||||
assert ac.add(configs=str(t), asset=AppriseAsset(), tag='b') is True
|
||||
# Add an item associated with tag a or b
|
||||
assert ac.add(configs=str(t), asset=AppriseAsset(), tag='c,d') is True
|
||||
|
||||
# Now filter: a:
|
||||
assert len(ac.servers(tag='a')) == 1
|
||||
# Now filter: a or b:
|
||||
assert len(ac.servers(tag='a,b')) == 2
|
||||
# Now filter: e
|
||||
# we'll match the `always'
|
||||
assert len(ac.servers(tag='e')) == 1
|
||||
assert len(ac.servers(tag='e', match_always=False)) == 0
|
||||
# all matches everything
|
||||
assert len(ac.servers(tag='all')) == 3
|
||||
|
||||
# Now filter: d
|
||||
# we'll match the `always' tag
|
||||
assert len(ac.servers(tag='d')) == 2
|
||||
assert len(ac.servers(tag='d', match_always=False)) == 1
|
||||
|
||||
|
||||
def test_apprise_config_instantiate():
|
||||
"""
|
||||
@ -1173,6 +1200,59 @@ def test_config_base_parse_yaml_file03(tmpdir):
|
||||
assert sum(1 for _ in a.find('test1, test3')) == 1
|
||||
|
||||
|
||||
def test_config_base_parse_yaml_file04(tmpdir):
|
||||
"""
|
||||
API: ConfigBase.parse_yaml_file (#4)
|
||||
|
||||
Test the always keyword
|
||||
|
||||
"""
|
||||
t = tmpdir.mkdir("always-keyword").join("apprise.yml")
|
||||
t.write("""urls:
|
||||
- pover://nsisxnvnqixq39t0cw54pxieyvtdd9@2jevtmstfg5a7hfxndiybasttxxfku:
|
||||
- tag: test1,always
|
||||
- pover://rg8ta87qngcrkc6t4qbykxktou0uug@tqs3i88xlufexwl8t4asglt4zp5wfn:
|
||||
- tag: test2
|
||||
- pover://jcqgnlyq2oetea4qg3iunahj8d5ijm@evalvutkhc8ipmz2lcgc70wtsm0qpb:
|
||||
- tag: test3""")
|
||||
|
||||
# Create ourselves a config object
|
||||
ac = AppriseConfig(paths=str(t))
|
||||
|
||||
# The number of configuration files that exist
|
||||
assert len(ac) == 1
|
||||
|
||||
# no notifications are loaded
|
||||
assert len(ac.servers()) == 3
|
||||
|
||||
# Test our ability to add Config objects to our apprise object
|
||||
a = Apprise()
|
||||
|
||||
# Add our configuration object
|
||||
assert a.add(servers=ac) is True
|
||||
|
||||
# Detect our 3 entry as they should have loaded successfully
|
||||
assert len(a) == 3
|
||||
|
||||
# No match still matches `always` keyword
|
||||
assert sum(1 for _ in a.find('no-match')) == 1
|
||||
# Unless we explicitly do not look for that file
|
||||
assert sum(1 for _ in a.find('no-match', match_always=False)) == 0
|
||||
# Match everything
|
||||
assert sum(1 for _ in a.find('all')) == 3
|
||||
# Match test1 entry (also has `always` keyword
|
||||
assert sum(1 for _ in a.find('test1')) == 1
|
||||
assert sum(1 for _ in a.find('test1', match_always=False)) == 1
|
||||
# Match test2 entry (and test1 due to always keyword)
|
||||
assert sum(1 for _ in a.find('test2')) == 2
|
||||
assert sum(1 for _ in a.find('test2', match_always=False)) == 1
|
||||
# Match test3 entry (and test1 due to always keyword)
|
||||
assert sum(1 for _ in a.find('test3')) == 2
|
||||
assert sum(1 for _ in a.find('test3', match_always=False)) == 1
|
||||
# Match test1 or test3 entry
|
||||
assert sum(1 for _ in a.find('test1, test3')) == 2
|
||||
|
||||
|
||||
def test_apprise_config_template_parse(tmpdir):
|
||||
"""
|
||||
API: AppriseConfig parsing of templates
|
||||
|
@ -1588,6 +1588,17 @@ def test_exclusive_match():
|
||||
assert utils.is_exclusive_match(logic=['www'], data=data) is False
|
||||
assert utils.is_exclusive_match(logic='all', data=data) is True
|
||||
|
||||
#
|
||||
# Update our data set so we can do more advance checks
|
||||
#
|
||||
data = set(['always', 'entry1'])
|
||||
# We'll always match on the with keyword always
|
||||
assert utils.is_exclusive_match(logic='always', data=data) is True
|
||||
assert utils.is_exclusive_match(logic='garbage', data=data) is True
|
||||
# However we will not match if we turn this feature off
|
||||
assert utils.is_exclusive_match(
|
||||
logic='garbage', data=data, match_always=False) is False
|
||||
|
||||
# Change default value from 'all' to 'match_me'. Logic matches
|
||||
# so we pass
|
||||
assert utils.is_exclusive_match(
|
||||
|
Loading…
Reference in New Issue
Block a user