You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
171 lines
4.3 KiB
171 lines
4.3 KiB
package nostr |
|
|
|
import ( |
|
"context" |
|
"fmt" |
|
"log" |
|
|
|
"github.com/nbd-wtf/go-nostr" |
|
) |
|
|
|
// EBooksService handles fetching top-level 30040 events |
|
type EBooksService struct { |
|
client *Client |
|
indexKind int |
|
relayURL string |
|
} |
|
|
|
// NewEBooksService creates a new e-books service |
|
func NewEBooksService(client *Client, indexKind int, relayURL string) *EBooksService { |
|
return &EBooksService{ |
|
client: client, |
|
indexKind: indexKind, |
|
relayURL: relayURL, |
|
} |
|
} |
|
|
|
// EBookInfo represents information about a top-level index event |
|
type EBookInfo struct { |
|
EventID string |
|
Title string |
|
DTag string |
|
Author string |
|
Summary string |
|
Type string |
|
CreatedAt int64 |
|
Naddr string |
|
} |
|
|
|
// FetchTopLevelIndexEvents fetches all top-level 30040 events from the specified relay |
|
// Top-level means the event is not referenced in any other 30040 event's 'a' tags |
|
func (es *EBooksService) FetchTopLevelIndexEvents(ctx context.Context) ([]EBookInfo, error) { |
|
// Connect to the specific relay |
|
relay, err := es.client.ConnectToRelay(ctx, es.relayURL) |
|
if err != nil { |
|
return nil, fmt.Errorf("failed to connect to relay %s: %w", es.relayURL, err) |
|
} |
|
defer relay.Close() |
|
|
|
// Fetch all 30040 events (limit 10k) |
|
filter := nostr.Filter{ |
|
Kinds: []int{es.indexKind}, |
|
Limit: 10000, |
|
} |
|
logFilter(filter, fmt.Sprintf("all index events (kind %d) from %s", es.indexKind, es.relayURL)) |
|
|
|
events, err := relay.QuerySync(ctx, filter) |
|
if err != nil { |
|
return nil, fmt.Errorf("failed to query events: %w", err) |
|
} |
|
|
|
log.Printf("Fetched %d index events from %s", len(events), es.relayURL) |
|
|
|
// Build a set of all referenced kind:pubkey:dtag from 'a' tags |
|
referencedSet := make(map[string]bool) |
|
for _, event := range events { |
|
for _, tag := range event.Tags { |
|
if len(tag) > 0 && tag[0] == "a" && len(tag) > 1 { |
|
// tag[1] is the kind:pubkey:dtag reference |
|
referencedSet[tag[1]] = true |
|
} |
|
} |
|
} |
|
|
|
log.Printf("Found %d referenced index events", len(referencedSet)) |
|
|
|
// Filter to only top-level events (not referenced in any other event) |
|
var topLevelEvents []*nostr.Event |
|
for _, event := range events { |
|
// Build the kind:pubkey:dtag identifier for this event |
|
var dTag string |
|
for _, tag := range event.Tags { |
|
if len(tag) > 0 && tag[0] == "d" && len(tag) > 1 { |
|
dTag = tag[1] |
|
break |
|
} |
|
} |
|
|
|
if dTag == "" { |
|
continue // Skip events without d tag |
|
} |
|
|
|
// Check if this event is referenced by any other event |
|
eventRef := fmt.Sprintf("%d:%s:%s", es.indexKind, event.PubKey, dTag) |
|
if !referencedSet[eventRef] { |
|
topLevelEvents = append(topLevelEvents, event) |
|
} |
|
} |
|
|
|
log.Printf("Found %d top-level index events", len(topLevelEvents)) |
|
|
|
// Convert to EBookInfo |
|
ebooks := make([]EBookInfo, 0, len(topLevelEvents)) |
|
for _, event := range topLevelEvents { |
|
ebook := EBookInfo{ |
|
EventID: event.ID, |
|
Author: event.PubKey, |
|
CreatedAt: int64(event.CreatedAt), |
|
} |
|
|
|
// Extract d tag |
|
for _, tag := range event.Tags { |
|
if len(tag) > 0 && tag[0] == "d" && len(tag) > 1 { |
|
ebook.DTag = tag[1] |
|
break |
|
} |
|
} |
|
|
|
// Extract title |
|
for _, tag := range event.Tags { |
|
if len(tag) > 0 && tag[0] == "title" && len(tag) > 1 { |
|
ebook.Title = tag[1] |
|
break |
|
} |
|
} |
|
if ebook.Title == "" { |
|
ebook.Title = ebook.DTag // Fallback to d tag |
|
} |
|
|
|
// Extract summary |
|
for _, tag := range event.Tags { |
|
if len(tag) > 0 && tag[0] == "summary" && len(tag) > 1 { |
|
ebook.Summary = tag[1] |
|
break |
|
} |
|
} |
|
|
|
// Extract type |
|
for _, tag := range event.Tags { |
|
if len(tag) > 0 && tag[0] == "type" && len(tag) > 1 { |
|
ebook.Type = tag[1] |
|
break |
|
} |
|
} |
|
|
|
// Build naddr - create proper bech32-encoded naddr |
|
// Extract relay hints from event tags if available |
|
var relays []string |
|
for _, tag := range event.Tags { |
|
if len(tag) > 0 && tag[0] == "relays" { |
|
relays = append(relays, tag[1:]...) |
|
} |
|
} |
|
// If no relays in tags, use the relay we fetched from |
|
if len(relays) == 0 { |
|
relays = []string{es.relayURL} |
|
} |
|
|
|
naddr, err := CreateNaddr(es.indexKind, ebook.Author, ebook.DTag, relays) |
|
if err != nil { |
|
log.Printf("Failed to create naddr for ebook %s: %v", ebook.DTag, err) |
|
// Fallback to kind:pubkey:dtag format |
|
ebook.Naddr = fmt.Sprintf("%d:%s:%s", es.indexKind, ebook.Author, ebook.DTag) |
|
} else { |
|
ebook.Naddr = naddr |
|
} |
|
|
|
ebooks = append(ebooks, ebook) |
|
} |
|
|
|
return ebooks, nil |
|
}
|
|
|