Виджет на сайте

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

Если вы здесь впервые, начните с быстрого старта — эта страница про подробности.

Сниппет

Готовая строка со всеми подставленными значениями лежит в кабинете: проект → «Код для сайта». Выглядит она так:

<script src="https://widget.globask.com/widget/widget.js" data-globask-key="pk_ваш_ключ"></script>

Вставьте её перед закрывающим </body> на всех страницах сайта — человек пишет с той страницы, на которой у него возник вопрос, а не с главной. Адрес страницы приходит менеджеру вместе с обращением.

Что нужно знать про этот скрипт:

  • pk_… — публичный ключ проекта, а не секрет. Он и должен лежать в исходном коде страницы. От установки вашего чата на чужой сайт защищает не он, а список доменов.
  • Он почти ничего не стоит странице. Весит около 7 КБ в gzip и рисует кнопку сам, не дожидаясь ответа сервера. Сниппет ставится перед </body>, поэтому отрисовке страницы не мешает; если вам нужнее поставить его выше, добавьте async — виджету это не вредит.
  • Панель чата создаётся только по клику. До первого открытия на странице нет ни iframe, ни его содержимого. Исключения два: посетитель, который уже писал вам (иначе ответ пришёл бы в никуда), и посетитель с неотправленным сообщением — его надо дослать.
  • Двойная установка не удваивает чат. Сниппет нередко попадает на страницу дважды — через тему сайта и через диспетчер тегов разом. Второй запуск с тем же ключом мы игнорируем.

Домены сайта

В кабинете (проект → «Настройки») перечислены домены, на которых виджету разрешено работать. Виджет заработает на них и на их поддоменах; на всех прочих — не заработает вовсе.

Это не формальность: публичный ключ виден в исходном коде каждой страницы, и без списка доменов чужой сайт мог бы поставить ваш чат себе, а обращения с него приходили бы вашим менеджерам.

«https://» и путь вводить можно — мы оставим только домен.

Если на сайте настроен CSP

Content-Security-Policy виджету не мешает, но его нужно пустить — тремя директивами на наш адрес:

script-src  https://widget.globask.com;
frame-src   https://widget.globask.com;
connect-src https://widget.globask.com;

Каждая отвечает за свой шаг, и пропуск любой ломает виджет молча, без ошибки на странице:

  • без script-src не загрузится сам сниппет;
  • без frame-src кнопка появится, а чат по клику не откроется — эту забывают чаще всего;
  • без connect-src кнопка останется в цвете по умолчанию: загрузчик спрашивает оформление у нас ещё до того, как посетитель что-то нажал.

unsafe-inline в style-src не нужен: стили кнопки ставятся через CSSOM. В старых браузерах, которые его не поддерживают (Safari до 16.4), загрузчик возвращается к обычному тегу <style> — там при строгой политике кнопка будет без оформления, но чат продолжит работать.

Открывать чат своей кнопкой

Клик по любому элементу с классом globask-open или атрибутом data-globask-open открывает чат. Это работает всегда, независимо от настроек, и не требует ни строчки JavaScript:

<a href="#" class="globask-open">Написать нам</a>

Клик по вложенному элементу — иконке или span внутри кнопки — тоже считается: мы ищем ближайшего родителя.

Если наша кнопка на странице не нужна и чат должен открываться только вашими элементами, выберите в кабинете (проект → «Внешний вид») пункт «Своим элементом сайта». Тогда виджет не добавляет на страницу свою кнопку.

Счётчик непрочитанных

Если менеджер ответил, пока чат у посетителя закрыт, наша кнопка показывает значок с числом новых ответов. У вашего элемента такого значка нет. Вместо него мы ставим на все элементы с классом globask-open или атрибутом data-globask-open атрибут data-globask-unread с тем же числом (больше девяти — «9+») и снимаем его, когда посетитель открывает чат. Показать число — ваша часть работы, достаточно одного правила CSS:

.globask-open[data-globask-unread]::after {
    content: attr(data-globask-unread);
}

Без него посетитель не узнает, что ему ответили. Ответ не пропадёт: он лежит в чате и будет виден, как только посетитель чат откроет. Но на странице ничего не изменится, и повода открыть чат у посетителя не будет. Письмом ответ тоже не придёт, пока посетитель на сайте: письмо получает только тот, кто оставил почту и ушёл со страницы.

Управление из скриптов сайта

Виджет выставляет наружу window.globask с тремя методами:

window.globask.open();
window.globask.close();
window.globask.toggle();

