-
Notifications
You must be signed in to change notification settings - Fork 0
/
message.go
308 lines (257 loc) · 9.43 KB
/
message.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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
package breeze
import (
"fmt"
"net/url"
"strconv"
)
const (
// Lowest priority does not generate any sort of notification.
Lowest = -2 + iota
// Low priority generates a pop-up notification but does not produce a sound.
Low
// Normal priority messages trigger a sound, vibration, and display a pop-up
// notification. This is the default.
Normal
// High priority messages produce the same notifications as normal priority,
// but can bypass quiet-hours.
High
// Emergency priority messages are like high priority, but are repeated until
// they are acknowledged by the receiver. Emergency priority messages require
// the use of the retry and expire paramters. When a message with emergency
// priority is posted, the Pushover API responds with a receipt that can be
// used to query the delivery information.
Emergency
)
const (
// MinRetryTime defines the minimum retry time as defined by the Pushover API.
MinRetryTime = 30
// MaxExpireTime defines the length of the time window which retries are
// attempted in.
MaxExpireTime = 86400
)
const (
// MaxTitleLen defines the maximum size of a message title as defined by the
// Pushover API.
MaxTitleLen = 250
// MaxMessageLen defines the maximum length of a message as defined by the
// Pushover API.
MaxMessageLen = 1024
// MaxSuppURLTitleLen defines the maximum length of a supplementary URL title
// as defined by the Pushover API.
MaxSuppURLTitleLen = 100
// MaxSuppURLLen defined the maximum legnth of the supplementary URL as
// defined by the Pushover API.
MaxSuppURLLen = 512
)
const (
// ErrMessageBlank indicates that the message was blank. The Pushover API
// requires that the message have content.
ErrMessageBlank = -(1 + iota)
// ErrMessageTooLong indicates the message was longer than MaxMessageLen.
ErrMessageTooLong
// ErrTitleTooLong indicates the message title was longer than MaxTitleLen.
ErrTitleTooLong
// ErrSuppURLTitleTooLong indicates that the supplementary url title was
// longer than MaxSuppURLTitleLen.
ErrSuppURLTitleTooLong
// ErrSuppURLTooLong indicates that the supplementary url was longer than
// MaxSuppURLLen.
ErrSuppURLTooLong
// ErrInvalidPriority indicates that the priority is invalid.
ErrInvalidPriority
// ErrMissingParameter indicates that some parameter is missing. This is often
// due to retry or expire missing on a priority message, or url missing but
// url title is given.
ErrMissingParameter
// ErrRetryTimeTooShort indicates that the retry time is less than
// MinRetryTime
ErrRetryTimeTooShort
// ErrExpireTimeTooLong indicates that the expire time is longer than
// MaxExpireTime.
ErrExpireTimeTooLong
// ErrNoSound indicates that the requested sound does not exist.
ErrNoSound
// ErrNoDevice indicates that a device validation failed.
ErrNoDevice
)
// ValueError represents some error in the parameters passed in by the user.
type ValueError struct {
What int
Why string
}
func (errValue *ValueError) Error() string {
return fmt.Sprintf("%d: %s", errValue.What, errValue.Why)
}
// Message represents the message being sent to the push receiver. Only the
// "Message" field is required. If a device is given, it will be validated.
// Requesting emergency priority requires also providing retry and expire.
type Message struct {
message string
title string
url string
urlTitle string
priority int
retry int
expire int
timestamp int64
sound string
device string
}
func (message *Message) addValues(values url.Values) {
if message.message != "" {
values.Add("message", message.message)
}
if message.title != "" {
values.Add("title", message.title)
}
if message.url != "" {
values.Add("url", message.url)
}
if message.urlTitle != "" {
values.Add("url_title", message.urlTitle)
}
values.Add("priority", strconv.Itoa(message.priority))
if message.priority == Emergency {
values.Add("retry", strconv.Itoa(message.retry))
values.Add("expire", strconv.Itoa(message.expire))
}
if message.timestamp != 0 {
values.Add("timestamp", strconv.FormatInt(message.timestamp, 10))
}
if message.sound != "" {
values.Add("sound", message.sound)
}
if message.device != "" {
values.Add("device", message.device)
}
}
func (message *Message) validateMessage(pushContext *PushContext) error {
if mlen := len(message.message); mlen == 0 {
return &ValueError{ErrMessageBlank, "message can't be empty."}
}
if mlen := len(message.message); mlen > MaxMessageLen {
msg := fmt.Sprintf("length exceeded by %d.", mlen-MaxMessageLen)
return &ValueError{ErrMessageTooLong, msg}
}
if tlen := len(message.title); tlen > MaxTitleLen {
msg := fmt.Sprintf("length exceeded by %d.", tlen-MaxTitleLen)
return &ValueError{ErrTitleTooLong, msg}
}
if ulen := len(message.url); ulen > MaxSuppURLLen {
msg := fmt.Sprintf("length exceeded by %d.", ulen-MaxSuppURLLen)
return &ValueError{ErrSuppURLTooLong, msg}
}
if message.urlTitle != "" && message.url == "" {
msg := "need a url to give it a title."
return &ValueError{ErrMissingParameter, msg}
}
if utlen := len(message.urlTitle); utlen > MaxSuppURLTitleLen {
msg := fmt.Sprintf("length exceeded by %d.", utlen-MaxSuppURLTitleLen)
return &ValueError{ErrSuppURLTitleTooLong, msg}
}
if message.priority < Lowest || message.priority > Emergency {
return &ValueError{ErrInvalidPriority, "invalid priority."}
}
if message.priority == Emergency {
if message.retry == 0 || message.expire == 0 {
msg := "retry and expire must be provided if priority is emergency."
return &ValueError{ErrMissingParameter, msg}
}
if message.retry < MinRetryTime {
msg := fmt.Sprintf("retry time below min by %d.",
MinRetryTime-message.retry)
return &ValueError{ErrRetryTimeTooShort, msg}
}
if message.expire > MaxExpireTime {
msg := fmt.Sprintf("expire time exceeded by %d.",
message.expire-MaxExpireTime)
return &ValueError{ErrExpireTimeTooLong, msg}
}
}
if message.sound != "" {
ok := pushContext.IsValidSound(message.sound)
if !ok {
msg := "requested sound not supported, check supported sounds property."
return &ValueError{ErrNoSound, msg}
}
}
if message.device != "" {
ok := pushContext.IsValidDevice(message.device)
if !ok {
msg := "requested device not supported. check supported devices property."
return &ValueError{ErrNoDevice, msg}
}
}
return nil
}
// AddTitle can be used to add a title to a message. The title is limited to a
// maximum length of MaxTitleLen, and will be verified before sending.
func (message *Message) AddTitle(title string) *Message {
message.title = title
return message
}
// AddURL can be used to add a URL to a message. The URL is limited to a maximum
// length of MaxSuppURLLen, and will be verified before sending.
func (message *Message) AddURL(url string) *Message {
message.url = url
return message
}
// AddURLTitle can be used to add a url title. The url title is limited to a
// maximum length of MaxSuppURLTitleLen, and will be verified before sending.
func (message *Message) AddURLTitle(title string) *Message {
message.urlTitle = title
return message
}
// AddPriority can be used to associate a priority with the message. The
// priority is limited to Lowest, Low, Normal, High, and Emergency. The default
// is Normal. If Emergency is set, then retry and expire must also be provided.
func (message *Message) AddPriority(priority int) *Message {
message.priority = priority
return message
}
// AddRetry can be used to establish an associated retry time and is only
// important when the message is sent with Emergency priority. The retry time
// dictates how many seconds the API will wait before re-pushing the message.
// The value is expressed in seconds. This value must be at least 30 seconds.
func (message *Message) AddRetry(retry int) *Message {
message.retry = retry
return message
}
// AddExpire can be used to establish an associated expire time and is only
// important when the message is sent with Emergency priority. The expire time
// determines the length of the window in which the API will attempt retries.
// The value is expressed in seconds. This value must be at most 86,400 seconds.
func (message *Message) AddExpire(expire int) *Message {
message.expire = expire
return message
}
// AddTimestamp can be used to associate a time stamp with a message. The
// timestamp expresses the time that the Pushover API received the push request.
// The timestamp given will be reflected in the receiving application. This
// value is expressed in epoch time.
func (message *Message) AddTimestamp(timestamp int64) *Message {
message.timestamp = timestamp
return message
}
// AddSound can be used to associate a sound with a message. The options are:
// pushover, bike, bugle, cashregister, classical, cosmic, falling, gamelan,
// incoming, intermission, magic, mechanical, pianobar, siren, spacealarm,
// tugboat, alien, climb, persistent, echo, updown, none.
// TODO: Verify sound is one of the above.
func (message *Message) AddSound(sound string) *Message {
message.sound = sound
return message
}
// AddDevice can be used to filter which device the message gets sent to. This
// will be validated using the Pushover API before the message is sent.
func (message *Message) AddDevice(device string) *Message {
message.device = device
return message
}
// NewMessage is the primary interface for receiving a new Message struct that
// can be given to the Push function of a PushContext.
func NewMessage(msg string) *Message {
var message = new(Message)
message.message = msg
return message
}