mirror of
https://github.com/crazy-max/diun.git
synced 2025-12-21 11:35:00 +01:00
switch to http client for pushover notifier
This commit is contained in:
@@ -12,6 +12,7 @@ You can send notifications using [Pushover](https://pushover.net/).
|
||||
recipient: gznej3rKEVAvPUxu9vvNnqpmZpokzF
|
||||
priority: -2
|
||||
sound: none
|
||||
timeout: 10s
|
||||
templateTitle: "{{ .Entry.Image }} released"
|
||||
templateBody: |
|
||||
Docker tag {{ .Entry.Image }} which you subscribed to through {{ .Entry.Provider }} provider has been released.
|
||||
@@ -25,6 +26,7 @@ You can send notifications using [Pushover](https://pushover.net/).
|
||||
| `recipientFile` | | Use content of secret file as User key if `recipient` not defined |
|
||||
| `priority` | | Priority of the notification |
|
||||
| `sound` | | Notification sound to be used |
|
||||
| `timeout` | `10s` | Timeout specifies a time limit for the request to be made |
|
||||
| `templateTitle`[^1] | See [below](#default-templatetitle) | [Notification template](../faq.md#notification-template) for message title |
|
||||
| `templateBody`[^1] | See [below](#default-templatebody) | [Notification template](../faq.md#notification-template) for message body |
|
||||
|
||||
@@ -35,6 +37,7 @@ You can send notifications using [Pushover](https://pushover.net/).
|
||||
* `DIUN_NOTIF_PUSHOVER_RECIPIENTFILE`
|
||||
* `DIUN_NOTIF_PUSHOVER_PRIORITY`
|
||||
* `DIUN_NOTIF_PUSHOVER_SOUND`
|
||||
* `DIUN_NOTIF_PUSHOVER_TIMEOUT`
|
||||
* `DIUN_NOTIF_PUSHOVER_TEMPLATETITLE`
|
||||
* `DIUN_NOTIF_PUSHOVER_TEMPLATEBODY`
|
||||
|
||||
|
||||
1
go.mod
1
go.mod
@@ -21,7 +21,6 @@ require (
|
||||
github.com/eclipse/paho.mqtt.golang v1.5.0
|
||||
github.com/go-gomail/gomail v0.0.0-20160411212932-81ebce5c23df
|
||||
github.com/go-playground/validator/v10 v10.27.0
|
||||
github.com/gregdel/pushover v1.3.1
|
||||
github.com/hashicorp/nomad/api v0.0.0-20231213195942-64e3dca9274b // v1.7.2
|
||||
github.com/jedib0t/go-pretty/v6 v6.6.8
|
||||
github.com/matcornic/hermes/v2 v2.1.0
|
||||
|
||||
2
go.sum
2
go.sum
@@ -167,8 +167,6 @@ github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWS
|
||||
github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo=
|
||||
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
|
||||
github.com/gregdel/pushover v1.3.1 h1:4bMLITOZ15+Zpi6qqoGqOPuVHCwSUvMCgVnN5Xhilfo=
|
||||
github.com/gregdel/pushover v1.3.1/go.mod h1:EcaO66Nn1StkpEm1iKtBTV3d2A16SoMsVER1PthX7to=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 h1:e9Rjr40Z98/clHv5Yg79Is0NtosR5LXRvdr7o/6NwbA=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1/go.mod h1:tIxuGz/9mpox++sgp9fJjHO0+q1X9/UOWd798aAm22M=
|
||||
github.com/hashicorp/cronexpr v1.1.2 h1:wG/ZYIKT+RT3QkOdgYc+xsKWVRgnxJ1OJtjjy84fJ9A=
|
||||
|
||||
@@ -160,6 +160,7 @@ for <code>{{ .Entry.Manifest.Platform }}</code> platform.
|
||||
Pushover: &model.NotifPushover{
|
||||
Token: "uQiRzpo4DXghDmr9QzzfQu27cmVRsG",
|
||||
Recipient: "gznej3rKEVAvPUxu9vvNnqpmZpokzF",
|
||||
Timeout: utl.NewDuration(10 * time.Second),
|
||||
TemplateTitle: model.NotifDefaultTemplateTitle,
|
||||
TemplateBody: model.NotifDefaultTemplateBody,
|
||||
},
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/crazy-max/diun/v4/pkg/utl"
|
||||
)
|
||||
|
||||
// NotifPushover holds Pushover notification configuration details
|
||||
type NotifPushover struct {
|
||||
Token string `yaml:"token,omitempty" json:"token,omitempty" validate:"omitempty"`
|
||||
TokenFile string `yaml:"tokenFile,omitempty" json:"tokenFile,omitempty" validate:"omitempty,file"`
|
||||
Recipient string `yaml:"recipient,omitempty" json:"recipient,omitempty" validate:"omitempty"`
|
||||
RecipientFile string `yaml:"recipientFile,omitempty" json:"recipientFile,omitempty" validate:"omitempty,file"`
|
||||
Priority int `yaml:"priority,omitempty" json:"priority,omitempty" validate:"omitempty,min=-2,max=2"`
|
||||
Sound string `yaml:"sound,omitempty" json:"sound,omitempty" validate:"omitempty"`
|
||||
TemplateTitle string `yaml:"templateTitle,omitempty" json:"templateTitle,omitempty" validate:"required"`
|
||||
TemplateBody string `yaml:"templateBody,omitempty" json:"templateBody,omitempty" validate:"required"`
|
||||
Token string `yaml:"token,omitempty" json:"token,omitempty" validate:"omitempty"`
|
||||
TokenFile string `yaml:"tokenFile,omitempty" json:"tokenFile,omitempty" validate:"omitempty,file"`
|
||||
Recipient string `yaml:"recipient,omitempty" json:"recipient,omitempty" validate:"omitempty"`
|
||||
RecipientFile string `yaml:"recipientFile,omitempty" json:"recipientFile,omitempty" validate:"omitempty,file"`
|
||||
Priority int `yaml:"priority,omitempty" json:"priority,omitempty" validate:"omitempty,min=-2,max=2"`
|
||||
Sound string `yaml:"sound,omitempty" json:"sound,omitempty" validate:"omitempty"`
|
||||
Timeout *time.Duration `yaml:"timeout,omitempty" json:"timeout,omitempty" validate:"required"`
|
||||
TemplateTitle string `yaml:"templateTitle,omitempty" json:"templateTitle,omitempty" validate:"required"`
|
||||
TemplateBody string `yaml:"templateBody,omitempty" json:"templateBody,omitempty" validate:"required"`
|
||||
}
|
||||
|
||||
// GetDefaults gets the default values
|
||||
@@ -21,6 +28,7 @@ func (s *NotifPushover) GetDefaults() *NotifPushover {
|
||||
|
||||
// SetDefaults sets the default values
|
||||
func (s *NotifPushover) SetDefaults() {
|
||||
s.Timeout = utl.NewDuration(10 * time.Second)
|
||||
s.TemplateTitle = NotifDefaultTemplateTitle
|
||||
s.TemplateBody = NotifDefaultTemplateBody
|
||||
}
|
||||
|
||||
@@ -1,16 +1,24 @@
|
||||
package pushover
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/crazy-max/diun/v4/internal/model"
|
||||
"github.com/crazy-max/diun/v4/internal/msg"
|
||||
"github.com/crazy-max/diun/v4/internal/notif/notifier"
|
||||
"github.com/crazy-max/diun/v4/pkg/utl"
|
||||
"github.com/gregdel/pushover"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
const pushoverAPIURL = "https://api.pushover.net/1/messages.json"
|
||||
|
||||
// Client represents an active Pushover notification object
|
||||
type Client struct {
|
||||
*notifier.Notifier
|
||||
@@ -38,11 +46,15 @@ func (c *Client) Send(entry model.NotifEntry) error {
|
||||
token, err := utl.GetSecret(c.cfg.Token, c.cfg.TokenFile)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "cannot retrieve token secret for Pushover notifier")
|
||||
} else if token == "" {
|
||||
return errors.New("Pushover API token cannot be empty")
|
||||
}
|
||||
|
||||
recipient, err := utl.GetSecret(c.cfg.Recipient, c.cfg.RecipientFile)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "cannot retrieve recipient secret for Pushover notifier")
|
||||
} else if recipient == "" {
|
||||
return errors.New("Pushover recipient cannot be empty")
|
||||
}
|
||||
|
||||
message, err := msg.New(msg.Options{
|
||||
@@ -60,16 +72,77 @@ func (c *Client) Send(entry model.NotifEntry) error {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = pushover.New(token).SendMessage(&pushover.Message{
|
||||
Title: string(title),
|
||||
Message: string(body),
|
||||
Priority: c.cfg.Priority,
|
||||
Sound: c.cfg.Sound,
|
||||
URL: c.meta.URL,
|
||||
URLTitle: c.meta.Name,
|
||||
Timestamp: time.Now().Unix(),
|
||||
HTML: true,
|
||||
}, pushover.NewRecipient(recipient))
|
||||
cancelCtx, cancel := context.WithCancelCause(context.Background())
|
||||
timeoutCtx, _ := context.WithTimeoutCause(cancelCtx, *c.cfg.Timeout, errors.WithStack(context.DeadlineExceeded)) //nolint:govet // no need to manually cancel this context as we already rely on parent
|
||||
defer func() { cancel(errors.WithStack(context.Canceled)) }()
|
||||
|
||||
return err
|
||||
form := url.Values{}
|
||||
form.Add("token", token)
|
||||
form.Add("user", recipient)
|
||||
form.Add("title", string(title))
|
||||
form.Add("message", string(body))
|
||||
form.Add("priority", strconv.Itoa(c.cfg.Priority))
|
||||
if c.cfg.Sound != "" {
|
||||
form.Add("sound", c.cfg.Sound)
|
||||
}
|
||||
if c.meta.URL != "" {
|
||||
form.Add("url", c.meta.URL)
|
||||
}
|
||||
if c.meta.Name != "" {
|
||||
form.Add("url_title", c.meta.Name)
|
||||
}
|
||||
form.Add("timestamp", strconv.FormatInt(time.Now().Unix(), 10))
|
||||
form.Add("html", "1")
|
||||
|
||||
hc := http.Client{}
|
||||
req, err := http.NewRequestWithContext(timeoutCtx, "POST", pushoverAPIURL, strings.NewReader(form.Encode()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
req.Header.Set("User-Agent", c.meta.UserAgent)
|
||||
|
||||
resp, err := hc.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.Header != nil {
|
||||
var appLimit, appRemaining int
|
||||
var appReset time.Time
|
||||
if limit := resp.Header.Get("X-Limit-App-Limit"); limit != "" {
|
||||
if i, err := strconv.Atoi(limit); err == nil {
|
||||
appLimit = i
|
||||
}
|
||||
}
|
||||
if remaining := resp.Header.Get("X-Limit-App-Remaining"); remaining != "" {
|
||||
if i, err := strconv.Atoi(remaining); err == nil {
|
||||
appRemaining = i
|
||||
}
|
||||
}
|
||||
if reset := resp.Header.Get("X-Limit-App-Reset"); reset != "" {
|
||||
if i, err := strconv.Atoi(reset); err == nil {
|
||||
appReset = time.Unix(int64(i), 0)
|
||||
}
|
||||
}
|
||||
log.Debug().Msgf("Pushover app limit: %d, remaining: %d, reset: %s", appLimit, appRemaining, appReset)
|
||||
}
|
||||
|
||||
var respBody struct {
|
||||
Status int `json:"status"`
|
||||
Request string `json:"request"`
|
||||
Errors []string `json:"errors"`
|
||||
User string `json:"user"`
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
||||
if err = json.NewDecoder(resp.Body).Decode(&respBody); err != nil {
|
||||
return errors.Wrapf(err, "cannot decode JSON body response for HTTP %d %s status: %+v", resp.StatusCode, http.StatusText(resp.StatusCode), respBody)
|
||||
}
|
||||
if respBody.Status != 1 {
|
||||
return errors.Errorf("Pushover API call failed with status %d: %v", respBody.Status, respBody.Errors)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
9
vendor/github.com/gregdel/pushover/.travis.yml
generated
vendored
9
vendor/github.com/gregdel/pushover/.travis.yml
generated
vendored
@@ -1,9 +0,0 @@
|
||||
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
21
vendor/github.com/gregdel/pushover/LICENSE
generated
vendored
@@ -1,21 +0,0 @@
|
||||
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
134
vendor/github.com/gregdel/pushover/README.md
generated
vendored
@@ -1,134 +0,0 @@
|
||||
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
18
vendor/github.com/gregdel/pushover/errors.go
generated
vendored
@@ -1,18 +0,0 @@
|
||||
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
|
||||
}
|
||||
118
vendor/github.com/gregdel/pushover/glances.go
generated
vendored
118
vendor/github.com/gregdel/pushover/glances.go
generated
vendored
@@ -1,118 +0,0 @@
|
||||
package pushover
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
const (
|
||||
// GlancesAllDevices can be passed as a device name to send a glances-message to all devices
|
||||
GlancesAllDevices = ""
|
||||
// GlancesMessageMaxTitleLength is the max title length in a pushover glance update
|
||||
GlancesMessageMaxTitleLength = 100
|
||||
// GlancesMessageMaxTextLength is the max text length in a pushover glance update
|
||||
GlancesMessageMaxTextLength = 100
|
||||
// GlancesMessageMaxSubtextLength is the max subtext length in a pushover glance update
|
||||
GlancesMessageMaxSubtextLength = 100
|
||||
)
|
||||
|
||||
// Glance represents a pushover glances update request.
|
||||
type Glance struct {
|
||||
// Title(max 100): a description of the data being shown, such as "Widgets Sold"
|
||||
Title *string
|
||||
// Text(max 100): the main line of data, used on most screens
|
||||
Text *string
|
||||
// Subtext(max 100): a second line of data
|
||||
Subtext *string
|
||||
// Count(can be negative): shown on smaller screens; useful for simple counts
|
||||
Count *int
|
||||
// Percent(0-100): shown on some screens as a progress bar/circle
|
||||
Percent *int
|
||||
DeviceName string
|
||||
}
|
||||
|
||||
// Int returns the pointer of the input i
|
||||
// Used to check for the Glance API if a parameter was left empty or if none was specified
|
||||
func Int(i int) *int {
|
||||
return &i
|
||||
}
|
||||
|
||||
// String returns the pointer of the input s
|
||||
// Used to check for the Glance API if a parameter was left empty or if none was specified
|
||||
func String(s string) *string {
|
||||
return &s
|
||||
}
|
||||
|
||||
func (m *Glance) validate() error {
|
||||
// check if data is present
|
||||
if m.Title == nil && m.Text == nil && m.Subtext == nil && m.Count == nil && m.Percent == nil {
|
||||
return ErrGlancesMissingData
|
||||
}
|
||||
if m.Title != nil && utf8.RuneCountInString(*m.Title) > GlancesMessageMaxTitleLength {
|
||||
return ErrGlancesTitleTooLong
|
||||
}
|
||||
if m.Text != nil && utf8.RuneCountInString(*m.Text) > GlancesMessageMaxTextLength {
|
||||
return ErrGlancesTextTooLong
|
||||
}
|
||||
if m.Subtext != nil && utf8.RuneCountInString(*m.Subtext) > GlancesMessageMaxSubtextLength {
|
||||
return ErrGlancesSubtextTooLong
|
||||
}
|
||||
if m.Percent != nil && (*m.Percent < 0 || *m.Percent > 100) {
|
||||
return ErrGlancesInvalidPercent
|
||||
}
|
||||
// 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
|
||||
}
|
||||
|
||||
// send sends the message using the pushover and the recipient tokens.
|
||||
func (m *Glance) send(pToken, rToken string) (*Response, error) {
|
||||
url := fmt.Sprintf("%s/glances.json", APIEndpoint)
|
||||
|
||||
params := map[string]string{
|
||||
"token": pToken,
|
||||
"user": rToken,
|
||||
}
|
||||
if m.DeviceName != "" {
|
||||
params["device"] = m.DeviceName
|
||||
}
|
||||
|
||||
// data
|
||||
if m.Count != nil {
|
||||
params["count"] = strconv.Itoa(*m.Count)
|
||||
}
|
||||
if m.Percent != nil {
|
||||
params["percent"] = strconv.Itoa(*m.Percent)
|
||||
}
|
||||
if m.Title != nil {
|
||||
params["title"] = *m.Title
|
||||
}
|
||||
if m.Text != nil {
|
||||
params["text"] = *m.Text
|
||||
}
|
||||
if m.Subtext != nil {
|
||||
params["subtext"] = *m.Subtext
|
||||
}
|
||||
|
||||
req, err := newURLEncodedRequest("POST", url, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := new(Response)
|
||||
if err = do(req, resp, true); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
45
vendor/github.com/gregdel/pushover/helpers.go
generated
vendored
45
vendor/github.com/gregdel/pushover/helpers.go
generated
vendored
@@ -1,45 +0,0 @@
|
||||
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
57
vendor/github.com/gregdel/pushover/limit.go
generated
vendored
@@ -1,57 +0,0 @@
|
||||
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
|
||||
}
|
||||
253
vendor/github.com/gregdel/pushover/message.go
generated
vendored
253
vendor/github.com/gregdel/pushover/message.go
generated
vendored
@@ -1,253 +0,0 @@
|
||||
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
|
||||
TTL time.Duration
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
if m.TTL != 0 {
|
||||
ret["ttl"] = strconv.FormatFloat(m.TTL.Seconds(), 'f', -1, 64)
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
236
vendor/github.com/gregdel/pushover/pushover.go
generated
vendored
236
vendor/github.com/gregdel/pushover/pushover.go
generated
vendored
@@ -1,236 +0,0 @@
|
||||
// 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")
|
||||
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")
|
||||
ErrGlancesMissingData = errors.New("pushover: glance update data missing")
|
||||
ErrGlancesTitleTooLong = errors.New("pushover: glance title too long")
|
||||
ErrGlancesTextTooLong = errors.New("pushover: glance text too long")
|
||||
ErrGlancesSubtextTooLong = errors.New("pushover: glance subtext too long")
|
||||
ErrGlancesInvalidPercent = errors.New("pushover: glance percent must be in range of 0-100")
|
||||
)
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// SendGlanceUpdate is used to send glance updates to a recipient.
|
||||
// It can be used to display widgets on a smart watch
|
||||
func (p *Pushover) SendGlanceUpdate(msg *Glance, rec *Recipient) (*Response, error) {
|
||||
// Validate pushover
|
||||
if err := p.validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Validate rec
|
||||
if err := rec.validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Validate msg
|
||||
if err := msg.validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return msg.send(p.token, rec.token)
|
||||
}
|
||||
|
||||
// GetReceiptDetails return detailed information 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. The Errors field of the
|
||||
// RecipientDetails object will contain an error 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("POST", 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
59
vendor/github.com/gregdel/pushover/receipt_details.go
generated
vendored
@@ -1,59 +0,0 @@
|
||||
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
43
vendor/github.com/gregdel/pushover/recipient.go
generated
vendored
@@ -1,43 +0,0 @@
|
||||
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
69
vendor/github.com/gregdel/pushover/request.go
generated
vendored
@@ -1,69 +0,0 @@
|
||||
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
|
||||
}
|
||||
26
vendor/github.com/gregdel/pushover/response.go
generated
vendored
26
vendor/github.com/gregdel/pushover/response.go
generated
vendored
@@ -1,26 +0,0 @@
|
||||
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("Status: %d\n", r.Status)
|
||||
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
|
||||
}
|
||||
3
vendor/modules.txt
vendored
3
vendor/modules.txt
vendored
@@ -278,9 +278,6 @@ github.com/gorilla/mux
|
||||
# github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674
|
||||
## explicit; go 1.20
|
||||
github.com/gorilla/websocket
|
||||
# github.com/gregdel/pushover v1.3.1
|
||||
## explicit; go 1.14
|
||||
github.com/gregdel/pushover
|
||||
# github.com/hashicorp/cronexpr v1.1.2
|
||||
## explicit
|
||||
github.com/hashicorp/cronexpr
|
||||
|
||||
Reference in New Issue
Block a user