-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 4b0cad7
Showing
13 changed files
with
448 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
{ | ||
// Use IntelliSense to learn about possible attributes. | ||
// Hover to view descriptions of existing attributes. | ||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 | ||
"version": "0.2.0", | ||
"configurations": [ | ||
{ | ||
"name": "Launch", | ||
"type": "go", | ||
"request": "launch", | ||
"mode": "auto", | ||
"program": "${fileDirname}", | ||
"env": {}, | ||
"args": [] | ||
} | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
{ | ||
// See https://go.microsoft.com/fwlink/?LinkId=733558 | ||
// for the documentation about the tasks.json format | ||
"version": "2.0.0", | ||
"tasks": [ | ||
{ | ||
"label": "go build", | ||
"type": "shell", | ||
"command": "go build", | ||
"problemMatcher": [], | ||
"group": { | ||
"kind": "build", | ||
"isDefault": true | ||
} | ||
} | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
MIT License | ||
|
||
Copyright (c) 2020 rkfg | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
# NS2 Query Bot | ||
|
||
This is a simple bot that queries the Natural Selection 2 servers and sends messages to the specified Discord channel | ||
if there are enough players to start the round. | ||
|
||
# Compiling | ||
|
||
Grab a [Golang](https://golang.org/dl/) installer or install it from your repository, then run `go get -u github.com/rkfg/ns2query`. | ||
You should find the binary in your `$GOPATH/bin` directory. Alternatively, clone the repository and run `go build` inside. | ||
|
||
# Configuration | ||
|
||
Copy the provided `config_sample.json` file to `config.json` and change it to your needs. You should put your Discord bot token to the | ||
`token` parameter, put the channel ID to the `channel_id` parameter (it's the last long number in the Discord URL: | ||
`https://discord.com/channels/AAAAAAAAAAAAAAA/BBBBBBBBBBBBBB`, you need to copy the `BBBBBBBBBBBBBB` part). `query_interval` specifies | ||
the interval (in seconds) between querying the same server. All servers are queried in parallel. | ||
|
||
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. | ||
|
||
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 | ||
server is getting filled but there are still slots if you want to play). The `cooldown` parameter is used when the number of players | ||
fluctuates between two adjacent states. For example, if the `seeding` parameter is `4` and some players join and leave so the number of | ||
players changes back and forth between 3 and 4, this cooldown parameter is used to temporarily mute the new messages about seeding. It's | ||
the number of seconds after the last promotion (getting a higher status) during which demotions (lowering the status) are ignored. If the | ||
server empties normally, then after this cooldown period the seeding announcements will be restored. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"os/signal" | ||
"syscall" | ||
"time" | ||
|
||
"github.com/bwmarrin/discordgo" | ||
) | ||
|
||
func sendMsg(c chan string, s *discordgo.Session) { | ||
for { | ||
s.ChannelMessageSend(config.ChannelID, <-c) | ||
time.Sleep(time.Second) | ||
} | ||
} | ||
|
||
func bot() error { | ||
dg, err := discordgo.New("Bot " + config.Token) | ||
if err != nil { | ||
return err | ||
} | ||
defer dg.Close() | ||
err = dg.Open() | ||
if err != nil { | ||
return err | ||
} | ||
sendChan := make(chan string, 10) | ||
go sendMsg(sendChan, dg) | ||
for i := range config.Servers { | ||
go query(&config.Servers[i], sendChan) | ||
} | ||
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) | ||
<-sc | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
package main | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"os" | ||
"time" | ||
) | ||
|
||
type state int | ||
|
||
const ( | ||
empty state = iota | ||
seedingstarted | ||
almostfull | ||
specsonly | ||
full | ||
) | ||
|
||
type ns2server struct { | ||
Name string | ||
Address string | ||
SpecSlots int `json:"spec_slots"` | ||
PlayerSlots int `json:"player_slots"` | ||
players []string | ||
serverState state | ||
maxStateToMessage state | ||
lastStatePromotion time.Time | ||
} | ||
|
||
func (s *ns2server) playersString() string { | ||
unknowns := 0 | ||
result := "" | ||
for _, p := range s.players { | ||
if p == "Unknown" { | ||
unknowns++ | ||
} else { | ||
if result == "" { | ||
result = p | ||
} else { | ||
result += ", " + p | ||
} | ||
} | ||
} | ||
if unknowns > 0 { | ||
suffix := "" | ||
if unknowns > 1 { | ||
suffix = "s" | ||
} | ||
if result == "" { | ||
return fmt.Sprintf("%d connecting player%s", unknowns, suffix) | ||
} | ||
return fmt.Sprintf("%s and %d connecting player%s", result, unknowns, suffix) | ||
} | ||
return result | ||
} | ||
|
||
type seeding struct { | ||
Seeding int | ||
AlmostFull int `json:"almost_full"` | ||
Cooldown int | ||
} | ||
|
||
var config struct { | ||
Token string | ||
ChannelID string `json:"channel_id"` | ||
QueryInterval time.Duration `json:"query_interval"` | ||
Servers []ns2server | ||
Seeding seeding | ||
} | ||
|
||
func loadConfig() error { | ||
return loadConfigFilename("config.json") | ||
} | ||
|
||
func loadConfigFilename(filename string) error { | ||
if file, err := os.Open(filename); err == nil { | ||
defer file.Close() | ||
json.NewDecoder(file).Decode(&config) | ||
} else { | ||
return err | ||
} | ||
for i := range config.Servers { | ||
config.Servers[i].maxStateToMessage = full | ||
} | ||
if config.QueryInterval < 1 { | ||
return fmt.Errorf("invalid query interval in config.json: %d", config.QueryInterval) | ||
} | ||
if config.ChannelID == "" { | ||
return fmt.Errorf("specify channel_id in config.json") | ||
} | ||
if config.Token == "" { | ||
return fmt.Errorf("specify token in config.json") | ||
} | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
{ | ||
"token": "your.discordbot.token", | ||
"channel_id": "channel identifier", | ||
"query_interval": 10, | ||
"servers": [ | ||
{ | ||
"name": "Server 1", | ||
"address": "192.168.0.1:27016", | ||
"player_slots": 20, | ||
"spec_slots": 6 | ||
}, | ||
{ | ||
"name": "Server 2", | ||
"address": "192.168.0.2:27018", | ||
"player_slots": 16, | ||
"spec_slots": 4 | ||
} | ||
], | ||
"seeding": { | ||
"seeding": 4, | ||
"almost_full": 12, | ||
"cooldown": 600 | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
{ | ||
"servers": [ | ||
{ | ||
"name": "TTO [Backup]", | ||
"address": "127.0.0.1:8080" | ||
} | ||
], | ||
"seeding": { | ||
"seeding": 4, | ||
"almost_full": 12, | ||
"specs_only": 20, | ||
"full": 26, | ||
"cooldown": 60 | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
module rkfg.me/ns2query | ||
|
||
go 1.15 | ||
|
||
require ( | ||
github.com/bwmarrin/discordgo v0.22.0 | ||
github.com/rumblefrog/go-a2s v1.0.0 | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
github.com/bwmarrin/discordgo v0.22.0 h1:uBxY1HmlVCsW1IuaPjpCGT6A2DBwRn0nvOguQIxDdFM= | ||
github.com/bwmarrin/discordgo v0.22.0/go.mod h1:c1WtWUGN6nREDmzIpyTp/iD3VYt4Fpx+bVyfBG7JE+M= | ||
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= | ||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= | ||
github.com/multiplay/go-source v0.0.0-20171201124051-cb29ef5320bb h1:n3wijII160M3fIQqHeLWULRK36kXmEWfrtSFMXNExk0= | ||
github.com/multiplay/go-source v0.0.0-20171201124051-cb29ef5320bb/go.mod h1:o/iuncLTF1DDwozJ6BFN+Q00cvguIvUyRMSlHKIc/FM= | ||
github.com/rumblefrog/go-a2s v1.0.0 h1:9IIVIOQ1bXZJeTilmzkJDeGa/9W1c089VciTbp+Wp1Y= | ||
github.com/rumblefrog/go-a2s v1.0.0/go.mod h1:JwbTgMTRGZcWzr3T2MUfDusrJU5Bdg8biEeZzPtN0So= | ||
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16 h1:y6ce7gCWtnH+m3dCjzQ1PCuwl28DDIc3VNnvY29DlIA= | ||
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package main | ||
|
||
import ( | ||
"log" | ||
) | ||
|
||
func main() { | ||
if err := loadConfig(); err != nil { | ||
log.Fatal(err) | ||
} | ||
err := bot() | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
"time" | ||
|
||
"github.com/rumblefrog/go-a2s" | ||
) | ||
|
||
func maybeNotify(srv *ns2server, sendChan chan string) { | ||
playersCount := len(srv.players) | ||
newState := empty | ||
if playersCount < config.Seeding.Seeding { | ||
newState = empty | ||
} else if playersCount < config.Seeding.AlmostFull { | ||
newState = seedingstarted | ||
} else if playersCount < srv.PlayerSlots { | ||
newState = almostfull | ||
} else if playersCount < srv.PlayerSlots+srv.SpecSlots { | ||
newState = specsonly | ||
} else { | ||
newState = full | ||
} | ||
if newState > srv.serverState && newState <= srv.maxStateToMessage { | ||
srv.lastStatePromotion = time.Now() | ||
srv.serverState = newState | ||
switch newState { | ||
case seedingstarted: | ||
sendChan <- fmt.Sprintf("%s started seeding! There are %d players there currently: %s", | ||
srv.Name, len(srv.players), srv.playersString()) | ||
srv.maxStateToMessage = specsonly | ||
case almostfull: | ||
sendChan <- fmt.Sprintf("%s is almost full! There are %d players there currently", | ||
srv.Name, len(srv.players)) | ||
case specsonly: | ||
srv.maxStateToMessage = seedingstarted | ||
sendChan <- fmt.Sprintf("%s is full but you can still make it! There are %d spectator slots available currently", | ||
srv.Name, srv.PlayerSlots+srv.SpecSlots-len(srv.players)) | ||
} | ||
} else { | ||
if time.Since(srv.lastStatePromotion).Seconds() > float64(config.Seeding.Cooldown) { | ||
srv.serverState = newState | ||
} | ||
} | ||
} | ||
|
||
func query(srv *ns2server, sendChan chan string) error { | ||
client, err := a2s.NewClient(srv.Address) | ||
if err != nil { | ||
return fmt.Errorf("error creating client: %s", err) | ||
} | ||
defer client.Close() | ||
for { | ||
info, err := client.QueryPlayer() | ||
if err != nil { | ||
log.Printf("query error: %s", err) | ||
} else { | ||
srv.players = srv.players[:0] | ||
for _, p := range info.Players { | ||
srv.players = append(srv.players, p.Name) | ||
} | ||
maybeNotify(srv, sendChan) | ||
} | ||
time.Sleep(config.QueryInterval * time.Second) | ||
} | ||
} |
Oops, something went wrong.