Introduction
π‘ Idea
Performance Bot was created when I needed a simple way to monitor my home server from outside my home, without using complicated commands.
At first, I considered building a web page, but I wanted to try something new. Thatβs when I came up with the idea of building a bot.
π Summary
To start my project, I needed to gather all the essential information from my server:
/systemβ System info/cpuβ CPU info/memoryβ Memory info/diskβ Disk info/networkβ Network info/dockerβ Docker info/sensorsβ Sensors info
After some research, I found two very useful Python libraries:
psutilfor gathering system infodocker, the Docker SDK for getting Docker info (images and containers)
βοΈ Implementation
With those libraries in mind, I started a WebSocket server using the FastAPI framework and began serving it on my localhost at port 8000.
Once I had the WebSocket server working, I started researching how to create a Telegram bot. I discovered a Telegram bot called TheBotFather, which lets you create and customize bot profiles and provides you with a token to work with.
After the bot was ready, I worked on the Dockerfile to make deployment easier, whether in the cloud or if the bot and backend are running on different servers. While working on the Dockerfile, I found that GitHub can help you create an image and push that image to Docker Hub automatically when you push to the GitHub repo.
π Documentation
Finally, I found mdBook, which helps me deploy documentation for the repo using Markdown files, making everything much easier.
π Documentation
Welcome to the documentation section. Here you'll find detailed guides and references for both the WebSocket server and the Telegram bot components of this project.
- WebSocket Documentation β Learn how the WebSocket server works, how to set it up, and how to extend its functionality.
- Telegram Bot Documentation β Instructions and examples for configuring and using the Telegram bot.
WebSocket
Welcome to the WebSocket server documentation. Here you'll find everything you need to understand, set up, and extend the WebSocket functionality in this project.
- WebSocket Imports β Overview of required libraries and modules.
- Create WebSocket β Step-by-step guide and example code for building the WebSocket server.
π¦WebSocket Imports
This section documents all the imports used in backend/main.py and their purposes.
Standard Library Imports
-
datetime
For handling dates and times, such as timestamps and formatting. -
platform
To retrieve information about the underlying platform (OS, node, release, etc.). -
time
For time-related functions, such as calculating uptime. -
typing (Dict, Set)
For type hinting dictionaries and sets. -
contextlib (asynccontextmanager)
To manage the application lifespan with asynchronous context managers. -
asyncio
For asynchronous programming, including tasks, locks, and sleep. -
json
For encoding and decoding JSON data.
Third-Party Imports
-
fastapi
FastAPI: The main FastAPI application class.WebSocket: For handling WebSocket connections.
-
fastapi.middleware.cors (CORSMiddleware)
To enable Cross-Origin Resource Sharing (CORS) for the API. -
psutil
For retrieving system information such as CPU, memory, disk, sensors, and network stats. -
docker
For interacting with Docker, including listing images and containers.
Summary Table
| Module | Purpose |
|---|---|
| datetime | Date and time handling |
| platform | System/platform information |
| time | Time calculations |
| typing | Type hints for Dict and Set |
| contextlib | Async context management |
| fastapi | API and WebSocket framework |
| fastapi.middleware.cors | CORS middleware for FastAPI |
| psutil | System resource monitoring |
| asyncio | Async programming primitives |
| json | JSON serialization/deserialization |
| docker | Docker API interaction |
Create WebSocket
This guide shows how to build a simple WebSocket server using FastAPI.
Clients can connect to the /ws endpoint and receive a JSON message with CPU, memory, and Docker container information.
You can use this as a starting point for more advanced real-time monitoring features.
Step 1: Basic FastAPI Setup
Start by creating a FastAPI app and enabling CORS for local frontend development.
from fastapi import FastAPI, WebSocket
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI(title="Performance Dashboard API")
app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:3000"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
pass # We'll implement this in the next steps
@app.get("/")
def read_root():
return {"message": "Performance Dashboard API is running."}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000, reload=True)
Step 2: Accept WebSocket Connections
Now, accept WebSocket connections and send a simple message.
# ... previous setup ...
@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
await websocket.accept()
await websocket.send_text("WebSocket connection established!")
await websocket.close()
# ... previous setup ...
Step 3: Send System and Docker Stats
Finally, gather system and Docker stats and send them as JSON.
import psutil
import docker
import json
# ... previous setup ...
docker_client = docker.from_env()
# WebSocket endpoint
@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
await websocket.accept()
docker_containers = []
for idx, container in enumerate(docker_client.containers.list(all=True)):
try:
container_data = {
"id": container.id[:12] if container.id else "unknown",
"name": container.name if container.name else "unknown",
"status": container.status if container.status else "unknown",
"image": container.image.tags[0] if container.image.tags else (container.image.id[:12] if container.image.id else "unknown"),
"created": container.attrs.get('Created', '')[:19] if container.attrs.get('Created') else 'unknown'
}
docker_containers.append({f"container{idx}": container_data})
except Exception as e:
print(f"Error processing container: {e}")
continue
latest_data = {
"cpu": {
"cores_count": psutil.cpu_count(logical=False),
"threads_count": psutil.cpu_count(),
"cpu": round(psutil.cpu_percent(), 1),
"frequency": round(psutil.cpu_freq().current, 1),
},
"memory": {
"ram_total": round(psutil.virtual_memory().total / (1024**3), 2),
"ram_available": round(psutil.virtual_memory().available / (1024**3), 2),
"ram_percent": round(psutil.virtual_memory().percent, 1),
},
"docker": {
"containers": docker_containers
}
}
await websocket.send_text(json.dumps(latest_data))
await websocket.close()
# ... previous setup ...
How to Run the Backend
Follow these steps to start the FastAPI backend server:
# Install dependencies
pip install -r requirements.txt
# Change directory to the backend
cd backend
# Run the backend
uvicorn main:app --reload
WebSocket Output
{
"cpu": {
"cores_count": 4,
"threads_count": 8,
"cpu": 4.5,
"frequency": 2611.2
},
"memory": {
"ram_total": 7.61,
"ram_available": 4.94,
"ram_percent": 35.1
},
"docker": {
"containers": [
{
"container0": {
"id": "970b24a9730e",
"name": "bot2",
"status": "exited",
"image": "performance-bot:latest",
"created": "2025-11-26T14:18:18"
}
},
{
"container1": {
"id": "900d303cb952",
"name": "bot",
"status": "exited",
"image": "snr1s3/performance-bot:latest",
"created": "2025-11-26T10:53:37"
}
}
]
}
}
Telegram Bot Documentation
Welcome to the Telegram bot documentation. Here you'll find everything you need to understand, set up, and extend the bot functionality in this project.
- Bot Imports β Overview of required libraries and modules.
- Create Bot β Step-by-step guide and example code for building and running the Telegram bot.
Telegram Bot Imports
Below is an overview of the main imports used in the Telegram bot component, along with their purposes:
Standard Library Imports
- os
Used for accessing environment variables, such as the Telegram bot token.
Third-Party Imports
-
telegram
Provides classes and methods for interacting with the Telegram Bot API. -
telegram.ext
Supplies higher-level tools for building bots, such asApplication,CommandHandler, andContextTypes.
Summary Table
| Module | Purpose |
|---|---|
| os | Access environment variables (e.g., bot token) |
| telegram | Telegram Bot API classes and methods | | telegram.ext | Bot framework utilities and handlers |
Create Bot
This guide will help you set up and run the Telegram bot for the Performance Dashboard project.
Step 1: Create Your Bot with BotFather
- Open Telegram and search for @BotFather.
- Start a chat and send
/newbot. - Follow the instructions to set your botβs name and username.
- Copy the token provided by BotFather β youβll need it for the next step.
Step 2: Set Up the Project
Try to connect to the bot creating bot.py
from telegram.ext import ApplicationBuilder
from config import BOT_TOKEN
from handlers import setup_handlers
def main() -> None:
app = ApplicationBuilder().token(BOT_TOKEN).build()
setup_handlers(app)
app.run_polling()
if __name__ == "__main__":
main()
Step 3: Add Command Handlers
Example for a /cpu command handler:
from telegram.ext import CommandHandler, ContextTypes
# ... previous setup ...
def setup_handlers(app):
app.add_handler(CommandHandler("cpu", cpu_Handler))
async def cpu_Handler(update, context: ContextTypes.DEFAULT_TYPE):
content = CpuInfo(SocketCon()).fetch()
msg = "<b>CPU INFO:</b>\n" + "\n".join(content)
await update.message.reply_text(msg, parse_mode="HTML")
Step 4: Create the CPU Handler
Create cpuInfo.py and infoBase.py:
# cpuInfo.py
from telegram import Update
from telegram.ext import ContextTypes
from .info_base import InfoBase
class CpuInfo(InfoBase):
def fetch(self):
def formatter(info):
arr = []
for key, value in info.items():
key_upper = key.upper()
if key_upper == "CPU":
value = str(value) + " %"
elif key_upper == "FREQUENCY":
value = str(value) + " GHz"
if key_upper not in ("CPU_CORE", "FREQUENCY_CORE"):
arr.append(f"{key_upper} : {value}")
return arr
return self.get_info("cpu", formatter)
# infoBase.py
import json
class InfoBase:
def __init__(self, socket_con):
self.socket_con = socket_con
def get_info(self, section: str, formatter: callable) -> list:
content = self.socket_con.receive()
arr = []
try:
data = json.loads(content)
if section:
info = data.get(section, {})
arr = formatter(info)
else:
return data
except Exception as e:
print("Error parsing JSON:", e)
print("Raw content:", content)
return arr
Step 5: Connect to the Backend via WebSocket Create socket_con.py:
import os
import websocket
import json
class SocketCon:
_instance = None
_ws = None
def __new__(cls):
if cls._instance is None:
cls._instance = super(SocketCon, cls).__new__(cls)
return cls._instance
def __init__(self):
self.ws = None
self.connected = False
def connect(self, url=os.getenv("WEB_SOCKET_URL", "")):
if self.ws is None:
self.ws = websocket.WebSocket()
try:
self.ws.connect(url)
self.connected = True
except Exception as e:
print(f"Could not connect to {url}: {e}")
self.ws = None
self.connected = False
def receive(self):
if self.ws and self.connected:
try:
return self.ws.recv()
except Exception as e:
print(f"WebSocket receive error: {e}")
self.ws = None
self.connected = False
print("Falling back to jsons/TEST.json")
try:
with open("jsons/TEST.json", "r") as f:
return json.dumps(json.load(f))
except Exception as e:
print(f"Error reading jsons/TEST.json: {e}")
return '{}'
How to Run the Bot
Follow these steps to start the Telegram bot:
# Install dependencies
pip install -r requirements.txt
# Change directory to the backend
cd bot
# Run the backend
python bot.py
Bot Output
When you send /cpu to the bot, you might see:
User BOT
--------> /cpu
<-------
CPU INFO:
CORES_COUNT: 4
THREAD_COUNT: 8
CPU: 4.5%
FREQUENCY: 2611.2 MHz