Skip to content

Commit

Permalink
Added Attachment Support for Apprise API (#873)
Browse files Browse the repository at this point in the history
  • Loading branch information
caronc authored May 12, 2023
1 parent 00ddb09 commit b327abf
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 2 deletions.
1 change: 1 addition & 0 deletions apprise/Apprise.py
Original file line number Diff line number Diff line change
Expand Up @@ -684,6 +684,7 @@ def details(self, lang=None, show_requirements=False, show_disabled=False):
'setup_url': getattr(plugin, 'setup_url', None),
# Placeholder - populated below
'details': None,

# Differentiat between what is a custom loaded plugin and
# which is native.
'category': getattr(plugin, 'category', None)
Expand Down
42 changes: 40 additions & 2 deletions apprise/plugins/NotifyAppriseAPI.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import re
import requests
from json import dumps
import base64

from .NotifyBase import NotifyBase
from ..URLBase import PrivacyMode
Expand Down Expand Up @@ -209,22 +210,59 @@ def url(self, privacy=False, *args, **kwargs):
token=self.pprint(self.token, privacy, safe=''),
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
"""

headers = {}
# Prepare HTTP Headers
headers = {
'User-Agent': self.app_id,
'Content-Type': 'application/json'
}

# Apply any/all header over-rides defined
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
payload = {
# Apprise API Payload
'title': title,
'body': body,
'type': notify_type,
'format': self.notify_format,
'attachments': attachments,
}

if self.__tags:
Expand Down
67 changes: 67 additions & 0 deletions test/test_plugin_apprise_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,22 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.

import os
from unittest import mock
from apprise.plugins.NotifyAppriseAPI import NotifyAppriseAPI
from helpers import AppriseURLTester
import requests
from apprise import Apprise
from apprise import AppriseAttachment
from apprise import NotifyType

# Disable logging for a cleaner testing output
import logging
logging.disable(logging.CRITICAL)

# Attachment Directory
TEST_VAR_DIR = os.path.join(os.path.dirname(__file__), 'var')

# Our Testing URLs
apprise_url_tests = (
('apprise://', {
Expand Down Expand Up @@ -172,3 +180,62 @@ def test_plugin_apprise_urls():

# Run our general tests
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

0 comments on commit b327abf

Please sign in to comment.