commit 0db4cd33a4ac99f2255e4bc3bd4822259447e24d
parent 4ceac52b5bd117c006c1dc34a285d70f6d019875
Author: MTRNord <mtrnord1@gmail.com>
Date: Thu, 15 Feb 2024 17:57:25 +0100
Implement basic ping logic and pong logic
Diffstat:
| M | config.go | | | 36 | ++++++++++++++++++++++++++++++++---- |
| M | go.mod | | | 21 | +++++++++++++++------ |
| M | go.sum | | | 61 | +++++++++++++++++++++++++++++++++++++++++++++++++++++-------- |
| M | main.go | | | 106 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- |
| M | ping_json.go | | | 22 | +++++++++++----------- |
| M | ping_manager.go | | | 107 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
6 files changed, 323 insertions(+), 30 deletions(-)
diff --git a/config.go b/config.go
@@ -5,12 +5,15 @@ import (
"github.com/goccy/go-yaml"
log "github.com/sirupsen/logrus"
+ "maunium.net/go/mautrix"
+ "maunium.net/go/mautrix/id"
)
type MatrixConfig struct {
Homeserver string `yaml:"homeserver"`
Username string `yaml:"username"`
Password string `yaml:"password"`
+ Client *mautrix.Client
}
type Config struct {
@@ -18,10 +21,11 @@ type Config struct {
OwnHomeserver MatrixConfig `yaml:"own_homeserver"`
RemoteHomeservers []MatrixConfig `yaml:"remote_homeservers"`
PingRoom string `yaml:"ping_room"`
- PingRateSeconds int `yaml:"ping_rate_seconds"`
- PingThresholdSeconds int `yaml:"ping_threshold_seconds"`
- PingJsonURL string `yaml:"ping_json_url"`
- BlacklistedHomeservers []string `yaml:"blacklisted_homeservers"`
+ PingRoomID id.RoomID
+ PingRateSeconds int `yaml:"ping_rate_seconds"`
+ PingThresholdSeconds int `yaml:"ping_threshold_seconds"`
+ PingJsonURL string `yaml:"ping_json_url"`
+ BlacklistedHomeservers []string `yaml:"blacklisted_homeservers"`
}
func ReadConfig() Config {
@@ -97,5 +101,29 @@ func ReadConfig() Config {
}
}
+ // Ensure that the Remote Homeservers are not blacklisted
+ for _, blacklistedHomeserver := range config.BlacklistedHomeservers {
+ for _, remoteHomeserver := range config.RemoteHomeservers {
+ if remoteHomeserver.Homeserver == blacklistedHomeserver {
+ log.Errorf("Remote homeserver %s is blacklisted", remoteHomeserver.Homeserver)
+ os.Exit(1)
+ }
+ }
+ }
+
+ // Ensure that the Remote Homeservers are different from the Own Homeserver or each other
+ for _, remoteHomeserver := range config.RemoteHomeservers {
+ if remoteHomeserver.Homeserver == config.OwnHomeserver.Homeserver {
+ log.Errorf("Remote homeserver %s is the same as the own homeserver", remoteHomeserver.Homeserver)
+ os.Exit(1)
+ }
+ for _, remoteHomeserver2 := range config.RemoteHomeservers {
+ if remoteHomeserver.Homeserver == remoteHomeserver2.Homeserver {
+ log.Errorf("Remote homeserver %s is the same as remote homeserver %s", remoteHomeserver.Homeserver, remoteHomeserver2.Homeserver)
+ os.Exit(1)
+ }
+ }
+ }
+
return config
}
diff --git a/go.mod b/go.mod
@@ -6,22 +6,31 @@ require github.com/prometheus/client_golang v1.18.0
require (
github.com/fatih/color v1.10.0 // indirect
- github.com/mattn/go-colorable v0.1.8 // indirect
- github.com/mattn/go-isatty v0.0.12 // indirect
+ github.com/mattn/go-colorable v0.1.13 // indirect
+ github.com/mattn/go-isatty v0.0.19 // indirect
+ github.com/rs/zerolog v1.31.0 // indirect
+ github.com/tidwall/gjson v1.17.0 // indirect
+ github.com/tidwall/match v1.1.1 // indirect
+ github.com/tidwall/pretty v1.2.0 // indirect
+ github.com/tidwall/sjson v1.2.5 // indirect
+ go.mau.fi/util v0.3.0 // indirect
+ golang.org/x/crypto v0.18.0 // indirect
+ golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 // indirect
+ golang.org/x/net v0.20.0 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
- gopkg.in/yaml.v2 v2.4.0 // indirect
+ maunium.net/go/maulogger/v2 v2.4.1 // indirect
)
require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
- github.com/ghodss/yaml v1.0.0
github.com/goccy/go-yaml v1.11.3
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
github.com/prometheus/client_model v0.5.0 // indirect
- github.com/prometheus/common v0.45.0 // indirect
+ github.com/prometheus/common v0.45.0
github.com/prometheus/procfs v0.12.0 // indirect
github.com/sirupsen/logrus v1.9.3
- golang.org/x/sys v0.15.0 // indirect
+ golang.org/x/sys v0.16.0 // indirect
google.golang.org/protobuf v1.31.0 // indirect
+ maunium.net/go/mautrix v0.17.0
)
diff --git a/go.sum b/go.sum
@@ -2,22 +2,38 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg=
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
-github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
-github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
+github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
+github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
+github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
+github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
+github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
github.com/goccy/go-yaml v1.11.3 h1:B3W9IdWbvrUu2OYQGwvU1nZtvMQJPBKgBUuweJjLj6I=
github.com/goccy/go-yaml v1.11.3/go.mod h1:wKnAMd44+9JAAnGQpWVEgBzGt3YuTaQ4uXoHvE4m7WU=
+github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
+github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
+github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
+github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
-github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
+github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
+github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
+github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
+github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
+github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg=
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk=
github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA=
@@ -27,15 +43,40 @@ github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lne
github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY=
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
+github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
+github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A=
+github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
+github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
+github.com/tidwall/gjson v1.17.0 h1:/Jocvlh98kcTfpN2+JzGQWQcqrPQwDrVEMApx/M5ZwM=
+github.com/tidwall/gjson v1.17.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
+github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
+github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
+github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
+github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
+github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
+github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
+go.mau.fi/util v0.3.0 h1:Lt3lbRXP6ZBqTINK0EieRWor3zEwwwrDT14Z5N8RUCs=
+go.mau.fi/util v0.3.0/go.mod h1:9dGsBCCbZJstx16YgnVMVi3O2bOizELoKpugLD4FoGs=
+golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
+golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
+golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 h1:hNQpMuAJe5CtcUqCXaWga3FHu+kQvCqcsoVaQgSV60o=
+golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08=
+golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
+golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
-golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
+golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -43,6 +84,10 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
-gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+maunium.net/go/maulogger/v2 v2.4.1 h1:N7zSdd0mZkB2m2JtFUsiGTQQAdP0YeFWT7YMc80yAL8=
+maunium.net/go/maulogger/v2 v2.4.1/go.mod h1:omPuYwYBILeVQobz8uO3XC8DIRuEb5rXYlQSuqrbCho=
+maunium.net/go/mautrix v0.17.0 h1:scc1qlUbzPn+wc+3eAPquyD+3gZwwy/hBANBm+iGKK8=
+maunium.net/go/mautrix v0.17.0/go.mod h1:j+puTEQCEydlVxhJ/dQP5chfa26TdvBO7X6F3Ataav8=
diff --git a/main.go b/main.go
@@ -1,13 +1,24 @@
package main
import (
+ "context"
+ "errors"
"flag"
"net/http"
+ "os"
+ "time"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/prometheus/common/version"
log "github.com/sirupsen/logrus"
+ "maunium.net/go/mautrix"
+ "maunium.net/go/mautrix/event"
+ "maunium.net/go/mautrix/id"
+)
+
+const (
+ collector = "matrix_ping_exporter"
)
func main() {
@@ -16,7 +27,16 @@ func main() {
flag.StringVar(&address, "address", "0.0.0.0:8080", "Address to listen on")
flag.Parse()
- prometheus.Register(version.NewCollector("matrix_ping_exporter"))
+ // Create a matrix client for each homeserver
+ config.OwnHomeserver.Client = createMatrixClient(config, config.OwnHomeserver)
+ for i := range config.RemoteHomeservers {
+ config.RemoteHomeservers[i].Client = createMatrixClient(config, config.RemoteHomeservers[i])
+ }
+
+ prometheus.Register(version.NewCollector(collector))
+ prometheus.Register(&PingCollector{
+ Config: config,
+ })
http.HandleFunc("/metrics", func(w http.ResponseWriter, r *http.Request) {
h := promhttp.HandlerFor(prometheus.Gatherers{
@@ -30,3 +50,87 @@ func main() {
log.Errorf("Failed to start http server: %s", err)
}
}
+
+type PongEventRelation struct {
+ RelType string `json:"rel_type"`
+ EventID string `json:"event_id"`
+}
+
+type PongEventContent struct {
+ MsgType string `json:"msgtype"`
+ Body string `json:"body"`
+ RelatesTo PongEventRelation `json:"m.relates_to"`
+}
+
+func createMatrixClient(Config Config, Homeserver MatrixConfig) *mautrix.Client {
+ var startTime = time.Now()
+
+ client, err := mautrix.NewClient(Homeserver.Homeserver, id.UserID(Homeserver.Username), "")
+ if err != nil {
+ log.Errorf("Failed to create client for %s: %s", Homeserver.Homeserver, err)
+ os.Exit(1)
+ }
+
+ ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+ defer cancel()
+ _, err = client.Login(ctx, &mautrix.ReqLogin{
+ Type: mautrix.AuthTypePassword,
+ Identifier: mautrix.UserIdentifier{Type: "m.id.user", User: Homeserver.Username},
+ Password: Homeserver.Password,
+ })
+ if err != nil {
+ log.Errorf("Failed to login to %s: %s", Homeserver.Homeserver, err)
+ os.Exit(1)
+ }
+
+ // join the ping room
+ resp, err := client.JoinRoom(ctx, Config.PingRoom, "", nil)
+ if err != nil {
+ log.Errorf("Failed to join ping room: %s", err)
+ os.Exit(1)
+ }
+ Config.PingRoomID = resp.RoomID
+
+ syncer := client.Syncer.(*mautrix.DefaultSyncer)
+ syncer.OnEventType(event.EventMessage, func(ctx context.Context, evt *event.Event) {
+ if evt.RoomID != Config.PingRoomID {
+ return
+ }
+ if evt.Sender == client.UserID {
+ return
+ }
+ // Make sure the message is not older than startTime
+ if evt.Timestamp < startTime.Unix()*1000 {
+ return
+ }
+
+ if evt.Content.AsMessage().Body == "!ping" {
+ // Respond to the ping
+ pong_event_content := PongEventContent{
+ MsgType: "m.notice",
+ Body: "Pong!",
+ RelatesTo: PongEventRelation{
+ RelType: "xyz.maubot.pong",
+ EventID: evt.ID.String(),
+ },
+ }
+ _, err := client.SendMessageEvent(ctx, evt.RoomID, event.EventMessage, pong_event_content)
+ if err != nil {
+ log.Errorf("Failed to send pong: %s", err)
+ }
+ }
+ })
+
+ syncCtx, cancelSync := context.WithCancel(context.Background())
+ defer cancelSync()
+
+ go func() {
+ err = client.SyncWithContext(syncCtx)
+ if err != nil && !errors.Is(err, context.Canceled) {
+ log.Errorf("Failed to sync: %s", err)
+ os.Exit(1)
+ }
+ }()
+
+ return client
+}
diff --git a/ping_json.go b/ping_json.go
@@ -1,26 +1,26 @@
package main
type Ping struct {
- Diffs map[string]int `json:"diffs"`
- Mean float64 `json:"mean"`
- Median float64 `json:"median"`
- GMean float64 `json:"gmean"`
+ Pongs map[string]Pong `json:"pongs"`
+ Pings []string `json:"pings"`
+ Mean float64 `json:"mean"`
+ Median float64 `json:"median"`
+ GMean float64 `json:"gmean"`
}
+// A map of homeserver names to ping data
type Pings map[string]Ping
type Pong struct {
- Pings []string `json:"pings"`
+ Diffs []string `json:"diffs"`
Mean float64 `json:"mean"`
Median float64 `json:"median"`
GMean float64 `json:"gmean"`
}
-type PongServers []string
-
type Data struct {
- Disclaimer string `json:"disclaimer"`
- Pings Pings `json:"pings"`
- Mean float64 `json:"mean"`
- PongServers PongServers `json:"pongservers"`
+ Disclaimer string `json:"disclaimer"`
+ Pings Pings `json:"pings"`
+ Mean float64 `json:"mean"`
+ PongServers []string `json:"pongservers"`
}
diff --git a/ping_manager.go b/ping_manager.go
@@ -1 +1,108 @@
package main
+
+import (
+ "context"
+ "encoding/json"
+ "net/http"
+ "time"
+
+ "github.com/prometheus/client_golang/prometheus"
+ log "github.com/sirupsen/logrus"
+ "maunium.net/go/mautrix"
+)
+
+type PingCollector struct {
+ Config Config
+ Metrics map[string]*prometheus.Desc
+ LastCollected time.Time
+}
+
+func (c *PingCollector) Describe(ch chan<- *prometheus.Desc) {
+ mean := prometheus.NewDesc(prometheus.BuildFQName(collector, "", "mean"), "Mean ping time", []string{"homeserver"}, nil)
+ median := prometheus.NewDesc(prometheus.BuildFQName(collector, "", "median"), "Median ping time", []string{"homeserver"}, nil)
+ gmean := prometheus.NewDesc(prometheus.BuildFQName(collector, "", "gmean"), "GMean ping time", []string{"homeserver"}, nil)
+ c.Metrics = make(map[string]*prometheus.Desc)
+ c.Metrics["mean"] = mean
+ c.Metrics["median"] = median
+ c.Metrics["gmean"] = gmean
+ log.Infof("Registered metrics")
+}
+
+// For collection of matrix metrics we first need to ping.
+// To do that we do 2 things:
+// 1. Write `!ping` in the ping room as a message from our own homeserver
+// 2. Send `!ping` from all remote homeservers to our ping room
+// We then track our event_ids and make sure that at least one event is reaching our own homeserver
+// And at least one event id (can be a different one) reaches the other homeservers.
+// We also do react on our own `!ping` message and send a `!pong` back to the ping room based on the maubot echobot logic.
+func (c *PingCollector) Collect(ch chan<- prometheus.Metric) {
+ log.Infoln("Starting Collecting metrics")
+}
+
+// This sends a ping into the ping room
+// It takes a homeserver config as an argument to know where to send the ping
+func (c *PingCollector) SendPing(ctx context.Context, client mautrix.Client, ch chan<- prometheus.Metric) {
+ if c.LastCollected.Add(time.Duration(c.Config.PingRateSeconds) * time.Second).After(time.Now()) {
+ log.Infof("Not sending ping as we sent one less than %d seconds ago", c.Config.PingRateSeconds)
+ return
+ }
+ log.Infof("Sending ping as %s", client.UserID)
+
+ // Send the ping
+ resp, err := client.SendText(ctx, c.Config.PingRoomID, "!ping")
+ if err != nil {
+ log.Errorf("Failed to send ping: %s", err)
+ }
+ log.Infof("Sent ping as %s with event_id %s", client.UserID, resp.EventID)
+
+ // Poll the json to check if any pongs have been received for this ping
+ // Otherwise timeout after PingThresholdSeconds
+ var currentData Data
+ var gotKnownPong bool = false
+
+outer:
+ for time.Now().Before(c.LastCollected.Add(time.Duration(c.Config.PingThresholdSeconds) * time.Second)) {
+ resp, err := http.Get(c.Config.PingJsonURL)
+ if err != nil {
+ log.Errorf("Failed to get ping json: %s", err)
+ }
+
+ // Parse the json
+ // If we have a pong for this ping, we can break the loop
+ // Otherwise we sleep for 1 second and try again
+ defer resp.Body.Close()
+ json.NewDecoder(resp.Body).Decode(¤tData)
+
+ // Check if we have a pong for this ping
+ if ping, ok := currentData.Pings[client.HomeserverURL.Host]; ok {
+ log.Infof("Got pong for ping as %s", client.UserID)
+
+ // Check if its from a known remote homeserver in ping.Pongs
+ for homeserver := range ping.Pongs {
+ if homeserver == client.HomeserverURL.Host {
+ continue
+ }
+ if _, ok := currentData.Pings[homeserver]; ok {
+ log.Infof("Got pong for ping as %s from %s", client.UserID, homeserver)
+ // break out of outer loop
+ gotKnownPong = true
+ break outer
+ }
+ }
+ }
+ }
+
+ // If we have not received a pong for this ping, we should log it
+ if !gotKnownPong {
+ log.Errorf("Failed to get pong for ping as %s", client.UserID)
+ }
+
+ // Update mean, median and gmean metrics
+ // All of these are per homeserver we received a pong from
+ // They are all of type histogram
+ for homeserver, ping := range currentData.Pings[client.HomeserverURL.Host].Pongs {
+ ch <- prometheus.MustNewConstMetric(c.Metrics["mean"], prometheus.GaugeValue, ping.Mean, homeserver)
+ ch <- prometheus.MustNewConstMetric(c.Metrics["median"], prometheus.GaugeValue, ping.Median, homeserver)
+ ch <- prometheus.MustNewConstMetric(c.Metrics["gmean"], prometheus.GaugeValue, ping.GMean, homeserver)
+ }
+}