Для безопасного хранения токенов и других чувствительных данных, вы можете использовать два подхода:
.env (рекомендуется)bot.setAppConfig({ env: './.env' });
Пример файла .env:
# Токены платформ
TELEGRAM_TOKEN=123456:ABC-DEF...
VK_TOKEN=vk1.a.abc123...
VK_CONFIRMATION_TOKEN=abcdef # обязательно для VK (для подтверждения вебхука)
VK_SECRET_KEY=abc123... # опционально: секретный ключ VK для проверки подлинности запросов
VIBER_TOKEN=1234567890-ABCDEF...
ALISA_TOKEN=y0_AgAAAAA... # OAuth-токен навыка (для аплоада медиа), без префикса "OAuth "
MARUSIA_TOKEN=abc.123...
MAX_TOKEN=abc123...
SMARTAPP_TOKEN=... # токен Сбер SmartApp (генерируется CLI; для работы адаптера не обязателен)
# Секреты вебхука (см. раздел «Проверка подписи вебхука» ниже). Попадают в
# tokens.telegram.webhookSecret / tokens.max_app.webhookSecret и включают проверку подписи.
# Их создаёт и регистрирует команда `npx umbot webhook <telegram|max> <https-url>`.
# Не оставляйте здесь заглушки: с любым непустым значением бот отклоняет запросы с другим секретом.
TELEGRAM_WEBHOOK_SECRET=...
MAX_WEBHOOK_SECRET=...
# Yandex SpeechKit — для TTS на чат-ботах (Telegram/VK/Max).
# Значение автоматически записывается в speech_kit_token всех трёх платформ.
# Рекомендуется API-ключ сервисного аккаунта (уходит как `Api-Key`); IAM-токен `t1.…`
# тоже принимается (уходит как `Bearer`), но живёт не больше 12 часов.
SPEECH_KIT_TOKEN=AQVN...
# Подключение к MongoDB (если используете MongoAdapter)
DB_HOST=mongodb://localhost:27017
DB_USER=root
DB_PASSWORD=secret
DB_NAME=umbot
Важно! Никогда не добавляйте .env файлы в репозиторий Git. Используйте разные токены для разработки и продакшена. Значение
ALISA_TOKENуказывайте без префиксаOAuth— фреймворк добавит его сам. Устаревшее имяYANDEX_TOKENподдерживается для обратной совместимости, но приоритет уALISA_TOKEN.
Что будет, если файл не найден? Фреймворк попробует получить токены из process.env. Если и там их нет — в лог-файле будет выведено сообщение об ошибке. Адаптер при этом остаётся зарегистрированным, но операции, требующие токена (отправка сообщений, загрузка медиа), работать не будут.
Переменные окружения без
env. Еслиenvне настроен вовсе, фреймворк всё равно тихо попробует прочитать известные переменные (TELEGRAM_TOKEN,VK_TOKEN, ...) изprocess.envи дозаполнить ими токены — это позволяет передавать токены черезdocker run -eили окружение serverless-функции безenv: 'local'. Уже заданные токены при этом не перезаписываются.
.envКлючи своих интеграций (API погоды, CRM) удобно держать в том же .env. Фреймворк читает из него только свои
переменные, а свои можно прочитать тем же разбором — функцией loadEnvFile из umbot/utils: те же правила
комментариев (« #» вне кавычек), кавычек и пустых значений, что у фреймворка.
import { loadEnvFile } from 'umbot/utils';
const envFile = loadEnvFile('./.env').data ?? {};
const weatherKey = process.env['WEATHER_KEY'] || envFile['WEATHER_KEY'] || '';
Проекты из umbot create from-flow генерируют для этого хелпер env('NAME') в src/utils.ts (см. json-format,
«HTTP-запросы: переменные и секреты»).
bot.setAppConfig({
db: {
host: 'mongodb://localhost:27017',
user: 'bot_user',
pass: 'secure_password',
database: 'bot_database',
},
tokens: {
telegram: {
token: 'your-telegram-token',
},
vk: {
token: 'your-vk-token',
},
},
});
Механика такая: токен конструктора адаптера (new TelegramAdapter('token')) записывается в конфиг при вызове
bot.use() (в init() адаптера). Дальше решает env:
env (файл или 'local') — setAppConfig({ env }) и каждый последующий
setPlatformParams вызывают перезапись токенов из env: значения из .env/process.env перезапишут
и токен конструктора, и inline-tokens (не важно, вызван ли setAppConfig до или после bot.use()).env не настроен вовсе: тогда setPlatformParams
лишь дозаполняет отсутствующие токены из process.env, не перезаписывая заданные.tokens в setAppConfig — сливается с существующими токенами платформы.process.env без настроенного env — только дозаполняет отсутствующие токены, ничего не перезаписывая.Практический совет: не смешивайте способы для одной платформы. Либо передавайте токен в конструкторе адаптера и не настраивайте
env, либо используйте.env/process.envи создавайте адаптеры без токена.
| Сценарий | Рекомендация |
|---|---|
| Разработка, прототип | env: '.env' — просто и безопасно |
| Production на сервере | env: 'local' + переменные окружения на сервере |
| Тесты | Прямая передача в config.tokens или в конструкторе адаптера |
| Несколько сред (dev/prod) | .env файлы с разными токенами, передавайте через env |
IAppConfigМетод setAppConfig принимает объект Partial<IAppConfig>:
| Поле | Тип | Описание |
|---|---|---|
error_log |
string |
Путь к папке для логов ошибок (error.log, warn.log). Файл больше 10 МБ переименовывается в <имя>.1 (хранится одна предыдущая копия), поэтому логи занимают не больше ~40 МБ |
json |
string |
Путь к папке для JSON-данных (используется FileAdapter) |
db |
IAppDB |
Параметры подключения к БД |
isLocalStorage |
boolean |
Использовать локальное хранилище платформы вместо БД |
memorySession |
IMemorySessionConfig | false |
Сессия userData в памяти процесса для Telegram/VK/MAX/Viber без БД при isLocalStorage: true. По умолчанию { maxSize: 10000, ttl: 86400000 }; false — выключить |
env |
string |
Путь к .env файлу ИЛИ строка 'local' для process.env |
tokens |
ITokenPlatform |
Токены платформ (для адаптеров, если не переданы через конструктор) |
Вложенные типы:
interface IAppDB {
host: string; // например, 'mongodb://localhost:27017'
user?: string;
pass?: string; // Обратите внимание: поле называется pass, а не password
database: string;
options?: Record<string, unknown>;
}
interface ITokenPlatform {
[platform: string]: {
token?: string;
// speech_kit_token — для TTS на Telegram/VK/Max
// (передаётся через индексную сигнатуру ниже, явно в интерфейсе не объявлен)
[key: string]: string | number | undefined;
};
}
Важно.
speech_kit_tokenне объявлен явно вITokenPlatform— он передаётся через индексную сигнатуру. На уровне TypeScript это работает:appConfig.tokens.telegram.speech_kit_tokenимеет типstring | number | undefined.
bot.setAppConfig({
json: './data', // папка для JSON-файлов (FileAdapter)
error_log: './errors', // папка для логов
isLocalStorage: true, // локальное хранилище (для голосовых платформ)
env: '.env', // путь к файлу с токенами
db: {
// подключение к MongoDB (если не isLocalStorage)
host: 'mongodb://localhost:27017',
database: 'umbot',
},
});
На Telegram/VK/MAX/Viber локального хранилища нет: при
isLocalStorage: trueбез DB-адаптераuserDataхранится в памяти процесса (memorySession). Данные теряются при перезапуске и не разделяются между процессами, репликами и вызовами serverless-функции — для продакшена с шагами диалога подключите БД.
IAppParamМетод setPlatformParams принимает объект IAppParam:
| Поле | Тип | Описание |
|---|---|---|
intents |
IAppIntent[] | null |
Обязательное. Список интентов для распознавания команд |
welcome_text |
string | string[] |
Текст приветствия (при messageId === 0) |
help_text |
string | string[] |
Текст помощи (при команде "помощь") |
empty_text |
string | string[] |
Текст при отсутствии подходящих команд |
isAuthUser |
boolean |
Требуется ли авторизация пользователя |
utm_text |
string | null |
UTM-метки для ссылок (при null к кнопкам-ссылкам без UTM автоматически добавляется utm_source=umbot&utm_medium=cpc&utm_campaign=phone; строка заменяет эти метки целиком) |
Интент:
interface IAppIntent {
name: string;
slots: (string | RegExp)[]; // строка → подстрока; RegExp → .test()
is_pattern?: boolean; // трактовать строки как regex (по умолчанию false)
}
bot.setPlatformParams({
welcome_text: 'Привет! Я умею считать.',
help_text: 'Это игра в математику.',
empty_text: 'Не поняла. Скажите "помощь".',
intents: [
{ name: 'game', slots: ['игра', 'начать игру'] },
{ name: 'bye', slots: ['пока', 'до свидания'] },
{ name: 'phone', slots: ['\\+?\\d{11}'], is_pattern: true }, // слот как regex
],
});
Важно: поле
intentsобязательно даже если оно пустое:intents: []. Без него TypeScript выдаст ошибку типа.
const ctx = bot.getAppContext();
ctx.appConfig; // заполненный IAppConfig (со всеми дефолтами)
ctx.platformParams; // IAppParam
ctx.platforms; // реестр платформ { alisa: AlisaAdapter, telegram: ... }
ctx.database.adapter; // активный DB-адаптер
ctx.command; // CommandReg (реестр команд)
ctx.httpClient; // функция fetch (можно переопределить)
ctx.log('...'); // лог
ctx.logError('msg', { error: 'details' });
ctx.logWarn('msg', { warning: 'details' });
ctx.logMetric('name', value, { platform: 'telegram' });
setAppMode| Режим | Логи | ReDoS-проверка | Когда использовать |
|---|---|---|---|
dev |
Подробные | Warn, но не блокирует | Разработка, BotTest |
prod |
Минимальные | Warn, но не блокирует (режим небезопасен, оставлен для обратной совместимости) | Pre-prod |
strict_prod |
Минимальные | Блокировка опасных | Production |
Режим по умолчанию. Пока setAppMode() не вызван, режим определяется переменной окружения NODE_ENV:
NODE_ENV=production — strict_prod, иначе — dev (так же на NODE_ENV ориентируются Express и сборщики
фронтенда). Явный setAppMode() всегда главнее. Docker-образ, который генерирует CLI, задаёт NODE_ENV=production.
Что делает strict_prod:
dev и prod опасные RegExp используются как есть: с предупреждением, если установлен re2, и с ошибкой в логах, если нет (без re2 штатный движок Node уязвим к катастрофическому бэктрекингу).Маскировка секретов в логах (токены, пароли заменяются на
***) работает во всех режимах и отключается только кастомным логгером сmaskSecrets: false.
Вебхук — единственная точка входа вашего приложения. Пока проверка подписи не включена, любой, кто узнает URL
вебхука, может отправлять запросы от имени любого пользователя: обходить авторизацию по userId, читать и
перезаписывать чужие userData, управлять чужими диалоговыми шагами и расходовать квоты API. URL вебхука — не секрет
(он виден платформе, логам, реестрам доменов), поэтому подпись — не опция, а обязательный шаг настройки.
Проверка подписи включается автоматически, как только задан секрет — отдельно «включать» ничего не нужно. Фреймворк предупредит в логе при старте, если для подключённой платформы секрет не задан.
| Платформа | Что задать | Где секрет живёт на стороне платформы |
|---|---|---|
| Telegram | TELEGRAM_WEBHOOK_SECRET / tokens.telegram.webhookSecret |
поле secret_token при вызове setWebhook |
| VK | tokens.vk.secret_key (или vk_secret_key) |
настройка «Секретный ключ» в настройках группы (VK Callback API) |
| MAX | MAX_WEBHOOK_SECRET / tokens.max_app.webhookSecret (или secret) |
поле secret в POST /subscriptions |
| Viber | tokens.viber.token |
токен бота — он же ключ HMAC (x-viber-content-signature) |
| Алиса, SmartApp, Маруся | — | платформа не подписывает запросы в принципе (см. ниже) |
Для Telegram и MAX проще всего одна команда в папке проекта: она берёт токен из .env, генерирует секрет,
регистрирует вебхук сразу с ним и сохраняет секрет в .env (TELEGRAM_WEBHOOK_SECRET / MAX_WEBHOOK_SECRET).
Секрет пишется только после успешной регистрации, поэтому значения в .env и на платформе не расходятся.
Если секрет в .env уже есть, используется он.
npx umbot webhook telegram https://ваш-домен/webhook
npx umbot webhook max https://ваш-домен/webhook # MAX: только HTTPS на порту 443
После команды перезапустите бота — проверка подписи включится автоматически.
Вручную сгенерировать секрет можно любой командой:
node -e "console.log(require('crypto').randomBytes(24).toString('base64url'))"
# или: openssl rand -base64 24
# 1. Секрет — одна и та же строка в обоих местах
curl "https://api.telegram.org/bot<ТОКЕН>/setWebhook" \
-d "url=https://ваш-домен/webhook" \
-d "secret_token=<СЕКРЕТ>"
// 2. Тот же секрет в конфигурации приложения: TELEGRAM_WEBHOOK_SECRET в .env/окружении
// подхватывается автоматически, либо явно:
bot.use(new TelegramAdapter('YOUR_BOT_TOKEN'));
bot.setAppConfig({
tokens: {
telegram: { token: 'YOUR_BOT_TOKEN', webhookSecret: process.env.TELEGRAM_WEBHOOK_SECRET },
},
});
Без webhookSecret адаптер принимает любой запрос с полем update_id — это допустимо только для локальной
отладки. С заданным секретом запросы без заголовка x-telegram-bot-api-secret-token (или с неверным значением)
отклоняются с 401 до выполнения какой-либо логики.
Включите «Секретный ключ» в настройках группы (Управление → Работа с API → Callback API) и передайте то же значение:
bot.use(new VkAdapter('YOUR_VK_TOKEN', { vk_secret_key: 'YOUR_SECRET' }));
// или через конфиг: tokens.vk.secret_key
VK присылает secret в теле каждого callback-запроса; адаптер сверяет его константным по времени сравнением
(timingSafeEqual). Если секрет в группе не включён — проверку включить нельзя (нечем сверять), см. ниже про
ipFilter.
MAX передаёт секрет заголовком x-max-bot-api-secret:
bot.use(new MaxAdapter('YOUR_MAX_TOKEN', { secret: 'YOUR_SECRET' }));
// или через конфиг: tokens.max_app.webhookSecret
Алиса, SmartApp и Маруся не предоставляют механизм подписи вебхука — всё содержимое payload, включая
user_id, контролирует отправитель запроса. Это ограничение платформ, а не фреймворка:
userId голосовой платформы аутентифицированной идентичностью;userData голосовых платформ данные, потеря или подмена которых критична.Дополнительный слой для любых платформ — ipFilter (см. middleware.md): ограничение входящих
запросов по диапазонам IP платформ (например, только для Telegram: 149.154.160.0/20, 91.108.4.0/22).
Фреймворк поддерживает работу с re2. За счет использования данной библиотеки, можно добиться существенного ускорения
обработки регулярных выражений, а также сокращения потребления памяти. Потребление памяти уменьшается
примерно в 3-7 раз, а время выполнения уменьшается в среднем в 2-15 раз.
npm install re2
Фреймворк автоматически определит, установлен ли re2, и будет использовать его.
Не рекомендуется использовать в релизной версии приложения файловую базу данных, так как данный подход может привести к падению приложения при большом количестве записей. Связано это с тем, что в файловой базе данные хранятся в оперативной памяти.
Для корректного сохранения данных в БД:
MongoAdapter), или создайте свой (bot.use(new MyAdapter()))bot.setAppConfig({db:{...}}), либо в конструкторе при подключении адаптера..env существует по указанному пути= — парсер обрезает их (TELEGRAM_TOKEN = abc работает так же, как TELEGRAM_TOKEN=abc; кавычки вокруг значения допускаются — парсер их снимет)dev для подробных логов: bot.setAppMode('dev')db.host: должен содержать протокол (mongodb://localhost:27017, а не localhost:27017)error_log покажет ошибку подключенияintents передан в setPlatformParams (даже если пустой: intents: [])userCommand)dev для просмотра процесса поиска командПодробнее о конфигурации (IAppConfig, IAppParam), приоритете токенов и содержимом .env — в разделе Конфигурация: IAppConfig и IAppParam.
Полный справочник — API v-3.1 · все версии.