Update Architecture

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