aboutsummaryrefslogtreecommitdiffstats

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

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
  • imagemagick (magick) — image compression and conversion for /compress and /convert
  • qalc (Qalculate) — used by /math to evaluate expressions
  • yt-dlp (optional) — used by /download
  • piper model files (optional) — local TTS for /tts and /senator ... tts_vc; TTS commands fail when unset

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).

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). There is no external TTS fallback: TTS commands fail when the model is missing.

5. Run

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.

docker compose up -d --build

Or without Compose:

docker buildx build --load -t senator-sperma:latest .
docker run -d --name senator-sperma --restart unless-stopped \
    --env-file .env \
    -e TTS_MODEL_PATH=/app/piper \
    -e PIPER_ESPEAKNG_DATA_DIRECTORY=/usr/share \
    -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 espeak-ng data used by Piper is baked into the image at /usr/share/espeak-ng-data; the container sets PIPER_ESPEAKNG_DATA_DIRECTORY=/usr/share. This overrides the host path in .env.
  • 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 or a local LLM runs on the host, use the host's LAN IP (for example http://10.0.0.3:2137/...) and make sure the service listens on 0.0.0.0 or that LAN IP, not only 127.0.0.1. localhost inside the container is the container itself.
  • After editing .env, recreate the container: docker compose up -d --force-recreate. docker compose restart does not reload env_file values. Verify what the container actually sees with: docker compose exec senator-sperma env | grep -E 'LLM_API_URL|TTS_MODEL_PATH|PIPER_ESPEAKNG' and check the resolved LLM URL with docker compose logs | grep 'LLM client'.
  • To test the LLM endpoint from inside the container: docker compose exec senator-sperma python3 -c 'import urllib.request; print(urllib.request.urlopen("http://10.0.0.3:2137/v1/models", timeout=5).status)' If that fails, the host service is probably listening only on 127.0.0.1; bind it to 0.0.0.0 (or the LAN IP) and allow the port through the firewall.
  • If the build fails to resolve crates.io or deb.debian.org, run docker buildx build --load --network=host -t senator-sperma:latest .; compose.yaml already sets network: host for builds on this host.
  • yt-dlp is pinned to a specific upstream release and verified against the release checksum at image build time. Bump YTDLP_VERSION in the Dockerfile to update it; pick a release that is at least a week old.

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), 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
  • /record — upload the last recording window (requires MANAGE_GUILD)
  • /leaderboard — me, messages, active, afk, total

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
  • "Pobierz z linku" (right-click message) — download every URL in the message

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, ffmpeg/symphonia → songbird input
  • src/net.rs — blocks private/link-local hosts for user-supplied URLs
  • src/recording/ — per-guild rolling PCM buffer for /record
  • src/events/ — handle_ready, voice-state, message (trigger matching)
  • src/tasks/ — 60s cron scheduler poll loop
  • src/lang.rs — user-facing Polish strings

All temporary files go through crate::config::temp_path() ($TMPDIR/senator_sperma, created with mode 0700 at startup).