mirror of
https://github.com/caronc/apprise-api.git
synced 2025-08-16 01:27:47 +02:00
Attachment Support Added (#118)
This commit is contained in:
@ -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(
|
||||
|
@ -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>
|
||||
|
@ -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/>
|
||||
apprise{% if request.is_secure %}s{% endif %}://{{request.META.HTTP_HOST}}{{BASE_URL}}/{{key}}?tags=all</code></pre>
|
||||
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/>
|
||||
apprise{% if request.is_secure %}s{% endif %}://{{request.META.HTTP_HOST}}{{BASE_URL}}/<em>{{key}}</em>/?tags=all \<br/>
|
||||
--attach=/path/to/an/attachment.jpeg \ <br/>
|
||||
--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/>
|
||||
--config={{request.scheme}}://{{request.META.HTTP_HOST}}{{BASE_URL}}/get/{{key}}</code></pre>
|
||||
--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'
|
||||
|
@ -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">
|
||||
|
@ -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/>
|
||||
-H "Content-Type: application/json" \<br/>
|
||||
{{request.scheme}}://{{request.META.HTTP_HOST}}{{BASE_URL}}/notify/</code></pre>
|
||||
"{{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/>
|
||||
-F 'title=test title' \<br/>
|
||||
-F 'body=test body' \<br/>
|
||||
-F attach1=@/path/to/attachment.doc \<br/>
|
||||
-F attach2=@Screenshot-2.png \ <br/>
|
||||
"{{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 @@
|
||||
'title': 'test title',<br/>
|
||||
'body': 'test body',<br/>
|
||||
}<br/>
|
||||
<br/># The URL<br/>
|
||||
req = Request(<br/>
|
||||
<br/># The Request<br/>
|
||||
response = Request(<br/>
|
||||
"{{request.scheme}}://{{request.META.HTTP_HOST}}{{BASE_URL}}/notify/",<br/>
|
||||
json.dumps(payload).encode('utf-8'),<br/>
|
||||
{"Content-Type": "application/json"},<br/>
|
||||
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/>
|
||||
'urls': 'mailto://user:pass@gmail.com',<br/>
|
||||
'title': 'test title',<br/>
|
||||
'body': 'test body',<br/>
|
||||
}<br/><br/>
|
||||
with open("my/path/to/attachment.png", 'rb') as fp:<br/>
|
||||
response = request.post("{{request.scheme}}://{{request.META.HTTP_HOST}}{{BASE_URL}}/notify/",<br/>
|
||||
data=payload,<br/>
|
||||
files={'attach1':('attachment.png', fp)},<br/>
|
||||
)
|
||||
</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"><?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"><?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/>
|
||||
'urls' => 'mailto://user:pass@hotmail.com',<br/>
|
||||
'title' => 'test title',<br/>
|
||||
'body' => 'test body',<br/>
|
||||
'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/>
|
||||
-H "Content-Type: application/json" \<br/>
|
||||
{{request.scheme}}://{{request.META.HTTP_HOST}}{{BASE_URL}}/add/<em>abc123</em></code></pre>
|
||||
{{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/>
|
||||
-H "Content-Type: application/json" \<br/>
|
||||
{{request.scheme}}://{{request.META.HTTP_HOST}}{{BASE_URL}}/add/abc123/
|
||||
{{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/>
|
||||
'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/>
|
||||
"{{request.scheme}}://{{request.META.HTTP_HOST}}{{BASE_URL}}/add/<em>abc123</em>",<br/>
|
||||
"{{request.scheme}}://{{request.META.HTTP_HOST}}{{BASE_URL}}/add/<em>{{key}}</em>",<br/>
|
||||
json.dumps(payload).encode('utf-8'),<br/>
|
||||
{"Content-Type": "application/json"},<br/>
|
||||
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"><?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/>
|
||||
"{{request.scheme}}://{{request.META.HTTP_HOST}}{{BASE_URL}}/del/<em>abc123</em>",<br/>
|
||||
"{{request.scheme}}://{{request.META.HTTP_HOST}}{{BASE_URL}}/del/<em>{{key}}</em>",<br/>
|
||||
json.dumps(payload).encode('utf-8'),<br/>
|
||||
{"Content-Type": "application/json"},<br/>
|
||||
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"><?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/>
|
||||
-F "tag=all" \ <br/>
|
||||
-F "body=test body" \ <br/>
|
||||
-F "title=test title" \ <br/>
|
||||
"{{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/>
|
||||
-H "Content-Type: application/json" \<br/>
|
||||
{{request.scheme}}://{{request.META.HTTP_HOST}}{{BASE_URL}}/notify/<em>KEY</em></code></pre>
|
||||
"{{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 @@
|
||||
'title': 'test title',<br/>
|
||||
'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/>
|
||||
"{{request.scheme}}://{{request.META.HTTP_HOST}}{{BASE_URL}}/notify/<em>abc123</em>",<br/>
|
||||
"{{request.scheme}}://{{request.META.HTTP_HOST}}{{BASE_URL}}/notify/<em>{{key}}</em>",<br/>
|
||||
json.dumps(payload).encode('utf-8'),<br/>
|
||||
{"Content-Type": "application/json"},<br/>
|
||||
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"><?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"><?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/>
|
||||
'urls' => 'mailto://user:pass@hotmail.com',<br/>
|
||||
'title' => 'test title',<br/>
|
||||
'body' => 'test body',<br/>
|
||||
'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>
|
||||
|
@ -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:
|
||||
|
@ -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:
|
||||
|
Reference in New Issue
Block a user