mirror of
https://github.com/caronc/apprise.git
synced 2024-11-28 19:14:11 +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 itertools import chain
|
||||||
from .common import NotifyType
|
from .common import NotifyType
|
||||||
from .common import MATCH_ALL_TAG
|
from .common import MATCH_ALL_TAG
|
||||||
|
from .common import MATCH_ALWAYS_TAG
|
||||||
from .conversion import convert_between
|
from .conversion import convert_between
|
||||||
from .utils import is_exclusive_match
|
from .utils import is_exclusive_match
|
||||||
from .utils import parse_list
|
from .utils import parse_list
|
||||||
@ -303,7 +304,7 @@ class Apprise(object):
|
|||||||
"""
|
"""
|
||||||
self.servers[:] = []
|
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.
|
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=[('tagA', 'tagC'), 'tagB'] = (tagA and tagC) or tagB
|
||||||
# tag=[('tagB', 'tagC')] = tagB and tagC
|
# 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
|
# Iterate over our loaded plugins
|
||||||
for entry in self.servers:
|
for entry in self.servers:
|
||||||
|
|
||||||
@ -332,13 +337,14 @@ class Apprise(object):
|
|||||||
for server in servers:
|
for server in servers:
|
||||||
# Apply our tag matching based on our defined logic
|
# Apply our tag matching based on our defined logic
|
||||||
if is_exclusive_match(
|
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
|
yield server
|
||||||
return
|
return
|
||||||
|
|
||||||
def notify(self, body, title='', notify_type=NotifyType.INFO,
|
def notify(self, body, title='', notify_type=NotifyType.INFO,
|
||||||
body_format=None, tag=MATCH_ALL_TAG, attach=None,
|
body_format=None, tag=MATCH_ALL_TAG, match_always=True,
|
||||||
interpret_escapes=None):
|
attach=None, interpret_escapes=None):
|
||||||
"""
|
"""
|
||||||
Send a notification to all of the plugins previously loaded.
|
Send a notification to all of the plugins previously loaded.
|
||||||
|
|
||||||
@ -368,7 +374,7 @@ class Apprise(object):
|
|||||||
self.async_notify(
|
self.async_notify(
|
||||||
body, title,
|
body, title,
|
||||||
notify_type=notify_type, body_format=body_format,
|
notify_type=notify_type, body_format=body_format,
|
||||||
tag=tag, attach=attach,
|
tag=tag, match_always=match_always, attach=attach,
|
||||||
interpret_escapes=interpret_escapes,
|
interpret_escapes=interpret_escapes,
|
||||||
),
|
),
|
||||||
debug=self.debug
|
debug=self.debug
|
||||||
@ -466,8 +472,8 @@ class Apprise(object):
|
|||||||
return py3compat.asyncio.toasyncwrap(status)
|
return py3compat.asyncio.toasyncwrap(status)
|
||||||
|
|
||||||
def _notifyall(self, handler, body, title='', notify_type=NotifyType.INFO,
|
def _notifyall(self, handler, body, title='', notify_type=NotifyType.INFO,
|
||||||
body_format=None, tag=MATCH_ALL_TAG, attach=None,
|
body_format=None, tag=MATCH_ALL_TAG, match_always=True,
|
||||||
interpret_escapes=None):
|
attach=None, interpret_escapes=None):
|
||||||
"""
|
"""
|
||||||
Creates notifications for all of the plugins loaded.
|
Creates notifications for all of the plugins loaded.
|
||||||
|
|
||||||
@ -509,7 +515,7 @@ class Apprise(object):
|
|||||||
if interpret_escapes is None else interpret_escapes
|
if interpret_escapes is None else interpret_escapes
|
||||||
|
|
||||||
# Iterate over our loaded plugins
|
# 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
|
# 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
|
# 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
|
# determined we need to notify the service it's associated with
|
||||||
|
@ -32,6 +32,7 @@ from . import URLBase
|
|||||||
from .AppriseAsset import AppriseAsset
|
from .AppriseAsset import AppriseAsset
|
||||||
|
|
||||||
from .common import MATCH_ALL_TAG
|
from .common import MATCH_ALL_TAG
|
||||||
|
from .common import MATCH_ALWAYS_TAG
|
||||||
from .utils import GET_SCHEMA_RE
|
from .utils import GET_SCHEMA_RE
|
||||||
from .utils import parse_list
|
from .utils import parse_list
|
||||||
from .utils import is_exclusive_match
|
from .utils import is_exclusive_match
|
||||||
@ -266,7 +267,7 @@ class AppriseConfig(object):
|
|||||||
# Return our status
|
# Return our status
|
||||||
return True
|
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
|
Returns all of our servers dynamically build based on parsed
|
||||||
configuration.
|
configuration.
|
||||||
@ -277,7 +278,15 @@ class AppriseConfig(object):
|
|||||||
This is for filtering the configuration files polled for
|
This is for filtering the configuration files polled for
|
||||||
results.
|
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
|
# Build our tag setup
|
||||||
# - top level entries are treated as an 'or'
|
# - top level entries are treated as an 'or'
|
||||||
# - second level (or more) entries are treated as 'and'
|
# - 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
|
# Apply our tag matching based on our defined logic
|
||||||
if is_exclusive_match(
|
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
|
# Build ourselves a list of services dynamically and return the
|
||||||
# as a list
|
# as a list
|
||||||
response.extend(entry.servers())
|
response.extend(entry.servers())
|
||||||
|
@ -187,3 +187,7 @@ CONTENT_LOCATIONS = (
|
|||||||
# This is a reserved tag that is automatically assigned to every
|
# This is a reserved tag that is automatically assigned to every
|
||||||
# Notification Plugin
|
# Notification Plugin
|
||||||
MATCH_ALL_TAG = 'all'
|
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 json
|
||||||
import contextlib
|
import contextlib
|
||||||
import os
|
import os
|
||||||
|
from itertools import chain
|
||||||
from os.path import expanduser
|
from os.path import expanduser
|
||||||
from functools import reduce
|
from functools import reduce
|
||||||
|
from .common import MATCH_ALL_TAG
|
||||||
|
from .common import MATCH_ALWAYS_TAG
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Python 2.7
|
# Python 2.7
|
||||||
@ -995,7 +998,8 @@ def parse_list(*args):
|
|||||||
return sorted([x for x in filter(bool, list(set(result)))])
|
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
|
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', 'tagB'] = tagA or tagB
|
||||||
logic=[('tagA', 'tagC'), 'tagB'] = (tagA and tagC) or tagB
|
logic=[('tagA', 'tagC'), 'tagB'] = (tagA and tagC) or tagB
|
||||||
logic=[('tagB', 'tagC')] = tagB and tagC
|
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):
|
if isinstance(logic, six.string_types):
|
||||||
@ -1026,6 +1033,10 @@ def is_exclusive_match(logic, data, match_all='all'):
|
|||||||
# garbage input
|
# garbage input
|
||||||
return False
|
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
|
# Track what we match against; but by default we do not match
|
||||||
# against anything
|
# against anything
|
||||||
matched = False
|
matched = False
|
||||||
|
@ -416,6 +416,33 @@ def test_apprise_config_tagging(tmpdir):
|
|||||||
# all matches everything
|
# all matches everything
|
||||||
assert len(ac.servers(tag='all')) == 3
|
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():
|
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
|
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):
|
def test_apprise_config_template_parse(tmpdir):
|
||||||
"""
|
"""
|
||||||
API: AppriseConfig parsing of templates
|
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=['www'], data=data) is False
|
||||||
assert utils.is_exclusive_match(logic='all', data=data) is True
|
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
|
# Change default value from 'all' to 'match_me'. Logic matches
|
||||||
# so we pass
|
# so we pass
|
||||||
assert utils.is_exclusive_match(
|
assert utils.is_exclusive_match(
|
||||||
|
Loading…
Reference in New Issue
Block a user