Объект появляется после загрузки скрипта, поэтому вызывать его из кода, который выполняется раньше, нельзя — проверьте, что он есть:

window.globask?.open();

Имена globask-open, data-globask-open, data-globask-unread и window.globask — часть публичного контракта: мы их не переименуем.

Туда же относится фрагмент адреса #globask-open: страница, открытая с ним, разворачивает чат сама, а сам фрагмент из адреса убирается. По таким ссылкам посетитель возвращается из письма с ответом менеджера — но пользоваться ими можно и вам, например в рассылке.

Что менеджер узнает о посетителе

Если сайт знает о человеке больше нас — тариф, номер заказа, город из его профиля, — положите это в window.globask.user, и менеджер увидит значения в карточке рядом с перепиской:

<script>
    window.globask = window.globask || {};
    window.globask.user = {
        Тариф: 'Pro',
        Заказов: 12,
        Оптовик: true,
    };
</script>
<script
    src="https://widget.globask.com/widget/widget.js"
    data-globask-key="pk_ваш_ключ"
></script>

Правила простые:

  • Меняйте объект целиком. Значения перечитываются при каждом открытии чата и при каждом присваивании window.globask.user = {…} — вход и выход в одностраничном приложении без перезагрузки доедут до следующего сообщения. Правка одного поля (window.globask.user.Тариф = 'Lite') мимо нас проходит незамеченной: присваивайте новый объект. Пользователь вышел — присвойте {}, и прежние значения сотрутся.
  • Только строки, числа и true/false. Вложенные объекты и массивы показать строкой в карточке нельзя, поэтому они отбрасываются — как и NaN с бесконечностями. Булево менеджер увидит словами «да» и «нет».
  • Не больше десяти полей, название до 40 символов, значение до 200. Лишнее отсекается молча.
  • Порядок скриптов не важен: наш загрузчик дописывает свои методы к тому, что уже лежит в window.globask, а не заменяет объект.

Важно понимать, откуда эти значения берутся: их задаёт страница в браузере посетителя, а значит, их можно подделать из консоли разработчика. Менеджеру они показываются отдельным блоком — в кабинете с пометкой «со страницы сайта», в Telegram списком «Доп. параметры» по команде /info — как подсказка, а не как выписка из вашей базы. Если вашему серверу нужно значение, которому можно верить, — подпишите его, как описано ниже.

Те же значения получает и ваш сервер: в API и в вебхуках они приходят в identity.attributes, свежими на момент каждого сообщения посетителя.

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

Если ваш бот по сообщению из чата делает что-то от имени пользователя — списывает баланс, запускает платную операцию, — верить uid на слово нельзя: вписать в консоли чужой идентификатор стоит одной строки. Подпишите его на своём сервере, а проверяйте там же, получив событие. Мы значение не разбираем — только храним и возвращаем.

При отрисовке страницы:

$exp = time() + 12 * 3600; // подпись живёт дольше сессии на странице
$sig = substr(rtrim(strtr(base64_encode(
    hash_hmac('sha256', "{$uid}.{$exp}", $secret, true)
), '+/', '-_'), '='), 0, 22);

$user = ['uid' => $uid, 'uid_sig' => "{$exp}.{$sig}"]; // → window.globask.user

При получении события — на PHP:

$attrs = $event['data']['identity']['attributes'] ?? [];
[$exp, $sig] = explode('.', (string) ($attrs['uid_sig'] ?? ''), 2) + [null, null];
$expected = substr(rtrim(strtr(base64_encode(
    hash_hmac('sha256', ($attrs['uid'] ?? '').'.'.$exp, $secret, true)
), '+/', '-_'), '='), 0, 22);

$trusted = $sig !== null && (int) $exp > time() && hash_equals($expected, $sig);

или на Node.js:

import { createHmac, timingSafeEqual } from 'node:crypto';

function trusted({ uid, uid_sig }, secret) {
    const [exp, sig] = String(uid_sig ?? '').split('.');
    if (!uid || !sig || Number(exp) * 1000 < Date.now()) return false;

    const expected = createHmac('sha256', secret)
        .update(`${uid}.${exp}`)
        .digest('base64url')
        .slice(0, 22);

    return (
        sig.length === expected.length &&
        timingSafeEqual(Buffer.from(sig), Buffer.from(expected))
    );
}

