- Python 95.2%
- Nix 4.8%
- every dialog flow (search panel, sign-in, profile settings, create/edit wizards, downloads) now lives in ONE anchor message edited in place; user inputs are deleted after processing (morph/consume in common.py) - profile settings became a text menu that morphs through field prompts; /me keeps the photo card - @nickname / @slug in captions are links to the site (profiles/@nick, @author) — they are not Telegram mentions - game page links use @author/@game: bare segments resolve as ids on the site, slugs need the @ prefix - inline results keep article titles but the posted message now shows the cover via link preview pointed at the CDN thumbnail 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.staging.viende.su/"
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;
# 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".