diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..723ef36 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.idea \ No newline at end of file diff --git a/Procfile b/Procfile new file mode 100644 index 0000000..4595213 --- /dev/null +++ b/Procfile @@ -0,0 +1 @@ +worker: bin/bot \ No newline at end of file diff --git a/bot.go b/bot.go new file mode 100644 index 0000000..fce0290 --- /dev/null +++ b/bot.go @@ -0,0 +1,31 @@ +package alasbot + +import "fmt" + +type Bot struct { + Game Game + Chat Chat +} + +type Game interface { + PlayerCount() (int, int, error) +} + +type Chat interface { + OnMessage(func(author, message string) string) +} + +func (bot Bot) Start() { + + bot.Chat.OnMessage(func(author, message string) string { + if message == "!server" { + count, max, err := bot.Game.PlayerCount() + if err != nil { + return "Sorry, could not get player count: " + err.Error() + } + + return fmt.Sprintf("There are %v/%v players connected", count, max) + } + return "" + }) +} diff --git a/cmd/bot/main.go b/cmd/bot/main.go new file mode 100644 index 0000000..b94124d --- /dev/null +++ b/cmd/bot/main.go @@ -0,0 +1,26 @@ +package main + +import ( + "fmt" + "github.com/tlanfer/alasbot" + "github.com/tlanfer/alasbot/plugins/discord" + "github.com/tlanfer/alasbot/plugins/sevendays" + "os" + "time" +) + +func main() { + + game := sevendays.New(os.Getenv("SEVEN_DAYS_SERVER")) + chat, _ := discord.New(os.Getenv("BOT_TOKEN")) + + bot := alasbot.Bot{ + Game: game, + Chat: chat, + } + + bot.Start() + for _ = range time.Tick(30 * time.Second) { + fmt.Println("still alive...") + } +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..3c437f8 --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module github.com/tlanfer/alasbot + +go 1.14 + +require github.com/bwmarrin/discordgo v0.22.0 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..f0c6772 --- /dev/null +++ b/go.sum @@ -0,0 +1,6 @@ +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= +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= diff --git a/plugins/discord/client.go b/plugins/discord/client.go new file mode 100644 index 0000000..fae96fb --- /dev/null +++ b/plugins/discord/client.go @@ -0,0 +1,45 @@ +package discord + +import ( + "github.com/bwmarrin/discordgo" + "github.com/tlanfer/alasbot" + "log" +) + +func New(token string) (alasbot.Chat, error) { + dg, err := discordgo.New(token) + + if err != nil { + return nil, err + } + + dg.Identify.Intents = discordgo.MakeIntent(discordgo.IntentsGuildMessages | discordgo.IntentsDirectMessages) + + err = dg.Open() + + return &discordClient{ + dg: dg, + }, nil +} + +type discordClient struct { + dg *discordgo.Session +} + +func (d *discordClient) OnMessage(f func(author, message string) string) { + d.dg.AddHandler(func(s *discordgo.Session, m *discordgo.MessageCreate) { + + user := m.Author.Username + message := m.Content + + response := f(user, message) + + if response != "" { + _, err := s.ChannelMessageSend(m.ChannelID, response) + + if err != nil { + log.Println("failed to respond:", err) + } + } + }) +} diff --git a/plugins/sevendays/client.go b/plugins/sevendays/client.go new file mode 100644 index 0000000..85ba8b2 --- /dev/null +++ b/plugins/sevendays/client.go @@ -0,0 +1,70 @@ +package sevendays + +import ( + "github.com/tlanfer/alasbot" + "io/ioutil" + "net" + "strconv" + "strings" +) + +const ( + PlayerCount = "CurrentPlayers" + MaxPlayers = "MaxPlayers" +) + +type client struct { + addr string +} + +func New(addr string) alasbot.Game { + return &client{ + addr: addr, + } +} + +func (c *client) PlayerCount() (int, int, error) { + props, err := c.props() + if err != nil { + return 0, 0, err + } + + count, err := strconv.Atoi(props[PlayerCount]) + + if err != nil { + return -1, -1, err + } + + max, err := strconv.Atoi(props[MaxPlayers]) + + if err != nil { + return -1, -1, err + } + + return count, max, nil +} + +func (c *client) props() (map[string]string, error) { + + conn, err := net.Dial("tcp", c.addr) + + if err != nil { + panic(err) + } + + bytes, err := ioutil.ReadAll(conn) + + lines := strings.Split(string(bytes), ";") + + properties := map[string]string{} + + for _, line := range lines { + parts := strings.SplitN(line, ":", 2) + if len(parts) == 2 { + properties[parts[0]] = parts[1] + } + + } + + return properties, nil +}