Adds NIP-33 support for replaceable events.

This commit is contained in:
Steve Perkins 2023-03-31 10:03:35 -04:00 committed by fiatjaf_
parent 7ea3cd5431
commit a82e5edb0d
5 changed files with 72 additions and 7 deletions

View File

@ -308,7 +308,7 @@ func (s *Server) handleWebsocket(w http.ResponseWriter, r *http.Request) {
func (s *Server) handleNIP11(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
supportedNIPs := []int{9, 11, 12, 15, 16, 20}
supportedNIPs := []int{9, 11, 12, 15, 16, 20, 33}
if _, ok := s.relay.(Auther); ok {
supportedNIPs = append(supportedNIPs, 42)
}

View File

@ -97,7 +97,12 @@ func (ess *ElasticsearchStorage) Init() error {
}
func (ess *ElasticsearchStorage) DeleteEvent(id string, pubkey string) error {
// todo: is pubkey match required?
// first do get by ID and check that pubkeys match
// this is cheaper than doing delete by query, which also doesn't work with bulk indexer.
found, _ := ess.getByID(&nostr.Filter{IDs: []string{id}})
if len(found) == 0 || found[0].PubKey != pubkey {
return nil
}
done := make(chan error)
err := ess.bi.Add(
@ -132,9 +137,9 @@ func (ess *ElasticsearchStorage) DeleteEvent(id string, pubkey string) error {
return err
}
func (ess *ElasticsearchStorage) SaveEvent(event *nostr.Event) error {
func (ess *ElasticsearchStorage) SaveEvent(evt *nostr.Event) error {
ie := &IndexedEvent{
Event: *event,
Event: *evt,
}
// post processing: index for FTS
@ -144,8 +149,8 @@ func (ess *ElasticsearchStorage) SaveEvent(event *nostr.Event) error {
// - if it's valid JSON just index the "values" and not the keys
// - more content introspection: language detection
// - denormalization... attach profile + ranking signals to events
if event.Kind != 4 {
ie.ContentSearch = event.Content
if evt.Kind != 4 {
ie.ContentSearch = evt.Content
}
data, err := json.Marshal(ie)
@ -155,13 +160,57 @@ func (ess *ElasticsearchStorage) SaveEvent(event *nostr.Event) error {
done := make(chan error)
// delete replaceable events
deleteIDs := []string{}
queryForDelete := func(filter *nostr.Filter) {
toDelete, _ := ess.QueryEvents(filter)
for _, e := range toDelete {
// KindRecommendServer: we can't query ES for exact content match
// so query by kind and loop over results to compare content
if evt.Kind == nostr.KindRecommendServer {
if e.Content == evt.Content {
deleteIDs = append(deleteIDs, e.ID)
}
} else {
deleteIDs = append(deleteIDs, e.ID)
}
}
}
if evt.Kind == nostr.KindSetMetadata || evt.Kind == nostr.KindContactList || evt.Kind == nostr.KindRecommendServer || (10000 <= evt.Kind && evt.Kind < 20000) {
// delete past events from this user
queryForDelete(&nostr.Filter{
Authors: []string{evt.PubKey},
Kinds: []int{evt.Kind},
})
} else if evt.Kind >= 30000 && evt.Kind < 40000 {
// NIP-33
d := evt.Tags.GetFirst([]string{"d"})
if d != nil {
queryForDelete(&nostr.Filter{
Authors: []string{evt.PubKey},
Kinds: []int{evt.Kind},
Tags: nostr.TagMap{
"d": []string{d.Value()},
},
})
}
}
for _, id := range deleteIDs {
ess.bi.Add(
context.Background(),
esutil.BulkIndexerItem{
Action: "delete",
DocumentID: id,
})
}
// adapted from:
// https://github.com/elastic/go-elasticsearch/blob/main/_examples/bulk/indexer.go#L196
err = ess.bi.Add(
context.Background(),
esutil.BulkIndexerItem{
Action: "index",
DocumentID: event.ID,
DocumentID: evt.ID,
Body: bytes.NewReader(data),
OnSuccess: func(ctx context.Context, item esutil.BulkIndexerItem, res esutil.BulkIndexerResponseItem) {
close(done)

View File

@ -172,6 +172,7 @@ func isGetByID(filter *nostr.Filter) bool {
len(filter.Authors) == 0 &&
len(filter.Kinds) == 0 &&
len(filter.Tags) == 0 &&
len(filter.Search) == 0 &&
filter.Since == nil &&
filter.Until == nil

View File

@ -16,6 +16,13 @@ func (b *PostgresBackend) SaveEvent(evt *nostr.Event) error {
// delete past recommend_server events equal to this one
b.DB.Exec(`DELETE FROM event WHERE pubkey = $1 AND kind = $2 AND content = $3`,
evt.PubKey, evt.Kind, evt.Content)
} else if evt.Kind >= 30000 && evt.Kind < 40000 {
// NIP-33
d := evt.Tags.GetFirst([]string{"d"})
if d != nil {
b.DB.Exec(`DELETE FROM event WHERE pubkey = $1 AND kind = $2 AND tagvalues && ARRAY[$3]`,
evt.PubKey, evt.Kind, d.Value())
}
}
// insert

View File

@ -2,6 +2,7 @@ package sqlite3
import (
"encoding/json"
"fmt"
"github.com/fiatjaf/relayer/storage"
"github.com/nbd-wtf/go-nostr"
@ -16,6 +17,13 @@ func (b *SQLite3Backend) SaveEvent(evt *nostr.Event) error {
// delete past recommend_server events equal to this one
b.DB.Exec(`DELETE FROM event WHERE pubkey = $1 AND kind = $2 AND content = $3`,
evt.PubKey, evt.Kind, evt.Content)
} else if evt.Kind >= 30000 && evt.Kind < 40000 {
// NIP-33
d := evt.Tags.GetFirst([]string{"d"})
if d != nil {
tagsLike := fmt.Sprintf(`%%"d","%s"%%`, d.Value())
b.DB.Exec(`DELETE FROM event WHERE pubkey = $1 AND kind = $2 AND tags LIKE $3`, evt.PubKey, evt.Kind, tagsLike)
}
}
// insert