aboutsummaryrefslogtreecommitdiffstats
path: root/AGENTS.md
blob: 760e7903b7563924648aa1e9ad8c883ec50b32d3 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# senator-sperma-rust — Agent Guide

## Project Map

```
src/
├── main.rs              — Entry: tracing, dotenvy, temp dir, Config/Data, poise+serenity
├── config.rs            — Config struct (env-driven), temp dir init/access
├── error.rs             — Unified Error enum + From impls, pub type Result<T>
├── net.rs               — public-IP check for user-supplied URLs (SSRF guard)
├── music.rs             — MusicState: per-guild queue, now/next/last, skip, auto-advance
├── recording/mod.rs     — per-guild rolling PCM recorder for /record
├── tts.rs               — TtsState (piper-rs in-process, bounded DashMap cache, resampler, WAV encoder)
├── commands/
│   ├── mod.rs           — all_commands() aggregates every submodule
│   ├── ai.rs            — /senator (LLM chat + optional TTS)
│   ├── autocomplete.rs  — Copyparty autocomplete + trigger_name
│   ├── cron.rs          — /cron group (6 subcommands)
│   ├── leaderboard.rs   — /leaderboard group (me/messages/active/afk/total)
│   ├── math.rs          — /math (qalc subprocess, 5s timeout)
│   ├── music.rs         — /music group (grajcuj, skip, queue, stop, join) + context menu
│   ├── qr.rs            — /qr encode + decode (dimension/allocation limits)
│   ├── record.rs        — /record (flush this guild's buffer to Copyparty)
│   ├── trigger.rs       — /trigger group (6 subcommands)
│   ├── tts.rs           — /tts (local Piper TTS → file/vc)
│   ├── utilities.rs     — compress, convert, upload, search, download, say
│   └── web.rs           — /web (DuckDuckGo scrape)
├── copyparty/mod.rs     — CopypartyClient (search, upload, credential-safe URLs)
├── database/mod.rs      — DbPool, migrations, query helpers (7 tables)
├── events/mod.rs        — handle_ready, voice-state, message (triggers + LLM)
├── openrouter/mod.rs    — OpenRouterClient (chat, chat_with_history, timeouts)
├── tasks/mod.rs         — cron_scheduler_loop (60s polling, real execution)
└── voice/mod.rs         — VoiceManager (connect, disconnect, get_call), ffmpeg/symphonia inputs
```

## Key Conventions

- **Framework**: poise 0.6 + serenity 0.12 + songbird 0.6
- **DB**: sqlx 0.9 async SQLite (pool stored in `Data.db_pool`)
- **Errors**: `crate::error::Error` enum with `?` propagation; use `Error::Custom(string)` for ad-hoc
- **Shared state**: `Data` struct in `main.rs` — passed as poise `user_data`
- **Commands**: each submodule exports `pub fn commands() -> Vec<poise::Command<Data, Error>>`
- **Permissions**: admin commands use `required_permissions`; recording/cron/trigger are `guild_only` and scoped by `guild_id` in the DB
- **Temp path**: always use `crate::config::temp_path()` (initialized with mode `0700`; never hardcode `/tmp/senator_sperma`)
- **Outbound URLs**: user-supplied `http(s)` URLs go through `crate::net::ensure_public_http_url` unless they belong to the configured Copyparty origin
- **TTS**: piper-rs in-process (no subprocess). `TtsState` holds `OnceLock<Arc<std::sync::Mutex<Piper>>>` + a bounded `DashMap` cache. Synthesize via `data.tts.synthesize(text)`; work runs on `spawn_blocking`. No external TTS fallback.
- **Recording**: opt-in (`RECORDING_ENABLED=false` by default), one buffer per guild.

## Build
- `build.rs` — compiles C stubs for espeak-rs-sys missing sonic/audio_object symbols

## Database Tables
`settings`, `voice_activity`, `user_stats`, `cron_jobs`, `triggers`, `trigger_keywords`, `trigger_responses`

## Commands
`/senator`, `/cron` (6), `/leaderboard` (5), `/math`, `/music` (5: grajcuj, skip, queue, stop, join), `/qr` (2), `/trigger` (6), `/tts`, `/record`, `/compress`, `/convert`, `/upload`, `/search`, `/download`, `/say`, `/web`.
Context menus: "Odtwórz", "Wyślij załączniki", "kompresja", "Pobierz z linku".