Код в приложении
Почти всё в мини-приложении собирается без кода. Когда готовых элементов и блоков не хватает, свой код можно добавить в трёх местах:
| Где | Когда выполняется | Для чего |
|---|---|---|
| Блок «JavaScript» | Когда сценарий дошёл до блока | Действие по нажатию: скопировать текст, открыть ссылку, посчитать значение, поговорить с мостом мессенджера |
| Код приложения | Один раз при запуске, до первого экрана, и дальше — по событиям | Счётчики аналитики, свои стили, внешние виджеты |
| «HTML-код» — элемент или весь экран | Пока экран открыт | Своя вёрстка, калькулятор, виджет стороннего сервиса |
Код выполняется на устройстве человека. Поэтому человек, который разбирается в браузере, может увидеть его и подделать то, что код отправляет на сервер. Не храните в коде секретные ключи и ограничивайте, какие переменные и теги код может менять, — об этом ниже.
Блок «JavaScript»
Блок из категории «Данные и логика». Когда приложение доходит до блока, оно выполняет код и идёт дальше по выходу «Выполнено» или «Ошибка».
| Настройка | Что задаёт |
|---|---|
| «Код» | Тело асинхронной функции: пишите с await и объектом app. До 20 000 символов |
| «Время на выполнение, секунд» | От 1 до 60, по умолчанию 10. Не уложился — сценарий пойдёт по выходу «Ошибка», а сам код может доработать в фоне |
| «Переменная результата» | Что вернул return. Объект запишется как JSON |
| «Переменная ошибки» | Текст ошибки — для экрана после выхода «Ошибка» |
| «Что может менять код» | Список разрешённого — см. ниже |


Подстановки {{ имя }} в коде не работают: значения переменных читайте через app.variables.get().
Переменные, записанные через app.variables.set(), и изменённые теги уходят на сервер вместе с выходом блока.
«Что может менять код»
Переключатель «Менять только перечисленное» включён у нового блока. С ним сервер принимает от кода только:
- переменные из списка «Переменные»;
- теги из списков «Теги контакта бота» и «Теги пользователя приложения» — пустой список значит, что эти теги код не меняет вовсе;
- переменные результата и ошибки — они разрешены всегда.
Остальное сервер отбросит и запишет в журнал ошибок «Блок отбросил записанное кодом». Если код пишет имена, которых нет в списках, настройки блока предупредят об этом и предложат «Добавить в списки».
Выключайте ограничение, только если от переменных и тегов ничего не зависит: иначе человек сможет записать себе, например, тег VIP или скидку.
Объект app
| Вызов | Что делает |
|---|---|
app.variables.get('name') |
Значение переменной |
app.variables.set('name', value) |
Записать переменную — уйдёт на сервер с выходом блока |
app.variables.all() |
Все переменные объектом |
app.tags.has('vip') / add('vip') / remove('vip') / all() |
Теги пользователя приложения — есть у каждого, даже без чат-бота |
app.contact?.name |
Контакт чат-бота: id, name, first_name, last_name, username, phone, email, address, source, created_at, telegram_id, max_id. null, если приложение открыли не из бота |
app.contact.variables.get('name') / set('name', value) |
Свои переменные контакта, отдельно от переменных приложения. Записанное уйдёт в контакт с выходом блока |
app.contact.tags.has('vip') / add / remove / all() |
Теги контакта бота |
app.platform |
Откуда открыто: telegram, max, webchat, pwa или browser |
app.startParam |
Параметр ссылки запуска (startapp) |
app.user |
language, name и username в мессенджере |
app.device |
mode (ios или md), dark, standalone, width, height, userAgent |
await app.toast(message) |
Всплывающее сообщение |
await app.alert(message, title) |
Сообщение с кнопкой OK — ждёт, пока закроют |
await app.confirm(question) |
Вопрос «да или нет» — вернёт true или false |
app.openLink('https://…') |
Открыть ссылку: в мессенджере — его средствами, иначе в новой вкладке |
await app.copy(text) |
Скопировать в буфер обмена |
await app.share({title, text, url}) |
Системное меню «Поделиться», без него — копия ссылки |
app.vibrate('success') |
Вибрация: light, medium, heavy, success, warning или error |
app.localStorage.setItem('key', 'value') |
Хранилище пользователя на сервере — см. ниже |
app.storage.get('key') / set / remove |
Хранилище на устройстве, своё у приложения |
await app.sleep(500) |
Подождать указанное число миллисекунд |
app.close() |
Закрыть приложение |
app.telegram |
Telegram.WebApp как есть, вне Telegram — null |
app.max |
Мост MAX как есть, вне MAX — null |
Пример из настроек блока:
// Тема и ширина экрана — в переменные
const device = app.device
app.variables.set('theme', device.dark ? 'dark' : 'light')
if (app.platform === 'telegram') {
app.vibrate('success')
}
// Что вернули — попадёт в переменную результата
return device.width
Ошибки кода видны в консоли браузера в предпросмотре и попадают в журнал ошибок — «Ошибка в коде JavaScript».
Код приложения
Код на всё приложение: выполняется один раз при запуске, до первого экрана, и слушает события. Открывается кнопкой «Код приложения» (значок «</>») на панели над схемой. Если код задан, значок окрашен.
В окне «Код приложения»:
- «Код» — до 100 000 символов;
- «Переменные, которые код может записать» — до 50 имён. Остальные переменные сервер не примет;
- шпаргалка событий и «Пример» с кнопкой «Вставить пример».


