Streaming service access bot: code login from Telegram
our own bot for Peregovorka (ToshaStream): code login, approvals, roles
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.
/loginsends a one-time code for logging in to the site. - Profile and name.
/meshows the profile,/namechanges the display name; without an argument the bot asks for the name in a separate message. - Avatar.
/avatartakes the service avatar from the Telegram profile photo. - Password.
/passwordissues a new web login password; the bot deletes that message after 120 seconds. - Log out everywhere.
/logoutcloses all web sessions. - Notifications.
/notifyholds personal settings for stream alerts and owner broadcasts.
For the owner (only the configured Telegram ID):
/userslists accounts page by page;/pendingshows requests with Approve and Deny buttons;/approve,/deny,/role,/kick,/sessions <login>./statsshows who is on the service now: viewers and voice channels./broadcastsends any message (text, image, voice) to everyone who has not opted out, with a confirmation step./announceis a global switch for stream-start alerts./versionis 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?
How can an owner approve new streaming-service accounts from Telegram?
Where does the bot store accounts and passwords?
What can an ordinary user do, and what is owner-only?
What happens to the site and voice chat if the bot stops?
How is the bot connected to the server if Telegram is unreachable from it?
More in this area
Finance Assistant: PDF bank statements to a Telegram budget
our own product: a bot and web dashboard, four banks' statements, Gemini
Telegram ticketing system for an IT department
for a museum complex in Moscow: tickets, asset tracking, knowledge base and analytics
Tech Poly VPN: VPN subscription billing in Telegram
our own product: SBP payments, key issuing in Marzban, a backup VKontakte bot
Need something similar?
Tell us about the task — we'll show how we solved it and estimate the scope.