Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .env.sample
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
BOT_TOKEN="XXX0XXXxXxX0XXXxXxXxXXX0Xx.XxXXXX.xxxxxXxXXxXxxXxXxXXxX0XxX00xxXx0xxX00x" # Bot token, found in developer portal: https://discord.com/developers/applications
GUILD_ID="000000000000000000" # Server ID for bot to join.
RCON_ADDRESS="127.0.0.1:25565" # Server RCON Address. Port must be supplied.
RCON_PASSWORD="chicken" # Server RCON Password
RCON_PASSWORD="hunter2" # Server RCON Password
ADMIN="000000000000000000" # UserID of Admin user. Only 1 ID accepted
START_SERVER_PATH="./startserversample.bat" # Path to .bat file that launches the game. Currently Windows only
SERVER_ADDRESS="" # Optional Server Address argument. If not provided will instead respond with host IP.
SERVER_PORT="" # Optional Server Port argument.
PLAYER_LIST=[IGN1:NN1,IGN2:NN2,IGN3:NN3] # Optional player list. Format is In game name(IGN):Nickname(NN). If IGN:NN combo is not provided then it will just print the IGN
64 changes: 35 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,37 +1,44 @@
# DiscordMinecraftHelper

This is a small self hosted Discord bot designed to monitor a Minecraft server. It features player count monitoring, server status in the sidebar, structured logging, and automatic daily restarts at 2AM. Currently only Windows compatible.
This is a small self hosted Discord bot designed to monitor a Minecraft server. It features player count monitoring, server status in the sidebar, and structured logging.

## Requirements
## Running the Bot

`go >= 1.21`
If you're just looking for a `docker-compose.yml` or `.exe` to run then head on over to the releases. You'll find ZIPs of both, as well as an `.env.sample` and a README that is a copy of this one.

To use the daily restarts and `/restart-server` command, you need a bat file which automatically restarts the server upon shutdown. The All The Mods 9 modpack has one, but all you need is to add this snippet to your `startserver.bat`, around the existing code that launches the `server.jar`
### Docker Compose

```bat
:START
:: Code that launches server jar
echo Restarting automatically in 10 seconds (press Ctrl + C to cancel)
timeout /t 10 /nobreak > NUL
goto:START
```

and you're good to go!

## Running the bot

**If you're just looking for an `exe` to run head over to the releases and download the latest zip. It has a README inside that will help you get started.**
I'm not sure the best way to do this so this is my docker-compose.yml that I am currently using.

```go run main.go```

## Setup
```YAML
services:
bot:
container_name: bot
image: "scidaj57/minecraft-helper"
ports:
- "8080:8080"
env_file:
- "./.env"
```

