| Мы стараемся поддерживать информацию в данной статье в актуальном состоянии. Все последние обновления для работы с сервисами TWIN в первую очередь публикуются на ресурсе https://developers.twin24.ai. |
| Команда TWIN постоянно совершенствуем свои сервисы. В связи с этим возможно внесение изменений в наше API. Просим вас учитывать этот факт при реализации интеграции чат-платформы TWIN с вашими приложениями. Важно обратить на это внимание при согласовании гарантийных обязательств. |
В дальнейшем интеграцию чат-платформу TWIN планируется осуществлять с помощью SDK (находиться в разработке). Это позволит производить процесс интеграции быстрее и обеспечит более высокую стабильность работы. |
Оглавление
Интеграция чат-платформы TWIN в приложение (кодовую базу) клиента идёт на стороне клиента.
Возможность интеграции и её срок определяется специалистами на стороне клиента.
Со своей стороны TWIN предоставляет необходимую документацию по методам подключения и взаимодействия платформы с приложением заказчика.
Для обеспечения двухсторонней связи используется библиотека Socket.IO, построенная на основе протокола WebSocket.
Интеграция осуществляется в несколько простых шагов:
Ниже детально описаны все перечисленные этапы с примерами кода на языке python версии 3.11.
Для примера интеграции мы рассмотрим простую программу. Суть и логика программы: запускается эхо-бот, который повторяет введенное в терминале пользователем сообщение. Завершение эхо-чата происходит по ключевому слову "стоп".

Пример отображения диалога в виджете. Здесь в качестве демонстрации показано как бот при старте диалога отправляет картинку. Также продемонстрирована возможность отправки файла в чат.

