discord-cluster-manager 1.0.1
Shard and cluster manager for discord.py bots
License: MIT
Requires Python: >=3.9

# Discord Cluster Manager

A **production-ready shard & cluster manager** for Discord bots.

This library lets you run large Discord bots across multiple processes with **automatic sharding, restarts, health monitoring, and manager-level events**, while keeping your bot code clean and framework-native.

---

## ✨ Features

* 🚀 **Mono-file Usage**: Run everything from your `main.py`.
* 📡 **Manager Events**: Listen for `on_cluster_ready`, `on_cluster_stop`, and `on_all_ready`.
* 🚀 Automatic shard & cluster calculation.
* 🔁 Crash detection with exponential backoff restarts.
* 🧠 **Automatic Cluster Readiness**: Signaling is handled automatically by the library.
* 🔒 Token-safe & Discord TOS-compliant.
* 🧩 Framework-agnostic (discord.py, pycord, nextcord, disnake).
* 📦 PyPI-ready, production-safe design.

---

## 📦 Installation

```bash
pip install discord-cluster-manager
```

**Python 3.9+** required.

---

## ▶️ Usage (`main.py`)

The recommended way is to use the **mono-file** approach. Your `main.py` acts as both the manager and the worker.

```python
import os
import discord
from discord.ext import commands
from cluster_manager import initialize, notify_ready, run_bot, ShardConfig

# 1. Setup your bot
bot = commands.AutoShardedBot(command_prefix="!", intents=discord.Intents.default())

@bot.event
async def on_ready():
    # Called in each cluster process when it's ready.
    # Note: notify_ready() is called automatically by initialize()!
    print(f"Cluster {bot.cluster_id} is online!")

# 2. Configure and Initialize
if __name__ == "__main__":
    config = ShardConfig(
        shards_per_cluster=4,
        # total_shards=8,  # Optional: skip Discord API fetch
    )
    
    # initialize returns the manager only in the manager process
    manager = initialize(bot, config)

    if manager:
        @manager.on_cluster_ready
        def handle_ready(cid):
            print(f"Manager: Cluster {cid} is ready and sharded!")

        @manager.on_all_ready
        def handle_all():
            print("Manager: All clusters are fully operational.")

    # 3. Start!
    # run_bot will start the manager OR the bot depending on the process.
    run_bot(bot, os.getenv("TOKEN"))
```

---

## ⚙️ Configuration Options

| Option               | Description                               | Default     |
| -------------------- | ----------------------------------------- | ----------- |
| `shards_per_cluster` | Shards per process                        | `4`         |
| `total_shards`       | Explicit shard count (disables API fetch) | `None`      |
| `identify_delay`     | Delay between cluster launches (seconds)  | `6`         |
| `boot_timeout`       | Max wait for cluster readiness            | `360`       |
| `monitor_interval`   | Process health check interval             | `10`        |
| `max_restarts`       | Max restart attempts per cluster          | `5`         |
| `restart_cooldown`   | Cooldown between restarts                 | `30`        |
| `bot_entry`          | Bot entry file to spawn                   | `"main.py"` |
| `log_level`          | Logging level (INFO, DEBUG, etc)          | `"INFO"`    |
| `log_format`         | Custom logging format string              | `"[%(asctime)s] [%(name)s] [%(levelname)s] %(message)s"` |

---

## 🔐 Token & Security Policy

### Token usage rules

* If `total_shards` **is set** → Discord API is **never contacted**
* If `total_shards` **is NOT set** → token is used **once** to fetch shard count
* Token is read **only from environment variables**
* Token is sent **only to Discord’s official API**
* Token is **never written to disk**

This design fully complies with the **Discord Developer Terms of Service**.

---

## 🌱 Environment Variables

### Required (only if `total_shards` is not set)

```bash
TOKEN=your_discord_bot_token
```

### Optional overrides

```bash
SHARDS_PER_CLUSTER=4
IDENTIFY_DELAY=6
BOOT_TIMEOUT=360
MAX_RESTARTS=5
RESTART_COOLDOWN=30
```

Environment variables override values from `shards.py`.

---

## 🧠 How It Works (High Level)

1. Loads configuration from `shards.py`
2. Resolves total shard count:

   * Uses `total_shards` if provided
   * Otherwise fetches from Discord API
3. Calculates clusters automatically
4. Spawns child processes using `bot_entry`
5. Injects shard info via environment variables
6. Matches Discord's `on_ready` event to signal readiness automatically.
7. Monitors processes and restarts on failure
8. Gracefully shuts down on SIGINT / SIGTERM

> [!TIP]
> While readiness is automatic, you can still call `notify_ready()` manually if you need to signal readiness *after* some custom async initialization in your `on_ready`.

Your bot code remains untouched and framework-native.

---

## 🧩 Supported Discord Libraries

Works with any client that supports:

* `client.shard_ids`
* `client.shard_count`
* `client.cluster_id`

Tested with:

* discord.py
* pycord
* nextcord
* disnake

---

## 🛑 What This Library Does *Not* Do

* ❌ Does not store or manage tokens
* ❌ Does not modify event loops
* ❌ Does not depend on a specific Discord framework

---

## 🧪 Production Notes

* Recommended for bots with **2000+ guilds**
* Designed for **Docker, systemd, and cloud deployments**
* Safe to run locally or in containers
* Works on Linux, macOS, and Windows

---

## 📜 License

MIT License — free for open-source and commercial use.

---

## 🤝 Contributing

Pull requests are welcome.

Please ensure:

* No token handling logic is added
* No framework-specific assumptions
* All changes remain Discord TOS-compliant