package khatru import ( "context" "errors" "fmt" "strconv" "strings" "fiatjaf.com/nostr" "golang.org/x/sync/errgroup" ) var ( ErrNothingToDelete = errors.New("blocked: nothing to delete") ErrNotAuthor = errors.New("blocked: you are not the author of this event") ) // event deletion -- nip09 func (rl *Relay) handleDeleteRequest(ctx context.Context, evt nostr.Event) error { if nil == rl.QueryStored || nil == rl.DeleteEvent { // if we don't have a way to query or to delete that means we won't delete anything return ErrNothingToDelete } haveDeletedSomething := false for _, tag := range evt.Tags { if len(tag) >= 2 { var f nostr.Filter switch tag[0] { case "e": id, err := nostr.IDFromHex(tag[1]) if err != nil { return fmt.Errorf("invalid 'e' tag '%s': %w", tag[1], err) } f = nostr.Filter{IDs: []nostr.ID{id}} case "a": spl := strings.SplitN(tag[1], ":", 3) if len(spl) != 3 { continue } kind, err := strconv.Atoi(spl[0]) if err != nil { continue } author, err := nostr.PubKeyFromHex(spl[1]) if err != nil { continue } identifier := spl[2] f = nostr.Filter{ Kinds: []nostr.Kind{nostr.Kind(kind)}, Authors: []nostr.PubKey{author}, Tags: nostr.TagMap{"d": []string{identifier}}, Until: evt.CreatedAt, } default: continue } ctx := context.WithValue(ctx, internalCallKey, struct{}{}) errg, ctx := errgroup.WithContext(ctx) for target := range rl.QueryStored(ctx, f) { // got the event, now check if the user can delete it if target.PubKey == evt.PubKey { // delete it errg.Go(func() error { if err := rl.DeleteEvent(ctx, target.ID); err != nil { return err } // if it was tracked to be expired that is not needed anymore if rl.expirationManager != nil { rl.expirationManager.removeEvent(target.ID) } haveDeletedSomething = true return nil }) } else { // fail and stop here return ErrNotAuthor } // don't try to query this same event again break } if err := errg.Wait(); err != nil { return err } } } if haveDeletedSomething { return nil } return ErrNothingToDelete }