Skip to content

3. Working on the Bot

Jace Manshadi edited this page Oct 18, 2023 · 9 revisions

1. Setup Python Environment

for Debian based OS

sudo apt-get install -y python3.9
curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
python3.9 get-pip.py --user
python3.9 -m pip install virtualenv --user
python3.9 -m virtualenv walle
. walle/bin/activate

for MacOS

https://www.python.org/downloads/release/python-3913/

python3.9 -m pip install --upgrade pip
python3.9 -m pip install virtualenv
python3.9 -m virtualenv walle
. walle/bin/activate

for Windows

open to anyone to make a PR adding this section

2. Setup and run Wall-E

If you encounter any errors doing the following commands, feel free to add it to the FAQs section for future reference :)

Pre-requisites: git

If you have not cloned your forked version yet
wget https://raw.githubusercontent.com/CSSS/wall_e/master/download_repo.sh
./download_repo.sh

If you have forked your version
./run_site.sh

2.1. Wall_e Coding Tips

2.1.1. Text Command Tips

2.1.1.1. Text Command Help Message Configurations

Please follow the format below when adding a new help message so that layout is unified across all commands

    @commands.command(
        brief="short help message in main help embed",
        help=(
            'Description of command purpose.\n'
            'Arguments:\n'
            '---argument_1: argument_1 description\n'
            '---argument_2: argument_2 description\n\n'
            'Example:\n'
            '.command_name "foo"\n'
            '.command_name "foo" "bar"\n\n'
        ),
        usage='foo [bar]'
    )
    async def command_name(self, ctx, *args):


2.1.1.2. Text Command Privilege Configurations
Role Based Validation
    @commands.has_any_role("role1", "role2")
    async def command_name(self, ctx, *args):

OR

    @commands.has_role("role1")
    async def command_name(self, ctx, *args):
Permission Based Validation
    @commands.has_guild_permissions(administrator=True, manage_roles=True) # this does an AND
    async def command_name(self, ctx, *args):
Customizable Permission Checking

If the above examples don't provide a way for the sort of permission checking you are attempting, you can write your own logic using the below example

    def user_has_perms(ctx):
        return ctx.author.guild_permissions.administrator or ctx.author.guild_permissions.manage_roles

    @commands.check(user_has_perms)
    async def command_name(self, ctx, *args):

2.1.2. Slash Command Tips

2.1.2.1. Slash Command Help Message Configurations
    @app_commands.command(name="command_name", description="command description")
    @app_commands.describe(argument_name="argument description")
    async def function_name(self, interaction: discord.Interaction, argument_name: str):


2.1.2.2. Slash Command Privilege Configurations
Role Based Validation
    @app_commands.checks.has_role("role1")
    async def command_name(self, interaction: discord.Interaction):

OR

    @app_commands.checks.has_any_role("role1", "role2")
    async def command_name(self, interaction: discord.Interaction):
Permission Based Validation
    @app_commands.checks.has_permissions(administrator=True, manage_roles=True) # this does an AND
    async def command_name(self, interaction: discord.Interaction):

If the above examples don't provide a way for the sort of permission checking you are attempting, you can write your own logic using the below example

    def user_permission_check(interaction: discord.Interaction) -> bool:
        return interaction.user.guild_permissions.administrator or interaction.user.guild_permissions.manage_roles

    @app_commands.check(user_permission_check)
    async def command_name(self, interaction: discord.Interaction):
2.1.2.3. Implementing Slash Commands Auto-Complete Menu

slash commands introduced the ability to provide users with a menu of options they can choose from for a command's argument. You can see this in action with the /iam command on the CSSS Discord guild OR

    @app_commands.autocomplete(argument_name=[
        # list of app_commands.Choice items
        # choice object example: app_commands.Choice(name="string that will be shown in discord, value="value passed to the command")
    ])
    async def command_name(self, interaction: discord.Interaction, argument_name: str):

If the options you want to provide need to be pulled from discord in some way, you can extend the functionality of the autocomplete function like this:

    async def argument_options(interaction: discord.Interaction, current: str) -> List[app_commands.Choice[str]]:
        // create choice object using data pulled from interaction and using "current" as a filter to pass the objects through
        return [choice]

    @app_commands.autocomplete(argument_name=argument_options)
    async def command_name(self, interaction: discord.Interaction, argument_name: str):

