diff --git a/docs/notif/pushover.md b/docs/notif/pushover.md
index 03a81d13..6a118661 100644
--- a/docs/notif/pushover.md
+++ b/docs/notif/pushover.md
@@ -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`
diff --git a/go.mod b/go.mod
index cb57080e..b4acfca3 100644
--- a/go.mod
+++ b/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
diff --git a/go.sum b/go.sum
index e8d8f71e..68016043 100644
--- a/go.sum
+++ b/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=
diff --git a/internal/config/config_test.go b/internal/config/config_test.go
index 1a282697..4f950896 100644
--- a/internal/config/config_test.go
+++ b/internal/config/config_test.go
@@ -160,6 +160,7 @@ for {{ .Entry.Manifest.Platform }} platform.
Pushover: &model.NotifPushover{
Token: "uQiRzpo4DXghDmr9QzzfQu27cmVRsG",
Recipient: "gznej3rKEVAvPUxu9vvNnqpmZpokzF",
+ Timeout: utl.NewDuration(10 * time.Second),
TemplateTitle: model.NotifDefaultTemplateTitle,
TemplateBody: model.NotifDefaultTemplateBody,
},
diff --git a/internal/model/notif_pushover.go b/internal/model/notif_pushover.go
index 76907449..5affe97d 100644
--- a/internal/model/notif_pushover.go
+++ b/internal/model/notif_pushover.go
@@ -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
}
diff --git a/internal/notif/pushover/client.go b/internal/notif/pushover/client.go
index 2f0ddafb..f942b160 100644
--- a/internal/notif/pushover/client.go
+++ b/internal/notif/pushover/client.go
@@ -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
}
diff --git a/vendor/github.com/gregdel/pushover/.travis.yml b/vendor/github.com/gregdel/pushover/.travis.yml
deleted file mode 100644
index 0668178b..00000000
--- a/vendor/github.com/gregdel/pushover/.travis.yml
+++ /dev/null
@@ -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
diff --git a/vendor/github.com/gregdel/pushover/LICENSE b/vendor/github.com/gregdel/pushover/LICENSE
deleted file mode 100644
index e0932e54..00000000
--- a/vendor/github.com/gregdel/pushover/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-The MIT License (MIT)
-
-Copyright (c) Grégoire Delattre
-
-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.
diff --git a/vendor/github.com/gregdel/pushover/README.md b/vendor/github.com/gregdel/pushover/README.md
deleted file mode 100644
index 7e26ac72..00000000
--- a/vendor/github.com/gregdel/pushover/README.md
+++ /dev/null
@@ -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)
-```
diff --git a/vendor/github.com/gregdel/pushover/errors.go b/vendor/github.com/gregdel/pushover/errors.go
deleted file mode 100644
index 452397d0..00000000
--- a/vendor/github.com/gregdel/pushover/errors.go
+++ /dev/null
@@ -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
-}
diff --git a/vendor/github.com/gregdel/pushover/glances.go b/vendor/github.com/gregdel/pushover/glances.go
deleted file mode 100644
index 0a0771de..00000000
--- a/vendor/github.com/gregdel/pushover/glances.go
+++ /dev/null
@@ -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
-}
diff --git a/vendor/github.com/gregdel/pushover/helpers.go b/vendor/github.com/gregdel/pushover/helpers.go
deleted file mode 100644
index e95b3895..00000000
--- a/vendor/github.com/gregdel/pushover/helpers.go
+++ /dev/null
@@ -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
-}
diff --git a/vendor/github.com/gregdel/pushover/limit.go b/vendor/github.com/gregdel/pushover/limit.go
deleted file mode 100644
index 9ac72336..00000000
--- a/vendor/github.com/gregdel/pushover/limit.go
+++ /dev/null
@@ -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
-}
diff --git a/vendor/github.com/gregdel/pushover/message.go b/vendor/github.com/gregdel/pushover/message.go
deleted file mode 100644
index 30ddda81..00000000
--- a/vendor/github.com/gregdel/pushover/message.go
+++ /dev/null
@@ -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))
-}
diff --git a/vendor/github.com/gregdel/pushover/pushover.go b/vendor/github.com/gregdel/pushover/pushover.go
deleted file mode 100644
index 2fcf6e89..00000000
--- a/vendor/github.com/gregdel/pushover/pushover.go
+++ /dev/null
@@ -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
-}
diff --git a/vendor/github.com/gregdel/pushover/receipt_details.go b/vendor/github.com/gregdel/pushover/receipt_details.go
deleted file mode 100644
index 38a21290..00000000
--- a/vendor/github.com/gregdel/pushover/receipt_details.go
+++ /dev/null
@@ -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
-}
diff --git a/vendor/github.com/gregdel/pushover/recipient.go b/vendor/github.com/gregdel/pushover/recipient.go
deleted file mode 100644
index 2074078c..00000000
--- a/vendor/github.com/gregdel/pushover/recipient.go
+++ /dev/null
@@ -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"`
-}
diff --git a/vendor/github.com/gregdel/pushover/request.go b/vendor/github.com/gregdel/pushover/request.go
deleted file mode 100644
index 0be7c29a..00000000
--- a/vendor/github.com/gregdel/pushover/request.go
+++ /dev/null
@@ -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
-}
diff --git a/vendor/github.com/gregdel/pushover/response.go b/vendor/github.com/gregdel/pushover/response.go
deleted file mode 100644
index f22cd2e7..00000000
--- a/vendor/github.com/gregdel/pushover/response.go
+++ /dev/null
@@ -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
-}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index dd84aeca..00952609 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -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