Код хранится в версии: новая версия и шаблон получают его копию. После сохранения открытый предпросмотр перезапускается.
События
| Подписка | Когда срабатывает |
|---|---|
app.on('start', ({platform, startParam}) => {}) |
Приложение запустилось |
app.on('screen', ({id, name, type}) => {}) |
Показан экран или окно поверх него |
app.on('event', ({blockId, eventId}) => {}) |
Нажатие на кнопку, пункт или элемент — до отправки на сервер |
app.on('field', ({variable, value}) => {}) |
Человек изменил поле ввода |
app.on('error', ({message}) => {}) |
Приложение показало ошибку |
app.on('hide', () => {}) / app.on('show', () => {}) |
Приложение свернули и вернули |
app.on('theme', ({dark}) => {}) |
Сменилась светлая или тёмная тема |
app.on('storage', ({key, newValue}) => {}) |
Код элемента «HTML» записал в localStorage |
- Подписка на
start,screenиthemeсразу получает текущее значение — даже если подписались послеawait. app.on()возвращает функцию отписки:const stop = app.on(…); stop(). Есть иapp.off(name, handler).await app.loadScript('https://…')подключает внешний скрипт: счётчик, виджет, библиотеку. Адрес — толькоhttp://илиhttps://.
Остальное — как у блока «JavaScript»: app.variables, app.toast, app.alert, app.openLink, app.storage, app.localStorage, app.telegram и другие. app.contact и app.tags здесь только для чтения: менять теги — дело блоков тегов.
Переменные, записанные через app.variables.set(), уходят на сервер со следующим событием.
Пример из окна — Яндекс Метрика:
// Подключаем Яндекс Метрику
await app.loadScript('https://mc.yandex.ru/metrika/tag.js')
ym(12345678, 'init', {defer: true})
// Каждый экран — просмотр страницы
app.on('screen', ({id, name}) => {
ym(12345678, 'hit', '/screen/' + id, {title: name})
})
// Нажатия — цели
app.on('event', ({eventId}) => {
ym(12345678, 'reachGoal', 'click_' + eventId)
})
Ошибки кода приложения — при запуске и в обработчиках — видны в консоли и попадают в журнал ошибок с причиной «Ошибка в коде приложения» и стеком вызовов. Одна и та же ошибка записывается один раз.
«HTML-код»: элемент и экран
Свой HTML со скриптами и стилями бывает двух видов:
- элемент «HTML-код» — в группе «Виджеты», встаёт в область экрана рядом с другими элементами;
- экран «HTML-код» — третий вид экрана после «Стандартного окна» и «Вкладок снизу»: весь экран — ваш код, без шапки и областей. Новый такой экран сразу получает пример с кнопкой и выход
next. Модальным окном его сделать нельзя.
Код работает в изолированном фрейме: сессия, данные приложения и остальной экран ему недоступны. Скрипты и стили — внутри кода. Цвет текста фрейм берёт из темы приложения.
| Настройка | Что задаёт |
|---|---|
| «Код» | HTML до 100 000 символов |
| «Высота» (у элемента) | «По содержимому» — подстраивается под код, в том числе когда он меняет содержимое сам; «Задать» — от 40 до 2000 пикселей, всё, что не поместится, прокручивается внутри |
| «Выходы» | До 20. У выхода — «Ключ» (латиница, цифры, _ и -) и «Название на схеме». Выход — переход на другой блок схемы |
| «Переменные, которые код может записать» | До 50 имён. Остальные сервер не примет |


Что доступно коду во фрейме
| Вызов | Что делает |
|---|---|
app.event('next') |
Перейти по выходу с этим ключом |
app.event('next', {name: 'value'}) |
Перейти и записать разрешённые переменные |
app.variables.set('name', 'value') |
Записать разрешённую переменную сразу, без перехода: экран не меняется |
localStorage.setItem('key', 'value') |
Хранилище пользователя на сервере — см. ниже. sessionStorage живёт, пока открыт экран |
app.openLink('https://…') |
Открыть ссылку в браузере. Обычные ссылки <a> открываются так же |
{{ name }} |
Подставить значение переменной прямо в код — как текст, с экранированием |
На схеме код не запускается — вместо него заглушка. В редакторе экрана фрейм показывает результат и перезапускается после правки кода.
Хранилища
localStorage на сервере |
app.storage |
|
|---|---|---|
| Где доступно | localStorage во фрейме «HTML-код», app.localStorage в блоке «JavaScript» и коде приложения |
Блок «JavaScript» и код приложения |
| Где лежит | На сервере, у пользователя приложения | В браузере человека |
| Переживает | Перезапуск, очистку телефона, смену устройства в том же мессенджере | Пока не очищены данные браузера |
| Общее | Для всех элементов «HTML-код», кода приложения и блоков «JavaScript» одного приложения | Своё у каждого приложения |
| Значения | Только строки, до 100 000 символов вместе с ключами | Любые значения, сохраняются как JSON |
Хранилище на сервере своё у каждой площадки: у одного человека в Telegram, MAX и браузере оно разное.
Ограничения
- Код блока «JavaScript» — до 20 000 символов, код приложения и HTML-кода — до 100 000.
- Время на выполнение блока «JavaScript» — до 60 секунд.
- В журнал попадает не больше 20 ошибок в минуту с одного блока.