Для разработки своего решения для поддержки клиентов мы выбрали сервис Gmail, так как это один из самых популярных почтовых клиентов. А для расширения его возможностей — готовую библиотеку InboxSDK. На момент разработки она обладала нужным нам функционалом, и такое решение помогло нам быстро выйти на рынок с первой версией продукта и набрать пользовательскую базу. С другой стороны, InboxSDK — закрытая библиотека от стороннего разработчика и она обладала недостатками, которые в дальнейшем надо было решать.
Deskun реализован в виде расширения для Google Chrome и Яндекс.Браузера. Оно полностью функционирует на базе почтового сервиса Gmail. С нашей стороны требовалось выбрать библиотеку, которая могла бы расширить функционал почты и работать с её клиентской частью. Мы остановились на InboxSDK (ISDK).
Ключевая особенность Gmail, за которую его так любят пользователи, – это автоматическая группировка писем по диалогам. На внутреннем уровне (на серверах Gmail) каждое письмо имеет собственный идентификатор и идентификатор диалога, которому оно принадлежит. Но проблема в том, что в веб-клиенте глобальные ID сообщений не содержатся в коде страницы нигде, а глобальные ID диалогов содержатся только в хэше URL открытого диалога. Вместо этого HTML-элементы каждого сообщения и диалога отмечены локальными ID вида «:a5, :b6 и т.д.», получаемыми основным (достаточно большим и обфусцированным) скриптом Gmail. Так как локальные ID меняются от страницы к странице и от сессии к сессии, то для нормальной работы расширений, имеющих дело с почтой Gmail, необходимо оперативно устанавливать связь между локальными и глобальными ID. Этим и занимается библиотека InboxSDK, в которой, помимо функций для получения идентификаторов писем, черновиков и диалогов, доступны и возможности по улучшению пользовательского интерфейса.
Несмотря на то, что ISDK содержит в себе большое количество различных методов для работы с Gmail, у этой библиотеки есть одна неприятная особенность — ее разработкой занимается компания Streak, которая, в свою очередь, предоставляет собственную CRM-систему внутри Gmail. То есть является косвенным конкурентом Deskun.
Развивать собственный закрытый проект на основе другого закрытого проекта по меньшей мере неправильно. Особенно, если эти проекты похожи и вам всегда могут закрыть доступ к API просто потому, что вы конкуренты. Даже если такого не произойдёт, то вы минимум не имеете никакого контроля над изменениями и доступностью. Разработчики библиотеки не спешат помогать в написании сторонних сервисов на основе своей библиотеки, а ченджлоги новых ревизий не несут никакой полезной информации. Даже сайт InboxSDK практически не обновляется с момента запуска. А уж про исправление багов по запросу пользователей или добавление возможностей, которые не касаются потребностей самой Streak, можно забыть. В первую очередь Steak делает библиотеку для себя.
Первая проблема. Для работы библиотеки используется привязка к каким-то определенным и стабильным (стабильным — которые не меняются при переключении вида, смены состояния и т.п.) классам. Например, в некоторых случаях эмулируются нажатия клавиш на сфокусированных элементах. И любое изменение интерфейса Gmail может нарушить работу самой библиотеки и сторонних разработок на основе InboxSDK.
Вторая проблема. Так как библиотека подключается удаленно при помощи загрузчика, то возможны перебои и в доставке контента по сети CDN. Некоторое время мы пытались понять что идет не так, а потом выяснили, что библиотека, на основе которой работает наш плагин, просто не загружается у некоторых пользователей. В какой-то момент оказалось, что уже 10% наших клиентов не могли работать с сервисом. Если возникали проблемы с загрузкой InboxSDK, то прекращалась работа всех платформ, построенных на этой библиотеке. Важно понимать, что проблема может иметь точечный характер, и далеко не всегда может зависеть от хостинга. И с нашей стороны исправить источник проблемы может быть совершенно невозможно и оставалось только ждать.
Третья проблема. InboxSDK не является открытой библиотекой. К публично объявленным функциям относится максимум треть из всех доступных. Это примерно 400 Кб упакованного кода, который вынужден парситься буквально сразу после парсинга основных скриптов Gmail. Время загрузки mail.google.com на медленных машинах — критично.
Четвертая проблема. Изменения в библиотеку вносятся не просто регулярно, а несколько раз в неделю. К сожалению, большинство этих изменений не содержат багфиксы и не добавляют новые возможности, а направлены на нужды основного разработчика Streak. Была ситуация, когда из-за одного обновления произошел конфликт с кодом в клиентской части нашего плагина. В декабре для InboxSDK вышла новая минорная (теневая, без ченджлога) версия, в которой была изменена функция вставки получателей. Она была реализована таким образом, что после добавления адресатов стоял триггер на сохранение черновика. Само сохранение было сделано довольно интересным способом — это была просто симуляция нажатия определенной клавиши по полю ввода. До всех изменений нажатие было реализовано по «Enter», а вот именно в том изменении они начали нажимать символ с кодом «190» (?). У нас же в тикетах при открытии формы ответа автоматически подставляются получатели этого тикета. Таким образом, получился конфликт: во время ответа в тикете в поле получателей подставлялся символ ?.
Переход с InboxSDK на gmail.js
В итоге мы постепенно решили перейти на что-то более открытое, и библиотека Gmail.js (GJS) стала фундаментом для будущего решения. В отличие от InboxSDK, библиотека Gmail.js полностью открыта. Ее развитие зависит от сообщества и функционал может развиваться очень хорошими темпами.
При переходе мы столкнулись с двумя основными проблемами:
Во-первых, для функционирования библиотеки Gmail.js необходим доступ к объекту window страницы, в котором Gmail хранит некоторую техническую информацию. Расширение же в своей «песочнице» имеет доступ только к DOM страницы, но не к ее окружению.
Во-вторых, для того, чтобы полностью исключить изменения клиентской части плагина, необходима не просто обертка, но нужно еще дописать функции, которых нет в самой библиотеке.
Для первой проблемы нет какого-то идеального решения. Все они связаны с какими-то неудобствами – реализация через добавление своего скрипта непосредственно на страницу сайта
Мы решили реализовать мост, который, используя GJS, сообщает об определенных событиях непосредственно плагину. В текущей реализации через технологию инжекта в страницу внедряется библиотека GJS и наш мост. Таким образом GJS работает в нужном окружении. Для всех расширений браузер Google Chrome предоставляет некоторый набор методов для работы с ним самим. Соответственно, если делать расширение через прямой инжект (как библиотеку GJS), то она потеряет все возможности работы с API-расширений, поэтому может потеряться очень важный и нужный функционал.
Для решения второй проблемы необходимо было проанализировать поведение не только InboxSDK, но и саму работу клиентской части Gmail. Сейчас в нашем расширении есть места в коде, которые завязаны на атрибутах и классах инбокса: в некоторых случаях они дополнительно расширяли возможности библиотеки, а в некоторых они исправляли какие-то ошибки инбокса. Следовательно, чтобы не нарушить работу клиентской части плагина, необходимо было таким же образом добавлять нужные классы и атрибуты, добавлять их на определенные элементы, по определенным событиям и при определенных условиях. Уже ранее упоминалось, что нет какого-либо API для работы с клиентской частью Gmail. А ведь там хранится очень много нужной информации. Например, настройки пользователя, список доступных почтовых псевдонимов, набор включенных экспериментальных возможностей и прочая важная техническая информация.
Нам удалось совершить переход с одной библиотеки на другую, не изменяя при этом логику и код в клиентской части плагина. Конечное решение представляет собой не просто использование библиотеки GJS, скорее мы создали свою собственную библиотеку-гибрид ISDK и GJS. Фундаментом послужила Gmail.js, но функционально наша разработка похожа на InboxSDK. Если сообществу Хабрахабр будет интересно, и будет много просьб поделиться нашим кодом, то мы приведем его в порядок и выложим.
Почему мы выбрали InboxSDK?
Deskun реализован в виде расширения для Google Chrome и Яндекс.Браузера. Оно полностью функционирует на базе почтового сервиса Gmail. С нашей стороны требовалось выбрать библиотеку, которая могла бы расширить функционал почты и работать с её клиентской частью. Мы остановились на InboxSDK (ISDK).
Ключевая особенность Gmail, за которую его так любят пользователи, – это автоматическая группировка писем по диалогам. На внутреннем уровне (на серверах Gmail) каждое письмо имеет собственный идентификатор и идентификатор диалога, которому оно принадлежит. Но проблема в том, что в веб-клиенте глобальные ID сообщений не содержатся в коде страницы нигде, а глобальные ID диалогов содержатся только в хэше URL открытого диалога. Вместо этого HTML-элементы каждого сообщения и диалога отмечены локальными ID вида «:a5, :b6 и т.д.», получаемыми основным (достаточно большим и обфусцированным) скриптом Gmail. Так как локальные ID меняются от страницы к странице и от сессии к сессии, то для нормальной работы расширений, имеющих дело с почтой Gmail, необходимо оперативно устанавливать связь между локальными и глобальными ID. Этим и занимается библиотека InboxSDK, в которой, помимо функций для получения идентификаторов писем, черновиков и диалогов, доступны и возможности по улучшению пользовательского интерфейса.
Несмотря на то, что ISDK содержит в себе большое количество различных методов для работы с Gmail, у этой библиотеки есть одна неприятная особенность — ее разработкой занимается компания Streak, которая, в свою очередь, предоставляет собственную CRM-систему внутри Gmail. То есть является косвенным конкурентом Deskun.
Проблемы InboxSDK
Развивать собственный закрытый проект на основе другого закрытого проекта по меньшей мере неправильно. Особенно, если эти проекты похожи и вам всегда могут закрыть доступ к API просто потому, что вы конкуренты. Даже если такого не произойдёт, то вы минимум не имеете никакого контроля над изменениями и доступностью. Разработчики библиотеки не спешат помогать в написании сторонних сервисов на основе своей библиотеки, а ченджлоги новых ревизий не несут никакой полезной информации. Даже сайт InboxSDK практически не обновляется с момента запуска. А уж про исправление багов по запросу пользователей или добавление возможностей, которые не касаются потребностей самой Streak, можно забыть. В первую очередь Steak делает библиотеку для себя.
Первая проблема. Для работы библиотеки используется привязка к каким-то определенным и стабильным (стабильным — которые не меняются при переключении вида, смены состояния и т.п.) классам. Например, в некоторых случаях эмулируются нажатия клавиш на сфокусированных элементах. И любое изменение интерфейса Gmail может нарушить работу самой библиотеки и сторонних разработок на основе InboxSDK.
Вторая проблема. Так как библиотека подключается удаленно при помощи загрузчика, то возможны перебои и в доставке контента по сети CDN. Некоторое время мы пытались понять что идет не так, а потом выяснили, что библиотека, на основе которой работает наш плагин, просто не загружается у некоторых пользователей. В какой-то момент оказалось, что уже 10% наших клиентов не могли работать с сервисом. Если возникали проблемы с загрузкой InboxSDK, то прекращалась работа всех платформ, построенных на этой библиотеке. Важно понимать, что проблема может иметь точечный характер, и далеко не всегда может зависеть от хостинга. И с нашей стороны исправить источник проблемы может быть совершенно невозможно и оставалось только ждать.
Третья проблема. InboxSDK не является открытой библиотекой. К публично объявленным функциям относится максимум треть из всех доступных. Это примерно 400 Кб упакованного кода, который вынужден парситься буквально сразу после парсинга основных скриптов Gmail. Время загрузки mail.google.com на медленных машинах — критично.
Четвертая проблема. Изменения в библиотеку вносятся не просто регулярно, а несколько раз в неделю. К сожалению, большинство этих изменений не содержат багфиксы и не добавляют новые возможности, а направлены на нужды основного разработчика Streak. Была ситуация, когда из-за одного обновления произошел конфликт с кодом в клиентской части нашего плагина. В декабре для InboxSDK вышла новая минорная (теневая, без ченджлога) версия, в которой была изменена функция вставки получателей. Она была реализована таким образом, что после добавления адресатов стоял триггер на сохранение черновика. Само сохранение было сделано довольно интересным способом — это была просто симуляция нажатия определенной клавиши по полю ввода. До всех изменений нажатие было реализовано по «Enter», а вот именно в том изменении они начали нажимать символ с кодом «190» (?). У нас же в тикетах при открытии формы ответа автоматически подставляются получатели этого тикета. Таким образом, получился конфликт: во время ответа в тикете в поле получателей подставлялся символ ?.
Переход с InboxSDK на gmail.js
В итоге мы постепенно решили перейти на что-то более открытое, и библиотека Gmail.js (GJS) стала фундаментом для будущего решения. В отличие от InboxSDK, библиотека Gmail.js полностью открыта. Ее развитие зависит от сообщества и функционал может развиваться очень хорошими темпами.
При переходе мы столкнулись с двумя основными проблемами:
Во-первых, для функционирования библиотеки Gmail.js необходим доступ к объекту window страницы, в котором Gmail хранит некоторую техническую информацию. Расширение же в своей «песочнице» имеет доступ только к DOM страницы, но не к ее окружению.
Во-вторых, для того, чтобы полностью исключить изменения клиентской части плагина, необходима не просто обертка, но нужно еще дописать функции, которых нет в самой библиотеке.
Для первой проблемы нет какого-то идеального решения. Все они связаны с какими-то неудобствами – реализация через добавление своего скрипта непосредственно на страницу сайта
script
с postMessage, Custom Event или записью напрямую в DOM.Мы решили реализовать мост, который, используя GJS, сообщает об определенных событиях непосредственно плагину. В текущей реализации через технологию инжекта в страницу внедряется библиотека GJS и наш мост. Таким образом GJS работает в нужном окружении. Для всех расширений браузер Google Chrome предоставляет некоторый набор методов для работы с ним самим. Соответственно, если делать расширение через прямой инжект (как библиотеку GJS), то она потеряет все возможности работы с API-расширений, поэтому может потеряться очень важный и нужный функционал.
Для решения второй проблемы необходимо было проанализировать поведение не только InboxSDK, но и саму работу клиентской части Gmail. Сейчас в нашем расширении есть места в коде, которые завязаны на атрибутах и классах инбокса: в некоторых случаях они дополнительно расширяли возможности библиотеки, а в некоторых они исправляли какие-то ошибки инбокса. Следовательно, чтобы не нарушить работу клиентской части плагина, необходимо было таким же образом добавлять нужные классы и атрибуты, добавлять их на определенные элементы, по определенным событиям и при определенных условиях. Уже ранее упоминалось, что нет какого-либо API для работы с клиентской частью Gmail. А ведь там хранится очень много нужной информации. Например, настройки пользователя, список доступных почтовых псевдонимов, набор включенных экспериментальных возможностей и прочая важная техническая информация.
Итог
Нам удалось совершить переход с одной библиотеки на другую, не изменяя при этом логику и код в клиентской части плагина. Конечное решение представляет собой не просто использование библиотеки GJS, скорее мы создали свою собственную библиотеку-гибрид ISDK и GJS. Фундаментом послужила Gmail.js, но функционально наша разработка похожа на InboxSDK. Если сообществу Хабрахабр будет интересно, и будет много просьб поделиться нашим кодом, то мы приведем его в порядок и выложим.