2.1.3. Adding a New Cog Class

General layout to follow when adding a new cog class

class NewCog(commands.Cog):

    def __init__(self, bot, config, bot_channel_manager):
        log_info = Loggers.get_logger(logger_name="NewCog")
        self.logger = log_info[0]
        self.debug_log_file_absolute_path = log_info[1]
        self.error_log_file_absolute_path = log_info[2]
        self.logger.info("[NewCog __init__()] initializing NewCog")
        self.bot = bot
        self.config = config
        self.guild: discord.Guild = None
        self.bot_channel_manager = bot_channel_manager

    @commands.Cog.listener(name="on_ready")
    async def get_guild(self):
        self.guild = self.bot.guilds[0]

    @commands.Cog.listener(name="on_ready")
    async def upload_debug_logs(self):
        if self.config.get_config_value('basic_config', 'ENVIRONMENT') != 'TEST':
            while self.guild is None:
                await asyncio.sleep(2)
            await start_file_uploading(
                self.logger, self.guild, self.bot, self.config, self.debug_log_file_absolute_path, "new_cog_debug"
            )

    @commands.Cog.listener(name="on_ready")
    async def upload_error_logs(self):
        if self.config.get_config_value('basic_config', 'ENVIRONMENT') != 'TEST':
            while self.guild is None:
                await asyncio.sleep(2)
            await start_file_uploading(
                self.logger, self.guild, self.bot, self.config, self.error_log_file_absolute_path, "new_cog_error"
            )

# specify any new command to listeners below
Adding to .ini files

Add the cog to the .ini files in the https://github.com/CSSS/wall_e/tree/master/wall_e/utilities/config folder

Listeners

If you need to add any logic to the cog class that the bot will execute as soon as the on_ready signal has been received [when its logged into the discord guild], use the below template

    @commands.Cog.listener(name='on_ready')
    async def function_name(self):
        while self.guild is None:
            await asyncio.sleep(2)
        # function logic

You can see the list of discord events that a discord bot can listen for here

2.1.4. App Commands Walk-through

the above documentation was adapted from

2.2 To make any needed changes to the models

2.2.1 Setup wall_e_models locally

git clone <fork of https://github.com/CSSS/wall_e_models.git> #do not do this in the wall_e repo

cd /path/to/walle_repo/wall_e
ln -sn /path/to/wall_e_models/wall_e_models wall_e_models

2.2.2 To save changes made to wall_e_models

cd /path/to/wall_e_models
# git add, commit and push and changes to your forked models repo and then open a PR to https://github.com/CSSS/wall_e_models.git

Local Testing Tips

  • if you are done deving locally, you can run /delete_log_channels to delete all the log channels that wall_e created for better visibility into which cogs had which errors
  • If you want to wipe a channel after populating it with a lot of test messages, just run /purge_messages <last_x_messages_to_delete>

3. Before opening a PR

Please submit PRs one week before they need to be merged.

Validating the code locally:

./CI/user_scripts/test_walle.sh

4. Testing on CSSS Bot Test Server

Only works with Text Commands

After you have tested on your own Discord Test Guild, create a PR to the Wall-E Repo that follows the rules for PRs before pushing your changes to Wall-E. Creating the PR will automatically load it into the CSSS Bot Test Server. the name of the channel will be pr-<PR number>.

FAQs

Issue: Experiencing a networking issue with docker-compose

ERROR: could not find an available, non-overlapping IPv4 address pool among the defaults to assign to the network

resolution: If you are using a VPN, please disconnect and try again.

Variable Initialization Details

This may be outdated at this point, not sure anymore

How the Variable needs to be initialized [only relevant to you if you like granularized variable initializations]

Variable Dockerized Non-Dockerized
env variable wall_e.env local.ini env variable local.ini
ENVIRONMENT X x
COMPOSE_PROJECT_NAME X X
POSTGRES_PASSWORD X X x
DOCKERIZED - - - -
WALL_E_DB_USER X x
WALL_E_DB_PASSWORD X x
WALL_E_DB_DBNAME X x
DB_ENABLED - - - -
HOST - - x
ORIGIN_IMAGE X
BOT_LOG_CHANNEL - - - -
BOT_GENERAL_CHANNEL - - - -
DB_PORT X X

X indicates that its necessary to be declared in that way
'-' indicates that the user can choose to declare it only that way

Clone this wiki locally