Discord bot for our Minecraft server, Part 1
2024, March 5
Creating a Discord bot for our Minecraft server.
Part 1: Allowing players to turn on the server on their own.
This is a continuation of the previous blog post:
Summary
- We created a Minecraft server in AWS EC2 with NixOS.
- Players can connect to the server.
- The server shuts down automatically to keep the bill small.
- But we still have to manually turn on the server whenever one of the players wants to join.
- In this post I will show you how to create a small bot for the Discord group-chat application.
- The bot will allow players in the Discord server to type slash commands such as
/turn-on
and/server-state
to signal the bot that the EC2 Minecraft Server must be turned on. - After this, the players will be able to start your Minecraft server without any manual intervention on your part.
- The app that controls the bot is written in Rust. Find it in this Github repo:
- Sleepful/minecraft-discord-bot-ec2
- Start by cloning the app and looking through the code .
Prerequisites
- You have your Minecraft server on an EC2 instance. This series is meant for the NixOS instance that we made, but this tutorial will be useful for any EC2 server.
- You made a Discord server.
Discord bot creation
First things first, we should go through Discord's developer UI to create our bot and get an API token. We will add this bot to our discord server.
- Go to Discord Developer Portal.
- Create "New Application" on the Top Right button.
- You will be redirected to the profile of the Discord app you just made.
- On the left Settings, click Bot, you will create and save a Token. This is the Token your bot will use. Keep it safe.
- Go to OAuth2 in the left Settings, scroll down to
OAuth2 URL Generator
, selectbot
andapplications.commands
. - Scroll down again to
Bot Permissions
and selectsend messages
. - Copy the generated URL. It will look something like:
https://discord.com/oauth2/authorize?client_id=...&permissions=...&scope=applications.commands+bot
- Paste the URL in your browser, add the bot to your server.
- Done
Run the Discord bot locally in your computer
Clone the repo if you haven't already Sleepful/minecraft-discord-bot-ec2
Make sure you have Cargo installed.
Make sure you have installed AWS CLI and added your credentials . Test with a command like aws describe-instances
, it should show you your EC2 instance if everything is working well.
Clone the repo.
Update .envrc
, you will add the bot token that was mentioned earlier. The .envrc
should also include the instace_id
from the EC2 instance with Minecraft.
Run the with cargo run
.
Try your bot, once you have added it to the Discord server and ran the app in your computer, you should be able to type into the chat the slash commands turn-on
and server-state
. You will see the bot reply with in the chat with the server's IP address. You can open your Minecraft and connect to the new IP address!
Now, as long as the bot is running in your PC, anyone can use it to turn on the Minecraft server. This might be good enough if you keep your PC ON
whenever your friends might want to play. :)
However, if you don't want the users to rely on your computer, you may put the app in an EC2 that is ON
at all times.
Putting the bot app on NixOS
So let's say you want to put the bot app on an EC2 and naturally you want to use NixOS. Cool! A t4g.nano
with 5G EBS
turned-on all the time is around 3USD/month, maybe 6USD now that AWS charges for public IPs, which might be too much, but if you have your own server to host multiple apps, then adding the bot is going to be really simple. Additionally if you run NixOS in your computer, read on for the Nix code.
If you are going to run the app in your computer but you are not using NixOS, you may stop reading this article and jump to Part 2.
The Nix config
We are going to have two nix files, one for building the Rust app and another one for configuring it in our NixOS system.
default.nix
builds the Rust code:
# default.nix
# allow our nixpkgs import to be overridden if desired
{ pkgs ? import <nixpkgs> {}}:
pkgs.rustPlatform.buildRustPackage {
pname = "mc-discord-bot";
version = "1.0.0";
src = pkgs.fetchFromGitHub {
owner = "Sleepful";
repo = "minecraft-discord-bot-ec2";
rev = "11a62344100367f997b32dbef15489ab515dc0d1";
hash = "sha256-ezICkv3ayyMPR2s3DOUvQJzr/bbiToZnqE/ZFXYLIz4=";
};
cargoHash = "sha256-UmgisgIBWEx3Dmvg7tteMBxdfSacGAekNaL9oXez5vk=";
meta = {
mainProgram = "mc_discord";
};
}
You can test this configuration by running nix-build ./default.nix
Sidenote: if you are on Mac you should test this build inside the Docker container, in my experience it does not build in the Mac terminal do to some obscure linking error.
Then index.nix
configures the package we just built for usage in our system. Consider that index.nix
and default.nix
are in the same directory.
# index.nix
{ lib, pkgs, config, ... }:
let
mcDiscordPkg = pkgs.callPackage ./default.nix {};
awscli = pkgs.awscli2;
in
{
# adding the derivation to systemPackages makes it available to us
environment.systemPackages = [ mcDiscordPkg pkgs.jq ];
users.users.mc-discord = {
isSystemUser = true;
extraGroups = [ "awscli" ];
group = "mc-discord";
};
users.groups.mc-discord = {};
systemd.services."mc-discord" = {
wantedBy = ["multi-user.target"];
description = "discord bot for Minecraft-server";
script = "AWS_SHARED_CREDENTIALS_FILE=/etc/aws_cli_creds DISCORD_TOKEN=<ADD_YOUR_TOKEN_HERE> INSTANCE_ID=<ADD_YOUR_ID_HERE> ${lib.getExe mcDiscordPkg}";
serviceConfig = {
Type = "simple";
User = "mc-discord";
};
path = [ awscli pkgs.jq ];
};
}
And index.nix
just needs to be called from your NixOS configuration.nix
through imports,
imports = [ ./index.nix ];
.
The implication here is that you have already configured awscli2
and jq
packages in your system because the Rust app makes use of those to turn on the EC2 instance with minecraft. This is why we give them the path to the packages:
path = [ awscli pkgs.jq ];
In this example it is also necessary to indicate the location of the AWS CLI credentials file to the service, this is done through the AWS_SHARED_CREDENTIALS_FILE
env var, and the DISCORD_TOKEN
as well as the INSTANCE_ID
env vars must be added to the config too.
script = "AWS_SHARED_CREDENTIALS_FILE=/etc/aws_cli_creds DISCORD_TOKEN=<ADD_YOUR_TOKEN_HERE> INSTANCE_ID=<ADD_YOUR_ID_HERE> ${lib.getExe mcDiscordPkg}";
In this case the /etc/aws_cli_creds
file can be created manually in the server. This might not be as robust as adding it declaratively through Nix configuration, but it makes it simpler to keep the secrets outside of the Nix Store. Remember that everything we add declaratively in Nix will be found in the Nix Store, and the Store is readable by every user in the system ("world readable" as they like to say).
Sidenote: To manage secrets in the Nix store there are multiple solutions, such as agenix. The wiki has a comparison of multiple such solutions.
You may choose a different path than /etc/aws_cli_creds
for your credentials file, the important thing is that you give the file the right permissions so that our service user mc-discord
is capable of reading the file. In this scenario we want to give the file a awscli
user group and allow the user group to read it.
chown root:awscli /etc/aws_cli_creds
chmod 0660 /etc/aws_cli_creds
And then, as shown above, the awscli
group is given to mc-discord
service user:
users.users.mc-discord = {
isSystemUser = true;
extraGroups = [ "awscli" ];
...
}
Deploy it and...
That's it
Now your players can play all by themselves, good job!
Finally, for the next blog post, we will add more Rust code to our Minecraft server.
Right now your friends can start the server and join, but we don't know what happens in the game if we are not online in Minecraft. Let's send logs from our Minecraft server to a Discord channel so that everyone can see when people join the game, leave it, or get killed by a Creeper.
Let's go to the final blog post, this one will be quick to read!
Go back to the top