clone

a self-hosted multitenant nostr relay with sites, file storage, git hosting, and inbox and outbox delivery

feat/ui-updates
tinyrelay / internal / daemon / direct_messages.go
direct_messages.go 2.5K
1package daemon23import (4 "context"5 "encoding/json"6 "errors"7 "fmt"8 "time"910 "github.com/FelineStateMachine/tinyrelay/internal/event"11 "github.com/FelineStateMachine/tinyrelay/internal/relay"12 "github.com/FelineStateMachine/tinyrelay/internal/storage"13)1415type directMessagesRequest struct {16 Cursor string `json:"cursor"`17 Limit int `json:"limit"`18}1920func directMessagesBrowseMethod(method string) bool { return method == "browsedirectmessages" }2122func (t *Tenant) executeDirectMessages(ctx context.Context, actor string, params []json.RawMessage) (any, error) {23 if actor == "" || !hex64(actor) {24 return nil, errors.New("auth-required: signed actor")25 }26 request := directMessagesRequest{Limit: 50}27 if len(params) > 0 {28 if err := json.Unmarshal(params[0], &request); err != nil {29 return nil, fmt.Errorf("invalid: direct message parameters: %w", err)30 }31 }32 if request.Limit <= 0 || request.Limit > 100 {33 request.Limit = 5034 }35 cursor, err := parseCollaborationCursor(request.Cursor)36 if err != nil {37 return nil, errors.New("invalid: direct message cursor")38 }39 return t.browseDirectMessages(ctx, actor, cursor, request.Limit)40}4142func (t *Tenant) browseDirectMessages(ctx context.Context, actor string, cursor *storage.EventCursor, limit int) (any, error) {43 filter := event.Filter{Kinds: []int{event.KIND_WRAP}, Tags: map[string][]string{"p": {actor}}}44 session := relay.Session{PubKeys: []string{actor}, RelayURL: t.RelayURL()}45 if _, err := t.gate.Read(ctx, []event.Filter{filter}, session); err != nil {46 return nil, err47 }48 rows := make([]event.Event, 0, limit)49 next := ""50 for scans := 0; scans < 100; scans++ {51 page, err := t.store.Query(ctx, filter, storage.QueryOptions{Now: time.Now().Unix(), Access: storage.Access{PubKeys: []string{actor}}, Limit: 100, Before: cursor})52 if err != nil {53 return nil, err54 }55 for index, row := range page.Events {56 cursor = &storage.EventCursor{CreatedAt: row.CreatedAt, ID: row.ID}57 if !t.gate.CanSee(ctx, row, session, &filter) {58 continue59 }60 rows = append(rows, row)61 if len(rows) == limit {62 if page.More || index+1 < len(page.Events) {63 next = collaborationCursor(collaborationItem{CreatedAt: row.CreatedAt, ID: row.ID})64 }65 return map[string]any{"events": rows, "next_cursor": next}, nil66 }67 }68 if !page.More || len(page.Events) == 0 {69 return map[string]any{"events": rows, "next_cursor": ""}, nil70 }71 }72 if cursor != nil {73 next = collaborationCursor(collaborationItem{CreatedAt: cursor.CreatedAt, ID: cursor.ID})74 }75 return map[string]any{"events": rows, "next_cursor": next}, nil76}