mirror of
https://github.com/nbd-wtf/go-nostr.git
synced 2025-05-06 08:40:15 +02:00
222 lines
4.4 KiB
Go
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
|
|
}
|