package ntfy import ( "bytes" "encoding/json" "fmt" "io" ) // Event is one NDJSON object from GET /topic/json. type Event struct { ID string `json:"id"` Time int64 `json:"time"` Expires int64 `json:"expires,omitempty"` Event string `json:"event"` Topic string `json:"topic"` Message string `json:"message"` Title string `json:"title"` Tags []string `json:"tags"` Priority int `json:"priority"` Click string `json:"click"` SequenceID string `json:"sequence_id,omitempty"` Attachment *Attachment `json:"attachment,omitempty"` Actions json.RawMessage `json:"actions,omitempty"` } // Attachment describes a file linked from a message. type Attachment struct { Name string `json:"name"` URL string `json:"url"` Type string `json:"type,omitempty"` Size int64 `json:"size,omitempty"` Expires int64 `json:"expires,omitempty"` } // ParseLine decodes one NDJSON line. ok is false for blank lines. func ParseLine(line []byte) (Event, bool, error) { line = bytes.TrimSpace(line) if len(line) == 0 { return Event{}, false, nil } var ev Event if err := json.Unmarshal(line, &ev); err != nil { return Event{}, false, err } if ev.Event == "" { return Event{}, false, fmt.Errorf("missing event") } return ev, true, nil } // ReadLines yields each decoded event until EOF or a read error. // Malformed JSON is reported and skipped so one bad line does not kill the stream. func ReadLines(r io.Reader, yield func(Event) error, onBad func(error)) error { sc := newScanner(r) for sc.Scan() { ev, ok, err := ParseLine(sc.Bytes()) if err != nil { if onBad != nil { onBad(err) } continue } if !ok { continue } if err := yield(ev); err != nil { return err } } return sc.Err() }