# xmppcb A lean XMPP AI chatbot in C. It joins MUC rooms, logs messages, and answers `@bot` commands via an OpenAI-compatible LLM HTTP API. A C reimplementation of the Python `bot.py` in the parent directory, written in the suckless spirit: one source file, one library dependency (OpenSSL), fixed buffers, no runtime config parser. There is **no end-to-end / OMEMO encryption**. Transport is still encrypted: STARTTLS for the XMPP connection, HTTPS for the LLM API. ## Build Needs a C compiler and OpenSSL development headers (`apt install libssl-dev`). ```sh make ``` The makefile copies `config.def.h` to `config.h` on first build. Edit `config.h` (not the `.def`) to set your server, rooms and model. ## Configure Non-secret settings live in `config.h`. Recompile after changing it. Secrets are read from the environment, never stored on disk by the bot: ```sh export XMPP_PASSWORD='the-bot-account-password' export LLM_API_KEY='sk-...' ``` `instruction.md` holds the system prompt; it is read once at startup. On a fresh checkout, copy `instruction.md.sample` to `instruction.md`, then edit it and restart to change the bot's behaviour. ### Choosing the LLM model and provider The bot speaks the OpenAI-compatible `/v1/chat/completions` protocol, so it works with any provider that does — OpenAI, OpenRouter, Together, a local Ollama/llama.cpp server, etc. Four macros in `config.h` select it: | Macro | Purpose | |-------------|--------------------------| | `LLM_HOST` | API hostname | | `LLM_PORT` | API port (usually 443) | | `LLM_PATH` | chat/completions path | | `LLM_MODEL` | model identifier | OpenAI (default): ```c #define LLM_HOST "api.openai.com" #define LLM_PATH "/v1/chat/completions" #define LLM_MODEL "gpt-5.4-mini" ``` OpenRouter: ```c #define LLM_HOST "openrouter.ai" #define LLM_PATH "/api/v1/chat/completions" #define LLM_MODEL "anthropic/claude-sonnet-4-6" ``` Change the macros, run `make` again, and set `LLM_API_KEY` to a key for that provider. The bot sends `max_completion_tokens`; a few non-OpenAI servers still expect the older `max_tokens` field and would need a source change. ## Run Run from the directory containing `instruction.md` and `config.h`: ```sh ./xmppcb ``` It reconnects automatically after a dropped connection. On an authentication failure it exits with status 1 (no retry loop). Logs go to stderr. Per-room chat history is appended to `history/.log`. ## Usage In a joined room: | Command | Effect | |---|---| | `@bot help` | Show commands | | `@bot summarize [period]` | Summarise the conversation | | `@bot tasks [period]` | Extract action items | | `@bot ` | Ask anything, using recent chat as context | Period tokens: `1h 1d 2d 1w 2w 1m`. Without one, the last 50 messages are used. ## Run under a supervisor The bot does not daemonise. Use a process supervisor so it restarts and starts at boot. systemd (`/etc/systemd/system/xmppcb.service`): ```ini [Unit] Description=xmppcb XMPP AI bot After=network-online.target [Service] WorkingDirectory=/home/youruser/xmpp-bot/xmppcb ExecStart=/home/youruser/xmpp-bot/xmppcb/xmppcb Environment=XMPP_PASSWORD=... Environment=LLM_API_KEY=... Restart=always RestartSec=10 [Install] WantedBy=multi-user.target ``` Prefer an `EnvironmentFile=` with mode 600 over inline secrets. ## Notes and limitations - The bot only sees messages sent while it is in the room. - LLM calls are blocking: the bot pauses for the few seconds a reply takes. Adequate for a low-traffic bot. - The XML parser is intentionally narrow — it understands only the XMPP stanzas this bot needs, not arbitrary XML. - Resource use is small: ~37 KB binary, a few MB RSS. ## Layout ``` xmppcb/ ├── xmppcb.c all logic: TCP, TLS, XML, SASL, MUC, HTTP, LLM ├── config.def.h config template (committed) ├── config.h your config (copied from .def, gitignored) ├── instruction.md.sample system prompt template (committed) ├── instruction.md system prompt in use (gitignored) ├── start.sample run-script template (committed) ├── start run script exporting the secrets (gitignored) ├── makefile └── history/ per-room chat logs (generated, gitignored) ```