Ответы на частые вопросы о мультиплатформенном фреймворке umbot — выбор редактора, платформы, команды и шаги, база данных, производительность и устранение типичных ошибок.
Ответ: umbot — это мультиплатформенный фреймворк на TypeScript, который позволяет писать логику один раз и
запускать её на 7 платформах: Алиса, Маруся, Сбер SmartApp, Telegram, VK, Viber, MAX.
Преимущества перед нативными SDK:
umbot — это мультиплатформенный фреймворк на TypeScript для создания навыков для голосовых платформ и чат-ботов.
Позволяет писать код один раз и запускать на 7 платформах: Алиса, Маруся, Сбер SmartApp, Telegram, VK, Viber, MAX.
next()), затем — цепочка middleware платформы. Если какое-то middleware не вызвало next(), диспетчер не
запускается. Дальше сам диспетчер (controller.run()) обрабатывает запрос по цепочке:
bot.addEvent(...)) — если адаптер распознал тип события (фото, голосовое,
нажатие кнопки) и есть зарегистрированный хендлер.oldIntentName, если пользователь внутри мультишага и шаг зарегистрирован).platformParams (включая встроенные welcome/help).*).
Значит, если вы ожидали интент, а попадает команда — сначала проверьте, нет ли более ранней команды,
которая перекрывает её по слоту.this.userCommand автоматически приводится к нижнему регистру. Ваши слоты тоже должны быть в нижнем
регистре.includes(). Если нужно точное совпадение, используйте регулярное
выражение (например, /^привет$/; флаг i нужен, только если в самом выражении есть заглавные буквы —
userCommand уже приведён к нижнему регистру).FALLBACK_COMMAND (эквивалент *), если она
зарегистрирована.addCommand и интентом из platformParams?addCommand — основной способ регистрации команд. Поддерживает callback, асинхронность и специфичную логику.
Выполняется до проверки интентов.platformParams используются для базовых действий (приветствие, помощь) и обрабатываются, только если
ни одна команда не подошла.Рекомендация: всю бизнес-логику реализуйте через addCommand, а интенты оставьте для стандартных текстов (welcome,
help).
Используйте this.userData или this.state:
// Шаг 1: сохраняем ввод
ctx.userData.name = ctx.userCommand;
ctx.thisIntentName = 'step2';
// Шаг 2: читаем
ctx.text = `Привет, ${ctx.userData.name}!`;
Шаг 2 должен быть зарегистрирован через bot.addStep('step2', (ctx) => { ... }), иначе переход не сработает.
userData сохраняется между сессиями (в БД или локальном хранилище).state хранится только в рамках текущей сессии (поддерживается не всеми платформами).Плагин в umbot — это модуль расширения функциональности, который регистрируется в контексте приложения (AppContext)
и позволяет добавлять новую логику без изменения ядра фреймворка.
Интерфейс: Плагин может быть реализован как класс с методом init(appContext, bot) или как функция со свойством
isPlugin = true.
Регистрация: Плагины подключаются через метод bot.use(plugin).
Встроенные типы: В системе зарезервированы слоты для системных плагинов:
i18n — локализация;
nlu — обработка естественного языка;
regExp — кастомная реализация регулярных выражений.
Адаптеры (платформы и базы данных) в версии 3.0.0 также реализованы через архитектуру плагинов.
Подключение происходит в точке входа приложения через цепочку методов use().
Пример кода:
import { Bot, createPlugin } from 'umbot';
import { fullPlatforms, MongoAdapter } from 'umbot/plugins';
const bot = new Bot();
// 1. Подключение готовых плагинов (платформы и БД)
bot.use(fullPlatforms);
bot.use(new MongoAdapter({/* конфиг */}));
// 2. Подключение кастомного плагина (пример)
const myPlugin = createPlugin((appContext, bot) => {
appContext.plugins['myPlugin'] = {
getData: (key) => `Value: ${key}`,
};
});
bot.use(myPlugin);
bot.start('localhost', 3000);
Плагин — это механизм расширения функциональности фреймворка без изменения его ядра. Он позволяет инкапсулировать логику в отдельные модули, которые можно подключать только когда это нужно.
Основные сценарии:
bot.clearUse() удаляет сразу все плагины, адаптеры, middleware и
платформы (это глобальная операция), поэтому для точечного управления функционалом его лучше не использовать.Пример:
// plugins/game.ts
import { Bot, AppContext, BotController, IUserData, createPlugin } from 'umbot';
interface GameData extends IUserData {
score: number;
}
export const gamePlugin = createPlugin((appContext: AppContext, bot: Bot): void => {
bot.addCommand('game_start', ['играть'], (_, bc: BotController<GameData>) => {
bc.userData.score = 0;
bc.text = 'Игра началась!';
});
});
// index.ts
import { gamePlugin } from './plugins/game';
const bot = new Bot();
bot.use(gamePlugin); // подключаем плагин
Когда использовать плагины:
| Ситуация | Использовать плагин |
|---|---|
| Большая кодовая база (>1000 строк) | Да |
| Несколько проектов с общей логикой | Да |
| Нужно включать/выключать функции | Да |
| Интеграция со сторонними API | Да |
Подробнее о создании плагинов — в разделе Архитектура расширений.
npm install umbot
npx umbot create my-bot
cd my-bot
npm install
npm run build
npm run start
npm run startзапускает собранный код изdist/, поэтому перед первым стартом (и после изменений) нуженnpm run build.
Сохраните токены в .env файл:
TELEGRAM_TOKEN=your-token
VK_TOKEN=your-token
VK_CONFIRMATION_TOKEN=your-token
VK_SECRET_KEY=your-secret
ALISA_TOKEN=your-token
MAX_TOKEN=your-token
SPEECH_KIT_TOKEN=your-token
# ... и другие токены
Затем укажите путь в конфигурации:
bot.setAppConfig({
env: './.env',
});
Проблема: В версии 3.0 произошел переход на плагинную архитектуру. Работа с платформами теперь осуществляется через адаптеры. Было (2.2.x):
import { Bot } from 'umbot';
const bot = new Bot();
bot.setPlatformParams(params);
bot.start('localhost', 3000);
Стало (3.0):
import { Bot } from 'umbot';
import { fullPlatforms, FileAdapter } from 'umbot/plugins';
const bot = new Bot()
.use(fullPlatforms) // Подключаем платформы как плагин
.use(new FileAdapter()) // Подключаем адаптер БД
.setPlatformParams(params)
.start('localhost', 3000);
Подробнее об изменениях можно прочитать тут
Важно: Версия 2.1.x содержит критическую архитектурную проблему. Обязательно обновитесь! Обновите пакет:
npm install umbot@2.2
Проверьте код на использование устаревших методов (они были удалены в 2.2.x)
npm list umbot
umbot способен обработать любое количество команд, но важно понимать что большое количество команд, как правило,
говорит о неоптимальной архитектуре приложения.
Также при большом количестве команд, время ответа приложения будет увеличиваться.
Рекомендуется не использовать более 1000 команд в своем приложении.
| Количество команд | Время обработки (холодный запуск, worst case) | Время обработки (с re2, кэш прогрет) | Рекомендация |
|---|---|---|---|
| 50 | до 0.5 мс | до 0.5 мс | Отлично |
| 500 | до 1.2 мс | до 0.7 мс | Отлично |
| 1000 | до 30 мс | < 1 мс | Хорошо |
| 10000 | до 1 сек | < 20 мс | Проверьте сервер |
| 20000 | до 1 сек | 22.44 мс | Используйте re2 |
Примечание: «Холодный запуск» — кэш RegExp пуст, выражения компилируются впервые. Значение «до 30 мс» для 1000 команд — worst case (все команды с RegExp, кэш пуст). В типичном сценарии (500 команд, строки) время составляет 0.26 мс. «С re2, кэш прогрет» —
re2установлен, кэш уже заполнен. Подробные результаты — в разделе BENCHMARKS.
re2 — это библиотека для работы с регулярными выражениями, которая:
Установка:
npm install re2
После установки umbot автоматически начнет использовать re2.
Node.js на Windows работает менее эффективно, чем на Unix-системах (Linux/macOS). Это может приводить к высокому потреблению памяти (до 4 ГБ против 400 МБ на Linux). Рекомендация: Для продакшена используйте Linux-сервер.
Не рекомендуется. Файловая БД (FileAdapter) хранит данные в оперативной памяти и рассчитана на быстрый старт или базы объёмом до нескольких сотен МБ — при превышении возможен Out of Memory и падение приложения.
Рекомендация: Используйте MongoAdapter или создайте свой адаптер:
import { MongoAdapter } from 'umbot/plugins';
bot.use(
new MongoAdapter({
host: 'mongodb://localhost:27017/my-bot',
database: 'bot_db',
user: 'user',
pass: '***',
}),
);
BaseDbAdapterisConnected, _select, _insert, _update, _removeuse:bot.use(new MyCustomAdapter(config));
Да, можно одновременно хранить данные как в локальном хранилище платформы, так и в вашей базе данных. Сделать это можно следующим образом:
import { Bot } from 'umbot';
import { FileAdapter } from 'umbot/plugins';
const bot = new Bot();
bot.use(new FileAdapter());
bot.addCommand('test', ['сохранить'], (_, cBot) => {
// ⚠️ Не делайте `cBot.userData = {}` — это перезаписывает ссылку и ломает отслеживание изменений.
// Вместо этого мутируйте объект:
Object.assign(cBot.userData, { key: 'value' }); // Данные в базу данных
// ⚠️ Не делайте `Object.assign(cBot.state, ...)` — на чат-платформах
// (Telegram, VK, Viber, Max) state равен null, и вызов бросит TypeError.
// Безопасная форма: фреймворк читает state после выполнения команды,
// поэтому перезапись через spread допустима ({ ...null } даёт пустой объект).
cBot.state = { ...cBot.state, key: 'value' }; // Данные в локальное хранилище платформы
// Ваша логика
});
Далее, при повторном запросе, данные из базы данных будут лежать в userData, а данные из платформы — в state.
При этом, важно учитывать тот факт, что логика с локальным хранилищем будет работать только в том случае, если сама
платформа поддерживает такое поведение. Локальное хранилище есть у голосовых платформ (Алиса, Маруся, SmartApp);
у чат-платформ (Telegram, VK, Viber, Max) state платформой не заполняется и остаётся null, пока вы сами его не
инициализируете — поэтому записывайте данные через безопасную форму cBot.state = { ...cBot.state, ... }.
Подобное сделать можно. Для этого напишите следующий код:
import { Bot } from 'umbot';
const bot = new Bot();
bot.setAppConfig({
isLocalStorage: true, // говорим что данные сохраняются в локальное хранилище платформы
});
bot.addCommand('test', ['сохранить'], (_, ctx) => {
ctx.userData.myKey = 'значение'; // Сохраняем через мутацию, а не переприсваивание
// Ваша логика
});
Обратите внимание, что используется userData, данная механика реализована для удобства работы в сценариях, когда не
подразумевается использование базы данных.
Также важно учитывать что задан isLocalStorage, без его указания, механика работать не будет.
Информация только для базовых адаптеров платформ. При использовании сторонних адаптеров, поведение может отличаться.
В случае, если происходит попытка записать данные в userData и в state, то поведение может быть следующим:
userData, будут сохранены в базу, а в локальное
хранилище запишется state.state.BasePlatformAdapterisPlatformOnQuery, setQueryData, getContentbot.use(new MyPlatformAdapter(token));
По умолчанию рекомендуется использовать fullPlatforms, который подключает все платформы. Для подключения конкретных
платформ выполните следующие действия:
import { TelegramAdapter, VkAdapter } from 'umbot/plugins';
// Подключаем телеграм и вк
bot.use(new TelegramAdapter(telegramToken)).use(new VkAdapter(vkToken));
Также не стоит забывать о том, что можно подключить только голосовые платформы (voicePlatforms), либо только платформы
для чат-ботов (botPlatforms)
Проверьте:
addLink, hide: false) отображаются как видимые
кнопки, а интерактивные (addBtn, hide: true) — как саджесты и скрываются после нажатия.Да, и вот почему.
umbot — это не «мультиплатформенная надстройка», а полноценный фреймворк, который даёт преимущества уже на первом
проекте, даже если вы никогда не планируете добавлять другие каналы.
Что вы получите, используя umbot для одной платформы:
Пример для одной платформы (только Алиса):
import { Bot, BotController } from 'umbot';
import { AlisaAdapter } from 'umbot/plugins'; // адаптер для Алисы
const bot = new Bot();
bot.use(new AlisaAdapter()); // вместо fullPlatforms
// ... вся остальная логика остаётся без изменений
Никакого оверхеда — вы используете ровно то, что нужно. Фреймворк не заставляет вас подключать лишние платформы.
Итог: umbot не только подходит для одной платформы, но и делает разработку на одной платформе более структурированной, безопасной и готовой к масштабированию. Попробуйте — и вы увидите, что код стал чище, а времени на рутину уходит меньше.
Используйте класс BotTest:
import { BotTest } from 'umbot/test';
import { fullPlatforms } from 'umbot/plugins';
const bot = new BotTest();
bot.use(fullPlatforms);
bot.test(); // Запускает интерактивный режим в консоли
Логи сохраняются в директорию, указанную в error_log:
bot.setAppConfig({
error_log: './logs',
});
В режиме разработки (dev) ошибки также выводятся в консоль.
bot.setAppMode('dev');
Входящий текст не совпал ни с одной командой, шагом или интентом. В этом случае базовый контроллер подставляет текст из
platformParams.empty_text (пустая строка, если не задан). Проверьте:
Проблема: Файловая БД переполняется, из-за чего приложение в какой-то момент может упасть с ошибкой. Решение:
Практический лимит ответа для Алисы — около 3 секунд: фреймворк пишет предупреждение в лог уже после 2 секунд обработки, а после 2,9 секунды — ошибку. У других платформ лимит отличается и может меняться. Оптимизация:
Проблема: В ваших регулярных выражениях обнаружена потенциальная уязвимость. Решение:
bot.setAppMode('strict_prod')Фреймворк автоматически проверяет регулярные выражения на уязвимости. В режиме strict_prod такие команды не регистрируются. Чтобы исправить:
.* без якорей.Проверьте, что вы подключили нужные адаптеры (bot.use(new TelegramAdapter()) и т.д.).
Убедитесь, что запрос приходит на правильный URL и содержит корректные заголовки (например, X-Telegram-Bot-Api-Secret-Token для Telegram).
Для локального тестирования используйте BotTest — он автоматически подставляет тестовые данные.
Иногда может возникнуть ситуация, когда фреймворк не смог корректно определить тип платформы, либо вам необходимо
самостоятельно определять тип платформы.
В таком случае можно использовать bot.setPlatformResolver(...), метод позволит зарегистрировать кастомный обработчик
для определения типа платформы.
Как использовать:
bot.setPlatformResolver((query, headers, detect) => {
const platform = detect?.(query, headers);
if (platform === 'telegram' && headers?.['x-force-vk']) {
return 'vk';
}
return platform;
});
Первым аргументом придет сам запрос от платформы, вторым заголовок, третьим придет функция обработчик со стандартной механикой определения платформы.
В случае если платформа определилась некорректно, рекомендуется использовать данную механику для проставления корректной платформы, после чего выписать bug-report с ошибкой, чтобы мы смогли оперативно её поправить.
umbot автоматически проверяет регулярные выражения на уязвимости.
В режиме strict_prod опасные RegExp отклоняются; в dev/prod они работают, но логируются (warning при
установленном re2, error — без него).
bot.getAppContext().httpClient = async (url, options) => {
// Ваша реализация
return fetch(url, options);
};
Да, через метод webhookHandle:
app.post('/webhook', (req, res) => {
bot.webhookHandle(req, res);
});
Это позволяет интегрировать приложение в существующий webhook.
// Вариант 1: объект
class MyI18nPlugin implements IPlugin {
init(appContext: AppContext, bot: Bot) {
appContext.plugins['i18n'] = {
getData(key: string, ...params: unknown[]): string {
return `Translated: ${key}`;
},
};
}
destroy(_bot: Bot) {}
}
// Вариант 2: функция (через createPlugin — флаг isPlugin выставится автоматически)
const myI18nPlugin = createPlugin((appContext: AppContext, bot: Bot) => {
appContext.plugins['i18n'] = (key: string, ...params: unknown[]) => {
return `Перевод для: ${key}`;
};
});
bot.use(myI18nPlugin);
bot.use(new MyI18nPlugin());