Add pushover notifications, this should be a super basic MVP

This commit is contained in:
Tony Blyler 2021-05-18 09:04:15 -04:00
parent ed13a5994f
commit d9917ab8b0
Signed by: tblyler
GPG key ID: 7F13D9A60C0D678E
505 changed files with 195741 additions and 9 deletions

9
vendor/github.com/gregdel/pushover/.travis.yml generated vendored Normal file
View 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
View 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
View file

@ -0,0 +1,134 @@
pushover
=========
[![GoDoc](https://godoc.org/github.com/gregdel/pushover?status.svg)](http://godoc.org/github.com/gregdel/pushover)
[![Build Status](https://travis-ci.org/gregdel/pushover.svg?branch=master)](https://travis-ci.org/gregdel/pushover)
[![Coverage Status](https://coveralls.io/repos/gregdel/pushover/badge.svg?branch=master&service=github)](https://coveralls.io/github/gregdel/pushover?branch=master)
[![Go Report Card](https://goreportcard.com/badge/github.com/gregdel/pushover)](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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
}