> ## Documentation Index
> Fetch the complete documentation index at: https://docs.fs.soquincy.qzz.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Extending Freesona with Custom Cog Modules

> Freesona is built to be extended. Add new cog modules, register them in the module system, and toggle them at runtime without restarting.

Freesona's cog-based architecture makes it straightforward to add new commands. All logic lives in focused modules under `cogs/`, shared utilities live in `utils/`, and a single registry file (`utils/modules.py`) controls what's available at runtime. Add a file, register it, and enable it from Discord — no restart required.

## How Modules Work

* Each feature lives in `cogs/<category>/<name>.py` as a discord.py `Cog` class.
* **Core modules** (`help`, `ping`, `status`, `admin`) always load on startup and cannot be toggled.
* **Optional modules** are registered in `OPTIONAL_MODULES` in `utils/modules.py` and can be toggled via `/module enable` and `/module disable` at runtime.
* Enabled/disabled state persists in `config.json` under `enabled_modules`, so your configuration survives restarts.
* Slash commands are automatically re-synced when you enable or disable a module.

## Creating a New Module

<Steps>
  <Step title="Create the cog file">
    Create `cogs/<category>/<name>.py`. At minimum, define a `Cog` subclass and a `setup` coroutine:

    ```python cogs/tools/mycommand.py theme={null}
    import discord
    from discord.ext import commands

    class MyCommand(commands.Cog):
        def __init__(self, bot):
            self.bot = bot

        @commands.command()
        async def mycommand(self, ctx, *, text: str):
            await ctx.send(f"You said: {text}")

    async def setup(bot):
        await bot.add_cog(MyCommand(bot))
    ```

    Place it under an existing category (`ai`, `media`, `moderation`, `system`, `tools`, `fun`) or create a new subfolder.
  </Step>

  <Step title="Register it in utils/modules.py">
    Add an entry to `OPTIONAL_MODULES` using a short lowercase key and the full dotted module path:

    ```python utils/modules.py theme={null}
    OPTIONAL_MODULES = {
        # ... existing entries ...
        "mycommand": "cogs.tools.mycommand",
    }
    ```

    Once registered, the key appears in `/module list` and supports autocomplete.
  </Step>

  <Step title="Enable it from Discord">
    Run the enable command in your server:

    ```text theme={null}
    /module enable mycommand
    ```

    The bot loads the cog immediately and re-syncs slash commands.
  </Step>

  <Step title="Verify with /module list">
    Run `/module list` — your new module should appear as enabled with its load status confirmed.
  </Step>
</Steps>

## Accessing Bot Utilities

Import from `utils/` for functionality shared across cogs:

| Module                | What it provides                                                                                            |
| --------------------- | ----------------------------------------------------------------------------------------------------------- |
| `utils/generation.py` | `safe_generate` — the main AI generation call with security checks, persona injection, and memory injection |
| `utils/memory.py`     | `get_user_facts_prompt`, `extract_and_store_fact` — read and write per-user long-term facts                 |
| `utils/persona.py`    | `assemble_persona` — build the active system instruction from the current persona fields                    |
| `utils/config.py`     | `load_config`, `save_config` — read and write `config.json` at runtime                                      |
| `utils/security.py`   | `detect_injection`, `sanitize_prompt`, `is_public_http_url` — input validation and URL guards               |
| `utils/rss.py`        | `load_rss_feeds`, `parse_feed`, `save_rss_feed` — RSS/Atom feed management                                  |

<Tip>
  Keep cogs focused — each file should handle one feature area. Cogs should not import directly from each other. If one cog needs functionality from another (like `mvsep.py` uses `ytdlp.py`), access it via `bot.get_cog("CogClassName")` at call time rather than a top-level import.
</Tip>

## Adding Slash Command Support

discord.py supports exposing a single command as both a prefix and a slash command via `@commands.hybrid_command`. Use this by default — one decorator, one callback:

```python theme={null}
import discord
from discord.ext import commands

class MyCommand(commands.Cog):
    def __init__(self, bot):
        self.bot = bot

    @commands.hybrid_command(name="mycommand", description="Does a thing")
    async def mycommand(self, ctx: commands.Context, *, text: str):
        await ctx.send(f"You said: {text}")
```

Only fall back to defining separate `@app_commands.command` and `@commands.command` callbacks when the slash version needs interaction-only features that `@commands.command` can't provide — for example, modals (`interaction.response.send_modal(...)`), which require a raw `discord.Interaction`.

After adding new slash commands, run `/sync` (Bot Owner) to register them globally with Discord. The `/module enable` and `/module disable` commands also trigger an automatic re-sync.

<Note>
  Run `python scripts/check_project.py` before deploying to verify syntax and config. On Windows PowerShell you can use `.\\scripts\\check.ps1` instead. Both run the same checks and exit non-zero on any failure.
</Note>
