MineDisbot
A Discord bot for managing Minecraft servers
Published: 3/13/2026 | Updated: 3/13/2026
Tags: code portfolio, programming, discord, minecraft, bot, showcase
Last summer I had a problem. I had three Minecraft servers for me and my friends that I wanted to run on the same machine, but I could only run one at a time, and I didn’t want to give my friends SSH access to my server. I needed a simple way for them to start and stop servers without letting them start multiple servers simultaneously. So, as usual, I decided to write a custom tool to solve the problem.
Setting up a discord bot in Python is pretty simple with the discord.py package. You
just initialize the bot, and then you can handle events with functions that have the
@bot.event decorator.
import discord
from discord.ext import commands
async def on_ready():
print(f'Logged in as {bot.user})')
intents = discord.Intents.default()
intents.message_content = True
bot = commands.Bot(command_prefix="!", intents=intents)
@bot.event
async def on_ready():
print(f'Logged in as {bot.user})')
@bot.event
async def on_message(message):
print(f'Message from {message.author}: {message.content}')
Then it’s just a matter of verifying the command prefix and channel and you can pass the commands off to command handlers. For example, here is the !listplayers command (you do need another function to trigger these commands, but that’s not shown here):
@bot.command()
async def listplayers(ctx):
''' Lists the players in the server '''
global active_server
if active_server is None:
await ctx.send("No server is currently active.")
return
subprocess.Popen(f"screen -S {active_server} -p 0 -X stuff 'list\n'", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
await ctx.send("Please wait, listing players may take a second...")
Notice that the action taken is to open a subprocess and execute a bash command that runs the list command in the screen session the Minecraft server is running in.
IPC (Inter-Process Communication)
One of the features I really wanted to support with the bot was being able to see and respond to server chat via Discord. I could have achieved that by writing an actual Minecraft mod or plugin, but when managing multiple servers at once that starts to get far more complicated than I wanted to deal with.
So I set up the hackiest, most ridiculous, and most insecure solution imaginable. I gave the bot direct access to the command line.
Now that’s not to say I was totally stupid about it. The only bot command that
could allow arbitrary execution is !command, and that will fail if there isn’t
an appropriately named screen instance. The only way to create one of those instances
is to start a server, which will then capture any stdin input, with the screen session
terminating when the server closes. An attacker would also have to be an admin in
Discord to even send !command, and since it’s only a few of my friends I am not
overly worried about the risk. I definitely would not use this bot for a public
server though.
With that disclaimer out of the way, the way I achieved this communication is rather interesting. See, I need the servers to be accessible to me when I log in via SSH, hence the screen sessions to manage them. Sending a command to a screen session is pretty straightforward, however, if stdout is going to a screen session, how do I monitor it?
I could tail the logs, but that is unreliable and very slow. The actual file system
writes for Minecraft logs can lag behind the server by many seconds. I could,
theoretically, have the discord bot hooked into the screen session at all times,
but that’s laggy too, not to
mention unstable. Fortunately, there is a utility perfectly suited to this situation.
I can use tee to split stdout to the screen session AND a file at the same time. And since
basically everything in linux is a file pointer, I can create a named pipe that is
monitored by the Discord bot and have my output visible from the command line and the
bot simultaneously.
Of course it’s a little bit more complicated than that. Capturing the output is what I wanted to achieve, but when the server writes a lot of messages in a short period of time it could cause the bot to stop listening for commands while it reads all those messages. That’s easily fixable by making the bot read the pipe on a separate thread, which with asyncio is significantly simpler in Python than in some of my C programs.
A live demo?!?
I wanted something interactive for this portfolio page, but with work, all the writing that I’m doing, and the general chaos of life, I didn’t feel like rewriting the whole thing in Typescript… so I threw AI at it. In my opinion one of the coolest use cases for AI-assisted coding is situations exactly like this. “Here’s a (pretty simple) tool. I need it to work in another environment, go make a version in Typescript.”
It almost never works on the first try, even with something as simple as this, but with some tweaking and bug fixes it doesn’t take long. I started with just asking for a simulator of the bot written in Typescript. That mostly worked, but it included an index.html file, and was not something I could just drop into my site, so I copied the files to my site and asked the agent to make a react component version.
That spit out a component that rendered (aside from easily fixed import bugs), but with a bunch of sidebar stuff and extra things I didn’t need, so I cut that code out and started testing it. The UI was still a little messed up, and it was not integrated with Astro correctly (client: load), so I did two final passes, one with Opus 4.6 to fix all the UI issues that were cause by me cutting out the unnecessary parts, and one with GPT 5.4 to fix the logic, and that’s the version you see above.
Comments
Loading comments…
I use cookies to prevent spam. To comment, please enable cookies.