Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Bark integration #160

Merged
merged 1 commit into from
Mar 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ Noti can send notifications on a number of services.
| Twilio | ✔ | ✔ | ✔ |
| GChat | ✔ | ✔ | ✔ |
| Chanify | ✔ | ✔ | ✔ |
| Bark | ✔ | ✔ | ✔ |

Check the [screenshots] directory to see what the notifications look like on different platforms.

Expand Down
23 changes: 23 additions & 0 deletions docs/noti.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ Noti can send notifications on a number of services.
| Twilio | ✔ | ✔ | ✔ |
| GChat | ✔ | ✔ | ✔ |
| ntfy | ✔ | ✔ | ✔ |
| Bark | ✔ | ✔ | ✔ |


## Installation
Expand Down Expand Up @@ -127,6 +128,10 @@ curl -L $(curl -s https://api.github.com/repos/variadico/noti/releases/latest |
Trigger a ntfy notification. This requires `ntfy.topic` be set. Optionally,
`ntfy.url` can also be set to use a different ntfy server.

--bark
Trigger a Bark notification. This requires `bark.key` to be set. Optionally,
`bark.apiurl` can also be set to use a different bark server.

-w , --pwatch
Monitor a process by PID and trigger a notification when the pid
disappears.
Expand Down Expand Up @@ -182,6 +187,8 @@ curl -L $(curl -s https://api.github.com/repos/variadico/noti/releases/latest |
* `NOTI_CHANIFY_SOUND`
* `NOTI_CHANIFY_PRIORITY`
* `NOTI_CHANIFY_INTERUPTIONLEVEL`
* `NOTI_BARK_KEY`
* `NOTI_BARK_APIURL`


## Files
Expand Down Expand Up @@ -354,6 +361,14 @@ url
topic
Topic ID to send messages to

Bark

apiurl
Bark server URL. Defaults to https://api.day.app/push

key
Device Key to send messages to

```

## Examples
Expand Down Expand Up @@ -434,6 +449,9 @@ chanify:
ntfy:
url: https://my.ntfy.url.com
topic: 'xxxxxxxxxxxxxxxx'
bark:
url: https://my.bark.url.com
key: '1234567890abcdefg'
```

## Setting up cloud accounts
Expand Down Expand Up @@ -503,6 +521,11 @@ Configure a Google Spaces webhook (see [setting up webhooks](https://developers.
Open up Chanify on your device, pick a channel and create a token. You have an option of token with best-by date or token with no expiration. Set the token as `chanify.channelURL` with the default host `api.chanify.net`. Your `chanify.channelURL` should look like this: `https://api.chanify.net/v1/sender/<TOKEN>`
To create token's expiration click on the ![Shield button](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB0AAAAcCAYAAACdz7SqAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAHaADAAQAAAABAAAAHAAAAADjGz/hAAADAklEQVRIDeVVTShEURT+5keJkgXSFJlSFhSRNNlYkGLBLDVFWVIaYaMkpSibWdhZMQ2JjYWIMpOiJguUn2k2itUsSKKmEcc9J2+aN97MvEnZOPV6752/75x77/muBQCp50/F+qdo32D/B9Sez/LW1NSgq6sLjY2NeHx8RCgUwsnJCRKJRD5pYFHeWQ9SVVUVenp65Kmrq5PkT09PKCkpgd1ux+vrKyYnJwX8/f3dFLghaGFhIfr6+uB2u9HU1CSJYrEYDg4OsLe3h/PzcwHt7u5Gb28v2tra8PLygv39fWxubuL6+jonOHeqewKBAEUiETo+Pqbp6Wlqbm4mi8Wi80mN8Xg8tLa2Rjc3N3R7e0sdHR0Zfb/j9ICs5EC/309WqzVXsM7OxXGx4+PjOn1qgfxtODKqKzw/P+Pz81P5ZBdVGJxOpzjxXrNwfDbJ6/QaJRocHJTTe3d3Z2Q21P0KlEfI6/WipaVFkptZGXY0XF7D8tKUvKyLi4uylB8fH2nW7L+mQPv7+8HzmirDw8MyTj6fL6nmQsxITi+e2bm5OaiRQFlZmeSsra3F2NgYzs7OsLq6agZH55MRVKs6Ho9DzSoqKyuxvLyMoqIiLCwsgNmH9an7qMUQ8cRkFsODxNSmdcWhu7u7cDgcmJiYwM7Ojiz1zMwMHh4edJnLy8vl/+3tTadP/zHsNBqNoqGhQbrTAlZWVrC+vi6ATPRbW1uaKfnu7OyUb47PJT/YQ/GuMIvaL1J7mrQzFTLrpOpUcrG3t7fT1dUVHR4eUkFBQTJGs6e9f9IgJ19aWhLg7e1tqq6uzppkaGhIAC8uLkhde1l9v8F/grLBZrPR1NSU8PDl5SWNjIwYdlhfXy/FBYNBUltiBpB9jEE1fWtrK6nrTBIfHR2RusoksTpoND8/LzfL7OwsFRcXmwUkZmZBZvRMwpf1wMAARkdHUVpaivv7e1RUVIBn+PT0FEwU+YrpCrkbRQoUDodpY2ODXC6X6VhVVNLXVKf5dpHL33BOcwX91v5/QL8AnJm6qDdN5A0AAAAASUVORK5CYII=) Shield button

### Bark

Open the Bark APP and copy the test URL like `https://api.day.app/your_key` (see [Bark tutorial](https://bark.day.app/#/en-us/tutorial)).
Next, fill `your_key` in `bark.key` to.

## Reporting bugs

Report bugs on GitHub at https://github.com/variadico/noti/issues.
Expand Down
11 changes: 11 additions & 0 deletions internal/command/cloud.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"time"

"github.com/spf13/viper"
"github.com/variadico/noti/service/bark"
"github.com/variadico/noti/service/bearychat"
"github.com/variadico/noti/service/chanify"
"github.com/variadico/noti/service/gchat"
Expand Down Expand Up @@ -190,3 +191,13 @@ func getNtfy(title, message string, v *viper.Viper) notification {
Client: httpClient,
}
}

func getBark(title, message string, v *viper.Viper) notification {
return &bark.Notification{
URL: v.GetString("bark.apiurl"),
DeviceKey: v.GetString("bark.key"),
Title: title,
Body: message,
Client: httpClient,
}
}
13 changes: 13 additions & 0 deletions internal/command/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ var baseDefaults = map[string]interface{}{

"ntfy.url": "https://ntfy.sh/",
"ntfy.topic": "",

"bark.apiurl": "https://api.day.app/push",
"bark.key": "",
}

func setNotiDefaults(v *viper.Viper) {
Expand Down Expand Up @@ -157,6 +160,9 @@ var keyEnvBindings = map[string]string{

"ntfy.url": "NOTI_NTFY_URL",
"ntfy.topic": "NOTI_NTFY_TOPIC",

"bark.apiurl": "NOTI_BARK_APIURL",
"bark.key": "NOTI_BARK_KEY",
}

var keyEnvBindingsDeprecated = map[string]string{
Expand Down Expand Up @@ -282,6 +288,7 @@ func enabledFromSlice(defaults []string) map[string]bool {
"twilio": false,
"chanify": false,
"ntfy": false,
"bark": false,
}

for _, name := range defaults {
Expand Down Expand Up @@ -313,6 +320,7 @@ func hasServiceFlags(flags *pflag.FlagSet) bool {
"twilio": false,
"chanify": false,
"ntfy": false,
"bark": false,
}

flags.Visit(func(f *pflag.Flag) {
Expand Down Expand Up @@ -347,6 +355,7 @@ func enabledFromFlags(flags *pflag.FlagSet) map[string]bool {
"twilio": false,
"chanify": false,
"ntfy": false,
"bark": false,
}

// Visit flags that have been set.
Expand Down Expand Up @@ -459,5 +468,9 @@ func getNotifications(v *viper.Viper, services map[string]struct{}) []notificati
notis = append(notis, getNtfy(title, message, v))
}

if _, ok := services["bark"]; ok {
notis = append(notis, getBark(title, message, v))
}

return notis
}
1 change: 1 addition & 0 deletions internal/command/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ func InitFlags(flags *pflag.FlagSet) {
flags.BoolP("zulip", "z", false, "Trigger a Zulip notification")
flags.Bool("twilio", false, "Trigger a twilio SMS notification")
flags.Bool("ntfy", false, "Trigger a Ntfy notification")
flags.Bool("bark", false, "Trigger a Bark notification")

flags.IntP("pwatch", "w", -1, "Monitor a process by PID and trigger a notification when the pid disappears.")
flags.StringP("file", "f", "", "Path to noti.yaml configuration file.")
Expand Down
49 changes: 49 additions & 0 deletions service/bark/bark.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package bark

import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
)

// Notification is a Bark notification.
type Notification struct {
URL string `json:"api_url"`
DeviceKey string `json:"device_key"`
Body string `json:"body"`
Title string `json:"title"`

Client *http.Client `json:"-"`
}

// Send sends a Bark notification.
func (n *Notification) Send() error {
if n.DeviceKey == "" {
return errors.New("missing DeviceKey")
}

jsonData, err := json.Marshal(n)
if err != nil {
return err
}

resp, err := n.Client.Post(n.URL, "application/json", bytes.NewBuffer(jsonData))
if err != nil {
return err
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
b, err := io.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("bark: error reading response %w", err)
}
return fmt.Errorf("bark: %d\n %s", resp.StatusCode, string(b))
}

return nil

}
75 changes: 75 additions & 0 deletions service/bark/bark_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package bark

import (
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"time"
)

func TestSend(t *testing.T) {
n := Notification{
Title: "title",
Body: "mesg",
DeviceKey: "key",
Client: &http.Client{Timeout: 3 * time.Second},
}
var hitServer bool

var statusCode int

ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
hitServer = true

if r.Method != "POST" {
t.Error("HTTP method should be POST")
}

if r.Header.Get("Content-Type") != "application/json" {
t.Error("content type should be application/json")
}

var msg Notification
err := json.NewDecoder(r.Body).Decode(&msg)
if err != nil {
t.Error(err)
}

if msg.DeviceKey == "" {
t.Error("missing device_key")
}
if msg.Body == "" {
t.Error("missing body")
}

if err != nil {
t.Error(err)
}
if statusCode == 0 {
rw.WriteHeader(http.StatusOK)
} else {
rw.WriteHeader(statusCode)
_, err := rw.Write([]byte("Error"))
if err != nil {
t.Error(err)
}
}
}))
defer ts.Close()

n.URL = ts.URL

if err := n.Send(); err != nil {
t.Error(err)
}

if !hitServer {
t.Error("didn't reach server")
}

statusCode = http.StatusNotImplemented
if err := n.Send(); err == nil {
t.Error("unexpected success")
}
}
Loading