2025-04-06 11:47:08 -03:00

222 lines
4.4 KiB
Go

package badgerh
import (
"encoding/hex"
"fmt"
"math"
"slices"
"github.com/dgraph-io/badger/v4"
"github.com/nbd-wtf/go-nostr"
"github.com/nbd-wtf/go-nostr/sdk/hints"
)
var _ hints.HintsDB = (*BadgerHints)(nil)
type BadgerHints struct {
db *badger.DB
}
func NewBadgerHints(path string) (*BadgerHints, error) {
opts := badger.DefaultOptions(path)
db, err := badger.Open(opts)
if err != nil {
return nil, err
}
return &BadgerHints{db: db}, nil
}
func (bh *BadgerHints) Close() {
bh.db.Close()
}
func (bh *BadgerHints) Save(pubkey string, relay string, hintkey hints.HintKey, ts nostr.Timestamp) {
if now := nostr.Now(); ts > now {
ts = now
}
err := bh.db.Update(func(txn *badger.Txn) error {
k := encodeKey(pubkey, relay)
var tss timestamps
item, err := txn.Get(k)
if err == nil {
err = item.Value(func(val []byte) error {
// there is a value, so we may update it or not
tss = parseValue(val)
return nil
})
if err != nil {
return err
}
} else if err != badger.ErrKeyNotFound {
return err
}
if tss[hintkey] < ts {
tss[hintkey] = ts
return txn.Set(k, encodeValue(tss))
}
return nil
})
if err != nil {
nostr.InfoLogger.Printf("[sdk/hints/badger] unexpected error on save: %s\n", err)
}
}
func (bh *BadgerHints) TopN(pubkey string, n int) []string {
type relayScore struct {
relay string
score int64
}
scores := make([]relayScore, 0, n)
err := bh.db.View(func(txn *badger.Txn) error {
opts := badger.DefaultIteratorOptions
opts.Prefix, _ = hex.DecodeString(pubkey)
it := txn.NewIterator(opts)
defer it.Close()
for it.Seek(opts.Prefix); it.Valid(); it.Next() {
item := it.Item()
k := item.Key()
relay := string(k[32:])
err := item.Value(func(val []byte) error {
tss := parseValue(val)
scores = append(scores, relayScore{relay, tss.sum()})
return nil
})
if err != nil {
continue
}
}
return nil
})
if err != nil {
nostr.InfoLogger.Printf("[sdk/hints/badger] unexpected error on topn: %s\n", err)
return nil
}
slices.SortFunc(scores, func(a, b relayScore) int {
return int(b.score - a.score)
})
result := make([]string, 0, n)
for i, rs := range scores {
if i >= n {
break
}
result = append(result, rs.relay)
}
return result
}
func (bh *BadgerHints) GetDetailedScores(pubkey string, n int) []hints.RelayScores {
type relayScore struct {
relay string
tss timestamps
score int64
}
scores := make([]relayScore, 0, n)
err := bh.db.View(func(txn *badger.Txn) error {
prefix, _ := hex.DecodeString(pubkey)
it := txn.NewIterator(badger.DefaultIteratorOptions)
defer it.Close()
for it.Seek(prefix); it.ValidForPrefix(prefix); it.Next() {
item := it.Item()
k := item.Key()
relay := string(k[32:])
var tss timestamps
err := item.Value(func(v []byte) error {
tss = parseValue(v)
return nil
})
if err != nil {
return err
}
scores = append(scores, relayScore{relay, tss, tss.sum()})
}
return nil
})
if err != nil {
return nil
}
slices.SortFunc(scores, func(a, b relayScore) int {
return int(b.score - a.score)
})
result := make([]hints.RelayScores, 0, n)
for i, rs := range scores {
if i >= n {
break
}
result = append(result, hints.RelayScores{
Relay: rs.relay,
Scores: rs.tss,
Sum: rs.score,
})
}
return result
}
func (bh *BadgerHints) PrintScores() {
fmt.Println("= print scores")
err := bh.db.View(func(txn *badger.Txn) error {
it := txn.NewIterator(badger.DefaultIteratorOptions)
defer it.Close()
var lastPubkey string
i := 0
for it.Seek(nil); it.Valid(); it.Next() {
item := it.Item()
k := item.Key()
pubkey, relay := parseKey(k)
if pubkey != lastPubkey {
fmt.Println("== relay scores for", pubkey)
lastPubkey = pubkey
i = 0
} else {
i++
}
err := item.Value(func(val []byte) error {
tss := parseValue(val)
fmt.Printf(" %3d :: %30s ::> %12d\n", i, relay, tss.sum())
return nil
})
if err != nil {
continue
}
}
return nil
})
if err != nil {
nostr.InfoLogger.Printf("[sdk/hints/badger] unexpected error on print: %s\n", err)
}
}
type timestamps [4]nostr.Timestamp
func (tss timestamps) sum() int64 {
now := nostr.Now() + 24*60*60
var sum int64
for i, ts := range tss {
if ts == 0 {
continue
}
value := float64(hints.HintKey(i).BasePoints()) * 10000000000 / math.Pow(float64(max(now-ts, 1)), 1.3)
// fmt.Println(" ", i, "value:", value)
sum += int64(value)
}
return sum
}