fixed queue skip error, implemented more /commands.
This commit is contained in:
353
web_dhasboard_plan.md
Normal file
353
web_dhasboard_plan.md
Normal file
@@ -0,0 +1,353 @@
|
||||
# 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 (
|
||||
<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
|
||||
```bash
|
||||
# Backend
|
||||
cd backend
|
||||
uvicorn main:app --reload --port 8000
|
||||
|
||||
# Frontend
|
||||
cd frontend
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### Production
|
||||
```bash
|
||||
# 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
|
||||
Reference in New Issue
Block a user