Multiple Developers; One Discord Bot? | Harvey Church's Log

Harvey Church's Log

Multiple Developers; One Discord Bot?

Note: This was originally posted to the fabulous dev.to in March 2022

I had a problem:

How do I keep a Python Discord bot online, allow multiple people to contribute to it and then deploy it when new features are added?

Several Google searches had come up short, partial solutions; no clear answer.

Here is my attempt at a solution (though no promise that it is the smartest, or best.. it has just worked for me ¯\_(ツ)_/¯)

Hosting and Deployment

This project uses discord.py and discord-py-slash-command to patch the missing slash command functionality and is hosted here.

The aim of the project was to allow for any member of a private Discord to contribute custom functionality, test locally and then deploy for all to use.


From Local Development to Deployment

  1. Code can be developed locally, and continually pushed to a GitHub repo.
  2. No code is pushed to the “Live Server” until a git tag is created for a specific release
  3. This tag triggers a GitHub Action that SSH’s into the “live server” and deploys the code
  4. For a user on a Discord server the bot is part of, can now run commands.
  5. Developers can continue to test/create features on a locally hosted bot, and push them without the live bot being effected

For hosting, I am using DigitalOcean.

I am sure any other cloud hosting provider will work, just a few changes in the GitHub action script would be required.

GitHub Actions & Server Setup

The Script

To actually upload the code and implement the triggering of the workflow when a tag is created, I have used the following GitHub workflow script.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
name: Deploy Code to Digital Ocean

on:
push:
branches:
- main
tags:
- 'v0.*'

jobs:
create_release:
if: github.ref_type == 'tag' && contains(github.ref_name,'v')
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2.4.0
- name: Create Release on GitHub
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ github.ref_name }}
token: ${{ secrets.GITHUB_TOKEN }}
push_to_digital_ocean:
needs: create_release
runs-on: ubuntu-latest
steps:
- name: SSH into Droplet and push
uses: garygrossgarten/github-action-ssh@release
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
passphrase: ${{ secrets.PASSWORD }}
privateKey: ${{secrets.SSHKEY}}
command: |
cd beeb-bot/
echo "Running Startup Script"
pm2 stop main_bot
git pull
# For if there have been any changes to the pipefile/dependancies
pipenv install
pm2 start main_bot

The core of the script is the SSH portion. GitHub actions are designed for the repeating of others code. This action makes use of Gary Großgarten’s “Github Action SSH”.

A private key is stored within the repository, with the matching Public Key on the server.

The Juicy Part

The important part of the script is the following commands:

1
2
3
4
5
6
7
cd beeb-bot/
echo "Running Startup Script"
pm2 stop main_bot
git pull
# For if there have been any changes to the pipefile/dependancies
pipenv install
pm2 start main_bot

pm2 in the script refers to a process manager, mainly used for Node.js projects – however it can run Python scripts.

pm2 is used used because just running something like python3 main.py locks up the action, and it can never exit. Using this means the script no longer locks up, and the action succeeds/fails.

main_bot is a short hand for pm2 start "pipenv run python3 main.py" --name main_bot --watch. This project makes use of pipenv to manage dependencies, and to make sure the virtual environment is used, pipenv run is called.

A git instance of repo is stored on the server. git pull gets a copy from GitHub (which should be the latest release).

pipenv install is run to ensure the virtual environment on the server matches the needed packages for the project.

pm2 start main_bot is called to start the process again and run the freshly pulled main.py script.

On the Server (specifically for this project)

The initial set-up on the server has a few requirements. pipenv and pm2 must then be installed.

Following this:

  1. The user must clone the repo on the server
  2. Run pipenv install to establish the same Python packages that the project is expecting (mainly discord.py and discord-py-slash-command)
  3. Have a token stored in the .env file so that when the bot is run, it can talk to Discord and authenticate.
  4. Create a pm2 proccess – pm2 start "pipenv run python3 main.py" --name main_bot – _Note: This project expects the proccess to be called “main_bot”, but in reality, any name can be used._

Final Remarks

This is not entirely comprehensive, but hopefully is a good jumping off point for others.

I am certain that it is not perfect, I have missed something obvious or that there is something simpler.

One big gap, the release process relying on tags is very manual. The usage of Semantic Version could improve this.


P