Added Attachment Support for Apprise API (#873)

This commit is contained in:
Chris Caron 2023-05-12 17:51:04 -04:00 committed by GitHub
parent 00ddb098e5
commit b327abf134
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 108 additions and 2 deletions

View File

@ -684,6 +684,7 @@ class Apprise:
'setup_url': getattr(plugin, 'setup_url', None), 'setup_url': getattr(plugin, 'setup_url', None),
# Placeholder - populated below # Placeholder - populated below
'details': None, 'details': None,
# Differentiat between what is a custom loaded plugin and # Differentiat between what is a custom loaded plugin and
# which is native. # which is native.
'category': getattr(plugin, 'category', None) 'category': getattr(plugin, 'category', None)

View File

@ -33,6 +33,7 @@
import re import re
import requests import requests
from json import dumps from json import dumps
import base64
from .NotifyBase import NotifyBase from .NotifyBase import NotifyBase
from ..URLBase import PrivacyMode from ..URLBase import PrivacyMode
@ -209,15 +210,51 @@ class NotifyAppriseAPI(NotifyBase):
token=self.pprint(self.token, privacy, safe=''), token=self.pprint(self.token, privacy, safe=''),
params=NotifyAppriseAPI.urlencode(params)) params=NotifyAppriseAPI.urlencode(params))
def send(self, body, title='', notify_type=NotifyType.INFO, **kwargs): def send(self, body, title='', notify_type=NotifyType.INFO, attach=None,
**kwargs):
""" """
Perform Apprise API Notification Perform Apprise API Notification
""" """
headers = {} # Prepare HTTP Headers
headers = {
'User-Agent': self.app_id,
'Content-Type': 'application/json'
}
# Apply any/all header over-rides defined # Apply any/all header over-rides defined
headers.update(self.headers) headers.update(self.headers)
# Track our potential attachments
attachments = []
if attach:
for attachment in attach:
# Perform some simple error checking
if not attachment:
# We could not access the attachment
self.logger.error(
'Could not access attachment {}.'.format(
attachment.url(privacy=True)))
return False
try:
with open(attachment.path, 'rb') as f:
# Output must be in a DataURL format (that's what
# PushSafer calls it):
attachments.append({
'filename': attachment.name,
'base64': base64.b64encode(f.read())
.decode('utf-8'),
'mimetype': attachment.mimetype,
})
except (OSError, IOError) as e:
self.logger.warning(
'An I/O error occurred while reading {}.'.format(
attachment.name if attachment else 'attachment'))
self.logger.debug('I/O Exception: %s' % str(e))
return False
# prepare Apprise API Object # prepare Apprise API Object
payload = { payload = {
# Apprise API Payload # Apprise API Payload
@ -225,6 +262,7 @@ class NotifyAppriseAPI(NotifyBase):
'body': body, 'body': body,
'type': notify_type, 'type': notify_type,
'format': self.notify_format, 'format': self.notify_format,
'attachments': attachments,
} }
if self.__tags: if self.__tags:

View File

@ -30,14 +30,22 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE. # POSSIBILITY OF SUCH DAMAGE.
import os
from unittest import mock
from apprise.plugins.NotifyAppriseAPI import NotifyAppriseAPI from apprise.plugins.NotifyAppriseAPI import NotifyAppriseAPI
from helpers import AppriseURLTester from helpers import AppriseURLTester
import requests import requests
from apprise import Apprise
from apprise import AppriseAttachment
from apprise import NotifyType
# Disable logging for a cleaner testing output # Disable logging for a cleaner testing output
import logging import logging
logging.disable(logging.CRITICAL) logging.disable(logging.CRITICAL)
# Attachment Directory
TEST_VAR_DIR = os.path.join(os.path.dirname(__file__), 'var')
# Our Testing URLs # Our Testing URLs
apprise_url_tests = ( apprise_url_tests = (
('apprise://', { ('apprise://', {
@ -172,3 +180,62 @@ def test_plugin_apprise_urls():
# Run our general tests # Run our general tests
AppriseURLTester(tests=apprise_url_tests).run_all() AppriseURLTester(tests=apprise_url_tests).run_all()
@mock.patch('requests.post')
def test_notify_apprise_api_attachments(mock_post):
"""
NotifyAppriseAPI() Attachments
"""
okay_response = requests.Request()
okay_response.status_code = requests.codes.ok
okay_response.content = ""
# Assign our mock object our return value
mock_post.return_value = okay_response
obj = Apprise.instantiate('apprise://user@localhost/mytoken1/')
assert isinstance(obj, NotifyAppriseAPI)
# Test Valid Attachment
path = os.path.join(TEST_VAR_DIR, 'apprise-test.gif')
attach = AppriseAttachment(path)
assert obj.notify(
body='body', title='title', notify_type=NotifyType.INFO,
attach=attach) is True
# Test invalid attachment
path = os.path.join(TEST_VAR_DIR, '/invalid/path/to/an/invalid/file.jpg')
assert obj.notify(
body='body', title='title', notify_type=NotifyType.INFO,
attach=path) is False
# Test Valid Attachment (load 3)
path = (
os.path.join(TEST_VAR_DIR, 'apprise-test.gif'),
os.path.join(TEST_VAR_DIR, 'apprise-test.gif'),
os.path.join(TEST_VAR_DIR, 'apprise-test.gif'),
)
attach = AppriseAttachment(path)
# Return our good configuration
mock_post.side_effect = None
mock_post.return_value = okay_response
with mock.patch('builtins.open', side_effect=OSError()):
# We can't send the message we can't open the attachment for reading
assert obj.notify(
body='body', title='title', notify_type=NotifyType.INFO,
attach=attach) is False
# test the handling of our batch modes
obj = Apprise.instantiate('apprise://user@localhost/mytoken1/')
assert isinstance(obj, NotifyAppriseAPI)
# Now send an attachment normally without issues
mock_post.reset_mock()
assert obj.notify(
body='body', title='title', notify_type=NotifyType.INFO,
attach=attach) is True
assert mock_post.call_count == 1