forked from nikoksr/notify
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbark.go
165 lines (137 loc) · 4.44 KB
/
bark.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
package bark
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"strings"
"time"
"github.com/pkg/errors"
)
// Service allow you to configure Bark service.
type Service struct {
deviceKey string
client *http.Client
serverURLs []string
}
func defaultHTTPClient() *http.Client {
return &http.Client{
Timeout: 5 * time.Second,
}
}
// DefaultServerURL is the default server to use for the bark service.
const DefaultServerURL = "https://api.day.app/"
// normalizeServerURL normalizes the server URL. It prefixes it with https:// if it's not already and appends a slash
// if it's not already there. If the serverURL is empty, the DefaultServerURL is used. We're not validating the url here
// on purpose, we leave that to the http client.
func normalizeServerURL(serverURL string) string {
if serverURL == "" {
return DefaultServerURL
}
// Normalize the url
if !strings.HasPrefix(serverURL, "http") {
serverURL = "https://" + serverURL
}
if !strings.HasSuffix(serverURL, "/") {
serverURL = serverURL + "/"
}
return serverURL
}
// AddReceivers adds server URLs to the list of servers to use for sending messages. We call it Receivers and not
// servers because strictly speaking, the server is still receiving the message, and additionally we're following the
// naming convention of the other services.
func (s *Service) AddReceivers(serverURLs ...string) {
for _, serverURL := range serverURLs {
serverURL = normalizeServerURL(serverURL)
s.serverURLs = append(s.serverURLs, serverURL)
}
}
// NewWithServers returns a new instance of Bark service. You can use this service to send messages to bark. You can
// specify the servers to send the messages to. By default, the service will use the default server
// (https://api.day.app/) if you don't specify any servers.
func NewWithServers(deviceKey string, serverURLs ...string) *Service {
s := &Service{
deviceKey: deviceKey,
client: defaultHTTPClient(),
}
if len(serverURLs) == 0 {
serverURLs = append(serverURLs, DefaultServerURL)
}
// Calling service.AddReceivers() instead of directly setting the serverURLs because we want to normalize the URLs.
s.AddReceivers(serverURLs...)
return s
}
// New returns a new instance of Bark service. You can use this service to send messages to bark. By default, the
// service will use the default server (https://api.day.app/).
func New(deviceKey string) *Service {
return NewWithServers(deviceKey)
}
// postData is the data to send to the bark server.
type postData struct {
DeviceKey string `json:"device_key"`
Title string `json:"title"`
Body string `json:"body,omitempty"`
Badge int `json:"badge,omitempty"`
Sound string `json:"sound,omitempty"`
Icon string `json:"icon,omitempty"`
Group string `json:"group,omitempty"`
URL string `json:"pushURL,omitempty"`
}
func (s *Service) send(ctx context.Context, serverURL, subject, content string) (err error) {
if serverURL == "" {
return errors.New("server url is empty")
}
// Marshal the message to post
message := &postData{
DeviceKey: s.deviceKey,
Title: subject,
Body: content,
Sound: "alarm.caf",
}
messageJSON, err := json.Marshal(message)
if err != nil {
return errors.Wrap(err, "marshal message")
}
pushURL := serverURL + "push"
// Create new request
req, err := http.NewRequestWithContext(ctx, http.MethodPost, pushURL, bytes.NewBuffer(messageJSON))
if err != nil {
return errors.Wrap(err, "create request")
}
req.Header.Set("Content-Type", "application/json; charset=utf-8")
// Send request
resp, err := s.client.Do(req)
if err != nil {
return errors.Wrap(err, "send request")
}
defer func() { _ = resp.Body.Close() }()
// Read response and verify success
result, err := io.ReadAll(resp.Body)
if err != nil {
return errors.Wrap(err, "read response")
}
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("bark returned status code %d: %s", resp.StatusCode, string(result))
}
return nil
}
// Send takes a message subject and a message content and sends them to bark application.
func (s *Service) Send(ctx context.Context, subject, content string) error {
if s.client == nil {
return errors.New("client is nil")
}
for _, serverURL := range s.serverURLs {
select {
case <-ctx.Done():
return ctx.Err()
default:
err := s.send(ctx, serverURL, subject, content)
if err != nil {
return errors.Wrapf(err, "failed to send message to bark server %q", serverURL)
}
}
}
return nil
}