Вообще мы в Voximplant занимаемся голосовой автоматикой. Принять звонок с сотового телефона, запустить JavaScript сценарий клиента – там пара тысяч строк логики что с этим звонком делать, включая исходящие на веб-браузеры и мобильные приложения – вот эта вся история. Отдельная история – видео. Образование, телемедицина, совещания. Тот же SIP, те же кодеки, только данных передается в сотню раз больше. При этом браузеры любят зависать и падать, когда им не нравится полученный с другой стороны WebRTC SDP пакет, а мы их от этого отучаем.
Но, как оказалось, голоса и видео клиентам мало: “Мы не хотим сами писать чатик, уже два раза все переписывали и три питониста уволилось. Дайте нам стек unified communications, чтобы всё было на одной платформе, и мы не парились”. И на прошлой неделе мы стали этим самым “Unified Communications”. Теперь на нас можно собрать “Skype for Web” целиком: не только голосовые и видеозвонки, но и обмен сообщениями. Под катом я хочу показать как работает сделанный нами мессаджинг и, надеюсь, получить от вас фидбек – насколько хорошим получилось API и все ли с помощью него можно сделать?
Как мы организовали API
Хорошее API должно позволять быстро собирать аналог Skype/Telegram, давать ответ на вопрос “Что будет, если устройство залогинилось через месяц, а там миллион сообщений?” и иметь какие-то механизмы, чтобы этот миллион сообщений куда-то прятать при закрытии приложения, а потом быстро показывать. Плюс было бы здорово, чтобы пользователь мог подключаться с разных устройств и как-то с этим жить.
Наше API построено вокруг объектов “Conversation”, которые собирают в одном месте пользователей и сообщения. С помощью Conversation можно отправить сообщение пользователю, сделать групповой чат или аналог “каналов” в Telegram. Conversation создаются с помощью метода createConversation (к моменту вызова метода SDK нужно подключить к облаку Voximplant и залогиниться):
Всем пользователям, которые указаны в списке участников, придет эвент CreateConversation, на который нужно подписаться. Пользователю, который создал Conversation, придет точно такой же эвент. То есть метод createConversation ничего не возвращает: SDK связывается с сервером, там создается Conversation, его описание рассылается всем подключенным SDK, которые залогинены с участниками этого Сonversation, и в этих SDK приходит эвент.
Точно так же организована отправка сообщений: в объекте Сonversation есть метод sendMessage и возможность подписаться на эвент SendMessage, который придет всем участникам этого Conversation, включая отправителя:
Обратите внимание, что вызов createConversation каждый раз создает новый объект. На практике при обмене сообщениями между двумя пользователями мы хотим, чтобы каждый раз это был один и тот же объект со всей историей сообщений. Для такого поведения у нас есть флаг distinct: если установить его для Сonversation, то повторная попытка создать distinct conversation с тем же набором участников и их флагов вернет id уже созданной.
Что делать, если пользователь offline?
Вы когда-нибудь видели, что происходит с мобильным Skype, когда его запускают на новом устройстве после длительного перерыва в использовании? Бедное приложение просто разрывает на части от количества эвентов, и оно надолго задумывается над тем, как это все отобразить.
Чтобы решить эту проблему, мы последовательно нумеруем все эвенты для Сonversation, которые приходят пользователю. Если пользователь был оффлайн, а затем вернулся в онлайн — сервер не будет пытаться отсывать ему все пять тысяч эвентов, которые случились за время отсутствия. Вместо этого клиент сам получает актуальный список своих Сonversation (могут появиться новые) с помощью getUser, а затем для каждого Сonversation сравнивает сохраненный у себя и полученный от сервера lastSeq. Если значения расходятся, можно смело вызывать retransmitEvents и получать эвенты, которые случились, пока клиент был оффлайн. Метод принимает диапазон sequence, что позволяет в случае особо большого их количества (запуск клиента на новом устройстве) забирать их небольшими порциями. Эти же sequence используются для отслеживания количества непрочитанных сообщений.
Встроенное кеширование
Пользователь ожидает, что запущенный мессенджер быстро стартует и быстро покажет каналы и сообщения. Чтобы при каждом запуске веб или мобильного приложения не запрашивать у сервера сообщения, их вместе с Conversations можно и нужно хранить локально. Для этого мы предусмотрели API для кеширования: объекты Conversation и Message имеют методы toCache, а объет Messenger позволяет восстанавливать их из кэша с помощью createConversationFromCache и createMessageFromCache.
Планы на будущее
Это первая версия Messaging API и дальнейшее его развитие зависит от того, что будут хотеть наши пользователи. Какие идеи есть на сейчас:
- HTTP API и хуки, чтобы можно было делать сложные решения. С помощью текущего API можно сделать аналог “Skype for Web” или Telegram без ботов, где все крутится вокруг пользователей и их сообщений. А вот сделать что-нибудь вроде “Живосайта”, где посетители могут общаться с операторами на страницах сайта, сейчас намного труднее: придется использовать один аккаунт для сайта, один аккаунт для всех операторов и через собственный backend координировать создание множества conversations.
- Внимательно слушать отзывы от early adopters и вносить изменения, делающие использование API лучше. Присоединяйтесь, до 1000 пользователей бесплатно!
Автор: Grigory Petrov