Для корректной работы описанной эхо-программы, рекомендуем установить следующие зависимости:
aiohttp==3.8.4 aiosignal==1.3.1 async-timeout==4.0.2 attrs==23.1.0 bidict==0.22.1 certifi==2023.5.7 charset-normalizer==3.1.0 frozenlist==1.3.3 idna==3.4 multidict==6.0.4 python-engineio==3.14.2 python-socketio==4.6.1 requests==2.31.0 six==1.16.0 urllib3==2.0.3 websocket-client==1.6.0 yarl==1.9.2 |
Метод: POST
Authorization: No Auth
URL: https://tcl.twin24.ai/api/chats/v1/chats/{chat_id}/sessions?x_widget=1
Пример функции на языке python:
def start_chat_session(chat_id: str, name: str = "integration_example") -> dict:
"""
Создаёт чат-сессию и возвращает коллекцию данных о ней.
:param chat_id: Идентификатор чата.
:param name: Имя чат-сессии. Задается для облегчения поиска чат-сессии в личном кабинете TWIN.
:return: Коллекция данных о созданной чат-сессии.
"""
url = f"https://tcl.twin24.ai/api/chats/v1/chats/{chat_id}/sessions?x_widget=1"
headers = {"Content-Type": "application/json"}
payload = json.dumps({"name": name})
response = requests.request("POST", url, headers=headers, data=payload)
return response.json() |
Описание параметров пути:
Поле | Тип | Обязательно | Описание |
|---|---|---|---|
chatId | string | Да | Идентификатор чата. Он определяет настройки чата и схему работы бота. |
Тело запроса
{
"name": "string",
"clientExternalId": "string",
"clientMetadata": {
"var1": "val1",
"var2": "val2",
"var3": "val3"
}
} |
Описание полей метода:
Поле | Тип | Обязательно | Описание |
|---|---|---|---|
name | string | Да | Имя сессии. |
clientExternalId | string | Нет | Определяемый пользователем идентификатор клиента, инициировавшего сеанс чата. |
clientMetadata | object | Нет | Любые определенные пользователем пары ключ/значение в качестве переменных бота. |
Ответы
Код 201
Description: Successful session creation
{
"id": "bce7d22e-dde6-4427-b391-ebbdfda44de6",
"clientId": "bce7d22e-dde6-4427-b391-ebbdfda44de6",
"startedAt": "2018-10-31T11:56:07+00:00",
"ttl": 3600,
"messages": [
{
"body": "string",
"answers": [
"string"
],
"actions": [
{
"key1": "value1",
"key2": "value2",
"key3": "value3"
}
],
"attachments": [
{
"id": "bce7d22e-dde6-4427-b391-ebbdfda44de6",
"isPrivate": true,
"createdAt": "2018-10-31T11:56:07+00:00",
"name": "bot.png",
"baseName": "bot",
"extension": "png",
"sugestedExtension": "png",
"path": "string",
"size": 12400,
"url": "string",
"downloadLink": "string"
}
]
}
]
} |
Описание полей ответа
Поле | Тип | Обязательно | Описание |
|---|---|---|---|
id | string | Да | Идентификатор чат-сессии. |
clientId | string | Идентификатор клиента. | |
startedAt | string | Отметка времени старта чат-сессии. | |
ttl | integer | Время жизни чат-сессии в секундах. | |
messages | array of objects | Массив данных о сообщении. | |
| body | string | Текс сообщения. | |
| answers | array of strings | Варианты ответов (кнопки с вариантами ответов). | |
| actions | array of objects | Информация о дополнительной функциональности в данном сообщении (например форма опроса). | |
| attachments | array of objects | Список идентификаторов вложений. | |
| | id | string | Идентификатор файла. | |
| | isPrivate | boolean | Отметка о том, что данное вложение приватно для текущего чата. | |
| | createdAt | string | Отметка времени о создании. | |
| | name | string | Полное имя файла (с расширением). | |
| | baseName | string | Имя файла. | |
| | extension | string | Расширение файла. | |
| | sugestedExtension | string | Предлагаемое расширение файла. | |
| | path | string | Путь (расположение) во внутреннем хранилище. | |
| | size | int64 | Размер файла в байтах. | |
| | url | string | Ссылка на файл во внутреннем хранилище. | |
| | downloadLink | string | Ссылка на скачивание файла. |
В успешном ответе содержится идентификатор чат-сессии. Именно этот параметр будет в дальнейшем использоваться для отправки сообщения в чат-сессию и подключения socket.io для "прослушивания" событий в данной чат-сессии.
Метод: POST
Authorization: No Auth
URL: https://chats-api.twin24.ai/api/v1/sessions/{session_id}/messages
Пример функции на языке python:
def send_msg_to_chat_session(session_id: str, message_text: str) -> dict:
"""
Отправляет сообщение.
:param session_id: Идентификатор чат-сессии.
:param message_text: Текст сообщения.
:return: Коллекция данных об отправленном сообщении.
"""
url = f"https://chats-api.twin24.ai/api/v1/sessions/{session_id}/messages"
headers = {'Content-Type': 'application/json'}
payload = json.dumps({"body": message_text, "attachments": []})
response = requests.request("POST", url, headers=headers, data=payload)
return response.json() |
Описание параметров пути:
Поле | Тип | Обязательно | Описание |
|---|---|---|---|
sessionId | string | Да | Идентификатор чат-сессии. |
Тело запроса
{
"body": "string",
"attachments": [
"bce7d22e-dde6-4427-b391-ebbdfda44de6"
],
"replyToMessageId": "string"
} |
Описание полей метода:
Поле | Тип | Обязательно | Описание |
|---|---|---|---|
body | string | Да | Текст сообщения |
attachments | array of strings | Нет | Список идентификаторов вложений (файлов), которые были прикреплены при отправке сообщения. |
replyToMessageId | string | Нет | Если сообщение является ответом, идентификатор исходного сообщения. |
Ответы
Код 201
Description: Successful message creation
{
"id": "bce7d22e-dde6-4427-b391-ebbdfda44de6",
"createdAt": "2018-10-31T11:56:07+00:00"
} |
Описание полей ответа
Поле | Тип | Обязательно | Описание |
|---|---|---|---|
id | string | Да | Идентификатор сообщения. |
createdAt | string | Да | Отметка даты и времени, когда было отправлено сообщение. |
Для отправки файлов в чат сессию для начала необходимо загрузить файл используя метод, описанный ниже.
Метод: POST
Authorization: No Auth
URL: https://tcl.twin24.ai/api/chats/v1/files?x_widget=1
Пример функции на языке python для отправки файла с расширением png:
def uploading_file_to_chat_session(file_name, file_path) -> dict:
"""
Функция загрузки файла изображения в хранилище TWIN.
:param file_name: Полное имя файла с расширением.
:param file_path: Путь к файлу.
:return: Массив данных о загруженном файле.
"""
url = "https://tcl.twin24.ai/api/chats/v1/files?x_widget=1"
payload = {}
files = [("file[]", (file_name, open(file_path, 'rb'), 'image/png'))]
headers = {}
response = requests.request("POST", url, headers=headers, data=payload, files=files)
return response.json() |
Примечание: данная функция демонстрирует загрузку изображений. Если необходимо отправить pdf-файл, необходимо указать в переменной file следующее:files = [("file[]", (file_name, open(file_path, 'rb'), 'application/pdf'))]
Ответы
Код 201
[
{
"id": "c419011c-a998-4189-b897-e82117e6a803",
"isPrivate": true,
"createdAt": "2022-06-26T15:44:32+00:00",
"contentType": "image/jpeg",
"name": "for_test.png",
"baseName": "for_test",
"extension": "png",
"suggestedExtension": "png",
"path": "",
"size": 18463,
"url": "string",
"downloadLink": "string",
"ownerId": null
}
] |
Описание полей ответа
Поле | Тип | Обязательно | Описание |
|---|---|---|---|
id | string | Да | Идентификатор файла. |
isPrivate | boolean | Да | Отметка о приватности. True если файл загружен пользователем или оператором, False - если ботом. |
name | string | Да | Полное имя файла (с расширением). |
baseName | string | Да | Имя файла. |
extension | string | Да | Расширение файла. |
| suggestedExtension | string | Да | Предлагаемое расширение файла. |
| path | string | Да | Путь (расположение) во внутреннем хранилище. |
| size | int64 | Да | Размер файла в байтах. |
| url | string | Да | Ссылка на файл во внутреннем хранилище. |
| downloadLink | string | Да | Ссылка на скачивание файла. |
ownerId | string | Да | ID владельца |
Далее, для отправки сообщения используется метод отправки сообщения в чат сессию (описан в начале текущего раздела). Важно, при отправке сообщения в котором прикреплён файл, указать в параметрах запроса attachments идентификатор(ы) файла(ов).
Socket.IO - это библиотека для создания приложений, работающих в режиме реального времени, имеющих двунаправленный канал связи и основанных на событиях. Более подробно ознакомиться с библиотекой можно на сайте официальной документации. |
Для взаимодействия с socket.io взята библиотека python-socketio версии 4.6.1 |
Протокол Socket.IO основан на событиях. Когда сервер хочет установить связь с клиентом, он создает событие. Каждое событие имеет имя и список аргументов.
Ниже представлены ключевые события, которые возникают в виджете и описаны параметры каждого события.
Данная информация носит справочный характер и позволяет определить, какие события необходимо инициировать для корректной интеграции чат-платформы в кодовую базу приложения на стороне клиента.
Процесс набора текста
[
"showTypingIndicatorEmit",
{
"authorType": "BOT"
}
] |
Описание параметров события showTypingIndicatorEmit
Поле | Тип | Обязательно | Описание |
|---|---|---|---|
authorType | string | Да | Тип автора. Определяет, кто отправил сообщение: бот или оператор. |
СООБЩЕНИЯ
Получение сообщения от бота
[
"chatMessageCreatedEmit",
{
"id": "d0f10a74-53fe-4a83-ac1f-ce55c0939df2",
"authorId": "4f8151ed-8e7a-4a12-a6a5-c94f99c58019",
"authorType": "BOT",
"authorName": "МетроБот",
"type": "REGULAR",
"body": "Переключаю на оператора",
"answers": [],
"createdAt": "2021-03-10T08:37:53+00:00",
"sessionId": "4d32dc5a-73d3-457e-9b30-5f7ff4c5fa24",
"attachments": [],
"actions": [],
"avatar": null
}
] |
Получение сообщения от оператора
[
"chatMessageCreatedEmit",
{
"id": "25d27d8f-f633-42ef-b359-aeff3ce05001",
"authorId": "389",
"authorType": "OPERATOR",
"authorName": "Первый Оператор V",
"type": "REGULAR",
"body": "\nHi",
"answers": [],
"createdAt": "2021-03-10T08:49:41+00:00",
"sessionId": "b1a69f04-a759-4376-b8e5-ee82a7e0d247",
"attachments": [],
"actions": [],
"avatar": {
"id": "ed661c07-2d50-4b1f-b2c8-32f09a83065e",
"isPrivate": false,
"createdAt": "2021-02-05T12:58:33+00:00",
"contentType": "image/jpeg",
"name": "мяч.jpg",
"baseName": "мяч",
"extension": "jpg",
"suggestedExtension": "jpg",
"path": "",
"size": 310723,
"url": "https://minio.twin24.ai/twin-iam-dev/public/2021-02-05/ed661c07-2d50-4b1f-b2c8-32f09a83065e?response-content-type=image%2Fjpeg&response-content-disposition=inline%3B%20filename%3Dmyac.jpg%3B%20filename%2A%3Dutf-8%27%27%25D0%25BC%25D1%258F%25D1%2587.jpg",
"downloadLink": "https://minio.twin24.ai/twin-iam-dev/public/2021-02-05/ed661c07-2d50-4b1f-b2c8-32f09a83065e?response-content-type=image%2Fjpeg&response-content-disposition=attachment%3B%20filename%3Dmyac.jpg%3B%20filename%2A%3Dutf-8%27%27%25D0%25BC%25D1%258F%25D1%2587.jpg&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=minio%2F20210310%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Date=20210310T084942Z&X-Amz-SignedHeaders=host&X-Amz-Expires=604799&X-Amz-Signature=8810cdb3f30b4ccff3c521e7db6ece5d4dfe5814fbecd36b9eed4ae65e99c6fb",
"ownerId": null
}
}
] |
Описание параметров события chatMessageCreatedEmit
Поле | Тип | Обязательно | Описание |
|---|---|---|---|
id | string | Да | Идентификатор сообщения. |
authorId | string | Идентификатор автора сообщения. | |
authorType | string | Тип автора. Определяет, кто отправил сообщение: бот или оператор. | |
authorName | string | Имя автора сообщения. | |
type | string | Тип сообщения. Может быть:REGULAR - обычное сообщение;TERMINAL - последнее (завершающее) сообщение в сессии. После этого сообщения сессия закрывается.HELP - запрос помощи оператора. | |
body | string | Текст сообщения. | |
answers | array of objects | Варианты ответов (кнопки с вариантами ответов). | |
createdAt | string | Отметка времени о созданном сообщении. | |
sessionId | string | Идентификатор чат-сессии. | |
attachments | array of objects | Список идентификаторов вложений. | |
actions | string | Информация о дополнительной функциональности в данном сообщении (например форма опроса). | |
avatar | string | Аватар бота или оператора. Массив данных. | |
| | id | string | Идентификатор файла. | |
| | isPrivate | boolean | False - бот, True - оператор | |
| | createdAt | string | Отметка времени о создании. | |
| | contentType | string | Content type. Тип контента (тип передачи файла). | |
| | name | string | Полное имя файла (с расширением). | |
| | baseName | string | Имя файла. | |
| | extension | string | Расширение файла. | |
| | suggestedExtension | string | Предлагаемое расширение файла. | |
| | path | string | Путь (расположение) во внутреннем хранилище. | |
| | size | string | Размер файла в байтах. | |
| | url | string | Ссылка на файл во внутреннем хранилище. | |
| | downloadLink | string | Ссылка на скачивание. | |
| | ownerId | string | Идентификатор владельца. |
Подтверждение о прочтении сообщения
[
"chatMessageReadEmit",
{
"messageId": "23280e7d-8d2f-409b-9480-82c805fcbb95"
}
] |
Описание параметров события chatMessageReadEmit
Поле | Тип | Обязательно | Описание |
|---|---|---|---|
messageId | string | Да | Идентификатор сообщения |
ОПЕРАТОРЫ
Назначение оператора после бота (первичное назначение)
[
"operatorChangedEmit",
{
"operatorId": "494",
"operatorName": "III Operator",
"avatar": null,
"previousOperatorId": "4f8151ed-8e7a-4a12-a6a5-c94f99c58019",
"previousOperatorName": "МетроБот",
"previousOperatorAvatar": null
}
] |
Назначение другого оператора (смена операторов)
[
"operatorChangedEmit",
{
"operatorId": "472",
"operatorName": "II Dev",
"avatar": null,
"previousOperatorId": "389",
"previousOperatorName": "Первый Оператор V",
"previousOperatorAvatar": {
"id": "ed661c07-2d50-4b1f-b2c8-32f09a83065e",
"isPrivate": false,
"createdAt": "2021-02-05T12:58:33+00:00",
"contentType": "image/jpeg",
"name": "мяч.jpg",
"baseName": "мяч",
"extension": "jpg",
"suggestedExtension": "jpg",
"path": "",
"size": 310723,
"url": "https://minio.twin24.ai/twin-iam-dev/public/2021-02-05/ed661c07-2d50-4b1f-b2c8-32f09a83065e?response-content-type=image%2Fjpeg&response-content-disposition=inline%3B%20filename%3Dmyac.jpg%3B%20filename%2A%3Dutf-8%27%27%25D0%25BC%25D1%258F%25D1%2587.jpg",
"downloadLink": "https://minio.twin24.ai/twin-iam-dev/public/2021-02-05/ed661c07-2d50-4b1f-b2c8-32f09a83065e?response-content-type=image%2Fjpeg&response-content-disposition=attachment%3B%20filename%3Dmyac.jpg%3B%20filename%2A%3Dutf-8%27%27%25D0%25BC%25D1%258F%25D1%2587.jpg&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=minio%2F20210310%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Date=20210310T090557Z&X-Amz-SignedHeaders=host&X-Amz-Expires=604799&X-Amz-Signature=6156df5e15d5ab535fdcf80204de440a3cfb8b8fa4d8e3a92b93cc6796f539a3",
"ownerId": null
}
}
] |
Описание параметров события chatMessageCreatedEmit
Поле | Тип | Обязательно | Описание |
|---|---|---|---|
id | string | Да | Идентификатор сообщения. |
operatorId | string | Идентификатор оператора. | |
operatorName | string | Имя оператора. | |
avatar | string | Аватар оператора. | |
previousOperatorId | string | Идентификатор предыдущего оператора. | |
previousOperatorName | string | Имя предыдущего оператора. | |
previousOperatorAvatar | string | Аватар предыдущего оператора. | |
| | id | string | Идентификатор файла. | |
| | isPrivate | boolean | Отметка о приватности. | |
| | createdAt | string | Отметка времени о создании. | |
| | contentType | string | Content type. Тип контента (тип передачи файла). | |
| | name | string | Полное имя файла (с расширением). | |
| | baseName | string | Предлагаемое расширение файла. | |
| | extension | string | Расширение файла. | |
| | suggestedExtension | string | Предлагаемое расширение файла. | |
| | path | string | Путь (расположение) во внутреннем хранилище. | |
| | size | string | Размер файла в байтах. | |
| | url | string | Ссылка на файл во внутреннем хранилище. |
Статус оператора
[
"operatorStatusChangedEmit",
{
"operatorId": "389",
"previousStatus": "ACTIVE",
"currentStatus": "PAUSED"
}
] |
Описание параметров события operatorStatusChangedEmit
Поле | Тип | Обязательно | Описание |
|---|---|---|---|
operatorId | string | Да | Идентификатор оператора |
previousStatus | string | Да | Предыдущий статус оператора |
currentStatus | string | Да | Текущий статус оператора |
Выход оператора из системы
[
"operatorLoggedOutEmit",
{
"operatorId": "389"
}
] |
Описание параметров события operatorStatusChangedEmit
Поле | Тип | Обязательно | Описание |
|---|---|---|---|
operatorId | string | Да | Идентификатор оператора |
Создание клиентского экземпляра
import socketio # Создаем экземпляр клиента socketio sio = socketio.Client() |
Определение обработчиков событий
Регистрируем функцию обработчика событий с помощью декоратора socketio.Client.on() указывая имя события. Также после отправки сообщения, инициируем событие messageReceived - подтверждение прочтения сообщения.
# Обработка события получения сообщения.
@sio.on("chatMessageCreatedEmit")
def msg_handler(data: dict):
"""
Обработка сообщений и общение с эхо-ботом.
:param data: Массив данных с параметрами сообщения.
"""
# Вывод в терминал
print(f"{data['authorType']}: {data['body']}")
# Подтверждение о прочтении сообщения
response = {"action": "chatMessageCreated", "data": {"id": data["id"]}}
sio.emit('messageReceived', response)
# Завершение диалога после введения ключевого слова в терминале
if data['type'] == 'TERMINAL':
globals()['FLAG'] = False |
Подключаемся к серверу используя TLS/SSL подключение.
URL для подключения к серверу: https://tcl.twin24.ai/operator/socket.io/?key={session_id}
Параметр session_id предварительно был получен при вызове функции start_chat_session
sio.connect(url=f'https://tcl.twin24.ai/operator/socket.io/?key={chat_session_id}',
transports=['polling', 'websocket'],
socketio_path='operator/socket.io') |
После этого шага, всё готово для того чтобы "слушать" происходящие события в указанной чат-сессии. Можно отправлять сообщения и в случае завершения диалога по ключевому слову закрывать соединение.
Ниже представлена кодовая база всей программы:
# Импортируем все необходимые библиотеки
import json
from datetime import datetime
from time import sleep
import requests
import socketio
# Указываем идентификатор чата (получить идентификатор необходимо в личном кабинете)
# Важно: необходимо указывать свои параметры идентификатора чата! Это пример!
CHAT_ID = "3eb5cf6d-4d20-41c2-b886-f60374d11d19"
# Создаем экземпляр клиента socketio
sio = socketio.Client()
FLAG = True
# Определяем функции, необходимые для работы программы
def start_chat_session(chat_id: str, name: str = "integration_example") -> dict:
"""
Создаёт чат-сессию и возвращает коллекцию данных о ней.
:param chat_id: Идентификатор чата.
:param name: Имя чат-сессии. Задается для облегчения поиска чат-сессии в личном кабинете TWIN.
:return: Коллекция данных о созданной чат-сессии.
"""
url = f"https://tcl.twin24.ai/api/chats/v1/chats/{chat_id}/sessions?x_widget=1"
headers = {"Content-Type": "application/json"}
payload = json.dumps({"name": name})
response = requests.request("POST", url, headers=headers, data=payload)
return response.json()
def send_msg_to_chat_session(session_id: str, message_text: str) -> dict:
"""
Отправляет сообщение.
:param session_id: Идентификатор чат-сессии.
:param message_text: Текст сообщения.
:return: Коллекция данных об отправленном сообщении.
"""
url = f"https://chats-api.twin24.ai/api/v1/sessions/{session_id}/messages"
headers = {'Content-Type': 'application/json'}
payload = json.dumps({"body": message_text, "attachments": []})
response = requests.request("POST", url, headers=headers, data=payload)
return response.json()
@sio.event
def connect():
pass
@sio.event
def connect_error(data):
print(f"The connection failed!\n{data}")
@sio.event
def disconnect():
pass
# Обработка события получения сообщения.
@sio.on("chatMessageCreatedEmit")
def msg_handler(data: dict):
"""
Обработка сообщений и общение с эхо-ботом.
:param data: Массив данных с параметрами сообщения.
"""
# Вывод в терминал
print(f"{data['authorType']}: {data['body']}")
# Подтверждение о прочтении сообщения
response = {"action": "chatMessageCreated", "data": {"id": data["id"]}}
sio.emit('messageReceived', response)
# Завершение диалога после введения ключевого слова в терминале
if data['type'] == 'TERMINAL':
globals()['FLAG'] = False
if __name__ == '__main__':
# Отметка времени
test_time = datetime.now().strftime("%d.%m.%Y_%H.%M.%S")
# Старт чат-сессии. Получение результатов
response_data = start_chat_session(CHAT_ID, f'Test: {test_time}')
# Получаем ID чат-сессии
chat_session_id = response_data['id']
# Блок диалога с эхо-ботом
msgs = response_data['messages']
for msg in msgs:
print(f"{msg['authorType']}: {msg['body']}")
sio.connect(url=f'https://tcl.twin24.ai/operator/socket.io/?key={chat_session_id}',
transports=['polling', 'websocket'],
socketio_path='operator/socket.io')
while FLAG:
send_msg_to_chat_session(chat_session_id, input('USER: '))
sleep(2)
sio.disconnect()
|
Это минимальная программа, отображающая все шаги, необходимые для интеграции чат-платформы TWIN в кодовую базу приложения клиента.