Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bd09fb4c54 | ||
|
|
63206f8581 | ||
|
|
de0c41ec3b | ||
|
|
eaefb436d6 | ||
|
|
5843de5dfc | ||
|
|
6abda93a14 |
1
.gitignore
vendored
@@ -2,4 +2,5 @@ dist/
|
|||||||
build/
|
build/
|
||||||
.idea/
|
.idea/
|
||||||
server/docs/
|
server/docs/
|
||||||
|
tools/fbsend/fbsend
|
||||||
*.iml
|
*.iml
|
||||||
|
|||||||
@@ -7,14 +7,15 @@ import (
|
|||||||
|
|
||||||
// Defines default config settings
|
// Defines default config settings
|
||||||
const (
|
const (
|
||||||
DefaultListenHTTP = ":80"
|
DefaultListenHTTP = ":80"
|
||||||
DefaultCacheDuration = 12 * time.Hour
|
DefaultCacheDuration = 12 * time.Hour
|
||||||
DefaultKeepaliveInterval = 30 * time.Second
|
DefaultKeepaliveInterval = 30 * time.Second
|
||||||
DefaultManagerInterval = time.Minute
|
DefaultManagerInterval = time.Minute
|
||||||
DefaultAtSenderInterval = 10 * time.Second
|
DefaultAtSenderInterval = 10 * time.Second
|
||||||
DefaultMinDelay = 10 * time.Second
|
DefaultMinDelay = 10 * time.Second
|
||||||
DefaultMaxDelay = 3 * 24 * time.Hour
|
DefaultMaxDelay = 3 * 24 * time.Hour
|
||||||
DefaultMessageLimit = 512
|
DefaultMessageLimit = 512
|
||||||
|
DefaultFirebaseKeepaliveInterval = time.Hour
|
||||||
)
|
)
|
||||||
|
|
||||||
// Defines all the limits
|
// Defines all the limits
|
||||||
@@ -40,6 +41,7 @@ type Config struct {
|
|||||||
KeepaliveInterval time.Duration
|
KeepaliveInterval time.Duration
|
||||||
ManagerInterval time.Duration
|
ManagerInterval time.Duration
|
||||||
AtSenderInterval time.Duration
|
AtSenderInterval time.Duration
|
||||||
|
FirebaseKeepaliveInterval time.Duration
|
||||||
MessageLimit int
|
MessageLimit int
|
||||||
MinDelay time.Duration
|
MinDelay time.Duration
|
||||||
MaxDelay time.Duration
|
MaxDelay time.Duration
|
||||||
@@ -66,6 +68,7 @@ func New(listenHTTP string) *Config {
|
|||||||
MinDelay: DefaultMinDelay,
|
MinDelay: DefaultMinDelay,
|
||||||
MaxDelay: DefaultMaxDelay,
|
MaxDelay: DefaultMaxDelay,
|
||||||
AtSenderInterval: DefaultAtSenderInterval,
|
AtSenderInterval: DefaultAtSenderInterval,
|
||||||
|
FirebaseKeepaliveInterval: DefaultFirebaseKeepaliveInterval,
|
||||||
GlobalTopicLimit: DefaultGlobalTopicLimit,
|
GlobalTopicLimit: DefaultGlobalTopicLimit,
|
||||||
VisitorRequestLimitBurst: DefaultVisitorRequestLimitBurst,
|
VisitorRequestLimitBurst: DefaultVisitorRequestLimitBurst,
|
||||||
VisitorRequestLimitReplenish: DefaultVisitorRequestLimitReplenish,
|
VisitorRequestLimitReplenish: DefaultVisitorRequestLimitReplenish,
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ is in the request body. Here's an example showing how to publish a simple messag
|
|||||||
This will create a notification that looks like this:
|
This will create a notification that looks like this:
|
||||||
|
|
||||||
<figure markdown>
|
<figure markdown>
|
||||||
{ width=500 }
|
{ width=500 }
|
||||||
<figcaption>Android notification</figcaption>
|
<figcaption>Android notification</figcaption>
|
||||||
</figure>
|
</figure>
|
||||||
|
|
||||||
@@ -76,7 +76,7 @@ That's it. You're all set. Go play and read the rest of the docs. I highly recom
|
|||||||
Here's another video showing the entire process:
|
Here's another video showing the entire process:
|
||||||
|
|
||||||
<figure>
|
<figure>
|
||||||
<video controls muted autoplay loop width="650" src="static/img/overview.mp4"></video>
|
<video controls muted autoplay loop width="650" src="static/img/android-video-overview.mp4"></video>
|
||||||
<figcaption>Sending push notifications to your Android phone</figcaption>
|
<figcaption>Sending push notifications to your Android phone</figcaption>
|
||||||
</figure>
|
</figure>
|
||||||
|
|
||||||
|
|||||||
@@ -20,21 +20,21 @@ deb/rpm packages.
|
|||||||
|
|
||||||
=== "x86_64/amd64"
|
=== "x86_64/amd64"
|
||||||
```bash
|
```bash
|
||||||
wget https://github.com/binwiederhier/ntfy/releases/download/v1.5.3/ntfy_1.5.3_linux_x86_64.tar.gz
|
wget https://github.com/binwiederhier/ntfy/releases/download/v1.6.1/ntfy_1.6.1_linux_x86_64.tar.gz
|
||||||
sudo tar -C /usr/bin -zxf ntfy_*.tar.gz ntfy
|
sudo tar -C /usr/bin -zxf ntfy_*.tar.gz ntfy
|
||||||
sudo ./ntfy
|
sudo ./ntfy
|
||||||
```
|
```
|
||||||
|
|
||||||
=== "armv7/armhf"
|
=== "armv7/armhf"
|
||||||
```bash
|
```bash
|
||||||
wget https://github.com/binwiederhier/ntfy/releases/download/v1.5.3/ntfy_1.5.3_linux_armv7.tar.gz
|
wget https://github.com/binwiederhier/ntfy/releases/download/v1.6.1/ntfy_1.6.1_linux_armv7.tar.gz
|
||||||
sudo tar -C /usr/bin -zxf ntfy_*.tar.gz ntfy
|
sudo tar -C /usr/bin -zxf ntfy_*.tar.gz ntfy
|
||||||
sudo ./ntfy
|
sudo ./ntfy
|
||||||
```
|
```
|
||||||
|
|
||||||
=== "arm64"
|
=== "arm64"
|
||||||
```bash
|
```bash
|
||||||
wget https://github.com/binwiederhier/ntfy/releases/download/v1.5.3/ntfy_1.5.3_linux_arm64.tar.gz
|
wget https://github.com/binwiederhier/ntfy/releases/download/v1.6.1/ntfy_1.6.1_linux_arm64.tar.gz
|
||||||
sudo tar -C /usr/bin -zxf ntfy_*.tar.gz ntfy
|
sudo tar -C /usr/bin -zxf ntfy_*.tar.gz ntfy
|
||||||
sudo ./ntfy
|
sudo ./ntfy
|
||||||
```
|
```
|
||||||
@@ -82,7 +82,7 @@ Manually installing the .deb file:
|
|||||||
|
|
||||||
=== "x86_64/amd64"
|
=== "x86_64/amd64"
|
||||||
```bash
|
```bash
|
||||||
wget https://github.com/binwiederhier/ntfy/releases/download/v1.5.3/ntfy_1.5.3_linux_amd64.deb
|
wget https://github.com/binwiederhier/ntfy/releases/download/v1.6.1/ntfy_1.6.1_linux_amd64.deb
|
||||||
sudo dpkg -i ntfy_*.deb
|
sudo dpkg -i ntfy_*.deb
|
||||||
sudo systemctl enable ntfy
|
sudo systemctl enable ntfy
|
||||||
sudo systemctl start ntfy
|
sudo systemctl start ntfy
|
||||||
@@ -90,7 +90,7 @@ Manually installing the .deb file:
|
|||||||
|
|
||||||
=== "armv7/armhf"
|
=== "armv7/armhf"
|
||||||
```bash
|
```bash
|
||||||
wget https://github.com/binwiederhier/ntfy/releases/download/v1.5.3/ntfy_1.5.3_linux_armv7.deb
|
wget https://github.com/binwiederhier/ntfy/releases/download/v1.6.1/ntfy_1.6.1_linux_armv7.deb
|
||||||
sudo dpkg -i ntfy_*.deb
|
sudo dpkg -i ntfy_*.deb
|
||||||
sudo systemctl enable ntfy
|
sudo systemctl enable ntfy
|
||||||
sudo systemctl start ntfy
|
sudo systemctl start ntfy
|
||||||
@@ -98,7 +98,7 @@ Manually installing the .deb file:
|
|||||||
|
|
||||||
=== "arm64"
|
=== "arm64"
|
||||||
```bash
|
```bash
|
||||||
wget https://github.com/binwiederhier/ntfy/releases/download/v1.5.3/ntfy_1.5.3_linux_arm64.deb
|
wget https://github.com/binwiederhier/ntfy/releases/download/v1.6.1/ntfy_1.6.1_linux_arm64.deb
|
||||||
sudo dpkg -i ntfy_*.deb
|
sudo dpkg -i ntfy_*.deb
|
||||||
sudo systemctl enable ntfy
|
sudo systemctl enable ntfy
|
||||||
sudo systemctl start ntfy
|
sudo systemctl start ntfy
|
||||||
@@ -108,21 +108,21 @@ Manually installing the .deb file:
|
|||||||
|
|
||||||
=== "x86_64/amd64"
|
=== "x86_64/amd64"
|
||||||
```bash
|
```bash
|
||||||
sudo rpm -ivh https://github.com/binwiederhier/ntfy/releases/download/v1.5.3/ntfy_1.5.3_linux_amd64.rpm
|
sudo rpm -ivh https://github.com/binwiederhier/ntfy/releases/download/v1.6.1/ntfy_1.6.1_linux_amd64.rpm
|
||||||
sudo systemctl enable ntfy
|
sudo systemctl enable ntfy
|
||||||
sudo systemctl start ntfy
|
sudo systemctl start ntfy
|
||||||
```
|
```
|
||||||
|
|
||||||
=== "armv7/armhf"
|
=== "armv7/armhf"
|
||||||
```bash
|
```bash
|
||||||
sudo rpm -ivh https://github.com/binwiederhier/ntfy/releases/download/v1.5.3/ntfy_1.5.3_linux_armv7.rpm
|
sudo rpm -ivh https://github.com/binwiederhier/ntfy/releases/download/v1.6.1/ntfy_1.6.1_linux_armv7.rpm
|
||||||
sudo systemctl enable ntfy
|
sudo systemctl enable ntfy
|
||||||
sudo systemctl start ntfy
|
sudo systemctl start ntfy
|
||||||
```
|
```
|
||||||
|
|
||||||
=== "arm64"
|
=== "arm64"
|
||||||
```bash
|
```bash
|
||||||
sudo rpm -ivh https://github.com/binwiederhier/ntfy/releases/download/v1.5.3/ntfy_1.5.3_linux_arm64.rpm
|
sudo rpm -ivh https://github.com/binwiederhier/ntfy/releases/download/v1.6.1/ntfy_1.6.1_linux_arm64.rpm
|
||||||
sudo systemctl enable ntfy
|
sudo systemctl enable ntfy
|
||||||
sudo systemctl start ntfy
|
sudo systemctl start ntfy
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ Here's an example showing how to publish a simple message using a POST request:
|
|||||||
If you have the [Android app](subscribe/phone.md) installed on your phone, this will create a notification that looks like this:
|
If you have the [Android app](subscribe/phone.md) installed on your phone, this will create a notification that looks like this:
|
||||||
|
|
||||||
<figure markdown>
|
<figure markdown>
|
||||||
{ width=500 }
|
{ width=500 }
|
||||||
<figcaption>Android notification</figcaption>
|
<figcaption>Android notification</figcaption>
|
||||||
</figure>
|
</figure>
|
||||||
|
|
||||||
|
|||||||
BIN
docs/static/img/android-notification-settings.png
vendored
|
Before Width: | Height: | Size: 111 KiB After Width: | Height: | Size: 49 KiB |
BIN
docs/static/img/android-screenshot-add-instant.jpg
vendored
|
Before Width: | Height: | Size: 297 KiB |
BIN
docs/static/img/android-screenshot-add-instant.png
vendored
Normal file
|
After Width: | Height: | Size: 93 KiB |
BIN
docs/static/img/android-screenshot-add-other.jpg
vendored
|
Before Width: | Height: | Size: 300 KiB |
BIN
docs/static/img/android-screenshot-add-other.png
vendored
Normal file
|
After Width: | Height: | Size: 96 KiB |
BIN
docs/static/img/android-screenshot-add.jpg
vendored
|
Before Width: | Height: | Size: 236 KiB |
BIN
docs/static/img/android-screenshot-add.png
vendored
Normal file
|
After Width: | Height: | Size: 77 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
BIN
docs/static/img/android-screenshot-detail.jpg
vendored
|
Before Width: | Height: | Size: 255 KiB |
BIN
docs/static/img/android-screenshot-detail.png
vendored
Normal file
|
After Width: | Height: | Size: 74 KiB |
BIN
docs/static/img/android-screenshot-macrodroid-action.png
vendored
Normal file
|
After Width: | Height: | Size: 102 KiB |
BIN
docs/static/img/android-screenshot-macrodroid-overview.png
vendored
Normal file
|
After Width: | Height: | Size: 90 KiB |
BIN
docs/static/img/android-screenshot-macrodroid-send-action.png
vendored
Normal file
|
After Width: | Height: | Size: 83 KiB |
BIN
docs/static/img/android-screenshot-macrodroid-send-macro.png
vendored
Normal file
|
After Width: | Height: | Size: 77 KiB |
BIN
docs/static/img/android-screenshot-macrodroid-trigger.png
vendored
Normal file
|
After Width: | Height: | Size: 95 KiB |
BIN
docs/static/img/android-screenshot-main.jpg
vendored
|
Before Width: | Height: | Size: 149 KiB |
BIN
docs/static/img/android-screenshot-main.png
vendored
Normal file
|
After Width: | Height: | Size: 53 KiB |
BIN
docs/static/img/android-screenshot-muted.png
vendored
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
docs/static/img/android-screenshot-pause.jpg
vendored
|
Before Width: | Height: | Size: 212 KiB |
BIN
docs/static/img/android-screenshot-pause.png
vendored
Normal file
|
After Width: | Height: | Size: 74 KiB |
BIN
docs/static/img/android-screenshot-tasker-action-edit.png
vendored
Normal file
|
After Width: | Height: | Size: 60 KiB |
BIN
docs/static/img/android-screenshot-tasker-action-http-post.png
vendored
Normal file
|
After Width: | Height: | Size: 110 KiB |
BIN
docs/static/img/android-screenshot-tasker-event-edit.png
vendored
Normal file
|
After Width: | Height: | Size: 74 KiB |
BIN
docs/static/img/android-screenshot-tasker-profile-send.png
vendored
Normal file
|
After Width: | Height: | Size: 51 KiB |
BIN
docs/static/img/android-screenshot-tasker-profiles.png
vendored
Normal file
|
After Width: | Height: | Size: 56 KiB |
BIN
docs/static/img/android-screenshot-tasker-task-edit-post.png
vendored
Normal file
|
After Width: | Height: | Size: 48 KiB |
BIN
docs/static/img/android-screenshot-tasker-task-edit.png
vendored
Normal file
|
After Width: | Height: | Size: 55 KiB |
BIN
docs/static/img/overview.gif
vendored
|
Before Width: | Height: | Size: 3.7 MiB |
BIN
docs/static/img/overview.mp4
vendored
@@ -3,7 +3,6 @@ You can use the [ntfy Android App](https://play.google.com/store/apps/details?id
|
|||||||
notifications directly on your phone. Just like the server, this app is also [open source](https://github.com/binwiederhier/ntfy-android).
|
notifications directly on your phone. Just like the server, this app is also [open source](https://github.com/binwiederhier/ntfy-android).
|
||||||
Since I don't have an iPhone or a Mac, I didn't make an iOS app yet. I'd be awesome if [someone else could help out](https://github.com/binwiederhier/ntfy/issues/4).
|
Since I don't have an iPhone or a Mac, I didn't make an iOS app yet. I'd be awesome if [someone else could help out](https://github.com/binwiederhier/ntfy/issues/4).
|
||||||
|
|
||||||
## Android
|
|
||||||
<a href="https://play.google.com/store/apps/details?id=io.heckel.ntfy"><img src="../../static/img/badge-googleplay.png"></a>
|
<a href="https://play.google.com/store/apps/details?id=io.heckel.ntfy"><img src="../../static/img/badge-googleplay.png"></a>
|
||||||
<a href="https://f-droid.org/en/packages/io.heckel.ntfy/"><img src="../../static/img/badge-fdroid.png"></a>
|
<a href="https://f-droid.org/en/packages/io.heckel.ntfy/"><img src="../../static/img/badge-fdroid.png"></a>
|
||||||
|
|
||||||
@@ -11,26 +10,27 @@ You can get the Android app from both [Google Play](https://play.google.com/stor
|
|||||||
from [F-Droid](https://f-droid.org/en/packages/io.heckel.ntfy/). Both are largely identical, with the one exception that
|
from [F-Droid](https://f-droid.org/en/packages/io.heckel.ntfy/). Both are largely identical, with the one exception that
|
||||||
the F-Droid flavor does not use Firebase.
|
the F-Droid flavor does not use Firebase.
|
||||||
|
|
||||||
### Overview
|
## Overview
|
||||||
A picture is worth a thousand words. Here are a few screenshots showing what the app looks like. It's all pretty
|
A picture is worth a thousand words. Here are a few screenshots showing what the app looks like. It's all pretty
|
||||||
straight forward. You can add topics and as soon as you add them, you can [publish messages](../publish.md) to them.
|
straight forward. You can add topics and as soon as you add them, you can [publish messages](../publish.md) to them.
|
||||||
|
|
||||||
<div id="android-screenshots" class="screenshots">
|
<div id="android-screenshots" class="screenshots">
|
||||||
<a href="../../static/img/android-screenshot-main.jpg"><img src="../../static/img/android-screenshot-main.jpg"/></a>
|
<a href="../../static/img/android-screenshot-main.png"><img src="../../static/img/android-screenshot-main.png"/></a>
|
||||||
<a href="../../static/img/android-screenshot-detail.jpg"><img src="../../static/img/android-screenshot-detail.jpg"/></a>
|
<a href="../../static/img/android-screenshot-detail.png"><img src="../../static/img/android-screenshot-detail.png"/></a>
|
||||||
<a href="../../static/img/android-screenshot-add.jpg"><img src="../../static/img/android-screenshot-add.jpg"/></a>
|
<a href="../../static/img/android-screenshot-pause.png"><img src="../../static/img/android-screenshot-pause.png"/></a>
|
||||||
<a href="../../static/img/android-screenshot-add-instant.jpg"><img src="../../static/img/android-screenshot-add-instant.jpg"/></a>
|
<a href="../../static/img/android-screenshot-add.png"><img src="../../static/img/android-screenshot-add.png"/></a>
|
||||||
<a href="../../static/img/android-screenshot-add-other.jpg"><img src="../../static/img/android-screenshot-add-other.jpg"/></a>
|
<a href="../../static/img/android-screenshot-add-instant.png"><img src="../../static/img/android-screenshot-add-instant.png"/></a>
|
||||||
|
<a href="../../static/img/android-screenshot-add-other.png"><img src="../../static/img/android-screenshot-add-other.png"/></a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
If those screenshots are still not enough, here's a video:
|
If those screenshots are still not enough, here's a video:
|
||||||
|
|
||||||
<figure>
|
<figure>
|
||||||
<video controls muted autoplay loop width="650" src="../../static/img/overview.mp4"></video>
|
<video controls muted autoplay loop width="650" src="../../static/img/android-video-overview.mp4"></video>
|
||||||
<figcaption>Sending push notifications to your Android phone</figcaption>
|
<figcaption>Sending push notifications to your Android phone</figcaption>
|
||||||
</figure>
|
</figure>
|
||||||
|
|
||||||
### Message priority
|
## Message priority
|
||||||
When you [publish messages](../publish.md#message-priority) to a topic, you can define a priority. This priority defines
|
When you [publish messages](../publish.md#message-priority) to a topic, you can define a priority. This priority defines
|
||||||
how urgently Android will notify you about the notification, and whether they make a sound and/or vibrate.
|
how urgently Android will notify you about the notification, and whether they make a sound and/or vibrate.
|
||||||
|
|
||||||
@@ -50,7 +50,7 @@ the settings (and custom sounds or vibration) for each of the priorities:
|
|||||||
<figcaption>Per-priority sound/vibration settings</figcaption>
|
<figcaption>Per-priority sound/vibration settings</figcaption>
|
||||||
</figure>
|
</figure>
|
||||||
|
|
||||||
### Instant delivery
|
## Instant delivery
|
||||||
Instant delivery allows you to receive messages on your phone instantly, **even when your phone is in doze mode**, i.e.
|
Instant delivery allows you to receive messages on your phone instantly, **even when your phone is in doze mode**, i.e.
|
||||||
when the screen turns off, and you leave it on the desk for a while. This is achieved with a foreground service, which
|
when the screen turns off, and you leave it on the desk for a while. This is achieved with a foreground service, which
|
||||||
you'll see as a permanent notification that looks like this:
|
you'll see as a permanent notification that looks like this:
|
||||||
@@ -69,8 +69,8 @@ To do so, long-press on the foreground notification (screenshot above) and navig
|
|||||||
<figcaption>Turning off the persistent instant delivery notification</figcaption>
|
<figcaption>Turning off the persistent instant delivery notification</figcaption>
|
||||||
</figure>
|
</figure>
|
||||||
|
|
||||||
### Limitations without instant delivery
|
**Limitations without instant delivery**: Without instant delivery, **messages may arrive with a significant delay**
|
||||||
Without instant delivery, **messages may arrive with a significant delay** (sometimes many minutes, or even hours later). If you've ever picked up your phone and
|
(sometimes many minutes, or even hours later). If you've ever picked up your phone and
|
||||||
suddenly had 10 messages that were sent long before you know what I'm talking about.
|
suddenly had 10 messages that were sent long before you know what I'm talking about.
|
||||||
|
|
||||||
The reason for this is [Firebase Cloud Messaging (FCM)](https://firebase.google.com/docs/cloud-messaging). FCM is the
|
The reason for this is [Firebase Cloud Messaging (FCM)](https://firebase.google.com/docs/cloud-messaging). FCM is the
|
||||||
@@ -80,6 +80,82 @@ notifications. Firebase is overall pretty bad at delivering messages in time, bu
|
|||||||
The ntfy Android app uses Firebase only for the main host `ntfy.sh`, and only in the Google Play flavor of the app.
|
The ntfy Android app uses Firebase only for the main host `ntfy.sh`, and only in the Google Play flavor of the app.
|
||||||
It won't use Firebase for any self-hosted servers, and not at all in the the F-Droid flavor.
|
It won't use Firebase for any self-hosted servers, and not at all in the the F-Droid flavor.
|
||||||
|
|
||||||
|
## Integrations
|
||||||
|
The ntfy Android app integrates nicely with automation apps such as [MacroDroid](https://play.google.com/store/apps/details?id=com.arlosoft.macrodroid)
|
||||||
|
or [Tasker](https://play.google.com/store/apps/details?id=net.dinglisch.android.taskerm). Using Android intents, you can
|
||||||
|
**react to incoming messages**, as well as **send messages**.
|
||||||
|
|
||||||
|
### React to incoming messages
|
||||||
|
To react on incoming notifications, you have to register to intents with the `io.heckel.ntfy.MESSAGE_RECEIVED` action (see
|
||||||
|
[code for details](https://github.com/binwiederhier/ntfy-android/blob/main/app/src/main/java/io/heckel/ntfy/msg/BroadcastService.kt)).
|
||||||
|
Here's an example using [MacroDroid](https://play.google.com/store/apps/details?id=com.arlosoft.macrodroid)
|
||||||
|
and [Tasker](https://play.google.com/store/apps/details?id=net.dinglisch.android.taskerm), but any app that can catch
|
||||||
|
broadcasts is supported:
|
||||||
|
|
||||||
|
<div id="integration-screenshots-receive" class="screenshots">
|
||||||
|
<a href="../../static/img/android-screenshot-macrodroid-overview.png"><img src="../../static/img/android-screenshot-macrodroid-overview.png"/></a>
|
||||||
|
<a href="../../static/img/android-screenshot-macrodroid-trigger.png"><img src="../../static/img/android-screenshot-macrodroid-trigger.png"/></a>
|
||||||
|
<a href="../../static/img/android-screenshot-macrodroid-action.png"><img src="../../static/img/android-screenshot-macrodroid-action.png"/></a>
|
||||||
|
<a href="../../static/img/android-screenshot-tasker-profiles.png"><img src="../../static/img/android-screenshot-tasker-profiles.png"/></a>
|
||||||
|
<a href="../../static/img/android-screenshot-tasker-event-edit.png"><img src="../../static/img/android-screenshot-tasker-event-edit.png"/></a>
|
||||||
|
<a href="../../static/img/android-screenshot-tasker-task-edit.png"><img src="../../static/img/android-screenshot-tasker-task-edit.png"/></a>
|
||||||
|
<a href="../../static/img/android-screenshot-tasker-action-edit.png"><img src="../../static/img/android-screenshot-tasker-action-edit.png"/></a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
For MacroDroid, be sure to type in the package name `io.heckel.ntfy`, otherwise intents may be silently swallowed.
|
||||||
|
If you're using topics to drive automation, you'll likely want to mute the topic in the ntfy app. This will prevent
|
||||||
|
notification popups:
|
||||||
|
|
||||||
|
<figure markdown>
|
||||||
|
{ width=500 }
|
||||||
|
<figcaption>Muting notifications to prevent popups</figcaption>
|
||||||
|
</figure>
|
||||||
|
|
||||||
|
Here's a list of extras you can access. Most likely, you'll want to filter for `topic` and react on `message`:
|
||||||
|
|
||||||
|
| Extra name | Type | Example | Description |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `id` | *string* | `bP8dMjO8ig` | Randomly chosen message identifier (likely not very useful for task automation) |
|
||||||
|
| `base_url` | *string* | `https://ntfy.sh` | Root URL of the ntfy server this message came from |
|
||||||
|
| `topic` ❤️ | *string* | `mytopic` | Topic name; **you'll likely want to filter for a specific topic** |
|
||||||
|
| `muted` | *bool* | `true` | Indicates whether the subscription was muted in the app |
|
||||||
|
| `muted_str` | *string (`true` or `false`)* | `true` | Same as `muted`, but as string `true` or `false` |
|
||||||
|
| `time` | *int* | `1635528741` | Message date time, as Unix time stamp |
|
||||||
|
| `title` | *string* | `Some title` | Message [title](../publish.md#message-title); may be empty if not set |
|
||||||
|
| `message` ❤️ | *string* | `Some message` | Message body; **this is likely what you're interested in** |
|
||||||
|
| `tags` | *string* | `tag1,tag2,..` | Comma-separated list of [tags](../publish.md#tags-emojis) |
|
||||||
|
| `tags_map` | *string* | `0=tag1,1=tag2,..` | Map of tags to make it easier to map first, second, ... tag |
|
||||||
|
| `priority` | *int (between 1-5)* | `4` | Message [priority](../publish.md#message-priority) with 1=min, 3=default and 5=max |
|
||||||
|
|
||||||
|
### Send messages using intents
|
||||||
|
To send messages from other apps (such as [MacroDroid](https://play.google.com/store/apps/details?id=com.arlosoft.macrodroid)
|
||||||
|
and [Tasker](https://play.google.com/store/apps/details?id=net.dinglisch.android.taskerm)), you can
|
||||||
|
broadcast an intent with the `io.heckel.ntfy.SEND_MESSAGE` action. The ntfy Android app will forward the intent as a HTTP
|
||||||
|
POST request to [publish a message](../publish.md). This is primarily useful for apps that do not support HTTP POST/PUT
|
||||||
|
(like MacroDroid). In Tasker, you can simply use the "HTTP Request" action, which is a little easier and also works if
|
||||||
|
ntfy is not installed.
|
||||||
|
|
||||||
|
Here's what that looks like:
|
||||||
|
|
||||||
|
<div id="integration-screenshots-send" class="screenshots">
|
||||||
|
<a href="../../static/img/android-screenshot-macrodroid-send-macro.png"><img src="../../static/img/android-screenshot-macrodroid-send-macro.png"/></a>
|
||||||
|
<a href="../../static/img/android-screenshot-macrodroid-send-action.png"><img src="../../static/img/android-screenshot-macrodroid-send-action.png"/></a>
|
||||||
|
<a href="../../static/img/android-screenshot-tasker-profile-send.png"><img src="../../static/img/android-screenshot-tasker-profile-send.png"/></a>
|
||||||
|
<a href="../../static/img/android-screenshot-tasker-task-edit-post.png"><img src="../../static/img/android-screenshot-tasker-task-edit-post.png"/></a>
|
||||||
|
<a href="../../static/img/android-screenshot-tasker-action-http-post.png"><img src="../../static/img/android-screenshot-tasker-action-http-post.png"/></a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
The following intent extras are supported when for the intent with the `io.heckel.ntfy.SEND_MESSAGE` action:
|
||||||
|
|
||||||
|
| Extra name | Required | Type | Example | Description |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| `base_url` | - | *string* | `https://ntfy.sh` | Root URL of the ntfy server this message came from, defaults to `https://ntfy.sh` |
|
||||||
|
| `topic` ❤️ | ✔ | *string* | `mytopic` | Topic name; **you must set this** |
|
||||||
|
| `title` | - | *string* | `Some title` | Message [title](../publish.md#message-title); may be empty if not set |
|
||||||
|
| `message` ❤️ | ✔ | *string* | `Some message` | Message body; **you must set this** |
|
||||||
|
| `tags` | - | *string* | `tag1,tag2,..` | Comma-separated list of [tags](../publish.md#tags-emojis) |
|
||||||
|
| `priority` | - | *string or int (between 1-5)* | `4` | Message [priority](../publish.md#message-priority) with 1=min, 3=default and 5=max |
|
||||||
|
|
||||||
## iPhone/iOS
|
## iPhone/iOS
|
||||||
I almost feel devious for putting the *Download on the App Store* button on this page. Currently, there is no iOS app
|
I almost feel devious for putting the *Download on the App Store* button on this page. Currently, there is no iOS app
|
||||||
for ntfy, but it's in the works. You can track the status on GitHub.
|
for ntfy, but it's in the works. You can track the status on GitHub.
|
||||||
|
|||||||
@@ -105,6 +105,10 @@ var (
|
|||||||
errHTTPTooManyRequests = &errHTTP{http.StatusTooManyRequests, http.StatusText(http.StatusTooManyRequests)}
|
errHTTPTooManyRequests = &errHTTP{http.StatusTooManyRequests, http.StatusText(http.StatusTooManyRequests)}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
firebaseControlTopic = "~control" // See Android if changed
|
||||||
|
)
|
||||||
|
|
||||||
// New instantiates a new Server. It creates the cache and adds a Firebase
|
// New instantiates a new Server. It creates the cache and adds a Firebase
|
||||||
// subscriber (if configured).
|
// subscriber (if configured).
|
||||||
func New(conf *config.Config) (*Server, error) {
|
func New(conf *config.Config) (*Server, error) {
|
||||||
@@ -152,9 +156,17 @@ func createFirebaseSubscriber(conf *config.Config) (subscriber, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return func(m *message) error {
|
return func(m *message) error {
|
||||||
_, err := msg.Send(context.Background(), &messaging.Message{
|
var data map[string]string // Matches https://ntfy.sh/docs/subscribe/api/#json-message-format
|
||||||
Topic: m.Topic,
|
switch m.Event {
|
||||||
Data: map[string]string{
|
case keepaliveEvent, openEvent:
|
||||||
|
data = map[string]string{
|
||||||
|
"id": m.ID,
|
||||||
|
"time": fmt.Sprintf("%d", m.Time),
|
||||||
|
"event": m.Event,
|
||||||
|
"topic": m.Topic,
|
||||||
|
}
|
||||||
|
case messageEvent:
|
||||||
|
data = map[string]string{
|
||||||
"id": m.ID,
|
"id": m.ID,
|
||||||
"time": fmt.Sprintf("%d", m.Time),
|
"time": fmt.Sprintf("%d", m.Time),
|
||||||
"event": m.Event,
|
"event": m.Event,
|
||||||
@@ -163,7 +175,11 @@ func createFirebaseSubscriber(conf *config.Config) (subscriber, error) {
|
|||||||
"tags": strings.Join(m.Tags, ","),
|
"tags": strings.Join(m.Tags, ","),
|
||||||
"title": m.Title,
|
"title": m.Title,
|
||||||
"message": m.Message,
|
"message": m.Message,
|
||||||
},
|
}
|
||||||
|
}
|
||||||
|
_, err := msg.Send(context.Background(), &messaging.Message{
|
||||||
|
Topic: m.Topic,
|
||||||
|
Data: data,
|
||||||
})
|
})
|
||||||
return err
|
return err
|
||||||
}, nil
|
}, nil
|
||||||
@@ -188,6 +204,17 @@ func (s *Server) Run() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
if s.firebase != nil {
|
||||||
|
go func() {
|
||||||
|
ticker := time.NewTicker(s.config.FirebaseKeepaliveInterval)
|
||||||
|
for {
|
||||||
|
<-ticker.C
|
||||||
|
if err := s.firebase(newKeepaliveMessage(firebaseControlTopic)); err != nil {
|
||||||
|
log.Printf("error sending Firebase keepalive message: %s", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
listenStr := fmt.Sprintf("%s/http", s.config.ListenHTTP)
|
listenStr := fmt.Sprintf("%s/http", s.config.ListenHTTP)
|
||||||
if s.config.ListenHTTPS != "" {
|
if s.config.ListenHTTPS != "" {
|
||||||
listenStr += fmt.Sprintf(" %s/https", s.config.ListenHTTPS)
|
listenStr += fmt.Sprintf(" %s/https", s.config.ListenHTTPS)
|
||||||
|
|||||||
50
tools/fbsend/main.go
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
firebase "firebase.google.com/go"
|
||||||
|
"firebase.google.com/go/messaging"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"google.golang.org/api/option"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
conffile := flag.String("config", "/etc/fbsend/fbsend.json", "config file")
|
||||||
|
flag.Parse()
|
||||||
|
if flag.NArg() < 2 {
|
||||||
|
fail("Syntax: fbsend [-config FILE] topic key=value ...")
|
||||||
|
}
|
||||||
|
topic := flag.Arg(0)
|
||||||
|
data := make(map[string]string)
|
||||||
|
for i := 1; i < flag.NArg(); i++ {
|
||||||
|
kv := strings.SplitN(flag.Arg(i), "=", 2)
|
||||||
|
if len(kv) != 2 {
|
||||||
|
fail(fmt.Sprintf("Invalid argument: %s (%v)", flag.Arg(i), kv))
|
||||||
|
}
|
||||||
|
data[kv[0]] = kv[1]
|
||||||
|
}
|
||||||
|
fb, err := firebase.NewApp(context.Background(), nil, option.WithCredentialsFile(*conffile))
|
||||||
|
if err != nil {
|
||||||
|
fail(err.Error())
|
||||||
|
}
|
||||||
|
msg, err := fb.Messaging(context.Background())
|
||||||
|
if err != nil {
|
||||||
|
fail(err.Error())
|
||||||
|
}
|
||||||
|
_, err = msg.Send(context.Background(), &messaging.Message{
|
||||||
|
Topic: topic,
|
||||||
|
Data: data,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
fail(err.Error())
|
||||||
|
}
|
||||||
|
fmt.Println("Sent successfully")
|
||||||
|
}
|
||||||
|
|
||||||
|
func fail(s string) {
|
||||||
|
fmt.Println(s)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||