Add pushover notifications, this should be a super basic MVP
This commit is contained in:
parent
ed13a5994f
commit
d9917ab8b0
505 changed files with 195741 additions and 9 deletions
9
vendor/github.com/gregdel/pushover/.travis.yml
generated
vendored
Normal file
9
vendor/github.com/gregdel/pushover/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
language: go
|
||||
go:
|
||||
- 1.15.2
|
||||
before_install:
|
||||
- go get github.com/axw/gocov/gocov
|
||||
- go get github.com/mattn/goveralls
|
||||
- if ! go get code.google.com/p/go.tools/cmd/cover; then go get golang.org/x/tools/cmd/cover; fi
|
||||
script:
|
||||
- $HOME/gopath/bin/goveralls -service=travis-ci
|
21
vendor/github.com/gregdel/pushover/LICENSE
generated
vendored
Normal file
21
vendor/github.com/gregdel/pushover/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) Grégoire Delattre <gregoire.delattre@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
134
vendor/github.com/gregdel/pushover/README.md
generated
vendored
Normal file
134
vendor/github.com/gregdel/pushover/README.md
generated
vendored
Normal file
|
@ -0,0 +1,134 @@
|
|||
pushover
|
||||
=========
|
||||
|
||||
[](http://godoc.org/github.com/gregdel/pushover)
|
||||
[](https://travis-ci.org/gregdel/pushover)
|
||||
[](https://coveralls.io/github/gregdel/pushover?branch=master)
|
||||
[](https://goreportcard.com/report/github.com/gregdel/pushover)
|
||||
|
||||
pushover is a wrapper around the Superblock's Pushover API written in go.
|
||||
Based on their [documentation](https://pushover.net/api). It's a convenient way to send notifications from a go program with only a few lines of code.
|
||||
|
||||
## Messages
|
||||
|
||||
### Send a simple message
|
||||
|
||||
Here is a simple example for sending a notification to a recipient. A recipient can be a user or a group. There is no real difference, they both use a notification token.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/gregdel/pushover"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Create a new pushover app with a token
|
||||
app := pushover.New("uQiRzpo4DXghDmr9QzzfQu27cmVRsG")
|
||||
|
||||
// Create a new recipient
|
||||
recipient := pushover.NewRecipient("gznej3rKEVAvPUxu9vvNnqpmZpokzF")
|
||||
|
||||
// Create the message to send
|
||||
message := pushover.NewMessage("Hello !")
|
||||
|
||||
// Send the message to the recipient
|
||||
response, err := app.SendMessage(message, recipient)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
|
||||
// Print the response if you want
|
||||
log.Println(response)
|
||||
}
|
||||
```
|
||||
|
||||
### Send a message with a title
|
||||
|
||||
There is a simple way to create a message with a title. Instead of using pushover.NewMessage you can use pushover.NewMessageWithTitle.
|
||||
|
||||
```go
|
||||
message := pushover.NewMessageWithTitle("My awesome message", "My title")
|
||||
```
|
||||
|
||||
### Send a fancy message
|
||||
|
||||
If you want a more detailed message you can still do it.
|
||||
|
||||
```go
|
||||
message := &pushover.Message{
|
||||
Message: "My awesome message",
|
||||
Title: "My title",
|
||||
Priority: pushover.PriorityEmergency,
|
||||
URL: "http://google.com",
|
||||
URLTitle: "Google",
|
||||
Timestamp: time.Now().Unix(),
|
||||
Retry: 60 * time.Second,
|
||||
Expire: time.Hour,
|
||||
DeviceName: "SuperDevice",
|
||||
CallbackURL: "http://yourapp.com/callback",
|
||||
Sound: pushover.SoundCosmic,
|
||||
}
|
||||
```
|
||||
|
||||
### Send a message with an attachment
|
||||
|
||||
You can send an image attachment along with the message.
|
||||
|
||||
```go
|
||||
file, err := os.Open("/some/image.png")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
message := pushover.NewMessage("Hello !")
|
||||
if err := message.AddAttachment(file); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
```
|
||||
|
||||
## Callbacks and receipts
|
||||
|
||||
If you're using an emergency notification you'll have to specify a retry period and an expiration delay. You can get the receipt details using the token in the message response.
|
||||
|
||||
|
||||
```go
|
||||
...
|
||||
response, err := app.SendMessage(message, recipient)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
|
||||
receiptDetails, err := app.GetReceiptDetails(response.Receipt)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
|
||||
fmt.Println("Acknowledged status :", receiptDetails.Acknowledged)
|
||||
```
|
||||
|
||||
You can also cancel an emergency notification before the expiration time.
|
||||
|
||||
```go
|
||||
response, err := app.CancelEmergencyNotification(response.Receipt)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
```
|
||||
|
||||
## User verification
|
||||
|
||||
If you want to validate that the recipient token is valid.
|
||||
|
||||
```go
|
||||
...
|
||||
recipientDetails, err := app.GetRecipientDetails(recipient)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
|
||||
fmt.Println(recipientDetails)
|
||||
```
|
18
vendor/github.com/gregdel/pushover/errors.go
generated
vendored
Normal file
18
vendor/github.com/gregdel/pushover/errors.go
generated
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
package pushover
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Errors represents the errors returned by pushover.
|
||||
type Errors []string
|
||||
|
||||
// Error represents the error as a string.
|
||||
func (e Errors) Error() string {
|
||||
ret := ""
|
||||
if len(e) > 0 {
|
||||
ret = "Errors:\n"
|
||||
ret += strings.Join(e, "\n")
|
||||
}
|
||||
return ret
|
||||
}
|
3
vendor/github.com/gregdel/pushover/go.mod
generated
vendored
Normal file
3
vendor/github.com/gregdel/pushover/go.mod
generated
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
module github.com/gregdel/pushover
|
||||
|
||||
go 1.14
|
45
vendor/github.com/gregdel/pushover/helpers.go
generated
vendored
Normal file
45
vendor/github.com/gregdel/pushover/helpers.go
generated
vendored
Normal file
|
@ -0,0 +1,45 @@
|
|||
package pushover
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Helper to unmarshal a timestamp as string to a time.Time.
|
||||
type timestamp struct{ *time.Time }
|
||||
|
||||
func (t *timestamp) UnmarshalJSON(data []byte) error {
|
||||
var i int64
|
||||
if err := json.Unmarshal(data, &i); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if i > 0 {
|
||||
unixTime := time.Unix(i, 0)
|
||||
*t = timestamp{&unixTime}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Helper to unmarshal a int as a boolean.
|
||||
type intBool bool
|
||||
|
||||
func (i *intBool) UnmarshalJSON(data []byte) error {
|
||||
var v int64
|
||||
if err := json.Unmarshal(data, &v); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch v {
|
||||
case 0:
|
||||
*i = false
|
||||
case 1:
|
||||
*i = true
|
||||
default:
|
||||
return fmt.Errorf("failed to unmarshal int to bool")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
57
vendor/github.com/gregdel/pushover/limit.go
generated
vendored
Normal file
57
vendor/github.com/gregdel/pushover/limit.go
generated
vendored
Normal file
|
@ -0,0 +1,57 @@
|
|||
package pushover
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Limit represents the limitation of the application. This information is
|
||||
// fetched when posting a new message.
|
||||
// Headers example:
|
||||
// X-Limit-App-Limit: 7500
|
||||
// X-Limit-App-Remaining: 7496
|
||||
// X-Limit-App-Reset: 1393653600
|
||||
type Limit struct {
|
||||
// Total number of messages you can send during a month.
|
||||
Total int
|
||||
// Remaining number of messages you can send until the next reset.
|
||||
Remaining int
|
||||
// NextReset is the time when all the app counters will be reseted.
|
||||
NextReset time.Time
|
||||
}
|
||||
|
||||
func newLimit(headers http.Header) (*Limit, error) {
|
||||
headersStrings := []string{
|
||||
"X-Limit-App-Limit",
|
||||
"X-Limit-App-Remaining",
|
||||
"X-Limit-App-Reset",
|
||||
}
|
||||
headersValues := map[string]int{}
|
||||
|
||||
for _, header := range headersStrings {
|
||||
// Check if the header is present
|
||||
h, ok := headers[header]
|
||||
if !ok {
|
||||
return nil, ErrInvalidHeaders
|
||||
}
|
||||
|
||||
// The header must have only one element
|
||||
if len(h) != 1 {
|
||||
return nil, ErrInvalidHeaders
|
||||
}
|
||||
|
||||
i, err := strconv.Atoi(h[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
headersValues[header] = i
|
||||
}
|
||||
|
||||
return &Limit{
|
||||
Total: headersValues["X-Limit-App-Limit"],
|
||||
Remaining: headersValues["X-Limit-App-Remaining"],
|
||||
NextReset: time.Unix(int64(headersValues["X-Limit-App-Reset"]), 0),
|
||||
}, nil
|
||||
}
|
248
vendor/github.com/gregdel/pushover/message.go
generated
vendored
Normal file
248
vendor/github.com/gregdel/pushover/message.go
generated
vendored
Normal file
|
@ -0,0 +1,248 @@
|
|||
package pushover
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
var deviceNameRegexp *regexp.Regexp
|
||||
|
||||
func init() {
|
||||
deviceNameRegexp = regexp.MustCompile(`^[A-Za-z0-9_-]{1,25}$`)
|
||||
}
|
||||
|
||||
// Message represents a pushover message.
|
||||
type Message struct {
|
||||
// Required
|
||||
Message string
|
||||
|
||||
// Optional
|
||||
Title string
|
||||
Priority int
|
||||
URL string
|
||||
URLTitle string
|
||||
Timestamp int64
|
||||
Retry time.Duration
|
||||
Expire time.Duration
|
||||
CallbackURL string
|
||||
DeviceName string
|
||||
Sound string
|
||||
HTML bool
|
||||
Monospace bool
|
||||
|
||||
// attachment
|
||||
attachment io.Reader
|
||||
}
|
||||
|
||||
// NewMessage returns a simple new message.
|
||||
func NewMessage(message string) *Message {
|
||||
return &Message{Message: message}
|
||||
}
|
||||
|
||||
// NewMessageWithTitle returns a simple new message with a title.
|
||||
func NewMessageWithTitle(message, title string) *Message {
|
||||
return &Message{Message: message, Title: title}
|
||||
}
|
||||
|
||||
// AddAttachment adds an attachment to the message it's programmer's
|
||||
// responsibility to close the reader.
|
||||
func (m *Message) AddAttachment(attachment io.Reader) error {
|
||||
m.attachment = attachment
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate the message values.
|
||||
func (m *Message) validate() error {
|
||||
// Message should no be empty
|
||||
if m.Message == "" {
|
||||
return ErrMessageEmpty
|
||||
}
|
||||
|
||||
// Validate message length
|
||||
if utf8.RuneCountInString(m.Message) > MessageMaxLength {
|
||||
return ErrMessageTooLong
|
||||
}
|
||||
|
||||
// Validate Title field length
|
||||
if utf8.RuneCountInString(m.Title) > MessageTitleMaxLength {
|
||||
return ErrMessageTitleTooLong
|
||||
}
|
||||
|
||||
// Validate URL field
|
||||
if utf8.RuneCountInString(m.URL) > MessageURLMaxLength {
|
||||
return ErrMessageURLTooLong
|
||||
}
|
||||
|
||||
// Validate URL title field
|
||||
if utf8.RuneCountInString(m.URLTitle) > MessageURLTitleMaxLength {
|
||||
return ErrMessageURLTitleTooLong
|
||||
}
|
||||
|
||||
// URLTitle should not be set with an empty URL
|
||||
if m.URL == "" && m.URLTitle != "" {
|
||||
return ErrEmptyURL
|
||||
}
|
||||
|
||||
// Validate priorities
|
||||
if m.Priority > PriorityEmergency || m.Priority < PriorityLowest {
|
||||
return ErrInvalidPriority
|
||||
}
|
||||
|
||||
// Validate emergency priority
|
||||
if m.Priority == PriorityEmergency {
|
||||
if m.Retry == 0 || m.Expire == 0 {
|
||||
return ErrMissingEmergencyParameter
|
||||
}
|
||||
}
|
||||
|
||||
// Test device name
|
||||
if m.DeviceName != "" {
|
||||
// Accept comma separated device names
|
||||
devices := strings.Split(m.DeviceName, ",")
|
||||
for _, d := range devices {
|
||||
if !deviceNameRegexp.MatchString(d) {
|
||||
return ErrInvalidDeviceName
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Return a map filled with the relevant data.
|
||||
func (m *Message) toMap(pToken, rToken string) map[string]string {
|
||||
ret := map[string]string{
|
||||
"token": pToken,
|
||||
"user": rToken,
|
||||
"message": m.Message,
|
||||
"priority": strconv.Itoa(m.Priority),
|
||||
}
|
||||
|
||||
if m.Title != "" {
|
||||
ret["title"] = m.Title
|
||||
}
|
||||
|
||||
if m.URL != "" {
|
||||
ret["url"] = m.URL
|
||||
}
|
||||
|
||||
if m.URLTitle != "" {
|
||||
ret["url_title"] = m.URLTitle
|
||||
}
|
||||
|
||||
if m.Sound != "" {
|
||||
ret["sound"] = m.Sound
|
||||
}
|
||||
|
||||
if m.DeviceName != "" {
|
||||
ret["device"] = m.DeviceName
|
||||
}
|
||||
|
||||
if m.Timestamp != 0 {
|
||||
ret["timestamp"] = strconv.FormatInt(m.Timestamp, 10)
|
||||
}
|
||||
|
||||
if m.HTML {
|
||||
ret["html"] = "1"
|
||||
}
|
||||
|
||||
if m.Monospace {
|
||||
ret["monospace"] = "1"
|
||||
}
|
||||
|
||||
if m.Priority == PriorityEmergency {
|
||||
ret["retry"] = strconv.FormatFloat(m.Retry.Seconds(), 'f', -1, 64)
|
||||
ret["expire"] = strconv.FormatFloat(m.Expire.Seconds(), 'f', -1, 64)
|
||||
if m.CallbackURL != "" {
|
||||
ret["callback"] = m.CallbackURL
|
||||
}
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
// Send sends the message using the pushover and the recipient tokens.
|
||||
func (m *Message) send(pToken, rToken string) (*Response, error) {
|
||||
url := fmt.Sprintf("%s/messages.json", APIEndpoint)
|
||||
|
||||
var f func(string, string, string) (*http.Request, error)
|
||||
if m.attachment == nil {
|
||||
// Use a URL-encoded request if there's no need to attach files
|
||||
f = m.urlEncodedRequest
|
||||
} else {
|
||||
// Use a multipart request if a file should be sent
|
||||
f = m.multipartRequest
|
||||
}
|
||||
|
||||
// Post the from and check the headers of the response
|
||||
req, err := f(pToken, rToken, url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &Response{}
|
||||
if err := do(req, resp, true); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// multipartRequest returns a new multipart POST request with a file attached.
|
||||
func (m *Message) multipartRequest(pToken, rToken, url string) (*http.Request, error) {
|
||||
body := &bytes.Buffer{}
|
||||
|
||||
if m.attachment == nil {
|
||||
return nil, ErrMissingAttachment
|
||||
}
|
||||
|
||||
// Write the body as multipart form data
|
||||
w := multipart.NewWriter(body)
|
||||
|
||||
// Write the file in the body
|
||||
fw, err := w.CreateFormFile("attachment", "attachment")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
written, err := io.Copy(fw, m.attachment)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if written > MessageMaxAttachmentByte {
|
||||
return nil, ErrMessageAttachmentTooLarge
|
||||
}
|
||||
|
||||
// Handle params
|
||||
for k, v := range m.toMap(pToken, rToken) {
|
||||
if err := w.WriteField(k, v); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if err := w.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("POST", url, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("Content-Type", w.FormDataContentType())
|
||||
|
||||
return req, nil
|
||||
}
|
||||
|
||||
// urlEncodedRequest returns a new url encoded request.
|
||||
func (m *Message) urlEncodedRequest(pToken, rToken, endpoint string) (*http.Request, error) {
|
||||
return newURLEncodedRequest("POST", endpoint, m.toMap(pToken, rToken))
|
||||
}
|
210
vendor/github.com/gregdel/pushover/pushover.go
generated
vendored
Normal file
210
vendor/github.com/gregdel/pushover/pushover.go
generated
vendored
Normal file
|
@ -0,0 +1,210 @@
|
|||
// Package pushover provides a wrapper around the Pushover API
|
||||
package pushover
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
// Regexp validation.
|
||||
var tokenRegexp *regexp.Regexp
|
||||
|
||||
func init() {
|
||||
tokenRegexp = regexp.MustCompile(`^[A-Za-z0-9]{30}$`)
|
||||
}
|
||||
|
||||
// APIEndpoint is the API base URL for any request.
|
||||
var APIEndpoint = "https://api.pushover.net/1"
|
||||
|
||||
// Pushover custom errors.
|
||||
var (
|
||||
ErrHTTPPushover = errors.New("pushover: http error")
|
||||
ErrEmptyToken = errors.New("pushover: empty API token")
|
||||
ErrEmptyURL = errors.New("pushover: empty URL, URLTitle needs an URL")
|
||||
ErrEmptyRecipientToken = errors.New("pushover: empty recipient token")
|
||||
ErrInvalidRecipientToken = errors.New("pushover: invalid recipient token")
|
||||
ErrInvalidRecipient = errors.New("pushover: invalid recipient")
|
||||
ErrInvalidHeaders = errors.New("pushover: invalid headers in server response")
|
||||
ErrInvalidPriority = errors.New("pushover: invalid priority")
|
||||
ErrInvalidToken = errors.New("pushover: invalid API token")
|
||||
ErrMessageEmpty = errors.New("pushover: message empty")
|
||||
ErrMessageTitleTooLong = errors.New("pushover: message title too long")
|
||||
ErrMessageTooLong = errors.New("pushover: message too long")
|
||||
ErrMessageAttachmentTooLarge = errors.New("pushover: message attachment is too large")
|
||||
ErrMessageURLTitleTooLong = errors.New("pushover: message URL title too long")
|
||||
ErrMessageURLTooLong = errors.New("pushover: message URL too long")
|
||||
ErrMissingAttachment = errors.New("pushover: missing attachment")
|
||||
ErrMissingEmergencyParameter = errors.New("pushover: missing emergency parameter")
|
||||
ErrInvalidDeviceName = errors.New("pushover: invalid device name")
|
||||
ErrEmptyReceipt = errors.New("pushover: empty receipt")
|
||||
)
|
||||
|
||||
// API limitations.
|
||||
const (
|
||||
// MessageMaxLength is the max message number of characters.
|
||||
MessageMaxLength = 1024
|
||||
// MessageTitleMaxLength is the max title number of characters.
|
||||
MessageTitleMaxLength = 250
|
||||
// MessageURLMaxLength is the max URL number of characters.
|
||||
MessageURLMaxLength = 512
|
||||
// MessageURLTitleMaxLength is the max URL title number of characters.
|
||||
MessageURLTitleMaxLength = 100
|
||||
// MessageMaxAttachmentByte is the max attachment size in byte.
|
||||
MessageMaxAttachmentByte = 2621440
|
||||
)
|
||||
|
||||
// Message priorities
|
||||
const (
|
||||
PriorityLowest = -2
|
||||
PriorityLow = -1
|
||||
PriorityNormal = 0
|
||||
PriorityHigh = 1
|
||||
PriorityEmergency = 2
|
||||
)
|
||||
|
||||
// Sounds
|
||||
const (
|
||||
SoundPushover = "pushover"
|
||||
SoundBike = "bike"
|
||||
SoundBugle = "bugle"
|
||||
SoundCashRegister = "cashregister"
|
||||
SoundClassical = "classical"
|
||||
SoundCosmic = "cosmic"
|
||||
SoundFalling = "falling"
|
||||
SoundGamelan = "gamelan"
|
||||
SoundIncoming = "incoming"
|
||||
SoundIntermission = "intermission"
|
||||
SoundMagic = "magic"
|
||||
SoundMechanical = "mechanical"
|
||||
SoundPianobar = "pianobar"
|
||||
SoundSiren = "siren"
|
||||
SoundSpaceAlarm = "spacealarm"
|
||||
SoundTugBoat = "tugboat"
|
||||
SoundAlien = "alien"
|
||||
SoundClimb = "climb"
|
||||
SoundPersistent = "persistent"
|
||||
SoundEcho = "echo"
|
||||
SoundUpDown = "updown"
|
||||
SoundVibrate = "vibrate"
|
||||
SoundNone = "none"
|
||||
)
|
||||
|
||||
// Pushover is the representation of an app using the pushover API.
|
||||
type Pushover struct {
|
||||
token string
|
||||
}
|
||||
|
||||
// New returns a new app to talk to the pushover API.
|
||||
func New(token string) *Pushover {
|
||||
return &Pushover{token}
|
||||
}
|
||||
|
||||
// Validate Pushover token.
|
||||
func (p *Pushover) validate() error {
|
||||
// Check empty token
|
||||
if p.token == "" {
|
||||
return ErrEmptyToken
|
||||
}
|
||||
|
||||
// Check invalid token
|
||||
if !tokenRegexp.MatchString(p.token) {
|
||||
return ErrInvalidToken
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SendMessage is used to send message to a recipient.
|
||||
func (p *Pushover) SendMessage(message *Message, recipient *Recipient) (*Response, error) {
|
||||
// Validate pushover
|
||||
if err := p.validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Validate recipient
|
||||
if err := recipient.validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Validate message
|
||||
if err := message.validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return message.send(p.token, recipient.token)
|
||||
}
|
||||
|
||||
// GetReceiptDetails return detailed informations about a receipt. This is used
|
||||
// used to check the acknowledged status of an Emergency notification.
|
||||
func (p *Pushover) GetReceiptDetails(receipt string) (*ReceiptDetails, error) {
|
||||
url := fmt.Sprintf("%s/receipts/%s.json?token=%s", APIEndpoint, receipt, p.token)
|
||||
|
||||
if receipt == "" {
|
||||
return nil, ErrEmptyReceipt
|
||||
}
|
||||
|
||||
// Send request
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Decode the JSON response
|
||||
var details *ReceiptDetails
|
||||
if err = json.NewDecoder(resp.Body).Decode(&details); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return details, nil
|
||||
}
|
||||
|
||||
// GetRecipientDetails allows to check if a recipient exists, if it's a group
|
||||
// and the devices associated to this recipient. It returns an
|
||||
// ErrInvalidRecipient if the recipient is not valid in the Pushover API.
|
||||
func (p *Pushover) GetRecipientDetails(recipient *Recipient) (*RecipientDetails, error) {
|
||||
endpoint := fmt.Sprintf("%s/users/validate.json", APIEndpoint)
|
||||
|
||||
// Validate pushover
|
||||
if err := p.validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Validate recipient
|
||||
if err := recipient.validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req, err := newURLEncodedRequest("POST", endpoint,
|
||||
map[string]string{"token": p.token, "user": recipient.token})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var response RecipientDetails
|
||||
if err := do(req, &response, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &response, nil
|
||||
}
|
||||
|
||||
// CancelEmergencyNotification helps stop a notification retry in case of a
|
||||
// notification with an Emergency priority before reaching the expiration time.
|
||||
// It requires the response receipt in order to stop the right notification.
|
||||
func (p *Pushover) CancelEmergencyNotification(receipt string) (*Response, error) {
|
||||
endpoint := fmt.Sprintf("%s/receipts/%s/cancel.json", APIEndpoint, receipt)
|
||||
|
||||
req, err := newURLEncodedRequest("GET", endpoint, map[string]string{"token": p.token})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
response := &Response{}
|
||||
if err := do(req, response, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
59
vendor/github.com/gregdel/pushover/receipt_details.go
generated
vendored
Normal file
59
vendor/github.com/gregdel/pushover/receipt_details.go
generated
vendored
Normal file
|
@ -0,0 +1,59 @@
|
|||
package pushover
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ReceiptDetails represents the receipt informations in case of emergency
|
||||
// priority.
|
||||
type ReceiptDetails struct {
|
||||
Status int
|
||||
Acknowledged bool
|
||||
AcknowledgedBy string
|
||||
Expired bool
|
||||
CalledBack bool
|
||||
ID string
|
||||
AcknowledgedAt *time.Time
|
||||
LastDeliveredAt *time.Time
|
||||
ExpiresAt *time.Time
|
||||
CalledBackAt *time.Time
|
||||
}
|
||||
|
||||
// UnmarshalJSON is a custom unmarshal function to handle timestamps and
|
||||
// boolean as int and convert them to the right type.
|
||||
func (r *ReceiptDetails) UnmarshalJSON(data []byte) error {
|
||||
dataBytes := bytes.NewReader(data)
|
||||
var aux struct {
|
||||
ID string `json:"request"`
|
||||
Status int `json:"status"`
|
||||
Acknowledged intBool `json:"acknowledged"`
|
||||
AcknowledgedBy string `json:"acknowledged_by"`
|
||||
Expired intBool `json:"expired"`
|
||||
CalledBack intBool `json:"called_back"`
|
||||
AcknowledgedAt *timestamp `json:"acknowledged_at"`
|
||||
LastDeliveredAt *timestamp `json:"last_delivered_at"`
|
||||
ExpiresAt *timestamp `json:"expires_at"`
|
||||
CalledBackAt *timestamp `json:"called_back_at"`
|
||||
}
|
||||
|
||||
// Decode json into the aux struct
|
||||
if err := json.NewDecoder(dataBytes).Decode(&aux); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Set the RecipientDetails with the right types
|
||||
r.Status = aux.Status
|
||||
r.Acknowledged = bool(aux.Acknowledged)
|
||||
r.AcknowledgedBy = aux.AcknowledgedBy
|
||||
r.Expired = bool(aux.Expired)
|
||||
r.CalledBack = bool(aux.CalledBack)
|
||||
r.ID = aux.ID
|
||||
r.AcknowledgedAt = aux.AcknowledgedAt.Time
|
||||
r.LastDeliveredAt = aux.LastDeliveredAt.Time
|
||||
r.ExpiresAt = aux.ExpiresAt.Time
|
||||
r.CalledBackAt = aux.CalledBackAt.Time
|
||||
|
||||
return nil
|
||||
}
|
43
vendor/github.com/gregdel/pushover/recipient.go
generated
vendored
Normal file
43
vendor/github.com/gregdel/pushover/recipient.go
generated
vendored
Normal file
|
@ -0,0 +1,43 @@
|
|||
package pushover
|
||||
|
||||
import "regexp"
|
||||
|
||||
var recipientRegexp *regexp.Regexp
|
||||
|
||||
func init() {
|
||||
recipientRegexp = regexp.MustCompile(`^[A-Za-z0-9]{30}$`)
|
||||
}
|
||||
|
||||
// Recipient represents the a recipient to notify.
|
||||
type Recipient struct {
|
||||
token string
|
||||
}
|
||||
|
||||
// NewRecipient is the representation of the recipient to notify.
|
||||
func NewRecipient(token string) *Recipient {
|
||||
return &Recipient{token}
|
||||
}
|
||||
|
||||
// Validates recipient token.
|
||||
func (r *Recipient) validate() error {
|
||||
// Check empty token
|
||||
if r.token == "" {
|
||||
return ErrEmptyRecipientToken
|
||||
}
|
||||
|
||||
// Check invalid token
|
||||
if !recipientRegexp.MatchString(r.token) {
|
||||
return ErrInvalidRecipientToken
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RecipientDetails represents the receipt informations in case of emergency
|
||||
// priority.
|
||||
type RecipientDetails struct {
|
||||
Status int `json:"status"`
|
||||
Group int `json:"group"`
|
||||
Devices []string `json:"devices"`
|
||||
RequestID string `json:"request"`
|
||||
Errors Errors `json:"errors"`
|
||||
}
|
69
vendor/github.com/gregdel/pushover/request.go
generated
vendored
Normal file
69
vendor/github.com/gregdel/pushover/request.go
generated
vendored
Normal file
|
@ -0,0 +1,69 @@
|
|||
package pushover
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// do is a generic function to send a request to the API.
|
||||
func do(req *http.Request, resType interface{}, returnHeaders bool) error {
|
||||
client := http.DefaultClient
|
||||
|
||||
// Send request
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Only 500 errors will not respond a readable result
|
||||
if resp.StatusCode >= http.StatusInternalServerError {
|
||||
return ErrHTTPPushover
|
||||
}
|
||||
|
||||
// Decode the JSON response
|
||||
if err := json.NewDecoder(resp.Body).Decode(&resType); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check if the unmarshaled data is a response
|
||||
r, ok := resType.(*Response)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check response status
|
||||
if r.Status != 1 {
|
||||
return r.Errors
|
||||
}
|
||||
|
||||
// The headers are only returned when posting a new notification
|
||||
if returnHeaders {
|
||||
// Get app limits from headers
|
||||
appLimits, err := newLimit(resp.Header)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.Limit = appLimits
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// urlEncodedRequest returns a new url encoded request.
|
||||
func newURLEncodedRequest(method, endpoint string, params map[string]string) (*http.Request, error) {
|
||||
urlValues := url.Values{}
|
||||
for k, v := range params {
|
||||
urlValues.Add(k, v)
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(method, endpoint, strings.NewReader(urlValues.Encode()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
|
||||
return req, nil
|
||||
}
|
25
vendor/github.com/gregdel/pushover/response.go
generated
vendored
Normal file
25
vendor/github.com/gregdel/pushover/response.go
generated
vendored
Normal file
|
@ -0,0 +1,25 @@
|
|||
package pushover
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Response represents a response from the API.
|
||||
type Response struct {
|
||||
Status int `json:"status"`
|
||||
ID string `json:"request"`
|
||||
Errors Errors `json:"errors"`
|
||||
Receipt string `json:"receipt"`
|
||||
Limit *Limit
|
||||
}
|
||||
|
||||
// String represents a printable form of the response.
|
||||
func (r Response) String() string {
|
||||
ret := fmt.Sprintf("Request id: %s\n", r.ID)
|
||||
if r.Receipt != "" {
|
||||
ret += fmt.Sprintf("Receipt: %s\n", r.Receipt)
|
||||
}
|
||||
if r.Limit != nil {
|
||||
ret += fmt.Sprintf("Usage %d/%d messages\nNext reset : %s",
|
||||
r.Limit.Remaining, r.Limit.Total, r.Limit.NextReset)
|
||||
}
|
||||
return ret
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue