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.
 
 
 
 
 

144 lines
4.2 KiB

package nostr
import (
"context"
"fmt"
"gitcitadel-online/internal/logger"
"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
Image 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
// Uses the standard ProcessEventsWithCache process, then filters for top-level events
func (es *EBooksService) FetchTopLevelIndexEvents(ctx context.Context) ([]EBookInfo, error) {
// For e-books, we want to fetch a large number to ensure we get all top-level events
// Use a high display limit (1000) to get all events, then filter for top-level
displayLimit := 1000
// Use standard process: fetch 2x limit, merge cache, deduplicate, filter deletions, sort, limit, fetch profiles
// For e-books, use a high display limit (1000) to get all events, then filter for top-level
// This means we'll fetch 2000 events, which should be enough for most cases
result, err := es.client.ProcessEventsWithCache(ctx, es.indexKind, displayLimit, make(map[string]*nostr.Event), es.relayURL, "", 0)
if err != nil {
return nil, fmt.Errorf("failed to process index events: %w", err)
}
events := result.Events
logger.WithFields(map[string]interface{}{
"events": len(events),
"relay": es.relayURL,
}).Debug("Processed index events using standard process")
// 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
}
}
}
logger.WithField("count", len(referencedSet)).Debug("Found referenced index events")
// 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)
}
}
logger.WithField("count", len(topLevelEvents)).Info("Found top-level index events")
// 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 common tags using utilities
ebook.DTag = getDTag(event.Tags)
ebook.Title = getTitle(event.Tags)
if ebook.Title == "" {
ebook.Title = ebook.DTag // Fallback to d tag
}
ebook.Summary = getSummary(event.Tags)
ebook.Type = getTagValue(event.Tags, "type")
ebook.Image = getImage(event.Tags)
// Extract relay hints from event tags if available
relays := getAllTagValues(event.Tags, "relays")
// 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 {
logger.WithFields(map[string]interface{}{
"ebook": ebook.DTag,
"error": err,
}).Warn("Failed to create naddr for ebook, using fallback format")
// 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
}