diff --git a/Gopkg.lock b/Gopkg.lock index 11bcf70..a6e2a98 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -2,12 +2,12 @@ [[projects]] - digest = "1:87c2e02fb01c27060ccc5ba7c5a407cc91147726f8f40b70cceeedbc52b1f3a8" + digest = "1:05eebdd5727fea23083fce0d98d307d70c86baed644178e81608aaa9f09ea469" name = "github.com/Sirupsen/logrus" packages = ["."] pruneopts = "UT" - revision = "e1e72e9de974bd926e5c56f83753fba2df402ce5" - version = "v1.3.0" + revision = "60c74ad9be0d874af0ab0daef6ab07c5c5911f0d" + version = "v1.6.0" [[projects]] branch = "master" @@ -22,20 +22,20 @@ revision = "8159197d3770eb6dbf3a9706a6d40462ebb69cec" [[projects]] - digest = "1:4fd5ce7844c22e194005b9e12fee8adc70fb5ba0bbba9e1964d2e3d1f301d789" + digest = "1:d87c9221a974263e3b369bfd3513707b2a53e27a6cd799d472f94dc6a6157e59" name = "github.com/bwmarrin/discordgo" packages = ["."] pruneopts = "UT" - revision = "4a33b9bc7c56cfdb9bb244e33e83cb3941fe2bdc" - version = "v0.18.0" + revision = "ed4d6904961d1688b3f5601b3d73e95a71046734" + version = "v0.20.3" [[projects]] - digest = "1:43dd08a10854b2056e615d1b1d22ac94559d822e1f8b6fcc92c1a1057e85188e" + digest = "1:6d29f02f0f01c627c2be40fb7347669a9ff2aa215cb97747294c1d13ffa74bdd" name = "github.com/gorilla/websocket" packages = ["."] pruneopts = "UT" - revision = "ea4d1f681babbce9545c9c5f3d5194a789c89f5b" - version = "v1.2.0" + revision = "b65e62901fc1c0d968042419e74789f6af455eb9" + version = "v1.4.2" [[projects]] branch = "master" @@ -46,36 +46,37 @@ revision = "2e40e0d508cbac591bab4ae18b231153295f3a0a" [[projects]] - digest = "1:0a69a1c0db3591fcefb47f115b224592c8dfa4368b7ba9fae509d5e16cdc95c8" + digest = "1:09cb61dc19af93deae01587e2fdb1c081e0bf48f1a5ad5fa24f48750dc57dce8" name = "github.com/konsorten/go-windows-terminal-sequences" packages = ["."] pruneopts = "UT" - revision = "5c8c8bd35d3832f5d134ae1e1e375b69a4d25242" - version = "v1.0.1" + revision = "edb144dfd453055e1e49a3d8b410a660b5a87613" + version = "v1.0.3" [[projects]] branch = "master" - digest = "1:e035fb07be76ce6e4ce005add75491d9f0749403f595f153d6e2823a71c24149" + digest = "1:1714bd928fd176237ccdea21695bf72801c1ee5f51bccefb69b87f0fd376a6aa" name = "golang.org/x/crypto" packages = [ + "internal/subtle", "nacl/secretbox", "poly1305", "salsa20/salsa", - "ssh/terminal", ] pruneopts = "UT" - revision = "c4a91bd4f524f10d064139674cf55852e055ad01" + revision = "70a84ac30bf957c7df57edd1935d2081871515e1" [[projects]] branch = "master" - digest = "1:43cde116ff48f299eddb7e6515677e6d0a2c915854bb05a333877f07c3bb3033" + digest = "1:145abe7dfa46d17ef35e5126ed1cc87b9100e78b9f428c9460deea34bfeabafb" name = "golang.org/x/sys" packages = [ + "cpu", + "internal/unsafeheader", "unix", - "windows", ] pruneopts = "UT" - revision = "11f53e03133963fb11ae0588e08b5e0b85be8be5" + revision = "226ff32320da7b90d0b5bc2365f4e359c466fb78" [solve-meta] analyzer-name = "dep" diff --git a/Gopkg.toml b/Gopkg.toml index e71da66..43dc884 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -31,7 +31,7 @@ [[constraint]] name = "github.com/bwmarrin/discordgo" - version = "0.18.0" + version = "0.20.0" [prune] go-tests = true diff --git a/config.template.json b/config.template.json index f65fe2d..6af593a 100644 --- a/config.template.json +++ b/config.template.json @@ -4,6 +4,7 @@ "RolesToNotify": ["<@&roleid>", "<@userid>"], "GameStatus": "current playing game", "PollingInterval": 10, + "BotPrefix": "!", "Servers": [ { "Name": "Your awesome server", diff --git a/config/config.go b/config/config.go index 69be37d..2a1b8ad 100644 --- a/config/config.go +++ b/config/config.go @@ -16,16 +16,20 @@ type configStruct struct { Token string `json:"Token"` RoomIDList []string `json:"RoomIDList"` RolesToNotify []string `json:"RolesToNotify"` - Servers []server `json:"Servers"` + Servers []Server `json:"Servers"` GameStatus string `json:"GameStatus"` PollingInterval time.Duration `json:"PollingInterval"` + BotPrefix string `json:"BotPrefix"` } -type server struct { +type Server struct { Name string `json:"Name"` Address string `json:"Address"` Port int `json:"Port"` Online bool `json:"Online,omitempty"` + // OnlineTimestamp - time of when the server last came online + OnlineTimestamp time.Time + OfflineTimestamp time.Time } func Configure() { diff --git a/makefile b/makefile index d582a52..8958b9b 100644 --- a/makefile +++ b/makefile @@ -4,7 +4,7 @@ run: go run ./src/main.go linux: - go build -o ./dist/ServerStatus-linux -ldflags="-X main.version=${VERSION}" ./main.go + GOOS=linux GOARCH=amd64 go build -o ./dist/ServerStatus-linux -ldflags="-X main.version=${VERSION}" ./main.go mac: GOOS=darwin GOARCH=amd64 go build -o ./dist/ServerStatus-mac -ldflags="-X main.version=${VERSION}" ./main.go diff --git a/readme.md b/readme.md index 4285d1c..630e9b5 100644 --- a/readme.md +++ b/readme.md @@ -43,7 +43,7 @@ services: ## Usage To get the current status of your servers simply type `!ServerStatus` in chat. -![Server Status](https://i.imgur.com/ZzQSBJp.png) +![Server Status](./readme_files/screenshot1.png) ## Compiling from source - Make sure Go and Make are installed @@ -55,5 +55,4 @@ https://github.com/reactiflux/discord-irc/wiki/Creating-a-discord-bot-&-getting- ### How to get your room ID To get IDs, turn on Developer Mode in the Discord client (User Settings -> Appearance) and then right-click your name/icon anywhere in the client and select Copy ID. - - + diff --git a/readme_files/screenshot1.png b/readme_files/screenshot1.png new file mode 100644 index 0000000..0060e1d Binary files /dev/null and b/readme_files/screenshot1.png differ diff --git a/readme_files/screenshot2.gif b/readme_files/screenshot2.gif new file mode 100644 index 0000000..a48072f Binary files /dev/null and b/readme_files/screenshot2.gif differ diff --git a/serverstatus/serverstatus.go b/serverstatus/serverstatus.go index 5ee2be0..1424d3d 100644 --- a/serverstatus/serverstatus.go +++ b/serverstatus/serverstatus.go @@ -1,12 +1,14 @@ package serverstatus import ( + "fmt" "log" "strconv" "strings" + "sync" "time" - "github.com/anvie/port-scanner" + portscanner "github.com/anvie/port-scanner" "github.com/bwmarrin/discordgo" steam "github.com/kidoman/go-steam" "github.com/mgerb/ServerStatus/bot" @@ -24,6 +26,8 @@ func Start() { //set each server status as online to start for i := range config.Config.Servers { config.Config.Servers[i].Online = true + config.Config.Servers[i].OnlineTimestamp = time.Now() + config.Config.Servers[i].OfflineTimestamp = time.Now() } err := bot.Session.UpdateStatus(0, config.Config.GameStatus) @@ -48,49 +52,74 @@ func scanServers() { for { - for index, server := range config.Config.Servers { - prevServerUp := server.Online //set value to previous server status + // use waitgroup to scan all servers concurrently + var wg sync.WaitGroup - serverScanner := portscanner.NewPortScanner(server.Address, time.Second*2, 1) - serverUp := serverScanner.IsOpen(server.Port) //check if the port is open - - // if server isn't up check RCON protocol (UDP) - if !serverUp { - host := server.Address + ":" + strconv.Itoa(server.Port) - steamConnection, err := steam.Connect(host) - if err == nil { - defer steamConnection.Close() - _, err := steamConnection.Ping() - if err == nil { - serverUp = true - } - } - } - - if serverUp && serverUp != prevServerUp { - sendMessageToRooms(green, server.Name, "Is now online :smiley:", true) - } else if !serverUp && serverUp != prevServerUp { - sendMessageToRooms(red, server.Name, "Has gone offline :frowning2:", true) - } - - config.Config.Servers[index].Online = serverUp + for index := range config.Config.Servers { + wg.Add(1) + go worker(index, &config.Config.Servers[index], &wg) } + wg.Wait() + time.Sleep(time.Second * config.Config.PollingInterval) } } +func worker(index int, server *config.Server, wg *sync.WaitGroup) { + defer wg.Done() + + prevServerUp := server.Online //set value to previous server status + + var serverUp bool + retryCounter := 0 + + // try reconnecting 5 times if failure persists (every 2 seconds) + for { + serverScanner := portscanner.NewPortScanner(server.Address, time.Second*2, 1) + serverUp = serverScanner.IsOpen(server.Port) //check if the port is open + if serverUp || retryCounter >= 5 { + break + } + retryCounter++ + time.Sleep(time.Second * 2) + } + + // if server isn't up check RCON protocol (UDP) + if !serverUp { + host := server.Address + ":" + strconv.Itoa(server.Port) + steamConnection, err := steam.Connect(host) + if err == nil { + defer steamConnection.Close() + _, err := steamConnection.Ping() + if err == nil { + serverUp = true + } + } + } + + if serverUp && serverUp != prevServerUp { + server.OnlineTimestamp = time.Now() + sendMessageToRooms(green, server.Name, "Is now online :smiley:", true) + } else if !serverUp && serverUp != prevServerUp { + server.OfflineTimestamp = time.Now() + sendMessageToRooms(red, server.Name, "Has gone offline :frowning2:", true) + } + + server.Online = serverUp +} + func sendMessageToRooms(color int, title, description string, mentionRoles bool) { for _, roomID := range config.Config.RoomIDList { if mentionRoles { content := strings.Join(config.Config.RolesToNotify, " ") bot.Session.ChannelMessageSend(roomID, content) } - sendEmbededMessage(roomID, color, title, description) + sendEmbeddedMessage(roomID, color, title, description) } } -func sendEmbededMessage(roomID string, color int, title, description string) { +func sendEmbeddedMessage(roomID string, color int, title, description string) { embed := &discordgo.MessageEmbed{ Color: color, @@ -110,13 +139,23 @@ func MessageHandler(s *discordgo.Session, m *discordgo.MessageCreate) { return } - if m.Content == "!ServerStatus" { + if m.Content == config.Config.BotPrefix+"ServerStatus" { for _, server := range config.Config.Servers { if server.Online { - sendEmbededMessage(m.ChannelID, green, server.Name, "Online!") + sendEmbeddedMessage(m.ChannelID, green, server.Name, "Online!\nUptime: "+fmtDuration(time.Since(server.OnlineTimestamp))) } else { - sendEmbededMessage(m.ChannelID, red, server.Name, "Offline!") + sendEmbeddedMessage(m.ChannelID, red, server.Name, "Offline!\nDowntime: "+fmtDuration(time.Since(server.OfflineTimestamp))) } } } } + +func fmtDuration(d time.Duration) string { + + days := int(d.Hours()) / 24 + hours := int(d.Hours()) % 60 + minutes := int(d.Minutes()) % 60 + seconds := int(d.Seconds()) % 60 + + return fmt.Sprintf("%dd %dh %dm %ds", days, hours, minutes, seconds) +}