|
|
@ -1,4 +1,5 @@
|
|
|
|
import apprise
|
|
|
|
import apprise
|
|
|
|
|
|
|
|
import time
|
|
|
|
from jinja2 import Environment, BaseLoader
|
|
|
|
from jinja2 import Environment, BaseLoader
|
|
|
|
from apprise import NotifyFormat
|
|
|
|
from apprise import NotifyFormat
|
|
|
|
import json
|
|
|
|
import json
|
|
|
@ -131,90 +132,94 @@ def process_notification(n_object, datastore):
|
|
|
|
# Initially text or whatever
|
|
|
|
# Initially text or whatever
|
|
|
|
n_format = datastore.data['settings']['application'].get('notification_format', valid_notification_formats[default_notification_format])
|
|
|
|
n_format = datastore.data['settings']['application'].get('notification_format', valid_notification_formats[default_notification_format])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# https://github.com/caronc/apprise/wiki/Development_LogCapture
|
|
|
|
# https://github.com/caronc/apprise/wiki/Development_LogCapture
|
|
|
|
# Anything higher than or equal to WARNING (which covers things like Connection errors)
|
|
|
|
# Anything higher than or equal to WARNING (which covers things like Connection errors)
|
|
|
|
# raise it as an exception
|
|
|
|
# raise it as an exception
|
|
|
|
apobjs=[]
|
|
|
|
|
|
|
|
sent_objs=[]
|
|
|
|
sent_objs = []
|
|
|
|
from .apprise_asset import asset
|
|
|
|
from .apprise_asset import asset
|
|
|
|
for url in n_object['notification_urls']:
|
|
|
|
apobj = apprise.Apprise(debug=True, asset=asset)
|
|
|
|
url = jinja2_env.from_string(url).render(**notification_parameters)
|
|
|
|
|
|
|
|
apobj = apprise.Apprise(debug=True, asset=asset)
|
|
|
|
if not n_object.get('notification_urls'):
|
|
|
|
url = url.strip()
|
|
|
|
return None
|
|
|
|
if len(url):
|
|
|
|
|
|
|
|
|
|
|
|
with apprise.LogCapture(level=apprise.logging.DEBUG) as logs:
|
|
|
|
|
|
|
|
for url in n_object['notification_urls']:
|
|
|
|
|
|
|
|
url = url.strip()
|
|
|
|
print(">> Process Notification: AppRise notifying {}".format(url))
|
|
|
|
print(">> Process Notification: AppRise notifying {}".format(url))
|
|
|
|
with apprise.LogCapture(level=apprise.logging.DEBUG) as logs:
|
|
|
|
url = jinja2_env.from_string(url).render(**notification_parameters)
|
|
|
|
# Re 323 - Limit discord length to their 2000 char limit total or it wont send.
|
|
|
|
|
|
|
|
# Because different notifications may require different pre-processing, run each sequentially :(
|
|
|
|
# Re 323 - Limit discord length to their 2000 char limit total or it wont send.
|
|
|
|
# 2000 bytes minus -
|
|
|
|
# Because different notifications may require different pre-processing, run each sequentially :(
|
|
|
|
# 200 bytes for the overhead of the _entire_ json payload, 200 bytes for {tts, wait, content} etc headers
|
|
|
|
# 2000 bytes minus -
|
|
|
|
# Length of URL - Incase they specify a longer custom avatar_url
|
|
|
|
# 200 bytes for the overhead of the _entire_ json payload, 200 bytes for {tts, wait, content} etc headers
|
|
|
|
|
|
|
|
# Length of URL - Incase they specify a longer custom avatar_url
|
|
|
|
# So if no avatar_url is specified, add one so it can be correctly calculated into the total payload
|
|
|
|
|
|
|
|
k = '?' if not '?' in url else '&'
|
|
|
|
# So if no avatar_url is specified, add one so it can be correctly calculated into the total payload
|
|
|
|
if not 'avatar_url' in url \
|
|
|
|
k = '?' if not '?' in url else '&'
|
|
|
|
and not url.startswith('mail') \
|
|
|
|
if not 'avatar_url' in url \
|
|
|
|
and not url.startswith('post') \
|
|
|
|
and not url.startswith('mail') \
|
|
|
|
and not url.startswith('get') \
|
|
|
|
and not url.startswith('post') \
|
|
|
|
and not url.startswith('delete') \
|
|
|
|
and not url.startswith('get') \
|
|
|
|
and not url.startswith('put'):
|
|
|
|
and not url.startswith('delete') \
|
|
|
|
url += k + 'avatar_url=https://raw.githubusercontent.com/dgtlmoon/changedetection.io/master/changedetectionio/static/images/avatar-256x256.png'
|
|
|
|
and not url.startswith('put'):
|
|
|
|
|
|
|
|
url += k + 'avatar_url=https://raw.githubusercontent.com/dgtlmoon/changedetection.io/master/changedetectionio/static/images/avatar-256x256.png'
|
|
|
|
if url.startswith('tgram://'):
|
|
|
|
|
|
|
|
# Telegram only supports a limit subset of HTML, remove the '<br>' we place in.
|
|
|
|
if url.startswith('tgram://'):
|
|
|
|
# re https://github.com/dgtlmoon/changedetection.io/issues/555
|
|
|
|
# Telegram only supports a limit subset of HTML, remove the '<br>' we place in.
|
|
|
|
# @todo re-use an existing library we have already imported to strip all non-allowed tags
|
|
|
|
# re https://github.com/dgtlmoon/changedetection.io/issues/555
|
|
|
|
n_body = n_body.replace('<br>', '\n')
|
|
|
|
# @todo re-use an existing library we have already imported to strip all non-allowed tags
|
|
|
|
n_body = n_body.replace('</br>', '\n')
|
|
|
|
n_body = n_body.replace('<br>', '\n')
|
|
|
|
# real limit is 4096, but minus some for extra metadata
|
|
|
|
n_body = n_body.replace('</br>', '\n')
|
|
|
|
payload_max_size = 3600
|
|
|
|
# real limit is 4096, but minus some for extra metadata
|
|
|
|
body_limit = max(0, payload_max_size - len(n_title))
|
|
|
|
payload_max_size = 3600
|
|
|
|
n_title = n_title[0:payload_max_size]
|
|
|
|
body_limit = max(0, payload_max_size - len(n_title))
|
|
|
|
n_body = n_body[0:body_limit]
|
|
|
|
n_title = n_title[0:payload_max_size]
|
|
|
|
|
|
|
|
n_body = n_body[0:body_limit]
|
|
|
|
elif url.startswith('discord://') or url.startswith('https://discordapp.com/api/webhooks') or url.startswith('https://discord.com/api'):
|
|
|
|
|
|
|
|
# real limit is 2000, but minus some for extra metadata
|
|
|
|
elif url.startswith('discord://') or url.startswith('https://discordapp.com/api/webhooks') or url.startswith(
|
|
|
|
payload_max_size = 1700
|
|
|
|
'https://discord.com/api'):
|
|
|
|
body_limit = max(0, payload_max_size - len(n_title))
|
|
|
|
# real limit is 2000, but minus some for extra metadata
|
|
|
|
n_title = n_title[0:payload_max_size]
|
|
|
|
payload_max_size = 1700
|
|
|
|
n_body = n_body[0:body_limit]
|
|
|
|
body_limit = max(0, payload_max_size - len(n_title))
|
|
|
|
|
|
|
|
n_title = n_title[0:payload_max_size]
|
|
|
|
elif url.startswith('mailto'):
|
|
|
|
n_body = n_body[0:body_limit]
|
|
|
|
# Apprise will default to HTML, so we need to override it
|
|
|
|
|
|
|
|
# So that whats' generated in n_body is in line with what is going to be sent.
|
|
|
|
elif url.startswith('mailto'):
|
|
|
|
# https://github.com/caronc/apprise/issues/633#issuecomment-1191449321
|
|
|
|
# Apprise will default to HTML, so we need to override it
|
|
|
|
if not 'format=' in url and (n_format == 'Text' or n_format == 'Markdown'):
|
|
|
|
# So that whats' generated in n_body is in line with what is going to be sent.
|
|
|
|
prefix = '?' if not '?' in url else '&'
|
|
|
|
# https://github.com/caronc/apprise/issues/633#issuecomment-1191449321
|
|
|
|
# Apprise format is lowercase text https://github.com/caronc/apprise/issues/633
|
|
|
|
if not 'format=' in url and (n_format == 'Text' or n_format == 'Markdown'):
|
|
|
|
n_format = n_format.lower()
|
|
|
|
prefix = '?' if not '?' in url else '&'
|
|
|
|
url = f"{url}{prefix}format={n_format}"
|
|
|
|
# Apprise format is lowercase text https://github.com/caronc/apprise/issues/633
|
|
|
|
# If n_format == HTML, then apprise email should default to text/html and we should be sending HTML only
|
|
|
|
n_format = n_format.lower()
|
|
|
|
|
|
|
|
url = f"{url}{prefix}format={n_format}"
|
|
|
|
apobj.add(url)
|
|
|
|
# If n_format == HTML, then apprise email should default to text/html and we should be sending HTML only
|
|
|
|
|
|
|
|
|
|
|
|
apobj.notify(
|
|
|
|
apobj.add(url)
|
|
|
|
title=n_title,
|
|
|
|
|
|
|
|
body=n_body,
|
|
|
|
sent_objs.append({'title': n_title,
|
|
|
|
body_format=n_format,
|
|
|
|
'body': n_body,
|
|
|
|
# False is not an option for AppRise, must be type None
|
|
|
|
'url': url,
|
|
|
|
attach=n_object.get('screenshot', None)
|
|
|
|
'body_format': n_format})
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
# Blast off the notifications tht are set in .add()
|
|
|
|
apobj.clear()
|
|
|
|
apobj.notify(
|
|
|
|
|
|
|
|
title=n_title,
|
|
|
|
# Incase it needs to exist in memory for a while after to process(?)
|
|
|
|
body=n_body,
|
|
|
|
apobjs.append(apobj)
|
|
|
|
body_format=n_format,
|
|
|
|
|
|
|
|
# False is not an option for AppRise, must be type None
|
|
|
|
# Returns empty string if nothing found, multi-line string otherwise
|
|
|
|
attach=n_object.get('screenshot', None)
|
|
|
|
log_value = logs.getvalue()
|
|
|
|
)
|
|
|
|
if log_value and 'WARNING' in log_value or 'ERROR' in log_value:
|
|
|
|
|
|
|
|
raise Exception(log_value)
|
|
|
|
# Give apprise time to register an error
|
|
|
|
|
|
|
|
time.sleep(3)
|
|
|
|
sent_objs.append({'title': n_title,
|
|
|
|
|
|
|
|
'body': n_body,
|
|
|
|
# Returns empty string if nothing found, multi-line string otherwise
|
|
|
|
'url' : url,
|
|
|
|
log_value = logs.getvalue()
|
|
|
|
'body_format': n_format})
|
|
|
|
|
|
|
|
|
|
|
|
if log_value and 'WARNING' in log_value or 'ERROR' in log_value:
|
|
|
|
|
|
|
|
raise Exception(log_value)
|
|
|
|
|
|
|
|
|
|
|
|
# Return what was sent for better logging - after the for loop
|
|
|
|
# Return what was sent for better logging - after the for loop
|
|
|
|
return sent_objs
|
|
|
|
return sent_objs
|
|
|
|