# Интеграция звонков в корпоративный мессенджер

Сценарий: сотрудники общаются в корпоративном мессенджере (Telegram/Slack/Teams/
Matrix/свой веб-мессенджер), и прямо из чата могут начать звонок — **1:1 (P2P)**
или **групповой** в беседе. Чат остаётся мессенджерным (наш встроенный чат можно
отключить), звонок встраивается как часть интерфейса, разворачивается на
полный экран или открывается в отдельной вкладке.

## 1. Модель

| Сценарий | Комната | Участники | Что делает мессенджер |
|---|---|---|---|
| Личный звонок 1:1 | `mode:'direct'`, `maxParticipants:2` | 2 | создаёт комнату, шлёт invite-ссылку собеседнику |
| Групповой звонок в беседе | `mode:'group'`, `context.conversationId` | N (mesh ≤8; SFU — roadmap) | создаёт комнату на беседу/встречу, шлёт карточку звонка |
| Звонок в канале/вебинаре | `mode:'group'` + `webinarMode` | N | карточка «подключиться» |

Комнаты создаются с контекстом:
```jsonc
POST /v1/admin/rooms
{
  "tenantId": "acme",
  "name": "Звонок с Анной",
  "mode": "direct",
  "context": { "messenger": "corp-chat", "conversationId": "dm-42", "directPeerId": "user-7", "title": "1:1" },
  "maxParticipants": 2
}
→ { room, joinUrl, embedUrl, passwordProtected }
```
`room.mode` и `room.context` возвращаются в `RoomInfo` (создание, join,
welcome, admin room detail) и уходят в вебхук `room.created`.

## 2. Поток «позвонить из чата»

```
Пользователь A нажимает «Позвонить» в чате компании
  → бэкенд компании: POST /v1/admin/rooms {mode:'direct', context:{conversationId,...}}
  → получает joinUrl/embedUrl (+ токен для A через POST /v1/tokens)
  → мессенджер рисует карточку звонка в переписке (joinUrl)
  → A открывает звонок: inline (iframe/SDK) или в новой вкладке
  → B получает карточку, открывает joinUrl → POST /v1/rooms/:id/join → звонок
```
Групповой: один `conversationId` → одна комната на беседу (или новая на каждую
встречу). Приглашения — `POST /v1/admin/rooms/:id/invites` (TTL), карточка в
чате; события `participant.joined/left`, `call.ended` (webhooks) обновляют
карточку/статус в мессенджере.

## 3. Встраивание и чат

- **Чат.** `session.chat.ui = 'hidden'` (или `'external'`) — внутренний чат
  выключен/заменён чатом мессенджера; сообщения звонка можно не показывать.
  Для гибрида: `providers.chat.mode='external'` — виджет не рисует чат, а
  компания шлёт/принимает сообщения через SDK/postMessage/API
  (`POST /v1/admin/rooms/:id/chat`), т.е. **чат остаётся один — мессенджерный**.
- **Как часть страницы:** `layout.chrome='none'` + `inheritHostStyles` — без
  своей шапки/фона, выглядит как элемент мессенджера; `layout.mode='grid'` или
  `'spotlight'` (для 1:1 — спотлайт).
- **Полный экран по кнопке:** `instance.requestFullscreen()` или кнопка в
  контролах; выход — `exitFullscreen()`/Esc.
- **Новая вкладка:** `instance.openInNewTab()` (использует `AGILCalls.createCallUrl`)
  или `window.open(joinUrl)`; состояние комнаты/токена передаётся в URL.
- **Свернуть в мини-окно:** `layout.minimized` + `minimize()` — звонок висит
  поверх мессенджера, пока пользователь листает чаты; в Electron — отдельное
  always-on-top окно (`docs/electron.md`).
- `persistAcrossPages`/`resume()` — переходы между страницами мессенджера.

## 4. Варианты по платформам

| Мессенджер | Как встроить | Замечания |
|---|---|---|
| Свой веб-мессенджер | SDK inline (`AGILCalls.mount`), чат — свой чат-компонент | рекомендуется: один чат, звонок часть страницы |
| Telegram | бот присылает inline-кнопку/Web App с `embedUrl`, «Открыть звонок» | Web App ограничивает iframe; лучше открывать в новой вкладке/браузере |
| Slack | Block Kit с кнопкой-ссылкой на `joinUrl`, Slack App Home с iframe | Slack iframe ограничен CSP; ссылка надёжнее |
| Microsoft Teams | Adaptive Card с `openUrl` на `joinUrl`; вкладка (tab) с iframe | проверьте CSP вашего тенанта |
| Matrix / Element | Widget API с `embedUrl`, событие в комнату | матричные виджеты поддерживают iframe |
| Корпоративный портал | SDK inline + `chrome:'none'` | нативный вид, мини-окно общее |

## 5. Безопасность

- Токен звонка выдаёт бэкенд компании (`POST /v1/tokens` с `permissions`);
  API-ключ никогда не попадает в клиент мессенджера.
- 1:1: токен комнаты только для двух участников, `maxParticipants:2`, TTL
  токена; при утечке ссылки — `lock`/`kick`/истечение инвайта.
- Группы: гео/пароли/лобби — по политике тенанта; `accessPolicy` ограничивает
  запись/файлы/ассистента по ролям (гость/участник/модератор).
- Карточки звонка содержат только `joinUrl` с токеном ограниченного TTL; для
  чувствительных бесед — режим «только приглашённые» (waiting room).
- Запись/STT/AI — только с согласием и по политике тенанта; уведомление
  участникам (`session.recording.notifyParticipants`).

## 6. Рекомендуемые настройки тенанта для мессенджера

```jsonc
{
  "layout": { "chrome": "none", "mode": "spotlight", "minimized": { "enabled": true, "mode": "bubble" } },
  "session": {
    "chat": { "ui": "hidden" },                 // чат — мессенджерный
    "recording": { "consentRequired": true },
    "fileShare": { "enabled": true }
  },
  "providers": {
    "chat": { "mode": "external", "providerName": "corp-messenger", "endpoint": "https://chat.company/agil/chat", "apiKeyEnv": "AGIL_CHAT_PROVIDER_KEY", "fallback": "builtin" }
  },
  "features": { "chat": false, "knowledgeAgent": true, "recording": false }
}
```

## 7. Подводные камни

- **CSP/iframe:** мессенджеры часто запрещают iframe — используйте отдельную
  вкладку (`openInNewTab`) или SDK на своей странице.
- **Autoplay** в iframe требует user gesture — начинайте звонок по клику
  («Позвонить»), а не автоматически.
- **Уведомления о звонке** (входящий) — это сценарий мессенджера (push/call
  card); наш сервер даёт события, но не звонит на устройство (CallKit/Web Push
  — roadmap).
- **Групповые >8** — mesh не годится, нужен SFU (roadmap, LiveKit).
- **Мобильные вкладки** ограничивают одновременные камеры — для 1:1 хватает.
