193 lines
5.8 KiB
Go
193 lines
5.8 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"github.com/rs/zerolog/log"
|
|
"net/http"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
const mastodonIndexUrl = "https://instances.social/list.json?q%5Busers%5D=&q%5Bsearch%5D=&strict=false"
|
|
|
|
var foreverago = time.Now().Add((-1 * 86400 * 365 * 100) * time.Second)
|
|
|
|
// check with indices only hourly
|
|
var INDEX_CHECK_INTERVAL = time.Second * 60 * 60
|
|
|
|
// thank fuck for https://mholt.github.io/json-to-go/ otherwise
|
|
// this would have been a giant pain in the dick
|
|
type MastodonIndexResponse struct {
|
|
Instances []struct {
|
|
ID string `json:"_id"`
|
|
AddedAt time.Time `json:"addedAt"`
|
|
Name string `json:"name"`
|
|
Downchecks int `json:"downchecks"`
|
|
Upchecks int `json:"upchecks"`
|
|
HTTPSRank interface{} `json:"https_rank"`
|
|
HTTPSScore int `json:"https_score"`
|
|
ObsRank string `json:"obs_rank"`
|
|
ObsScore int `json:"obs_score"`
|
|
Ipv6 bool `json:"ipv6"`
|
|
Up bool `json:"up"`
|
|
Users int `json:"users"`
|
|
Statuses string `json:"statuses"`
|
|
Connections int `json:"connections"`
|
|
OpenRegistrations bool `json:"openRegistrations"`
|
|
Uptime float64 `json:"uptime"`
|
|
Version string `json:"version"`
|
|
VersionScore int `json:"version_score"`
|
|
UpdatedAt time.Time `json:"updatedAt"`
|
|
CheckedAt time.Time `json:"checkedAt"`
|
|
Dead bool `json:"dead"`
|
|
ObsDate time.Time `json:"obs_date"`
|
|
Second60 int `json:"second60"`
|
|
Second int `json:"second"`
|
|
ActiveUserCount interface{} `json:"active_user_count,omitempty"`
|
|
FirstUserCreatedAt interface{} `json:"first_user_created_at,omitempty"`
|
|
Thumbnail string `json:"thumbnail"`
|
|
ApUpdatedAt time.Time `json:"apUpdatedAt"`
|
|
Second5 int `json:"second5"`
|
|
RawVersion string `json:"raw_version"`
|
|
ActivityPrevw struct {
|
|
Statuses int `json:"statuses"`
|
|
Logins int `json:"logins"`
|
|
Registrations int `json:"registrations"`
|
|
} `json:"activity_prevw,omitempty"`
|
|
Mastodon bool `json:"mastodon"`
|
|
UptimeStr string `json:"uptime_str"`
|
|
Score int `json:"score"`
|
|
ScoreStr string `json:"score_str"`
|
|
} `json:"instances"`
|
|
}
|
|
|
|
const pleromaIndexUrl = "https://distsn.org/cgi-bin/distsn-pleroma-instances-api.cgi"
|
|
|
|
type PleromaIndexResponse []struct {
|
|
Domain string `json:"domain"`
|
|
Title string `json:"title"`
|
|
Thumbnail string `json:"thumbnail"`
|
|
Registration bool `json:"registration"`
|
|
Chat bool `json:"chat"`
|
|
Gopher bool `json:"gopher"`
|
|
WhoToFollow bool `json:"who_to_follow"`
|
|
MediaProxy bool `json:"media_proxy"`
|
|
ScopeOptions bool `json:"scope_options"`
|
|
AccountActivationRequired bool `json:"account_activation_required"`
|
|
TextLimit int `json:"text_limit"`
|
|
}
|
|
|
|
type InstanceLocator struct {
|
|
pleromaIndexLastRefresh *time.Time
|
|
mastodonIndexLastRefresh *time.Time
|
|
instances map[string]*Instance
|
|
sync.Mutex
|
|
}
|
|
|
|
func NewInstanceLocator() *InstanceLocator {
|
|
i := new(InstanceLocator)
|
|
i.instances = make(map[string]*Instance)
|
|
i.pleromaIndexLastRefresh = &foreverago
|
|
i.mastodonIndexLastRefresh = &foreverago
|
|
return i
|
|
}
|
|
|
|
func (i *InstanceLocator) addInstance(hostname string) {
|
|
// only add it if we haven't seen the hostname before
|
|
if i.instances[hostname] == nil {
|
|
log.Debug().Str("hostname", hostname).Msgf("adding discovered instance")
|
|
i.Lock()
|
|
i.instances[hostname] = NewInstance(hostname)
|
|
i.Unlock()
|
|
}
|
|
|
|
}
|
|
|
|
func (i *InstanceLocator) Locate() {
|
|
log.Debug().Str("lastmastodonupdate", i.mastodonIndexLastRefresh.Format(time.RFC3339)).Send()
|
|
log.Debug().Str("lastpleromaupdate", i.pleromaIndexLastRefresh.Format(time.RFC3339)).Send()
|
|
i.locateMastodon()
|
|
i.locatePleroma()
|
|
time.Sleep(120 * time.Second)
|
|
i.instanceReport()
|
|
}
|
|
|
|
func (i *InstanceLocator) instanceReport() {
|
|
var upInstances int = 0
|
|
var identifiedInstances int = 0
|
|
var totalInstances int = 0
|
|
|
|
totalInstances = len(i.instances)
|
|
|
|
for _, elem := range i.instances {
|
|
if elem.identified == true {
|
|
identifiedInstances = identifiedInstances + 1
|
|
}
|
|
}
|
|
|
|
for _, elem := range i.instances {
|
|
if elem.up == true {
|
|
upInstances = upInstances + 1
|
|
}
|
|
}
|
|
|
|
log.Info().
|
|
Int("up", upInstances).
|
|
Int("total", totalInstances).
|
|
Int("identified", identifiedInstances).
|
|
Msg("instance report")
|
|
}
|
|
|
|
func (i *InstanceLocator) locateMastodon() {
|
|
var netClient = &http.Client{
|
|
Timeout: time.Second * 20,
|
|
}
|
|
resp, err := netClient.Get(mastodonIndexUrl)
|
|
defer resp.Body.Close()
|
|
|
|
if err != nil {
|
|
log.Warn().Msgf("unable to fetch mastodon instance list: %s", err)
|
|
} else {
|
|
// it worked
|
|
mi := new(MastodonIndexResponse)
|
|
err = json.NewDecoder(resp.Body).Decode(&mi)
|
|
if err != nil {
|
|
log.Warn().Msgf("unable to parse mastodon instance list: %s", err)
|
|
} else {
|
|
for _, instance := range mi.Instances {
|
|
i.addInstance(instance.Name)
|
|
}
|
|
|
|
i.Lock()
|
|
t := time.Now()
|
|
i.mastodonIndexLastRefresh = &t
|
|
i.Unlock()
|
|
}
|
|
}
|
|
}
|
|
|
|
func (i *InstanceLocator) locatePleroma() {
|
|
var netClient = &http.Client{
|
|
Timeout: time.Second * 20,
|
|
}
|
|
resp, err := netClient.Get(pleromaIndexUrl)
|
|
if err != nil {
|
|
log.Warn().Msgf("unable to fetch pleroma instance list: %s", err)
|
|
} else {
|
|
// fetch worked
|
|
pi := new(PleromaIndexResponse)
|
|
err = json.NewDecoder(resp.Body).Decode(&pi)
|
|
if err != nil {
|
|
log.Warn().Msgf("unable to parse pleroma instance list: %s", err)
|
|
} else {
|
|
for _, instance := range *pi {
|
|
i.addInstance(instance.Domain)
|
|
}
|
|
i.Lock()
|
|
t := time.Now()
|
|
i.pleromaIndexLastRefresh = &t
|
|
i.Unlock()
|
|
}
|
|
}
|
|
}
|