🎟

Streaming service access bot: code login from Telegram

our own bot for Peregovorka (ToshaStream): code login, approvals, roles

A Telegram bot for managing streaming-service accounts: Telegram linking, one-time-code login, request approval and roles from the chat. Python, aiogram 3, no database of its own, frp tunnel to the service.

In production Β· in-house product
No own database
the bot calls the service's internal API, so accounts live in one place
11 commands
open to every participant: linking, code login, profile, name, avatar, password, logout, notifications
120 seconds
after this time the bot deletes the message with an issued password from the chat

The task

A service with chat, voice and streaming for a private team (this is Peregovorka, also called ToshaStream, a separate case) had registration open to anyone who was sent the link. The owner learned about a new account after the fact. Requests got lost, and even small things like changing a name or a password meant going to the owner or opening the web admin panel.

Every service account had to be tied to a real person in Telegram. For the participant: password-free login with a one-time code, and changing the password or name without the owner's help. For the owner: registration requests to approve, a list of accounts and notifications about service events. The original brief is kept in the repository as a separate specification document.

The solution

Stream Bot is our own Telegram bot for managing accounts. Implemented in version 1.2.0:

For the participant

  • Account linking. /link: the bot asks for a login (matched by login and by display name) and issues a code to enter on the site.
  • Password-free login. /login sends a one-time code for logging in to the site.
  • Profile and name. /me shows the profile, /name changes the display name; without an argument the bot asks for the name in a separate message.
  • Avatar. /avatar takes the service avatar from the Telegram profile photo.
  • Password. /password issues a new web login password; the bot deletes that message after 120 seconds.
  • Log out everywhere. /logout closes all web sessions.
  • Notifications. /notify holds personal settings for stream alerts and owner broadcasts.

For the owner (only the configured Telegram ID):

  • /users lists accounts page by page; /pending shows requests with Approve and Deny buttons; /approve, /deny, /role, /kick, /sessions <login>.
  • /stats shows who is on the service now: viewers and voice channels.
  • /broadcast sends any message (text, image, voice) to everyone who has not opted out, with a confirmation step.
  • /announce is a global switch for stream-start alerts.
  • /version is a hidden command that shows the version of the running instance.

A permanent button menu is built by role: an unlinked person sees a single linking button, and the owner gets an extra row with accounts, requests and who is online.

How it works

Login to a site with a code from Telegram

An account is linked to Telegram once, through /link. After that a login code is issued by /login, and the bot sends confirmations of linking and of login to the person. The code is a one-time string of characters typed on the site instead of a password. Login with a username and password remains a separate way in, so stopping the bot does not lock anyone out.

A bot with no database: an internal API with a shared secret

The bot stores no accounts and never reads the service's database directly. It calls the service's internal API over HTTP (an API is a set of addresses through which one program asks another to do something), and every request carries a shared secret in a header. There is a single owner of the data, so the bot and the service cannot drift apart. Even notification settings are kept by the service: its settings table holds the fields for stream alerts and broadcasts.

Service events: polling the queue every 20 seconds

Every 20 seconds the bot asks the service for its event queue. The owner receives registration requests, logins from a new address, a streak of failed logins, and the start and end of a stream. A person receives confirmation of linking and of code login. If the tunnel drops, the bot replies that the chat service is not responding, and after recovery it reads the queue from the previous cursor. At startup the bot skips the queue's tail so that it does not resend old events.

An frp tunnel between the service server and the bot

The service runs on a server in Russia, from which Telegram is unreachable. So the bot runs on a foreign machine in a separate Docker Compose project. The link is built with frp, a reverse tunnel tool: the client on the service side makes an outbound connection to the server on the bot side and exposes the internal API over TLS. The control port on the bot side is open only to the address of the service server. An earlier variant, a reverse SSH tunnel, is kept in the repository as a fallback.

Long polling: a bot without an open port

