| 1 | package daemon |
| 2 | |
| 3 | import ( |
| 4 | "context" |
| 5 | "errors" |
| 6 | "time" |
| 7 | |
| 8 | "github.com/FelineStateMachine/tinyrelay/internal/event" |
| 9 | "github.com/FelineStateMachine/tinyrelay/internal/relay" |
| 10 | ) |
| 11 | |
| 12 | // nip43Invite returns the relay-signed, ephemeral invite response for a |
| 13 | // kind-28935 request. The response is deliberately generated outside storage |
| 14 | // and is only available to an authenticated member allowed by invite policy. |
| 15 | func (t *Tenant) nip43Invite(ctx context.Context, f event.Filter, s relay.Session) (event.Event, bool, error) { |
| 16 | if !containsKind(f.Kinds, event.KIND_NIP43_INVITE) { |
| 17 | return event.Event{}, false, nil |
| 18 | } |
| 19 | if f.Limit != nil && *f.Limit <= 0 { |
| 20 | return event.Event{}, false, nil |
| 21 | } |
| 22 | now := time.Now().Unix() |
| 23 | if !nip43FilterCouldMatch(f, t.records.PublicKey(), now) { |
| 24 | return event.Event{}, false, nil |
| 25 | } |
| 26 | if len(s.PubKeys) == 0 { |
| 27 | return event.Event{}, false, errors.New("auth-required: NIP-43 invite requests require AUTH" ) |
| 28 | } |
| 29 | invite, err := t.community.IssueNIP43Invite(ctx, s.PubKeys[0]) |
| 30 | if err != nil { |
| 31 | return event.Event{}, false, err |
| 32 | } |
| 33 | response, err := t.records.GenerateEphemeral(ctx, event.KIND_NIP43_INVITE, [][]string{{"-" }, {"claim" , invite.Code}}, "" , now) |
| 34 | if err != nil { |
| 35 | return event.Event{}, false, err |
| 36 | } |
| 37 | return response, true, nil |
| 38 | } |
| 39 | |
| 40 | func nip43FilterCouldMatch(f event.Filter, relayPubKey string, now int64) bool { |
| 41 | if f.IDs != nil || f.Search != "" { |
| 42 | return false |
| 43 | } |
| 44 | if f.Authors != nil && !membershipContainsString(f.Authors, relayPubKey) { |
| 45 | return false |
| 46 | } |
| 47 | if f.Since != nil && now < *f.Since || f.Until != nil && now > *f.Until { |
| 48 | return false |
| 49 | } |
| 50 | for name, values := range f.Tags { |
| 51 | if name != "claim" || len(values) == 0 { |
| 52 | return false |
| 53 | } |
| 54 | } |
| 55 | return true |
| 56 | } |
| 57 | |
| 58 | func withoutNIP43Invite(f event.Filter) (event.Filter, bool) { |
| 59 | if len(f.Kinds) == 0 { |
| 60 | return f, true |
| 61 | } |
| 62 | kinds := make([]int, 0, len(f.Kinds)) |
| 63 | for _, kind := range f.Kinds { |
| 64 | if kind != event.KIND_NIP43_INVITE { |
| 65 | kinds = append(kinds, kind) |
| 66 | } |
| 67 | } |
| 68 | if len(kinds) == 0 { |
| 69 | return event.Filter{}, false |
| 70 | } |
| 71 | f.Kinds = kinds |
| 72 | return f, true |
| 73 | } |
| 74 | |
| 75 | func containsKind(kinds []int, wanted int) bool { |
| 76 | for _, kind := range kinds { |
| 77 | if kind == wanted { |
| 78 | return true |
| 79 | } |
| 80 | } |
| 81 | return false |
| 82 | } |
| 83 | |
| 84 | func membershipContainsString(values []string, wanted string) bool { |
| 85 | for _, value := range values { |
| 86 | if value == wanted { |
| 87 | return true |
| 88 | } |
| 89 | } |
| 90 | return false |
| 91 | } |