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
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 |
|
}
|
|
|