mirror of
https://github.com/caronc/apprise.git
synced 2024-11-25 17:44:00 +01:00
Title & body not required if attachment specified (#916)
This commit is contained in:
parent
236e67f497
commit
3d16cbf3d3
@ -458,7 +458,7 @@ class Apprise:
|
||||
logger.error(msg)
|
||||
raise TypeError(msg)
|
||||
|
||||
if not (title or body):
|
||||
if not (title or body or attach):
|
||||
msg = "No message content specified to deliver"
|
||||
logger.error(msg)
|
||||
raise TypeError(msg)
|
||||
@ -689,6 +689,11 @@ class Apprise:
|
||||
# Placeholder - populated below
|
||||
'details': None,
|
||||
|
||||
# Let upstream service know of the plugins that support
|
||||
# attachments
|
||||
'attachment_support': getattr(
|
||||
plugin, 'attachment_support', False),
|
||||
|
||||
# Differentiat between what is a custom loaded plugin and
|
||||
# which is native.
|
||||
'category': getattr(plugin, 'category', None)
|
||||
|
@ -77,6 +77,9 @@ class NotifyAppriseAPI(NotifyBase):
|
||||
# A URL that takes you to the setup/help of the specific protocol
|
||||
setup_url = 'https://github.com/caronc/apprise/wiki/Notify_apprise_api'
|
||||
|
||||
# Support attachments
|
||||
attachment_support = True
|
||||
|
||||
# Depending on the number of transactions/notifications taking place, this
|
||||
# could take a while. 30 seconds should be enough to perform the task
|
||||
socket_read_timeout = 30.0
|
||||
@ -260,7 +263,7 @@ class NotifyAppriseAPI(NotifyBase):
|
||||
|
||||
attachments = []
|
||||
files = []
|
||||
if attach:
|
||||
if attach and self.attachment_support:
|
||||
for no, attachment in enumerate(attach, start=1):
|
||||
# Perform some simple error checking
|
||||
if not attachment:
|
||||
@ -310,7 +313,10 @@ class NotifyAppriseAPI(NotifyBase):
|
||||
|
||||
if self.method == AppriseAPIMethod.JSON:
|
||||
headers['Content-Type'] = 'application/json'
|
||||
payload['attachments'] = attachments
|
||||
|
||||
if attachments:
|
||||
payload['attachments'] = attachments
|
||||
|
||||
payload = dumps(payload)
|
||||
|
||||
if self.__tags:
|
||||
|
@ -139,6 +139,18 @@ class NotifyBase(URLBase):
|
||||
# Default Overflow Mode
|
||||
overflow_mode = OverflowMode.UPSTREAM
|
||||
|
||||
# Support Attachments; this defaults to being disabled.
|
||||
# Since apprise allows you to send attachments without a body or title
|
||||
# defined, by letting Apprise know the plugin won't support attachments
|
||||
# up front, it can quickly pass over and ignore calls to these end points.
|
||||
|
||||
# You must set this to true if your application can handle attachments.
|
||||
# You must also consider a flow change to your notification if this is set
|
||||
# to True as well as now there will be cases where both the body and title
|
||||
# may not be set. There will never be a case where a body, or attachment
|
||||
# isn't set in the same call to your notify() function.
|
||||
attachment_support = False
|
||||
|
||||
# Default Title HTML Tagging
|
||||
# When a title is specified for a notification service that doesn't accept
|
||||
# titles, by default apprise tries to give a plesant view and convert the
|
||||
@ -316,7 +328,7 @@ class NotifyBase(URLBase):
|
||||
the_cors = (do_send(**kwargs2) for kwargs2 in send_calls)
|
||||
return all(await asyncio.gather(*the_cors))
|
||||
|
||||
def _build_send_calls(self, body, title=None,
|
||||
def _build_send_calls(self, body=None, title=None,
|
||||
notify_type=NotifyType.INFO, overflow=None,
|
||||
attach=None, body_format=None, **kwargs):
|
||||
"""
|
||||
@ -339,6 +351,28 @@ class NotifyBase(URLBase):
|
||||
# bad attachments
|
||||
raise
|
||||
|
||||
# Handle situations where the body is None
|
||||
body = '' if not body else body
|
||||
|
||||
elif not (body or attach):
|
||||
# If there is not an attachment at the very least, a body must be
|
||||
# present
|
||||
msg = "No message body or attachment was specified."
|
||||
self.logger.warning(msg)
|
||||
raise TypeError(msg)
|
||||
|
||||
if not body and not self.attachment_support:
|
||||
# If no body was specified, then we know that an attachment
|
||||
# was. This is logic checked earlier in the code.
|
||||
#
|
||||
# Knowing this, if the plugin itself doesn't support sending
|
||||
# attachments, there is nothing further to do here, just move
|
||||
# along.
|
||||
msg = f"{self.service_name} does not support attachments; " \
|
||||
" service skipped"
|
||||
self.logger.warning(msg)
|
||||
raise TypeError(msg)
|
||||
|
||||
# Handle situations where the title is None
|
||||
title = '' if not title else title
|
||||
|
||||
|
@ -84,6 +84,9 @@ class NotifyDiscord(NotifyBase):
|
||||
# Discord Webhook
|
||||
notify_url = 'https://discord.com/api/webhooks'
|
||||
|
||||
# Support attachments
|
||||
attachment_support = True
|
||||
|
||||
# Allows the user to specify the NotifyImageSize object
|
||||
image_size = NotifyImageSize.XY_256
|
||||
|
||||
@ -255,61 +258,6 @@ class NotifyDiscord(NotifyBase):
|
||||
# Acquire image_url
|
||||
image_url = self.image_url(notify_type)
|
||||
|
||||
# our fields variable
|
||||
fields = []
|
||||
|
||||
if self.notify_format == NotifyFormat.MARKDOWN:
|
||||
# Use embeds for payload
|
||||
payload['embeds'] = [{
|
||||
'author': {
|
||||
'name': self.app_id,
|
||||
'url': self.app_url,
|
||||
},
|
||||
'title': title,
|
||||
'description': body,
|
||||
|
||||
# Our color associated with our notification
|
||||
'color': self.color(notify_type, int),
|
||||
}]
|
||||
|
||||
if self.footer:
|
||||
# Acquire logo URL
|
||||
logo_url = self.image_url(notify_type, logo=True)
|
||||
|
||||
# Set Footer text to our app description
|
||||
payload['embeds'][0]['footer'] = {
|
||||
'text': self.app_desc,
|
||||
}
|
||||
|
||||
if self.footer_logo and logo_url:
|
||||
payload['embeds'][0]['footer']['icon_url'] = logo_url
|
||||
|
||||
if self.include_image and image_url:
|
||||
payload['embeds'][0]['thumbnail'] = {
|
||||
'url': image_url,
|
||||
'height': 256,
|
||||
'width': 256,
|
||||
}
|
||||
|
||||
if self.fields:
|
||||
# Break titles out so that we can sort them in embeds
|
||||
description, fields = self.extract_markdown_sections(body)
|
||||
|
||||
# Swap first entry for description
|
||||
payload['embeds'][0]['description'] = description
|
||||
if fields:
|
||||
# Apply our additional parsing for a better presentation
|
||||
payload['embeds'][0]['fields'] = \
|
||||
fields[:self.discord_max_fields]
|
||||
|
||||
# Remove entry from head of fields
|
||||
fields = fields[self.discord_max_fields:]
|
||||
|
||||
else:
|
||||
# not markdown
|
||||
payload['content'] = \
|
||||
body if not title else "{}\r\n{}".format(title, body)
|
||||
|
||||
if self.avatar and (image_url or self.avatar_url):
|
||||
payload['avatar_url'] = \
|
||||
self.avatar_url if self.avatar_url else image_url
|
||||
@ -318,22 +266,81 @@ class NotifyDiscord(NotifyBase):
|
||||
# Optionally override the default username of the webhook
|
||||
payload['username'] = self.user
|
||||
|
||||
# Associate our thread_id with our message
|
||||
params = {'thread_id': self.thread_id} if self.thread_id else None
|
||||
if not self._send(payload, params=params):
|
||||
# We failed to post our message
|
||||
return False
|
||||
|
||||
# Process any remaining fields IF set
|
||||
if fields:
|
||||
payload['embeds'][0]['description'] = ''
|
||||
for i in range(0, len(fields), self.discord_max_fields):
|
||||
payload['embeds'][0]['fields'] = \
|
||||
fields[i:i + self.discord_max_fields]
|
||||
if not self._send(payload):
|
||||
# We failed to post our message
|
||||
return False
|
||||
if body:
|
||||
# our fields variable
|
||||
fields = []
|
||||
|
||||
if attach:
|
||||
if self.notify_format == NotifyFormat.MARKDOWN:
|
||||
# Use embeds for payload
|
||||
payload['embeds'] = [{
|
||||
'author': {
|
||||
'name': self.app_id,
|
||||
'url': self.app_url,
|
||||
},
|
||||
'title': title,
|
||||
'description': body,
|
||||
|
||||
# Our color associated with our notification
|
||||
'color': self.color(notify_type, int),
|
||||
}]
|
||||
|
||||
if self.footer:
|
||||
# Acquire logo URL
|
||||
logo_url = self.image_url(notify_type, logo=True)
|
||||
|
||||
# Set Footer text to our app description
|
||||
payload['embeds'][0]['footer'] = {
|
||||
'text': self.app_desc,
|
||||
}
|
||||
|
||||
if self.footer_logo and logo_url:
|
||||
payload['embeds'][0]['footer']['icon_url'] = logo_url
|
||||
|
||||
if self.include_image and image_url:
|
||||
payload['embeds'][0]['thumbnail'] = {
|
||||
'url': image_url,
|
||||
'height': 256,
|
||||
'width': 256,
|
||||
}
|
||||
|
||||
if self.fields:
|
||||
# Break titles out so that we can sort them in embeds
|
||||
description, fields = self.extract_markdown_sections(body)
|
||||
|
||||
# Swap first entry for description
|
||||
payload['embeds'][0]['description'] = description
|
||||
if fields:
|
||||
# Apply our additional parsing for a better
|
||||
# presentation
|
||||
payload['embeds'][0]['fields'] = \
|
||||
fields[:self.discord_max_fields]
|
||||
|
||||
# Remove entry from head of fields
|
||||
fields = fields[self.discord_max_fields:]
|
||||
|
||||
else:
|
||||
# not markdown
|
||||
payload['content'] = \
|
||||
body if not title else "{}\r\n{}".format(title, body)
|
||||
|
||||
if not self._send(payload, params=params):
|
||||
# We failed to post our message
|
||||
return False
|
||||
|
||||
# Process any remaining fields IF set
|
||||
if fields:
|
||||
payload['embeds'][0]['description'] = ''
|
||||
for i in range(0, len(fields), self.discord_max_fields):
|
||||
payload['embeds'][0]['fields'] = \
|
||||
fields[i:i + self.discord_max_fields]
|
||||
if not self._send(payload):
|
||||
# We failed to post our message
|
||||
return False
|
||||
|
||||
if attach and self.attachment_support:
|
||||
# Update our payload; the idea is to preserve it's other detected
|
||||
# and assigned values for re-use here too
|
||||
payload.update({
|
||||
@ -356,7 +363,7 @@ class NotifyDiscord(NotifyBase):
|
||||
for attachment in attach:
|
||||
self.logger.info(
|
||||
'Posting Discord Attachment {}'.format(attachment.name))
|
||||
if not self._send(payload, attach=attachment):
|
||||
if not self._send(payload, params=params, attach=attachment):
|
||||
# We failed to post our message
|
||||
return False
|
||||
|
||||
|
@ -341,6 +341,9 @@ class NotifyEmail(NotifyBase):
|
||||
# A URL that takes you to the setup/help of the specific protocol
|
||||
setup_url = 'https://github.com/caronc/apprise/wiki/Notify_email'
|
||||
|
||||
# Support attachments
|
||||
attachment_support = True
|
||||
|
||||
# Default Notify Format
|
||||
notify_format = NotifyFormat.HTML
|
||||
|
||||
@ -770,7 +773,7 @@ class NotifyEmail(NotifyBase):
|
||||
else:
|
||||
base = MIMEText(body, 'plain', 'utf-8')
|
||||
|
||||
if attach:
|
||||
if attach and self.attachment_support:
|
||||
mixed = MIMEMultipart("mixed")
|
||||
mixed.attach(base)
|
||||
# Now store our attachments
|
||||
|
@ -99,6 +99,9 @@ class NotifyForm(NotifyBase):
|
||||
# A URL that takes you to the setup/help of the specific protocol
|
||||
setup_url = 'https://github.com/caronc/apprise/wiki/Notify_Custom_Form'
|
||||
|
||||
# Support attachments
|
||||
attachment_support = True
|
||||
|
||||
# Allows the user to specify the NotifyImageSize object
|
||||
image_size = NotifyImageSize.XY_128
|
||||
|
||||
@ -345,7 +348,7 @@ class NotifyForm(NotifyBase):
|
||||
|
||||
# Track our potential attachments
|
||||
files = []
|
||||
if attach:
|
||||
if attach and self.attachment_support:
|
||||
for no, attachment in enumerate(attach, start=1):
|
||||
# Perform some simple error checking
|
||||
if not attachment:
|
||||
|
@ -80,6 +80,9 @@ class NotifyJSON(NotifyBase):
|
||||
# A URL that takes you to the setup/help of the specific protocol
|
||||
setup_url = 'https://github.com/caronc/apprise/wiki/Notify_Custom_JSON'
|
||||
|
||||
# Support attachments
|
||||
attachment_support = True
|
||||
|
||||
# Allows the user to specify the NotifyImageSize object
|
||||
image_size = NotifyImageSize.XY_128
|
||||
|
||||
@ -289,7 +292,7 @@ class NotifyJSON(NotifyBase):
|
||||
|
||||
# Track our potential attachments
|
||||
attachments = []
|
||||
if attach:
|
||||
if attach and self.attachment_support:
|
||||
for attachment in attach:
|
||||
# Perform some simple error checking
|
||||
if not attachment:
|
||||
|
@ -121,6 +121,9 @@ class NotifyMailgun(NotifyBase):
|
||||
# A URL that takes you to the setup/help of the specific protocol
|
||||
setup_url = 'https://github.com/caronc/apprise/wiki/Notify_mailgun'
|
||||
|
||||
# Support attachments
|
||||
attachment_support = True
|
||||
|
||||
# Default Notify Format
|
||||
notify_format = NotifyFormat.HTML
|
||||
|
||||
@ -371,7 +374,7 @@ class NotifyMailgun(NotifyBase):
|
||||
# Track our potential files
|
||||
files = {}
|
||||
|
||||
if attach:
|
||||
if attach and self.attachment_support:
|
||||
for idx, attachment in enumerate(attach):
|
||||
# Perform some simple error checking
|
||||
if not attachment:
|
||||
|
@ -111,6 +111,10 @@ class NotifyMastodon(NotifyBase):
|
||||
# A URL that takes you to the setup/help of the specific protocol
|
||||
setup_url = 'https://github.com/caronc/apprise/wiki/Notify_mastodon'
|
||||
|
||||
# Support attachments
|
||||
attachment_support = True
|
||||
|
||||
# Allows the user to specify the NotifyImageSize object
|
||||
# Allows the user to specify the NotifyImageSize object; this is supported
|
||||
# through the webhook
|
||||
image_size = NotifyImageSize.XY_128
|
||||
@ -414,11 +418,10 @@ class NotifyMastodon(NotifyBase):
|
||||
else:
|
||||
targets.add(myself)
|
||||
|
||||
if attach:
|
||||
if attach and self.attachment_support:
|
||||
# We need to upload our payload first so that we can source it
|
||||
# in remaining messages
|
||||
for attachment in attach:
|
||||
|
||||
# Perform some simple error checking
|
||||
if not attachment:
|
||||
# We could not access the attachment
|
||||
@ -578,7 +581,7 @@ class NotifyMastodon(NotifyBase):
|
||||
_payload = deepcopy(payload)
|
||||
_payload['media_ids'] = media_ids
|
||||
|
||||
if no:
|
||||
if no or not body:
|
||||
# strip text and replace it with the image representation
|
||||
_payload['status'] = \
|
||||
'{:02d}/{:02d}'.format(no + 1, len(batches))
|
||||
|
@ -172,6 +172,9 @@ class NotifyNtfy(NotifyBase):
|
||||
# Default upstream/cloud host if none is defined
|
||||
cloud_notify_url = 'https://ntfy.sh'
|
||||
|
||||
# Support attachments
|
||||
attachment_support = True
|
||||
|
||||
# Allows the user to specify the NotifyImageSize object
|
||||
image_size = NotifyImageSize.XY_256
|
||||
|
||||
@ -405,14 +408,14 @@ class NotifyNtfy(NotifyBase):
|
||||
# Retrieve our topic
|
||||
topic = topics.pop()
|
||||
|
||||
if attach:
|
||||
if attach and self.attachment_support:
|
||||
# We need to upload our payload first so that we can source it
|
||||
# in remaining messages
|
||||
for no, attachment in enumerate(attach):
|
||||
|
||||
# First message only includes the text
|
||||
_body = body if not no else None
|
||||
_title = title if not no else None
|
||||
# First message only includes the text (if defined)
|
||||
_body = body if not no and body else None
|
||||
_title = title if not no and title else None
|
||||
|
||||
# Perform some simple error checking
|
||||
if not attachment:
|
||||
@ -543,11 +546,8 @@ class NotifyNtfy(NotifyBase):
|
||||
# Default response type
|
||||
response = None
|
||||
|
||||
if data:
|
||||
data = data if attach else dumps(data)
|
||||
|
||||
else: # not data:
|
||||
data = None
|
||||
if not attach:
|
||||
data = dumps(data)
|
||||
|
||||
try:
|
||||
r = requests.post(
|
||||
|
@ -75,6 +75,9 @@ class NotifyPushBullet(NotifyBase):
|
||||
# PushBullet uses the http protocol with JSON requests
|
||||
notify_url = 'https://api.pushbullet.com/v2/{}'
|
||||
|
||||
# Support attachments
|
||||
attachment_support = True
|
||||
|
||||
# Define object templates
|
||||
templates = (
|
||||
'{schema}://{accesstoken}',
|
||||
@ -150,7 +153,7 @@ class NotifyPushBullet(NotifyBase):
|
||||
# Build a list of our attachments
|
||||
attachments = []
|
||||
|
||||
if attach:
|
||||
if attach and self.attachment_support:
|
||||
# We need to upload our payload first so that we can source it
|
||||
# in remaining messages
|
||||
for attachment in attach:
|
||||
@ -261,14 +264,15 @@ class NotifyPushBullet(NotifyBase):
|
||||
"PushBullet recipient {} parsed as a device"
|
||||
.format(recipient))
|
||||
|
||||
okay, response = self._send(
|
||||
self.notify_url.format('pushes'), payload)
|
||||
if not okay:
|
||||
has_error = True
|
||||
continue
|
||||
if body:
|
||||
okay, response = self._send(
|
||||
self.notify_url.format('pushes'), payload)
|
||||
if not okay:
|
||||
has_error = True
|
||||
continue
|
||||
|
||||
self.logger.info(
|
||||
'Sent PushBullet notification to "%s".' % (recipient))
|
||||
self.logger.info(
|
||||
'Sent PushBullet notification to "%s".' % (recipient))
|
||||
|
||||
for attach_payload in attachments:
|
||||
# Send our attachments to our same user (already prepared as
|
||||
|
@ -336,6 +336,9 @@ class NotifyPushSafer(NotifyBase):
|
||||
# The default secure protocol
|
||||
secure_protocol = 'psafers'
|
||||
|
||||
# Support attachments
|
||||
attachment_support = True
|
||||
|
||||
# Number of requests to a allow per second
|
||||
request_rate_per_sec = 1.2
|
||||
|
||||
@ -546,7 +549,7 @@ class NotifyPushSafer(NotifyBase):
|
||||
# Initialize our list of attachments
|
||||
attachments = []
|
||||
|
||||
if attach:
|
||||
if attach and self.attachment_support:
|
||||
# We need to upload our payload first so that we can source it
|
||||
# in remaining messages
|
||||
for attachment in attach:
|
||||
|
@ -164,6 +164,9 @@ class NotifyPushover(NotifyBase):
|
||||
# Pushover uses the http protocol with JSON requests
|
||||
notify_url = 'https://api.pushover.net/1/messages.json'
|
||||
|
||||
# Support attachments
|
||||
attachment_support = True
|
||||
|
||||
# The maximum allowable characters allowed in the body per message
|
||||
body_maxlen = 1024
|
||||
|
||||
@ -381,22 +384,25 @@ class NotifyPushover(NotifyBase):
|
||||
if self.priority == PushoverPriority.EMERGENCY:
|
||||
payload.update({'retry': self.retry, 'expire': self.expire})
|
||||
|
||||
if attach:
|
||||
if attach and self.attachment_support:
|
||||
# Create a copy of our payload
|
||||
_payload = payload.copy()
|
||||
|
||||
# Send with attachments
|
||||
for attachment in attach:
|
||||
# Simple send
|
||||
for no, attachment in enumerate(attach):
|
||||
if no or not body:
|
||||
# To handle multiple attachments, clean up our message
|
||||
_payload['message'] = attachment.name
|
||||
|
||||
if not self._send(_payload, attachment):
|
||||
# Mark our failure
|
||||
has_error = True
|
||||
# clean exit from our attachment loop
|
||||
break
|
||||
|
||||
# To handle multiple attachments, clean up our message
|
||||
_payload['title'] = '...'
|
||||
_payload['message'] = attachment.name
|
||||
# Clear our title if previously set
|
||||
_payload['title'] = ''
|
||||
|
||||
# No need to alarm for each consecutive attachment uploaded
|
||||
# afterwards
|
||||
_payload['sound'] = PushoverSound.NONE
|
||||
|
@ -136,6 +136,9 @@ class NotifySES(NotifyBase):
|
||||
# A URL that takes you to the setup/help of the specific protocol
|
||||
setup_url = 'https://github.com/caronc/apprise/wiki/Notify_ses'
|
||||
|
||||
# Support attachments
|
||||
attachment_support = True
|
||||
|
||||
# AWS is pretty good for handling data load so request limits
|
||||
# can occur in much shorter bursts
|
||||
request_rate_per_sec = 2.5
|
||||
@ -427,7 +430,8 @@ class NotifySES(NotifyBase):
|
||||
content = MIMEText(body, 'plain', 'utf-8')
|
||||
|
||||
# Create a Multipart container if there is an attachment
|
||||
base = MIMEMultipart() if attach else content
|
||||
base = MIMEMultipart() \
|
||||
if attach and self.attachment_support else content
|
||||
|
||||
# TODO: Deduplicate with `NotifyEmail`?
|
||||
base['Subject'] = Header(title, 'utf-8')
|
||||
@ -443,7 +447,7 @@ class NotifySES(NotifyBase):
|
||||
timezone.utc).strftime("%a, %d %b %Y %H:%M:%S +0000")
|
||||
base['X-Application'] = self.app_id
|
||||
|
||||
if attach:
|
||||
if attach and self.attachment_support:
|
||||
# First attach our body to our content as the first element
|
||||
base.attach(content)
|
||||
|
||||
|
@ -112,6 +112,9 @@ class NotifySMSEagle(NotifyBase):
|
||||
# The path we send our notification to
|
||||
notify_path = '/jsonrpc/sms'
|
||||
|
||||
# Support attachments
|
||||
attachment_support = True
|
||||
|
||||
# The maxumum length of the text message
|
||||
# The actual limit is 160 but SMSEagle looks after the handling
|
||||
# of large messages in it's upstream service
|
||||
@ -340,7 +343,7 @@ class NotifySMSEagle(NotifyBase):
|
||||
has_error = False
|
||||
|
||||
attachments = []
|
||||
if attach:
|
||||
if attach and self.attachment_support:
|
||||
for attachment in attach:
|
||||
# Perform some simple error checking
|
||||
if not attachment:
|
||||
|
@ -91,6 +91,9 @@ class NotifySMTP2Go(NotifyBase):
|
||||
# Notify URL
|
||||
notify_url = 'https://api.smtp2go.com/v3/email/send'
|
||||
|
||||
# Support attachments
|
||||
attachment_support = True
|
||||
|
||||
# Default Notify Format
|
||||
notify_format = NotifyFormat.HTML
|
||||
|
||||
@ -294,8 +297,8 @@ class NotifySMTP2Go(NotifyBase):
|
||||
# Track our potential attachments
|
||||
attachments = []
|
||||
|
||||
if attach:
|
||||
for idx, attachment in enumerate(attach):
|
||||
if attach and self.attachment_support:
|
||||
for attachment in attach:
|
||||
# Perform some simple error checking
|
||||
if not attachment:
|
||||
# We could not access the attachment
|
||||
|
@ -68,6 +68,9 @@ class NotifySignalAPI(NotifyBase):
|
||||
# A URL that takes you to the setup/help of the specific protocol
|
||||
setup_url = 'https://github.com/caronc/apprise/wiki/Notify_signal'
|
||||
|
||||
# Support attachments
|
||||
attachment_support = True
|
||||
|
||||
# The maximum targets to include when doing batch transfers
|
||||
default_batch_size = 10
|
||||
|
||||
@ -234,7 +237,7 @@ class NotifySignalAPI(NotifyBase):
|
||||
has_error = False
|
||||
|
||||
attachments = []
|
||||
if attach:
|
||||
if attach and self.attachment_support:
|
||||
for attachment in attach:
|
||||
# Perform some simple error checking
|
||||
if not attachment:
|
||||
@ -281,7 +284,7 @@ class NotifySignalAPI(NotifyBase):
|
||||
payload = {
|
||||
'message': "{}{}".format(
|
||||
'' if not self.status else '{} '.format(
|
||||
self.asset.ascii(notify_type)), body),
|
||||
self.asset.ascii(notify_type)), body).rstrip(),
|
||||
"number": self.source,
|
||||
"recipients": []
|
||||
}
|
||||
|
@ -143,6 +143,10 @@ class NotifySlack(NotifyBase):
|
||||
# A URL that takes you to the setup/help of the specific protocol
|
||||
setup_url = 'https://github.com/caronc/apprise/wiki/Notify_slack'
|
||||
|
||||
# Support attachments
|
||||
attachment_support = True
|
||||
|
||||
# The maximum targets to include when doing batch transfers
|
||||
# Slack Webhook URL
|
||||
webhook_url = 'https://hooks.slack.com/services'
|
||||
|
||||
@ -522,7 +526,8 @@ class NotifySlack(NotifyBase):
|
||||
# Include the footer only if specified to do so
|
||||
payload['attachments'][0]['footer'] = self.app_id
|
||||
|
||||
if attach and self.mode is SlackMode.WEBHOOK:
|
||||
if attach and self.attachment_support \
|
||||
and self.mode is SlackMode.WEBHOOK:
|
||||
# Be friendly; let the user know why they can't send their
|
||||
# attachments if using the Webhook mode
|
||||
self.logger.warning(
|
||||
@ -600,7 +605,8 @@ class NotifySlack(NotifyBase):
|
||||
' to {}'.format(channel)
|
||||
if channel is not None else ''))
|
||||
|
||||
if attach and self.mode is SlackMode.BOT and attach_channel_list:
|
||||
if attach and self.attachment_support and \
|
||||
self.mode is SlackMode.BOT and attach_channel_list:
|
||||
# Send our attachments (can only be done in bot mode)
|
||||
for attachment in attach:
|
||||
|
||||
|
@ -118,6 +118,9 @@ class NotifySparkPost(NotifyBase):
|
||||
# The services URL
|
||||
service_url = 'https://sparkpost.com/'
|
||||
|
||||
# Support attachments
|
||||
attachment_support = True
|
||||
|
||||
# All notification requests are secure
|
||||
secure_protocol = 'sparkpost'
|
||||
|
||||
@ -543,7 +546,7 @@ class NotifySparkPost(NotifyBase):
|
||||
else:
|
||||
payload['content']['text'] = body
|
||||
|
||||
if attach:
|
||||
if attach and self.attachment_support:
|
||||
# Prepare ourselves an attachment object
|
||||
payload['content']['attachments'] = []
|
||||
|
||||
|
@ -277,8 +277,7 @@ class NotifyStreamlabs(NotifyBase):
|
||||
|
||||
return
|
||||
|
||||
def send(self, body, title='', notify_type=NotifyType.INFO, attach=None,
|
||||
**kwargs):
|
||||
def send(self, body, title='', notify_type=NotifyType.INFO, **kwargs):
|
||||
"""
|
||||
Perform Streamlabs notification call (either donation or alert)
|
||||
"""
|
||||
|
@ -123,6 +123,9 @@ class NotifyTelegram(NotifyBase):
|
||||
# Telegram uses the http protocol with JSON requests
|
||||
notify_url = 'https://api.telegram.org/bot'
|
||||
|
||||
# Support attachments
|
||||
attachment_support = True
|
||||
|
||||
# Allows the user to specify the NotifyImageSize object
|
||||
image_size = NotifyImageSize.XY_256
|
||||
|
||||
@ -715,6 +718,10 @@ class NotifyTelegram(NotifyBase):
|
||||
# Prepare our payload based on HTML or TEXT
|
||||
payload['text'] = body
|
||||
|
||||
# Handle payloads without a body specified (but an attachment present)
|
||||
attach_content = \
|
||||
TelegramContentPlacement.AFTER if not body else self.content
|
||||
|
||||
# Create a copy of the chat_ids list
|
||||
targets = list(self.targets)
|
||||
while len(targets):
|
||||
@ -748,7 +755,8 @@ class NotifyTelegram(NotifyBase):
|
||||
'Failed to send Telegram type image to {}.',
|
||||
payload['chat_id'])
|
||||
|
||||
if attach and self.content == TelegramContentPlacement.AFTER:
|
||||
if attach and self.attachment_support and \
|
||||
attach_content == TelegramContentPlacement.AFTER:
|
||||
# Send our attachments now (if specified and if it exists)
|
||||
if not self._send_attachments(
|
||||
chat_id=payload['chat_id'], notify_type=notify_type,
|
||||
@ -757,6 +765,10 @@ class NotifyTelegram(NotifyBase):
|
||||
has_error = True
|
||||
continue
|
||||
|
||||
if not body:
|
||||
# Nothing more to do; move along to the next attachment
|
||||
continue
|
||||
|
||||
# Always call throttle before any remote server i/o is made;
|
||||
# Telegram throttles to occur before sending the image so that
|
||||
# content can arrive together.
|
||||
@ -819,7 +831,8 @@ class NotifyTelegram(NotifyBase):
|
||||
|
||||
self.logger.info('Sent Telegram notification.')
|
||||
|
||||
if attach and self.content == TelegramContentPlacement.BEFORE:
|
||||
if attach and self.attachment_support \
|
||||
and attach_content == TelegramContentPlacement.BEFORE:
|
||||
# Send our attachments now (if specified and if it exists) as
|
||||
# it was identified to send the content before the attachments
|
||||
# which is now done.
|
||||
|
@ -88,6 +88,9 @@ class NotifyTwitter(NotifyBase):
|
||||
# A URL that takes you to the setup/help of the specific protocol
|
||||
setup_url = 'https://github.com/caronc/apprise/wiki/Notify_twitter'
|
||||
|
||||
# Support attachments
|
||||
attachment_support = True
|
||||
|
||||
# Do not set body_maxlen as it is set in a property value below
|
||||
# since the length varies depending if we are doing a direct message
|
||||
# or a tweet
|
||||
@ -285,7 +288,7 @@ class NotifyTwitter(NotifyBase):
|
||||
# Build a list of our attachments
|
||||
attachments = []
|
||||
|
||||
if attach:
|
||||
if attach and self.attachment_support:
|
||||
# We need to upload our payload first so that we can source it
|
||||
# in remaining messages
|
||||
for attachment in attach:
|
||||
@ -414,7 +417,7 @@ class NotifyTwitter(NotifyBase):
|
||||
_payload = deepcopy(payload)
|
||||
_payload['media_ids'] = media_ids
|
||||
|
||||
if no:
|
||||
if no or not body:
|
||||
# strip text and replace it with the image representation
|
||||
_payload['status'] = \
|
||||
'{:02d}/{:02d}'.format(no + 1, len(batches))
|
||||
@ -514,7 +517,7 @@ class NotifyTwitter(NotifyBase):
|
||||
'additional_owners':
|
||||
','.join([str(x) for x in targets.values()])
|
||||
}
|
||||
if no:
|
||||
if no or not body:
|
||||
# strip text and replace it with the image representation
|
||||
_data['text'] = \
|
||||
'{:02d}/{:02d}'.format(no + 1, len(attachments))
|
||||
|
@ -79,6 +79,9 @@ class NotifyXML(NotifyBase):
|
||||
# A URL that takes you to the setup/help of the specific protocol
|
||||
setup_url = 'https://github.com/caronc/apprise/wiki/Notify_Custom_XML'
|
||||
|
||||
# Support attachments
|
||||
attachment_support = True
|
||||
|
||||
# Allows the user to specify the NotifyImageSize object
|
||||
image_size = NotifyImageSize.XY_128
|
||||
|
||||
@ -340,7 +343,7 @@ class NotifyXML(NotifyBase):
|
||||
['<{}>{}</{}>'.format(k, v, k) for k, v in payload_base.items()])
|
||||
|
||||
attachments = []
|
||||
if attach:
|
||||
if attach and self.attachment_support:
|
||||
for attachment in attach:
|
||||
# Perform some simple error checking
|
||||
if not attachment:
|
||||
|
@ -14,6 +14,13 @@ services:
|
||||
volumes:
|
||||
- ./:/apprise
|
||||
|
||||
test.py311:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile.py311
|
||||
volumes:
|
||||
- ./:/apprise
|
||||
|
||||
rpmbuild.el8:
|
||||
build:
|
||||
context: .
|
||||
@ -36,6 +43,17 @@ services:
|
||||
- ./:/apprise
|
||||
|
||||
|
||||
#
|
||||
# Every Day testing
|
||||
#
|
||||
# Connect to web and create a new project using the manage script
|
||||
# -> docker-compose run --rm test.py311 bash
|
||||
# bin/apprise -
|
||||
# bin/checkdone.sh
|
||||
|
||||
#
|
||||
# Other Testing
|
||||
#
|
||||
# Connect to web and create a new project using the manage script
|
||||
# -> docker-compose run --rm test.py36 bash
|
||||
# bin/apprise -
|
||||
|
@ -477,6 +477,52 @@ class AppriseURLTester:
|
||||
notify_type=notify_type,
|
||||
attach=attach) == attach_response
|
||||
|
||||
if obj.attachment_support:
|
||||
#
|
||||
# Services that support attachments should support
|
||||
# sending a attachment (or more) without a body or
|
||||
# title specified:
|
||||
#
|
||||
assert obj.notify(
|
||||
body=None, title=None,
|
||||
notify_type=notify_type,
|
||||
attach=attach) == attach_response
|
||||
|
||||
# Turn off attachment support on the notifications
|
||||
# that support it so we can test that any logic we
|
||||
# have ot test against this flag is ran
|
||||
obj.attachment_support = False
|
||||
|
||||
#
|
||||
# Notifications should still transmit as normal if
|
||||
# Attachment support is flipped off
|
||||
#
|
||||
assert obj.notify(
|
||||
body=self.body, title=self.title,
|
||||
notify_type=notify_type,
|
||||
attach=attach) == notify_response
|
||||
|
||||
#
|
||||
# We should not be able to send a message without a
|
||||
# body or title in this circumstance
|
||||
#
|
||||
assert obj.notify(
|
||||
body=None, title=None,
|
||||
notify_type=notify_type,
|
||||
attach=attach) is False
|
||||
|
||||
# Toggle Back
|
||||
obj.attachment_support = True
|
||||
|
||||
else: # No Attachment support
|
||||
#
|
||||
# We should not be able to send a message without a
|
||||
# body or title in this circumstance
|
||||
#
|
||||
assert obj.notify(
|
||||
body=None, title=None,
|
||||
notify_type=notify_type,
|
||||
attach=attach) is False
|
||||
else:
|
||||
|
||||
for _exception in self.req_exceptions:
|
||||
|
@ -257,9 +257,11 @@ def apprise_test(do_notify):
|
||||
assert do_notify(a, title=object(), body=b'bytes') is False
|
||||
assert do_notify(a, title=b"bytes", body=object()) is False
|
||||
|
||||
# As long as one is present, we're good
|
||||
# A Body must be present
|
||||
assert do_notify(a, title='present', body=None) is False
|
||||
|
||||
# Other combinations work fine
|
||||
assert do_notify(a, title=None, body='present') is True
|
||||
assert do_notify(a, title='present', body=None) is True
|
||||
assert do_notify(a, title="present", body="present") is True
|
||||
|
||||
# Send Attachment with success
|
||||
|
@ -170,7 +170,13 @@ def test_plugin_boxcar_edge_cases(mock_post, mock_get):
|
||||
# Test notifications without a body or a title
|
||||
p = NotifyBoxcar(access=access, secret=secret, targets=None)
|
||||
|
||||
assert p.notify(body=None, title=None, notify_type=NotifyType.INFO) is True
|
||||
# Neither a title or body was specified
|
||||
assert p.notify(
|
||||
body=None, title=None, notify_type=NotifyType.INFO) is False
|
||||
|
||||
# Acceptable when data is provided:
|
||||
assert p.notify(
|
||||
body="Test", title=None, notify_type=NotifyType.INFO) is True
|
||||
|
||||
# Test comma, separate values
|
||||
device = 'a' * 64
|
||||
|
Loading…
Reference in New Issue
Block a user