# Senator Sperma (Rust) A Discord bot rebuilt in Rust from the original Python implementation. Music playback via songbird, AI chat via OpenRouter or anything OpenAI compatible (ollama.cpp and such), TTS, web/file search scheduled jobs, per-server triggers, QR codes, media conversion, and misc utilities. Built on poise 0.6 + serenity 0.12 + songbird 0.6, with async SQLite (sqlx 0.9) for persistence. ## Setup ### 1. Clone ```bash git clone https://codeberg.org/salmonik/senator-sperma cd senator-sperma ``` ### 2. System Dependencies The following binaries must be available on `PATH`: - `ffmpeg` — audio decoding/encoding for playback, conversion, and compression - `qalc` (Qalculate) — used by `/math` to evaluate expressions - `yt-dlp` (optional) — used by `/download` - `piper` (optional) — local TTS for `/tts` and `/senator ... tts_vc`; falls back to Google Translate TTS if unavailable Unlike the Python version, QR encode/decode is handled by the pure-Rust `qrcode`/`rxing` crates — no `zbar` system library required. ### 3. Build Requires a recent Rust toolchain (edition 2024). ```bash cargo build --release ``` ### 4. Configure Environment Create a `.env` in the project root look at `.env.example` on what to put there On launch, missing variables are reported via tracing (`error!` for required, `warn!` for optional with defaults). The database and schema migrations are created automatically on first run. For local TTS, set `TTS_MODEL_PATH` to a Piper `.onnx` model file (or a directory of models, in which case `TTS_VOICE` selects the model, defaulting to `pl_PL-bass-high`). When `TTS_MODEL_PATH` is unset or Piper is unavailable, the bot falls back to Google Translate TTS. ### 5. Run ```bash cargo run --release ``` ## Docker The image builds and runs on Debian trixie, so it does not depend on the host's gcc/glibc version. This is the recommended way to deploy on Debian when developing on a rolling-release distro. ```bash docker compose up -d --build ``` Or without Compose: ```bash docker build -t senator-sperma . docker run -d --name senator-sperma --restart unless-stopped \ --env-file .env \ -e TTS_MODEL_PATH=/app/piper \ -v "$PWD/data:/app/data" \ -v "$PWD/piper:/app/piper:ro" \ senator-sperma ``` Notes: - `data/` holds the SQLite database and is mounted for persistence. Create it first (`mkdir -p data`) so it is owned by your user rather than root. - `piper/` holds the Piper model and is mounted read-only; the container overrides `TTS_MODEL_PATH` to `/app/piper`, so the absolute host path in `.env` is harmless. Put the `.onnx` and `.onnx.json` files there (the same directory used for local runs). - The container runs as uid 1000. Make sure `data/` is writable by that uid. - `TZ` defaults to `Europe/Warsaw` for cron jobs; override it in `compose.yaml` or with `-e TZ=...` if needed. - If Copyparty runs on the host, use `http://host.docker.internal:` in `.env` and enable the commented `extra_hosts` entry in `compose.yaml`. - If the build fails to resolve `crates.io` or `deb.debian.org`, run `docker build --network=host -t senator-sperma .`; `compose.yaml` already sets `network: host` for builds on this host. - yt-dlp is fetched from the latest upstream release at image build time. Rebuild with `--no-cache` (`docker compose build --no-cache`) to refresh it. ## Commands Slash commands are autoregistered globally on startup. Some are registered under their original Polish verb names to match the UI expected by existing servers. ### AI - `/senator` — chat with the OpenRouter model ### Music (songbird) - `/music grajcuj` — play from Copyparty search, a direct URL, or an uploaded file; optional `position` (`now`, `next`, `last`, default `last`) - `/music queue` — show the current track followed by the pending queue - `/music skip` — stop the current track and play the next queued one - `/music stop` — stop playback, clear the queue, and leave voice - `/music join` — join the caller's voice channel ### TTS - `/tts` — local Piper TTS (Polish, Google Translate fallback), sent as a file or played in voice ### Search & Web - `/web` — DuckDuckGo HTML scrape (soon to use 4get) - `/search` — search Copyparty and return a direct download URL - `/download` — fetch media from any yt-dlp-supported URL ### Media Tools - `/compress` (alias `kompresja`) — compress a media attachment to low quality - `/convert` (alias `konwersja`) — convert media between formats ### Utilities - `/upload` — push Discord attachments into Copyparty - `/say` — repeat the given text as the bot - `/math` — evaluate an ASCII math expression through `qalc` - `/qr generowanie` / `/qr dekodowanie` — QR encode / decode ### Triggers - `/trigger create` — new trigger group - `/trigger add` — add keywords/responses to a group - `/trigger remove` — remove a keyword/response - `/trigger delete` — delete a group - `/trigger list` — list a group's keywords and responses - `/trigger help` — usage info ### Cron - `/cron dodaj_audio` — schedule audio playback - `/cron dodaj_wiadomość` — schedule a periodic message - `/cron pokaż` — list all cron jobs - `/cron usuń` — remove a cron job - `/cron pauza` — pause a job - `/cron wznów` — resume a job ### Message Context Menu - "Wyślij załączniki" (right-click message) — upload attachments to Copyparty - "kompresja" (right-click message) — compress all attachments ## Architecture The runtime is organised around a shared `Data` struct (poise `user_data`) that holds the DB pool, HTTP client, Copyparty client, voice manager, the in-memory trigger cache, autocomplete cache, and per-guild music queue. - `src/main.rs` — boot: tracing, dotenvy, Config, DB pool, framework, cron task - `src/config.rs` — env-driven `Config` + variable validation - `src/commands/` — one module per command group - `src/copyparty/` — Copyparty search/upload client - `src/openrouter/` — OpenRouter chat client - `src/database/` — sqlx pool, migrations, query helpers (6 tables) - `src/voice/` — `VoiceManager`, mp3 → songbird input - `src/events/` — `handle_ready`, voice-state, message (trigger matching) - `src/tasks/` — 30s cron scheduler poll loop - `src/lang.rs` — user-facing Polish strings All temporary files go through `crate::config::TEMP_PATH` (`/tmp/senator_sperma`).