This commit is contained in:
Jean Chevronnet 2026-07-28 14:29:34 +00:00
commit 05c004d32d
No known key found for this signature in database
GPG key ID: 439666D63A9477E4
6 changed files with 1062 additions and 0 deletions

109
README.md Normal file
View file

@ -0,0 +1,109 @@
# rustbot
A small, modular IRC bot in Rust — **zero dependencies**, with its own IRC
protocol layer built to the [modern IRC client spec](https://modern.ircdocs.horse/).
## Structure
| File | Layer | Responsibility |
|---------------|------------------|-----------------------------------------------------------|
| `src/irc.rs` | protocol + I/O | Parse/serialize IRC messages (incl. IRCv3 tags), TCP transport |
| `src/bot.rs` | behavior | Registration, event loop, command handling |
| `src/main.rs` | wiring | Config from env vars, connect/reconnect supervisor |
The layers are deliberately separable: `irc.rs` knows nothing about the bot, and
`bot.rs` knows nothing about how the process is launched.
## Build & run
```sh
cargo build # or: cargo build --release
cargo test # unit tests for the message parser
cargo run
```
## Configuration
Settings are layered, each overriding the previous:
1. **Built-in defaults** (`Config::default()` in `src/bot.rs`)
2. **A config file**`key = value`, see [`rustbot.conf`](rustbot.conf)
3. **Environment variables** — handy for one-off overrides
The config file is found via: the **first CLI argument**, else `RUSTBOT_CONFIG`,
else `./rustbot.conf` if present.
```sh
cargo run # uses ./rustbot.conf
cargo run -- /etc/rustbot.conf # explicit path
RUSTBOT_CONFIG=/etc/rustbot.conf cargo run
```
Config file keys: `server`, `port`, `nick`, `user`, `realname`,
`channels` (comma/space-separated), `prefix`, `password`.
Whole-line `#`/`;` comments only — inline comments would clash with channel `#`s.
Environment overrides: `IRC_SERVER`, `IRC_PORT`, `IRC_NICK`, `IRC_REALNAME`,
`IRC_CHANNELS`, `IRC_PREFIX`, `IRC_PASSWORD`.
```sh
IRC_NICK=mybot IRC_CHANNELS='#test' cargo run # override the file for one run
```
> If you put a real `password` in `rustbot.conf`, add it to `.gitignore` so the
> secret isn't committed.
## Built-in commands
With the default `!` prefix:
- `!ping``pong`
- `!echo <text>` → echoes text back
- `!hello` → greets the sender
- `!help` → lists commands
Add your own in `Bot::handle_command` in `src/bot.rs`.
## Deployment (systemd)
Runs as a system service (`User=debian`), auto-restarts on crash, and starts on
boot. The unit is kept in the repo as [`rustbot.service`](rustbot.service).
```sh
cargo build --release
sudo cp rustbot.service /etc/systemd/system/rustbot.service
sudo systemctl daemon-reload
sudo systemctl enable --now rustbot # start now + on boot
systemctl status rustbot # health
journalctl -u rustbot -f # live logs (<< in, >> out)
sudo systemctl restart rustbot # after a rebuild
```
One-off overrides without touching `rustbot.conf`: put `IRC_*` lines in
`/etc/default/rustbot` (env wins over the config file), then restart.
## IRCv3
The bot negotiates capabilities (`CAP LS 302``CAP REQ``CAP END`) and
enables everything useful the server offers: `message-tags`, `server-time`,
`batch`, `echo-message`, `account-tag`, `extended-join`, `multi-prefix`,
`away-notify`, `chghost`, `setname`, `userhost-in-names`, `cap-notify`,
`labeled-response`.
Two of these do real work:
- **`server-time` + `batch`** let the bot recognise **replayed history**. On
join, InspIRCd replays recent channel lines (wrapped in a `chathistory`
batch). Without this the bot re-answered old commands on every reconnect;
now such messages are ignored (see `Bot::is_historical`).
- **`sasl`** (PLAIN) authenticates to services during negotiation. Set
`sasl_user`/`sasl_pass` in the config (or `IRC_SASL_USER`/`IRC_SASL_PASS`).
base64 is hand-rolled, so this stays dependency-free.
## Roadmap / natural next steps
- **TLS** (port 6697, or the `tls` STARTTLS cap) — the one remaining item that
needs a dependency (`rustls`/`native-tls`).
- **Read timeout + client-sent PING** to detect dead connections faster.
- Split commands into a registry/trait once there are many of them.