Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bd09fb4c54 | ||
|
|
63206f8581 | ||
|
|
de0c41ec3b | ||
|
|
eaefb436d6 | ||
|
|
5843de5dfc | ||
|
|
6abda93a14 |
1
.gitignore
vendored
@@ -2,4 +2,5 @@ dist/
|
||||
build/
|
||||
.idea/
|
||||
server/docs/
|
||||
tools/fbsend/fbsend
|
||||
*.iml
|
||||
|
||||
@@ -7,14 +7,15 @@ import (
|
||||
|
||||
// Defines default config settings
|
||||
const (
|
||||
DefaultListenHTTP = ":80"
|
||||
DefaultCacheDuration = 12 * time.Hour
|
||||
DefaultKeepaliveInterval = 30 * time.Second
|
||||
DefaultManagerInterval = time.Minute
|
||||
DefaultAtSenderInterval = 10 * time.Second
|
||||
DefaultMinDelay = 10 * time.Second
|
||||
DefaultMaxDelay = 3 * 24 * time.Hour
|
||||
DefaultMessageLimit = 512
|
||||
DefaultListenHTTP = ":80"
|
||||
DefaultCacheDuration = 12 * time.Hour
|
||||
DefaultKeepaliveInterval = 30 * time.Second
|
||||
DefaultManagerInterval = time.Minute
|
||||
DefaultAtSenderInterval = 10 * time.Second
|
||||
DefaultMinDelay = 10 * time.Second
|
||||
DefaultMaxDelay = 3 * 24 * time.Hour
|
||||
DefaultMessageLimit = 512
|
||||
DefaultFirebaseKeepaliveInterval = time.Hour
|
||||
)
|
||||
|
||||
// Defines all the limits
|
||||
@@ -40,6 +41,7 @@ type Config struct {
|
||||
KeepaliveInterval time.Duration
|
||||
ManagerInterval time.Duration
|
||||
AtSenderInterval time.Duration
|
||||
FirebaseKeepaliveInterval time.Duration
|
||||
MessageLimit int
|
||||
MinDelay time.Duration
|
||||
MaxDelay time.Duration
|
||||
@@ -66,6 +68,7 @@ func New(listenHTTP string) *Config {
|
||||
MinDelay: DefaultMinDelay,
|
||||
MaxDelay: DefaultMaxDelay,
|
||||
AtSenderInterval: DefaultAtSenderInterval,
|
||||
FirebaseKeepaliveInterval: DefaultFirebaseKeepaliveInterval,
|
||||
GlobalTopicLimit: DefaultGlobalTopicLimit,
|
||||
VisitorRequestLimitBurst: DefaultVisitorRequestLimitBurst,
|
||||
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:
|
||||
|
||||
<figure markdown>
|
||||
{ width=500 }
|
||||
{ width=500 }
|
||||
<figcaption>Android notification</figcaption>
|
||||
</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:
|
||||
|
||||
<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>
|
||||
</figure>
|
||||
|
||||
|
||||
@@ -20,21 +20,21 @@ deb/rpm packages.
|
||||
|
||||
=== "x86_64/amd64"
|
||||
```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 ./ntfy
|
||||
```
|
||||
|
||||
=== "armv7/armhf"
|
||||
```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 ./ntfy
|
||||
```
|
||||
|
||||
=== "arm64"
|
||||
```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 ./ntfy
|
||||
```
|
||||
@@ -82,7 +82,7 @@ Manually installing the .deb file:
|
||||
|
||||
=== "x86_64/amd64"
|
||||
```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 systemctl enable ntfy
|
||||
sudo systemctl start ntfy
|
||||
@@ -90,7 +90,7 @@ Manually installing the .deb file:
|
||||
|
||||
=== "armv7/armhf"
|
||||
```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 systemctl enable ntfy
|
||||
sudo systemctl start ntfy
|
||||
@@ -98,7 +98,7 @@ Manually installing the .deb file:
|
||||
|
||||
=== "arm64"
|
||||
```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 systemctl enable ntfy
|
||||
sudo systemctl start ntfy
|
||||
@@ -108,21 +108,21 @@ Manually installing the .deb file:
|
||||
|
||||
=== "x86_64/amd64"
|
||||
```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 start ntfy
|
||||
```
|
||||
|
||||
=== "armv7/armhf"
|
||||
```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 start ntfy
|
||||
```
|
||||
|
||||
=== "arm64"
|
||||
```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 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:
|
||||
|
||||
<figure markdown>
|
||||
{ width=500 }
|
||||
{ width=500 }
|
||||
<figcaption>Android notification</figcaption>
|
||||
</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).
|
||||
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://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
|
||||
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
|
||||
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">
|
||||
<a href="../../static/img/android-screenshot-main.jpg"><img src="../../static/img/android-screenshot-main.jpg"/></a>
|
||||
<a href="../../static/img/android-screenshot-detail.jpg"><img src="../../static/img/android-screenshot-detail.jpg"/></a>
|
||||
<a href="../../static/img/android-screenshot-add.jpg"><img src="../../static/img/android-screenshot-add.jpg"/></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-other.jpg"><img src="../../static/img/android-screenshot-add-other.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.png"><img src="../../static/img/android-screenshot-detail.png"/></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.png"><img src="../../static/img/android-screenshot-add.png"/></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>
|
||||
|
||||
If those screenshots are still not enough, here's a video:
|
||||
|
||||
<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>
|
||||
</figure>
|
||||
|
||||
### Message priority
|
||||
## Message priority
|
||||
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.
|
||||
|
||||
@@ -50,7 +50,7 @@ the settings (and custom sounds or vibration) for each of the priorities:
|
||||
<figcaption>Per-priority sound/vibration settings</figcaption>
|
||||
</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.
|
||||
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:
|
||||
@@ -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>
|
||||
</figure>
|
||||
|
||||
### Limitations without instant delivery
|
||||
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
|
||||
**Limitations without instant delivery**: 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
|
||||
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
|
||||
@@ -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.
|
||||
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
|
||||
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.
|
||||
|
||||
@@ -105,6 +105,10 @@ var (
|
||||
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
|
||||
// subscriber (if configured).
|
||||
func New(conf *config.Config) (*Server, error) {
|
||||
@@ -152,9 +156,17 @@ func createFirebaseSubscriber(conf *config.Config) (subscriber, error) {
|
||||
return nil, err
|
||||
}
|
||||
return func(m *message) error {
|
||||
_, err := msg.Send(context.Background(), &messaging.Message{
|
||||
Topic: m.Topic,
|
||||
Data: map[string]string{
|
||||
var data map[string]string // Matches https://ntfy.sh/docs/subscribe/api/#json-message-format
|
||||
switch m.Event {
|
||||
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,
|
||||
"time": fmt.Sprintf("%d", m.Time),
|
||||
"event": m.Event,
|
||||
@@ -163,7 +175,11 @@ func createFirebaseSubscriber(conf *config.Config) (subscriber, error) {
|
||||
"tags": strings.Join(m.Tags, ","),
|
||||
"title": m.Title,
|
||||
"message": m.Message,
|
||||
},
|
||||
}
|
||||
}
|
||||
_, err := msg.Send(context.Background(), &messaging.Message{
|
||||
Topic: m.Topic,
|
||||
Data: data,
|
||||
})
|
||||
return err
|
||||
}, 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)
|
||||
if 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)
|
||||
}
|
||||