Skip to content

Commit

Permalink
Add status template
Browse files Browse the repository at this point in the history
  • Loading branch information
rkfg committed Nov 5, 2021
1 parent dece2ea commit 7ed323d
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 6 deletions.
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,12 @@ the interval (in seconds) between querying the same server. All servers are quer

Then setup the servers you want to watch. `name` can be anything, the bot will use it for announcing, address should be in the `ip:port`
form (where port is `the game port + 1`, i.e. if you see 27015 in the Steam server browser use 27016 here). `player_slots` is the number of
slots for players and `spec_slots` is spectator slots. The bot uses those to post "last minute" notifications.
slots for players and `spec_slots` is spectator slots. The bot uses those to post "last minute" notifications. `status_template` is an
optional parameter that defines the bot's status line. It's used to quickly see the server status without asking the bot directly.
The status is displayed on Discord as "Playing ...", you can specify the format in this parameter using Go's template syntax. See
`config_sample.json` for a full example with all available variables. tl;dr variables are used as `{{ .VarName }}`, all other characters
are printed as is. The variables are: `ServerName`, `Players`, `PlayerSlots`, `SpecSlots`, `FreeSlots`, `TotalSlots`, `Map`, `Skill`.
Hopefully, they're self-describing.

The `seeding` section defines the player number boundaries. Inside that section there are two most important parameters, `seeding` (the bot
will announce that the server is getting seeded when at least this many players have connected) and `almost_full` (it will say that the
Expand Down
67 changes: 66 additions & 1 deletion bot.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"bytes"
"crypto/tls"
"fmt"
"log"
Expand All @@ -10,6 +11,7 @@ import (
"os/signal"
"strings"
"syscall"
"text/template"
"time"

"github.com/bwmarrin/discordgo"
Expand All @@ -32,6 +34,17 @@ type message struct {
channelID string
}

type currentServerStatus struct {
ServerName string
Players int
TotalSlots int
PlayerSlots int
SpecSlots int
FreeSlots int
Map string
Skill int
}

var (
sendChan = make(chan message, 10)
)
Expand Down Expand Up @@ -142,6 +155,49 @@ func handleCommand(s *discordgo.Session, m *discordgo.MessageCreate) {
}
}

func statusUpdate(restartChan chan struct{}, s *discordgo.Session) {
for {
status := &bytes.Buffer{}
for _, s := range config.Servers {
if s.statusTemplate != nil {
if status.Len() > 0 {
status.WriteString(" | ")
}
cs := currentServerStatus{
ServerName: s.Name,
Players: len(s.players),
PlayerSlots: s.PlayerSlots,
SpecSlots: s.SpecSlots,
FreeSlots: s.SpecSlots + s.PlayerSlots - len(s.players),
TotalSlots: s.SpecSlots + s.PlayerSlots,
Map: s.currentMap,
Skill: s.avgSkill,
}
if err := s.statusTemplate.Execute(status, cs); err != nil {
log.Printf("Error executing template for server %s: %s", s.Name, err)
}
}
}
statusStr := "Natural Selection 2"
if status.Len() > 0 {
statusStr = status.String()
}
s.UpdateStatusComplex(discordgo.UpdateStatusData{
Status: "online",
Activities: []*discordgo.Activity{{
Type: discordgo.ActivityTypeGame,
Name: statusStr,
}},
})
select {
case <-time.After(config.QueryInterval * time.Second):
case <-restartChan:
log.Print("Restart request received, stopping status updater")
return
}
}
}

func sendMsg(c chan message, s *discordgo.Session) {
for msg := range c {
channelID := msg.channelID
Expand Down Expand Up @@ -186,16 +242,25 @@ func bot() (err error) {
defer dg.Close()
dg.AddHandler(handleCommand)
go sendMsg(sendChan, dg)
restartChan := make(chan bool)
restartChan := make(chan struct{})
for i := range config.Servers {
config.Servers[i].restartChan = restartChan
if config.Servers[i].StatusTemplate != "" {
t, err := template.New(config.Servers[i].Address + "/template").Parse(config.Servers[i].StatusTemplate)
if err != nil {
log.Printf("Error in status template '%s': %s", config.Servers[i].StatusTemplate, err)
} else {
config.Servers[i].statusTemplate = t
}
}
go query(config.Servers[i])
}
for tid := range config.Threads {
if err := dg.ThreadJoin(tid); err != nil {
log.Printf("Error joining thread %s: %s", tid, err)
}
}
go statusUpdate(restartChan, dg)
fmt.Println("Bot is now running. Press CTRL-C to exit.")
sc := make(chan os.Signal, 1)
signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt)
Expand Down
9 changes: 6 additions & 3 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
"os"
"text/template"
"time"
)

Expand All @@ -20,16 +21,18 @@ const (
type ns2server struct {
Name string
Address string
SpecSlots int `json:"spec_slots"`
PlayerSlots int `json:"player_slots"`
SpecSlots int `json:"spec_slots"`
PlayerSlots int `json:"player_slots"`
StatusTemplate string `json:"status_template"`
statusTemplate *template.Template
players []string
serverState state
maxStateToMessage state
lastStateAnnounced state
lastStatePromotion time.Time
currentMap string
avgSkill int
restartChan chan bool
restartChan chan struct{}
failures int
downSince *time.Time
}
Expand Down
3 changes: 2 additions & 1 deletion config_sample.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
"name": "Server 1",
"address": "192.168.0.1:27016",
"player_slots": 20,
"spec_slots": 6
"spec_slots": 6,
"status_template": "{{ .ServerName }} Pl:{{ .Players }}/{{ .PlayerSlots }}+{{ .SpecSlots }}={{ .TotalSlots }};{{ .Map }}@{{ .Skill }}"
},
{
"name": "Server 2",
Expand Down

0 comments on commit 7ed323d

Please sign in to comment.