// 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 }