diff --git a/relay_interface.go b/relay_interface.go new file mode 100644 index 0000000..a017212 --- /dev/null +++ b/relay_interface.go @@ -0,0 +1,75 @@ +package eventstore + +import ( + "context" + "fmt" + + "github.com/nbd-wtf/go-nostr" +) + +// RelayInterface is a wrapper thing that unifies Store and nostr.Relay under a common API. +type RelayInterface interface { + Publish(ctx context.Context, event nostr.Event) (nostr.Status, error) + QuerySync(ctx context.Context, filter nostr.Filter, opts ...nostr.SubscriptionOption) ([]*nostr.Event, error) +} + +type RelayWrapper struct { + Store +} + +func (w RelayWrapper) Publish(ctx context.Context, evt *nostr.Event) error { + if evt == nil { + return fmt.Errorf("event is nil") + } + + if 20000 <= evt.Kind && evt.Kind < 30000 { + // do not store ephemeral events + return nil + } else if evt.Kind == 0 || evt.Kind == 3 || (10000 <= evt.Kind && evt.Kind < 20000) { + // replaceable event, delete before storing + ch, err := w.Store.QueryEvents(ctx, nostr.Filter{Authors: []string{evt.PubKey}, Kinds: []int{evt.Kind}}) + if err != nil { + return fmt.Errorf("failed to query before replacing: %w", err) + } + previous := <-ch + if err := w.Store.DeleteEvent(ctx, previous); err != nil { + return fmt.Errorf("failed to delete event for replacing: %w", err) + } + } else if 30000 <= evt.Kind && evt.Kind < 40000 { + // parameterized replaceable event, delete before storing + d := evt.Tags.GetFirst([]string{"d", ""}) + if d != nil { + ch, err := w.Store.QueryEvents(ctx, nostr.Filter{Authors: []string{evt.PubKey}, Kinds: []int{evt.Kind}, Tags: nostr.TagMap{"d": []string{d.Value()}}}) + if err != nil { + return fmt.Errorf("failed to query before parameterized replacing: %w", err) + } + previous := <-ch + if previous != nil { + if err := w.Store.DeleteEvent(ctx, previous); err != nil { + return fmt.Errorf("failed to delete event for parameterized replacing: %w", err) + } + } + } + } + + return w.SaveEvent(ctx, evt) +} + +func (w RelayWrapper) QuerySync(ctx context.Context, filter nostr.Filter, opts ...nostr.SubscriptionOption) ([]*nostr.Event, error) { + ch, err := w.Store.QueryEvents(ctx, filter) + if err != nil { + return nil, fmt.Errorf("failed to query: %w", err) + } + + n := filter.Limit + if n == 0 { + n = 500 + } + + results := make([]*nostr.Event, 0, n) + for evt := range ch { + results = append(results, evt) + } + + return results, nil +} diff --git a/interface.go b/store.go similarity index 100% rename from interface.go rename to store.go diff --git a/wrapper.go b/wrapper.go deleted file mode 100644 index 79c043a..0000000 --- a/wrapper.go +++ /dev/null @@ -1,17 +0,0 @@ -package eventstore - -import ( - "context" - - "github.com/nbd-wtf/go-nostr" -) - -type Wrapper struct { - Store -} - -func (w Wrapper) InjectEvent(ctx context.Context, evt *nostr.Event) error { - w.SaveEvent(ctx, evt) - - return nil -}