Files
lazyntfy/internal/store/store.go
T

145 lines
2.8 KiB
Go
Raw Normal View History

// Package store keeps an in-memory, newest-first view of ntfy messages.
package store
import (
"strings"
"sync"
)
// Attachment is a file linked from a message.
type Attachment struct {
Name string
URL string
Type string
Size int64
}
// Message is one delivered ntfy notification.
type Message struct {
ID string
Time int64
Expires int64
Topic string
Title string
Body string
Tags []string
Priority int
Click string
Attachment *Attachment
Actions string
}
// Store is safe for concurrent readers and writers.
type Store struct {
mu sync.Mutex
byTopic map[string][]Message
index map[string]string
order []string
}
// New returns an empty store.
func New() *Store {
return &Store{
byTopic: map[string][]Message{},
index: map[string]string{},
}
}
// Put inserts a message. A repeated id is ignored.
func (s *Store) Put(m Message) {
s.mu.Lock()
defer s.mu.Unlock()
if m.ID != "" {
if _, ok := s.index[m.ID]; ok {
return
}
s.index[m.ID] = m.Topic
}
s.ensure(m.Topic)
s.byTopic[m.Topic] = insertNewest(s.byTopic[m.Topic], m)
}
// Delete removes the message with id.
func (s *Store) Delete(id string) {
s.mu.Lock()
defer s.mu.Unlock()
topic, ok := s.index[id]
if !ok {
return
}
list := s.byTopic[topic]
for i, m := range list {
if m.ID == id {
s.byTopic[topic] = append(list[:i], list[i+1:]...)
break
}
}
delete(s.index, id)
}
// Clear removes every message for a topic.
func (s *Store) Clear(topic string) {
s.mu.Lock()
defer s.mu.Unlock()
for _, m := range s.byTopic[topic] {
if m.ID != "" {
delete(s.index, m.ID)
}
}
s.byTopic[topic] = nil
}
// List returns a copy of one topic, newest first.
func (s *Store) List(topic string) []Message {
s.mu.Lock()
defer s.mu.Unlock()
src := s.byTopic[topic]
out := make([]Message, len(src))
copy(out, src)
return out
}
// Topics returns topic names in the order they were first seen.
func (s *Store) Topics() []string {
s.mu.Lock()
defer s.mu.Unlock()
out := make([]string, len(s.order))
copy(out, s.order)
return out
}
func (s *Store) ensure(topic string) {
if _, ok := s.byTopic[topic]; ok {
return
}
s.byTopic[topic] = nil
s.order = append(s.order, topic)
}
func insertNewest(list []Message, m Message) []Message {
i := 0
for i < len(list) && list[i].Time > m.Time {
i++
}
list = append(list, Message{})
copy(list[i+1:], list[i:])
list[i] = m
return list
}
// Match reports whether needle is contained in the message's visible fields.
func Match(m Message, title, needle string) bool {
needle = strings.ToLower(strings.TrimSpace(needle))
if needle == "" {
return true
}
hay := strings.ToLower(strings.Join([]string{
title,
m.Title,
m.Body,
m.ID,
strings.Join(m.Tags, " "),
}, " "))
return strings.Contains(hay, needle)
}