Jamdroid Server Manager
In the summer of 2021, I found myself using a friend’s game server management panel. This runs on the same server that he uses to run a number of game servers (mostly Minecraft, but with the ability to host other games too) and allows the operator to start, stop, and run commands on the servers with a nice user interface. This gave me the idea to work on my own server panel system, though over time I have adjusted the project to fulfill a number of different roles (such as the webhook integrations described later).
The server is hosted using the free tier of Google Cloud Platform’s (GCP) e2 family of machines, providing more than enough performance for the purposes of a web server. It does mean that, should I want to host any game servers, I would need to either use a separate server or increase the tier of machine being used by the web server.
The server is written in Python 3. I started writing it using Python 3.9, and eventually updated through versions to Python 3.12. This has allowed me to use newer features such as structural pattern matching (which makes some chat bot commands somewhat neater to implement), as well as some improvements to type annotations (in particular, typing.Self is something that I make a lot of use of). The project utilises the Flask microframework, as well as the Gunicorn WSGI HTTP server.
User accounts on the site are handled by utilising GitHub’s OAuth functionality, meaning that features such as 2FA can be utilised without any additional effort. This also means that users’ passwords do not need to be saved on the server, helping to improve its security. As an additional touch, the user’s GitHub avatar is shown on the index page when they are logged in.
Overall, the site works on a concept of “apps”, which originally were intended to represent a game server. For a user that has logged in, and that has access to a given app, they will see it appear on the index page along with a number of controls related to that app (e.g. opening a settings page, starting/stopping the app, etc.). The current most fully fledged apps are:
- Discord Bot
Discord allows developers to create bots that can respond to chat messages. Recently, they also implemented a feature allowing users to explicitly run a bot’s application commands, including a UI within the client prompting for command parameters and alowing for some type restrictions. Jamdroid includes an endpoint to allow Discord to send interaction data via a webhook, with responses being returned as JSON objects. This method better fits a web server environment than the alternative, which requires a more active connection to Discord’s gateway in order to receive events. - Webhook Handlers
- GitHub
GitHub repositories and organisations can be set up to send webhooks when certain events take place, such as new commits being pushed or releases being created. Previously, I had a bot written using Hubot that would receive these webhooks and send messages to a specific Slack channel if something of importance had happened. This functionality has been implemented in Jamdroid, which means that the logic can now be written in Python rather than Coffeescript, which I am more comfortable with overall. - Twitch
The Twitch webhook integration sends a message to a specific Discord channel when it receives a payload indication a channel has started broadcasting. It also listens for events showing the channel has stopped broadcasting, and provides a small API in return for our Gameshow and charity stream websites, such that they can show a banner when the channel is currently live. - Twitter
Prior to X (formerly known as Twitter) preventing data being read whilst using the free tier of its API, Jamdroid included functionality to register for and then handle webhook events. The only event that it listened for was that for a new tweet being sent, at which point it would forward a link to the tweet to a specific Slack channel and a specific Discord channel. This set up allowed messages to be sent nearly instantly, as opposed to somewhere between a few minutes and an hour or two when using a service such as IFTTT. In place of webhook events, this functionality is now triggered using a/tweet
command registered in the Discord bot. - YouTube
YouTube uses WebSub (PubSubHubbub) to send webhooks when channels publish or update videos. One of the main difficulties is that the subscription must be kept active in order to continue, otherwise once the expiration time is met no webhooks will be sent. The other main difficulty for my purposes is that updates trigger webhooks, whereas only new videos should trigger a message to be sent to a specific Discord channel. As a result, Jamdroid maintains a list of all videos it has seen before (which was populated with previously uploaded videos manually) such that it will not send a duplicate announcement message.
- GitHub
- URL Shortener
Somewhat simpler than the webhook handlers, Jamdroid powers a URL shortener hosted on a different domain name (jmy.fyi). This required setting up the main Flask app to care about the host header being sent by the browser, and allows the two different domain names to be served from the same server on the same IP address. As a result, urls such as jmy.fyi/about to be resolved without needing to set up more than one server in GCP. - Miscellaneous Services for RASA Studios
Jamdroid, being run on a server 24/7, is able to run some services for RASA Studios. These include, but are not limited to, regularly checking some websites/APIs in order to find updates and then relaying these to a specific Discord channel.
In addition, there are some quality of life features that I have included, such as:
- Automatic Updates
As the source code for the server is hosted on GitHub, I can use the same system for handling GitHub webhooks and use it to pull any changes and restart the server to apply them automatically. - Logs
Users with the correct permissions are able to see the server logs from the current day, and from any other day for which logs are still available. - Basic Analytics
Users with the correct permissions are able to see (amongst other statistics) the number of incoming requests for each route within the last month, the number of times HTTP status code was returned, and the number of outgoing requests for all external webpages within the last month. - Dark Mode
The site has an automatic dark mode applied utilising the CSS@media (prefers-color-scheme: dark)
rule, which (amongst other things) sets the page backgrounds to a dark gray and the text to white.