Skip to content

Development Guide

Benj edited this page Jul 31, 2024 · 24 revisions

Setting Up

Requirements

  • Python 3.10>
  • PostgreSQL
  • Poetry

After Cloning or Forking

Go to your project terminal and type poetry install, (BUT make sure you have poetry installed or else it won't work. although).

Configurations

We have a configurations file named config.yml inside the config/ folder. Since all developers have their own instance of the bot, you have to copy it to the root folder of our project and edit it to match your test server environment. Our configurations are loaded by startup. If you change any of your local configurations, you'd have to restart the bot. We have a config.py file where we load configurations into Python objects.

Note: Not all configurations are in config.yml. Some are saved inside the database (mostly the configurations that can be modified while the bot is running). You can use the handler in src/data/admin/config_auto.py to get/set configurations saved in the database.

Environment Variables

Create a .env file in the root folder. We have an .env.example file where you can check all the required environment variables.

│   .gitignore
└───config 
│     └─config.yml
│     └─dev-config.yml
│   LICENSE.md
│   poetry.lock
│   pyproject.toml
│   README.md
│   .env  # <<< here
└───src
     └─bot
        └─config.py

For the currency_api_key value

Go to Currency Data API and subscribe to the free tier to get your token.

After setting up .env

Go to Discord Applications and create a new application. Next, under Settings on the left sidebar click Bot and look for Privileged Gateway Intents. Check PRESENCE INTENT, SERVER MEMBERS INTENT, and MESSAGE CONTENT INTENT

Get your bot token

Under Settings, Click Bot and you'll see Reset Token, Click that and accept the confirmation. Copy the token and paste it on your .env

Invite your bot to your server

Under Settings, Click OAuth2 and then URL Generator, Check Bot application.commands and Administrator. Scroll down and copy the invite link to invite the bot.

Wrapping up

Make sure you have enabled Developer Mode on your discord account, you can see it at settings > advanced for you to be able to copy your development server's id, role's id, and so on to be put on user-config.yml guild values

Database Migrations

We also have a migration library, yoyo. It automatically migrates your files when you run the bot (poetry run progphil).

Cogs

Inside src/cogs/ folder, we will put our Cogs in there.

Ui

We will store all discord UIs in src/ui/ folder.

  • views/ - All views in here
  • modals/ - All modals in here

Database

All database handlers are in src/data/ directory.

Utilities

All utilities are in src/utils/ directory.

Main file

The only things we should put inside src/bot/main.py are the things needed to setup the bot. FYI we didn't invoke the main/run function to run the bot this is intended because we are utilizing poetry to run the bot instead(check pyproject.toml file under tool.poetry.scripts).

Styling

  • Follow PEP8 standard styling.
  • Always annotate your function parameters, and import the Class directly if ever ex:
from discord import Member, TextChannel, Interaction, ...
from discord.app_commands import command


@command()
async def test(self, interaction: Interaction, ...):
    ...

Documentations

Utility functions

decorators

We will put our decorator utility functions in here. You can refer to this if you want to make your own decorator.

Contents:


is_staff

A decorator for checking if the command invoker is a staff.

def is_staff():
    """A decorator for checking if the
    command invoker is a staff

    Example:
    ```
        @is_staff()
        @command(description='Bans a member')
        @describe(member='the member to ban')
        async def ban(interaction: discord.Interaction, member: discord.Member):
            await interaction.response.send_message(f'Banned {member}')
    ```
    """
    async def predicate(interaction: Interaction[Bot]) -> bool:
        guild_config = interaction.client.config.guild
        staff = False

        for id_ in guild_config.staff_roles:
            role = interaction.guild.get_role(id_)

            if role in interaction.user.roles:
                staff = True
                break  # Break the loop and continue the comand if the invoker is a staff

        return staff

    return app_commands.check(predicate)
Clone this wiki locally