mirror of
https://github.com/caronc/apprise.git
synced 2025-01-08 15:09:27 +01:00
Refactored (Split and Truncate) Overflow Engine (#1038)
This commit is contained in:
parent
f3c699ab82
commit
1da1d4800c
@ -199,6 +199,54 @@ class NotifyBase(URLBase):
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
#
|
||||||
|
# Overflow Defaults / Configuration applicable to SPLIT mode only
|
||||||
|
#
|
||||||
|
|
||||||
|
# Display Count [X/X]
|
||||||
|
# ^^^^^^
|
||||||
|
# \\\\\\
|
||||||
|
# 6 characters (space + count)
|
||||||
|
# Display Count [XX/XX]
|
||||||
|
# ^^^^^^^^
|
||||||
|
# \\\\\\\\
|
||||||
|
# 8 characters (space + count)
|
||||||
|
# Display Count [XXX/XXX]
|
||||||
|
# ^^^^^^^^^^
|
||||||
|
# \\\\\\\\\\
|
||||||
|
# 10 characters (space + count)
|
||||||
|
# Display Count [XXXX/XXXX]
|
||||||
|
# ^^^^^^^^^^^^
|
||||||
|
# \\\\\\\\\\\\
|
||||||
|
# 12 characters (space + count)
|
||||||
|
#
|
||||||
|
# Given the above + some buffer we come up with the following:
|
||||||
|
# If this value is exceeded, display counts automatically shut off
|
||||||
|
overflow_max_display_count_width = 12
|
||||||
|
|
||||||
|
# The number of characters to reserver for whitespace buffering
|
||||||
|
# This is detected automatically, but you can enforce a value if
|
||||||
|
# you desire:
|
||||||
|
overflow_buffer = 0
|
||||||
|
|
||||||
|
# the min accepted length of a title to allow for a counter display
|
||||||
|
overflow_display_count_threshold = 130
|
||||||
|
|
||||||
|
# Whether or not when over-flow occurs, if the title should be repeated
|
||||||
|
# each time the message is split up
|
||||||
|
# - None: Detect
|
||||||
|
# - True: Always display title once
|
||||||
|
# - False: Display the title for each occurance
|
||||||
|
overflow_display_title_once = None
|
||||||
|
|
||||||
|
# If this is set to to True:
|
||||||
|
# The title_maxlen should be considered as a subset of the body_maxlen
|
||||||
|
# Hence: len(title) + len(body) should never be greater then body_maxlen
|
||||||
|
#
|
||||||
|
# If set to False, then there is no corrorlation between title_maxlen
|
||||||
|
# restrictions and that of body_maxlen
|
||||||
|
overflow_amalgamate_title = False
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
"""
|
"""
|
||||||
Initialize some general configuration that will keep things consistent
|
Initialize some general configuration that will keep things consistent
|
||||||
@ -454,7 +502,6 @@ class NotifyBase(URLBase):
|
|||||||
overflow = self.overflow_mode
|
overflow = self.overflow_mode
|
||||||
|
|
||||||
if self.title_maxlen <= 0 and len(title) > 0:
|
if self.title_maxlen <= 0 and len(title) > 0:
|
||||||
|
|
||||||
if self.notify_format == NotifyFormat.HTML:
|
if self.notify_format == NotifyFormat.HTML:
|
||||||
# Content is appended to body as html
|
# Content is appended to body as html
|
||||||
body = '<{open_tag}>{title}</{close_tag}>' \
|
body = '<{open_tag}>{title}</{close_tag}>' \
|
||||||
@ -490,17 +537,37 @@ class NotifyBase(URLBase):
|
|||||||
response.append({'body': body, 'title': title})
|
response.append({'body': body, 'title': title})
|
||||||
return response
|
return response
|
||||||
|
|
||||||
elif len(title) > self.title_maxlen:
|
# a value of '2' allows for the \r\n that is applied when
|
||||||
# Truncate our Title
|
# amalgamating the title
|
||||||
title = title[:self.title_maxlen]
|
overflow_buffer = max(2, self.overflow_buffer) \
|
||||||
|
if (self.title_maxlen == 0 and len(title)) \
|
||||||
|
else self.overflow_buffer
|
||||||
|
|
||||||
if self.body_maxlen > self.title_maxlen - 2:
|
#
|
||||||
# Combine title length into body if defined (2 for \r\n)
|
# If we reach here in our code, then we're using TRUNCATE, or SPLIT
|
||||||
body_maxlen = self.body_maxlen \
|
# actions which require some math to handle the data
|
||||||
if not title else self.body_maxlen - len(title) - 2
|
#
|
||||||
|
|
||||||
|
# Handle situations where our body and title are amalamated into one
|
||||||
|
# calculation
|
||||||
|
title_maxlen = self.title_maxlen \
|
||||||
|
if not self.overflow_amalgamate_title \
|
||||||
|
else min(len(title) + self.overflow_max_display_count_width,
|
||||||
|
self.title_maxlen, self.body_maxlen)
|
||||||
|
|
||||||
|
if len(title) > title_maxlen:
|
||||||
|
# Truncate our Title
|
||||||
|
title = title[:title_maxlen].rstrip()
|
||||||
|
|
||||||
|
if self.overflow_amalgamate_title and (
|
||||||
|
self.body_maxlen - overflow_buffer) >= title_maxlen:
|
||||||
|
body_maxlen = (self.body_maxlen if not title else (
|
||||||
|
self.body_maxlen - title_maxlen)) - overflow_buffer
|
||||||
else:
|
else:
|
||||||
# status quo
|
# status quo
|
||||||
body_maxlen = self.body_maxlen
|
body_maxlen = self.body_maxlen \
|
||||||
|
if not self.overflow_amalgamate_title else \
|
||||||
|
(self.body_maxlen - overflow_buffer)
|
||||||
|
|
||||||
if body_maxlen > 0 and len(body) <= body_maxlen:
|
if body_maxlen > 0 and len(body) <= body_maxlen:
|
||||||
response.append({'body': body, 'title': title})
|
response.append({'body': body, 'title': title})
|
||||||
@ -509,43 +576,109 @@ class NotifyBase(URLBase):
|
|||||||
if overflow == OverflowMode.TRUNCATE:
|
if overflow == OverflowMode.TRUNCATE:
|
||||||
# Truncate our body and return
|
# Truncate our body and return
|
||||||
response.append({
|
response.append({
|
||||||
'body': body[:body_maxlen],
|
'body': body[:body_maxlen].lstrip('\r\n\x0b\x0c').rstrip(),
|
||||||
'title': title,
|
'title': title,
|
||||||
})
|
})
|
||||||
# For truncate mode, we're done now
|
# For truncate mode, we're done now
|
||||||
return response
|
return response
|
||||||
|
|
||||||
# Display Count [XX/XX]
|
if self.overflow_display_title_once is None:
|
||||||
# ^^^^^^^^
|
# Detect if we only display our title once or not:
|
||||||
# \\\\\\\\
|
overflow_display_title_once = \
|
||||||
# 8 characters (space + count)
|
True if self.overflow_amalgamate_title and \
|
||||||
display_count_width = 8
|
body_maxlen < self.overflow_display_count_threshold \
|
||||||
|
else False
|
||||||
|
else:
|
||||||
|
# Take on defined value
|
||||||
|
|
||||||
# the min accepted length of a title to allow for a counter display
|
overflow_display_title_once = self.overflow_display_title_once
|
||||||
display_count_threshold = 130
|
|
||||||
|
|
||||||
show_counter = title and len(body) > body_maxlen \
|
|
||||||
and self.title_maxlen > \
|
|
||||||
(display_count_threshold + display_count_width)
|
|
||||||
|
|
||||||
count = 0
|
|
||||||
if show_counter:
|
|
||||||
count = int(len(body) / body_maxlen) \
|
|
||||||
+ (1 if len(body) % body_maxlen else 0)
|
|
||||||
|
|
||||||
if len(title) > self.title_maxlen - display_count_width:
|
|
||||||
# Truncate our title further
|
|
||||||
title = title[:self.title_maxlen - display_count_width]
|
|
||||||
|
|
||||||
# If we reach here, then we are in SPLIT mode.
|
# If we reach here, then we are in SPLIT mode.
|
||||||
# For here, we want to split the message as many times as we have to
|
# For here, we want to split the message as many times as we have to
|
||||||
# in order to fit it within the designated limits.
|
# in order to fit it within the designated limits.
|
||||||
response = [{
|
if not overflow_display_title_once and not (
|
||||||
'body': body[i: i + body_maxlen],
|
# edge case that can occur when overflow_display_title_once is
|
||||||
'title': title + (
|
# forced off, but no body exists
|
||||||
'' if not count else
|
self.overflow_amalgamate_title and body_maxlen <= 0):
|
||||||
' [{:02}/{:02}]'.format(idx, count))} for idx, i in
|
|
||||||
enumerate(range(0, len(body), body_maxlen), start=1)]
|
show_counter = title and len(body) > body_maxlen and \
|
||||||
|
((self.overflow_amalgamate_title and
|
||||||
|
body_maxlen >= self.overflow_display_count_threshold) or
|
||||||
|
(not self.overflow_amalgamate_title and
|
||||||
|
title_maxlen > self.overflow_display_count_threshold)) and (
|
||||||
|
title_maxlen > (self.overflow_max_display_count_width +
|
||||||
|
overflow_buffer) and
|
||||||
|
self.title_maxlen >= self.overflow_display_count_threshold)
|
||||||
|
|
||||||
|
count = 0
|
||||||
|
template = ''
|
||||||
|
if show_counter:
|
||||||
|
# introduce padding
|
||||||
|
body_maxlen -= overflow_buffer
|
||||||
|
|
||||||
|
count = int(len(body) / body_maxlen) \
|
||||||
|
+ (1 if len(body) % body_maxlen else 0)
|
||||||
|
|
||||||
|
# Detect padding and prepare template
|
||||||
|
digits = len(str(count))
|
||||||
|
template = ' [{:0%d}/{:0%d}]' % (digits, digits)
|
||||||
|
|
||||||
|
# Update our counter
|
||||||
|
overflow_display_count_width = 4 + (digits * 2)
|
||||||
|
if overflow_display_count_width <= \
|
||||||
|
self.overflow_max_display_count_width:
|
||||||
|
if len(title) > \
|
||||||
|
title_maxlen - overflow_display_count_width:
|
||||||
|
# Truncate our title further
|
||||||
|
title = title[:title_maxlen -
|
||||||
|
overflow_display_count_width]
|
||||||
|
|
||||||
|
else: # Way to many messages to display
|
||||||
|
show_counter = False
|
||||||
|
|
||||||
|
response = [{
|
||||||
|
'body': body[i: i + body_maxlen]
|
||||||
|
.lstrip('\r\n\x0b\x0c').rstrip(),
|
||||||
|
'title': title + (
|
||||||
|
'' if not show_counter else
|
||||||
|
template.format(idx, count))} for idx, i in
|
||||||
|
enumerate(range(0, len(body), body_maxlen), start=1)]
|
||||||
|
|
||||||
|
else: # Display title once and move on
|
||||||
|
response = []
|
||||||
|
try:
|
||||||
|
i = range(0, len(body), body_maxlen)[0]
|
||||||
|
|
||||||
|
response.append({
|
||||||
|
'body': body[i: i + body_maxlen]
|
||||||
|
.lstrip('\r\n\x0b\x0c').rstrip(),
|
||||||
|
'title': title,
|
||||||
|
})
|
||||||
|
|
||||||
|
except (ValueError, IndexError):
|
||||||
|
# IndexError:
|
||||||
|
# - This happens if there simply was no body to display
|
||||||
|
|
||||||
|
# ValueError:
|
||||||
|
# - This happens when body_maxlen < 0 (due to title being
|
||||||
|
# so large)
|
||||||
|
|
||||||
|
# No worries; send title along
|
||||||
|
response.append({
|
||||||
|
'body': '',
|
||||||
|
'title': title,
|
||||||
|
})
|
||||||
|
|
||||||
|
# Ensure our start is set properly
|
||||||
|
body_maxlen = 0
|
||||||
|
|
||||||
|
# Now re-calculate based on the increased length
|
||||||
|
for i in range(body_maxlen, len(body), self.body_maxlen):
|
||||||
|
response.append({
|
||||||
|
'body': body[i: i + self.body_maxlen]
|
||||||
|
.lstrip('\r\n\x0b\x0c').rstrip(),
|
||||||
|
'title': '',
|
||||||
|
})
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
@ -105,6 +105,10 @@ class NotifyDiscord(NotifyBase):
|
|||||||
# The maximum allowable characters allowed in the body per message
|
# The maximum allowable characters allowed in the body per message
|
||||||
body_maxlen = 2000
|
body_maxlen = 2000
|
||||||
|
|
||||||
|
# The 2000 characters above defined by the body_maxlen include that of the
|
||||||
|
# title. Setting this to True ensures overflow options behave properly
|
||||||
|
overflow_amalgamate_title = True
|
||||||
|
|
||||||
# Discord has a limit of the number of fields you can include in an
|
# Discord has a limit of the number of fields you can include in an
|
||||||
# embeds message. This value allows the discord message to safely
|
# embeds message. This value allows the discord message to safely
|
||||||
# break into multiple messages to handle these cases.
|
# break into multiple messages to handle these cases.
|
||||||
|
@ -652,7 +652,7 @@ def test_plugin_discord_overflow(mock_post):
|
|||||||
# Ensure we never exceed 2000 characters
|
# Ensure we never exceed 2000 characters
|
||||||
for entry in results:
|
for entry in results:
|
||||||
assert len(entry['title']) <= instance.title_maxlen
|
assert len(entry['title']) <= instance.title_maxlen
|
||||||
assert len(entry['title']) + len(entry['body']) < instance.body_maxlen
|
assert len(entry['title']) + len(entry['body']) <= instance.body_maxlen
|
||||||
|
|
||||||
|
|
||||||
@mock.patch('requests.post')
|
@mock.patch('requests.post')
|
||||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user