Attachment Support Added (#118)

This commit is contained in:
Chris Caron
2023-05-15 17:10:15 -04:00
committed by GitHub
parent a5f8767094
commit 86c9f16d69
14 changed files with 504 additions and 74 deletions

View File

@ -138,6 +138,12 @@ class NotifyForm(forms.Form):
max_length=apprise.NotifyBase.body_maxlen,
)
# Attachment Support
attachment = forms.FileField(
label=_('Attachment'),
required=False,
)
tag = forms.CharField(
label=_('Tags'),
widget=forms.TextInput(

View File

@ -40,7 +40,7 @@
<!-- Page Layout here -->
<div class="row">
<div class="col s3">
<div class="col s3" style="width:20em">
{% if STATEFUL_MODE != 'disabled' %}
<ul class="collection z-depth-1">
<a class="collection-item" href="{% url 'config' 'apprise' %}"><i class="tiny material-icons">settings</i>

View File

@ -23,6 +23,7 @@
{% blocktrans %}
Here is where you can store your Apprise configuration associated with the key <code>{{key}}</code>.
{% endblocktrans %}
For some examples on how to build a development environment around this, <strong><a href="{% url 'welcome'%}?key={{key}}">click here</a></strong>.
</li>
<li>
{% blocktrans %}
@ -78,7 +79,14 @@
{% blocktrans %}The following command would cause apprise to directly notify all of your services:{% endblocktrans %}
<br />
<pre><code class="bash">apprise --body="Test Message" \<br/>
&nbsp;&nbsp;&nbsp;&nbsp;apprise{% if request.is_secure %}s{% endif %}://{{request.META.HTTP_HOST}}{{BASE_URL}}/{{key}}?tags=all</code></pre>
&nbsp;&nbsp;&nbsp;&nbsp;apprise{% if request.is_secure %}s{% endif %}://{{request.META.HTTP_HOST}}{{BASE_URL}}/<em>{{key}}</em>/?tags=all</code></pre>
<br />
{% blocktrans %}Send one or more attachments like this:{% endblocktrans %}
<pre><code class="bash">apprise --body="Test Message" \<br/>
&nbsp;&nbsp;&nbsp;&nbsp;apprise{% if request.is_secure %}s{% endif %}://{{request.META.HTTP_HOST}}{{BASE_URL}}/<em>{{key}}</em>/?tags=all \<br/>
&nbsp;&nbsp;&nbsp;&nbsp;--attach=/path/to/an/attachment.jpeg \ <br/>
&nbsp;&nbsp;&nbsp;&nbsp;--attach=https://raw.githubusercontent.com/caronc/apprise/master/apprise/assets/themes/default/apprise-logo.png<br/>
</code></pre>
</p>
{% if not CONFIG_LOCK %}
<p>
@ -86,7 +94,7 @@
send a test notification to all of your added services:{% endblocktrans %}
<br />
<pre><code class="bash">apprise --body="Test Message" --tag=all \<br/>
&nbsp;&nbsp;&nbsp;&nbsp;--config={{request.scheme}}://{{request.META.HTTP_HOST}}{{BASE_URL}}/get/{{key}}</code></pre>
&nbsp;&nbsp;&nbsp;&nbsp;--config={{request.scheme}}://{{request.META.HTTP_HOST}}{{BASE_URL}}/get/<em>{{key}}</em></code></pre>
</p>
{% endif %}
</div>
@ -122,7 +130,7 @@
{% blocktrans %}
You can send a notification using the loaded configuration:
{% endblocktrans %}
<form id="donotify" action="{% url "notify" key %}" method="post">
<form id="donotify" enctype="multipart/form-data" action="{% url "notify" key %}" method="post">
{{ form_notify }}
<button class="btn waves-effect waves-light" type="submit" name="action">{% trans "Send Notification" %}
<i class="material-icons right">send</i>
@ -411,8 +419,8 @@ function notify_init() {
return s;
}, []).join(",")
const form = this;
const body = new URLSearchParams(new FormData(form));
// our Form
const form = new FormData(this);
// perform our notification
Swal.fire(
@ -422,7 +430,7 @@ function notify_init() {
Swal.showLoading()
let response = fetch('{% url "notify" key %}', {
method: 'POST',
body: body,
body: form,
headers: {
'Accept': 'text/html',
'X-Apprise-Log-Level': 'info'

View File

@ -71,7 +71,7 @@
<ul class="collapsible">
<li>
<div class="collapsible-header">
<i class="material-icons">code</i>curl example
<i class="material-icons">code</i>Curl Example
</div>
<div class="collapsible-body">
<pre><code class="bash">
@ -83,7 +83,7 @@
</li>
<li>
<div class="collapsible-header">
<i class="material-icons">code</i>python example
<i class="material-icons">code</i>Python Example
</div>
<div class="collapsible-body">
<pre><code class="python">
@ -101,7 +101,7 @@
</li>
<li>
<div class="collapsible-header">
<i class="material-icons">code</i>php example
<i class="material-icons">code</i>PHP Example
</div>
<div class="collapsible-body">
<pre><code class="php">

View File

@ -25,7 +25,7 @@
<table class="highlighted">
<thead>
<tr>
<th>{% trans "URL" %}</th>
<th style="min-width: 18%;">{% trans "URL" %}</th>
<th>{% trans "Description" %}</th>
</tr>
</thead>
@ -83,17 +83,26 @@
</table>
<ul class="collapsible">
<li>
<div class="collapsible-header"><i class="material-icons">code</i>curl example</div>
<div class="collapsible-header"><i class="material-icons">code</i>Curl Example</div>
<div class="collapsible-body">
<pre><code class="bash">
# {% blocktrans %}Notifies an email address{% endblocktrans %}<br/>
curl -X POST -d '{"urls":"mailto://user:pass@gmail.com","body":"test body","title":"test title"}' \<br/>
&nbsp;&nbsp;&nbsp;&nbsp;-H "Content-Type: application/json" \<br/>
&nbsp;&nbsp;&nbsp;&nbsp;{{request.scheme}}://{{request.META.HTTP_HOST}}{{BASE_URL}}/notify/</code></pre>
&nbsp;&nbsp;&nbsp;&nbsp;"{{request.scheme}}://{{request.META.HTTP_HOST}}{{BASE_URL}}/notify/"
<br/><br/>
# {% blocktrans %}Notifies an email address with attachments{% endblocktrans %}<br/>
curl -X POST -F 'urls=mailto://user:pass@gmail.com' \<br/>
&nbsp;&nbsp;&nbsp;&nbsp;-F 'title=test title' \<br/>
&nbsp;&nbsp;&nbsp;&nbsp;-F 'body=test body' \<br/>
&nbsp;&nbsp;&nbsp;&nbsp;-F attach1=@/path/to/attachment.doc \<br/>
&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>
</div>
</li>
<li>
<div class="collapsible-header"><i class="material-icons">code</i>python example</div>
<div class="collapsible-header"><i class="material-icons">code</i>Python Example</div>
<div class="collapsible-body">
<pre><code class="python">
import json<br/>
@ -104,18 +113,33 @@
&nbsp;&nbsp;&nbsp;&nbsp;'title': 'test title',<br/>
&nbsp;&nbsp;&nbsp;&nbsp;'body': 'test body',<br/>
}<br/>
<br/># The URL<br/>
req = Request(<br/>
<br/># The Request<br/>
response = Request(<br/>
&nbsp;&nbsp;&nbsp;&nbsp;"{{request.scheme}}://{{request.META.HTTP_HOST}}{{BASE_URL}}/notify/",<br/>
&nbsp;&nbsp;&nbsp;&nbsp;json.dumps(payload).encode('utf-8'),<br/>
&nbsp;&nbsp;&nbsp;&nbsp;{"Content-Type": "application/json"},<br/>
&nbsp;&nbsp;&nbsp;&nbsp;method='POST',<br/>
)
<br/>
</code></pre>
<pre><code class="python">
# {% blocktrans %}Notifies an email address with attachments{% endblocktrans %}<br/>
import requests<br/><br/>
payload = {<br/>
&nbsp;&nbsp;&nbsp;&nbsp;'urls': 'mailto://user:pass@gmail.com',<br/>
&nbsp;&nbsp;&nbsp;&nbsp;'title': 'test title',<br/>
&nbsp;&nbsp;&nbsp;&nbsp;'body': 'test body',<br/>
}<br/><br/>
with open("my/path/to/attachment.png", 'rb') as fp:<br/>
&nbsp;&nbsp;&nbsp;&nbsp;response = request.post("{{request.scheme}}://{{request.META.HTTP_HOST}}{{BASE_URL}}/notify/",<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;data=payload,<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;files={'attach1':('attachment.png', fp)},<br/>
&nbsp;&nbsp;&nbsp;&nbsp;)
</code></pre>
</div>
</li>
<li>
<div class="collapsible-header"><i class="material-icons">code</i>php example</div>
<div class="collapsible-header"><i class="material-icons">code</i>PHP Example</div>
<div class="collapsible-body">
<pre><code class="php">&lt;?php<br/>
<br/>
@ -145,7 +169,49 @@
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));<br/>
<br/>
//Execute the request<br/>
$result = curl_exec($ch);
$result = curl_exec($ch);<br/>
<br/>
// Close our handler<br/>
curl_close($ch);<br/>
?>
</code></pre>
<pre><code class="php">&lt;?php<br/>
// Sending an Attachment using PHP<br/><br/>
// The URL<br/>
$url = '{{request.scheme}}://{{request.META.HTTP_HOST}}{{BASE_URL}}/notify/';<br/>
<br/>
//Initiate cURL.<br/>
$ch = curl_init($url);<br/>
<br/>
// Prepare our File attachment<br/>
$path = '/path/to/photo.jpg';<br/>
<br/>
// Acquire our Filename<br/>
$fname = basename($path);<br/>
<br/>
// Get our attachment mime-type (in this case it's 'image/jpg')<br/>
$mimeType = mime_content_type($path);<br/>
<br/>
//The multipart data.<br/>
$data = array(<br/>
&nbsp;&nbsp;&nbsp;&nbsp;'urls' => 'mailto://user:pass@hotmail.com',<br/>
&nbsp;&nbsp;&nbsp;&nbsp;'title' => 'test title',<br/>
&nbsp;&nbsp;&nbsp;&nbsp;'body' => 'test body',<br/>
&nbsp;&nbsp;&nbsp;&nbsp;'attach1' => new CURLFile($path, $mimeType, $fname)<br/>
);<br/>
<br/>
//Tell cURL that we want to send a POST request.<br/>
curl_setopt($ch, CURLOPT_POST, 1);<br/>
<br/>
//Attach our data to the POST fields.<br/>
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);<br/>
<br/>
//Execute the request<br/>
$result = curl_exec($ch);<br/>
<br/>
// Close our handler<br/>
curl_close($ch);<br/>
?>
</code></pre>
</div>
</li>
@ -171,17 +237,17 @@
<table class="highlighted">
<thead>
<tr>
<th>{% trans "URL" %}</th>
<th style="min-width: 18%;">{% trans "URL" %}</th>
<th>{% trans "Description" %}</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>{{BASE_URL}}/add/<em>{% trans "KEY" %}</em></code></td>
<td><code>{{BASE_URL}}/add/<em>{{key}}</em></code></td>
<td>
{% blocktrans %}Used to add a new Apprise configuration or a set of URLs and associates them with the
specified <em>KEY</em>. See the <a target="_blank"
{% blocktrans %}Used to add a new Apprise configuration or a set of URLs and associates them with configuration
identified with the id of <em>{{key}}</em>. See the <a target="_blank"
href="https://github.com/caronc/apprise/wiki#notification-services">Apprise Wiki</a> if you need help
constructing your URLs.{% endblocktrans %}
<div class="section">
@ -229,23 +295,23 @@
</table>
<ul class="collapsible">
<li>
<div class="collapsible-header"><i class="material-icons">code</i>curl example</div>
<div class="collapsible-header"><i class="material-icons">code</i>Curl Example</div>
<div class="collapsible-body">
<pre><code class="bash">
# {% blocktrans %}Load a single URL and assign it to the <em>KEY</em> of abc123{% endblocktrans %}<br/>
# {% blocktrans %}Load a single URL and assign it to: <em>{{key}}</em>{% endblocktrans %}<br/>
curl -X POST -d '{"urls":"mailto://user:pass@gmail.com"}' \<br/>
&nbsp;&nbsp;&nbsp;&nbsp;-H "Content-Type: application/json" \<br/>
&nbsp;&nbsp;&nbsp;&nbsp;{{request.scheme}}://{{request.META.HTTP_HOST}}{{BASE_URL}}/add/<em>abc123</em></code></pre>
&nbsp;&nbsp;&nbsp;&nbsp;{{request.scheme}}://{{request.META.HTTP_HOST}}{{BASE_URL}}/add/<em>{{key}}</em></code></pre>
<pre><code class="bash">
# {% blocktrans %}Load a simple TEXT config entry <em>KEY</em> abc123{% endblocktrans %}<br/>
# {% blocktrans %}Load a simple TEXT config entry sent to: <em>{{key}}</em>{% endblocktrans %}<br/>
curl -X POST -d '{"format":"text","config":"devops=mailto://user:pass@gmail.com"}' \<br/>
&nbsp;&nbsp;&nbsp;&nbsp;-H "Content-Type: application/json" \<br/>
&nbsp;&nbsp;&nbsp;&nbsp;{{request.scheme}}://{{request.META.HTTP_HOST}}{{BASE_URL}}/add/abc123/
&nbsp;&nbsp;&nbsp;&nbsp;{{request.scheme}}://{{request.META.HTTP_HOST}}{{BASE_URL}}/add/{{key}}/
</code></pre>
</div>
</li>
<li>
<div class="collapsible-header"><i class="material-icons">code</i>python example</div>
<div class="collapsible-header"><i class="material-icons">code</i>Python Example</div>
<div class="collapsible-body">
<pre><code class="python">
import json<br/>
@ -254,9 +320,9 @@
payload = {<br/>
&nbsp;&nbsp;&nbsp;&nbsp;'urls': 'mailto://user:pass@gmail.com',<br/>
}<br/>
<br/># The URL if the key was <em>abc123</em><br/>
<br/># The URL if the key was <em>{{key}}</em><br/>
req = Request(<br/>
&nbsp;&nbsp;&nbsp;&nbsp;"{{request.scheme}}://{{request.META.HTTP_HOST}}{{BASE_URL}}/add/<em>abc123</em>",<br/>
&nbsp;&nbsp;&nbsp;&nbsp;"{{request.scheme}}://{{request.META.HTTP_HOST}}{{BASE_URL}}/add/<em>{{key}}</em>",<br/>
&nbsp;&nbsp;&nbsp;&nbsp;json.dumps(payload).encode('utf-8'),<br/>
&nbsp;&nbsp;&nbsp;&nbsp;{"Content-Type": "application/json"},<br/>
&nbsp;&nbsp;&nbsp;&nbsp;method='POST',<br/>
@ -265,12 +331,12 @@
</div>
</li>
<li>
<div class="collapsible-header"><i class="material-icons">code</i>php example</div>
<div class="collapsible-header"><i class="material-icons">code</i>PHP Example</div>
<div class="collapsible-body">
<pre><code class="php">&lt;?php<br/>
<br/>
// The URL if the key was <em>abc123</em><br/>
$url = '{{request.scheme}}://{{request.META.HTTP_HOST}}{{BASE_URL}}/add/<em>abc123</em>';<br/>
// The URL if the key was <em>{{key}}</em><br/>
$url = '{{request.scheme}}://{{request.META.HTTP_HOST}}{{BASE_URL}}/add/<em>{{key}}</em>';<br/>
<br/>
//Initiate cURL.<br/>
$ch = curl_init($url);<br/>
@ -293,7 +359,11 @@
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));<br/>
<br/>
//Execute the request<br/>
$result = curl_exec($ch);
$result = curl_exec($ch);<br/>
<br/>
// Close our handler<br/>
curl_close($ch);<br/>
?>
</code></pre>
</div>
</li>
@ -302,28 +372,27 @@
</td>
</tr>
<tr>
<td><code>{{BASE_URL}}/del/<em>{% trans "KEY" %}</em></code></td>
<td>{% blocktrans %}There are no arguments required. If the <em>KEY</em> exists and has data associated with
it,
it will be removed.{% endblocktrans %}
<td><code>{{BASE_URL}}/del/<em>{{key}}</em></code></td>
<td>{% blocktrans %}There are no arguments required. If configuration id of <em>{{key}}</em> exists and has data associated with
it, it will be removed.{% endblocktrans %}
<ul class="collapsible">
<li>
<div class="collapsible-header"><i class="material-icons">code</i>curl example</div>
<div class="collapsible-header"><i class="material-icons">code</i>Curl Example</div>
<div class="collapsible-body">
<pre><code class="bash">
# {% blocktrans %}Remove previously loaded configuration associated with the <em>KEY</em> of abc123{% endblocktrans %}<br/>
curl -X POST {{request.scheme}}://{{request.META.HTTP_HOST}}{{BASE_URL}}/del/<em>abc123</em></code></pre>
# {% blocktrans %}Remove previously loaded configuration associated with the id of <em>{{key}}</em>{% endblocktrans %}<br/>
curl -X POST {{request.scheme}}://{{request.META.HTTP_HOST}}{{BASE_URL}}/del/<em>{{key}}</em></code></pre>
</div>
</li>
<li>
<div class="collapsible-header"><i class="material-icons">code</i>python example</div>
<div class="collapsible-header"><i class="material-icons">code</i>Python Example</div>
<div class="collapsible-body">
<pre><code class="python">
import json<br/>
from urllib.request import Request<br/>
<br/># The request if the key was <em>abc123</em><br/>
<br/># The request if the key was <em>{{key}}</em><br/>
req = Request(<br/>
&nbsp;&nbsp;&nbsp;&nbsp;"{{request.scheme}}://{{request.META.HTTP_HOST}}{{BASE_URL}}/del/<em>abc123</em>",<br/>
&nbsp;&nbsp;&nbsp;&nbsp;"{{request.scheme}}://{{request.META.HTTP_HOST}}{{BASE_URL}}/del/<em>{{key}}</em>",<br/>
&nbsp;&nbsp;&nbsp;&nbsp;json.dumps(payload).encode('utf-8'),<br/>
&nbsp;&nbsp;&nbsp;&nbsp;{"Content-Type": "application/json"},<br/>
&nbsp;&nbsp;&nbsp;&nbsp;method='POST',<br/>
@ -332,12 +401,12 @@
</div>
<li>
<li>
<div class="collapsible-header"><i class="material-icons">code</i>php example</div>
<div class="collapsible-header"><i class="material-icons">code</i>PHP Example</div>
<div class="collapsible-body">
<pre><code class="php">&lt;?php<br/>
<br/>
// The URL if the key was <em>abc123</em><br/>
$url = '{{request.scheme}}://{{request.META.HTTP_HOST}}{{BASE_URL}}/del/<em>abc123</em>';<br/>
// The URL if the key was <em>{{key}}</em><br/>
$url = '{{request.scheme}}://{{request.META.HTTP_HOST}}{{BASE_URL}}/del/<em>{{key}}</em>';<br/>
<br/>
//Initiate cURL.<br/>
$ch = curl_init($url);<br/>
@ -346,7 +415,11 @@
curl_setopt($ch, CURLOPT_POST, 1);<br/>
<br/>
//Execute the request<br/>
$result = curl_exec($ch);
$result = curl_exec($ch);<br/>
<br/>
// Close our handler<br/>
curl_close($ch);<br/>
?>
</code></pre>
</div>
</li>
@ -354,17 +427,17 @@
</td>
</tr>
<tr>
<td><code>{{BASE_URL}}/get/<em>{% trans "KEY" %}</em></code></td>
<td><code>{{BASE_URL}}/get/<em>{{key}}</em></code></td>
<td>{% blocktrans %}This feature can be used by Apprise itself. It provides a means of remotely fetching it's
configuration.{% endblocktrans %}<br />
the configuration associated with the configuration identified through the id of <em>{{key}}</em>.{% endblocktrans %}<br />
<pre><code lang="bash"># Use Apprise to retrieve your configuration:<br/>
apprise --body="test message" --config={{ request.scheme }}://{{request.META.HTTP_HOST}}{{BASE_URL}}/get/<em>{% trans "KEY" %}</em></code></pre>
apprise --body="test message" --config={{ request.scheme }}://{{request.META.HTTP_HOST}}{{BASE_URL}}/get/<em>{{key}}</em></code></pre>
</p>
</td>
</tr>
<tr>
<td><code>{{BASE_URL}}/notify/<em>{% trans "KEY" %}</em></code></td>
<td>{% blocktrans %}Notifies the URLs associated with the specified <em>KEY</em>.{% endblocktrans %}
<td><code>{{BASE_URL}}/notify/<em>{{key}}</em></code></td>
<td>{% blocktrans %}Notifies the URLs associated with configuration identified by the id of <em>{{key}}</em>.{% endblocktrans %}
<div class="section">
<table>
<thead>
@ -411,18 +484,42 @@
</table>
<ul class="collapsible">
<li>
<div class="collapsible-header"><i class="material-icons">code</i>curl example</div>
<div class="collapsible-header"><i class="material-icons">code</i>Curl Example</div>
<div class="collapsible-body">
<pre><code class="bash">
# {% blocktrans %}Notifies all URLs assigned to the configuration{% endblocktrans %}<br/>
curl -X POST \<br/>
&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 %}Notifies all URLs assigned the <em>devops</em> tag{% endblocktrans %}<br/>
curl -X POST -d '{"tag":"devops","body":"test body","title":"test title"}' \<br/>
&nbsp;&nbsp;&nbsp;&nbsp;-H "Content-Type: application/json" \<br/>
&nbsp;&nbsp;&nbsp;&nbsp;{{request.scheme}}://{{request.META.HTTP_HOST}}{{BASE_URL}}/notify/<em>KEY</em></code></pre>
&nbsp;&nbsp;&nbsp;&nbsp;"{{request.scheme}}://{{request.META.HTTP_HOST}}{{BASE_URL}}/notify/<em>{{key}}</em>"</code></pre>
</div>
</li>
<li>
<div class="collapsible-header"><i class="material-icons">code</i>python example</div>
<div class="collapsible-header"><i class="material-icons">code</i>Python Example</div>
<div class="collapsible-body">
Ideally you should leverage the Apprise Library, it will make your life much easier:
<pre><code class="python">
import apprise<br/>
<br/>
# Create an Apprise Instance<br/>
aobj = apprise.Apprise()<br/>
<br/>
# Add our URL</br>
aobj.add("apprise{% if secure %}s{%else%}{%endif%}://{{request.META.HTTP_HOST}}{{BASE_URL}}/<em>{{key}}</em>")<br/>
<br/>
# Send our notification:<br/>
aobj.notify("test body", "test title")<br/>
<br/>
# Sending an attachment is just as easy:<br/>
aobj.notify("test body", "test title", attach="/path/to/file")<br/>
</code></pre>
The legacy (but more compatible and light weight) way of doing things:
<pre><code class="python">
import json<br/>
from urllib.request import Request<br/>
@ -432,9 +529,9 @@
&nbsp;&nbsp;&nbsp;&nbsp;'title': 'test title',<br/>
&nbsp;&nbsp;&nbsp;&nbsp;'body': 'test body',<br/>
}<br/>
<br/># The URL if the key was <em>abc123</em><br/>
<br/># The URL if the key was <em>{{key}}</em><br/>
req = Request(<br/>
&nbsp;&nbsp;&nbsp;&nbsp;"{{request.scheme}}://{{request.META.HTTP_HOST}}{{BASE_URL}}/notify/<em>abc123</em>",<br/>
&nbsp;&nbsp;&nbsp;&nbsp;"{{request.scheme}}://{{request.META.HTTP_HOST}}{{BASE_URL}}/notify/<em>{{key}}</em>",<br/>
&nbsp;&nbsp;&nbsp;&nbsp;json.dumps(payload).encode('utf-8'),<br/>
&nbsp;&nbsp;&nbsp;&nbsp;{"Content-Type": "application/json"},<br/>
&nbsp;&nbsp;&nbsp;&nbsp;method='POST',<br/>
@ -443,12 +540,12 @@
</div>
<li>
<li>
<div class="collapsible-header"><i class="material-icons">code</i>php example</div>
<div class="collapsible-header"><i class="material-icons">code</i>PHP Example</div>
<div class="collapsible-body">
<pre><code class="php">&lt;?php<br/>
<br/>
// The URL if the key was <em>abc123</em><br/>
$url = '{{request.scheme}}://{{request.META.HTTP_HOST}}{{BASE_URL}}/notify/<em>abc123</em>';<br/>
// The URL if the key was <em>{{key}}</em><br/>
$url = '{{request.scheme}}://{{request.META.HTTP_HOST}}{{BASE_URL}}/notify/<em>{{key}}</em>';<br/>
<br/>
//Initiate cURL.<br/>
$ch = curl_init($url);<br/>
@ -473,7 +570,49 @@
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));<br/>
<br/>
//Execute the request<br/>
$result = curl_exec($ch);
$result = curl_exec($ch);<br/>
<br/>
// Close our handler<br/>
curl_close($ch);<br/>
?>
</code></pre>
<pre><code class="php">&lt;?php<br/>
// Sending an Attachment using PHP<br/><br/>
// The URL<br/>
$url = '{{request.scheme}}://{{request.META.HTTP_HOST}}{{BASE_URL}}/notify/<em>{{key}}</em>';<br/>
<br/>
//Initiate cURL.<br/>
$ch = curl_init($url);<br/>
<br/>
// Prepare our File attachment<br/>
$path = '/path/to/photo.jpg';<br/>
<br/>
// Acquire our Filename<br/>
$fname = basename($path);<br/>
<br/>
// Get our attachment mime-type (in this case it's 'image/jpg')<br/>
$mimeType = mime_content_type($path);<br/>
<br/>
//The multipart data.<br/>
$data = array(<br/>
&nbsp;&nbsp;&nbsp;&nbsp;'urls' => 'mailto://user:pass@hotmail.com',<br/>
&nbsp;&nbsp;&nbsp;&nbsp;'title' => 'test title',<br/>
&nbsp;&nbsp;&nbsp;&nbsp;'body' => 'test body',<br/>
&nbsp;&nbsp;&nbsp;&nbsp;'attach1' => new CURLFile($path, $mimeType, $fname)<br/>
);<br/>
<br/>
//Tell cURL that we want to send a POST request.<br/>
curl_setopt($ch, CURLOPT_POST, 1);<br/>
<br/>
//Attach our data to the POST fields.<br/>
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);<br/>
<br/>
//Execute the request<br/>
$result = curl_exec($ch);<br/>
<br/>
// Close our handler<br/>
curl_close($ch);<br/>
?>
</code></pre>
</div>
</li>
@ -487,7 +626,7 @@
<div class="section">
<h4>{% trans "Endpoint Notes" %}</h4>
<p>
The <em>KEY</em> you plan to associate your configuration with:
The Configuration ID (<em>{{key}}</em>) you plan to associate your configuration with:
<ol>
<li>Can not have spaces and/or special characters in it. Both a dash (<code>-</code>) and underscore
(<code>_</code>) are the only exceptions to this rule.</li>

