---
title: "Обновления вживую: меняйте сообщения mssgs после публикации"
description: "Обновляйте или удаляйте сообщение mssgs через его callback URL, показывайте ход работы индикатором загрузки и слушайте ответы, реакции и нажатия в живом потоке."
canonical: https://mss.gs/ru/docs/live-updates
language: ru
---

# Обновляйте сообщения вживую

Сообщение не обязано оставаться таким, каким его опубликовали. Показывайте ход работы, пока идёт задача, заменяйте индикатор загрузки результатом, убирайте кнопки, когда решение принято, или удаляйте сообщение. Все в канале сразу видят изменение.

## Что можно сделать

- **Обновляйте карточку** Меняйте текст, цвет и кнопки прямо на месте.

- **Показывайте ход работы** Индикатор загрузки, который проходит по шагам, а затем результат.

- **Удаляйте** Убирайте сообщение, когда оно перестало быть актуальным.

- **Слушайте** Ответы, реакции и нажатия кнопок на вашем сообщении, вживую.

В приложении

Опубликовано с индикатором загрузки

Обновлено: шаг 2 из 3

#### Деплой завершён

Обновлено: готово, с кнопкой

Одно сообщение, дважды обновлённое через callback URL. Никто не видит три сообщения, только одно, которое меняется.

- [Быстрый старт](#quick-start)

- [Обновление](#update)

- [Ход работы](#progress)

- [Удаление](#delete)

- [Прослушивание](#stream)

## Быстрый старт

### Сохраните callback URL

Каждая публикация через [вебхук](https://mss.gs/ru/docs/webhooks) и каждый запрос [команды](https://mss.gs/ru/docs/commands) приходят с callback_url для этого сообщения.

```bash
BODY='{"message_container": {"color": "blue", "loader": true, "loader_text": "Deploying…"}}'
SIG=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$MSSGS_WEBHOOK_SECRET" | sed 's/^.* //')

curl -X POST "$MSSGS_WEBHOOK_URL" -H "Content-Type: application/json" \
  -H "X-Mssgs-Signature: sha256=$SIG" -d "$BODY"

# {"success": true, "message_id": "...", "callback_url": "https://mss.gs/api/v1/trigger-callback/8f14e45f-..."}
```

### PUT, чтобы обновить

Отправьте новое состояние. Карточка изменится на месте для всех.

```bash
curl -X PUT "$CALLBACK_URL" -H "Content-Type: application/json" \
  -d '{"message_container": {"color": "green", "title": "Deploy complete", "description": "v2.1 is live."}}'
```

### DELETE, чтобы удалить

Тело не нужно.

```bash
curl -X DELETE "$CALLBACK_URL"
```

## Обновление сообщения

Отправьте JSON методом PUT на callback_url . Передавайте только то, что хотите изменить.

| Поле | Тип | Что делает |
| --- | --- | --- |
| message_container | object | Новая карточка. См. [карточки сообщений](https://mss.gs/ru/docs/bots). |
| actions | array | Новые кнопки. "actions": [] убирает их все; если не передать поле, кнопки останутся. |
| content | string | Новый текст. |
| title , description , color , loader , ... | string | Сокращение: поля карточки на верхнем уровне автоматически оборачиваются в карточку. |

### Ответы

| Статус | Код | Что означает |
| --- | --- | --- |
| 200 | {"success": true} | Принято. Обновление последует сразу же. |
| 400 | MISSING_FIELDS | В теле нечего обновлять. |
| 400 | INVALID_BODY | Тело не является корректным JSON. |
| 400 | код ошибки кнопки | Что-то не так с кнопкой, см. [кнопки](https://mss.gs/ru/docs/buttons). |
| 401 | INVALID_TOKEN | URL недействителен. |
| 404 | TOKEN_NOT_FOUND | Срок действия URL истёк, или через него уже удалили сообщение. |
| 502 | PUBLISH_FAILED | Обновление не удалось доставить. Повторите попытку. |

### Сколько это работает

30 минут с момента публикации сообщения или использования команды. Обновление этот срок не продлевает. После удаления сообщения URL больше не действует. Через обновление нельзя добавить файлы.

## Показ хода работы

Опубликуйте карточку с индикатором загрузки, обновляйте её дополнительную строку по мере продвижения задачи и закончите результатом. Индикатор загрузки представляет собой спиннер со строкой и строкой поменьше под ней ( loader_text , loader_sub_text ).

```javascript
const { callback_url } = await post({
  message_container: { color: 'blue', loader: true, loader_text: 'Deploying…', loader_sub_text: 'Step 1 of 3: building' }
});

const update = (body) => {
  return fetch(callback_url, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) });
};

await build();
await update({ message_container: { color: 'blue', loader: true, loader_text: 'Deploying…', loader_sub_text: 'Step 2 of 3: running migrations' } });

await migrate();
await update({
  message_container: { color: 'green', title: 'Deploy complete', description: 'v2.1 is live on production.' },
  actions: [{ type: 'url:https://ci.example.com/deploys/218', text: 'View logs', color: 'green' }]
});
```

## Удаление сообщения

Отправьте DELETE на callback_url , и сообщение исчезнет у всех. После этого URL использовать нельзя.

## Прослушивание сообщения

stream_url представляет собой живой поток (Server-Sent Events) того, что происходит с вашим сообщением. Откройте его, и события будут приходить по мере того, как они происходят, каждое в виде JSON-строки data: , в которой type говорит, что это за событие:

```sse
curl -N "$STREAM_URL"

data: {"type": "reaction", "message_id": "...", "emoji": ":tada:", "action": "add", "member_guid": "...", "member": {...}, "ts": 1790000000000}

data: {"type": "action", "message_id": "...", "action_id": "approve", "action": {"id": "approve", "payload": {"deploy": 218}, ...}, "member_guid": "...", "member": {...}, "ts": 1790000004200}

data: {"type": "reply", "message_id": "...", "content": "Ship it!", "member_guid": "...", "member": {...}, "ts": 1790000009800}

event: expired
data: {}
```

| Событие | Когда | Данные |
| --- | --- | --- |
| action | Нажата кнопка. | action_id и сохранённая кнопка в action с её payload |
| reaction | Реакция добавлена или удалена. | emoji и action : add, remove или removeall |
| reply | Кто-то ответил на сообщение. | content ответа |
| expired | Поток закрывается. Отправляется как именованное событие. | Нет |

### Прослушивание на JavaScript

```javascript
const events = new EventSource(streamUrl);

// Every event arrives as a plain message; its kind is in "type".
events.onmessage = (e) => {
  const ev = JSON.parse(e.data);
  if ((ev.type === 'action') && (ev.action_id === 'approve')) {
    startDeploy(ev.action.payload.deploy);
  }
};

// The one named event: the stream is closing.
events.addEventListener('expired', () => {
  events.close();
});
```

### Где его взять

| Откуда он берётся | Открыт в течение |
| --- | --- |
| Публикация через вебхук с кнопками | 10 минут или час с "sse_event_extended_timeout": true |
| Каждый запрос команды | 10 минут |

### Ошибки

| Статус | Код | Что означает |
| --- | --- | --- |
| 401 | INVALID_TOKEN | Неверный токен в URL. |
| 404 | NOT_FOUND | Срок действия потока истёк, или его никогда не было. |

## Что дальше
