vibeseeker

vibeseeker

Вайбкодинг на практике: продуктовая разработка, архитектура Telegram Mini Apps и фишки @GoosleeBot.
На Пикабу
в топе авторов на 173 месте
100 рейтинг 0 подписчиков 1 подписка 2 поста 0 в горячем

Инлайн-кнопка одна на всех: параметры, аутентификация и обратная связь в Telegram miniapp

Серия Анатомия Telegram Mini Apps: о разработке изнутри

Примеры кода — на C#/.NET, потому что так написан наш прод. Но вся статья — про механику Telegram, а не про язык: то же самое собирается на Node, Python или Go без изменений. Именно ради переносимой механики статья и написана.

О чём статья? Это вторая часть серии о том, как мы вайб-кодингом построили GoosleeBot — мост из Telegram в Google Meet, Zoom и другие сервисы видеовстреч. Здесь — самая недокументированная часть разработки под Telegram: механизм сессий, который связывает чат, miniapp и внешний браузер в одно приложение. Вот что внутри:

  • как передать параметры из инлайн-бота в miniapp, если кнопка в чате одна на всех участников;

  • как аутентифицировать пользователя miniapp за один запрос — с готовым скелетом валидации initData;

  • как сохранить состояние там, где переменные Telegram не работают вовсе — во внешнем браузере;

  • как доставлять изменения из чата в miniapp мгновенно (спойлер: WebSocket в telegram-webview работает);

  • как редактировать сообщения в чате асинхронной очередью и не словить бан от Bot API;

  • и другие.

А это вся серия:

  • Часть 1 — история продукта и честный вердикт Telegram как платформе: правила для памяти агента, функция «где я?», туннели.

  • Часть 2 (вы здесь) — механизм сессий: параметры, аутентификация, обратная связь, редактирование без бана.

  • Часть 3 — UX-хаки: бот, который «звонит» как телефон, онбординг-воронка одним JSON-файлом и почему ссылки надо готовить до клика.

Проблема: кнопка общая, ссылка тупая, а половина флоу — вообще не в Telegram

Наш главный сценарий выглядит невинно: в чате лежит сообщение бота с кнопкой «Присоединиться к звонку». Но у этой кнопки три неприятных свойства, о которых не пишут в туториалах:

Первое: кнопка одна на всех. Сообщение в чате общее — его видят все участники. Нажать может любой, и для каждого miniapp должен открыться со «своим» контекстом: кто ты, в каком звонке, что тебе можно. А кнопка умеет ровно одно — открыть ссылку. Одинаковую для всех.

Второе: нужна аутентификация и обратная связь. Мало открыть страницу — надо понять, кто её открыл (и не поверить на слово), а потом вернуть результат действий обратно: в сообщение в чате, другим участникам, в их miniapp.

Третье, самое коварное: часть флоу уходит во внешний браузер. OAuth-авторизация у Google или Zoom по правилам провайдеров происходит в системном браузере — и в момент, когда пользователь туда перешёл, все переменные miniapp перестают существовать. Нет `Telegram.WebApp`, нет initData, нет ничего. Из всего контекста Telegram остаётся ровно то, что вы сами успели положить в URL.

Три проблемы — одно решение: серверные сессии. И сразу важная рамка: это не трюк для проброса параметров. Это полноценный аналог классической веб-сессии — серверный объект с временем жизни и типизированным хранилищем значений, на котором держится вся серверная логика. Если в вашем продукте есть хотя бы сценарий «выйти из miniapp наружу и вернуться» — механизм сессий обязателен, без вариантов.

Сессия как серверный объект

