Блок «Скрипт (JS) / Калькулятор»

Блок выполняет ваш JavaScript-код в защищённой песочнице и складывает результат в переменные клиента. Это нужно, когда возможностей блоков «Условие» и «Действие» не хватает: посчитать сложную скидку, разобрать JSON из вебхука, сгенерировать промокод, провести нестандартную проверку.

Код запускается не в браузере, а на сервере, в изолированной среде (isolated-vm). У него нет доступа к файловой системе, сети и системным объектам — только к переменным клиента и набору безопасных функций. Любая ошибка или зависание не уронят воронку: блок просто отработает «вхолостую» и flow пойдёт дальше.

Когда использовать

  • Сложные вычисления, которые не выражаются простой формулой (многоуровневые скидки, тарифы, баллы лояльности).
  • Разбор JSON-ответа от внешнего сервиса (поле из вебхука или API-блока).
  • Генерация значений: промокод, случайный номер, форматированная строка.
  • Проверки и трансформации данных перед отправкой клиенту.

Настройки

В редакторе блока есть вкладка «Скрипт (JS)» с этими полями:

  • JS код — основное поле. Сюда пишете JavaScript. Доступны `variables` (чтение/запись), `client` (read-only снимок профиля), `system`, `input` (последнее сообщение клиента) и набор helper-функций. Чтобы вернуть значение, используйте `return`. Поле сохраняется в `scriptCode`. Лимит длины кода — 50 000 символов.
  • Таймаут выполнения (мс) — сколько максимум ждать выполнения скрипта. Диапазон 500–30000 мс, шаг 500, по умолчанию 5000 мс. Сохраняется в `scriptTimeoutMs`.
  • Шпаргалка — раскрывающийся блок со списком доступных переменных, helper-функций и ограничений.
  • Примеры скриптов — готовые сниппеты (подсчёт скидки, парсинг JSON, генерация кода), вставляются в редактор одним кликом.

Отдельно есть вкладка «Калькулятор» (поле `calculatorCode`) — упрощённый язык формул в стиле Salebot для тех, кому не нужен полноценный JS. В нём 120+ готовых функций (`round`, `if`, `upper`, `tg_send_message`, `requests_get` и др.), браузер переменных `#{var}` и поиск функций. Калькулятор удобнее для коротких формул вида `total = #{price} * #{qty}`.

Как работает

  • Блок последовательный: у него нет веток «Да/Нет». После выполнения flow всегда идёт к следующему блоку.
  • Переменные передаются в скрипт через объект `variables`. Менять их можно напрямую (`variables.x = ...`) или через `setVariable(key, value)`. После выполнения все изменённые переменные сохраняются клиенту в базу.
  • Профиль клиента доступен только для чтения: `client.firstName`, `client.lastName`, `client.email`, `client.phone`, `client.username`, `client.platform`.
  • Доступны безопасные встроенные функции: `JSON.parse/stringify`, `Math.*`, `Date`, `console.log`, а также helpers — `round`, `floor`, `ceil`, `random`, `randomInt`, `trim`, `lower`, `upper`, `contains`, `split`, `join`, `keys`, `values`, `first`, `last`, `unique`, `sum`, `avg`, `filter`, `map`, `find`, `sort` и другие.
  • Лимит памяти — 128 MB. Таймаут — из настройки блока (по умолчанию 5000 мс).
  • Код выполняется фоновым воркером через очередь. Если воркер завис, общее ожидание ограничено wall-clock лимитом (таймаут + 5 секунд, но не более 30 секунд).

Пример

Многоуровневая скидка по сумме заказа:

``` const total = Number(variables.order_total) || 0; let discount = 0; if (total > 10000) discount = 15; else if (total > 5000) discount = 10; else if (total > 1000) discount = 5; setVariable('discount_percent', discount); return discount; ```

После блока переменная `#{discount_percent}` доступна в следующих блоках — например, в тексте сообщения «Ваша скидка: #{discount_percent}%».

Частые ошибки

  • `fetch`, `require`, `import` недоступны. Сеть из песочницы закрыта. Если нужен HTTP-запрос — используйте отдельный блок «API» или функции `requests_*` во вкладке «Калькулятор». Запрещены также `process`, `eval`, `Function`, `fs`, доступ к `constructor` и `__proto__` — такой код блок отклонит с ошибкой «Forbidden pattern».
  • Скрипт молча «ничего не сделал». Если код упал с ошибкой, превысил таймаут или лимит памяти, блок логирует ошибку и НЕ прерывает flow — переменные просто не обновятся. Проверьте логику через примеры и `console.log`, и оборачивайте разбор JSON в `try/catch`.
  • Переменные приходят строками. Значения из `variables` часто текстовые. Перед арифметикой приводите тип: `Number(variables.price)`, иначе вместо суммы получите склейку строк.
  • Слишком маленький таймаут для тяжёлой логики. Если скрипт сложный, поднимите «Таймаут выполнения» (до 30000 мс). При превышении блок вернёт ошибку таймаута и переменные не сохранятся.