Update Architecture
1 parent
eb650d846e
commit
99cfeec7b5
1 file changed
+190
-189
+190
-189
@@ -1,190 +1,191 @@
|
||||
# Architecture
|
||||
|
||||
## Overview
|
||||
|
||||
Pterodisbot is a modular, asynchronous Discord bot built with Python 3.8+ using the `discord.py` library. It follows a clean architecture pattern with clear separation of concerns across multiple modules.
|
||||
|
||||
## System Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ Discord Client │
|
||||
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐ │
|
||||
│ │ Slash Cmds │ │ Buttons │ │ Dropdown Menus │ │
|
||||
│ └──────┬──────┘ └──────┬──────┘ └───────────┬─────────────┘ │
|
||||
└─────────┼────────────────┼──────────────────────┼────────────────┘
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ PterodactylBot (commands.Bot) │
|
||||
│ ┌───────────────────────────────────────────────────────────┐ │
|
||||
│ │ Background Task: update_status (10s interval) │ │
|
||||
│ │ • Smart update detection (state changes, CPU, uptime) │ │
|
||||
│ │ • Rate limit protection │ │
|
||||
│ └───────────────────────────────────────────────────────────┘ │
|
||||
└─────────────────────────────┬───────────────────────────────────┘
|
||||
│
|
||||
┌─────────────────────┼─────────────────────┐
|
||||
▼ ▼ ▼
|
||||
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
|
||||
│ EmbedManager │ │ PanelAPI │ │MetricsManager │
|
||||
│ │ │ │ │ │
|
||||
│ • Create │ │ • Client API │ │ • Data points │
|
||||
│ • Update │ │ • App API │ │ • Graphs │
|
||||
│ • Track │ │ • Auth │ │ • Trends │
|
||||
│ • Persist │ │ • Rate limit │ │ │
|
||||
└───────┬───────┘ └───────┬───────┘ └───────┬───────┘
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
|
||||
│ JSON Storage │ │Pterodactyl/Pelican│ │ Matplotlib │
|
||||
│(embed_locs) │ │ Panel API │ │ (PNG graphs) │
|
||||
└───────────────┘ └───────────────┘ └───────────────┘
|
||||
```
|
||||
|
||||
## Module Structure
|
||||
|
||||
| Module | Responsibility | Key Classes |
|
||||
|--------|---------------|-------------|
|
||||
| `main.py` | Entry point, logging setup | `main()` |
|
||||
| `config.py` | Configuration loading/validation | `Config`, `load_config()` |
|
||||
| `bot.py` | Core bot, commands, background tasks | `PterodactylBot`, `setup_commands()` |
|
||||
| `api.py` | Pterodactyl/Pelican Panel API client | `PanelAPI` |
|
||||
| `embeds.py` | Embed creation, tracking, persistence | `EmbedManager` |
|
||||
| `views.py` | Discord UI components (buttons, dropdowns) | `ServerStatusView`, `ServerDropdown` |
|
||||
| `metrics.py` | Metrics collection & graph generation | `ServerMetricsManager`, `ServerMetricsGraphs` |
|
||||
| `models.py` | Pydantic data models | `ServerData`, `ServerResources`, etc. |
|
||||
| `exceptions.py` | Custom exception hierarchy | `APIError`, `ConfigValidationError`, etc. |
|
||||
| `logging_config.py` | Logging configuration | `setup_logging()` |
|
||||
| `generate_config.py` | Config generation from env vars | `generate_config_from_env()` |
|
||||
|
||||
## Data Flow
|
||||
|
||||
### 1. Bot Startup
|
||||
|
||||
```
|
||||
main() → load_config() → PterodactylBot() → setup_commands() → bot.start()
|
||||
↓
|
||||
setup_hook() → api.initialize()
|
||||
↓
|
||||
embed_manager.load_locations()
|
||||
↓
|
||||
update_status.start()
|
||||
```
|
||||
|
||||
### 2. Status Update Cycle (Every 10s)
|
||||
|
||||
```
|
||||
update_status() → api.get_servers() → For each tracked embed:
|
||||
→ api.get_server_resources()
|
||||
→ metrics_manager.add_server_data() (if running)
|
||||
→ Smart update detection:
|
||||
• Power state changed? → UPDATE
|
||||
• CPU change >50%? → UPDATE
|
||||
• First time seeing server? → UPDATE
|
||||
• Running >10min? → UPDATE (uptime accuracy)
|
||||
→ embed_manager.update_single_embed()
|
||||
→ metrics_manager.generate_combined_graph()
|
||||
→ Discord message.edit()
|
||||
```
|
||||
|
||||
### 3. User Interaction Flow
|
||||
|
||||
```
|
||||
/server_status command
|
||||
→ api.get_servers()
|
||||
→ Create stats embed + dropdown
|
||||
→ User selects server
|
||||
→ api.get_server_resources()
|
||||
→ Delete old embed (if exists)
|
||||
→ embed_manager.create_server_embed()
|
||||
→ Send new embed with ServerStatusView
|
||||
→ embed_manager.track_embed()
|
||||
|
||||
Button press (Start/Stop/Restart/Show Address)
|
||||
→ ServerStatusView.interaction_check() (guild + role)
|
||||
→ api.send_power_action() or api.get_server_default_allocation()
|
||||
→ Ephemeral response to user
|
||||
```
|
||||
|
||||
## Concurrency Model
|
||||
|
||||
- **Async/await** throughout for non-blocking I/O
|
||||
- **asyncio.Lock** in `PanelAPI._request()` for rate limit protection
|
||||
- **asyncio.Lock** in `PterodactylBot.update_status` for update cycle serialization
|
||||
- **deque(maxlen=6)** for automatic metrics rotation (1 min history)
|
||||
- **Background task** with `@tasks.loop(seconds=10)` for periodic updates
|
||||
|
||||
## Persistence
|
||||
|
||||
- **Embed locations**: JSON file (`embed/embed_locations.json`)
|
||||
- **Metrics**: In-memory only (last 6 data points = 1 minute)
|
||||
- **Configuration**: INI file (`config.ini`) or environment variables
|
||||
- **Logs**: Rotating files (5MB, 3 backups) in `logs/`
|
||||
|
||||
## Security Architecture
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────────────────────────┐
|
||||
│ Access Control Layers │
|
||||
├────────────────────────────────────────────────────────────┤
|
||||
│ 1. Guild Restriction (config.allowed_guild_id) │
|
||||
│ └─ Checked in: on_interaction, check_allowed_guild, │
|
||||
│ ServerStatusView.interaction_check │
|
||||
├────────────────────────────────────────────────────────────┤
|
||||
│ 2. Role-Based Access ("Game Server User" role) │
|
||||
│ └─ Checked in: ServerStatusView.interaction_check │
|
||||
├────────────────────────────────────────────────────────────┤
|
||||
│ 3. Admin Permissions (Discord Administrator) │
|
||||
│ └─ Required for: /refresh_embeds, /purge_embeds │
|
||||
└────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## API Integration
|
||||
|
||||
### Dual API Key System
|
||||
|
||||
- **Client API Key** (`ptlc_` / `pacc_`): Server operations (power, resources)
|
||||
- **Application API Key** (`ptla_` / `papp_`): Admin operations (list servers, details)
|
||||
|
||||
### Supported Panels
|
||||
|
||||
- **Pterodactyl Panel** (standard)
|
||||
- **Pelican Panel** (fork, uses `pacc_`/`papp_` prefixes)
|
||||
- Auto-detected via `Config.is_pelican_panel` property
|
||||
|
||||
## Error Handling Strategy
|
||||
|
||||
1. **Validation at startup** - Config validation fails fast
|
||||
2. **Graceful degradation** - Failed embed updates don't crash the bot
|
||||
3. **Structured exceptions** - Custom exception types for different failure modes
|
||||
4. **Comprehensive logging** - DEBUG for operations, ERROR for failures
|
||||
5. **Automatic cleanup** - Missing embeds removed from tracking
|
||||
|
||||
## Deployment Architectures
|
||||
|
||||
### Traditional
|
||||
|
||||
```
|
||||
Python 3.8+ → pip install -r requirements.txt → python main.py
|
||||
```
|
||||
|
||||
### Docker
|
||||
|
||||
```
|
||||
Dockerfile → docker build → docker run -v config:/app
|
||||
```
|
||||
|
||||
### Kubernetes
|
||||
|
||||
```
|
||||
Deployment + Secret + PVC → kubectl apply
|
||||
```
|
||||
|
||||
## Related Pages
|
||||
|
||||
- [Features](/Features) - Detailed feature breakdown
|
||||
- [Installation](/Installation) - Deployment guides
|
||||
- [Configuration](/Configuration) - Config options
|
||||
- [Usage](/Usage) - Commands and workflows
|
||||
# Architecture
|
||||
|
||||
## Overview
|
||||
|
||||
Pterodisbot is a modular, asynchronous Discord bot built with Python 3.8+ using the `discord.py` library. It follows a clean architecture pattern with clear separation of concerns across multiple modules.
|
||||
|
||||
## System Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ Discord Client │
|
||||
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐ │
|
||||
│ │ Slash Cmds │ │ Buttons │ │ Dropdown Menus │ │
|
||||
│ └──────┬──────┘ └──────┬──────┘ └───────────┬─────────────┘ │
|
||||
└─────────┼────────────────┼──────────────────────┼───────────────┘
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ PterodactylBot (commands.Bot) │
|
||||
│ ┌───────────────────────────────────────────────────────────┐ │
|
||||
│ │ Background Task: update_status (10s interval) │ │
|
||||
│ │ • Smart update detection (state changes, CPU, uptime) │ │
|
||||
│ │ • Rate limit protection │ │
|
||||
│ └───────────────────────────────────────────────────────────┘ │
|
||||
└─────────────────────────────┬───────────────────────────────────┘
|
||||
│
|
||||
┌─────────────────────┼─────────────────────┐
|
||||
▼ ▼ ▼
|
||||
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
|
||||
│ EmbedManager │ │ PanelAPI │ │MetricsManager │
|
||||
│ │ │ │ │ │
|
||||
│ • Create │ │ • Client API │ │ • Data points │
|
||||
│ • Update │ │ • App API │ │ • Graphs │
|
||||
│ • Track │ │ • Auth │ │ • Trends │
|
||||
│ • Persist │ │ • Rate limit │ │ │
|
||||
└───────┬───────┘ └───────┬───────┘ └───────┬───────┘
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
|
||||
│ JSON Storage │ │ Pterodactyl/ │ │ Matplotlib │
|
||||
│ (embed_locs) │ │ Pelican │ │ (PNG graphs) │
|
||||
| | | Panel API | | |
|
||||
└───────────────┘ └───────────────┘ └───────────────┘
|
||||
```
|
||||
|
||||
## Module Structure
|
||||
|
||||
| Module | Responsibility | Key Classes |
|
||||
|--------|---------------|-------------|
|
||||
| `main.py` | Entry point, logging setup | `main()` |
|
||||
| `config.py` | Configuration loading/validation | `Config`, `load_config()` |
|
||||
| `bot.py` | Core bot, commands, background tasks | `PterodactylBot`, `setup_commands()` |
|
||||
| `api.py` | Pterodactyl/Pelican Panel API client | `PanelAPI` |
|
||||
| `embeds.py` | Embed creation, tracking, persistence | `EmbedManager` |
|
||||
| `views.py` | Discord UI components (buttons, dropdowns) | `ServerStatusView`, `ServerDropdown` |
|
||||
| `metrics.py` | Metrics collection & graph generation | `ServerMetricsManager`, `ServerMetricsGraphs` |
|
||||
| `models.py` | Pydantic data models | `ServerData`, `ServerResources`, etc. |
|
||||
| `exceptions.py` | Custom exception hierarchy | `APIError`, `ConfigValidationError`, etc. |
|
||||
| `logging_config.py` | Logging configuration | `setup_logging()` |
|
||||
| `generate_config.py` | Config generation from env vars | `generate_config_from_env()` |
|
||||
|
||||
## Data Flow
|
||||
|
||||
### 1. Bot Startup
|
||||
|
||||
```
|
||||
main() → load_config() → PterodactylBot() → setup_commands() → bot.start()
|
||||
↓
|
||||
setup_hook() → api.initialize()
|
||||
↓
|
||||
embed_manager.load_locations()
|
||||
↓
|
||||
update_status.start()
|
||||
```
|
||||
|
||||
### 2. Status Update Cycle (Every 10s)
|
||||
|
||||
```
|
||||
update_status() → api.get_servers() → For each tracked embed:
|
||||
→ api.get_server_resources()
|
||||
→ metrics_manager.add_server_data() (if running)
|
||||
→ Smart update detection:
|
||||
• Power state changed? → UPDATE
|
||||
• CPU change >50%? → UPDATE
|
||||
• First time seeing server? → UPDATE
|
||||
• Running >10min? → UPDATE (uptime accuracy)
|
||||
→ embed_manager.update_single_embed()
|
||||
→ metrics_manager.generate_combined_graph()
|
||||
→ Discord message.edit()
|
||||
```
|
||||
|
||||
### 3. User Interaction Flow
|
||||
|
||||
```
|
||||
/server_status command
|
||||
→ api.get_servers()
|
||||
→ Create stats embed + dropdown
|
||||
→ User selects server
|
||||
→ api.get_server_resources()
|
||||
→ Delete old embed (if exists)
|
||||
→ embed_manager.create_server_embed()
|
||||
→ Send new embed with ServerStatusView
|
||||
→ embed_manager.track_embed()
|
||||
|
||||
Button press (Start/Stop/Restart/Show Address)
|
||||
→ ServerStatusView.interaction_check() (guild + role)
|
||||
→ api.send_power_action() or api.get_server_default_allocation()
|
||||
→ Ephemeral response to user
|
||||
```
|
||||
|
||||
## Concurrency Model
|
||||
|
||||
- **Async/await** throughout for non-blocking I/O
|
||||
- **asyncio.Lock** in `PanelAPI._request()` for rate limit protection
|
||||
- **asyncio.Lock** in `PterodactylBot.update_status` for update cycle serialization
|
||||
- **deque(maxlen=6)** for automatic metrics rotation (1 min history)
|
||||
- **Background task** with `@tasks.loop(seconds=10)` for periodic updates
|
||||
|
||||
## Persistence
|
||||
|
||||
- **Embed locations**: JSON file (`embed/embed_locations.json`)
|
||||
- **Metrics**: In-memory only (last 6 data points = 1 minute)
|
||||
- **Configuration**: INI file (`config.ini`) or environment variables
|
||||
- **Logs**: Rotating files (5MB, 3 backups) in `logs/`
|
||||
|
||||
## Security Architecture
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────────────────────────┐
|
||||
│ Access Control Layers │
|
||||
├────────────────────────────────────────────────────────────┤
|
||||
│ 1. Guild Restriction (config.allowed_guild_id) │
|
||||
│ └─ Checked in: on_interaction, check_allowed_guild, │
|
||||
│ ServerStatusView.interaction_check │
|
||||
├────────────────────────────────────────────────────────────┤
|
||||
│ 2. Role-Based Access ("Game Server User" role) │
|
||||
│ └─ Checked in: ServerStatusView.interaction_check │
|
||||
├────────────────────────────────────────────────────────────┤
|
||||
│ 3. Admin Permissions (Discord Administrator) │
|
||||
│ └─ Required for: /refresh_embeds, /purge_embeds │
|
||||
└────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## API Integration
|
||||
|
||||
### Dual API Key System
|
||||
|
||||
- **Client API Key** (`ptlc_` / `pacc_`): Server operations (power, resources)
|
||||
- **Application API Key** (`ptla_` / `papp_`): Admin operations (list servers, details)
|
||||
|
||||
### Supported Panels
|
||||
|
||||
- **Pterodactyl Panel** (standard)
|
||||
- **Pelican Panel** (fork, uses `pacc_`/`papp_` prefixes)
|
||||
- Auto-detected via `Config.is_pelican_panel` property
|
||||
|
||||
## Error Handling Strategy
|
||||
|
||||
1. **Validation at startup** - Config validation fails fast
|
||||
2. **Graceful degradation** - Failed embed updates don't crash the bot
|
||||
3. **Structured exceptions** - Custom exception types for different failure modes
|
||||
4. **Comprehensive logging** - DEBUG for operations, ERROR for failures
|
||||
5. **Automatic cleanup** - Missing embeds removed from tracking
|
||||
|
||||
## Deployment Architectures
|
||||
|
||||
### Traditional
|
||||
|
||||
```
|
||||
Python 3.8+ → pip install -r requirements.txt → python main.py
|
||||
```
|
||||
|
||||
### Docker
|
||||
|
||||
```
|
||||
Dockerfile → docker build → docker run -v config:/app
|
||||
```
|
||||
|
||||
### Kubernetes
|
||||
|
||||
```
|
||||
Deployment + Secret + PVC → kubectl apply
|
||||
```
|
||||
|
||||
## Related Pages
|
||||
|
||||
- [Features](/Features) - Detailed feature breakdown
|
||||
- [Installation](/Installation) - Deployment guides
|
||||
- [Configuration](/Configuration) - Config options
|
||||
- [Usage](/Usage) - Commands and workflows
|
||||
- [Contributing](/Contributing) - Development guidelines
|
||||
Reference in new issue
Block a user