Skip to content

🧭 Coding Rules

This page documents the main coding rules for the Magikal bot and web panel.

These rules are based on the current live project structure and coding patterns.

Core workflow

Before changing code:

  1. Check the current live file.
  2. Confirm the project Git status.
  3. Make or confirm a restore point before risky work.
  4. Change one thing at a time.
  5. Test/build.
  6. Restart only the affected service.
  7. Check logs.
  8. Commit the finished change.

Project paths

Project Path
Discord bot /home/magikalbot/magikal-bot
Web panel /home/magikalbot/magikal-panel
Documentation /home/magikalbot/magikal-docs

Live files first

Do not patch from memory.

Before editing, inspect the current live file from the VPS.

For bot work:

cd /home/magikalbot/magikal-bot
git status --short

For panel work:

cd /home/magikalbot/magikal-panel
git status --short

For docs work:

cd /home/magikalbot/magikal-docs
git status --short

One step at a time

Avoid large, mixed changes.

Good:

  • one cog fix
  • one panel layout fix
  • one migration
  • one docs page
  • one logging cleanup

Avoid:

  • changing multiple cogs and panel pages in the same patch
  • mixing migrations with unrelated UI work
  • refactoring and feature work at the same time
  • changing working architecture without approval

Bot architecture rules

The bot entry point is:

bot/main.py

Core bot setup happens there, including:

  • bot config
  • database manager
  • config storage
  • state storage
  • cog loading
  • slash command sync
  • startup and shutdown logging

Normal feature code should live in cogs:

bot/cogs/

Shared helpers should live in:

bot/utils/

Storage adapters should live in:

bot/storage/

Database models and database manager code live in:

bot/database.py

Cog structure

New cogs should follow the existing Discord.py pattern.

Basic shape:

class ExampleCog(commands.Cog):
    def __init__(self, bot):
        self.bot = bot
        self.config_storage = getattr(bot, "config_storage", None)

async def setup(bot: commands.Bot):
    await bot.add_cog(ExampleCog(bot))

Cog loading

Cogs are loaded from the bot startup extension list in:

bot/main.py

When adding a new cog:

  1. Create the cog file under bot/cogs/.
  2. Add an async def setup(bot) function.
  3. Register the extension in the bot startup load list if it should load automatically.
  4. Restart the bot.
  5. Check logs for cog.loaded or cog.load_failed.

Storage rules

Postgres is the primary source of truth.

Normal cogs should use:

self.config_storage

or:

self.bot.config_storage

The current adapter is usually:

PgConfigStorage

Avoid direct database/session access in normal cogs.

Direct access to DatabaseManager or session_factory should be treated as an advanced/approved exception.

Observed current exception:

banking.py

That cog currently uses bot.database_manager directly. Do not copy that pattern into new cogs unless the architecture is explicitly approved.

State storage rules

Persistent config and records belong in Postgres.

Temporary runtime state may use a state storage adapter.

Current state storage patterns include:

  • Postgres state adapter for Temp VC live state
  • Redis for sensitive/ephemeral helper data where still required
  • in-process fallback storage only for non-critical runtime state

Do not introduce SQLite unless explicitly approved.

Logging rules

Use structured logging.

Good:

logger.info(event="feature.action", result="ok")

Avoid:

logger.info(f"User {user.id} did something")

Every useful log should have:

  • stable event name
  • short useful fields
  • no secrets
  • no raw Discord mentions
  • no raw user IDs
  • no raw Discord objects

Privacy rules

Use privacy-safe PIDs for user-related records and logs.

Helper:

make_pid(guild_id, user_id)

Common fields:

guild_pid
user_pid
actor_pid
channel_pid

Do not log:

  • raw Discord user IDs
  • mentions
  • tokens
  • secrets
  • full Discord objects
  • private message contents
  • unnecessary personal data

Known legacy cleanup area

Some older live code still contains logs using raw guild_id or channel_id.

New code should not copy those patterns.

When touching older code, prefer improving logs to the privacy-safe pattern if it is safe and in scope.

Command rules

Commands should be clear about:

  • who can use them
  • whether they are slash commands or text commands
  • required Discord permissions
  • required role/capability gates
  • whether they are guild-only
  • whether they post publicly or ephemerally

Use @app_commands.guild_only() for slash commands that should not work in DMs.

Use Discord permission decorators where appropriate.

Examples seen in live cogs:

@app_commands.default_permissions(manage_guild=True)
@app_commands.guild_only()
@commands.has_permissions(manage_messages=True)
@commands.guild_only()

Error handling

Catch expected Discord/API errors.

Good patterns:

  • log the event
  • keep the error short
  • send a user-friendly message
  • do not expose secrets or raw traceback to users
  • do not continue after a failed critical step

Comments and docstrings

Use comments for why something exists, not for obvious Python.

Good comments explain:

  • privacy decisions
  • storage decisions
  • fallback behaviour
  • Discord permission edge cases
  • migration safety
  • rate-limit or API assumptions

Dates and display format

Use UK date format in user-facing bot output and documentation examples.

Preferred:

DD/MM/YYYY

Before restarting the bot

Check Git state:

cd /home/magikalbot/magikal-bot
git status --short

Restart:

sudo systemctl restart magikal-bot

Check logs:

sudo journalctl -u magikal-bot -n 120 --no-pager

Red flags

Stop and review before continuing if a change:

  • touches database models
  • needs Alembic
  • changes logging/privacy behaviour
  • changes permissions or command gates
  • changes ticket/recruitment state
  • changes Temp VC state
  • changes panel save/load behaviour
  • introduces a new storage backend
  • requires multiple services to restart

Rule

Keep changes small, privacy-safe, and based on the current live files.