Мы стараемся поддерживать информацию в данной статье в актуальном состоянии. Все последние обновления для работы с сервисами TWIN в первую очередь публикуются на ресурсе https://developers.twin24.ai
Команда TWIN постоянно совершенствуем свои сервисы. В связи с этим возможно внесение изменений в наше API. Просим вас учитывать этот факт при реализации интеграции чат-платформы TWIN с вашими приложениями. Важно обратить на это внимание при согласовании гарантийных обязательств.

В дальнейшем интеграцию чат-платформу TWIN планируется осуществлять с помощью SDK (находиться в разработке). Это позволит производить процесс интеграции быстрее и обеспечит более высокую стабильность работы. 


Оглавление



Информация об интеграции

Интеграция чат-платформы TWIN в приложение (кодовую базу) клиента идёт на стороне клиента. 

Возможность интеграции и её срок определяется специалистами на стороне клиента.

Со своей стороны TWIN предоставляет необходимую документацию по методам подключения и взаимодействия платформы с приложением заказчика.

Для обеспечения двухсторонней связи используется библиотека Socket.IO, построенная на основе протокола WebSocket.


Интеграция осуществляется в несколько простых шагов:

  1. Старт чат-сессии. Происходит при помощи API.
  2. Отправка сообщения в чат сессию. Происходит при помощи API.
  3. Получение сообщений, подтверждение получения сообщений - происходит с помощью socket.io. Важно: на каждое полученное сообщение необходимо отправить событие (emit) о прочтении сообщения.


Ниже детально описаны все перечисленные этапы с примерами кода на языке 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Расширение файла.
suggestedExtensionstringПредлагаемое расширение файла.
pathstringПуть (расположение) во внутреннем хранилище.
sizeint64Размер файла в байтах.
urlstringСсылка на файл во внутреннем хранилище.
downloadLinkstringСсылка на скачивание файла.
ownerId
stringID владельца


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



Подключение к сокетам

Socket.IO - это библиотека для создания приложений, работающих в режиме реального времени, имеющих двунаправленный канал связи и основанных на событиях. Более подробно ознакомиться с библиотекой можно на сайте официальной документации.

Для взаимодействия с socket.io взята библиотека python-socketio версии 4.6.1

Справочная информация о событиях socket.io для виджета чат-платформы TWIN

Протокол 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Аватар бота или оператора. Массив данных.
|  idstringИдентификатор файла.
|  isPrivatebooleanFalse - бот, True - оператор
|  createdAtstringОтметка времени о создании.
|  contentTypestringContent type. Тип контента (тип передачи файла).
|  namestringПолное имя файла (с расширением).
|  baseNamestringИмя файла.
|  extensionstringРасширение файла.
|  suggestedExtensionstringПредлагаемое расширение файла.
|  pathstringПуть (расположение) во внутреннем хранилище.
|  sizestringРазмер файла в байтах.
|  urlstringСсылка на файл во внутреннем хранилище.
|  downloadLinkstringСсылка на скачивание.
|  ownerIdstringИдентификатор владельца.


Подтверждение о прочтении сообщения

[
    "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Аватар предыдущего оператора.
|  idstringИдентификатор файла.
|  isPrivatebooleanОтметка о приватности.
|  createdAtstringОтметка времени о создании.
|  contentTypestringContent type. Тип контента (тип передачи файла).
|  namestringПолное имя файла (с расширением).
|  baseNamestringПредлагаемое расширение файла.
|  extensionstringРасширение файла.
|  suggestedExtensionstringПредлагаемое расширение файла.
|  pathstringПуть (расположение) во внутреннем хранилище.
|  sizestringРазмер файла в байтах.
|  urlstringСсылка на файл во внутреннем хранилище.


Статус оператора

[
    "operatorStatusChangedEmit",
    {
        "operatorId": "389",
        "previousStatus": "ACTIVE",
        "currentStatus": "PAUSED"
    }
]

Описание параметров события operatorStatusChangedEmit

Поле

Тип

Описание

operatorId
stringИдентификатор оператора
previousStatus
stringПредыдущий статус оператора
currentStatus
stringТекущий статус оператора


Выход оператора из системы

[
    "operatorLoggedOutEmit",
    {
        "operatorId": "389"
    }
]

Описание параметров события operatorStatusChangedEmit

Поле

Тип

Описание

operatorId
stringИдентификатор оператора


Подключение чат-сессии к socket.io

Создание клиентского экземпляра

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 в кодовую базу приложения клиента.