mirror of
https://github.com/caronc/apprise-api.git
synced 2025-01-21 05:19:04 +01:00
Added a Apprise URL Details Endpoint (/details) (#105)
This commit is contained in:
parent
ae99d35671
commit
30d87c8284
@ -128,6 +128,7 @@ You can pre-save all of your Apprise configuration and/or set of Apprise URLs an
|
||||
| `/get/{KEY}` | POST | Returns the Apprise Configuration from the persistent store. This can be directly used with the *Apprise CLI* and/or the *AppriseConfig()* object ([see here for details](https://github.com/caronc/apprise/wiki/config)).
|
||||
| `/notify/{KEY}` | POST | Sends notification(s) to all of the end points you've previously configured associated with a *{KEY}*.<br/>*Payload Parameters*<br/>📌 **body**: Your message body. This is the *only* required field.<br/>📌 **title**: Optionally define a title to go along with the *body*.<br/>📌 **type**: Defines the message type you want to send as. The valid options are `info`, `success`, `warning`, and `failure`. If no *type* is specified then `info` is the default value used.<br/>📌 **tag**: Optionally notify only those tagged accordingly.<br/>📌 **format**: Optionally identify the text format of the data you're feeding Apprise. The valid options are `text`, `markdown`, `html`. The default value if nothing is specified is `text`.
|
||||
| `/json/urls/{KEY}` | GET | Returns a JSON response object that contains all of the URLS and Tags associated with the key specified.
|
||||
| `/details` | GET | Set the `Accept` Header to `application/json` and retrieve a JSON response object that contains all of the supported Apprise URLs. See [here for more details](https://github.com/caronc/apprise/wiki/Development_Apprise_Details#apprise-details)
|
||||
|
||||
As an example, the `/json/urls/{KEY}` response might return something like this:
|
||||
|
||||
|
@ -48,6 +48,8 @@
|
||||
</ul>
|
||||
{% endif %}
|
||||
<ul class="collection z-depth-1">
|
||||
<a class="collection-item" href="{% url 'details' %}"><i class="tiny material-icons">settings</i>
|
||||
{% trans "Apprise Details" %}</a>
|
||||
<a class="collection-item" target="_blank"
|
||||
href="https://github.com/caronc/apprise/wiki#notification-services">📣
|
||||
{% trans "Notification Services" %}</a>
|
||||
|
128
apprise_api/api/templates/details.html
Normal file
128
apprise_api/api/templates/details.html
Normal file
@ -0,0 +1,128 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block body %}
|
||||
<h4>{% trans 'Apprise Details' %}</h4>
|
||||
<p>
|
||||
{% url 'details' as href %}
|
||||
{% blocktrans %}The following services are supported by this <a target="_blank" href="https://github.com/caronc/apprise">Apprise</a> instance.{% endblocktrans %}
|
||||
</p>
|
||||
<p>
|
||||
{% if show_all %}
|
||||
{% blocktrans %}To see a simplified listing that only identifies the Apprise services enabled click <a href="{{href}}">here</a>.{% endblocktrans %}
|
||||
{% else %}
|
||||
{% blocktrans %}To see a listing that identifies all of Apprise services available to this version (enabled or not) click <a href="{{href}}?all=yes">here</a>.{% endblocktrans %}
|
||||
{% endif %}
|
||||
<ul>
|
||||
<li>
|
||||
<i class="tiny material-icons">chevron_right</i><strong>{% blocktrans %}Apprise Version:{% endblocktrans %}</strong> {{ details.version }}
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="collapsible">
|
||||
{% for entry in details.schemas %}
|
||||
<li>
|
||||
<div class="collapsible-header">
|
||||
{{ forloop.counter|stringformat:"03d"}} <i class="tiny material-icons">chevron_right</i>
|
||||
{% if show_all %}{% if entry.enabled %}<i class="url-enabled material-icons">check_circle</i>{%else%}<i class="url-disabled material-icons">remove_circle</i>{%endif%}{% endif%}
|
||||
<div style="width:40rem;" lass="service_name">{{ entry.service_name }}</div>
|
||||
<ul style="width:90%; min-width: 60%;" class="right detail-buttons">
|
||||
<li class="right">
|
||||
<a href="{{ entry.setup_url }}" target="_blank" class="service_name"><i class="material-icons">help</i></a>
|
||||
<a href="{{ entry.service_url }}" target="_blank" class="service_name"><i class="material-icons">explore</i></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="collapsible-body">
|
||||
<h5>{{ entry.service_name }}</h5>
|
||||
{% if show_all and not entry.enabled %}
|
||||
<i class="url-disabled material-icons">report</i>{% blocktrans %}<strong>Note:</strong> This service is unavailable due to the service being disabled by the administrator or the required libraries needed to drive it is not installed or functioning correctly.{% endblocktrans %}
|
||||
{% endif %}
|
||||
<hr/>
|
||||
|
||||
<ul class="detail-buttons">
|
||||
<li><strong>{% blocktrans %}Category{% endblocktrans %}:</strong> {{entry.category}}</li>
|
||||
{% if entry.protocols %}
|
||||
<li><strong>{% blocktrans %}Insecure Schema(s){% endblocktrans %}:</strong> {{ entry.protocols|join:", " }}</li>
|
||||
{% endif %}
|
||||
{% if entry.secure_protocols %}
|
||||
<li><strong>{% blocktrans %}Secure Schema(s){% endblocktrans %}:</strong> {{ entry.secure_protocols|join:", " }}</li>
|
||||
{% endif %}
|
||||
<li><pre><code class="bash">
|
||||
# {% blocktrans %}Apprise URL Formatting{% endblocktrans %}</br>
|
||||
{% for url in entry.details.templates %}
|
||||
{{url}}<br/>
|
||||
{% endfor %}
|
||||
</code></pre>
|
||||
</li>
|
||||
<li>{% blocktrans %}For more details and additional Apprise configuration options available to this service:{% endblocktrans %}
|
||||
<a href="{{ entry.setup_url }}" target="_blank" class="service_name">Click Here</a>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</p>
|
||||
|
||||
<div class="section">
|
||||
<h4>{% trans 'API Endpoints' %}</h4>
|
||||
<p>
|
||||
{% blocktrans %}Developers who wish to receive this result set in a JSON parseable string for their application can perform the following to achive this:{% endblocktrans %}
|
||||
</p>
|
||||
<ul class="collapsible">
|
||||
<li>
|
||||
<div class="collapsible-header">
|
||||
<i class="material-icons">code</i>curl example
|
||||
</div>
|
||||
<div class="collapsible-body">
|
||||
<pre><code class="bash">
|
||||
#{% blocktrans %}Retrieve JSON Formatted Apprise Details{% endblocktrans %}<br />
|
||||
curl -H "Accept: application/json" \<br />
|
||||
"{{ request.scheme }}://{{ request.META.HTTP_HOST }}{{ BASE_URL }}/details/{% if show_all %}?all=yes{% endif %}"
|
||||
</code></pre>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="collapsible-header">
|
||||
<i class="material-icons">code</i>python example
|
||||
</div>
|
||||
<div class="collapsible-body">
|
||||
<pre><code class="python">
|
||||
import json<br />
|
||||
from urllib.request import Request<br />
|
||||
<br /># The URL<br />
|
||||
req = Request(<br />
|
||||
"{{ request.scheme }}://{{ request.META.HTTP_HOST }}{{ BASE_URL }}/details/{% if show_all %}?all=yes{% endif %}",<br />
|
||||
json.dumps(payload).encode('utf-8'),<br />
|
||||
{"Accept": "application/json"},<br />
|
||||
method='GET',<br />
|
||||
)
|
||||
</code></pre>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="collapsible-header">
|
||||
<i class="material-icons">code</i>php example
|
||||
</div>
|
||||
<div class="collapsible-body">
|
||||
<pre><code class="php">
|
||||
<?php<br />
|
||||
<br />
|
||||
// The URL<br />
|
||||
$url = '{{ request.scheme }}://{{ request.META.HTTP_HOST }}{{ BASE_URL }}/details/{% if show_all %}?all=yes{% endif %}';<br />
|
||||
<br />
|
||||
//Initiate cURL.<br />
|
||||
$ch = curl_init($url);<br />
|
||||
<br />
|
||||
//Set the content type to application/json<br />
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Accept: application/json'));<br />
|
||||
<br />
|
||||
//Execute the request<br />
|
||||
$result = curl_exec($ch);
|
||||
</code></pre>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
{% blocktrans %}More details on the JSON format can be found <a href="https://github.com/caronc/apprise/wiki/Development_Apprise_Details#details" target="_blank">here</a>.{% endblocktrans %}
|
||||
</div>
|
||||
{% endblock %}
|
78
apprise_api/api/tests/test_details.py
Normal file
78
apprise_api/api/tests/test_details.py
Normal file
@ -0,0 +1,78 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2023 Chris Caron <lead2gold@gmail.com>
|
||||
# All rights reserved.
|
||||
#
|
||||
# This code is licensed under the MIT License.
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files(the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions :
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
from django.test import SimpleTestCase
|
||||
|
||||
|
||||
class DetailTests(SimpleTestCase):
|
||||
|
||||
def test_post_not_supported(self):
|
||||
"""
|
||||
Test POST requests
|
||||
"""
|
||||
response = self.client.post('/details')
|
||||
# 405 as posting is not allowed
|
||||
assert response.status_code == 405
|
||||
|
||||
def test_details_simple(self):
|
||||
"""
|
||||
Test retrieving details
|
||||
"""
|
||||
|
||||
# Nothing to return
|
||||
response = self.client.get('/details')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
assert response['Content-Type'].startswith('text/html')
|
||||
|
||||
# JSON Response
|
||||
response = self.client.get(
|
||||
'/details', content_type='application/json',
|
||||
**{'HTTP_CONTENT_TYPE': 'application/json'})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
assert response['Content-Type'].startswith('application/json')
|
||||
|
||||
# JSON Response
|
||||
response = self.client.get(
|
||||
'/details', content_type='application/json',
|
||||
**{'HTTP_ACCEPT': 'application/json'})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
assert response['Content-Type'].startswith('application/json')
|
||||
|
||||
response = self.client.get('/details?all=yes')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
assert response['Content-Type'].startswith('text/html')
|
||||
|
||||
# JSON Response
|
||||
response = self.client.get(
|
||||
'/details?all=yes', content_type='application/json',
|
||||
**{'HTTP_CONTENT_TYPE': 'application/json'})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
assert response['Content-Type'].startswith('application/json')
|
||||
|
||||
# JSON Response
|
||||
response = self.client.get(
|
||||
'/details?all=yes', content_type='application/json',
|
||||
**{'HTTP_ACCEPT': 'application/json'})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
assert response['Content-Type'].startswith('application/json')
|
@ -29,6 +29,9 @@ urlpatterns = [
|
||||
re_path(
|
||||
r'^$',
|
||||
views.WelcomeView.as_view(), name='welcome'),
|
||||
re_path(
|
||||
r'^details/?',
|
||||
views.DetailsView.as_view(), name='details'),
|
||||
re_path(
|
||||
r'^cfg/(?P<key>[\w_-]{1,64})/?',
|
||||
views.ConfigView.as_view(), name='config'),
|
||||
|
@ -22,6 +22,7 @@
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
import re
|
||||
import os
|
||||
import tempfile
|
||||
import shutil
|
||||
@ -318,3 +319,62 @@ class AppriseConfigCache(object):
|
||||
ConfigCache = AppriseConfigCache(
|
||||
settings.APPRISE_CONFIG_DIR, salt=settings.SECRET_KEY,
|
||||
mode=settings.APPRISE_STATEFUL_MODE)
|
||||
|
||||
|
||||
def apply_global_filters():
|
||||
#
|
||||
# Apply Any Global Filters (if identified)
|
||||
#
|
||||
if settings.APPRISE_ALLOW_SERVICES:
|
||||
alphanum_re = re.compile(
|
||||
r'^(?P<name>[a-z][a-z0-9]+)', re.IGNORECASE)
|
||||
entries = \
|
||||
[alphanum_re.match(x).group('name').lower()
|
||||
for x in re.split(r'[ ,]+', settings.APPRISE_ALLOW_SERVICES)
|
||||
if alphanum_re.match(x)]
|
||||
|
||||
for plugin in set(apprise.common.NOTIFY_SCHEMA_MAP.values()):
|
||||
if entries:
|
||||
# Get a list of the current schema's associated with
|
||||
# a given plugin
|
||||
schemas = set(apprise.plugins.details(plugin)
|
||||
['tokens']['schema']['values'])
|
||||
|
||||
# Check what was defined and see if there is a hit
|
||||
for entry in entries:
|
||||
if entry in schemas:
|
||||
# We had a hit; we're done
|
||||
break
|
||||
|
||||
if entry in schemas:
|
||||
entries.remove(entry)
|
||||
# We can keep this plugin enabled and move along to the
|
||||
# next one...
|
||||
continue
|
||||
|
||||
# if we reach here, we have to block our plugin
|
||||
plugin.enabled = False
|
||||
|
||||
for entry in entries:
|
||||
# Generate some noise for those who have bad configurations
|
||||
logger.warning(
|
||||
'APPRISE_ALLOW_SERVICES plugin %s:// was not found - '
|
||||
'ignoring.', entry)
|
||||
|
||||
elif settings.APPRISE_DENY_SERVICES:
|
||||
alphanum_re = re.compile(
|
||||
r'^(?P<name>[a-z][a-z0-9]+)', re.IGNORECASE)
|
||||
entries = \
|
||||
[alphanum_re.match(x).group('name').lower()
|
||||
for x in re.split(r'[ ,]+', settings.APPRISE_DENY_SERVICES)
|
||||
if alphanum_re.match(x)]
|
||||
|
||||
for name in entries:
|
||||
try:
|
||||
# Force plugin to be disabled
|
||||
apprise.common.NOTIFY_SCHEMA_MAP[name].enabled = False
|
||||
|
||||
except KeyError:
|
||||
logger.warning(
|
||||
'APPRISE_DENY_SERVICES plugin %s:// was not found -'
|
||||
' ignoring.', name)
|
||||
|
@ -35,6 +35,7 @@ from django.utils.translation import gettext_lazy as _
|
||||
from django.core.serializers.json import DjangoJSONEncoder
|
||||
|
||||
from .utils import ConfigCache
|
||||
from .utils import apply_global_filters
|
||||
from .forms import AddByUrlForm
|
||||
from .forms import AddByConfigForm
|
||||
from .forms import NotifyForm
|
||||
@ -76,6 +77,10 @@ class JSONEncoder(DjangoJSONEncoder):
|
||||
def default(self, obj):
|
||||
if isinstance(obj, set):
|
||||
return list(obj)
|
||||
|
||||
elif isinstance(obj, apprise.AppriseLocale.LazyTranslation):
|
||||
return str(obj)
|
||||
|
||||
return super().default(obj)
|
||||
|
||||
|
||||
@ -104,6 +109,63 @@ class WelcomeView(View):
|
||||
return render(request, self.template_name, {})
|
||||
|
||||
|
||||
@method_decorator((gzip_page, never_cache), name='dispatch')
|
||||
class DetailsView(View):
|
||||
"""
|
||||
A Django view used to list all supported endpoints
|
||||
"""
|
||||
|
||||
template_name = 'details.html'
|
||||
|
||||
def get(self, request):
|
||||
"""
|
||||
Handle a GET request
|
||||
"""
|
||||
|
||||
# Detect the format our response should be in
|
||||
json_response = \
|
||||
MIME_IS_JSON.match(
|
||||
request.content_type
|
||||
if request.content_type
|
||||
else request.headers.get(
|
||||
'accept', request.headers.get(
|
||||
'content-type', ''))) is not None
|
||||
|
||||
# Show All flag
|
||||
# Support 'yes', '1', 'true', 'enable', 'active', and +
|
||||
show_all = request.GET.get('all', 'no')[0].lower() in (
|
||||
'a', 'y', '1', 't', 'e', '+')
|
||||
|
||||
# Our status
|
||||
status = ResponseCode.okay
|
||||
|
||||
#
|
||||
# Apply Any Global Filters (if identified)
|
||||
#
|
||||
apply_global_filters()
|
||||
|
||||
# Create an Apprise Object
|
||||
a_obj = apprise.Apprise()
|
||||
|
||||
# Load our details
|
||||
details = a_obj.details(show_disabled=show_all)
|
||||
|
||||
# Sort our result set
|
||||
details['schemas'] = sorted(
|
||||
details['schemas'], key=lambda i: str(i['service_name']))
|
||||
|
||||
# Return our content
|
||||
return render(request, self.template_name, {
|
||||
'show_all': show_all,
|
||||
'details': details,
|
||||
}, status=status) if not json_response else \
|
||||
JsonResponse(
|
||||
details,
|
||||
encoder=JSONEncoder,
|
||||
safe=False,
|
||||
status=status)
|
||||
|
||||
|
||||
@method_decorator(never_cache, name='dispatch')
|
||||
class ConfigView(View):
|
||||
"""
|
||||
@ -134,7 +196,13 @@ class AddView(View):
|
||||
Handle a POST request
|
||||
"""
|
||||
# Detect the format our response should be in
|
||||
json_response = MIME_IS_JSON.match(request.content_type) is not None
|
||||
json_response = \
|
||||
MIME_IS_JSON.match(
|
||||
request.content_type
|
||||
if request.content_type
|
||||
else request.headers.get(
|
||||
'accept', request.headers.get(
|
||||
'content-type', ''))) is not None
|
||||
|
||||
if settings.APPRISE_CONFIG_LOCK:
|
||||
# General Access Control
|
||||
@ -323,7 +391,13 @@ class DelView(View):
|
||||
Handle a POST request
|
||||
"""
|
||||
# Detect the format our response should be in
|
||||
json_response = MIME_IS_JSON.match(request.content_type) is not None
|
||||
json_response = \
|
||||
MIME_IS_JSON.match(
|
||||
request.content_type
|
||||
if request.content_type
|
||||
else request.headers.get(
|
||||
'accept', request.headers.get(
|
||||
'content-type', ''))) is not None
|
||||
|
||||
if settings.APPRISE_CONFIG_LOCK:
|
||||
# General Access Control
|
||||
@ -383,7 +457,13 @@ class GetView(View):
|
||||
"""
|
||||
|
||||
# Detect the format our response should be in
|
||||
json_response = MIME_IS_JSON.match(request.content_type) is not None
|
||||
json_response = \
|
||||
MIME_IS_JSON.match(
|
||||
request.content_type
|
||||
if request.content_type
|
||||
else request.headers.get(
|
||||
'accept', request.headers.get(
|
||||
'content-type', ''))) is not None
|
||||
|
||||
if settings.APPRISE_CONFIG_LOCK:
|
||||
# General Access Control
|
||||
@ -465,7 +545,13 @@ class NotifyView(View):
|
||||
Handle a POST request
|
||||
"""
|
||||
# Detect the format our response should be in
|
||||
json_response = MIME_IS_JSON.match(request.content_type) is not None
|
||||
json_response = \
|
||||
MIME_IS_JSON.match(
|
||||
request.content_type
|
||||
if request.content_type
|
||||
else request.headers.get(
|
||||
'accept', request.headers.get(
|
||||
'content-type', ''))) is not None
|
||||
|
||||
# our content
|
||||
content = {}
|
||||
@ -599,59 +685,7 @@ class NotifyView(View):
|
||||
#
|
||||
# Apply Any Global Filters (if identified)
|
||||
#
|
||||
if settings.APPRISE_ALLOW_SERVICES:
|
||||
alphanum_re = re.compile(
|
||||
r'^(?P<name>[a-z][a-z0-9]+)', re.IGNORECASE)
|
||||
entries = \
|
||||
[alphanum_re.match(x).group('name').lower()
|
||||
for x in re.split(r'[ ,]+', settings.APPRISE_ALLOW_SERVICES)
|
||||
if alphanum_re.match(x)]
|
||||
|
||||
for plugin in set(apprise.common.NOTIFY_SCHEMA_MAP.values()):
|
||||
if entries:
|
||||
# Get a list of the current schema's associated with
|
||||
# a given plugin
|
||||
schemas = set(apprise.plugins.details(plugin)
|
||||
['tokens']['schema']['values'])
|
||||
|
||||
# Check what was defined and see if there is a hit
|
||||
for entry in entries:
|
||||
if entry in schemas:
|
||||
# We had a hit; we're done
|
||||
break
|
||||
|
||||
if entry in schemas:
|
||||
entries.remove(entry)
|
||||
# We can keep this plugin enabled and move along to the
|
||||
# next one...
|
||||
continue
|
||||
|
||||
# if we reach here, we have to block our plugin
|
||||
plugin.enabled = False
|
||||
|
||||
for entry in entries:
|
||||
# Generate some noise for those who have bad configurations
|
||||
logger.warning(
|
||||
'APPRISE_ALLOW_SERVICES plugin %s:// was not found - '
|
||||
'ignoring.', entry)
|
||||
|
||||
elif settings.APPRISE_DENY_SERVICES:
|
||||
alphanum_re = re.compile(
|
||||
r'^(?P<name>[a-z][a-z0-9]+)', re.IGNORECASE)
|
||||
entries = \
|
||||
[alphanum_re.match(x).group('name').lower()
|
||||
for x in re.split(r'[ ,]+', settings.APPRISE_DENY_SERVICES)
|
||||
if alphanum_re.match(x)]
|
||||
|
||||
for name in entries:
|
||||
try:
|
||||
# Force plugin to be disabled
|
||||
apprise.common.NOTIFY_SCHEMA_MAP[name].enabled = False
|
||||
|
||||
except KeyError:
|
||||
logger.warning(
|
||||
'APPRISE_DENY_SERVICES plugin %s:// was not found -'
|
||||
' ignoring.', name)
|
||||
apply_global_filters()
|
||||
|
||||
# Prepare our keyword arguments (to be passed into an AppriseAsset
|
||||
# object)
|
||||
@ -892,59 +926,7 @@ class StatelessNotifyView(View):
|
||||
#
|
||||
# Apply Any Global Filters (if identified)
|
||||
#
|
||||
if settings.APPRISE_ALLOW_SERVICES:
|
||||
alphanum_re = re.compile(
|
||||
r'^(?P<name>[a-z][a-z0-9]+)', re.IGNORECASE)
|
||||
entries = \
|
||||
[alphanum_re.match(x).group('name').lower()
|
||||
for x in re.split(r'[ ,]+', settings.APPRISE_ALLOW_SERVICES)
|
||||
if alphanum_re.match(x)]
|
||||
|
||||
for plugin in set(apprise.common.NOTIFY_SCHEMA_MAP.values()):
|
||||
if entries:
|
||||
# Get a list of the current schema's associated with
|
||||
# a given plugin
|
||||
schemas = set(apprise.plugins.details(plugin)
|
||||
['tokens']['schema']['values'])
|
||||
|
||||
# Check what was defined and see if there is a hit
|
||||
for entry in entries:
|
||||
if entry in schemas:
|
||||
# We had a hit; we're done
|
||||
break
|
||||
|
||||
if entry in schemas:
|
||||
entries.remove(entry)
|
||||
# We can keep this plugin enabled and move along to the
|
||||
# next one...
|
||||
continue
|
||||
|
||||
# if we reach here, we have to block our plugin
|
||||
plugin.enabled = False
|
||||
|
||||
for entry in entries:
|
||||
# Generate some noise for those who have bad configurations
|
||||
logger.warning(
|
||||
'APPRISE_ALLOW_SERVICES plugin %s:// was not found - '
|
||||
'ignoring.', entry)
|
||||
|
||||
elif settings.APPRISE_DENY_SERVICES:
|
||||
alphanum_re = re.compile(
|
||||
r'^(?P<name>[a-z][a-z0-9]+)', re.IGNORECASE)
|
||||
entries = \
|
||||
[alphanum_re.match(x).group('name').lower()
|
||||
for x in re.split(r'[ ,]+', settings.APPRISE_DENY_SERVICES)
|
||||
if alphanum_re.match(x)]
|
||||
|
||||
for name in entries:
|
||||
try:
|
||||
# Force plugin to be disabled
|
||||
apprise.common.NOTIFY_SCHEMA_MAP[name].enabled = False
|
||||
|
||||
except KeyError:
|
||||
logger.warning(
|
||||
'APPRISE_DENY_SERVICES plugin %s:// was not found -'
|
||||
' ignoring.', name)
|
||||
apply_global_filters()
|
||||
|
||||
# Prepare our apprise object
|
||||
a_obj = apprise.Apprise(asset=asset)
|
||||
@ -1012,7 +994,7 @@ class JsonUrlView(View):
|
||||
# Privacy flag
|
||||
# Support 'yes', '1', 'true', 'enable', 'active', and +
|
||||
privacy = settings.APPRISE_CONFIG_LOCK or \
|
||||
request.GET.get('privacy', 'no')[0] in (
|
||||
request.GET.get('privacy', 'no')[0].lower() in (
|
||||
'a', 'y', '1', 't', 'e', '+')
|
||||
|
||||
# Optionally filter on tags. Use comma to identify more then one
|
||||
|
@ -24,6 +24,9 @@
|
||||
# THE SOFTWARE.
|
||||
import os
|
||||
|
||||
# Disable Timezones
|
||||
USE_TZ = False
|
||||
|
||||
# Base Directory (relative to settings)
|
||||
BASE_DIR = os.path.dirname(
|
||||
os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
@ -148,3 +148,10 @@ ul.logs li.log_WARNING {
|
||||
ul.logs li.log_ERROR {
|
||||
color: #8B0000;
|
||||
}
|
||||
|
||||
.url-enabled {
|
||||
color:#004d40;
|
||||
}
|
||||
.url-disabled {
|
||||
color: #8B0000;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user