# Astro Bot Web Dashboard Architecture ## Overview A real-time web dashboard for controlling the Discord music bot from a browser. Users can manage queues, adjust settings, and control playback without Discord. ## Tech Stack ### Backend (Flask/FastAPI) **Recommended: FastAPI** (modern, async, WebSocket support built-in) ```python # Dependencies fastapi==0.104.1 uvicorn[standard]==0.24.0 websockets==12.0 python-socketio==5.10.0 # For real-time updates aioredis==2.0.1 # For session management ``` **Why FastAPI:** - Native async support (works well with discord.py) - Built-in WebSocket support - Auto-generated API docs - Fast performance ### Frontend **Recommended: React + Tailwind CSS** ``` React 18 Tailwind CSS Socket.IO client (for real-time) Axios (for API calls) ``` **Alternative (simpler):** Vanilla JS + Tailwind if you want less complexity --- ## Architecture Diagram ``` ┌─────────────┐ ┌──────────────┐ ┌─────────────┐ │ Browser │◄───────►│ FastAPI │◄───────►│ Discord Bot │ │ (React UI) │ HTTP/WS │ Backend │ IPC │ (Python) │ └─────────────┘ └──────────────┘ └─────────────┘ │ ▼ ┌──────────────┐ │ Database │ │ (SQLite) │ └──────────────┘ ``` --- ## Communication Flow ### 1. Bot → Web (Status Updates) Discord bot sends real-time updates to web backend via: - **Shared Database** (simplest) - Bot writes to DB, web reads - **Redis Pub/Sub** (better) - Bot publishes events, web subscribes - **WebSocket/Socket.IO** (best) - Direct real-time connection ### 2. Web → Bot (Commands) Web backend sends commands to bot via: - **Database flags** (simplest) - Web writes commands, bot polls - **Redis Queue** (better) - Web publishes, bot consumes - **Direct IPC** (best) - Web calls bot functions directly --- ## Detailed Implementation ### Phase 1: Database-Based (Easiest Start) **How it works:** 1. Bot writes current state to database 2. Web reads database and displays 3. Web writes commands to "commands" table 4. Bot polls table every second **Pros:** - Simple to implement - No new dependencies - Works immediately **Cons:** - Not truly real-time (polling delay) - Database writes on every update ### Phase 2: Redis-Based (Production Ready) **How it works:** 1. Bot publishes events to Redis: `PUBLISH bot:status {"song": "...", "queue": [...]}` 2. Web subscribes to Redis channel 3. Web publishes commands: `PUBLISH bot:commands {"action": "skip"}` 4. Bot subscribes and executes **Pros:** - True real-time - Fast and efficient - Decoupled architecture **Cons:** - Requires Redis server - More complex setup --- ## API Endpoints ### GET Endpoints (Read) ``` GET /api/servers # List all servers bot is in GET /api/servers/{id}/queue # Get current queue GET /api/servers/{id}/status # Get playback status GET /api/servers/{id}/settings # Get volume/loop/effect ``` ### POST Endpoints (Write) ``` POST /api/servers/{id}/play # Add song to queue POST /api/servers/{id}/skip # Skip current song POST /api/servers/{id}/volume # Set volume POST /api/servers/{id}/loop # Set loop mode POST /api/servers/{id}/effect # Set audio effect POST /api/servers/{id}/shuffle # Shuffle queue POST /api/servers/{id}/clear # Clear queue ``` ### WebSocket Events ``` ws://localhost:8000/ws/{server_id} # Bot → Web {"event": "song_changed", "data": {...}} {"event": "queue_updated", "data": [...]} {"event": "volume_changed", "data": 150} # Web → Bot {"action": "skip"} {"action": "volume", "value": 120} ``` --- ## Example Code Structure ### Backend (FastAPI) ```python # main.py from fastapi import FastAPI, WebSocket from fastapi.middleware.cors import CORSMiddleware import asyncio import sqlite3 app = FastAPI() # Allow frontend to connect app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"], ) # WebSocket connection for real-time updates @app.websocket("/ws/{server_id}") async def websocket_endpoint(websocket: WebSocket, server_id: str): await websocket.accept() # Send updates every second while True: # Read from database queue_data = get_queue_from_db(server_id) await websocket.send_json(queue_data) await asyncio.sleep(1) # API endpoint to skip song @app.post("/api/servers/{server_id}/skip") async def skip_song(server_id: str): # Write command to database conn = sqlite3.connect("./data/music.db") cursor = conn.cursor() cursor.execute("INSERT INTO commands (server_id, action) VALUES (?, ?)", (server_id, "skip")) conn.commit() conn.close() return {"status": "ok"} ``` ### Bot Integration ```python # In your bot code, add command polling @tasks.loop(seconds=1) async def process_web_commands(): """Check for commands from web dashboard""" conn = sqlite3.connect("./data/music.db") cursor = conn.cursor() cursor.execute("SELECT * FROM commands WHERE processed = 0") commands = cursor.fetchall() for cmd in commands: server_id, action, data = cmd[1], cmd[2], cmd[3] # Execute command if action == "skip": guild = bot.get_guild(int(server_id)) if guild and guild.voice_client: guild.voice_client.stop() # Mark as processed cursor.execute("UPDATE commands SET processed = 1 WHERE id = ?", (cmd[0],)) conn.commit() conn.close() ``` ### Frontend (React) ```javascript // Dashboard.jsx import { useEffect, useState } from 'react'; function Dashboard({ serverId }) { const [queue, setQueue] = useState([]); const [ws, setWs] = useState(null); useEffect(() => { // Connect to WebSocket const websocket = new WebSocket(`ws://localhost:8000/ws/${serverId}`); websocket.onmessage = (event) => { const data = JSON.parse(event.data); setQueue(data.queue); }; setWs(websocket); return () => websocket.close(); }, [serverId]); const skipSong = async () => { await fetch(`http://localhost:8000/api/servers/${serverId}/skip`, { method: 'POST', }); }; return (