The bot receives messages by long polling: it holds a request to Telegram itself and waits for the answer. So it needs no public address and no webhook. Dialog states (for example, "the bot is waiting for a name") are kept in memory, and a restart cuts off unfinished dialogs.

Hidden owner commands

Owner commands are not shown in the menu. For a foreign Telegram ID, /users or /version get the reply that a nonexistent command would get. Permissions come from a variable that holds the owner's ID: without it, owner commands and notifications are switched off.

What else is in the repository

Besides the bot, the deploy/ folder holds patches and new files for the chat service (Go and JS) used to extend it on the production server: emotes, replies and reactions, history search, message deletion, attachments, screen sharing, a connection-quality monitor, stream recordings, a watchdog and nightly backups. The tunnel configuration is there too. The full source of the service itself is not in the repository.

Results

  • Each account is tied to a real person in Telegram, and password-free login works with a one-time code.
  • Registration requests reach the owner's chat and are resolved with buttons or commands.
  • Changing a name or password, logging out of all sessions and setting roles are done from the chat, without the web admin.
  • The bot has no database of its own: accounts live in one place, in the service.
  • Stopping the bot does not stop the site, chat, voice or stream.
  • The bot runs in Docker as a non-root user, and its version is written to the log at startup.

Technologies and why

  • Python 3.12 and aiogram 3.15 β€” an asynchronous Telegram bot, with dialogs built on finite state machines.
  • aiohttp β€” the client for the service's internal API and long polling.
  • The service's internal API with a shared secret β€” the single source of account data.
  • frp β€” a tunnel between the service server in Russia and the bot's foreign server, over TLS.
  • Docker β€” the python:3.12-slim image, a non-root process, TZ=Europe/Moscow.

Status

Our own product, part of the Peregovorka (ToshaStream) setup. The current version, 1.2.0 of 10 September 2026, added /notify, /broadcast, /announce and linking by display name. Since 14 September the code also contains, without a version bump, the /avatar command and improvements to the chat service itself in deploy/.

Limitations: there are no automated tests, so checking is manual, and dialog states do not survive a restart. Not established from the code: whether mandatory Telegram linking for joining voice is enabled on the production server. No separate roadmap items are listed.

Questions about this project

How do I let users log in to a website with a code from Telegram, without a password?
A participant links the account with /link: the bot asks for a login or display name and issues a code to enter on the site. After that, /login sends a one-time code for password-free login. Confirmation of linking and of login arrives in the person's chat.
How can an owner approve new streaming-service accounts from Telegram?
A registration request lands in the owner's chat, and /pending lists all waiting requests with Approve and Deny buttons. The same can be done with /approve and /deny. The owner also gets alerts about a login from a new address, a streak of failed logins, and the start and end of a stream.
Where does the bot store accounts and passwords?
Nowhere: the bot has no database of its own. Every action goes over HTTP to the service's internal API with a shared secret in a header. Even notification settings are kept by the service, not by the bot.
What can an ordinary user do, and what is owner-only?
A participant manages their own account: /link, /login, /me, /name, /avatar, /password, /logout, /notify. The owner commands (/users, /pending, /approve, /deny, /role, /kick, /sessions, /stats, /broadcast, /announce) work only for the configured Telegram ID; for anyone else the bot replies as to a typo.
What happens to the site and voice chat if the bot stops?
The site, chat, voice and stream keep running. If the tunnel drops, the bot answers that the chat service is not responding, and once it recovers the bot reads the event queue from where it left off. Unfinished dialogs are cut off when the bot restarts, because their state is kept in memory.
How is the bot connected to the server if Telegram is unreachable from it?
The service runs on a server in Russia, from which Telegram is unreachable, so the bot runs on a foreign machine. The service itself makes an outbound connection and exposes its internal API to the bot through an frp tunnel over TLS. The bot needs no open port or public address: it polls Telegram itself (long polling).

Need something similar?

Tell us about the task β€” we'll show how we solved it and estimate the scope.