Telegram frontend for the VienDesu! backend
  • Python 95.2%
  • Nix 4.8%
Find a file
Aleksandr 3a165f8571 Single-message dialogs, site links in captions, inline images
- 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
2026-07-12 20:00:35 +03:00
node Single-message dialogs, site links in captions, inline images 2026-07-12 20:00:35 +03:00
.gitignore Node: VienDesu Telegram bot (Python prototype) 2026-07-12 18:30:54 +03:00
flake.lock Node: VienDesu Telegram bot (Python prototype) 2026-07-12 18:30:54 +03:00
flake.nix Telegram proxy support and self-provisioned bot identity 2026-07-12 18:54:27 +03:00
pyproject.toml Telegram proxy support and self-provisioned bot identity 2026-07-12 18:54:27 +03:00
README.md Production site URL, private-only commands, wording 2026-07-12 18:38:31 +03:00

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 viendesu workspace 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".