- Python 94.9%
- Nix 5.1%
When off, sign-in and registration are hidden everywhere: main-menu buttons, the /signin bot command, and the signin/signup handlers. Existing sessions keep working. Exposed as services.node.authEnabled in the NixOS module. Co-Authored-By: Eva |
||
|---|---|---|
| node | ||
| .gitignore | ||
| flake.lock | ||
| flake.nix | ||
| pyproject.toml | ||
| README.md | ||
Node
VienDesu Telegram bot, named after the rabbit librarian from BLACK SOULS II.
Feature parity with the web frontend: game/author search with filters and cursor pagination, entity cards, profile management, author/game creation and editing — built around inline (callback) keyboards, plus inline mode for sharing games in any chat.
Status: this Python implementation is a working prototype. The bot will be rewritten in Rust (on top of the
viendesuworkspace crates) once the feature set settles; treat the Python code as the executable spec.
Run
export NODE_BOT_TOKEN="123456:ABC..." # or NODE_BOT_TOKEN_FILE=/path/to/secret
# optional overrides:
export NODE_API_URL="https://api.desu.church/"
export NODE_CDN_URL="https://cdn.viende.su/"
export NODE_SITE_URL="https://viende.su/"
export NODE_DB="node.db"
python -m node # or: node (after pip install -e .; mind the name clash
# with node.js if both end up on one PATH)
Dependencies: aiogram>=3.13, httpx, aiosqlite, mistune>=3.0
(see pyproject.toml). On Nix: nix develop for a shell, nix build
for the package.
For inline mode, enable it for the bot in @BotFather (/setinline).
NixOS module
# flake inputs: node-bot.url = "git+ssh://forgejo@git.viende.su:61488/VienDesu/Node.git";
{
imports = [ node-bot.nixosModules.default ];
services.node = {
enable = true;
# Secret file with the bot token; loaded via systemd LoadCredential,
# so root-owned agenix/sops-nix paths work as-is:
tokenFile = config.age.secrets.node-bot-token.path;
# apiUrl (staging) / cdnUrl / siteUrl (viende.su) — optional;
# authEnabled = false hides sign-in/registration everywhere — optional;
# environmentFile — optional extra NODE_* overrides (also for secrets).
};
}
The service runs as a DynamicUser with StateDirectory=node
(sqlite lives in /var/lib/node/node.db) and a hardened sandbox.
The token never enters the Nix store or the process environment — the
bot reads it from the credential file via NODE_BOT_TOKEN_FILE.
Layout
node/api.py— typed client for the core API; mirrors the frontend's api layer:{ok}/{error}envelope,{change: v}patches, cursor pagination.node/handlers/— one router per area: auth, profile, game search, game/author/user cards, content management (create/edit wizards), inline mode.node/keyboards.py,node/cb.py— inline keyboards and callback-data factories (callback_data is capped at 64 bytes; entity ids are ~39 digits).node/db.py— sqlite: per-user session tokens and search state.node/md.py— markdown → Telegram-HTML (mistune with a Telegram renderer).node/genres.py— genre slugs + Russian labels, mirrored from the frontend.
Feature parity notes
Deliberately mirrors frontend behaviour, including its stubs: registration and password change show "unavailable", rating submission shows "soon".