2024-09-06 11:16:40 -04:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
"log"
|
|
|
|
"net/http"
|
|
|
|
"os"
|
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/fiatjaf/eventstore/lmdb"
|
|
|
|
"github.com/fiatjaf/khatru"
|
|
|
|
"github.com/joho/godotenv"
|
|
|
|
"github.com/nbd-wtf/go-nostr"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Config struct {
|
|
|
|
RelayName string
|
|
|
|
RelayPubkey string
|
|
|
|
RelayDescription string
|
|
|
|
DBPath string
|
|
|
|
}
|
|
|
|
|
|
|
|
var pool *nostr.SimplePool
|
|
|
|
var relays []string
|
|
|
|
var config Config
|
|
|
|
var trustNetwork []string
|
|
|
|
var mu sync.Mutex
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
fmt.Println("starting")
|
|
|
|
relay := khatru.NewRelay()
|
|
|
|
ctx := context.Background()
|
|
|
|
|
|
|
|
config = LoadConfig()
|
|
|
|
|
|
|
|
relay.Info.Name = config.RelayName
|
|
|
|
relay.Info.PubKey = config.RelayPubkey
|
|
|
|
relay.Info.Description = config.RelayDescription
|
|
|
|
trustNetwork = append(trustNetwork, config.RelayPubkey)
|
|
|
|
|
|
|
|
db := lmdb.LMDBBackend{
|
|
|
|
Path: getEnv("DB_PATH"),
|
|
|
|
}
|
|
|
|
if err := db.Init(); err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
2024-09-06 13:24:33 -04:00
|
|
|
go refreshTrustNetwork(relay)
|
2024-09-06 11:16:40 -04:00
|
|
|
go archiveTrustedNotes(relay)
|
|
|
|
|
|
|
|
relay.StoreEvent = append(relay.StoreEvent, db.SaveEvent)
|
|
|
|
relay.QueryEvents = append(relay.QueryEvents, db.QueryEvents)
|
|
|
|
relay.RejectEvent = append(relay.RejectEvent, func(ctx context.Context, event *nostr.Event) (bool, string) {
|
|
|
|
for _, pk := range trustNetwork {
|
|
|
|
if pk == event.PubKey {
|
|
|
|
return false, ""
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true, "you are not in the web of trust"
|
|
|
|
})
|
|
|
|
|
|
|
|
relays = []string{
|
|
|
|
"wss://nos.lol",
|
|
|
|
"wss://nostr.mom",
|
|
|
|
"wss://purplepag.es",
|
|
|
|
"wss://purplerelay.com",
|
|
|
|
"wss://relay.damus.io",
|
|
|
|
"wss://relay.mostr.pub",
|
|
|
|
"wss://relay.nos.social",
|
|
|
|
"wss://relay.nostr.band",
|
|
|
|
"wss://relay.snort.social",
|
|
|
|
"wss://relayable.org",
|
|
|
|
"wss://pyramid.fiatjaf.com",
|
|
|
|
"wss://relay.primal.net",
|
|
|
|
"wss://relay.nostr.bg",
|
|
|
|
"wss://no.str.cr",
|
|
|
|
"wss://nostr21.com",
|
|
|
|
"wss://nostrue.com",
|
|
|
|
"wss://relay.siamstr.com",
|
|
|
|
"wss://nostrarchives.com",
|
|
|
|
}
|
|
|
|
|
|
|
|
pool = nostr.NewSimplePool(ctx)
|
|
|
|
|
|
|
|
fmt.Println("running on :3334")
|
|
|
|
http.ListenAndServe(":3334", relay)
|
|
|
|
}
|
|
|
|
|
|
|
|
func LoadConfig() Config {
|
|
|
|
err := godotenv.Load(".env")
|
|
|
|
if err != nil {
|
|
|
|
log.Fatalf("Error loading .env file")
|
|
|
|
}
|
|
|
|
|
|
|
|
config := Config{
|
|
|
|
RelayName: getEnv("RELAY_NAME"),
|
|
|
|
RelayPubkey: getEnv("RELAY_PUBKEY"),
|
|
|
|
RelayDescription: getEnv("RELAY_DESCRIPTION"),
|
|
|
|
DBPath: getEnv("DB_PATH"),
|
|
|
|
}
|
|
|
|
|
|
|
|
return config
|
|
|
|
}
|
|
|
|
|
|
|
|
func getEnv(key string) string {
|
|
|
|
value, exists := os.LookupEnv(key)
|
|
|
|
if !exists {
|
|
|
|
log.Fatalf("Environment variable %s not set", key)
|
|
|
|
}
|
|
|
|
return value
|
|
|
|
}
|
|
|
|
|
2024-09-06 13:24:33 -04:00
|
|
|
func refreshTrustNetwork(relay *khatru.Relay) []string {
|
2024-09-06 11:16:40 -04:00
|
|
|
ctx := context.Background()
|
2024-09-06 13:30:29 -04:00
|
|
|
ticker := time.NewTicker(10 * time.Minute)
|
2024-09-06 11:16:40 -04:00
|
|
|
for range ticker.C {
|
|
|
|
|
2024-09-06 14:06:32 -04:00
|
|
|
timeoutCtx, cancel := context.WithTimeout(ctx, 3*time.Second)
|
2024-09-06 11:16:40 -04:00
|
|
|
defer cancel()
|
|
|
|
|
|
|
|
filters := []nostr.Filter{{
|
|
|
|
Authors: []string{config.RelayPubkey},
|
|
|
|
Kinds: []int{nostr.KindContactList},
|
|
|
|
}}
|
|
|
|
|
|
|
|
for ev := range pool.SubManyEose(timeoutCtx, relays, filters) {
|
|
|
|
for _, contact := range ev.Event.Tags.GetAll([]string{"p"}) {
|
|
|
|
appendPubkey(contact[1])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
chunks := make([][]string, 0)
|
|
|
|
for i := 0; i < len(trustNetwork); i += 100 {
|
|
|
|
end := i + 100
|
|
|
|
if end > len(trustNetwork) {
|
|
|
|
end = len(trustNetwork)
|
|
|
|
}
|
|
|
|
chunks = append(chunks, trustNetwork[i:end])
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, chunk := range chunks {
|
|
|
|
threeTimeoutCtx, tenCancel := context.WithTimeout(ctx, 3*time.Second)
|
|
|
|
defer tenCancel()
|
|
|
|
|
|
|
|
filters = []nostr.Filter{{
|
|
|
|
Authors: chunk,
|
|
|
|
Kinds: []int{nostr.KindContactList},
|
|
|
|
}}
|
|
|
|
|
|
|
|
for ev := range pool.SubManyEose(threeTimeoutCtx, relays, filters) {
|
|
|
|
for _, contact := range ev.Event.Tags.GetAll([]string{"p"}) {
|
|
|
|
if len(contact) > 1 {
|
|
|
|
appendPubkey(contact[1])
|
|
|
|
} else {
|
|
|
|
fmt.Println("Skipping malformed tag: ", contact)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fmt.Println("trust network size:", len(trustNetwork))
|
2024-09-06 13:24:33 -04:00
|
|
|
getTrustNetworkProfileMetadata(relay)
|
2024-09-06 11:16:40 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return trustNetwork
|
|
|
|
}
|
|
|
|
|
2024-09-06 13:24:33 -04:00
|
|
|
func getTrustNetworkProfileMetadata(relay *khatru.Relay) {
|
|
|
|
ctx := context.Background()
|
|
|
|
|
|
|
|
chunks := make([][]string, 0)
|
|
|
|
for i := 0; i < len(trustNetwork); i += 100 {
|
|
|
|
end := i + 100
|
|
|
|
if end > len(trustNetwork) {
|
|
|
|
end = len(trustNetwork)
|
|
|
|
}
|
|
|
|
chunks = append(chunks, trustNetwork[i:end])
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, chunk := range chunks {
|
|
|
|
timeoutCtx, cancel := context.WithTimeout(ctx, 3*time.Second)
|
|
|
|
defer cancel()
|
|
|
|
filters := []nostr.Filter{{
|
|
|
|
Authors: chunk,
|
|
|
|
Kinds: []int{nostr.KindProfileMetadata},
|
|
|
|
}}
|
|
|
|
|
|
|
|
for ev := range pool.SubManyEose(timeoutCtx, relays, filters) {
|
|
|
|
relay.AddEvent(timeoutCtx, ev.Event)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-09-06 11:16:40 -04:00
|
|
|
func appendPubkey(pubkey string) {
|
|
|
|
mu.Lock()
|
|
|
|
defer mu.Unlock()
|
|
|
|
|
|
|
|
for _, pk := range trustNetwork {
|
|
|
|
if pk == pubkey {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
trustNetwork = append(trustNetwork, pubkey)
|
|
|
|
}
|
|
|
|
|
|
|
|
func archiveTrustedNotes(relay *khatru.Relay) {
|
2024-09-06 14:06:32 -04:00
|
|
|
// Create a ticker to restart the function every minute
|
|
|
|
ticker := time.NewTicker(1 * time.Minute)
|
|
|
|
defer ticker.Stop()
|
|
|
|
|
|
|
|
for range ticker.C {
|
|
|
|
// Copy the current state of trustNetwork at the start of the function
|
|
|
|
mu.Lock() // Lock while copying the trustNetwork to avoid partial reads
|
|
|
|
localTrustNetwork := make([]string, len(trustNetwork))
|
|
|
|
copy(localTrustNetwork, trustNetwork)
|
|
|
|
mu.Unlock() // Unlock immediately after copying
|
|
|
|
|
|
|
|
ctx := context.Background()
|
|
|
|
filters := []nostr.Filter{{
|
|
|
|
Kinds: []int{
|
|
|
|
nostr.KindArticle,
|
|
|
|
nostr.KindDeletion,
|
|
|
|
nostr.KindContactList,
|
|
|
|
nostr.KindEncryptedDirectMessage,
|
|
|
|
nostr.KindMuteList,
|
|
|
|
nostr.KindReaction,
|
|
|
|
nostr.KindRelayListMetadata,
|
|
|
|
nostr.KindRepost,
|
|
|
|
nostr.KindZapRequest,
|
|
|
|
nostr.KindZap,
|
|
|
|
nostr.KindTextNote,
|
|
|
|
},
|
|
|
|
}}
|
|
|
|
|
|
|
|
// Use the local copy of trustNetwork in the event loop
|
|
|
|
for ev := range pool.SubMany(ctx, relays, filters) {
|
|
|
|
for _, trustedPubkey := range localTrustNetwork {
|
|
|
|
if ev.Event.PubKey == trustedPubkey {
|
|
|
|
relay.AddEvent(ctx, ev.Event)
|
|
|
|
}
|
2024-09-06 11:16:40 -04:00
|
|
|
}
|
|
|
|
}
|
2024-09-06 14:06:32 -04:00
|
|
|
|
|
|
|
fmt.Println("archiveTrustedNotes: finished one cycle, will restart in 1 minute")
|
2024-09-06 11:16:40 -04:00
|
|
|
}
|
|
|
|
}
|