Skip to content

Instantly share code, notes, and snippets.

@Stankye
Created July 7, 2025 01:22
Show Gist options
  • Save Stankye/afb77591abaffd8f9367c73216c88e1b to your computer and use it in GitHub Desktop.
Save Stankye/afb77591abaffd8f9367c73216c88e1b to your computer and use it in GitHub Desktop.
ChatGPT Genned
///////////////////////
// SERVER (Axum API) //
///////////////////////
// File: server/src/main.rs
use axum::{extract::State, http::StatusCode, routing::post, Router};
use qbittorrent_rust::{Credentials, QbitApi};
use std::{net::SocketAddr, sync::Arc};
use tokio::sync::Mutex;
use tracing::{error, info};
use tracing_subscriber;
#[tokio::main]
async fn main() {
tracing_subscriber::fmt::init();
let creds = Credentials::new("admin", "adminadmin");
let api = match QbitApi::new("http://localhost:8080", creds).await {
Ok(api) => Arc::new(Mutex::new(api)),
Err(e) => {
error!(?e, "Failed to initialize qBittorrent API");
std::process::exit(1);
}
};
let app = Router::new()
.route("/control", post(control_handler))
.with_state(api);
let addr = SocketAddr::from(([0, 0, 0, 0], 5000));
info!("Listening on {}", addr);
axum::Server::bind(&addr)
.serve(app.into_make_service())
.await
.unwrap();
}
async fn control_handler(
State(api): State<Arc<Mutex<QbitApi>>>,
body: String,
) -> StatusCode {
let mut api = api.lock().await;
match body.as_str() {
"restrict" => {
if api.transfer().set_global_upload_limit(50 * 1024).await.is_ok()
&& api.transfer().set_global_download_limit(100 * 1024).await.is_ok()
{
StatusCode::NO_CONTENT
} else {
error!("Failed to restrict bandwidth");
StatusCode::INTERNAL_SERVER_ERROR
}
}
"unrestrict" => {
if api.transfer().set_global_upload_limit(0).await.is_ok()
&& api.transfer().set_global_download_limit(0).await.is_ok()
{
StatusCode::NO_CONTENT
} else {
error!("Failed to unrestrict bandwidth");
StatusCode::INTERNAL_SERVER_ERROR
}
}
_ => StatusCode::BAD_REQUEST,
}
}
///////////////////////
// Dockerfile (server)
///////////////////////
# File: server/Dockerfile
FROM rust:1.77 as builder
WORKDIR /usr/src/app
COPY . .
RUN cargo build --release
FROM debian:buster-slim
WORKDIR /app
COPY --from=builder /usr/src/app/target/release/server /app/server
CMD ["/app/server"]
//////////////////////////
// Client (Windows Rust) //
//////////////////////////
// File: client/src/main.rs
use reqwest::blocking::Client;
use std::{thread, time::Duration};
use sysinfo::{ProcessExt, System, SystemExt};
use tracing::{info, error};
use tracing_subscriber;
const GAMES: &[&str] = &["eldenring.exe", "witcher3.exe", "cs2.exe"];
const SERVER_URL: &str = "http://192.168.1.100:5000/control";
const POLL_INTERVAL: Duration = Duration::from_secs(10);
fn is_game_running(sys: &System) -> bool {
for process in sys.processes().values() {
let name = process.name().to_lowercase();
if GAMES.iter().any(|g| name.contains(g)) {
return true;
}
}
false
}
fn main() {
tracing_subscriber::fmt().init();
let client = Client::new();
let mut sys = System::new_all();
let mut game_was_running = false;
loop {
sys.refresh_processes();
let game_running = is_game_running(&sys);
if game_running && !game_was_running {
info!("Game detected, sending restrict");
if client.post(SERVER_URL).body("restrict").send().is_err() {
error!("Failed to send restrict");
}
game_was_running = true;
} else if !game_running && game_was_running {
info!("No game running, sending unrestrict");
if client.post(SERVER_URL).body("unrestrict").send().is_err() {
error!("Failed to send unrestrict");
}
game_was_running = false;
}
thread::sleep(POLL_INTERVAL);
}
}
/////////////////////
// Cargo.toml Guide
/////////////////////
# File: Cargo.toml (shared for both)
[workspace]
members = ["server", "client"]
# In server/Cargo.toml:
[package]
name = "server"
version = "0.1.0"
edition = "2021"
[dependencies]
axum = "0.7"
tokio = { version = "1", features = ["full"] }
qbittorrent-rust = "0.1.3"
tracing = "0.1"
tracing-subscriber = "0.3"
# In client/Cargo.toml:
[package]
name = "client"
version = "0.1.0"
edition = "2021"
[dependencies]
reqwest = { version = "0.12", features = ["blocking"] }
sysinfo = "0.30"
tracing = "0.1"
tracing-subscriber = "0.3"
/////////////////////
// .cargo/config.toml
/////////////////////
# Optimize client for small binary size
# File: client/.cargo/config.toml
[profile.release]
opt-level = "z"
strip = true
lto = true
codegen-units = 1
panic = "abort"
[target.'cfg(windows)']
rustflags = ["-C", "target-feature=+crt-static"]
/////////////////////////
// Background Setup (Win)
/////////////////////////
// Use NSSM (Non-Sucking Service Manager) or Windows Task Scheduler:
// nssm install GameQoSDetector "C:\path\to\client.exe"
// Or run via Task Scheduler with "Run whether user is logged in or not"
// and trigger at logon or on startup.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment