🏠 Home

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:

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.

  1. Go to Discord Developer Portal.
  2. Create "New Application" on the Top Right button.
  3. You will be redirected to the profile of the Discord app you just made.
  4. On the left Settings, click Bot, you will create and save a Token. This is the Token your bot will use. Keep it safe.
  5. Go to OAuth2 in the left Settings, scroll down to OAuth2 URL Generator, select bot and applications.commands.
  6. Scroll down again to Bot Permissions and select send messages.
  7. Copy the generated URL. It will look something like: https://discord.com/oauth2/authorize?client_id=...&permissions=...&scope=applications.commands+bot
  8. Paste the URL in your browser, add the bot to your server.
  9. 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!


Subscribe to my mailing list

Only to update you when I submit a new blog post.


Go back to the top
© Copyright 2023 by Jose.