Skip to content

Instantly share code, notes, and snippets.

@jiftechnify
Last active February 16, 2023 08:03
Show Gist options
  • Save jiftechnify/da37e1058e994c21c38e03a715aa45ae to your computer and use it in GitHub Desktop.
Save jiftechnify/da37e1058e994c21c38e03a715aa45ae to your computer and use it in GitHub Desktop.
most used nostr relay impls
161 "https://git.sr.ht/~gheartsfield/nostr-rs-relay"
124 "git+https://github.com/Cameri/nostream.git"
17 "git+https://github.com/hoytech/strfry.git"
6 "https://github.com/fiatjaf/relayer"
3 "https://code.pobblelabs.org/fossil/nostr_relay"
2 "https://relay.nostr.band"
1 "https://github.com/permadao/ArNostr-relayer"
1 "https://github.com/lpicanco/knostr"
1 "https://github.com/libitx/nex"
1 "https://github.com/hoytech/strfry"
1 "https://github.com/atdixon/me.untethr.nostr-relay"
1 "https://code.pobblelabs.org/fossil/nostr_relay.fossil"
1 "git+https://github.com/UTXOnly/nostream_Boston.git"
1 "git+https://github.com/Cameri/nostr-ts-relay.git"
1 "custom"
package main
import (
"context"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"net/url"
"sync"
"time"
)
func main() {
ctxGetRelays, cancelGetRelays := context.WithTimeout(context.Background(), 10*time.Second)
defer cancelGetRelays()
relayURLs, err := getRelayURLs(ctxGetRelays, "https://api.nostr.watch/v1/online")
if err != nil {
log.Fatal(err)
}
resChan := make(chan NostrRelayInfo)
errChan := make(chan error)
ctx, stopAggr := context.WithCancel(context.Background())
var wgAggr sync.WaitGroup
wgAggr.Add(1)
go func() {
cntPerSoftware := make(map[string]int)
for {
select {
case <-ctx.Done():
for software, cnt := range cntPerSoftware {
fmt.Printf("%d %q\n", cnt, software)
}
wgAggr.Done()
return
case res := <-resChan:
cntPerSoftware[res.Software] += 1
case err := <-errChan:
log.Printf("error: %v", err)
}
}
}()
var wgGetInfo sync.WaitGroup
sema := make(chan struct{}, 50)
for _, relayURL := range relayURLs {
sema <- struct{}{}
wgGetInfo.Add(1)
go func(url string) {
defer func() {
<-sema
wgGetInfo.Done()
}()
ctxTimeout, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
getRelayInfo(ctxTimeout, url, resChan, errChan)
}(relayURL)
}
wgGetInfo.Wait()
stopAggr()
wgAggr.Wait()
}
func getRelayURLs(ctx context.Context, relayListEndpoint string) ([]string, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, relayListEndpoint, nil)
if err != nil {
return nil, err
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer func() {
io.Copy(io.Discard, resp.Body)
resp.Body.Close()
}()
relayURLs := make([]string, 0)
if err := json.NewDecoder(resp.Body).Decode(&relayURLs); err != nil {
return nil, err
}
return relayURLs, nil
}
type NostrRelayInfo struct {
Name string `json:"name"`
Description string `json:"description"`
Pubkey string `json:"pubkey"`
Contact string `json:"contact"`
SupportedNIPs []int `json:"supported_nips"`
Software string `json:"software"`
Version string `json:"version"`
}
func getRelayInfo(ctx context.Context, relayURL string, resChan chan NostrRelayInfo, errChan chan error) {
log.Printf("querying %s...", relayURL)
// wss:// -> https://
u, err := url.Parse(relayURL)
if err != nil {
errChan <- err
return
}
u.Scheme = "https"
req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil)
req.Header.Set("Accept", "application/nostr+json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
errChan <- fmt.Errorf("failed to request relay info: %v", err)
return
}
defer func() {
io.Copy(io.Discard, resp.Body)
resp.Body.Close()
}()
if resp.StatusCode != 200 {
errChan <- fmt.Errorf("relay %s responsed with non-OK status: %d", relayURL, resp.StatusCode)
return
}
var relayInfo NostrRelayInfo
if err := json.NewDecoder(resp.Body).Decode(&relayInfo); err != nil {
errChan <- fmt.Errorf("relay %s returned malformed response: %v", relayURL, err)
return
}
resChan <- relayInfo
}
go run main.go | sort -nr > most_used_nostr_relay_impls.txt
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment