package adapter import ( "context" "encoding/json" "errors" "fmt" . "forge.lightcrystal.systems/lightcrystal/underbbs/models" nostr "github.com/nbd-wtf/go-nostr" "strings" ) type Adapter interface { Init(Settings, chan SocketData) error Subscribe(string) []error SendMessage(Message) error Follow(Author) error Unfollow(Author) error GetFollowers() error UpdateMetadata(interface{}) error DefaultSubscriptionFilter() string } type NostrAdapter struct { msgChan chan SocketData nickname string privkey string relays []*nostr.Relay } func (self *NostrAdapter) Init(settings Settings, msgChan chan SocketData) error { self.nickname = settings.Nickname self.privkey = *settings.PrivKey self.msgChan = msgChan ctx := context.Background() relays := strings.Split(*settings.Relays, ",") for _, r := range relays { pr, _ := nostr.RelayConnect(ctx, strings.Trim(r, " ")) if pr == nil { return errors.New("Relay connection could not be completed") } self.relays = append(self.relays, pr) } return nil } func (self *NostrAdapter) Subscribe(filter string) []error { var filters nostr.Filters err := json.Unmarshal([]byte(filter), &filters) if err != nil { return []error{err} } errs := make([]error, 1) for _, r := range self.relays { sub, err := r.Subscribe(context.Background(), filters) if err != nil { errs = append(errs, err) } else { go func() { for ev := range sub.Events { // try sequentially to encode into an underbbs object // and send it to the appropriate channel m, err := nostrEventToMsg(ev) if err == nil { self.msgChan <- m } } }() } } if len(errs) > 0 { return errs } return nil } func (self *NostrAdapter) SendMessage(msg Message) error { return nil } func (self *NostrAdapter) Follow(author Author) error { return nil } func (self *NostrAdapter) Unfollow(author Author) error { return nil } func (self *NostrAdapter) GetFollowers() error { return nil } func (self *NostrAdapter) UpdateMetadata(data interface{}) error { return nil } func (self *NostrAdapter) DefaultSubscriptionFilter() string { return "[{'iDs':null,'kinds':[1],'authors':null,'tags':nil,'since':nil,'until':nil,'limit':0}]" } func nostrEventToMsg(evt *nostr.Event) (Message, error) { m := Message{ Protocol: "nostr", } if evt == nil { return m, errors.New("no event") } switch evt.Kind { case nostr.KindTextNote: m.Uri = evt.ID m.Author = Author{ Id: evt.PubKey, } m.Created = evt.CreatedAt.Time() m.Content = evt.Content return m, nil default: return m, errors.New(fmt.Sprintf("unsupported event kind: %d", evt.Kind)) } }