Три правила, без которых подпись не защищает:

  • Проверяйте на каждом сообщении, а не один раз на диалог. Разговор привязан к браузеру, а не к человеку: на общем компьютере следующим напишет уже другой пользователь.
  • Подписывайте uid вместе со сроком, а не одно значение: иначе подсмотренная однажды подпись действует вечно.
  • Отбрасывайте повторы событий по заголовку X-Globask-Delivery до того, как что-то списать: повторная доставка после таймаута — штатная ситуация (подробнее).

Помимо этого менеджер и так видит: с какой страницы человек написал, откуда пришёл на сайт, по какой рекламной метке, с какого устройства, последние десять просмотренных страниц, город по IP и местное время. Сам IP-адрес мы не сохраняем.

Что видит посетитель в переписке

Кроме самих сообщений панель показывает несколько вещей, о которых стоит знать заранее — их спрашивают.

Отметки у своего сообщения. Одна галочка значит, что сообщение доставлено менеджерам. Вторая — что его прочитали: менеджер открыл диалог в кабинете или ответил, в том числе из темы в Telegram — ответ подтверждает, что прочитано всё написанное до него. А вот если менеджер прочитал сообщение в Telegram и не ответил, второй галочки не будет: узнать о таком прочтении неоткуда, а показывать его по факту доставки было бы обманом. Подробнее — где отвечают менеджеры.

«Печатает ответ…» — когда менеджер набирает ответ в кабинете. Из Telegram такого сигнала тоже нет.

Подпись сервиса — строка со ссылкой на нас в подвале панели. Сейчас она показывается всем и не отключается; снятие подписи — из тех возможностей, что появятся с платными тарифами.

Подпись автора. Посетитель видит публичное имя менеджера — то, что менеджер сам указал в кабинете. Не указал — будет обезличенный «Оператор»; так же подписан и ответ из Telegram от того, кто ещё не привязал свой аккаунт. Настоящие имена и почта сотрудников наружу не уходят никогда.

Пометка автоответа. Сообщение, отправленное автоматически, подписано так, чтобы его не приняли за живого человека. Уведомление из вашей системы через API подписывается именем проекта — это не автоответ, а самое нужное сообщение в переписке.

Просьба оставить почту. Появляется под автоответом — то есть только там, где ответить сразу некому, — полем ввода внутри переписки. Анкеты до начала разговора у виджета нет и не будет: она отсекает обращения ради контактов, которые без разговора ничего не стоят.

Зачем адрес: если посетитель уйдёт со страницы, ваш ответ придёт ему письмом со ссылкой обратно в тот же чат. Пока чат открыт, писем нет. Пропустить просьбу можно одним нажатием, отказ запоминается. В письме есть ссылка «Не присылать письма» — она удаляет адрес.

Просьба включается и переформулируется там же, где автоответ (проект → «Автоответы»).

Внешний вид

Настраивается в кабинете (проект → «Внешний вид») и применяется без правки сниппета:

Что Значение
Цвет ваш #rrggbb или один из пресетов; вся палитра панели строится от него
Заголовок панели до 40 символов
Приветствие до 200 символов, показывается в пустом чате
Сторона справа или слева
Форма кнопки круг, скруглённый квадрат или квадрат
Размер кнопки 48, 56 или 64 пикселя
Отступы от края 0…200 px по горизонтали и вертикали
Чем открывается нашей кнопкой или элементом сайта

Цвет текста на кнопке и в шапке мы считаем сами — светлый или почти-чёрный, смотря что читается поверх вашего.

Кнопка «Подобрать по моему сайту» читает цвет, который вы уже объявили своим: метатег theme-color, theme_color в манифесте, цвет плитки Windows или иконки Safari. Найденное подставляется в поле — сохраняете вы сами. Если ничего не нашлось, значит на сайте этих объявлений нет: это частый случай, и цвет проще взять из брендбука.

Отступы нужны, когда угол уже занят: своей кнопкой «наверх», панелью согласия с cookie, чужим виджетом. Панель чата следует за кнопкой сама. На телефоне отступ больше 40 px не применяется — иначе кнопка ушла бы за край экрана; там же учитывается системная полоса внизу iPhone.

Отдельно настраивается автоответ (проект → «Автоответы»): что написать посетителю в нерабочее время и что — если менеджеры молчат дольше заданного числа минут. Расписание и оба текста ваши. Там же — просьба оставить почту: её можно выключить или переписать своими словами.

Что дальше

  • Где отвечают менеджеры — куда попадают обращения и что умеет кабинет.
  • Серверное API — выгрузка переписки и отправка сообщения посетителю со своего сервера.
  • Вебхуки — события диалогов в реальном времени, без опроса.