From afa2a78b8ed8f8e43b7d33cf56dd3ee5567f156f Mon Sep 17 00:00:00 2001 From: Wolfy Date: Sat, 30 Apr 2022 17:30:36 +0200 Subject: [PATCH 1/5] initial commit --- .gitignore | 2 ++ env/env.go | 51 ++++++++++++++++++++++++++++++++++++++++++++ go.mod | 15 +++++++++++++ go.sum | 16 ++++++++++++++ handler/handler.go | 53 ++++++++++++++++++++++++++++++++++++++++++++++ main.go | 10 +++++++++ runner/runner.go | 41 +++++++++++++++++++++++++++++++++++ utils/utils.go | 53 ++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 241 insertions(+) create mode 100644 env/env.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 handler/handler.go create mode 100644 main.go create mode 100644 runner/runner.go create mode 100644 utils/utils.go diff --git a/.gitignore b/.gitignore index 66fd13c..bee0cda 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,5 @@ # Dependency directories (remove the comment below to include it) # vendor/ + +config.json \ No newline at end of file diff --git a/env/env.go b/env/env.go new file mode 100644 index 0000000..73cff1d --- /dev/null +++ b/env/env.go @@ -0,0 +1,51 @@ +package env + +import ( + "encoding/json" + "io/ioutil" + "log" + "os" +) + +var ( + configuration *Static + ConfigFlag string = "DOWNDETECTOR_CONFIG" +) + +// Later move these to types.go +type Static struct { + BotToken string `json:"bot_token"` + BotGuild string `json:"bot_guild"` + Address string `json:"address"` + Checks []Check +} + +type Check struct { + ChannelName string `json:"channelName"` + Port int `json:"port"` + Type string `json:"type"` + Value string `json:"value"` + Interval string `json:"interval"` +} + +func Configuration() *Static { + if configuration == nil { + var path string + if path = os.Getenv(ConfigFlag); path == "" { + path = "./config.json" + } + // read from path + var s Static + // json unmarshal into s + configuration = &s + + file, err := ioutil.ReadFile(path) + if err != nil { + log.Printf("[ERROR] %s", err.Error()) + } + + json.Unmarshal(file, &configuration) + + } + return configuration +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..a9573e1 --- /dev/null +++ b/go.mod @@ -0,0 +1,15 @@ +module github.com/infiniteloopcloud/discord-downdetector + +go 1.17 + +require ( + github.com/Clinet/discordgo-embed v0.0.0-20220113222025-bafe0c917646 + github.com/bwmarrin/discordgo v0.25.0 + github.com/infiniteloopcloud/discord-jira v0.2.0 +) + +require ( + github.com/gorilla/websocket v1.4.2 // indirect + golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b // indirect + golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..619619b --- /dev/null +++ b/go.sum @@ -0,0 +1,16 @@ +github.com/Clinet/discordgo-embed v0.0.0-20220113222025-bafe0c917646 h1:KkKIDMzyOhNnW5ew6KpRvEflciWqo09NmgVGqTZEj3M= +github.com/Clinet/discordgo-embed v0.0.0-20220113222025-bafe0c917646/go.mod h1:0ydUl+01209LCyzJk68BeRtCN1IMrNJgX4IBmwmC1f8= +github.com/bwmarrin/discordgo v0.25.0 h1:NXhdfHRNxtwso6FPdzW2i3uBvvU7UIQTghmV2T4nqAs= +github.com/bwmarrin/discordgo v0.25.0/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY= +github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/infiniteloopcloud/discord-jira v0.2.0 h1:w3PuND+cojzqfbkk1UinZHSU3c86giqwSF9idFbDBJY= +github.com/infiniteloopcloud/discord-jira v0.2.0/go.mod h1:G3sQnTp4hX9/rcl0qc0dhRSPlJU/R9lbk/xOqc8dy10= +golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b h1:7mWr3k41Qtv8XlltBkDkl8LoP3mpSgBW8BUoxtEdbXg= +golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/handler/handler.go b/handler/handler.go new file mode 100644 index 0000000..658f1d0 --- /dev/null +++ b/handler/handler.go @@ -0,0 +1,53 @@ +package handler + +import ( + "log" + "net" + "strconv" + "time" + + embed "github.com/Clinet/discordgo-embed" + "github.com/bwmarrin/discordgo" + env "github.com/infiniteloopcloud/discord-downdetector/env" +) + +const ( + warning = 0xD10000 +) + +var channelName string + +func Handle(body env.Check) (string, *discordgo.MessageEmbed, error) { + if !checkHealth(body) { + return unreachable(body) + } else { + return "", nil, nil + } +} + +func unreachable(check env.Check) (string, *discordgo.MessageEmbed, error) { + + message := embed.NewEmbed(). + SetAuthor(check.Type). + SetTitle("["+check.Value+"] Host unreachable"). + SetColor(warning) + + return check.ChannelName, message.MessageEmbed, nil +} + +func checkHealth(check env.Check) bool { + timeout := 1 * time.Second + port := strconv.Itoa(check.Port) + + conn, err := net.DialTimeout("tcp", net.JoinHostPort(check.Value, port), timeout) + if err != nil { + log.Println("Connecting error:", err) + } + if conn != nil { + defer conn.Close() + log.Println("[DEBUG] Reachable", net.JoinHostPort(check.Value, port)) + return true + } + + return false +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..94b873c --- /dev/null +++ b/main.go @@ -0,0 +1,10 @@ +package main + +import ( + + runner "github.com/infiniteloopcloud/discord-downdetector/runner" +) + +func main() { + runner.Run() +} diff --git a/runner/runner.go b/runner/runner.go new file mode 100644 index 0000000..5b0f6a4 --- /dev/null +++ b/runner/runner.go @@ -0,0 +1,41 @@ +package runner + +import ( + "log" + + "github.com/infiniteloopcloud/discord-downdetector/env" + handler "github.com/infiniteloopcloud/discord-downdetector/handler" + utils "github.com/infiniteloopcloud/discord-downdetector/utils" +) + +func check(body env.Check) { + channel, message, err := handler.Handle(body) + if err != nil { + log.Printf("[ERROR] %s", err.Error()) + return + } + channelID := utils.GetChannelID(channel) + if channelID == "" { + channelID = utils.GetChannelID("unknown") + } + if channelID != "" && message != nil { + _, err = utils.GetSession().ChannelMessageSendEmbed(channelID, message) + if err != nil { + log.Printf("[ERROR] %s", err.Error()) + } + } +} + +// TODO +// I could remove the type from Check +func Run() { + log.Printf("[RUNNING] Downdetector") + + for i := range env.Configuration().Checks { + handler.Handle(env.Configuration().Checks[i]) + } + + + + +} diff --git a/utils/utils.go b/utils/utils.go new file mode 100644 index 0000000..b5bf6fc --- /dev/null +++ b/utils/utils.go @@ -0,0 +1,53 @@ +package utils + +import ( + "encoding/json" + "log" + + "github.com/bwmarrin/discordgo" + "github.com/infiniteloopcloud/discord-downdetector/env" +) + +var session *discordgo.Session +var channelsCache map[string]string + +func GetEvent(raw []byte) (string, error) { + var static env.Static + err := json.Unmarshal(raw, &static) + if err != nil { + return "", err + } + return static.Checks[0].Type, nil +} + +func GetChannelID(name string) string { + if channelsCache == nil { + channelsCache = make(map[string]string) + } + if id, ok := channelsCache[name]; ok { + return id + } else { + channels, err := GetSession().GuildChannels(env.Configuration().BotGuild) + if err != nil { + log.Print(err) + } + for _, channel := range channels { + if name == channel.Name { + channelsCache[channel.Name] = channel.ID + return channel.ID + } + } + } + return "" +} + +func GetSession() *discordgo.Session { + if session == nil { + var err error + session, err = discordgo.New("Bot " + env.Configuration().BotToken) + if err != nil { + log.Printf("[ERROR] %s", err.Error()) + } + } + return session +} From 4ac28b280ca9649316b3266c6fbbd0ecd10de51a Mon Sep 17 00:00:00 2001 From: Wolfy Date: Sat, 30 Apr 2022 18:23:02 +0200 Subject: [PATCH 2/5] added time.sleep --- env/env.go | 13 ++++++------- handler/handler.go | 13 +++++-------- main.go | 1 - runner/runner.go | 22 +++++++++++++++++----- utils/utils.go | 10 +++++++++- 5 files changed, 37 insertions(+), 22 deletions(-) diff --git a/env/env.go b/env/env.go index 73cff1d..95ee76d 100644 --- a/env/env.go +++ b/env/env.go @@ -12,20 +12,19 @@ var ( ConfigFlag string = "DOWNDETECTOR_CONFIG" ) -// Later move these to types.go type Static struct { BotToken string `json:"bot_token"` BotGuild string `json:"bot_guild"` Address string `json:"address"` - Checks []Check + ChannelName string `json:"channelName"` + Checks []Check } type Check struct { - ChannelName string `json:"channelName"` - Port int `json:"port"` - Type string `json:"type"` - Value string `json:"value"` - Interval string `json:"interval"` + Port string `json:"port"` + Type string `json:"type"` + Value string `json:"value"` + Interval string `json:"interval"` } func Configuration() *Static { diff --git a/handler/handler.go b/handler/handler.go index 658f1d0..e6eeb34 100644 --- a/handler/handler.go +++ b/handler/handler.go @@ -3,7 +3,6 @@ package handler import ( "log" "net" - "strconv" "time" embed "github.com/Clinet/discordgo-embed" @@ -28,24 +27,22 @@ func Handle(body env.Check) (string, *discordgo.MessageEmbed, error) { func unreachable(check env.Check) (string, *discordgo.MessageEmbed, error) { message := embed.NewEmbed(). - SetAuthor(check.Type). - SetTitle("["+check.Value+"] Host unreachable"). + SetAuthor("Port " + check.Port). + SetTitle("[Host unreachable] " + check.Value). SetColor(warning) - return check.ChannelName, message.MessageEmbed, nil + return env.Configuration().ChannelName, message.MessageEmbed, nil } func checkHealth(check env.Check) bool { timeout := 1 * time.Second - port := strconv.Itoa(check.Port) - conn, err := net.DialTimeout("tcp", net.JoinHostPort(check.Value, port), timeout) + conn, err := net.DialTimeout("tcp", net.JoinHostPort(check.Value, check.Port), timeout) if err != nil { - log.Println("Connecting error:", err) + log.Println("[ERROR] unreachable", check.Value+":"+check.Port) } if conn != nil { defer conn.Close() - log.Println("[DEBUG] Reachable", net.JoinHostPort(check.Value, port)) return true } diff --git a/main.go b/main.go index 94b873c..a2fcd9a 100644 --- a/main.go +++ b/main.go @@ -1,7 +1,6 @@ package main import ( - runner "github.com/infiniteloopcloud/discord-downdetector/runner" ) diff --git a/runner/runner.go b/runner/runner.go index 5b0f6a4..155d00b 100644 --- a/runner/runner.go +++ b/runner/runner.go @@ -2,6 +2,7 @@ package runner import ( "log" + "time" "github.com/infiniteloopcloud/discord-downdetector/env" handler "github.com/infiniteloopcloud/discord-downdetector/handler" @@ -31,11 +32,22 @@ func check(body env.Check) { func Run() { log.Printf("[RUNNING] Downdetector") - for i := range env.Configuration().Checks { - handler.Handle(env.Configuration().Checks[i]) + // A loop what runs forever to check is the host reachable + for { + for i := range env.Configuration().Checks { + check(env.Configuration().Checks[i]) + } + // Checks only the first object's interval + // Don't wait between objects + interval, unit := utils.GetTime(env.Configuration().Checks[0].Interval) + switch unit { + case "h": + time.Sleep(time.Duration(interval) * time.Hour) + case "m": + time.Sleep(time.Duration(interval) * time.Minute) + case "s": + time.Sleep(time.Duration(interval) * time.Second) + } } - - - } diff --git a/utils/utils.go b/utils/utils.go index b5bf6fc..868aa57 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -3,6 +3,7 @@ package utils import ( "encoding/json" "log" + "strconv" "github.com/bwmarrin/discordgo" "github.com/infiniteloopcloud/discord-downdetector/env" @@ -11,13 +12,20 @@ import ( var session *discordgo.Session var channelsCache map[string]string +func GetTime(x string) (int, string) { + i := x[len(x)-1:] + unitVal := x[:len(x)-1] + unit, _ := strconv.Atoi(unitVal) + return unit, i +} + func GetEvent(raw []byte) (string, error) { var static env.Static err := json.Unmarshal(raw, &static) if err != nil { return "", err } - return static.Checks[0].Type, nil + return env.Configuration().ChannelName, nil } func GetChannelID(name string) string { From a967c4d20819f4d1d1271ba7915ea98d626db93b Mon Sep 17 00:00:00 2001 From: Wolfy Date: Sat, 30 Apr 2022 19:00:05 +0200 Subject: [PATCH 3/5] Sending request to endpoints --- env/env.go | 1 - handler/handler.go | 35 ++++++++++++++++------------------- 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/env/env.go b/env/env.go index 95ee76d..5b70723 100644 --- a/env/env.go +++ b/env/env.go @@ -21,7 +21,6 @@ type Static struct { } type Check struct { - Port string `json:"port"` Type string `json:"type"` Value string `json:"value"` Interval string `json:"interval"` diff --git a/handler/handler.go b/handler/handler.go index e6eeb34..3acc653 100644 --- a/handler/handler.go +++ b/handler/handler.go @@ -2,8 +2,8 @@ package handler import ( "log" - "net" - "time" + "net/http" + "strconv" embed "github.com/Clinet/discordgo-embed" "github.com/bwmarrin/discordgo" @@ -17,34 +17,31 @@ const ( var channelName string func Handle(body env.Check) (string, *discordgo.MessageEmbed, error) { - if !checkHealth(body) { - return unreachable(body) + code := checkHealth(body) + if code != 200 { + return unreachable(body, code) } else { return "", nil, nil } } -func unreachable(check env.Check) (string, *discordgo.MessageEmbed, error) { - +func unreachable(check env.Check, code int) (string, *discordgo.MessageEmbed, error) { + status := strconv.Itoa(code) message := embed.NewEmbed(). - SetAuthor("Port " + check.Port). + SetAuthor("Status code: " + status). SetTitle("[Host unreachable] " + check.Value). SetColor(warning) return env.Configuration().ChannelName, message.MessageEmbed, nil } -func checkHealth(check env.Check) bool { - timeout := 1 * time.Second - - conn, err := net.DialTimeout("tcp", net.JoinHostPort(check.Value, check.Port), timeout) - if err != nil { - log.Println("[ERROR] unreachable", check.Value+":"+check.Port) - } - if conn != nil { - defer conn.Close() - return true - } +func checkHealth(check env.Check) int { + resp, err := http.Get(check.Type+"://"+check.Value) + if err != nil { + log.Println("[ERROR]", err) + return resp.StatusCode + } + defer resp.Body.Close() - return false + return resp.StatusCode } From 2c958b5d432717edf56349c60e4eee37aef837b2 Mon Sep 17 00:00:00 2001 From: Wolfy Date: Sat, 30 Apr 2022 19:08:59 +0200 Subject: [PATCH 4/5] added comments --- handler/handler.go | 13 ++++++++----- runner/runner.go | 16 +++++++--------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/handler/handler.go b/handler/handler.go index 3acc653..52e99fb 100644 --- a/handler/handler.go +++ b/handler/handler.go @@ -16,6 +16,7 @@ const ( var channelName string +// Check is the endpoint alive func Handle(body env.Check) (string, *discordgo.MessageEmbed, error) { code := checkHealth(body) if code != 200 { @@ -25,6 +26,7 @@ func Handle(body env.Check) (string, *discordgo.MessageEmbed, error) { } } +// Send an embed to the downdetector channel func unreachable(check env.Check, code int) (string, *discordgo.MessageEmbed, error) { status := strconv.Itoa(code) message := embed.NewEmbed(). @@ -35,13 +37,14 @@ func unreachable(check env.Check, code int) (string, *discordgo.MessageEmbed, er return env.Configuration().ChannelName, message.MessageEmbed, nil } +// Return the status code of the request func checkHealth(check env.Check) int { - resp, err := http.Get(check.Type+"://"+check.Value) - if err != nil { - log.Println("[ERROR]", err) + resp, err := http.Get(check.Type + "://" + check.Value) + if err != nil { + log.Println("[ERROR]", err) return resp.StatusCode - } - defer resp.Body.Close() + } + defer resp.Body.Close() return resp.StatusCode } diff --git a/runner/runner.go b/runner/runner.go index 155d00b..9cdebc9 100644 --- a/runner/runner.go +++ b/runner/runner.go @@ -27,8 +27,6 @@ func check(body env.Check) { } } -// TODO -// I could remove the type from Check func Run() { log.Printf("[RUNNING] Downdetector") @@ -41,13 +39,13 @@ func Run() { // Don't wait between objects interval, unit := utils.GetTime(env.Configuration().Checks[0].Interval) switch unit { - case "h": - time.Sleep(time.Duration(interval) * time.Hour) - case "m": - time.Sleep(time.Duration(interval) * time.Minute) - case "s": - time.Sleep(time.Duration(interval) * time.Second) + case "h": + time.Sleep(time.Duration(interval) * time.Hour) + case "m": + time.Sleep(time.Duration(interval) * time.Minute) + case "s": + time.Sleep(time.Duration(interval) * time.Second) } } - + } From 9ab48c3141a42876d72f3e87876135ca6efd56d8 Mon Sep 17 00:00:00 2001 From: Wolfy Date: Mon, 2 May 2022 17:21:14 +0200 Subject: [PATCH 5/5] minor changes --- .gitignore | 2 ++ env/env.go | 28 +++++++++++++++++----------- go.mod | 15 --------------- handler/handler.go | 8 +++++++- 4 files changed, 26 insertions(+), 27 deletions(-) delete mode 100644 go.mod diff --git a/.gitignore b/.gitignore index bee0cda..f0c6af6 100644 --- a/.gitignore +++ b/.gitignore @@ -13,5 +13,7 @@ # Dependency directories (remove the comment below to include it) # vendor/ +go.mod +go.sum config.json \ No newline at end of file diff --git a/env/env.go b/env/env.go index 5b70723..9cd0ed5 100644 --- a/env/env.go +++ b/env/env.go @@ -21,9 +21,14 @@ type Static struct { } type Check struct { - Type string `json:"type"` - Value string `json:"value"` - Interval string `json:"interval"` + Type string `json:"type"` + Value string `json:"value"` + Interval string `json:"interval"` + Parameters *Parameters `json:"parameters"` +} + +type Parameters struct { + StatusCode int `json:"status_code"` } func Configuration() *Static { @@ -32,17 +37,18 @@ func Configuration() *Static { if path = os.Getenv(ConfigFlag); path == "" { path = "./config.json" } - // read from path - var s Static - // json unmarshal into s - configuration = &s - + file, err := ioutil.ReadFile(path) if err != nil { - log.Printf("[ERROR] %s", err.Error()) + log.Printf("[ERROR] %s\n", err.Error()) + return nil } - - json.Unmarshal(file, &configuration) + var s Static + if err := json.Unmarshal(file, &s); err != nil { + log.Printf("[ERROR] unmarshal file: %s", err.Error()) + return nil + } + configuration = &s } return configuration diff --git a/go.mod b/go.mod deleted file mode 100644 index a9573e1..0000000 --- a/go.mod +++ /dev/null @@ -1,15 +0,0 @@ -module github.com/infiniteloopcloud/discord-downdetector - -go 1.17 - -require ( - github.com/Clinet/discordgo-embed v0.0.0-20220113222025-bafe0c917646 - github.com/bwmarrin/discordgo v0.25.0 - github.com/infiniteloopcloud/discord-jira v0.2.0 -) - -require ( - github.com/gorilla/websocket v1.4.2 // indirect - golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b // indirect - golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 // indirect -) diff --git a/handler/handler.go b/handler/handler.go index 52e99fb..df96a48 100644 --- a/handler/handler.go +++ b/handler/handler.go @@ -18,12 +18,18 @@ var channelName string // Check is the endpoint alive func Handle(body env.Check) (string, *discordgo.MessageEmbed, error) { + var statusCode = http.StatusOK code := checkHealth(body) - if code != 200 { + + if body.Parameters != nil && body.Parameters.StatusCode != 0 { + statusCode = body.Parameters.StatusCode + } + if code != statusCode { return unreachable(body, code) } else { return "", nil, nil } + } // Send an embed to the downdetector channel