View File

@ -23,6 +23,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
import re
import binascii
import os
import tempfile
import shutil
@ -30,6 +31,7 @@ import gzip
import apprise
import hashlib
import errno
import base64
from django.conf import settings
@ -63,6 +65,202 @@ STORE_MODES = (
)
class Attachment(apprise.attachment.AttachFile):
"""
A Light Weight Attachment Object for Auto-cleanup that wraps the Apprise
Attachments
"""
def __init__(self, filename, path=None, delete=True):
"""
Initialize our attachment
"""
self._filename = filename
try:
os.makedirs(settings.APPRISE_ATTACH_DIR, exist_ok=True)
except OSError:
# Permission error
raise ValueError('Could not create directory {}'.format(
settings.APPRISE_ATTACH_DIR))
if not path:
try:
d, path = tempfile.mkstemp(dir=settings.APPRISE_ATTACH_DIR)
# Close our file descriptor
os.close(d)
except FileNotFoundError:
raise ValueError(
'Could not prepare {} attachment in {}'.format(
filename, settings.APPRISE_ATTACH_DIR))
self._path = path
self.delete = delete
# Prepare our item
super().__init__(path=self._path, name=filename)
@property
def filename(self):
return self._filename
@property
def size(self):
"""
Return filesize
"""
return os.stat(self._path).st_size
def __del__(self):
"""
De-Construtor is used to tidy up files during garbage collection
"""
if self.delete:
try:
os.remove(self._path)
except FileNotFoundError:
# no problem
pass
def parse_attachments(attachment_payload, files_request):
"""
Takes the payload provided in a `/notify` call and extracts the
attachments out of it.
Content is written to a temporary directory until the garbage
collection kicks in.
"""
attachments = []
# Attachment Count
count = sum([
0 if not isinstance(attachment_payload, (tuple, list))
else len(attachment_payload),
0 if not isinstance(files_request, dict) else len(files_request),
])
if settings.APPRISE_MAX_ATTACHMENTS > 0 and \
count > settings.APPRISE_MAX_ATTACHMENTS:
raise ValueError(
"There is a maximum of %d attachments" %
settings.APPRISE_MAX_ATTACHMENTS)
if isinstance(attachment_payload, (tuple, list)):
for no, entry in enumerate(attachment_payload, start=1):
if isinstance(entry, str):
filename = "attachment.%.3d" % no
elif isinstance(entry, dict):
try:
filename = entry.get("filename", "").strip()
# Max filename size is 250
if len(filename) > 250:
raise ValueError(
"The filename associated with attachment "
"%d is too long" % no)
elif not filename:
filename = "attachment.%.3d" % no
except TypeError:
raise ValueError(
"An invalid filename was provided for attachment %d" %
no)
else:
# you must pass in a base64 string, or a dict containing our
# required parameters
raise ValueError(
"An invalid filename was provided for attachment %d" % no)
#
# Prepare our Attachment
#
attachment = Attachment(filename)
try:
with open(attachment.path, 'wb') as f:
# Write our content to disk
f.write(base64.b64decode(entry["base64"]))
except binascii.Error:
# The file ws not base64 encoded
raise ValueError(
"Invalid filecontent was provided for attachment %s" %
filename)
except OSError:
raise ValueError(
"Could not write attachment %s to disk" % filename)
#
# Some Validation
#
if settings.APPRISE_MAX_ATTACHMENT_SIZE > 0 and \
attachment.size > settings.APPRISE_MAX_ATTACHMENT_SIZE:
raise ValueError(
"attachment %s's filesize is to large" % filename)
# Add our attachment
attachments.append(attachment)
#
# Now handle the request.FILES
#
if isinstance(files_request, dict):
for no, (key, meta) in enumerate(
files_request.items(), start=len(attachments) + 1):
try:
# Filetype is presumed to be of base class
# django.core.files.UploadedFile
filename = meta.name.strip()
# Max filename size is 250
if len(filename) > 250:
raise ValueError(
"The filename associated with attachment "
"%d is too long" % no)
elif not filename:
filename = "attachment.%.3d" % no
except (AttributeError, TypeError):
raise ValueError(
"An invalid filename was provided for attachment %d" %
no)
#
# Prepare our Attachment
#
attachment = Attachment(filename)
try:
with open(attachment.path, 'wb') as f:
# Write our content to disk
f.write(meta.read())
except OSError:
raise ValueError(
"Could not write attachment %s to disk" % filename)
#
# Some Validation
#
if settings.APPRISE_MAX_ATTACHMENT_SIZE > 0 and \
attachment.size > settings.APPRISE_MAX_ATTACHMENT_SIZE:
raise ValueError(
"attachment %s's filesize is to large" % filename)
# Add our attachment
attachments.append(attachment)
return attachments
class SimpleFileExtension(object):
"""
Defines the simple file exension lookups
@ -130,10 +328,10 @@ class AppriseConfigCache(object):
return False
# Write our file to a temporary file
_, tmp_path = tempfile.mkstemp(suffix='.tmp', dir=path)
d, tmp_path = tempfile.mkstemp(suffix='.tmp', dir=path)
# Close the file handle provided by mkstemp()
# We're reopening it, and it can't be renamed while open on Windows
os.close(_)
os.close(d)
if self.mode == AppriseStoreMode.HASH:
try:

View File

@ -34,6 +34,7 @@ from django.views.decorators.gzip import gzip_page
from django.utils.translation import gettext_lazy as _
from django.core.serializers.json import DjangoJSONEncoder
from .utils import parse_attachments
from .utils import ConfigCache
from .utils import apply_global_filters
from .forms import AddByUrlForm
@ -121,7 +122,12 @@ class WelcomeView(View):
template_name = 'welcome.html'
def get(self, request):
return render(request, self.template_name, {})
default_key = 'KEY'
key = request.GET.get('key', default_key).strip()
return render(request, self.template_name, {
'secure': request.scheme[-1].lower() == 's',
'key': key if key else default_key,
})
@method_decorator((gzip_page, never_cache), name='dispatch')
@ -571,9 +577,7 @@ class NotifyView(View):
# our content
content = {}
if MIME_IS_FORM.match(request.content_type):
content = {}
form = NotifyForm(request.POST)
form = NotifyForm(data=request.POST, files=request.FILES)
if form.is_valid():
content.update(form.cleaned_data)
@ -604,6 +608,12 @@ class NotifyView(View):
status=status,
)
# Handle Attachments
attach = None
if 'attachments' in content or request.FILES:
attach = parse_attachments(
content.get('attachments'), request.FILES)
#
# Allow 'tag' value to be specified as part of the URL parameters
# if not found otherwise defined.
@ -837,6 +847,7 @@ class NotifyView(View):
title=content.get('title', ''),
notify_type=content.get('type', apprise.NotifyType.INFO),
tag=content.get('tag'),
attach=attach,
)
if content_type == 'text/html':
@ -863,6 +874,7 @@ class NotifyView(View):
title=content.get('title', ''),
notify_type=content.get('type', apprise.NotifyType.INFO),
tag=content.get('tag'),
attach=attach,
)
if not result:
@ -901,7 +913,7 @@ class StatelessNotifyView(View):
content = {}
if MIME_IS_FORM.match(request.content_type):
content = {}
form = NotifyByUrlForm(request.POST)
form = NotifyByUrlForm(request.POST, request.FILES)
if form.is_valid():
content.update(form.cleaned_data)
@ -999,12 +1011,19 @@ class StatelessNotifyView(View):
status=ResponseCode.no_content,
)
# Handle Attachments
attach = None
if 'attachments' in content or request.FILES:
attach = parse_attachments(
content.get('attachments'), request.FILES)
# Perform our notification at this point
result = a_obj.notify(
content.get('body'),
title=content.get('title', ''),
notify_type=content.get('type', apprise.NotifyType.INFO),
tag='all',
attach=attach,
)
if not result: