Update Architecture
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
|
||||||
Reference in new issue
Block a user