🧭 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:
- Check the current live file.
- Confirm the project Git status.
- Make or confirm a restore point before risky work.
- Change one thing at a time.
- Test/build.
- Restart only the affected service.
- Check logs.
- 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:
- Create the cog file under
bot/cogs/. - Add an
async def setup(bot)function. - Register the extension in the bot startup load list if it should load automatically.
- Restart the bot.
- Check logs for
cog.loadedorcog.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
eventname - 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.