Files
groovy-zilean/web_dhasboard_plan.md

8.6 KiB

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)

# 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)

# 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

# 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)

// 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 (
    <div>
      <h1>Now Playing: {queue[0]?.title}</h1>
      <button onClick={skipSong}>Skip</button>
      
      <ul>
        {queue.map((song, i) => (
          <li key={i}>{song.title}</li>
        ))}
      </ul>
    </div>
  );
}

Authentication (Important!)

Problem: Anyone with the URL can control your bot.

Solutions:

  1. Discord OAuth2 (Recommended)

    • Users log in with Discord
    • Check if user is in the server
    • Only show servers they're members of
  2. API Keys

    • Generate unique key per server
    • Server admins share key with trusted users
  3. IP Whitelist

    • Only allow specific IPs to access

Deployment

Development

# Backend
cd backend
uvicorn main:app --reload --port 8000

# Frontend
cd frontend
npm run dev

Production

# Backend (systemd service)
uvicorn main:app --host 0.0.0.0 --port 8000

# Frontend (build static files)
npm run build
# Serve with nginx or deploy to Vercel/Netlify

File Structure

astro-bot/
├── bot.py                 # Discord bot
├── cogs/
│   └── music/
│       ├── main.py
│       ├── queue.py
│       └── util.py
├── web/
│   ├── backend/
│   │   ├── main.py       # FastAPI app
│   │   ├── routes/
│   │   │   ├── servers.py
│   │   │   └── playback.py
│   │   └── websockets.py
│   └── frontend/
│       ├── src/
│       │   ├── components/
│       │   │   ├── Queue.jsx
│       │   │   ├── Controls.jsx
│       │   │   └── NowPlaying.jsx
│       │   ├── App.jsx
│       │   └── main.jsx
│       └── package.json
└── data/
    └── music.db

Next Steps

  1. Start with database-based approach - Get it working first
  2. Add WebSocket for real-time - Once basic functionality works
  3. Build simple UI - Focus on core features (play, queue, skip)
  4. Add authentication - Discord OAuth2
  5. Polish and deploy - Make it production-ready