Скелет предельно простой (напоминаю, здесь C#, но это словарь с TTL — он есть в любом языке):

```csharp

public sealed class Session

{

public string Key { get; init; } // Guid без дефисов — едет в URL

public SessionType SessionType { get; set; } // RequestCall | MiniAppRoute | AccessToken | ...

public TimeSpan SessionLifeTime { get; set; }

public bool IsExpired => UtcNow - CreatedDate > SessionLifeTime;

public T? GetData<T>() where T : class { ... } // типизированные данные сценария

public void SetData<T>(T? value) where T : class { ... }

}

```

Хранилище — обычный `ConcurrentDictionary<string, Session>` в памяти процесса, фоновый воркер выметает протухшие. Ключ — случайный Guid: он непредсказуем, поэтому сам по себе работает как секрет.

Главное здесь — `GetData<T>()`. У каждого типа сессии свой класс данных, и это данные не «для передачи», а рабочее состояние, на которое опирается серверная логика. Например, у сессии инлайн-звонка внутри лежит: кто создал звонок, список пользователей, нажимавших кнопки, выбранный провайдер, статус звонка, id инлайн-сообщения в чате, последний отрисованный текст этого сообщения. Сервер читает и меняет это состояние на каждом шаге сценария — ровно как классическая сессия в вебе, просто ключ к ней приезжает не в cookie (помните правило из части 1: cookies в webview ненадёжны), а в URL и токенах.

Цепочка: от кнопки в чате до персональной страницы

Теперь само решение — цепочка из трёх сессий. Звучит громоздко, на деле — три словарных записи и два редиректа:

```text

[чат] RequestCall-сессия ─── общая, одна на звонок

│ ключ уезжает в кнопку: t.me/MyBot/app?startapp= {key}

[miniapp] роутер-страница: собирает Telegram.WebApp.initData → POST /init

│ сервер валидирует подпись, находит RequestCall по ключу

[сервер] AccessToken-сессия ── персональная, у каждого пользователя своя,

│ внутри ссылка на общую OriginalSession

[страница] редирект на целевую страницу с персональным токеном в URL

```

По шагам:

1. Создание. Когда пользователь вызывает бота в чате, сервер создаёт `RequestCall`-сессию — общую для этого звонка. Ключ уходит в кнопки. Для callback-кнопок мы кодируем его прямо в payload (`{key}@@@{команда}`), для кнопки miniapp — в параметр `startapp` диплинка. Это единственный канал: Telegram не даст кнопке передать ничего, кроме этой строки.

2. Роутер. Кнопка открывает не целевую страницу, а лёгкую страницу-роутер. Её JS собирает `initData` и шлёт на сервер:

```js

const tg = window.Telegram.WebApp;

const resp = await fetch('/miniapp/init', {

method: 'POST',

body: JSON.stringify({

initData: tg.initData, // подписанная строка — для проверки

startParam: tg.initDataUnsafe.start_param, // ключ RequestCall-сессии

platform: tg.platform, // помните «где я?» из части 1

}),

});

const { redirectUrl } = await resp.json();

location.replace(redirectUrl);

```

3. Проверка и обмен. Сервер валидирует подпись initData (следующий раздел), находит общую сессию по ключу из `start_param` — и выпускает персональную `AccessToken`-сессию: «пользователь X в контексте звонка Y». Внутри неё — ссылка на общую:

```csharp

var accessSession = SessionService.Create(SessionType.AccessToken, lifeTime);

accessSession.SetData(new AccessTokenSessionData {

UserId = user.Id,

OriginalSession = requestCallSession, // общий контекст звонка — один на всех

});

return RedirectUrlFor(page, accessSession.Key); // токен — в URL целевой страницы

```

Одна общая сессия звонка — много персональных токенов поверх неё. Любой запрос с целевой страницы несёт персональный токен, и сервер за один lookup знает и «кто», и «в каком звонке». Проблема «кнопка одна на всех» решена.

4. Внешний браузер. А теперь то, ради чего это всё. Когда пользователь уходит на OAuth к Google и возвращается на наш callback-URL — он приходит в обычном браузере, без единой переменной Telegram. Но токен-то мы положили в URL заранее. Сервер достаёт по нему AccessToken-сессию → из неё OriginalSession → и продолжает сценарий, как будто никто никуда не уходил. Состояние пережило переход чат → miniapp → внешний браузер → обратно, потому что оно никогда и не покидало сервер.

Аутентификация: не верьте `initDataUnsafe` на слово

Telegram кладёт в miniapp данные пользователя двумя способами: `initDataUnsafe` (удобный распарсенный объект) и `initData` (сырая строка с криптографической подписью). Слово «Unsafe» в названии — не кокетство: эти данные каждый умеет подделать, отправив вам любой user_id. Использовать их для UI — можно, для серверных решений — только после проверки подписи сырой строки.

Алгоритм проверки описан в доках Telegram, вот его скелет целиком — это один статический метод:

```csharp

public static bool Validate(string initData, string botToken)

{

var parsed = ParseQuery(initData); // initData — это query string

var receivedHash = parsed["hash"];

var dataCheckString = string.Join('\n', // все поля кроме hash,

parsed.Where(kv => kv.Key != "hash") // отсортированы по ключу

.OrderBy(kv => kv.Key, StringComparer.Ordinal)

.Select(kv => $"{kv.Key}={kv.Value}"));

var secretKey = HMACSHA256(key: "WebAppData", message: botToken);

var expected = HMACSHA256(key: secretKey, message: dataCheckString);

if (!FixedTimeEquals(expected, receivedHash)) return false; // константное время!

return UtcNow - FromUnix(parsed["auth_date"]) < MaxAge; // и свежесть: у нас 1 час

}

```

Три детали, которые агент при вайб-кодинге стабильно упускает — проверьте руками:

  • Сортировка полей ordinal, не culture-зависимая — иначе подпись «иногда не сходится».

  • Сравнение хэшей в константное время (`FixedTimeEquals`), а не `==` — защита от timing-атак на подбор подписи.

  • Проверка `auth_date`. Подпись без срока годности — это вечный пропуск: перехваченная один раз строка initData работала бы годами.

После валидации `user_id` из initData можно считать доказанным — Telegram расписался. Заметьте, что получилось: регистрация, логин и восстановление пароля в вашем приложении не существуют как задачи. Это одна из главных причин строить бизнес-приложения на Telegram вообще.

Врезка честности: всё это лежит в памяти процесса

Да, наши сессии — in-memory, `ConcurrentDictionary`, без Redis. Это осознанный трейдофф, и вот его границы:

  • Когда это нормально: один инстанс приложения; сессии короткоживущие (минуты-часы) и по природе восстановимые — пользователь в худшем случае нажмёт кнопку ещё раз; критичное для бизнеса состояние (звонки, пользователи, настройки) и так живёт в PostgreSQL, сессия лишь оркестрирует сценарий.

  • Когда сломается: рестарт или деплой обнуляет живые сценарии (у нас это «кнопка перестала отвечать, вызовите меню заново»); второй инстанс за балансировщиком не увидит чужих сессий — привет, sticky sessions или внешний стор.

Для вайб-кодера мораль такая: начинайте со словаря в памяти — это ноль инфраструктуры и вся механика статьи работает как есть. Но заведите интерфейс хранилища сессий с первого дня, чтобы Redis, когда понадобится, был заменой одного класса, а не переписыванием.

Обратная связь: сигнал по WebSocket, данные по запросу

Осталась последняя стрелка: изменения должны лететь обратно. Собеседник выбрал провайдера в своём miniapp — у вас на странице должен обновиться статус; кто-то подключился к встрече — сообщение в чате должно это показать.

В части 1 я уже спойлерил: SignalR (WebSocket) внутри telegram-webview работает штатно на всех платформах. Схема такая:

  • При открытии страницы клиент вступает в группу по ключу общей сессии звонка: все участники одного звонка — в одной комнате.

  • Когда серверное состояние меняется, сервер шлёт в группу голое событие `StateChanged` — без данных.

  • Клиент, получив сигнал, сам запрашивает актуальное состояние обычным HTTP-запросом со своим персональным токеном.

```csharp

// сервер: состояние изменилось → пнуть группу (с debounce, см. ниже)

await hub.Clients.Group(callSessionKey).SendAsync("StateChanged");

// клиент: сигнал — не данные, а приглашение спросить

connection.on('StateChanged', () => refreshState()); // GET со своим токеном

```

Почему сигнал пустой? Три причины: не надо думать о правах в push-канале (каждый забирает состояние своим токеном — сервер сам решит, что ему видно); потерянный сигнал ничего не ломает (следующий `refreshState` всё выровняет); и события можно дебаунсить — при шквале изменений группа получает один пинок в N миллисекунд, а не очередь устаревших снапшотов. Плюс дешёвая страховка: редкий фоновый poll на случай, если WebSocket всё-таки умер.

Бонус-лайфхак: редактирование сообщений через очередь — иначе бан

Обратная связь долетает не только до miniapp, но и до того самого инлайн-сообщения в чате: «Готово. Подключились: Аня, Дмитрий». И вот здесь вайб-кодеры массово наступают на грабли: наивный код дёргает `editMessageText` на каждое изменение состояния. Два участника нажали кнопки одновременно — два edit'а; шквал событий — шквал edit'ов. А у Bot API на это две реакции: ошибка `400: message is not modified` (текст совпал с текущим) и flood-лимиты вплоть до временной блокировки бота — для продукта, который живёт в чужих чатах, это смерть.

Решение — не редактировать сообщение из бизнес-логики вообще. Вместо этого:

```text

логика: «состояние сессии X изменилось» → пометка X в очереди (дедупликация по ключу)

воркер: берёт ключ → строит текст и клавиатуру ИЗ ТЕКУЩЕГО состояния сессии

→ сравнивает с последним отправленным текстом (он хранится в сессии!)

→ совпало: молча пропустить | изменилось: editMessageText, запомнить текст

```

Три свойства, ради которых всё затевалось: дедупликация — десять изменений за секунду дают одно редактирование, потому что пометки схлопываются по ключу сессии, а текст строится по финальному состоянию; идемпотентность — сравнение с `LastInlineMessageText` из сессии гарантирует, что мы никогда не пошлём Telegram то, что у него уже есть (заметьте: это ещё одна работа для серверного состояния сессии); асинхронность — бизнес-логика не ждёт Telegram API и не падает от его ошибок.

Правило в память агента: сообщение в чате — это проекция состояния сессии, обновляемая фоновым воркером. Бизнес-логика сообщений не трогает.

Итого

Механика из этой статьи — переиспользуемый каркас для любого бизнес-приложения на miniapp, где есть «общая кнопка» и «выход наружу»: серверная сессия с типизированным состоянием; персональные токены поверх общего контекста; подпись initData как замена всей системе логина; пустые сигналы по WebSocket вместо данных в push; и очередь-проекция для сообщений в чате. Ни один из этих элементов не завязан на C# — это протокол работы с платформой.

В части 3 — фокусы: как бот «звонит» пользователю так, что телефон ведёт себя как при настоящем входящем вызове (спойлер: удаляем и переотправляем сообщение — и почему это работает). Почему в Telegram невозможен привычный веб-паттерн «клик → сервер сформировал ссылку → редирект» и как жить с тем, что конечный URL должен лежать в кнопке до клика. И скрипт-чат: онбординг-воронка целиком в одном JSON-файле — с эффектом печати, слайдами и аналитикой по шагам.


Всё описанное написано для Telegram, но в большей части справедливо и для MAX — механика ботов, miniapp и сессий там строится по тем же принципам. Если есть желание сделать адаптацию под MAX и российские сервисы видеовстреч — велком в личку.

Потрогать сам мост: @GoosleeBot · gooslibot.com

Показать полностью
0

Нюансы разработки бизнес-приложения на платформе Telegram Mini Apps: что я понял про Telegram как платформу

Серия Анатомия Telegram Mini Apps: о разработке изнутри

Примеры кода в серии — на C#/.NET, потому что так написан наш прод. Но всё описанное — про механику Telegram, а не про язык: то же самое собирается на Node, Python или Go без изменений. Собственно, ради этой переносимой механики статья и написана.

О чём статья? Мы вайб-кодингом построили мост из Telegram в Google Meet, Zoom и другие сервисы видеовстреч — и набили все шишки, которые можно набить о miniapp при разработке бизнес-приложения. В этой статье — четыре реальных лайфхака, которые стопроцентно пригодятся вам при написании любого бизнес-приложения на Telegram miniapp. Вот некоторые из них:

  • готовый блок правил для памяти агента — ограничения веб-движка Telegram плюс требования каталогов (копируется в CLAUDE.md как есть);

  • функция «где я?» — платформа и режим, без которой каждая страница будет полем чудес;

  • туннель с первого дня — приём, который превращает деплой-на-каждый-чих в итерации по секундам;

  • как хранить состояние между вызовами бота, miniapp и внешним браузером;

  • бот, который «звонит» как телефон;

  • и другие;

А это вся серия:

  • Часть 1 (вы здесь) — история продукта и честный вердикт Telegram как платформе для бизнес-приложений: что дёшево, что больно, что прописать агенту до первой строчки кода.

  • Часть 2 — механизм сессий: как передать параметры из инлайн-бота в miniapp, а потом прочитать их во внешнем браузере; аутентификация, обратная связь в чат и как не словить бан за редактирование сообщений.

  • Часть 3 — UX-хаки: бот, который «звонит» как телефон, онбординг-воронка одним JSON-файлом и почему ссылки надо готовить до клика.

С чего всё началось

Звонки в Telegram работают — но именно как звонки: голос, видео, положил трубку. Встречи — другой жанр, и он в Telegram почти не развит. А вот в Google Meet и Zoom вокруг встречи выросла целая экосистема: транскрибация, AI-саммари, запись, расширения, интеграции с календарями и CRM. Когда разговор рабочий — хочется именно встречу, со всем этим обвесом.

Но переписка-то живёт в Telegram: там группа с коллегами, там клиент, которому «удобнее в телеге». И каждый раз, когда переписка дозревает до «давай голосом», начинается ритуал: открыть Google Meet или Zoom, создать встречу, скопировать ссылку, вернуться в чат, вставить, объяснить, куда нажимать.

Идея была наглая в своей простоте: пусть ссылку создаёт бот. Прямо в чате, одной кнопкой, в том сервисе, который доступен и у меня, и у собеседника. Так появился GoosleeBot — мост из Telegram в мир видеовстреч: Google Meet, Zoom, Teams, Jitsi, FaceTime и далее по списку.

Дальше был чистый вайб-кодинг: агент, промпты, итерации. Цифры такие: первый MVP — Google Meet, Zoom и Jitsi — был готов за два дня и честно доказал, что идея работает. А потом были четыре недели доводки. И вот что важно: главным расходом времени был не код — код агент пишет быстро. Время съело множество сценариев, которые пришлось охватить и протестировать: два собеседника или группа, первый звонок или сотый, подключён ли у автора провайдер, iOS или десктоп, свой чат или чужой — и каждую комбинацию надо прогнать руками в настоящем Telegram.

Именно поэтому статья устроена не как «смотрите, какие мы молодцы», а как список вещей, которые я бы прописал агенту в память до первой строчки кода. Потому что главный урок такой: Telegram — отличная платформа для продуктивити-приложений, но её веб-движок — не браузер, а Bot API — не REST-бэкенд. Если агент этого не знает, он уверенно нагенерит красивое неработающее.

Как это выглядит для пользователя

Сценарий: в любом чате вызываешь бота, жмёшь кнопку — бот создаёт комнату у доступного провайдера и кладёт в чат кнопку «Присоединиться». Если собеседник не реагирует — бот умеет «позвонить»: слать сообщение-звонок с повтором, как настоящий входящий вызов (как это сделано — в третьей статье серии, там простой и наглый трюк).

Ни один участник не устанавливает ничего нового. В этом вся ставка: дистрибуция через чат — фича Telegram, которой нет ни у одной «нормальной» платформы.

Правила, которые нужно прописать агенту в память до старта

Это центральная часть статьи. Если вы вайб-кодите под Telegram — скопируйте этот блок в CLAUDE.md / system prompt агента как есть. Каждое правило оплачено часами отладки «а почему на айфоне не так».

Блок А. Веб-движок Telegram — не браузер

  1. Никогда не использовать `100vh`. Только `viewportStableHeight` / CSS-переменную `var(--tg-viewport-height)` и подписку на событие `viewportChanged`. Что сломается: на iOS низ страницы уедет под панель, на Android клавиатура «сожмёт» экран и разложит вёрстку.

  2. Никаких `window.open` и `target="\_blank"`. Только `Telegram.WebApp.openLink()` для внешних ссылок и `openTelegramLink()` для внутренних. Что сломается: на части клиентов клик молча не сделает ничего.

  3. Никаких `alert` / `confirm` / `prompt`. Только `showAlert` / `showConfirm` / `showPopup`. Что сломается: то же самое — молчание вместо диалога, причём не везде, а «у некоторых пользователей», что хуже.

  4. Не полагаться на cookies для авторизации в miniapp. Webview теряет их непредсказуемо. Авторизация — токеном, переданным явно (заголовок или параметр). Что сломается: пользователь «разлогинивается» между открытиями, а вы неделю ищете несуществующий баг на сервере.

  5. `localStorage` — это кэш, а не хранилище. Всё важное — на сервере (или в `CloudStorage`). Что сломается: состояние пользователя внезапно обнулится, и вы об этом не узнаете.

  6. Навигация «назад» — через `BackButton` Telegram, а не через history браузера. Что сломается: системная кнопка «назад» на Android закроет miniapp целиком вместо возврата на шаг.

  7. Первым делом на каждой странице — определить, где ты: платформа и режим (miniapp или обычный браузер). От этого ветвятся CSS и поведение. Функция ниже.

  8. Всё, по чему пользователь кликает, должно быть готово до клика. Привычная веб-схема «клик → сервер обработал → сформировал URL → редирект» в Telegram не работает: переадресации нет. Конечный URL должен лежать в кнопке уже в момент её отрисовки — то есть всю «нагрузку редиректа» надо вычислить заранее (подробный разбор — в третьей статье).

  9. Тестировать минимум на трёх клиентах: iOS, Android, Desktop. Это три разных webview с разным рендером и разным поведением. «Работает у меня на десктопе» — это ноль из трёх.

Блок Б. Требования публикации — с первого дня, а не «потом отполируем»

У каталогов (и у самого Telegram, и у витрин miniapp-ов) есть требования к приложению. Переделывать под них готовый продукт — самая дорогая работа в проекте. Поэтому в память агента, жёстко:

  1. Мультиязычность с первого экрана. Ни одной строки текста в разметке — всё через локаль-файлы. Язык пользователя приходит сам: `initDataUnsafe.user.language\_code`. Добавить второй язык в проект, где строки зашиты в вёрстку, — это переписать проект

  2. Тема — только через `themeParams`. Все цвета — из CSS-переменных `--tg-theme-\*`, тёмная и светлая темы обязательны, подписка на `themeChanged`. Ни одного захардкоженного цвета. Что сломается: белый текст на белом фоне у половины аудитории — и отказ каталога.

  3. Каталог проверяет: языки, обе темы, все платформы. Это требования релиза, а не полировка. Агент должен считать их частью definition of done каждой страницы.

Функция «где я?» — обязательный первый кирпич

Одна и та же страница у нас открывается на iOS, Android, десктопе — и иногда просто в браузере (пользователь скопировал ссылку). CSS и поведение различаются во всех четырёх случаях, поэтому первым в проекте появился вот такой помощник:

```js

function whereAmI() {

const tg = window.Telegram?.WebApp;

const inMiniApp = !!tg \&\& tg.initData !== ''; // пустой initData = обычный браузер

const platform = tg?.platform ?? 'browser'; // ios | android | tdesktop | macos | weba | ...

const isMobile = platform === 'ios' || platform === 'android';

return { inMiniApp, platform, isMobile };

}

// дальше — класс на body и ветвление поведения

document.body.classList.add(`plt-${platform}`, inMiniApp ? 'in-tg' : 'in-browser');

```

Где это стреляет на практике:

  • CSS. Отступы под шапку/низ, safe area на iOS, реакция на клавиатуру на Android — всё вешается на классы `plt-\*`.

  • Редиректы. После OAuth-авторизации у Google мобильного пользователя надо вернуть в Telegram диплинком `tg://`, а десктопного — обычным `https`-редиректом на страницу. Перепутаешь — получишь либо зависший браузер, либо выброшенного из миниаппа пользователя.

  • Заглушка для «не-Telegram». Если `initData` пуст — это не miniapp, и вместо приложения показываем страницу «откройте через бота».

Мелочь? Мелочь. Но без неё каждая страница превращается в поле чудес.

Лайфхак: туннель с первого дня

Telegram не умеет «localhost»: и webhook бота, и URL miniapp-а должны быть публичными HTTPS-адресами. Поэтому первым инструментом в проекте — раньше базы, раньше CI — должен стать туннель: ngrok, cloudflared, dev tunnels в Visual Studio — что угодно, дающее постоянный публичный адрес на вашу локальную машину.

Рабочий цикл получается такой: бот и miniapp настроены на адрес туннеля, код правится локально с hot reload, а проверяешь — в настоящем Telegram на настоящем телефоне, сразу. Без туннеля каждая итерация — это деплой; с туннелем — секунды. Помните про количество сценариев, которое съело четыре недели? Без туннеля это были бы не недели.

Вердикт: Telegram как платформа для продуктивити-приложений

Что дёшево — неприлично дёшево:

  • Дистрибуция. Продукт распространяется пересылкой кнопки в чат. Ноль установок, ноль лендингов на старте: собеседник видит кнопку — собеседник уже пользователь.

  • Аутентификация из коробки. Telegram сам подписывает данные пользователя и отдаёт их miniapp-у. Не нужно ни регистрации, ни паролей, ни «войти через Google» — нужно только правильно проверить подпись (об этом — вся вторая статья).

  • Inline-механика. Бот работает в любом чате, даже там, где его нет в участниках. Это и есть «мост»: продукт живёт внутри чужих разговоров, а не ждёт пользователей у себя.

  • Realtime — работает, и его нужно делать. Мы сомневались, взлетит ли SignalR (WebSocket) внутри telegram-webview. Взлетел: полноценный хаб с группами живёт в бою на всех платформах. Обновление статуса звонка прилетает в miniapp мгновенно, без перезагрузок. Fallback на polling мы держим — но как страховку, а не как основной канал. Если ваш агент предлагает «давай просто поллинг каждую секунду» — не соглашайтесь, соединение честно работает.

Что больно:

  • Зоопарк клиентов. iOS, Android, Desktop, web — четыре разных Telegram с разным webview. Отсюда правило №9 и функция «где я?».

  • Веб-движок с характером. Весь блок А выше — это его портрет.

  • Требования каталогов. Языки и темы — не опция. Кто узнаёт об этом в конце разработки, тот переписывает вёрстку.

  • Из скучного: приём апдейтов от Telegram у нас переключается флагом между webhook и polling — удобно для отладки, и это всё, что стоит знать об этой теме.

Итого: если ваш продукт — про коммуникацию, координацию или «сделать что-то не выходя из чата», Telegram даёт фору любой платформе. Плата за это — дисциплина по отношению к webview. Дисциплину можно делегировать: просто отдайте агенту правила из этой статьи.

Что дальше в серии

Часть 2 — про самое неочевидное. Инлайн-кнопка в чате — одна на всех участников, и открывает она просто ссылку. Как передать в miniapp параметры конкретного звонка? Как понять, кто именно нажал, и не дать себя обмануть? Как вернуть ответ из miniapp обратно в чат? И отдельная ловушка: часть флоу неизбежно уходит во внешний браузер (например, OAuth-авторизация у Google) — а там переменные miniapp не работают вовсе, и из контекста Telegram остаётся только то, что вы сами положили в URL. Расскажу про механизм сессий, который закрывает всё это разом — причём это не трюк для проброса параметров, а полноценный серверный аналог сессии: типизированное хранилище значений, на котором держится вся серверная логика звонка. Бонус: как редактировать сообщения в чате асинхронной очередью и не словить бан от Bot API.

Часть 3 — про фокусы. Как заставить бота «звонить» — с повторными уведомлениями, как настоящий входящий вызов (спойлер: удаляем и переотправляем сообщение). Почему ссылки нужно готовить заранее — и это не про классическое «пре-генерите URL»: в обычном вебе вы кликаете, сервер обрабатывает запрос, формирует ссылку и делает переадресацию — в Telegram эта схема не работает в принципе, редиректа нет, и всю «нагрузку редиректа» приходится вычислять заранее, до клика. Как провести пользователя по продукту JSON-файлом вместо онбординг-движка: скрипт-чат, который печатает, показывает слайды и пишет воронку в аналитику.


Всё описанное написано для Telegram, но в большей части справедливо и для MAX — механика ботов, miniapp и сессий там строится по тем же принципам. Если есть желание сделать адаптацию под MAX и российские сервисы видеовстреч — велком в личку.

Потрогать сам мост: @GoosleeBot · gooslibot.com

UPD:

Часть 2: Инлайн-кнопка одна на всех: параметры, аутентификация и обратная связь в Telegram miniapp - 24.08.26 15:22 | Пикабу

Показать полностью 4
Отличная работа, все прочитано!

Темы

Политика

Теги

Популярные авторы

Сообщества

18+

Теги

Популярные авторы

Сообщества

Игры

Теги

Популярные авторы

Сообщества

Юмор

Теги

Популярные авторы

Сообщества

Отношения

Теги

Популярные авторы

Сообщества

Здоровье

Теги

Популярные авторы

Сообщества

Путешествия

Теги

Популярные авторы

Сообщества

Спорт

Теги

Популярные авторы

Сообщества

Хобби

Теги

Популярные авторы

Сообщества

Сервис

Теги

Популярные авторы

Сообщества

Природа

Теги

Популярные авторы

Сообщества

Бизнес

Теги

Популярные авторы

Сообщества

Транспорт

Теги

Популярные авторы

Сообщества

Общение

Теги

Популярные авторы

Сообщества

Юриспруденция

Теги

Популярные авторы

Сообщества

Наука

Теги

Популярные авторы

Сообщества

IT

Теги

Популярные авторы

Сообщества

Животные

Теги

Популярные авторы

Сообщества

Кино и сериалы

Теги

Популярные авторы

Сообщества

Экономика

Теги

Популярные авторы

Сообщества

Кулинария

Теги

Популярные авторы

Сообщества

История

Теги

Популярные авторы

Сообщества

Недвижимость и ремонт

Теги

Популярные авторы

Сообщества