Support "attach" keyword (alias of "attachment") (#173)

This commit is contained in:
Chris Caron 2024-02-17 21:39:11 -05:00 committed by GitHub
parent b4805f01e1
commit 479a3bd6ed
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 150 additions and 8 deletions

View File

@ -168,6 +168,13 @@ curl -X POST -d 'urls=mailto://user:pass@gmail.com' \
curl -X POST -d '{"urls": "mailto://user:pass@gmail.com", "body":"test message"}' \
-H "Content-Type: application/json" \
http://localhost:8000/notify
# attach= is an alias of attachment=
# Send a notification with a URL based attachment
curl -X POST \
-F 'urls=mailto://user:pass@gmail.com' \
-F attach=attach=https://raw.githubusercontent.com/caronc/apprise/master/apprise/assets/themes/default/apprise-logo.png \
http://localhost:8000/notify
```
You can also send notifications that are URLs. Apprise will download the item so that it can send it along to all end points that should be notified about it.
@ -252,6 +259,13 @@ curl -X POST \
-F attach1=@Screenshot-1.png \
-F attach2=@/my/path/to/Apprise.doc \
http://localhost:8000/notify/abc123
# attach= is an alias of attachment=
# Send a notification with a URL based attachment
curl -X POST \
-F 'urls=mailto://user:pass@gmail.com' \
-F attach=attach=https://raw.githubusercontent.com/caronc/apprise/master/apprise/assets/themes/default/apprise-logo.png \
http://localhost:8000/notify/abc123
```
🏷️ You can also leverage *tagging* which allows you to associate one or more tags with your Apprise URLs. By doing this, notifications only need to be referred to by their easy to remember notify tag name such as `devops`, `admin`, `family`, etc. You can very easily group more than one notification service under the same *tag* allowing you to notify a group of services at once. This is accomplished through configuration files ([documented here](https://github.com/caronc/apprise/wiki/config)) that can be saved to the persistent storage previously associated with a `{KEY}`.

View File

@ -115,6 +115,12 @@
&nbsp;&nbsp;&nbsp;&nbsp;-F attach1=@Screenshot-1.png \<br/>
&nbsp;&nbsp;&nbsp;&nbsp;-F attach2=@/my/path/to/Apprise.doc \<br/>
&nbsp;&nbsp;&nbsp;&nbsp;http{% if request.is_secure %}s{% endif %}://{{request.META.HTTP_HOST}}{{BASE_URL}}/notify/<em>{{key}}</em></code></pre>
{% blocktrans %}Sends a notification to our endpoints with an attachment{% endblocktrans %}
<pre><code class="bash">
curl -X POST \<br/>
&nbsp;&nbsp;&nbsp;&nbsp;-F "tag=all" \ <br/>
&nbsp;&nbsp;&nbsp;&nbsp;-F "attach=https://raw.githubusercontent.com/caronc/apprise/master/apprise/assets/themes/default/apprise-logo.png" \ <br/>
&nbsp;&nbsp;&nbsp;&nbsp;"{{request.scheme}}://{{request.META.HTTP_HOST}}{{BASE_URL}}/notify/<em>{{key}}</em>"</code></pre>
</p>
</div>
<div class="section">

View File

@ -99,6 +99,11 @@
&nbsp;&nbsp;&nbsp;&nbsp;-F attach2=@Screenshot-2.png \ <br/>
&nbsp;&nbsp;&nbsp;&nbsp;"{{request.scheme}}://{{request.META.HTTP_HOST}}{{BASE_URL}}/notify/"
</code></pre>
<pre><code class="bash">
# {% blocktrans %}Send an web based file attachment to a <a href="https://github.com/caronc/apprise/wiki/Notify_discord" target="_blank">Discord</a> server:{% endblocktrans %}<br/>
curl -X POST -F 'urls=discord://credentials' \<br/>
&nbsp;&nbsp;&nbsp;&nbsp;-F "attach=https://raw.githubusercontent.com/caronc/apprise/master/apprise/assets/themes/default/apprise-logo.png" \ <br/>
&nbsp;&nbsp;&nbsp;&nbsp;"{{request.scheme}}://{{request.META.HTTP_HOST}}{{BASE_URL}}/notify/"</code></pre>
</div>
</li>
<li>
@ -492,6 +497,12 @@
&nbsp;&nbsp;&nbsp;&nbsp;-F "tag=all" \ <br/>
&nbsp;&nbsp;&nbsp;&nbsp;-F "body=test body" \ <br/>
&nbsp;&nbsp;&nbsp;&nbsp;-F "title=test title" \ <br/>
&nbsp;&nbsp;&nbsp;&nbsp;"{{request.scheme}}://{{request.META.HTTP_HOST}}{{BASE_URL}}/notify/<em>{{key}}</em>"</code></pre>
<pre><code class="bash">
# {% blocktrans %}Sends a notification to our endpoints with an attachment{% endblocktrans %}<br/>
curl -X POST \<br/>
&nbsp;&nbsp;&nbsp;&nbsp;-F "tag=all" \ <br/>
&nbsp;&nbsp;&nbsp;&nbsp;-F "attach=https://raw.githubusercontent.com/caronc/apprise/master/apprise/assets/themes/default/apprise-logo.png" \ <br/>
&nbsp;&nbsp;&nbsp;&nbsp;"{{request.scheme}}://{{request.META.HTTP_HOST}}{{BASE_URL}}/notify/<em>{{key}}</em>"</code></pre>
<pre><code class="bash">
# {% blocktrans %}Notifies all URLs assigned the <em>devops</em> tag{% endblocktrans %}<br/>

View File

@ -847,6 +847,22 @@ class NotifyTests(SimpleTestCase):
assert response.status_code == 400
assert mock_notify.call_count == 0
# Reset our mock object
mock_notify.reset_mock()
# Preare our form data
form_data = {
'body': 'test notifiction',
'attach': 'https://localhost/invalid/path/to/image.png',
}
# Send our notification
response = self.client.post(
'/notify/{}'.format(key), form_data)
# We fail because we couldn't retrieve our attachment
assert response.status_code == 400
assert mock_notify.call_count == 0
@mock.patch('apprise.Apprise.notify')
def test_notify_by_loaded_urls_with_json(self, mock_notify):
"""
@ -998,11 +1014,38 @@ class NotifyTests(SimpleTestCase):
assert response.status_code == 200
assert mock_notify.call_count == 1
# Reset our mock object
mock_notify.reset_mock()
# If an empty format is specified, it is accepted and
# no imput format is specified
json_data = {
'body': 'test message',
'format': None,
'attach': 'https://localhost/invalid/path/to/image.png',
}
# Test case with format changed
response = self.client.post(
'/notify/{}'.format(key),
data=json.dumps(json_data),
content_type='application/json',
)
# We failed to send notification because we couldn't fetch the
# attachment
assert response.status_code == 400
assert mock_notify.call_count == 0
# Reset our mock object
mock_notify.reset_mock()
json_data = {
'body': 'test message',
}
# Same results for any empty string:
json_data['format'] = ''
response = self.client.post(
'/notify/{}'.format(key),
data=json.dumps(json_data),

View File

@ -250,6 +250,49 @@ class StatelessNotifyTests(SimpleTestCase):
assert response.status_code == 400
assert mock_notify.call_count == 0
# Reset our mock object
mock_notify.reset_mock()
# Preare our form data (support attach keyword)
form_data = {
'body': 'test notifiction',
'urls': ', '.join([
'mailto://user:pass@hotmail.com',
'mailto://user:pass@gmail.com',
]),
'attach': 'https://localhost/invalid/path/to/image.png',
}
# Send our notification
response = self.client.post('/notify', form_data)
# We fail because we couldn't retrieve our attachment
assert response.status_code == 400
assert mock_notify.call_count == 0
# Reset our mock object
mock_notify.reset_mock()
# Preare our json data (and support attach keyword as alias)
json_data = {
'body': 'test notifiction',
'urls': ', '.join([
'mailto://user:pass@hotmail.com',
'mailto://user:pass@gmail.com',
]),
'attach': 'https://localhost/invalid/path/to/image.png',
}
# Same results
response = self.client.post(
'/notify/',
data=json.dumps(json_data),
content_type='application/json',
)
# We fail because we couldn't retrieve our attachment
assert response.status_code == 400
assert mock_notify.call_count == 0
@override_settings(APPRISE_RECURSION_MAX=1)
@mock.patch('apprise.Apprise.notify')
def test_stateless_notify_recursion(self, mock_notify):

View File

@ -610,10 +610,20 @@ class NotifyView(View):
# Handle Attachments
attach = None
if not content.get('attachment') and 'attachment' in request.POST:
if not content.get('attachment'):
if 'attachment' in request.POST:
# Acquire attachments to work with them
content['attachment'] = request.POST.getlist('attachment')
elif 'attach' in request.POST:
# Acquire kw (alias) attach to work with them
content['attachment'] = request.POST.getlist('attach')
elif content.get('attach'):
# Acquire kw (alias) attach from payload to work with
content['attachment'] = content['attach']
del content['attach']
if 'attachment' in content or request.FILES:
try:
attach = parse_attachments(
@ -1148,16 +1158,31 @@ class StatelessNotifyView(View):
# Handle Attachments
attach = None
if not content.get('attachment') and 'attachment' in request.POST:
if not content.get('attachment'):
if 'attachment' in request.POST:
# Acquire attachments to work with them
content['attachment'] = request.POST.getlist('attachment')
elif 'attach' in request.POST:
# Acquire kw (alias) attach to work with them
content['attachment'] = request.POST.getlist('attach')
elif content.get('attach'):
# Acquire kw (alias) attach from payload to work with
content['attachment'] = content['attach']
del content['attach']
if 'attachment' in content or request.FILES:
try:
attach = parse_attachments(
content.get('attachment'), request.FILES)
except (TypeError, ValueError):
# Invalid entry found in list
logger.warning(
'NOTIFY - %s - Bad attachment specified',
request.META['REMOTE_ADDR'])
return HttpResponse(
_('Bad attachment'),
status=ResponseCode.bad_request)