Recipe
Stream console output
The live console in the panel is a WebSocket you can open from your own code too: every line your bot prints, its status and its resource use, as they happen.
← API docsUpdated
Browse the docs
How it works
Ask the API for a token with GET /servers/{server}/websocket, which needs the websocket.connect permission. It returns a short-lived token and the address of the node's socket. Open the socket with an Origin: https://sillydev.co.uk header, send {"event": "auth", "args": ["<token>"]}, and wait for auth success.
From then on every message is JSON with an event and a list of args. console output carries one line; status says the server is starting, running, stopping or offline; stats carries resource use. Send send logs to get recent history first, and send command to type into the console.
The token lasts a few minutes. Shortly before it runs out you get token expiring: fetch a new token from the API and send auth again on the same socket.
Python
With the websockets and requests packages.
console.py
import asyncio
import json
import os
import requests
import websockets
API = "https://sillydev.co.uk/panel/api/client"
SERVER = "1a7ce997"
HEADERS = {"Authorization": f"Bearer {os.environ['SILLYDEV_API_KEY']}", "Accept": "application/json"}
def credentials() -> dict:
response = requests.get(f"{API}/servers/{SERVER}/websocket", headers=HEADERS, timeout=10)
response.raise_for_status()
return response.json()["data"]
async def main() -> None:
first = credentials()
async with websockets.connect(first["socket"], origin="https://sillydev.co.uk") as socket:
await socket.send(json.dumps({"event": "auth", "args": [first["token"]]}))
async for raw in socket:
message = json.loads(raw)
event, args = message["event"], message.get("args") or []
if event == "auth success":
await socket.send(json.dumps({"event": "send logs"}))
elif event == "console output":
print(args[0])
elif event == "status":
print(f"[status: {args[0]}]")
elif event == "token expiring":
fresh = credentials()
await socket.send(json.dumps({"event": "auth", "args": [fresh["token"]]}))
asyncio.run(main())JavaScript
With the ws package in Node.js, which can set the Origin header. Bun and Deno have WebSocket built in.
console.mjs
import WebSocket from 'ws';
const API = 'https://sillydev.co.uk/panel/api/client';
const SERVER = '1a7ce997';
const headers = { Authorization: `Bearer ${process.env.SILLYDEV_API_KEY}`, Accept: 'application/json' };
const credentials = async () => {
const response = await fetch(`${API}/servers/${SERVER}/websocket`, { headers });
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return (await response.json()).data;
};
const { socket: url, token } = await credentials();
const socket = new WebSocket(url, { origin: 'https://sillydev.co.uk' });
const send = (event, args = []) => socket.send(JSON.stringify({ event, args }));
socket.on('open', () => send('auth', [token]));
socket.on('message', async (raw) => {
const { event, args = [] } = JSON.parse(raw.toString());
if (event === 'auth success') send('send logs');
if (event === 'console output') console.log(args[0]);
if (event === 'status') console.log(`[status: ${args[0]}]`);
if (event === 'token expiring') send('auth', [(await credentials()).token]);
});FAQ
Questions
Can I send console commands this way?
Yes, with the control.console permission: send the event "send command" with the command as its only argument. For a single command, POST /servers/{server}/command is simpler.
Read next