mirror of https://github.com/caronc/apprise
Improve split and truncate overflow methods (#1035)
parent
bb4acd0bdf
commit
f3c699ab82
|
@ -494,25 +494,58 @@ class NotifyBase(URLBase):
|
|||
# Truncate our Title
|
||||
title = title[:self.title_maxlen]
|
||||
|
||||
if self.body_maxlen > 0 and len(body) <= self.body_maxlen:
|
||||
if self.body_maxlen > self.title_maxlen - 2:
|
||||
# Combine title length into body if defined (2 for \r\n)
|
||||
body_maxlen = self.body_maxlen \
|
||||
if not title else self.body_maxlen - len(title) - 2
|
||||
else:
|
||||
# status quo
|
||||
body_maxlen = self.body_maxlen
|
||||
|
||||
if body_maxlen > 0 and len(body) <= body_maxlen:
|
||||
response.append({'body': body, 'title': title})
|
||||
return response
|
||||
|
||||
if overflow == OverflowMode.TRUNCATE:
|
||||
# Truncate our body and return
|
||||
response.append({
|
||||
'body': body[:self.body_maxlen],
|
||||
'body': body[:body_maxlen],
|
||||
'title': title,
|
||||
})
|
||||
# For truncate mode, we're done now
|
||||
return response
|
||||
|
||||
# Display Count [XX/XX]
|
||||
# ^^^^^^^^
|
||||
# \\\\\\\\
|
||||
# 8 characters (space + count)
|
||||
display_count_width = 8
|
||||
|
||||
# the min accepted length of a title to allow for a counter display
|
||||
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.
|
||||
# For here, we want to split the message as many times as we have to
|
||||
# in order to fit it within the designated limits.
|
||||
response = [{
|
||||
'body': body[i: i + self.body_maxlen],
|
||||
'title': title} for i in range(0, len(body), self.body_maxlen)]
|
||||
'body': body[i: i + body_maxlen],
|
||||
'title': title + (
|
||||
'' if not count else
|
||||
' [{:02}/{:02}]'.format(idx, count))} for idx, i in
|
||||
enumerate(range(0, len(body), body_maxlen), start=1)]
|
||||
|
||||
return response
|
||||
|
||||
|
|
|
@ -40,6 +40,11 @@ from apprise import Apprise
|
|||
from apprise import AppriseAttachment
|
||||
from apprise import NotifyType
|
||||
from apprise import NotifyFormat
|
||||
from apprise.common import OverflowMode
|
||||
|
||||
from random import choice
|
||||
from string import ascii_uppercase as str_alpha
|
||||
from string import digits as str_num
|
||||
|
||||
# Disable logging for a cleaner testing output
|
||||
import logging
|
||||
|
@ -593,6 +598,63 @@ def test_plugin_discord_general(mock_post):
|
|||
assert response['params'].get('thread_id') == '12345'
|
||||
|
||||
|
||||
@mock.patch('requests.post')
|
||||
def test_plugin_discord_overflow(mock_post):
|
||||
"""
|
||||
NotifyDiscord() Overflow Checks
|
||||
|
||||
"""
|
||||
|
||||
# Initialize some generic (but valid) tokens
|
||||
webhook_id = 'A' * 24
|
||||
webhook_token = 'B' * 64
|
||||
|
||||
# Prepare Mock
|
||||
mock_post.return_value = requests.Request()
|
||||
mock_post.return_value.status_code = requests.codes.ok
|
||||
|
||||
# Some variables we use to control the data we work with
|
||||
body_len = 8000
|
||||
title_len = 1024
|
||||
|
||||
# Number of characters per line
|
||||
row = 24
|
||||
|
||||
# Create a large body and title with random data
|
||||
body = ''.join(choice(str_alpha + str_num + ' ') for _ in range(body_len))
|
||||
body = '\r\n'.join([body[i: i + row] for i in range(0, len(body), row)])
|
||||
|
||||
# Create our title using random data
|
||||
title = ''.join(choice(str_alpha + str_num) for _ in range(title_len))
|
||||
|
||||
results = NotifyDiscord.parse_url(
|
||||
f'discord://{webhook_id}/{webhook_token}/?overflow=split')
|
||||
|
||||
assert isinstance(results, dict)
|
||||
assert results['user'] is None
|
||||
assert results['webhook_id'] == webhook_id
|
||||
assert results['webhook_token'] == webhook_token
|
||||
assert results['password'] is None
|
||||
assert results['port'] is None
|
||||
assert results['host'] == webhook_id
|
||||
assert results['fullpath'] == f'/{webhook_token}/'
|
||||
assert results['path'] == f'/{webhook_token}/'
|
||||
assert results['query'] is None
|
||||
assert results['schema'] == 'discord'
|
||||
assert results['url'] == f'discord://{webhook_id}/{webhook_token}/'
|
||||
|
||||
instance = NotifyDiscord(**results)
|
||||
assert isinstance(instance, NotifyDiscord)
|
||||
|
||||
results = instance._apply_overflow(
|
||||
body, title=title, overflow=OverflowMode.SPLIT)
|
||||
|
||||
# Ensure we never exceed 2000 characters
|
||||
for entry in results:
|
||||
assert len(entry['title']) <= instance.title_maxlen
|
||||
assert len(entry['title']) + len(entry['body']) < instance.body_maxlen
|
||||
|
||||
|
||||
@mock.patch('requests.post')
|
||||
def test_plugin_discord_markdown_extra(mock_post):
|
||||
"""
|
||||
|
|
|
@ -163,6 +163,7 @@ def test_notify_overflow_truncate():
|
|||
# and that the body remains untouched
|
||||
chunks = obj._apply_overflow(body=body, title=title)
|
||||
assert len(chunks) == 1
|
||||
# -2 because \r\n are factored into calculation (safe whitespace)
|
||||
assert body[0:TestNotification.body_maxlen] == chunks[0].get('body')
|
||||
assert title == chunks[0].get('title')
|
||||
|
||||
|
@ -327,10 +328,26 @@ def test_notify_overflow_split():
|
|||
chunks = obj._apply_overflow(body=body, title=title)
|
||||
offset = 0
|
||||
assert len(chunks) == 4
|
||||
for chunk in chunks:
|
||||
# Our title never changes
|
||||
assert title == chunk.get('title')
|
||||
for idx, chunk in enumerate(chunks, start=1):
|
||||
# Our title has a counter added to it
|
||||
assert title[:-8] == chunk.get('title')[:-8]
|
||||
assert chunk.get('title')[-8:] == \
|
||||
' [{:02}/{:02}]'.format(idx, len(chunks))
|
||||
# Our body is only broken up; not lost
|
||||
_body = chunk.get('body')
|
||||
assert body[offset: len(_body) + offset].rstrip() == _body
|
||||
offset += len(_body)
|
||||
|
||||
# Another edge case where the title just isn't that long leaving
|
||||
# a lot of space for the [xx/xx] entries (no truncation needed)
|
||||
chunks = obj._apply_overflow(body=body, title=title[:20])
|
||||
offset = 0
|
||||
assert len(chunks) == 4
|
||||
for idx, chunk in enumerate(chunks, start=1):
|
||||
# Our title has a counter added to it
|
||||
assert title[:20] == chunk.get('title')[:-8]
|
||||
assert chunk.get('title')[-8:] == \
|
||||
' [{:02}/{:02}]'.format(idx, len(chunks))
|
||||
# Our body is only broken up; not lost
|
||||
_body = chunk.get('body')
|
||||
assert body[offset: len(_body) + offset].rstrip() == _body
|
||||
|
@ -386,6 +403,44 @@ def test_notify_overflow_split():
|
|||
assert bulk[offset: len(_body) + offset] == _body
|
||||
offset += len(_body)
|
||||
|
||||
#
|
||||
# Test case where our title_len is shorter then the value
|
||||
# that would otherwise trigger the [XX/XX] elements
|
||||
#
|
||||
|
||||
class TestNotification(NotifyBase):
|
||||
|
||||
# Set a small title length
|
||||
title_maxlen = 100
|
||||
|
||||
# Enforce a body length. Make sure it's an int.
|
||||
body_maxlen = int(body_len / 4)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
|
||||
def notify(self, *args, **kwargs):
|
||||
# Pretend everything is okay
|
||||
return True
|
||||
|
||||
# Load our object
|
||||
obj = TestNotification(overflow=OverflowMode.SPLIT)
|
||||
assert obj is not None
|
||||
|
||||
# Verify that we break the title to a max length of our title_max
|
||||
# and that the body remains untouched
|
||||
chunks = obj._apply_overflow(body=body, title=title)
|
||||
offset = 0
|
||||
assert len(chunks) == 7
|
||||
for idx, chunk in enumerate(chunks, start=1):
|
||||
# Our title is truncated and no counter added
|
||||
assert title[:100] == chunk.get('title')
|
||||
|
||||
# Our body is only broken up; not lost
|
||||
_body = chunk.get('body')
|
||||
assert body[offset: len(_body) + offset].rstrip() == _body
|
||||
offset += len(_body)
|
||||
|
||||
|
||||
def test_notify_markdown_general():
|
||||
"""
|
||||
|
|
Loading…
Reference in New Issue