1. Clone this repo ```git clone git@github.com:ScidaJ/DiscordMinecraftHelper.git```
2. CD into the new directory ```cd DiscordMinecraftHelper```
3. Install dependencies ```go mod download```
4. Make a copy of `.env.sample` and rename to `.env`. The variables in that file are explained [further on.](#.env)
You can also define your environment variables within the `docker-compose.yml` like so

```YAML
services:
bot:
container_name: bot
image: "scidaj57/minecraft-helper"
ports:
- "8080:8080"
environment:
BOT_TOKEN: "XXX0XXXxXxX0XXXxXxXxXXX0Xx"
GUILD_ID: "000000000000000000"
RCON_ADDRESS: "127.0.0.1:25565"
RCON_PASSWORD: "hunter2"
ADMIN: "000000000000000000"
```

## Everything Else
## Discord Setup

This requires making an application with Discord on the Discord Developer Portal, found [here.](https://discord.com/developers/applications) There are a few pieces of information that we need from there, so lets go over what they are.

Expand Down Expand Up @@ -62,15 +69,14 @@ This will be a quick overview of the variables in the `.env` file.
* `RCON_ADDRESS` - This is set in your `server.properties` file or similar. Port must be supplied with the address.
* `RCON_PASSWORD` - This is set in your `server.properties` file or similar.
* `ADMIN` - The User ID of the "Admin" user for the bot/server. They will be pinged if there is an issue with the server.
* `START_SERVER_PATH` The path to your `startserver.bat` file, needed for `/start` and `/restart` commands, as well as the auto-restarting.
* `SERVER_ADDRESS` Optional. The `/address` command just returns the IP of the host machine, as this bot is assuming that the server and bot are running on the same machine. If this variable is filled in then it will instead return this value.
* `SERVER_PORT` Optional.
* `PLAYER_LIST` Optional. For use with `/list` command. If value is provided in the format of `[InGameName1:Nickname1,InGameName2:Nickname2,InGameName3:Nickname3]` then it will replace the in game name with the provided nickname in the list. If no nickname is provided then it will print the in game name instead.

## Commands

The bot only has four commands as it is fairly simple in scope. They are listed below
The bot only has three commands as it is fairly simple in scope. They are listed below

* `/address` Prints out the address of the server. As the bot assumes that the server is running on the same machine it will return the IP of the host machine. **If you do not want this to be the case then fill in the `SERVER_ADDRESS` variable in the `.env` file. It will print that value instead.**
* `/list` List the players currently on the server. If the `.env` variable `PLAYER_LIST` is populated the bot will replace any matching usernames with the corresponding nickname.
* `/restart` Sends the `/stop` command to the server. When paired with the batch script given above the server will restart after 10 seconds.
* `/start` Starts the server by executing the given batch file in `START_SERVER_PATH`
* `/restart` Sends the `/stop` command to the server then checks every 30 seconds for 5 minutes if it has relaunched. You must have some way of restarting your server automatically.
94 changes: 0 additions & 94 deletions bot/bot.go

This file was deleted.

16 changes: 16 additions & 0 deletions build/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# syntax=docker/dockerfile:1
FROM golang:latest AS build
WORKDIR /app
COPY go.mod go.sum main.go /app/
COPY internal/ /app/internal
RUN go mod download
RUN CGO_ENABLED=0 GOOS=linux go build -o /bin/bot ./main.go

FROM alpine:latest AS certs
RUN apk --update add ca-certificates

FROM scratch
COPY --from=certs /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=build /bin/bot /bin/bot
EXPOSE 8080
CMD [ "/bin/bot" ]
5 changes: 0 additions & 5 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,12 @@ go 1.22.3

require (
github.com/bwmarrin/discordgo v0.28.1
github.com/go-co-op/gocron/v2 v2.5.0
github.com/gorcon/rcon v1.3.5
github.com/joho/godotenv v1.5.1
)

require (
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/websocket v1.4.2 // indirect
github.com/jonboulle/clockwork v0.4.0 // indirect
github.com/robfig/cron/v3 v3.0.1 // indirect
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b // indirect
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f // indirect
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 // indirect
)
20 changes: 0 additions & 20 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,36 +1,16 @@
github.com/bwmarrin/discordgo v0.28.1 h1:gXsuo2GBO7NbR6uqmrrBDplPUx2T3nzu775q/Rd1aG4=
github.com/bwmarrin/discordgo v0.28.1/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-co-op/gocron/v2 v2.5.0 h1:ff/TJX9GdTJBDL1il9cyd/Sj3WnS+BB7ZzwHKSNL5p8=
github.com/go-co-op/gocron/v2 v2.5.0/go.mod h1:ckPQw96ZuZLRUGu88vVpd9a6d9HakI14KWahFZtGvNw=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorcon/rcon v1.3.5 h1:YE/Vrw6R99uEP08wp0EjdPAP3Jwz/ys3J8qxI1nYoeU=
github.com/gorcon/rcon v1.3.5/go.mod h1:zR1qfKZttF8vAgH1NsP6CdpachOvLDq8jE64NboTpIM=
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/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4=
github.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
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/exp v0.0.0-20240416160154-fe59bbe5cc7f h1:99ci1mjWVBWwJiEKYY6jWa4d2nTQVIEhZIptnrVb1XY=
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI=
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=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
43 changes: 43 additions & 0 deletions internal/bot/bot.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package bot

import (
botrcon "DiscordMinecraftHelper/internal/server"
"fmt"

"github.com/bwmarrin/discordgo"
)

func UpdateBotStatus(s *discordgo.Session, server *botrcon.Server) {
playerCount, _ := server.GetPlayerCount()
if server.ServerRunning() {
activity := discordgo.Activity{
Name: fmt.Sprintf("Players: %v online", playerCount),
Type: discordgo.ActivityTypeWatching,
State: "Online",
Details: fmt.Sprintf("%v player(s) online!", playerCount),
}
presence := discordgo.UpdateStatusData{
Activities: []*discordgo.Activity{
&activity,
},
Status: string(discordgo.StatusOnline),
AFK: false,
}
s.UpdateStatusComplex(presence)
} else {
activity := discordgo.Activity{
Name: "Server offline",
Type: discordgo.ActivityTypeWatching,
State: "Offline",
Details: "Server offline",
}
presence := discordgo.UpdateStatusData{
Activities: []*discordgo.Activity{
&activity,
},
Status: string(discordgo.StatusOnline),
AFK: false,
}
s.UpdateStatusComplex(presence)
}
}
32 changes: 13 additions & 19 deletions commands/commands.go → internal/commands/commands.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package commands

import (
botrcon "DiscordMinecraftHelper/server"
botrcon "DiscordMinecraftHelper/internal/server"
"log/slog"

"github.com/bwmarrin/discordgo"
Expand All @@ -14,32 +14,28 @@ type (
Options []*discordgo.ApplicationCommandOption
}

HandleFunc func(s *discordgo.Session, i *discordgo.InteractionCreate, g botrcon.Server)
HandleFunc func(s *discordgo.Session, i *discordgo.InteractionCreate, g *botrcon.Server)
)

var (
Address = SlashCommand{
Name: "address",
Description: "Return the current server IP + port.",
}
List = SlashCommand{
Name: "list",
Description: "List the players currently active on the server.",
}
Start = SlashCommand{
Name: "start",
Description: "Starts the Minecraft server. Will not restart it if already started.",
}
Restart = SlashCommand{
Name: "restart",
Description: "Restarts the Minecraft server manually. This is done every night automatically.",
}
Address = SlashCommand{
Name: "address",
Description: "Return the current server IP + port.",
Description: "Restarts the Minecraft server manually. Admin user only.",
}
)

func AddCommandHandlers(s *discordgo.Session, server botrcon.Server, logger *slog.Logger) {
func AddCommandHandlers(s *discordgo.Session, server *botrcon.Server, logger *slog.Logger) {
s.AddHandler(func(s *discordgo.Session, i *discordgo.InteractionCreate) {
commandHandlers := GetCommandsHandlers()
logger.Info("command received", "command", i.ApplicationCommandData().Name, "user", i.Member.User.Username)
logger.Info("command received", "command", i.ApplicationCommandData().Name)
if h, ok := commandHandlers[i.ApplicationCommandData().Name]; ok {
h(s, i, server)
}
Expand All @@ -48,10 +44,9 @@ func AddCommandHandlers(s *discordgo.Session, server botrcon.Server, logger *slo

func GetCommands() []SlashCommand {
return []SlashCommand{
Address,
List,
Start,
Restart,
Address,
}
}

Expand All @@ -60,10 +55,9 @@ func GetCommands() []SlashCommand {
// not be registered.
func GetCommandsHandlers() map[string]HandleFunc {
return map[string]HandleFunc{
"list": PlayerListHandler,
"restart": RestartServerHandler,
"start": StartServerHandler,
"address": ServerAddressHandler,
"address": AddressHandler,
"list": ListHandler,
"restart": RestartHandler,
}
}

Expand Down
Loading