Библиотека плагинов
- Плагин для Mindbox
- Диалоговое путешествие (Dialog Journey)
- Business.Helpers.Response
- Интеграция с Google Sheets
- Документация Telegram-плагина
- Плагин для работы с CustomStorage
Плагин для Mindbox
| Название плагина | Mindbox |
| Разработчик | Официальные плагины от Metabot |
| Авторы |
Петрова Ирина Дмитриевна (ira.petrova@metabot.org) Гарашко Артем Юрьевич (artem@metabot.org) |
| Дата создания | 22 Ноября 2022 |
| Последняя дата обновления | 26 Ноября 2022 |
Описание
Mindbox — это платформа автоматизации маркетинга и клиентских данных. Этот плагин к платформе Metabot позволяет интегрировать ваш чат-бот, разрабатываемый на Metabot, с платформой Mindbox. Подробнее с Mindbox можно ознакомиться на их официальном сайте: https://mindbox.ru.
Плагин позволяет автоматически добавлять клиентские данные, собранные чат-ботом в ходе диалога с клиентом, в Mindbox. С помощью плагина вы сможете организовать передачу данных в единый профиль клиента в Mindbox прямо из диалога в окне чата на сайте, в мессенджере или социальной сети.
Подключение
Для интеграции Mindbox с вашим чат-ботом вам необходимо сделать несколько вещей:
- На стороне Mindbox настройте все нужные точки интеграции и операции. Подробнее смотрите в документации Mindbox.
- Зарегистрируйтесь на платформе Metabot, подтвердите почту, авторизуйтесь и создайте чат-бот.
- Используйте готовый общий плагин или создайте плагин для своего бизнеса и скопируйте в него наш исходный код. Инструкции для обоих вариантов будут указаны ниже.
- Создайте в чат-боте системный атрибут mindbox.secretKey.<Системное имя точки интеграции> и сохраните в него секретный ключ (токен) авторизации API запросов к Mindbox.
- Если у вас планируется несколько точек интеграции в чат-боте, то нужно задать свой ключ для каждой из них. Например, так:
- Mindbox.SecretKey.MyBusiness.Chatbot - атрибута с ключом для точки MyBusiness.Chatbot
- Mindbox.SecretKey.MyBusiness.Chatbot.Shop - атрибута с ключом для точки MyBusiness.Chatbot.Shop
- Внимание! Называйте атрибуты в точности и с учетом регистра именно так, как они указаны в Mindbox.
- Если у вас планируется несколько точек интеграции в чат-боте, то нужно задать свой ключ для каждой из них. Например, так:
- Реализуйте диалоговый сценарий (скрипт) с опросом данных пользователя (например, имя, фамилия, email адрес, телефон и прочее).
-
В конце диалога, обязательно (!) запросите согласие пользователя на обработку персональных данных и пришлите ссылку на положение о конфиденциальности компании. Если подписываете на маркетинговую рассылку, то дополнительно запросите согласие. Храните согласие в атрибуте лида вашего чат-бота на случай, если это понадобится юридической службе вашей компании.
- Выберите универсальный или упрощенный метод для генерации запроса к Mindbox (смотрите детали ниже) и скопируйте код примера к себе в скрипт.
- Кастомизируйте код интеграции под свои задачи. Если у вас возникнут затруднения, обратитесь за помощью в Телеграм-чат cообщества или поддержку Metabot.
Вызов в диалоге
Вы можете использовать одну из двух функций на ваш выбор. Первая - универсальная - принимает тело JSON запроса в качестве одного из параметров, вторая - параметризованная - принимает поля в качестве параметров и формируем тело запроса JSON с помощью первой функции.
Способ 1. Универсальная (JSON) функция
Первый способ использования плагина в диалоге — с помощью универсальной функции callMindboxEndpoint(). Вы сами формируете тело запроса в формате JSON и передаете его в функцию.
Пример использования универсального (JSON) метода:
let email = lead.getAttr("email")
let phone = lead.getAttr("phone")
let lastName = lead.getAttr("lastName")
let firstName = lead.getAttr("firstName")
let endpointId = "<Идентификатор точки интеграции>"
let operation = "<Системное имя операции>"
let jsonBody = { "customer": {
"email": email,
"mobilePhone": phone,
"lastName": lastName,
"firstName": firstName,
"customFields": {
"AdCommunicationAgreement": true,
"PersonalDataAgreement": true
},
"subscriptions": [
{
"brand": "<Системное имя бренда подписки клиента>",
"pointOfContact": "<Системное имя канала подписки: Email, SMS, Viber, Webpush, Mobilepush>",
"topic": "<Внешний идентификатор тематики подписки>"
}
]
},
"pointOfContact": "<Внешний идентификатор точки контакта>"
}
// Подключаем сниппет кода из плагина Mindbox
snippet('Common.Mindbox.Operations')
// Вызываем точку интеграциии Mindbox и передаем нужный нам JSON
callMindboxEndpoint(endpointId, operation, jsonBody)
Функция требует передачи трех переменных в строгом порядке:
| endpointId |
Уникальный идентификатор интеграции. Не забудьте, что каждому endpointId соответствует свой secretKey, который нужно сохранить в системную атрибуту бота. Интеграции настраивается в системе Mindbox. |
| operation | Название операции в Mindbox. Каждому типу действия в Mindbox соответствует своя операция. Список операций настраивается в системе Mindbox. |
| jsonBody | Тело запроса в формате JSON. Формат тела запроса, различается в зависимости от типа операции. |
Способ 2. Альтернативная (параметризованная) функция
Второй способ использования плагина — с помощью альтернативной функции callMindboxEndpointAlt(), в которую вы передаете параметры запроса, а функция внутри себя формирует тело запроса в формате JSON и затем вызывает описанную выше универсальную функцию.
Пример использования альтернативного (параметризованного) метода:
let email = lead.getAttr("email")
let phone = lead.getAttr("phone")
let lastName = lead.getAttr("lastName")
let firstName = lead.getAttr("firstName")
let endpointId = "<Идентификатор точки интеграции>"
let operation = "<Системное имя операции>"
// Подключаем сниппет кода из плагина Mindbox
snippet('Common.Mindbox.Operations')
// Вызываем точку интеграциии Mindbox и передаем нужные нам параметры
callMindboxEndpointAlt(endpointId, operation,
email, phone, lastName, firstName,
subscriptionTopic, pointOfContact)
Функция требует передачи нескольких переменных в строгом порядке. Добавляйте и удаляйте параметры по вкусу ;) но при этом не забудьте поменять в плагине код формирования JSON.
| endpointId |
Уникальный идентификатор интеграции. Не забудьте, что каждому endpointId соответствует свой secretKey, который нужно сохранить в системную атрибуту бота. Интеграции настраивается в системе Mindbox. |
| operation | Название операции в Mindbox. Каждому типу действия в Mindbox соответствует своя операция. Список операций настраивается в системе Mindbox. |
| Email пользователя | |
| phone |
Мобильный телефон |
| lastName |
Фамилия |
| firstName |
Имя |
| subscriptionTopic |
Внешний идентификатор тематики подписки. Настраивается в Mindbox. |
| pointOfContact |
Внешний идентификатор точки контакта. Настраивается в Mindbox. |
Варианты подключения плагина
Вы можете использовать один из двух вариантов: воспользоваться общим плагином без модификаций кода или же скопировать код плагина к себе и модифицировать код.
Вы можете использовать общий плагин и вызвать скрипт "Вызов операции" в точности следуя примерам выше. В этом случае, для использования общего плагина используйте вызов сниппета из коллекции общих плагинов Common:
snippet('Common.Mindbox.Operations');
Либо, вы можете создать свой плагин в вашем бизнесе, скопировать наш и изменив код под себя, для этого:
- Cоздайте плагин в вашем бизнесе и назовите его Mindbox.
- Создайте в плагине скрипт и назовите его Operations.
- Скопируйте код, размещенный ниже, в код нового скрипта.
В этому случае, в примерах, указанных выше, замените вызов общего сниппета на ваш собственный:
snippet('Business.Mindbox.Operations');
В обоих случаях, перед вызовом сниппета требуется объявить все необходимые переменные и передать им соответствующие значения.
При успешном запросе во вкладке "Клиенты" в Mindbox будет создан клиент с переданными из чат-бота данными о нем:
Так же, во вкладке "Действия" вы сможете найти историю выполненной операции:
Обработка ошибок
В случае успешного выполнения оба метода возвращают true. В случае ошибки оба метода возвращают false, а в атрибутах лида и бота вы сможете найти информацию о последней ошибке. Название атрибуты и описание указано в таблице ниже.
| Хранилище | Название атрибута | Описание |
| lead | plugin.mindbox.lastError.code |
В случае ошибки, будет содержать код ошибки плагина. Смотрите таблицу ошибок ниже. |
| bot | ||
| lead | plugin.mindbox.lastError.message |
В случае ошибки, будет содержать сообщение об ошибке плагина. Смотрите таблицу ошибок ниже. |
| bot |
Список ошибок
В случае ошибки плагин может вернуть одну из следующих ошибок, а также вернет сообщение об ошибке. В таблице ниже представлены все возможные ошибки плагина и рекомендации, что вы можете сделать в каждом конкретном случае.
| Код ошибки | Сообщение об ошибке | Рекомендации |
| 1 |
Ошибка вызова Mindbox API ({код ошибки}). |
Плагин вернет код ошибки Mindbox API. Поскольку чат-бот не запоминает детали вызова API потому что вызовы асинхронны (async), при получении данной ошибки детали смотрите на стороне Mindbox. Все возможные коды ошибок Mindbox API cмотрите в документации к Mindbox: https://developers.mindbox.ru/docs/error_processing Подробнее про вызов операции через Mindbox API, который применяется в этом плагине, смотрите по ссылке: https://developers.mindbox.ru/docs/v3 |
| 2 |
Не задан ключ интеграции {имя атрибута} в атрибутах бота. |
Проверьте не ошиблись ли вы в регистре символов или названии. |
| 3 |
Не заданы обязательные параметры параметризованного метода. |
Проверьте не пытаетесь ли вы передать пустые данные в Mindbox в одном из полей (смотрите поля в атрибутах лида). А если параметр допустимо передавать пустым, удалите соответствующую валидацию в коде в callMindboxEndpointAlt(). |
Исходный код плагина
Cкопируйте указанный ниже код в скрипт Operations вашего плагина Mindbox и измените как вам требуется.
/**
* Универсальная функция для регистрации операции в Mindbox через точку интеграции.
* Используется явное указание тела запроса в формате JSON.
* @param {string} endpointID - Идентификатор точки интеграции в Mindbox
* @param {string} operation - Системное имя операции
* @param {object} jsonBody - Тело запроса (берется из настроек операции в Mindbox)
* @returns {bool} - Результат выполнения функции: успешно (true), проблемы (false).
*/
function callMindboxEndpoint(endpointId, operation, jsonBody) {
// Считываем ключ авторизации из аттрибуты бота
let secretKey = bot.getAttr('mindbox.secretKey.'+ endpointId)
// Если ключ не настроен
if (!secretKey) {
outputError(endpointId, operation, '2', 'Не задан ключ интеграции plugin.Mindbox.secretKey в атрибутах чат-бота.')
return false
}
// Задаем заголовок запроса
api.setHeaders({'authorization':'Mindbox secretKey="' + secretKey + '"'})
// Задаем URL запроса (используем асинхронный 'async' метод)
let url = "https://api.mindbox.ru/v3/operations/async?endpointId=" + endpointId + "&operation=" + operation
// Выполняем запрос с помощью метода POST и запоминаем результат
let jsonResponse = api.postJson(url, jsonBody)
let jsonResponseCode = api.getLastResponseCode()
// Возникла ошибка
if (jsonResponseCode != 200) {
outputError(endpointId, operation, '1', 'Проблема вызова Mindbox API. Детали смотрите в Mindbox.')
return false // Выполнено с проблемами
}
return true // Все ок
}
/**
* Альтернативная (параметризованная) функция для регистрации операции в Mindbox, который подготавливает тело запроса.
* Адаптируйте эту функцию под свой проект или создайте копию.
* @param {string} endpointID - Идентификатор точки интеграции в Mindbox
* @param {string} operation - Системное имя операции
* @param {string} email - Email
* @param {string} phone - Мобильный телефон
* @param {string} lastName - Фамилия
* @param {string} firstName - Имя
* @param {string} subscriptionTopic - Внешний идентификатор тематики подписки
* @param {string} pointOfContact - Внешний идентификатор точки контакта
* @returns {bool} - Результат выполнения функции: успешно (true), проблемы (false).
*/
function callMindboxEndpointAlt(endpointId, operation, email, phone, lastName, firstName, subscriptionTopic, pointOfContact) {
// Если передали пустые значения
if (isStrEmpty(endpointId) ||
isStrEmpty(operation) ||
isStrEmpty(email) ||
isStrEmpty(phone) ||
isStrEmpty(lastName) ||
isStrEmpty(firstName) ||
isStrEmpty(subscriptionTopic) ||
isStrEmpty(pointOfContact))
{
outputError('3', 'Не заданы обязательные параметры параметризованного метода.')
return false
}
// Формируем тело запроса в JSON (в вашем проекте запрос может отличаться)
let jsonBody = {
"customer": {
"email": email,
"mobilePhone": phone,
"lastName": lastName,
"firstName": firstName,
"customFields": {
"AdCommunicationAgreement": true, // Согласие на рассылку
"PersonalDataAgreement": true // Согласие на обработку персональных данных
},
"subscriptions": [
{
"brand": "<Системное имя бренда подписки клиента>",
"pointOfContact": "<Системное имя канала подписки: Email, SMS, Viber, Webpush, Mobilepush>",
"topic": subscriptionTopic
}
]
},
"pointOfContact": pointOfContact
}
// Вызываем универсальную функцию и возвращем результат
return callMindboxEndpoint(endpointId, operation, jsonBody)
}
/**
* Вспомогательная функция, которая сохраняет сведения об ошибке в атрибутах лида и атрибутах бота.
* @param {string} endpointId - Точка интеграции
* @param {string} operation - Операция
* @param {string} code - Код ошибки
* @param {string} message - Сообщение об ошибке
*/
function outputError(endpointId, operation, code, message) {
// Добавляем в конце сообщения доп. инфу.
message = message + ' (точка=' + endpointId + ', операция=' + operation + ')'
lead.setAttr("plugin.Mindbox.lastError.Code", code)
lead.setAttr("plugin.Mindbox.lastError.Message", message)
bot.setAttr("plugin.Mindbox.lastError.Code", code)
bot.setAttr("plugin.Mindbox.lastError.Message", message)
}
/**
* Вспомогательная функцию, которая проверяет является ли строка пустой.
* @param {string} code - Код ошибки
* @returns {bool} - если строка пустая (true), иначе (false).
*/
function isStrEmpty(str) {
return (typeof str === 'string' && str.trim().length === 0) ? true : false
}
Диалоговое путешествие (Dialog Journey)
| Название плагина | Диалоговое путешествие (Dialog Journey) |
| Разработчик | Официальные плагины от Metabot |
| Авторы |
Гарашко Артем Юрьевич (artem@metabot.org) |
| Дата создания | 04 Января 2023 |
| Последняя дата обновления | 06 Января 2023 |
Описание
Плагин позволит организовать развитие вашего чат-бота таким образом, чтобы все разрозненные диалоги объединялись в единую коммуникационную стратегию компании, представленную в виде путешествий (journeys), разбитых на фазы (phases) с целями (goals), предоставляемой пользой (values) и измеряемыми показателями (metrics). Более подробное описание концепции и устройства плагина, а также предлагаемой методологии маркетинговой стратегии смотрите ниже.
Пример
Пример работы чат-бота с интегрированным плагином можете посмотреть перейдя по ссылке.
Настройка
Для интеграции плагина DJ с вашим чат-ботом вам необходимо сделать следующее:
- Создать кастомные таблицы и заполнить их, согласно схеме базы данных, опубликованной в разделе Справочники.
- Спроектировать путешествия для ваших клиентских сегментов и настроить справочники, описания назначения которых смотрите ниже.
- Классы персон.
- Подклассы персоны.
- Путешествия.
- Фазы путешествия.
- Польза.
- Цели.
- Показатели.
- Активности.
- Скачать готовый шаблон чат-бота, в который уже интегрирован плагин DJ, адаптировать его код и структуру под ваши задачи в своем чат-боте.
- Ссылка.
- Ознакомиться с примерами когда вызова методов DJ из вашего чат-бота в разделе JS команды.
- Ознакомиться с инструкций как пользоваться аналитикой и отчетами в разделе Аналитика и отчеты.
Если разработка чат-бота ведется на сторонней платформе, в дополнение к шагам, описанным выше, вам также необходимо настроить точки интеграции от вашего чат-бота, созданного на сторонней платформе, к чат-боту, созданному на Metabot, работающему как внешнее хранилище данных для следующих целей:
- Чтобы ваш чат-бот мог передавать в базу данных Мetabot все нужные вам события для аналитики, например, сообщать, что пользователь достиг цели или перешел к следующей фазе путешествия.
- Чтобы ваш чат-бот мог запрашивать в базе данных Metabot информацию о состоянии путешествия пользователя, например, проверять достиг ли пользователь цели, были ли предоставлена польза, узнавать фазу путешествия на которой находится пользователь и так далее.
Инструкцию о том, как подключить Dialog Journeys к чат-боту (а может и не только к чат-боту), создаваемому на сторонней платформе, смотрите в разделе Интеграция со сторонними системами.
Методология
Предисловие
Представленное решение — это уникальный синтез нашего собственного практического опыта и изысканий с идеями ведущих мировых теоретиков из сферы маркетинга, управления бизнесом и инноваций.
Возможно модель подойдет к вашей бизнес-практике идеально «как есть». Мы постарались сделать модель простой и в то же время достаточно гибкой — принципы, лежащие в ее основе, универсальны и действенны.
Возможно вы не согласитесь с представленной моделью работы с клиентами и решите сделать по-другому, либо захотите расширить или откорректировать модель. Вы вправе это сделать. Если вам потребуется разработка уникального плагина под ваше видение, обращайтесь в наш отдел разработки и мы сделаем решение «под ключ».
Методология маркетинга, на которой строится работа данного плагина, стоит на нескольких китах. Во-первых, это решение для современного маркетинга для «экономики связей» или «экономики подключения» (Connection Economy). Подробнее об этой концепции смотрите здесь.
Во-вторых, это решение строится на маркетинге доверия. Подробнее об этой концепции смотрите соответствующие обучающие материалы и экспертов. Например, здесь.
Справочники
Классы персон
Проектирование начинается с описания клиентских сегментов — мы их называем Классы персон. Подобно, RPG играм, опишите все классы, с которыми имеет дело ваш бизнес.
- Пример из сказочный: воин, лучник, волшебник;
- Пример из жизни: строитель, заказчик, партнер.
Подклассы персон
Создайте Подклассы персон, если вам нужно разбить клиентов на более узкие сегменты и строить с ними более персонализированные отношения.
- Пример для класса лучника: стрелок, снайпер, охотник;
- Пример для класса строителя: индивидуальный строитель, профессиональная бригада, крупный застройщик.
Путешествия
Создайте все необходимые Путешествия, как общие для всех классов и подклассов, так и индивидуальные для каждого сегмента.
Если вы впервые сталкивайтесь с разработкой чат-ботов, мы рекомендуем не усложнять и начать автоматизацию с одного наиболее популярного и критичного путешествия, например, с вовлечения и онбординга нового пользователя.
В любое случае, рекомендуем обдумать все путешествия заранее, хотя бы верхнеуровнево, которые "проживают" или будут "проживать" ваши клиенты, взаимодействия с вашей компанией.
Примеры путешествий:
- Онбординг нового партнера;
- Путь к первой покупке;
- Путь к повторной покупке;
- Обучаем стрелять из лука.
Плагин работает так, что запоминает один единственный раз, когда пользователь пустился в путешествие. Плагин на текущий момент не поддерживает повторные путешествия по одному и тому же путешествию. Если вам необходимо заново отправить пользователя в повторное путешествие, для этого создайте новое путешествие или очистите старые данные по путешествию в базе данных.
Цели
Следующим шагом, который на самом деле надо делать одновременно с предыдущим, мы рекомендуем заполнить Цели для путешествий. Мы же с вами занимаемся бизнесом и не хотим создавать бесцельные путешествия, верно?
Пример целей для онбординга партнеров:
- Регистрация в партнерской программе;
- Сертификация партнера.
Пример целей для обучения стрельбе из луков:
- Обучить лучника;
- Продать лук и стрелы;
- Продать билеты на соревнование по стрельбе.
Плагин работает так, что позволяет записать достижения цели по каждому путешествию для каждого пользователя только один единственный раз. Если в чат-боте пользователь несколько раз пройдет по сценарию, то повторные достижения одной и той же цели будут проигнорированы.
Фазы путешествия
Разбейте каждое путешествие на Фазы. Мы сознательно используем слово «фазы», а не «этапы» или «шаги», потому что оно нам больше нравится — ведь переход от фазы к фазе т.е. так называемый «фазовый переход» подразумевает некую качественно новую форму, например, лед при нагревании превращается в воду, а при еще большем нагревании в пар. Мы же с вами хотим строить диалоги с нашей аудиторией так, чтобы каждый раз выходить на новый уровень взаимоотношений, верно?
Пример фаз для онбординга нового партнера:
- Вовлечение;
- Знакомство;
- Сертификация;
- Первая транзакция.
Пример фаз для учебы стрельбе из лука:
- Изучает основы по стрельбе и безопасности;
- Учится делать DIY лук и стрелы;
- Учится стрелять;
- Сдать экзамен по стрельбе;
- Записать на соревнование по стрельбе.
Согласитесь, что каждая из фаз, приведенных в примерах выше, символизирует качественно новое состояние, дойдя до которого, наш пользователь, уже не сможет вернуться назад и забыть все то, что было до этого?
Наша цель при проектировании и разработке чат-ботов и авто-воронок заключается в том, чтобы выстраивать диалоговые коммуникации таким образом, чтобы они помогали пользователю двигаться от фазы к фазе, получая пользу, и тем самым доходить до завершения путешествия и помогая нам достигать бизнес-цели. Как именно строить такие коммуникации мы разберем ниже.
Плагин работает так, что запоминает один единственный раз, когда пользователь завершал одну фазу путешествия и начинал новую. Плагин запоминает дату и время фазовых переходов, чтобы по ним строить аналитику. Если пользователь пришел в сценарий в чат-боте, в котором в DJ сообщается о смене фазы, то плагин проигнорирует повторные команды.
Польза
Теперь самое интересное. Мы расписали наши путешествия, фазы и цели. Теперь давайте подумаем о том, как же мы будем влиять на наших пользователей, чтобы они достигали нужных нам целей?
Спланируйте Пользу, которую будете предоставлять вашей аудитории, на каждой фазе путешествия, чтобы выстраивать доверительные отношения и двигаться вместе с ней к целям.
Мы предлагаем воспользоваться следующими принципами современной научной теории маркетинга, который на самом деле, если хорошо подумать, представляют из себя обычный здравый смысл из сферы человеческих отношений и житейскую мудрость, а именно:
- Эмпатия
- Давайте мы будем пытаться понять кто перед нами, а не бомбардировать людей не актуальной для них информацией;
- Давайте понимать на каком этапе принятий решения о покупке (buyer's journey) находится человек, чтобы давать именно то, что нужно сейчас;
- Давайте стараться учитывать чувства и эмоции людей, чтобы отвечать корректно контексту ситуации;
- Здорово, что чат-боты помогают все это реализовать, ведь чат-бот это диалог, в котором можно задавать вопросы и запоминать ответы, а благодаря технологии распознавания естественного языка (NLP) можно понять намерение пользователя по свободному вводу и даже распознать эмоции;
- Пример для строительной сферы: в самом начале коммуникации с целевой аудиторией, задайте 2-3 квалифицирующих вопроса, которые помогут определить к какому сегменту (классу и подклассу) относится человек. Также, вы можете узнать квалификацию строителя (профессионал или новичок), чтобы в последствии вести каждый сегмент по своему уникальному пути.
- Пример для сказочной истории: аналогично, cоздайте квалификационную анкету, в которой будет несколько вопросов, которые помогут вообще понять кто перед нами. Вы не не захотите "впаривать" стрельбу из лука волшебнику 100500 уровня, который обидится и превратит вас в лягушку? =)
- Персонализация
- Раз мы можем говорить с пользователем и задавать интересующие нам вопросы, так давайте использовать полученную информацию для построения персонализированных диалогов. А если интегрировать чат-бот с корпоративными информационными системами, например, с E-Commerce веб-сайтом, где хранится история покупок пользователя, то можно строить еще более полезные персонализированные диалоги и предложения.
- Пример: как минимум, можно иногда обращаться по имени. Конечно, это не заставит пользователя прийти в восторг и сделать покупку, на, как минимум, повлияет на общее восприятие вашего бренда и позволит заработать очки доверия. Ведь мы же строим долгосрочные отношения на доверии, а значит должны всегда заботиться о целостности и последовательности наших коммуникаций.
- Пример из строительной отрасли: узнав сегмент, опыт строителя и регион, вы можете сделать персональное предложение, например, предложить партнерам присылать заявки на строительный заказы из этого региона, предложить горячие скидки на продукцию любимого бренда, пройти обучение для новичков и так далее.
- Пример для сказочной истории: узнав, что перед нами маг, мы можем предложить ему магазин для магов от наших партнеров или просто выпить чашечку кофе.
- Такт и ритм
- Мы настоятельно рекомендуем пользоваться житейским здравым смыслом и бизнес этикой при программировании автоматических коммуникаций.
- Также как и в реальных отношениях в нашей жизни, давайте cоблюдать чувство такта и ритма в цифровых отношениях, которые автоматизируем. Людям не понравится, если мы пишем им, как назойливая муха, с поводом или без повода. Также, люди могут про нас начитать забывать и контакт потом восстановить будет сложнее, если мы совсем перестанем общаться. Если же мы будем присылать информацию, которая не релеванта, не принимая обратную связь и не корректируя коммуникацию, то люди могут отправить нас "в баню". И так далее.
- Пример из строительной отрасли: когда мы узнаем темп обучения, который удобен коллеге-строителю для повышения своей квалификации, мы можем начать присылать ему обучающие материалы прямо в чат-бот так часто как будет удобно: раз в день, неделю, месяц.
- Пример из сказочной отрасли: узнав, что перед нами опытный следопыт, который отправляется в охотничьи вылазки раз в месяц, мы можем отправлять ему информацию о новинках прямо на кануне очередного похода, о котором можем узнать, спросив об этом.
- Доверие
- Доверие клиента или партнера — это самое главное конкурентное преимущество и один из самых главных активов. Никто не свяжет свою жизнь с человеком, в котором не уверен. То же самое происходит в бизнесе. Теряя доверие к бренду или компании, вы уходите к другим, верно? Ваши клиенты делают так же.
-
Завоевание доверия это сложный и длительный процесс, требующим особого подхода. Большое доверие увеличивает продажи и доходы компании, а потеря доверия увеличивает расходы на рекламу и маркетинг. Доверие зарабатывается по крупицам, а потерять его можно в один момент.
- Конечно, доверие к бренду или компании складывается из многих факторов, на которые мы не можем влиять чат-ботом, например, из качества продукции. Если продукция плохая, чат-бот вряд ли сильно поможет в выстраивании доверия, какие бы красиво он не говорил.
- Однако, если в компании порядок с продукций и сервисом, то создание захватывающего, полезного и удобного клиентского опыта (CX) в виде бесшовного диалога в чат-боте, интегрированного в бизнес-процессы и информационные системы предприятия, может стать тем самым ключом к конкурентному преимуществу, который изменит баланс весов в пользу вашей компании.
- Пример доверия: хорошо понимать своих клиентов, проектировать правильные путешествия, которые дают пользу, помнить о долгосрочности диалогов и отношений, выстроить полезный сервис в чат-боте, которым хочется пользоваться и рассказать другим.
- Пример потери доверия: игнорировать запросы клиентов, не решать пожарные ситуации, долго строить отношения по стратегии, а потом начать рассылать спам и прочее.
- Щедрость
- Тоже не менее важный принцип, как и доверие. В эпоху перепроизводства и высокой конкурентности (когда в каждой категории на полке десятки и сотни продуктов), щедрость это то, что позволяет выстраивать доверительные отношения.
- Как и в жизни, так и в бизнесе, никто не захочет дружить с теми кто только «берет» и не готов «отдавать».
- Что означает щедрость в применении к чат-ботам: полезный контент, которым вы можете делиться; полезные вебинары; выставки; сервисы и так далее.
Аналогично целям, предоставление пользы фиксируется один единственный раз и повторные выдачи той же самой пользы не фиксируются в базе данных, поэтому вы можете не опасаться за то, что пользователь несколько раз обратился за одним и тем же контентом — это не собьет статистику. Предоставление пользы будет отслежено только самый первый раз.
Показатели
Спланируйте Показатели (или метрики), которые вам необходимо измерять.
Пример из жизни:
- Вы можете завести метрику, которая отображает % потребления контента, чтобы оценивать степень "созревания" пользователя;
- Индекс NPS (удовлетворенность компанией);
- Настроение, например, вы можете периодически опрашивать как у ваших пользователей дела ;
- Степень осведомленности о продукции компании (от 0 до 100).
Сказочный пример:
- Количество купленных стрел;
- Количество сломанных луков.
Показатели, в отличие от Целей и Пользы, можно измерять сколько угодно раз и в любое время. То есть, если с течением времени показатель меняется, у вас будет вся история изменений.
В текущей версии плагина показатели закрепляются за путешествием и фазой во время которых они были собраны. В будущих релизах планируется сделать поддержку сбора общих показателей, не относящихся к конкретному путешествию или фазе.
Активности
Активности — это события, которые происходят в ходе путешествия или действия, которые совершает пользователя, во время путешествия.
Существует 7 основных событий, которые позволяют управлять ходом путешествия и отслеживать его хронологию:
| ID | Код | Название | Пояснение |
| 1 | journeyStarted | Путешествие начато | Используется в самом начале путешествия, когда стало понятно, что пользователь "отправился в путешествие". |
| 2 | phaseStarted | Фаза начата | Используется при запуске следующей фазы, а также автоматически в начале путешествия. |
| 3 | phaseCompleted | Фаза завершена | Используется при запуске следующей фазы, завершая предыдущую, и при завершении всего путешествия. |
| 4 | phaseInterrupted | Фаза прервана | Используется при прерывании фазы, которое происходит либо при прерывании всего путешествия, либо когда по каким-то причинам в нам нужно будет прервать фазу. |
| 5 | phaseSkipped | Фаза пропущена | Используется по каким-то причинам когда вам необходимо пропустить целую фазу. |
| 6 | journeyCancelled | Путешествие отменено | Используется когда путешествие было отменено, например, пользователь передумал. |
| 7 | journeyCompleted | Путешествие завершено | Путешествие успешно завершено, пользователь дошел до победного конца. |
В одном из будущих релизов планируется возможность добавлять пользовательские активности, чтобы вы могли отслеживать промежуточные шаги во время фазы, если вам это понадобится.
Журналы
Все что происходит в чат-боте сохраняется в несколько Журналов, а также сохраняется в Состоянии пользователей в путешествии. Это системные таблицы, в которых накапливаются исторические данные, которые вам не нужно трогать. На основе журналов и справочников, формируются аналитические отчеты и визуализации.
Ни в коем случае не нарушайте целостность данных, если уже запустили трафик и идет сбор данных о путешествиях пользователей. Если у вас есть журналы с данными, а вы решите удалить или изменить данные в Справочниках, отдавайте себе отчет, что это может повлиять на исторические данные, ранее собранные чат-ботом, а значит и на отчеты.
Журнал путешествий
В этом журнале хранится история фаз и активностей, а также начала и завершения путешествий.
Журнал пользы
В этом журнале хранится история предоставления пользы пользователям.
Журнал целей
В этом журнале хранится история достижения целей.
Журнал показателей
В этом журнале хранятся собранные метрики.
Состояние пользователя в путешествии
В этой таблице хранится состояние пользователя в конкретном путешествии.
Ни в коем случае не трогайте эти данные на продакшене, кроме случаев отладки, иначе нарушите ход путешествия для пользователей.
JS команды
Для отправления информации в плагин и фиксации данных в базе необходимо в нужном вам месте в чат-боте подключить плагин, инициализировать менеджер путешествий и выполнить нужную команды.
Список команд и примеры кода представлены в таблице ниже.
| № | Название метода | Пример кода |
| 1 |
Начать путешествие |
При инициализации JourneyManager для лида будет создана персона, если она еще не была создана! В текущей версии плагин позволяет отслеживать пути персон и не возможно отслеживание путей лидов. Поэтому персона создается для каждого лида на самой ранней стадии пути, чтобы иметь возможность отслеживать путь как можно раньше - даже когда лид/персона еще не взаимодействовали с бизнесом. В качестве роли персоны будет использована роль по умолчанию, которую необходимо задать в настройках чат-бота. |
| 2 |
Завершить путешествие |
|
| 3 | Следующая фаза |
|
| 4 | Предоставить пользу |
|
| 5 | Достигнуть цель |
|
| 6 | Записать показатель |
|
Другие примеры кода с использованием плагина смотрите в шаблоне чат-бота, доступного для скачивания здесь.
Интеграция со сторонними системами
Вы можете использовать DJ плагин для сбора данных о путешествии пользователей не только в чат-боте, созданном на платформе Metabot. Вы можете вести разработку на других бот-платформах и использовать Metabot для сбора и визуализации аналитики.
Также, вы можете отправлять в Metabot информацию о событиях из других систем, чтобы фиксировать прохождение пользователя по пути, когда пользователь совершает нужное целевое действие на сайте (например, размещает товар в корзину, но не оплачивает).
Для интеграции сторонних систем с вашим Dialog Journey в чат-боте на Metabot, необходимо настроить точки интеграции для всех необходимых событий, которые вам нужно регистрировать в Metabot. Информацию о точках интеграции смотрите в разделе Точки интеграции и конструктор API. В коде точки интеграции разработайте код согласно примерам выше.
Business.Helpers.Response
Плагин Business.Helpers.Response служит утилитой для стандартизации ответов API в бизнес-приложениях. Его основная цель - упростить создание последовательных и структурированных ответов для различных сценариев, возникающих во время взаимодействия с API.
/**
* Prepares and returns an API response object for a failed operation.
* This should be used when an operation does not complete successfully,
* with the provided error message included in the response.
*
* @param {string} errorMessage - The error message to be included in the response.
* @returns {Object} An object representing a failed operation response.
*/
function getErrorResponse(errorMessage) {
return {
success: false,
message: errorMessage
};
}
/**
* Prepares and returns an API response object for a successful operation.
* This should be used when an operation completes successfully
* and no additional data needs to be returned.
*
* @returns {Object} An object representing a successful operation response.
*/
function getSuccessResponse() {
return {
success: true
};
}
/**
* Prepares and returns an API response object for a successful operation
* with additional JSON data. This should be used when an operation completes
* successfully and there is additional data to return in the response.
*
* @param {Object} json - The JSON data to be included in the response.
* @returns {Object} An object representing a successful operation response with additional data.
*/
function getSuccessResponseWithJson(json) {
return {
...json,
success: true
}
}
Интеграция с Google Sheets
Первым делом необходимо создать новую таблицу в Google Sheets и добавить нового редактора api@metabot.org.
Далее копируем ID таблицы из адресной строки.
Копируем название листа.
Копируем название столбцов в таблице.
Метод для добавления нового столбца
Записываем в нужное место скрипта следующий код:
var GoogleSheetsService = require('Common.Integrations.GoogleSheets') // Плагин для работы с Google Sheets
GoogleSheetsService.sheetId = '11muAnepqhpRQ9ElE9CzC3E-edmf9JbRE3gwmBTDa5pE' // ID скопированный из таблицы
GoogleSheetsService.listName = 'list' // Название листа
// Параметры где ключ - название столбца, значение - данные которые занесутся в строку
let params = {
"region": "Москвская область",
"name": 'Тест',
"age": "24",
"city": "Москва",
}
let result = GoogleSheetsService.addRow(params) // Функция для добавления строк в таблицу
debug(result) // Вернётся результат выполенния с Id в строки в которую записались данные
Пример ответа:
{
"status": "success",
"message": "Row added successfully", // Сообщение, если есть ошибка - вернётся описание ошибки
"rowId": 8 // Id в строки в которую записались данные
}
Метод для поиска и замены значения в ячейке
Записываем в нужное место скрипта следующий код:
var GoogleSheetsService = require('Common.Integrations.GoogleSheets') // Плагин для работы с Google Sheets
GoogleSheetsService.sheetId = '11muAnepqhpRQ9ElE9CzC3E-edmf9JbRE3gwmBTDa5pE' // ID скопированный из таблицы
GoogleSheetsService.listName = 'list' // Название листа
// Параметры со настройками для замены
let params = {
colomn_search_name: 'region',
colomn_edit_name: 'region',
search_value: '123123',
match_entire_cell: true,
new_value: "Антон"
}
let result = GoogleSheetsService.searchAndEditRow(params) // Функция для поиска и замены строк
debug(result) // Вернётся результат выполенния или код ошибки
Пример ответа:
{
"status": "success",
"message": 'Значение найдёно и измененно'
}
Документация Telegram-плагина
| Название плагина | Telegram |
| Разработчик | Официальные плагины от Metabot |
| Авторы |
Борисов Павел (https://t.me/mr_result) |
| Дата создания | 02 Октября 2023 |
| Последняя дата обновления | 25 Апреля 2024 |
Описание
Этот плагин предоставляет удобный интерфейс для работы с Telegram Bot API. Он поддерживает отправку текстовых сообщений, фотографий, создание клавиатур и обработку ответов пользователя.
Основной класс TelegramMessage
Подключение и инициализация
let TelegramMessage = require('Common.Integrations.Telegram')
let msg = new TelegramMessage()
Основные параметры
msg.text = "Ваше сообщение" // Текст сообщения
msg.parse_mode = "HTML" // Режим форматирования (HTML или MarkdownV2)
msg.protect_content = true // Защита контента от пересылки
msg.keyboard = "Да[yes]==Нет[no]" // Создание клавиатуры
Форматы клавиатуры
- Вертикальное разделение — используйте ==;
msg.keyboard = "Кнопка1[btn1]==Кнопка2[btn2]" // Кнопки будут расположены вертикально
- Горизонтальное разделение — используйте ||;
msg.keyboard = "Кнопка1[btn1]||Кнопка2[btn2]" // Кнопки будут расположены горизонтально
- Комбинированное разделение;
msg.keyboard = "Кнопка1[btn1]||Кнопка2[btn2]==Кнопка3[btn3]||Кнопка4[btn4]"
Специальные типы кнопок
- Запрос контакта:
msg.keyboard = "Отправить контакт[telegram_contact]"
- Запрос локации:
msg.keyboard = "Отправить локацию[telegram_location]"
- Ссылки:
msg.keyboard = "Посетить сайт[<https://example.com>]"
- Web App:
msg.keyboard = "Открыть приложение{<https://webapp-url.com>}"
- Ссылка на пользователя:
msg.keyboard = "Написать админу[tg://user?id=123456789]"
Примеры использования
Простое меню с обработкой ответов
Код будет работать корректно только в команде JS Callback.
let TelegramMessage = require('Common.Integrations.Telegram')
let msg = new TelegramMessage()
msg.text = "Выберите действие:"
msg.keyboard = "Информация[info]==Помощь[help]==Настройки[settings]"
msg.parse_mode = "HTML"
switch (true) {
case (isFirstImmediateCall):
msg.send()
return false
case (msg.getMessagePayload()?.callback_data == "info"):
msg.addReplyToText()
bot.sendMessage("Информация о боте...")
return false
case (msg.getMessagePayload()?.callback_data == "help"):
msg.addReplyToText()
bot.sendMessage("Справка по использованию...")
return false
case (msg.getMessagePayload()?.callback_data == "settings"):
msg.addReplyToText()
bot.runScriptForLead(123, leadId) // Запуск скрипта настроек
return false
}
Справочник методов
Основные методы отправки
- send() — отправка нового сообщения;
- edit() — редактирование существующего сообщения;
- addReplyToText() — добавление ответа к существующему сообщению;
- removeInlineKeyboard() — удаление клавиатуры.
Получение информации
- getMessagePayload() — получение информации о сообщении из вебхука:
- message_id — ID сообщения;
- text — текст сообщения/кнопки;
- input_type — тип ввода ('write'/'press');
- callback_data — данные колбэка;
- payload — полные данные вебхука.
- message_id — ID сообщения;
Обработка ответов пользователя
// Проверка типа ввода
if (msg.getMessagePayload()?.input_type == "write") {
// Пользователь написал текст
}
// Проверка нажатия кнопки
if (msg.getMessagePayload()?.callback_data == "button_id") {
// Пользователь нажал кнопку
}
Отладка
msg.debug("Отладочное сообщение") // Отправка отладочной информации.
//Не отправляется пользователю но логируется в карточке лида
Лучшие практики
- Всегда проверяйте первый вызов:
if (isFirstImmediateCall) {
msg.send()
return false
}
- Добавляйте обработку текстовых сообщений:
if (msg.getMessagePayload()?.input_type == "write") {
bot.sendMessage('Пожалуйста, используйте кнопки')
return false
}
- Скрывайте клавиатуру после использования:
msg.addReplyToText() // После нажатия на кнопку записывает её в сообщение
Форматирование текста и entities
В плагине доступно два способа форматирования текста: HTML и MarkdownV2. По умолчанию параметр parse_mode не установлен — это сделано специально для возможности работы с entities (когда нужно сохранить форматирование текста, которое пользователь отправил в Telegram).
HTML форматирование
Документация: https://core.telegram.org/bots/api#html-style
msg.text = "<b>Жирный текст</b>\n<i>Курсив</i>\n<code>Моноширинный текст</code>"
msg.parse_mode = "HTML"
MarkdownV2 форматирование
Документация: https://core.telegram.org/bots/api#markdownv2-style
msg.text = "**Жирный текст**\n__Курсив__\n`Моноширинный текст`"
msg.parse_mode = "MarkdownV2"
Работа с entities
Entities используются, когда нужно сохранить форматирование текста, которое пользователь отправил в Telegram. Это особенно полезно при создании различных конструкторов сообщений или при пересылке сообщений с сохранением форматирования.
Пример сохранения форматированного сообщения пользователя:
let TelegramMessage = require('Common.Integrations.Telegram')
let msg = new TelegramMessage()
msg.text = `Введите текст для рассылки`
msg.keyboard = `↩️ Назад[${backScripts}]`
msg.parse_mode = 'HTML'
switch (true) {
case (isFirstImmediateCall):
msg.send()
return false
case (msg.getMessagePayload()?.input_type == "write"):
msg.removeInlineKeyboard()
// Получаем webhook и извлекаем текст с entities
let webhook = msg.getMessagePayload()
let messageData = {
"text": webhook?.payload?.message?.text,
"entities": webhook?.payload?.message?.entities // Сохраняем entities для сохранения форматирования
}
// Сохраняем сообщение с форматированием в базу данных
lead.setJsonAttr("messageData", messageData)
bot.run({
script_id: nextScript
})
return false
default:
msg.addReplyToText()
bot.run({
script_id: msg.getMessagePayload().callback_data
})
return true
}
В этом примере:
- Когда пользователь отправляет форматированное сообщение, мы получаем не только сам текст, но и массив entities.
- Entities содержат информацию о форматировании: жирный текст, курсив, ссылки и т.д.
- Мы сохраняем и текст, и entities, чтобы позже можно было воспроизвести сообщение с точно таким же форматированием.
- Важно: для работы с entities параметр parse_mode должен быть не установлен (режим по умолчанию).
Теперь после запоминания entities мы можем сделать его вывод таким образом:
let TelegramMessage = require('Common.Integrations.Telegram')
let msg = new TelegramMessage()
let messageData = lead.getJsonAttr("messageData")
msg.text = messageData?.text
msg.entities = messageData?.entities
// Если попробовать использовать parse_mode то форматирование будет некорректным
Работа с файлами
Плагин позволяет обрабатывать файлы, которые пользователи отправляют в Telegram. Вы можете получить URL файла и сохранить его в карточку лида, не отправляя обратно в Telegram.
Получение и обработка файлов
// Подключаем библиотеку
let TelegramMessage = require('Common.Integrations.Telegram')
let msg = new TelegramMessage()
// Все входящие файлы
let attachments = bot.getAllAttachments()
if(Boolean(attachments?.[0]?.url)){
uploadData = bot.downloadFileFromUrl(attachments[0].url)
msg.debug('Пользователь отправил файл: ' + uploadData.url)
msg.debug('Файл ID : ' + JSON.stringify(attachments[0]?.payload?.file_id)) // данная строка вспомогательная для
// автоматизации вывода полученных
lead.setAttr('file', uploadData.url) // файлов в других местах бота
// Запуск скрипта...
bot.runScriptByCodeForLead("recieveFile", lead.getData('id'))
bot.stop()
}
В этом примере:
- Проверяем наличие прикрепленных файлов с помощью bot.getAllAttachments().
- Если файл есть, скачиваем его через bot.downloadFileFromUrl().
- Логируем получение файла с помощью msg.debug().
- Сохраняем URL файла в атрибут лида.
- Запускаем скрипт обработки файла.
- Останавливаем текущий скрипт.
Дополнительный пример:
let TelegramMessage = require('Common.Integrations.Telegram')
let msg = new TelegramMessage()
msg.text = 'А теперь жду твоё фото 😉'
msg.keyboard = ``
msg.parse_mode = 'HTML'
let scriptCode = bot.getCurrentScriptCode()
lead.setAttr("script_code", scriptCode)
let data = bot.getAllAttachments()
switch (true) {
case (isFirstImmediateCall):
msg.send()
return false
break
case (Boolean(data?.[0]?.type != 'image')):
bot.sendMessage('Отправь сжатое изображение. Такой формат не подходит')
return false
break
case (Boolean(data?.[0]?.type == 'image')):
uploadData = bot.downloadFileFromUrl(data[0].url)
msg.debug('Пользователь отправил файл: ' + uploadData.url)
lead.setAttr('photo', uploadData.url)
bot.sendMessage('Класс! Фото загрузил')
return true
break
default:
msg.addReplyToText()
return true
}
Рекомендации по работе с файлами
- Всегда проверяйте наличие файлов перед их обработкой;
- Логируйте получение файлов для отладки;
- Сохраняйте URL файлов в атрибуты лида для дальнейшего использования;
- Запускайте отдельный скрипт для обработки файлов, чтобы разделить логику.
Плагин для работы с CustomStorage
Плагин CustomStorage
Плагин CustomStorage предназначен для работы с кастомными таблицами атрибутов в JavaScript скриптах. Плагин позволяет сохранять и получать данные из кастомных таблиц, аналогично работе с атрибутами лидов через lead.setAttr() и lead.getAttr() и т.д.
Плагин поддерживает:
- Работу с обычными атрибутами (строки, числа, булевы значения)
- Работу с JSON атрибутами (объекты, массивы, вложенные структуры)
- Точечную нотацию для вложенных JSON структур (например,
user.profile.name) - Подкатегории для группировки данных через
key2иkey3 - Прямую работу с базой данных без кэширования в памяти
Первоначальная настройка (Setup)
Перед использованием плагина необходимо создать кастомную таблицу для хранения атрибутов. Это можно сделать двумя способами:
Способ 1: Автоматическое создание через метод setup()
Метод setup() автоматически создает кастомную таблицу со всеми необходимыми полями и уникальным индексом.
Важно: Метод setup() можно вызвать только один раз для каждой таблицы. При повторном вызове будет выброшена ошибка, если таблица уже существует.
Пример использования:
let customStorage = require('Common.Platform.CustomStorage')
// Создаем новую таблицу для атрибутов
customStorage.setup('my_attributes_table')
// Теперь можно работать с таблицей
customStorage.setAttr('server_name', 'production-server-01')
Что делает метод setup():
- Проверяет, что таблицы с таким именем еще не существует
- Создает кастомную таблицу с необходимыми полями:
-
id- автоинкрементный идентификатор -
key- первый ключ (обязательный) -
key2- второй ключ (опциональный, для подкатегорий) -
key3- третий ключ (опциональный, для подкатегорий) -
value_type- тип значения (stringилиjson) -
value- значение атрибута -
created_at- дата создания -
updated_at- дата обновления
-
- Создает уникальный индекс на комбинацию
(key, key2, key3)для предотвращения дубликатов
Способ 2: Ручное создание через интерфейс админки
Вы можете создать таблицу вручную через интерфейс админки в разделе Таблицы, используя структуру из JSON примера ниже.
Структура таблицы (JSON для импорта):
{
"version": "2.50.0",
"format": 2,
"custom_tables": {
"business_attributes": {
"is_enabled": 1,
"name": "business_attributes",
"title": "Атрибуты бизнеса",
"comment": null,
"is_sync_struct": 1,
"is_show_in_menu": 1,
"has_api_access": 0,
"sort_order": 0,
"is_compact_view": 0,
"line_height": 1,
"max_width_px": null,
"css": null,
"js": null,
"sort_order_data": 1,
"fields": {
"id": {
"is_enabled": 1,
"name": "id",
"title": "ID",
"type": "AUTOINC",
"is_required": 1,
"size": null,
"default_value": null,
"hint": null,
"tooltip": null,
"sort_order": 0,
"precision": null,
"is_sync_struct": 1,
"is_locked_for_regular_user": 0,
"max_width_px": null
},
"key": {
"is_enabled": 1,
"name": "key",
"title": "Первый ключ",
"type": "TEXT",
"is_required": 1,
"size": null,
"default_value": "",
"hint": null,
"tooltip": null,
"sort_order": 2,
"precision": null,
"is_sync_struct": 1,
"is_locked_for_regular_user": 0,
"max_width_px": null
},
"key2": {
"is_enabled": 1,
"name": "key2",
"title": "Второй ключ",
"type": "TEXT",
"is_required": 0,
"size": null,
"default_value": "",
"hint": null,
"tooltip": null,
"sort_order": 3,
"precision": null,
"is_sync_struct": 1,
"is_locked_for_regular_user": 0,
"max_width_px": null
},
"key3": {
"is_enabled": 1,
"name": "key3",
"title": "Третий ключ",
"type": "TEXT",
"is_required": 0,
"size": null,
"default_value": "",
"hint": null,
"tooltip": null,
"sort_order": 4,
"precision": null,
"is_sync_struct": 1,
"is_locked_for_regular_user": 0,
"max_width_px": null
},
"value_type": {
"is_enabled": 1,
"name": "value_type",
"title": "Тип значения",
"type": "TEXT",
"is_required": 0,
"size": null,
"default_value": "",
"hint": null,
"tooltip": null,
"sort_order": 5,
"precision": null,
"is_sync_struct": 1,
"is_locked_for_regular_user": 0,
"max_width_px": null
},
"value": {
"is_enabled": 1,
"name": "value",
"title": "Значение",
"type": "TEXT",
"is_required": 1,
"size": null,
"default_value": "",
"hint": null,
"tooltip": null,
"sort_order": 6,
"precision": null,
"is_sync_struct": 1,
"is_locked_for_regular_user": 0,
"max_width_px": null
},
"created_at": {
"is_enabled": 1,
"name": "created_at",
"title": "Дата создания",
"type": "CREATED_AT",
"is_required": 1,
"size": null,
"default_value": "NOW",
"hint": null,
"tooltip": null,
"sort_order": 7,
"precision": null,
"is_sync_struct": 1,
"is_locked_for_regular_user": 0,
"max_width_px": null
},
"updated_at": {
"is_enabled": 1,
"name": "updated_at",
"title": "Дата изменения",
"type": "UPDATED_AT",
"is_required": 0,
"size": null,
"default_value": null,
"hint": null,
"tooltip": null,
"sort_order": 8,
"precision": null,
"is_sync_struct": 1,
"is_locked_for_regular_user": 0,
"max_width_px": null
}
}
}
}
}
Важно: После создания таблицы вручную необходимо создать уникальный индекс на комбинацию (key, key2, key3) в базе данных.
Подключение плагина
Для использования плагина в JavaScript скрипте необходимо подключить его через require():
let customStorage = require('Common.Platform.CustomStorage')
После подключения необходимо установить имя таблицы, с которой будет работать плагин:
customStorage.setTableName('business_attributes')
Методы работы с таблицей
setTableName()
Устанавливает имя кастомной таблицы для работы.
Сигнатура:
customStorage.setTableName(string $tableName): self
Параметры:
-
$tableName(string) - Имя кастомной таблицы, которая должна существовать в текущем бизнесе
Возвращает: self - Возвращает сам объект для цепочки вызовов
Пример:
customStorage.setTableName('business_attributes')
getTableName()
Возвращает имя текущей установленной таблицы.
Сигнатура:
customStorage.getTableName(): string|null
Возвращает: string|null - Имя таблицы или null, если таблица не установлена
Пример:
let tableName = customStorage.getTableName()
bot.sendText('Работаем с таблицей: ' + tableName)
setup()
Создает новую кастомную таблицу для атрибутов со всеми необходимыми полями и уникальным индексом.
Сигнатура:
customStorage.setup(string $tableName): self
Параметры:
-
$tableName(string) - Имя создаваемой таблицы
Возвращает: self - Возвращает сам объект для цепочки вызовов
Исключения:
- Выбрасывает ошибку, если таблица с таким именем уже существует
- Выбрасывает ошибку, если не указан Business ID
Пример:
// Создаем новую таблицу
customStorage.setup('my_attributes_table')
// Теперь можно работать с ней
customStorage.setAttr('test_key', 'test_value')
Важно: Метод setup() автоматически устанавливает созданную таблицу как текущую, поэтому после вызова setup() можно сразу начинать работу с атрибутами.
Методы работы с обычными атрибутами
setAttr()
Устанавливает значение обычного атрибута в базе данных.
Сигнатура:
customStorage.setAttr(string $key, mixed $value, ?string $key2 = null, ?string $key3 = null): self
Параметры:
-
$key(string) - Ключ атрибута (обязательный) -
$value(mixed) - Значение атрибута (строка, число, булево значение) -
$key2(string|null) - Второй ключ для подкатегории (опциональный) -
$key3(string|null) - Третий ключ для подкатегории (опциональный)
Возвращает: self - Возвращает сам объект для цепочки вызовов
Примеры:
// Простое сохранение
customStorage.setAttr('server_name', 'production-server-01')
customStorage.setAttr('server_port', '8080')
// С подкатегориями
customStorage.setAttr('server_name', 'server-01', 'region1', 'dc1')
customStorage.setAttr('server_ip', '192.168.1.1', 'region1', 'dc1')
Примечание: Если для комбинации (key, key2, key3) уже существует запись, она будет обновлена. Если найдено несколько записей, будет выброшена ошибка.
getAttr()
Получает значение обычного атрибута из базы данных.
Сигнатура:
customStorage.getAttr(string $key, ?string $key2 = null, ?string $key3 = null): mixed|null
Параметры:
-
$key(string) - Ключ атрибута (обязательный) -
$key2(string|null) - Второй ключ для подкатегории (опциональный) -
$key3(string|null) - Третий ключ для подкатегории (опциональный)
Возвращает: mixed|null - Значение атрибута или null, если атрибут не найден
Примеры:
// Простое получение
let serverName = customStorage.getAttr('server_name')
// С подкатегориями
let serverName = customStorage.getAttr('server_name', 'region1', 'dc1')
let serverIp = customStorage.getAttr('server_ip', 'region1', 'dc1')
Примечание: Если для комбинации (key, key2, key3) найдено несколько записей, будет выброшена ошибка.
getIntAttr()
Получает значение атрибута, преобразованное в целое число.
Сигнатура:
customStorage.getIntAttr(string $key, ?int $default = 0, ?string $key2 = null, ?string $key3 = null): int|null
Параметры:
-
$key(string) - Ключ атрибута (обязательный) -
$default(int|null) - Значение по умолчанию, если атрибут не найден (по умолчанию:0) -
$key2(string|null) - Второй ключ для подкатегории (опциональный) -
$key3(string|null) - Третий ключ для подкатегории (опциональный)
Возвращает: int|null - Целое число или значение по умолчанию
Примеры:
// Получение с значением по умолчанию
let port = customStorage.getIntAttr('server_port', 8080)
let nonExistent = customStorage.getIntAttr('non_existent', 100) // вернет 100
// С подкатегориями
let port = customStorage.getIntAttr('server_port', 8080, 'region1', 'dc1')
getFloatAttr()
Получает значение атрибута, преобразованное в число с плавающей точкой.
Сигнатура:
customStorage.getFloatAttr(string $key, ?float $default = 0.0, ?string $key2 = null, ?string $key3 = null): float|null
Параметры:
-
$key(string) - Ключ атрибута (обязательный) -
$default(float|null) - Значение по умолчанию, если атрибут не найден (по умолчанию:0.0) -
$key2(string|null) - Второй ключ для подкатегории (опциональный) -
$key3(string|null) - Третий ключ для подкатегории (опциональный)
Возвращает: float|null - Число с плавающей точкой или значение по умолчанию
Примеры:
let price = customStorage.getFloatAttr('price', 0.0)
let nonExistent = customStorage.getFloatAttr('non_existent', 2.5) // вернет 2.5
getBoolAttr()
Получает значение атрибута, преобразованное в булево значение.
Сигнатура:
customStorage.getBoolAttr(string $key, ?bool $default = false, ?string $key2 = null, ?string $key3 = null): bool|null
Параметры:
-
$key(string) - Ключ атрибута (обязательный) -
$default(bool|null) - Значение по умолчанию, если атрибут не найден (по умолчанию:false) -
$key2(string|null) - Второй ключ для подкатегории (опциональный) -
$key3(string|null) - Третий ключ для подкатегории (опциональный)
Возвращает: bool|null - Булево значение или значение по умолчанию
Особенности обработки строковых значений:
- Строки
'false','0','','no','off'преобразуются вfalse - Строки
'true','1','yes','on'преобразуются вtrue - Остальные строки преобразуются по стандартным правилам PHP
Примеры:
let isActive = customStorage.getBoolAttr('is_active', false)
let nonExistent = customStorage.getBoolAttr('non_existent', true) // вернет true
// Строковые значения обрабатываются корректно
customStorage.setAttr('bool_true', 'true')
customStorage.setAttr('bool_false', 'false')
let val1 = customStorage.getBoolAttr('bool_true') // вернет true
let val2 = customStorage.getBoolAttr('bool_false') // вернет false
isAttrExist()
Проверяет существование атрибута в базе данных.
Сигнатура:
customStorage.isAttrExist(string $key, ?string $key2 = null, ?string $key3 = null): bool
Параметры:
-
$key(string) - Ключ атрибута (обязательный) -
$key2(string|null) - Второй ключ для подкатегории (опциональный) -
$key3(string|null) - Третий ключ для подкатегории (опциональный)
Возвращает: bool - true, если атрибут существует, false - если не существует
Примеры:
if (customStorage.isAttrExist('server_name')) {
bot.sendText('Сервер найден: ' + customStorage.getAttr('server_name'))
} else {
bot.sendText('Сервер не найден')
}
// С подкатегориями
if (customStorage.isAttrExist('server_name', 'region1', 'dc1')) {
bot.sendText('Сервер найден в регионе 1')
}
issetAttr()
Проверяет существование атрибута в базе данных (аналог isAttrExist()).
Сигнатура:
customStorage.issetAttr(string $key, ?string $key2 = null, ?string $key3 = null): bool
Параметры:
-
$key(string) - Ключ атрибута (обязательный) -
$key2(string|null) - Второй ключ для подкатегории (опциональный) -
$key3(string|null) - Третий ключ для подкатегории (опциональный)
Возвращает: bool - true, если атрибут существует, false - если не существует
Примеры:
if (customStorage.issetAttr('server_name')) {
bot.sendText('Сервер найден')
}
deleteAttr()
Удаляет атрибут из базы данных.
Сигнатура:
customStorage.deleteAttr(string $key, ?string $key2 = null, ?string $key3 = null): self
Параметры:
-
$key(string) - Ключ атрибута (обязательный) -
$key2(string|null) - Второй ключ для подкатегории (опциональный) -
$key3(string|null) - Третий ключ для подкатегории (опциональный)
Возвращает: self - Возвращает сам объект для цепочки вызовов
Примеры:
// Удаление простого атрибута
customStorage.deleteAttr('server_port')
// Удаление с подкатегориями
customStorage.deleteAttr('server_name', 'region1', 'dc1')
getAllAttr()
Получает все обычные атрибуты (не JSON) из базы данных.
Сигнатура:
customStorage.getAllAttr(): array
Возвращает: array - Ассоциативный массив всех атрибутов, где ключ - это комбинация key, key2, key3 (разделенные точками), а значение - значение атрибута
Примеры:
let allAttrs = customStorage.getAllAttr()
bot.sendText('Всего атрибутов: ' + Object.keys(allAttrs).length)
// Пример структуры результата:
// {
// "server_name": "server-01",
// "server_name.region1.dc1": "server-01",
// "server_ip.region1.dc1": "192.168.1.1"
// }
Методы работы с JSON атрибутами
setJsonAttr()
Устанавливает значение JSON атрибута в базе данных. Поддерживает точечную нотацию для вложенных структур.
Сигнатура:
customStorage.setJsonAttr(string $key, mixed $value, ?string $key2 = null, ?string $key3 = null): self
Параметры:
-
$key(string) - Ключ атрибута. Может использоваться точечная нотация для вложенных структур (например,user.profile.name) -
$value(mixed) - Значение для установки. Может быть объектом, массивом, строкой, числом, булевым значением -
$key2(string|null) - Второй ключ для подкатегории (опциональный, не связан с вложенностью JSON) -
$key3(string|null) - Третий ключ для подкатегории (опциональный, не связан с вложенностью JSON)
Возвращает: self - Возвращает сам объект для цепочки вызовов
Особенности:
-
Точечная нотация: При использовании точечной нотации (например,
user.profile.name) значение устанавливается внутрь существующего JSON объекта. Верхний ключ (в данном случаеuser) используется какkeyв БД, а остальные части пути создают вложенную структуру. - Без точечной нотации: Если точечная нотация не используется, значение должно быть массивом или объектом (для верхнего уровня).
- С точечной нотацией: Разрешены любые типы значений (строки, числа, bool, массивы, объекты).
Примеры:
// Простой JSON объект
customStorage.setJsonAttr('config', {
timeout: 30,
retries: 3,
enabled: true
})
// Точечная нотация для вложенных значений
customStorage.setJsonAttr('user.profile.name', 'John Doe')
customStorage.setJsonAttr('user.profile.email', 'john@example.com')
customStorage.setJsonAttr('user.profile.age', 30)
customStorage.setJsonAttr('user.settings.theme', 'dark')
// Глубокая вложенность
customStorage.setJsonAttr('app.settings.database.host', 'localhost')
customStorage.setJsonAttr('app.settings.database.port', 5432)
customStorage.setJsonAttr('app.settings.cache.enabled', true)
// С подкатегориями (key2, key3)
customStorage.setJsonAttr('server.config', { cpu: 8, ram: 32 }, 'region1', 'dc1')
customStorage.setJsonAttr('server.config', { cpu: 16, ram: 64 }, 'region2', 'dc2')
// Массивы
customStorage.setJsonAttr('packages', [
{ name: 'nginx', version: '1.18.0' },
{ name: 'php', version: '8.1.0' },
{ name: 'mysql', version: '8.0.0' }
])
Примечание: Если для комбинации (key, key2, key3) уже существует запись, JSON объект будет обновлен. Если найдено несколько записей, будет выброшена ошибка.
getJsonAttr()
Получает значение JSON атрибута из базы данных. Поддерживает точечную нотацию для извлечения вложенных значений.
Сигнатура:
customStorage.getJsonAttr(string $key, ?string $key2 = null, ?string $key3 = null): mixed|null
Параметры:
-
$key(string) - Ключ атрибута. Может использоваться точечная нотация для вложенных структур (например,user.profile.name) -
$key2(string|null) - Второй ключ для подкатегории (опциональный) -
$key3(string|null) - Третий ключ для подкатегории (опциональный)
Возвращает: mixed|null - Значение JSON атрибута или null, если атрибут не найден
Примеры:
// Получение простого JSON объекта
let config = customStorage.getJsonAttr('config')
if (config) {
bot.sendText('Timeout: ' + config.timeout)
bot.sendText('Retries: ' + config.retries)
}
// Получение вложенных значений через точечную нотацию
let userName = customStorage.getJsonAttr('user.profile.name')
let userEmail = customStorage.getJsonAttr('user.profile.email')
let userAge = customStorage.getJsonAttr('user.profile.age')
// Получение всего объекта верхнего уровня
let userProfile = customStorage.getJsonAttr('user')
// userProfile будет содержать: { profile: { name: "John Doe", email: "john@example.com", age: 30 }, settings: { theme: "dark" } }
// Глубокая вложенность
let dbHost = customStorage.getJsonAttr('app.settings.database.host')
let dbPort = customStorage.getJsonAttr('app.settings.database.port')
// С подкатегориями
let serverConfig = customStorage.getJsonAttr('server.config', 'region1', 'dc1')
if (serverConfig) {
bot.sendText('CPU: ' + serverConfig.cpu + ' cores')
bot.sendText('RAM: ' + serverConfig.ram + ' GB')
}
Примечание: Если для комбинации (key, key2, key3) найдено несколько записей, будет выброшена ошибка.
isJsonAttrKeyExist()
Проверяет существование JSON атрибута в базе данных.
Сигнатура:
customStorage.isJsonAttrKeyExist(string $key, ?string $key2 = null, ?string $key3 = null): bool
Параметры:
-
$key(string) - Ключ атрибута. Может использоваться точечная нотация (например,user.profile.name) -
$key2(string|null) - Второй ключ для подкатегории (опциональный) -
$key3(string|null) - Третий ключ для подкатегории (опциональный)
Возвращает: bool - true, если JSON атрибут существует, false - если не существует
Примеры:
if (customStorage.isJsonAttrKeyExist('user.profile.name')) {
bot.sendText('Имя пользователя: ' + customStorage.getJsonAttr('user.profile.name'))
}
// С подкатегориями
if (customStorage.isJsonAttrKeyExist('server.config', 'region1', 'dc1')) {
bot.sendText('Конфигурация сервера найдена')
}
issetJsonAttr()
Проверяет существование JSON атрибута в базе данных (аналог isJsonAttrKeyExist()).
Сигнатура:
customStorage.issetJsonAttr(string $key, ?string $key2 = null, ?string $key3 = null): bool
Параметры:
-
$key(string) - Ключ атрибута. Может использоваться точечная нотация (например,user.profile.name) -
$key2(string|null) - Второй ключ для подкатегории (опциональный) -
$key3(string|null) - Третий ключ для подкатегории (опциональный)
Возвращает: bool - true, если JSON атрибут существует, false - если не существует
Примеры:
if (customStorage.issetJsonAttr('user.profile.email')) {
bot.sendText('Email установлен')
}
deleteJsonAttr()
Удаляет JSON атрибут из базы данных.
Сигнатура:
customStorage.deleteJsonAttr(string $key, ?string $key2 = null, ?string $key3 = null): self
Параметры:
-
$key(string) - Ключ атрибута. Может использоваться точечная нотация (например,user.profile.name) -
$key2(string|null) - Второй ключ для подкатегории (опциональный) -
$key3(string|null) - Третий ключ для подкатегории (опциональный)
Возвращает: self - Возвращает сам объект для цепочки вызовов
Важно: При удалении через точечную нотацию удаляется весь объект верхнего уровня. Например, deleteJsonAttr('user') удалит всю запись с key='user', включая все вложенные значения (user.profile.name, user.profile.email и т.д.).
Примеры:
// Удаление простого JSON атрибута
customStorage.deleteJsonAttr('config')
// Удаление вложенного JSON атрибута (удаляет весь верхний уровень)
customStorage.deleteJsonAttr('user') // Удалит всю запись с key='user'
// Удаление с подкатегориями
customStorage.deleteJsonAttr('server.config', 'region1', 'dc1')
getAllJsonAttrs()
Получает все JSON атрибуты из базы данных.
Сигнатура:
customStorage.getAllJsonAttrs(): array
Возвращает: array - Ассоциативный массив всех JSON атрибутов, где ключ - это комбинация key, key2, key3 (разделенные точками), а значение - распарсенный JSON объект
Примеры:
let allJsonAttrs = customStorage.getAllJsonAttrs()
bot.sendText('Всего JSON атрибутов: ' + Object.keys(allJsonAttrs).length)
// Пример структуры результата:
// {
// "config": { timeout: 30, retries: 3, enabled: true },
// "user": { profile: { name: "John Doe", email: "john@example.com" } },
// "server.region1.dc1": { config: { cpu: 8, ram: 32 } }
// }
Полный справочник методов
Методы работы с таблицей
| Метод | Описание |
| setTableName(string $tableName): self | Устанавливает имя кастомной таблицы для работы |
| getTableName(): string|null | Возвращает имя текущей установленной таблицы |
| setup(string $tableName): self | Создает новую кастомную таблицу для атрибутов со всеми необходимыми полями и уникальным индексом |
Методы работы с обычными атрибутами
| Метод | Описание |
| setAttr(string $key, mixed $value, ?string $key2 = null, ?string $key3 = null): self | Устанавливает значение обычного атрибута в базе данных |
| getAttr(string $key, ?string $key2 = null, ?string $key3 = null): mixed|null | Получает значение обычного атрибута из базы данных |
| getIntAttr(string $key, ?int $default = 0, ?string $key2 = null, ?string $key3 = null): int|null | Получает значение атрибута, преобразованное в целое число |
| getFloatAttr(string $key, ?float $default = 0.0, ?string $key2 = null, ?string $key3 = null): float|null | Получает значение атрибута, преобразованное в число с плавающей точкой |
| getBoolAttr(string $key, ?bool $default = false, ?string $key2 = null, ?string $key3 = null): bool|null | Получает значение атрибута, преобразованное в булево значение |
| isAttrExist(string $key, ?string $key2 = null, ?string $key3 = null): bool | Проверяет существование атрибута в базе данных |
| issetAttr(string $key, ?string $key2 = null, ?string $key3 = null): bool | Проверяет существование атрибута в базе данных (аналог isAttrExist) |
| deleteAttr(string $key, ?string $key2 = null, ?string $key3 = null): self | Удаляет атрибут из базы данных |
| getAllAttr(): array | Получает все обычные атрибуты (не JSON) из базы данных |
Методы работы с JSON атрибутами
| Метод | Описание |
| setJsonAttr(string $key, mixed $value, ?string $key2 = null, ?string $key3 = null): self | Устанавливает значение JSON атрибута в базе данных. Поддерживает точечную нотацию для вложенных структур |
| getJsonAttr(string $key, ?string $key2 = null, ?string $key3 = null): mixed|null | Получает значение JSON атрибута из базы данных. Поддерживает точечную нотацию для извлечения вложенных значений |
| isJsonAttrKeyExist(string $key, ?string $key2 = null, ?string $key3 = null): bool | Проверяет существование JSON атрибута в базе данных |
| issetJsonAttr(string $key, ?string $key2 = null, ?string $key3 = null): bool | Проверяет существование JSON атрибута в базе данных (аналог isJsonAttrKeyExist) |
| deleteJsonAttr(string $key, ?string $key2 = null, ?string $key3 = null): self | Удаляет JSON атрибут из базы данных |
| getAllJsonAttrs(): array | Получает все JSON атрибуты из базы данных |
Примеры использования
Пример 1: Базовое использование
let customStorage = require('Common.Platform.CustomStorage')
// Устанавливаем таблицу
customStorage.setTableName('business_attributes')
// Сохранение простых атрибутов
customStorage.setAttr('server_name', 'production-server-01')
customStorage.setAttr('server_ip', '192.168.1.100')
customStorage.setAttr('server_port', '8080')
// Получение атрибутов
let serverName = customStorage.getAttr('server_name')
let serverIp = customStorage.getAttr('server_ip')
let port = customStorage.getIntAttr('server_port', 0)
bot.sendText('Сервер: ' + serverName + ' (' + serverIp + ':' + port + ')')
Пример 2: Использование подкатегорий (key2, key3)
let customStorage = require('Common.Platform.CustomStorage')
customStorage.setTableName('business_attributes')
// Сохранение данных для разных регионов и датацентров
customStorage.setAttr('server_name', 'server-01', 'us-east', 'dc-01')
customStorage.setAttr('server_ip', '10.0.1.10', 'us-east', 'dc-01')
customStorage.setAttr('server_name', 'server-02', 'us-east', 'dc-02')
customStorage.setAttr('server_ip', '10.0.2.10', 'us-east', 'dc-02')
// Получение данных для конкретного региона и датацентра
let serverName = customStorage.getAttr('server_name', 'us-east', 'dc-01')
let serverIp = customStorage.getAttr('server_ip', 'us-east', 'dc-01')
bot.sendText('Сервер в us-east/dc-01: ' + serverName + ' (' + serverIp + ')')
Пример 3: Работа с JSON атрибутами
let customStorage = require('Common.Platform.CustomStorage')
customStorage.setTableName('business_attributes')
// Сохранение простого JSON объекта
customStorage.setJsonAttr('config', {
timeout: 30,
retries: 3,
enabled: true
})
// Получение JSON объекта
let config = customStorage.getJsonAttr('config')
if (config) {
bot.sendText('Timeout: ' + config.timeout)
bot.sendText('Retries: ' + config.retries)
bot.sendText('Enabled: ' + config.enabled)
}
Пример 4: Точечная нотация для вложенных JSON структур
let customStorage = require('Common.Platform.CustomStorage')
customStorage.setTableName('business_attributes')
// Установка вложенных значений через точечную нотацию
customStorage.setJsonAttr('user.profile.name', 'John Doe')
customStorage.setJsonAttr('user.profile.email', 'john@example.com')
customStorage.setJsonAttr('user.profile.age', 30)
customStorage.setJsonAttr('user.settings.theme', 'dark')
customStorage.setJsonAttr('user.settings.language', 'ru')
// Получение вложенных значений
let userName = customStorage.getJsonAttr('user.profile.name')
let userEmail = customStorage.getJsonAttr('user.profile.email')
let userTheme = customStorage.getJsonAttr('user.settings.theme')
bot.sendText('Пользователь: ' + userName + ' (' + userEmail + ')')
bot.sendText('Тема: ' + userTheme)
// Получение всего объекта верхнего уровня
let userProfile = customStorage.getJsonAttr('user')
// userProfile будет содержать: { profile: { name: "John Doe", email: "john@example.com", age: 30 }, settings: { theme: "dark", language: "ru" } }
Пример 5: JSON атрибуты с подкатегориями
let customStorage = require('Common.Platform.CustomStorage')
customStorage.setTableName('business_attributes')
// Сохранение JSON конфигурации для разных регионов
customStorage.setJsonAttr('server.config', { cpu: 8, ram: 32 }, 'region1', 'dc1')
customStorage.setJsonAttr('server.config', { cpu: 16, ram: 64 }, 'region2', 'dc2')
// Получение конфигурации для конкретного региона
let config1 = customStorage.getJsonAttr('server.config', 'region1', 'dc1')
let config2 = customStorage.getJsonAttr('server.config', 'region2', 'dc2')
if (config1) {
bot.sendText('Регион 1: CPU=' + config1.cpu + ', RAM=' + config1.ram)
}
if (config2) {
bot.sendText('Регион 2: CPU=' + config2.cpu + ', RAM=' + config2.ram)
}
Пример 6: Глубокая вложенность JSON
let customStorage = require('Common.Platform.CustomStorage')
customStorage.setTableName('business_attributes')
// Установка глубоко вложенных значений
customStorage.setJsonAttr('app.settings.database.host', 'localhost')
customStorage.setJsonAttr('app.settings.database.port', 5432)
customStorage.setJsonAttr('app.settings.database.name', 'myapp')
customStorage.setJsonAttr('app.settings.cache.enabled', true)
customStorage.setJsonAttr('app.settings.cache.ttl', 3600)
// Получение глубоко вложенных значений
let dbHost = customStorage.getJsonAttr('app.settings.database.host')
let dbPort = customStorage.getJsonAttr('app.settings.database.port')
let cacheEnabled = customStorage.getJsonAttr('app.settings.cache.enabled')
bot.sendText('DB Host: ' + dbHost)
bot.sendText('DB Port: ' + dbPort)
bot.sendText('Cache Enabled: ' + cacheEnabled)
Пример 7: Работа с массивами
let customStorage = require('Common.Platform.CustomStorage')
customStorage.setTableName('business_attributes')
// Сохранение массива
customStorage.setJsonAttr('packages', [
{ name: 'nginx', version: '1.18.0' },
{ name: 'php', version: '8.1.0' },
{ name: 'mysql', version: '8.0.0' }
])
// Получение массива
let packages = customStorage.getJsonAttr('packages')
if (packages && Array.isArray(packages)) {
bot.sendText('Установлено пакетов: ' + packages.length)
packages.forEach(function(pkg) {
bot.sendText('- ' + pkg.name + ' v' + pkg.version)
})
}
Пример 8: Проверка существования и удаление
let customStorage = require('Common.Platform.CustomStorage')
customStorage.setTableName('business_attributes')
// Проверка существования
if (customStorage.isAttrExist('server_name')) {
let serverName = customStorage.getAttr('server_name')
bot.sendText('Сервер найден: ' + serverName)
} else {
bot.sendText('Сервер не найден')
}
// Проверка JSON атрибута
if (customStorage.isJsonAttrKeyExist('user.profile.name')) {
let userName = customStorage.getJsonAttr('user.profile.name')
bot.sendText('Имя пользователя: ' + userName)
}
// Удаление атрибутов
customStorage.deleteAttr('server_port')
customStorage.deleteJsonAttr('config')
// Удаление с подкатегориями
customStorage.deleteAttr('server_name', 'region1', 'dc1')
customStorage.deleteJsonAttr('server.config', 'region1', 'dc1')
Пример 9: Массовые операции
let customStorage = require('Common.Platform.CustomStorage')
customStorage.setTableName('business_attributes')
// Получение всех обычных атрибутов
let allAttrs = customStorage.getAllAttr()
bot.sendText('Всего обычных атрибутов: ' + Object.keys(allAttrs).length)
// Получение всех JSON атрибутов
let allJsonAttrs = customStorage.getAllJsonAttrs()
bot.sendText('Всего JSON атрибутов: ' + Object.keys(allJsonAttrs).length)
Важные замечания
Безопасность
- Плагин работает только с кастомными таблицами текущего бизнеса
- Все операции проверяют принадлежность таблицы к текущему бизнесу
- При попытке доступа к таблице другого бизнеса будет выброшена ошибка
Уникальность записей
- В таблице создается уникальный индекс на комбинацию
(key, key2, key3) - При попытке установить значение для существующей комбинации
(key, key2, key3)запись будет обновлена - Если в базе данных найдено несколько записей для одной комбинации
(key, key2, key3), будет выброшена ошибка с просьбой вручную разрешить дубликаты
Работа с памятью
- Плагин работает напрямую с базой данных без кэширования в памяти
- Все операции чтения и записи выполняются непосредственно в БД
- Это обеспечивает актуальность данных, но может быть медленнее при частых обращениях
Точечная нотация для JSON
- При использовании точечной нотации (например,
user.profile.name) верхний ключ (user) используется какkeyв БД - Остальные части пути (
profile,name) создают вложенную структуру в JSON объекте - При установке нескольких значений с одним верхним ключом они объединяются в один JSON объект
Подкатегории key2 и key3
-
key2иkey3используются для группировки данных и не связаны с вложенностью JSON - Они позволяют хранить разные значения для одного и того же
keyв разных контекстах - Например, можно хранить конфигурацию сервера для разных регионов:
server.configсkey2='region1',key3='dc1'
Обработка ошибок
Все методы плагина могут выбрасывать исключения типа UnauthotizedV8Exception в следующих случаях:
- Таблица не найдена
- Таблица не принадлежит текущему бизнесу
- Найдено несколько записей для одной комбинации
(key, key2, key3) - Ошибка при работе с базой данных
Важно: В V8Js невозможно отлавливать исключения бэкенда через try/catch в JavaScript. Поэтому рекомендуется использовать специальные методы проверки существования атрибутов перед их использованием:
Проверка существования обычных атрибутов
Для проверки существования обычных атрибутов используйте методы isAttrExist() или issetAttr():
let customStorage = require('Common.Platform.CustomStorage')
customStorage.setTableName('business_attributes')
// Безопасное получение атрибута с проверкой существования
if (customStorage.isAttrExist('server_name')) {
let value = customStorage.getAttr('server_name')
bot.sendText('Значение: ' + value)
} else {
bot.sendText('Атрибут не найден')
}
// Альтернативный вариант с issetAttr()
if (customStorage.issetAttr('server_name')) {
let value = customStorage.getAttr('server_name')
bot.sendText('Значение: ' + value)
}
// С подкатегориями
if (customStorage.isAttrExist('server_name', 'region1', 'dc1')) {
let value = customStorage.getAttr('server_name', 'region1', 'dc1')
bot.sendText('Сервер в регионе 1: ' + value)
}
Проверка существования JSON атрибутов
Для проверки существования JSON атрибутов используйте методы isJsonAttrKeyExist() или issetJsonAttr():
let customStorage = require('Common.Platform.CustomStorage')
customStorage.setTableName('business_attributes')
// Проверка простого JSON атрибута
if (customStorage.isJsonAttrKeyExist('config')) {
let config = customStorage.getJsonAttr('config')
bot.sendText('Timeout: ' + config.timeout)
}
// Проверка вложенного JSON атрибута с точечной нотацией
if (customStorage.isJsonAttrKeyExist('user.profile.name')) {
let userName = customStorage.getJsonAttr('user.profile.name')
bot.sendText('Имя пользователя: ' + userName)
}
// Альтернативный вариант с issetJsonAttr()
if (customStorage.issetJsonAttr('user.profile.email')) {
let email = customStorage.getJsonAttr('user.profile.email')
bot.sendText('Email: ' + email)
}
// С подкатегориями
if (customStorage.isJsonAttrKeyExist('server.config', 'region1', 'dc1')) {
let config = customStorage.getJsonAttr('server.config', 'region1', 'dc1')
bot.sendText('CPU: ' + config.cpu + ' cores')
}
Рекомендация: Всегда используйте методы проверки существования (isAttrExist(), issetAttr(), isJsonAttrKeyExist(), issetJsonAttr()) вместо проверки на null после вызова getAttr() или getJsonAttr(). Это более явный и безопасный способ проверки.
Сравнение с атрибутами лидов
Плагин CustomStorage предоставляет аналогичный функционал, что и работа с атрибутами лидов через lead.setAttr() и lead.getAttr() и т.д., но с дополнительными возможностями:
| Функция | lead | customStorage |
|---|---|---|
| Работа с обычными атрибутами | ✅ | ✅ |
| Работа с JSON атрибутами | ✅ | ✅ |
| Точечная нотация для JSON | ✅ | ✅ |
| Подкатегории (key2, key3) | ❌ | ✅ |
| Работа с кастомными таблицами | ❌ | ✅ |
| Прямая работа с БД | ❌ | ✅ |
Заключение
Плагин CustomStorage предоставляет мощный инструмент для работы с кастомными таблицами атрибутов в JavaScript скриптах. Он позволяет гибко хранить и получать данные с поддержкой подкатегорий и вложенных JSON структур, что делает его незаменимым инструментом для сложных сценариев работы с данными.