umbot - это универсальный фреймворк для создания чат-ботов и голосовых навыков. Основные преимущества:
npm install umbot
Для базового варианта создадим контроллер нашего бота
import { Bot, BotController, WELCOME_INTENT_NAME } from 'umbot';
import { fullPlatforms } from 'umbot/plugins';
import { join } from 'node:path';
// Создаем контроллер с логикой навыка
class MyController extends BotController {
public action(intentName: string): void {
switch (intentName) {
case WELCOME_INTENT_NAME:
this.text = 'Привет! Я новый навык.';
this.buttons.addBtn('Помощь');
break;
case 'help':
this.text = 'Я умею отвечать на команды и показывать кнопки';
break;
default:
this.text = this.userCommand || 'Вы ничего не сказали';
break;
}
}
}
// Инициализируем бота
const bot = new Bot();
bot.use(fullPlatforms);
// Настраиваем команды
bot.setPlatformParams({
intents: [
{
name: 'help',
slots: ['помощь', 'что ты умеешь'],
},
],
});
// Настраиваем параметры
bot.setAppConfig({
json: join(__dirname, 'data'),
error_log: join(__dirname, 'logs'),
isLocalStorage: true,
});
// Подключаем контроллер
bot.initBotController(MyController);
bot.start('localhost', 3000);
Также можно совсем не создавать BotController, и решить все задачи за счет динамического добавления команд.
Также обратите внимание на FALLBACK_COMMAND, обработчик будет выполнен в том случае, если не удалось найти нужную
команду. Также можно просто указать "*", что также равносильно заданию через константу.
import { Bot, BotController, FALLBACK_COMMAND, HELP_INTENT_NAME, WELCOME_INTENT_NAME } from 'umbot';
import { fullPlatforms } from 'umbot/plugins';
import { join } from 'node:path';
const bot = new Bot()
.use(fullPlatforms)
.setAppConfig({
json: join(__dirname, 'data'),
error_log: join(__dirname, 'logs'),
isLocalStorage: true,
})
.addCommand(WELCOME_INTENT_NAME, ['привет'], (_: string, bc: BotController) => {
bc.text = 'Привет! Я новый навык.';
bc.buttons.addBtn('Помощь');
})
.addCommand(HELP_INTENT_NAME, ['помощь'], (_: string, bc: BotController) => {
bc.text = 'Я умею отвечать на команды и показывать кнопки';
})
.addCommand(FALLBACK_COMMAND, [], (_: string, bc: BotController) => {
bc.text = bc.userCommand || 'Вы ничего не сказали';
})
.start('localhost', 3000);
Базовый класс для реализации логики навыка. Предоставляет:
this.text = 'Ответ пользователю'; // Текст ответа
this.tts = 'Ответ для озвучки'; // TTS версия (опционально)
this.buttons
.addBtn('Кнопка 1') // Простая кнопка
.addBtn('Ссылка', 'https://...') // Кнопка-ссылка
.addBtn('Действие', null, {
// Кнопка с данными
action: 'custom',
value: 123,
});
this.card
.addImage('image.jpg') // Добавить изображение
.addTitle('Заголовок') // Добавить заголовок
.addDescription('Описание'); // Добавить описание
// Сохранение данных
this.userData.counter = 42;
// Получение данных
const counter = this.userData.counter || 0;
bot.setPlatformParams({
intents: [
{
name: 'start_game',
slots: ['начать игру', 'играть', 'старт'],
},
],
});
bot.addCommand('greeting', ['привет', 'здравствуй'], (cmd, controller) => {
controller.text = 'Здравствуйте!';
});
src/
├── controllers/ # Контроллеры с логикой (Если необходимо)
├── plugins/ # Дополнительные плагины (Если необходимо)
├── utils/ # Вспомогательные функции (Если необходимо)
├── config/ # Конфигурация (Если необходимо)
└── index.ts # Точка входа
interface IGameState {
score: number;
level: number;
lastAction?: string;
}
class GameController extends BotController<IGameState> {
public action(intentName: string): void {
// Теперь this.userData типизирован как IGameState
this.userData.score = 100;
}
}
try {
const result = await this.processUserInput();
this.text = `Успешно: ${result}`;
} catch (error) {
console.error('Ошибка:', error);
this.text = 'Извините, произошла ошибка';
}
// Проверка первого запуска
if (!this.userData.initialized) {
this.userData.initialized = true;
this.userData.score = 0;
}
// Сброс состояния
if (intentName === 'restart') {
this.userData = {};
this.text = 'Игра начата заново';
}
import { BotTest } from 'umbot';
import { fullPlatforms } from 'umbot/plugins';
const bot = new BotTest();
bot.use(fullPlatforms);
// Запускает интерактивный режим в консоли: вы вводите фразы, бот отвечает
bot.test();
// В контроллере
console.log('Данные:', this.userData);
console.log('Команда:', this.userCommand);
// В конфигурации
bot.setAppConfig({
error_log: './logs',
});
bot.setAppMode('dev');
Фреймворк автоматически проверяет регулярные выражения на потенциальные ReDoS-уязвимости при вызове
addCommand(..., isPattern: true).
⚠️ По умолчанию (strictMode: false) небезопасные регулярки всё равно регистрируются!
Это сделано для гибкости в разработке, но порой недопустимо в production.
✅ Рекомендация для production включить строгую проверку:
const bot = new Bot();
bot.setAppMode('strict_prod'); // ← обязательно включите!
При appMode = strict_prod любая потенциально опасная регулярка будет отклонена, а её использование вызовет ошибку в логах.
⚠️ Если вы используете slots с RegExp, убедитесь, что ваши выражения:
Достаточно создать адаптер для нужной платформы согласно документации, и после подключить его к приложению. Если все сделано верно, то при получении запроса от новой платформы, фреймворк корректно отработает запрос, и вернет данные в нужном для платформы виде.
Достаточно сохранить чувствительные данные в .env файл, передав путь к нему:
bot.setAppConfig({
env: './.env', // путь до файла
});
Пример содержимого .env файла:
TELEGRAM_TOKEN=your-telegram-token
VK_TOKEN=your-vk-token
VK_CONFIRMATION_TOKEN=your-vk-confirmation-token
VIBER_TOKEN=your-viber-token
YANDEX_TOKEN=your-alisa-token
MARUSIA_TOKEN=your-marusia-token
DB_HOST=localhost
DB_USER=user
DB_PASSWORD=password
DB_NAME=bot_db
Если все необходимые токены лежат в process.env, то можно в свойство env передать значение local.
bot.setAppConfig({
env: 'local', // Получить данные из process.env
});
Данные в this.userData автоматически сохраняются между сессиями. Выберите способ хранения через параметр
isLocalStorage:
bot.setAppConfig({
isLocalStorage: true, // Данные хранятся на стороне платформы
// или
isLocalStorage: false, // данные хранятся в вашей БД
});
this.buttons.addBtn('Да').addBtn('Нет').addBtn('Не знаю');
this.card
.addImage('image1.jpg', 'Заголовок 1', 'Описание 1')
.addImage('image2.jpg', 'Заголовок 2', 'Описание 2')
.addTitle('Галерея изображений');
Больше вопросов и ответов можно найти тут