Skip to content

V 3.1.0 - #94

Open
max36895 wants to merge 8 commits into
mainfrom
v-3.1.0
Open

V 3.1.0#94
max36895 wants to merge 8 commits into
mainfrom
v-3.1.0

Conversation

@max36895

Copy link
Copy Markdown
Owner

Добавлено

  • Env: новое каноническое имя переменной окружения ALISA_TOKEN для Алисы. Старое имя YANDEX_TOKEN сохраняется для обратной совместимости — при одновременном задании обеих приоритет у ALISA_TOKEN.
  • CLI: новая команда npx umbot validate <flow.json> для проверки корректности flow.json перед генерацией (битые ссылки в edges, дубликаты id, невалидные идентификаторы, невалидный JSON). Циклы в графе допустимы и ошибкой не считаются.
  • CLI: новая команда npx umbot stats --log <path> для агрегации метрик из лога (счётчики строк/ошибок/ворнингов, топ команд, p50/p95/p99 latency).
  • Middleware: добавлены стандартные middleware в src/middleware/ и umbot/middleware:
    • authGuard(check, options) — проверка авторизации пользователя перед выполнением остальных обработчиков (кастомный deniedText, поддержка async check, исключения в check трактуются как отказ).
    • requestId() — проставление уникального requestId в platformOptions.requestId для сквозного трейсинга. Запасной вариант для Node < 14.17 (без crypto.randomUUID).
    • maintenance(check, options) — возврат "сервис на техобслуживании" пока check() возвращает true. Ошибки в check логируются и не ломают pipeline.
    • ipFilter(options) — фильтрация входящих webhook-запросов по IP клиента. Поддерживает IPv4 и CIDR, автоматически нормализует IPv6-mapped. IP берётся из сокета HTTP-запроса (заполняется в webhookHandle в platformOptions.clientIp). Fail-open вне HTTP-контекста (bot.run() напрямую — запрос пропускается).
  • Bot: новый метод bot.addForm(formName, options) для многошаговых форм (опросников). Реализован поверх существующего addStep — для каждого поля регистрируется отдельный шаг __form_<formName>_<idx>, состояние ответа сохраняется в userData.__formdata_<formName>. Поддерживаются синхронные и асинхронные валидаторы, prompt-функция, команды отмены с кастомным текстом.
  • Bot: класс Bot теперь принимает дополнительный generic TPlatformState. Пример: new Bot<MyUserData, MyState>(). Это даёт типобезопасный доступ к ctx.state.
  • BotController: аналогично Bot — второй generic TPlatformState для типизации локального хранилища платформы (state).
  • IPlatformData: в интерфейс добавлено опциональное служебное поле oldIntentName для восстановления шагов диалога через локальное хранилище платформы.
  • BotTest: новый метод bot.simulate(query, { platform, userId, count, state }) — сам генерирует webhook payload через getQueryExample платформы и возвращает структурированный ответ. Упрощает написание интеграционных тестов.
  • VK: метод sendMessageEvent() в VkRequest для корректного подтверждения callback-кнопок.
  • AppContext: поле eventId в IPlatformOptions для хранения ID события callback-кнопки VK.
  • Маруся: поддержка типа карточки ImageGallery.
  • Маруся: обработка health-check (ping → pong) для корректного ответа на служебные запросы платформы.
  • Маруся: исправлено формирование session-объекта в ответе — добавлен fallback для user_id.
  • Маруся: расширен тип account_linking_complete_event с boolean до object | boolean.
  • CLI: флаг --force для create from-flow, чтобы перезапись непустой папки была явным действием.
  • Плагины: новый хелпер createPlugin(fn) (экспортируется из umbot) — оборачивает функцию-плагин и автоматически выставляет маркер isPlugin = true. Убирает частую ошибку, когда забытый флаг заставлял bot.use() трактовать плагин как middleware.

Изменено

  • [Breaking] Telegram: автоматический parse_mode отключён. Разметка применяется только при явном telegram_parse_mode; разработчик сам отвечает за экранирование динамических данных.
  • Telegram: устаревший режим markdown заменён на MarkdownV2.
  • Telegram: sendPoll приведён к Bot API 10.2: 1–12 объектов InputPollOption, актуальное поле correct_option_ids и проверка индексов без незаметного удаления вариантов.
  • Telegram: sendMediaGroup сохраняет media при передаче дополнительных параметров через FormData и отклоняет группы вне диапазона 2–10 без молчаливой потери элементов.
  • API версий: VK API обновлён с 5.103 до 5.199; версия Viber API по умолчанию повышена с 2 до 7 (вынесено в константу VIBER_DEFAULT_API_VERSION).
  • Yandex SpeechKit: исправлен некорректный код языка en_EN на стандартный en-US. Добавлены новые голоса: kirkorov, lera, madusa.
  • Команды: базовые команды (welcome, help), добавленные через addCommand без явного указания слотов, теперь автоматически регистрируются со слотами по умолчанию.
  • MongoDB: пакет mongodb переведён в опциональные peer-зависимости и подгружается лениво (динамический import) только при вызове MongoAdapter.connect(). Пользователи, которым Mongo не нужен (например, только FileAdapter), больше не обязаны его устанавливать. Если пакет не установлен, connect() возвращает false и пишет в лог инструкцию npm install mongodb вместо падения процесса.
  • Ядро (рефакторинг): константы FALLBACK_COMMAND, WELCOME_INTENT_NAME, WELCOME_INTENT_SLOTS, HELP_INTENT_NAME, HELP_INTENT_SLOTS больше не реэкспортируются из AppContext и CommandReg — все потребители и публичный баррель core/index.ts ссылаются напрямую на листовой модуль core/constants.ts. Публичный API не изменился: константы по-прежнему доступны из корневого umbot.
  • Ядро (рефакторинг): разорвано последнее runtime-ребро циклической зависимости core/Bot → controller. Дефолтный BaseBotController подгружается лениво (require внутри #getBotController) вместо статического импорта в шапке файла — к моменту вызова конструктора все модули уже инициализированы. Типы BotController/IUserData/IPlatformData импортируются как import type и стираются при сборке. Поведение и публичный API не изменились.

Исправлено

  • [Критично] Ядро: Bot читал сохранённые данные пользователя по userToken, а записывал по userId — у авторизованных пользователей (например, Алиса с isAuthUser) userData не находилась между запросами и фактически не персистилась. Чтение унифицировано на userId (ключ записи). Добавлен регресс-тест в tests/Bot/bot.test.ts.

  • [Критично] VK: users.get всегда возвращает массив, но адаптер читал first_name/last_name как у объекта — имя пользователя VK в ctx.nlu.getUserName() всегда было null. VkRequest.usersGet теперь типизирован как IVkUsersGet[] | null (по аналогии с photosSaveMessagesPhoto), адаптер берёт первый элемент массива. Добавлены регресс-тесты в tests/Platforms/VK/adapter.test.ts.

  • [Критично] Маруся: MarusiaRequest наследовал токен VK от родителя и при отсутствии токена Маруси отправлял его в запросы к API Маруси (утечка секрета на чужой endpoint + нерабочие загрузки). Теперь при отсутствии tokens.marusia.token токен сбрасывается в null. Добавлен регресс-тест в tests/Request/MarusiaRequest.test.ts.

  • Документация: action() вызывается фреймворком синхронно и не ожидается — примеры с async action() в JSDoc BotController, UsersData и GUIDE.md переписаны (асинхронная логика показана через addCommand/fire-and-forget), в JSDoc action() добавлено явное предупреждение.

  • [Критично] Ядро: устранена циклическая зависимость core ↔ controller, из-за которой при стандартном импорте from 'umbot' константы FALLBACK_COMMAND, WELCOME_INTENT_NAME, HELP_INTENT_NAME захватывались как undefined. В результате fallback-команда '*', welcome_text и help_text молча не работали. Константы вынесены в листовой модуль core/constants.ts без импортов, который всегда полностью инициализирован независимо от порядка загрузки. Добавлен регресс-тест tests/Bot/fallbackConstants.test.ts.

  • Middleware: requestId() теперь корректно возвращает промис цепочки (await next()) — ранее middleware обрывал ожидание, из-за чего запрос мог обрабатываться параллельно с ответом, а ошибки в последующих middleware приводили к unhandled promise rejection.

  • Middleware: при блокировке запроса middleware (authGuard, maintenance, ipFilter) ответ формируется через адаптер платформы из ctx.text — пользователь получает deniedText валидным ответом платформы вместо служебной строки.

  • Middleware: ipFilter получает реальный IP клиента из сокета HTTP-запроса (platformOptions.clientIp, заполняется в webhookHandle) — ранее IP искался в теле запроса платформы и фильтрация не работала.

  • Env: ALISA_TOKEN и VK_SECRET_KEY теперь реально читаются из окружения в AppContext (ранее были только объявлены в IEnvConfig).

  • CLI: токен Алисы (alisa_token) больше не попадает в сгенерированный src/config/{{name}}Params.ts — удаляется вместе с остальными токенами перед сериализацией.

  • CLI: validate проверяет целостность рёбер edges (существование from/to, допустимый type), по которым реально ходит генератор; циклы в графе допустимы.

  • CLI: при генерации с --prod теперь создаётся .dockerignore (исключает .env, node_modules, логи из образа), а Dockerfile использует npm ci при наличии lock-файла — версии зависимостей совпадают между build и runtime стадиями.

  • CLI: сгенерированный deploy.yml больше не передаёт секреты флагами -e (они видны в ps и истории docker) — используется --env-file; имя образа и контейнера подставляется из имени проекта; добавлены npm ci, шаг с тестами и --restart unless-stopped.

  • CLI: генерация .env больше не пишет строки вида TELEGRAM_TOKEN=undefined — в файл попадают только переменные, для которых реально передано значение.

  • CLI: flowGenerator для database.type === 'mongo' учитывает config.host из flow.json (ранее всегда подставлялся localhost); bot.start() берёт опциональные hostname/port из документа вместо захардкоженных localhost:3000.

  • CLI: в шаблоне package.json команда deploy использует pm2 startOrRestart вместо pm2 restart — корректно работает и при первом запуске процесса.

  • CLI: из шаблона indexDevMin убран неиспользуемый импорт Bot.

  • Документация: next-release.md переименован в migration-2x-to-3x.md (содержимое — гайд миграции 2.x→3.0, старое название вводило в заблуждение); ссылки в README/FAQ/CHANGELOG обновлены.

  • Документация: актуализированы api-reference.md и GUIDE.md — добавлены addForm/removeForm, BotTest.simulate(), исправлен пример кастомной Model (публичные абстрактные методы, допустимые типы правил), возвращаемые типы Preload.loadImages/loadSounds, версия фреймворка.

  • Bot: addForm дожидается асинхронного onComplete (тип расширен до void | Promise<void>) и сохраняет ответы пользователя в исходном регистре (originalUserCommand), а не в нижнем.

  • BotController: асинхронная ошибка в callback шага теперь тоже выставляет безопасный fallback-текст, если обработчик не успел задать свой.

  • AppContext: маскировка метаданных в logError/logWarn защищена try/catch — кидающий геттер в meta больше не ломает логирование и обработку запроса.

  • BotTest: simulate() без явной платформы берёт первую зарегистрированную при appType='auto' вместо несуществующей платформы 'auto'.

  • RateLimiter: LRU-вытеснение при переполнении пропускает записи, которые прямо сейчас обрабатывают очередь, — больше не создаются две параллельные очереди на пользователя.

  • [Критично] Производительность/Логирование: из асинхронной saveData убрана валидация через JSON.parse. Она никогда не блокировала запись (в отличие от saveDataSync), но для строк лога в режиме дозаписи (начинаются с [timestamp]) парс гарантированно падал и через errorLogger запускал бесконечный цикл самовоспроизводящихся ошибок с ростом error.log каждые ~200 мс. Для saveFileData тот же парс выполнял лишнюю полную десериализацию всей таблицы при каждом сохранении FileAdapter.

  • Производительность/Viber: ViberRequest.call() сериализует тело запроса один раз и переиспользует строку как тело (postInString) — ранее JSON.stringify выполнялся дважды (для проверки размера 30 КБ и внутри Request._getOptions).

  • Производительность/Telegram: getContent больше не инстанцирует Card (и вложенный Buttons) на каждый запрос — добавлена проверка isCardInit(), как в остальных адаптерах.

  • Viber: добавлена обработка всех типов событий webhook: subscribed, unsubscribed, delivered, seen, failed — ранее они вызывали ошибку, так как setQueryData возвращал false. Теперь subscribed/unsubscribed логируются, а delivered/seen/failed корректно подтверждаются без ошибок.

  • Viber: исправлена детекция платформы — isPlatformOnQuery больше не требует message_token, так как события conversation_started, subscribed, unsubscribed его не содержат.

  • Viber: для не-текстовых сообщений (picture, video, file, location, contact, sticker) теперь сохраняется тип сообщения (viberMessageType) и дополнительные данные (media, location, contact, sticker_id) в controller.payload.

  • Viber: getQueryExample теперь включает поле timestamp, чтобы пример проходил isPlatformOnQuery.

  • Viber: IViberContent.message_token и sender сделаны опциональными в интерфейсе — для событий без этих полей.

  • Viber: Rich Media карточки без кнопок получают нейтральный ActionType: none и больше не отправляют придуманный фреймворком текст от имени пользователя.

  • Viber: исправлено обращение к query.user.name вместо query.sender.name в событии conversation_started.

  • Max: базовый URL API обновлён с platform-api.max.ru на platform-api2.max.ru в соответствии с миграцией платформы (июнь 2026).

  • Max: в IMaxButton.type добавлен тип clipboard — кнопка, копирующая текст из payload в буфер обмена пользователя.

  • Max: исправлено значение update_type с message_editing на message_edited для соответствия документации платформы.

  • Max: добавлена опциональная проверка подписи webhook через заголовок X-Max-Bot-Api-Secret. Включается передачей secret в additionalPlatformOptions конструктора адаптера.

  • Max: при skipAutoReply=true больше не выполняются лишние HTTP-вызовы загрузки изображений и аудио (buttonProcessing, cardProcessing, soundProcessing).

  • Max: добавлена поддержка групповых чатов и каналов — при наличии chat_id в webhook ответ отправляется в чат, а не в личный диалог.

  • AppContext: кастомный logger.error/logger.warn теперь маскирует секреты в сообщении и вложенных meta по умолчанию; maskSecrets: false по-прежнему оставляет явный opt-out для отладки.

  • BotController: синхронные исключения в callback шага теперь логируются и переводят диалог в безопасный ответ вместо падения обработки.

  • MongoAdapter: при переподключении закрывается предыдущий mongoClient, а не новый клиент перед connect().

  • Telegram: multi-image карточки ограничены лимитом sendMediaGroup в 10 элементов; одиночное изображение после фильтрации отправляется через sendPhoto без повторного вызова sendMediaGroup.

  • CLI: generated HTTP-запросы теперь используют fetchWithTimeout, корректно обрабатывают catch (e: unknown) и безопасно сериализуют JSON body с переменными {{var}}.

  • CLI: serverless.yml больше не получает реальные значения токенов из flow.json, а использует ссылки на переменные окружения.

  • CLI: create from-flow больше не перезаписывает непустую директорию без --force.

  • CLI Docker: builder-stage устанавливает devDependencies, поэтому TypeScript-компилятор доступен при npm run build.

  • Bot: исправлена потенциальная потеря данных: #saveUserData теперь обёрнут в try/finally, гарантируя сохранение даже при исключениях в бизнес-логике.

  • Bot: ошибка подключения к БД теперь корректно возвращает undefined вместо нерабочего экземпляра адаптера.

  • VK: исправлена обработка message_event: адаптер теперь использует messages.sendMessageEventAnswer с обязательными event_id, user_id и peer_id, а также валидирует размер event_data.

  • VK: преобразователь кнопок больше не мутирует исходные универсальные кнопки и не переносит в VK посторонние поля из options.

  • VK: payload кнопки проверяется после сериализации; значение больше 255 байт больше не обрезается до повреждённого JSON — кнопка пропускается с предупреждением.

  • VK: исправлен поиск hash для кнопок vkpay при передаче строкового payload.

  • Telegram: добавлена проверка длины callback_data (лимит 64 байта). При превышении кнопка пропускается с предупреждением, а идентификатор разработчика не изменяется.

  • Telegram: исправлена обработка multi-image: при фильтрации и остатке менее 2 элементов изображение корректно отправляется через sendPhoto.

  • Viber: исправлен лимит Columns в карточках с 7 на 6 (в соответствии с Viber Rich Media API).

  • Viber: ActionBody в кнопках теперь использует button.payload (если он есть), а не всегда button.title.

  • Viber: текст ограничивается документированными 7000 символами, запросы больше 30 КБ не отправляются, а обязательные поля текстового сообщения нельзя случайно перезаписать через params.

  • Viber: универсальные опции кнопок фильтруются и сопоставляются с нативными действиями; обязательные поля webhook/Rich Media защищены от перезаписи, а видимое имя sender больше не придумывается фреймворком.

  • Viber: событие conversation_started теперь корректно обрабатывается (ранее блокировалось условием if (query.message)).

  • Маруся: добавлена строгая валидация размера payload (лимит 4096 байт) и state (лимит 3584 байта).

  • AppContext: JSON.stringify в методах logError/logWarn обёрнут в try/catch для защиты от циклических ссылок в метаданных.

  • AppContext: setAppConfig теперь выполняет глубокое слияние (deep merge) для поля db.

  • AppContext: setPlatformParams больше не мутирует исходные интенты пользователя.

  • Max Card / Telegram Card: добавлена проверка границ (bounds) для images[0] при showOne=true.

  • Max Sound: исправлена потеря данных: при отсутствии speech_kit_token теперь возвращаются уже обработанные звуки вместо null.

  • SmartApp Card: buttonProcessing теперь возвращает null вместо [], если isCard=true, но валидной кнопки не найдено.

  • CLI: исправлен краш при выполнении add docker / add deploy (проблема инициализации #name вне createProject()).

  • CLI: исправлена генерация невалидного кода при пустых условиях в шаблонах.

  • CLI: tsconfig.json в flow-генераторе обновлён с устаревшего moduleResolution: 'node' на Node16.

  • Utils: httpBuildQuery теперь использует encodeURIComponent вместо encodeURI для корректного кодирования спецсимволов (&, =, #) в значениях параметров.

  • Utils: fwriteSync/fwrite теперь гарантированно удаляют временные файлы при ошибке переименования.

  • Utils: удаление временных файлов в модулях VK Sound и Telegram Sound обёрнуто в try/catch.

  • EnvConfig: добавлена корректная обработка inline-комментариев (#) в .env файлах.

  • RateLimiter: destroyRateLimiter теперь очищает очередь (drain) перед полным удалением.

  • UsersData: исправлено несоответствие ключей между RULES и ATTRS_LABEL (platformName заменён на platform).

  • CLI: .gitignore в шаблоне проекта всегда оказывался пустым, из-за чего .env с токенами коммитился в Git. Теперь читается из правильного пути cli/template/.gitignore.

  • HTTP: Request по умолчанию не имел таймаута — вешал бота при зависании API платформы. Установлен безопасный default 2000 мс (переопределяется через свойство request.maxTimeQuery).

  • CLI: при формате tokens в flow.json в стиле {telegram: {token: "..."}} (как в README) процесс генерации падал с TypeError. Поддержаны оба формата — плоский и вложенный.

  • CLI: текстовые шаблоны вида {{user.name}} не разворачивались (regex \w+ не захватывал точку). Теперь поддержаны.

  • CLI: execFileSync(prettier) без таймаута мог навсегда зависнуть при сломанном prettier. Добавлен timeout: 30_000.

  • CLI: CreateController.init() ломался на Windows при путях с обратным слешом. Поддержаны оба разделителя.

  • Navigation: selectedElement('12') возвращал первый элемент вместо двенадцатого (+data[0][0] вместо +data[0]).

  • Telegram: webhook-проверка подписи использовала HMAC от тела, но Telegram шлёт x-telegram-bot-api-secret-token как plain string. Все webhook-запросы отклонялись. Теперь — прямое timingSafeEqual с опциональным appConfig.tokens.telegram.webhookSecret.

  • Telegram: исправлен тип photo в ITelegramResultContent — заменён с одиночного ITelegramPhoto на массив ITelegramPhoto[] в соответствии с Telegram Bot API (sendPhoto возвращает PhotoSize[]). file_id теперь извлекается из последнего элемента массива (самое большое разрешение).

  • Telegram: ITelegramResult.result сделан опциональным (result?: ITelegramResultContent | null) — при ok: false результат отсутствует.

  • Telegram: добавлена обработка edited_message и edited_channel_post в setQueryData() (ранее определялись в isPlatformOnQuery, но не обрабатывались).

  • Telegram: для reply-клавиатуры добавлен resize_keyboard: true для автоматического подгона размера под экран.

  • Telegram: типизированы поля edited_message и edited_channel_post в ITelegramContent — заменены с Record<string, unknown> на ITelegramMessage.

  • Telegram: в ITelegramKeyboard добавлены поля resize_keyboard, one_time_keyboard, input_field_placeholder.

  • Telegram: добавлены юнит-тесты для TelegramAdapter, Button.ts, Card.ts и Sound.ts.

  • Telegram: callback подтверждается отдельно от сообщения в чат: controller.text больше не дублируется как toast. Явный platformOptions.callbackNotificationText и прямой answerCallbackQuery.text ограничиваются 200 символами.

  • Viber: одиночная карточка (одно изображение) молча не отправлялась — cardProcessing возвращал объект, а не массив.

  • Marusia: marusiaCreateAudio отправлял audio_meta как [object Object] вместо JSON-строки — загрузка звуков не работала.

  • MongoDB: повторный вызов connect() переиспользовал "закрытый" MongoClient, вызывая ошибку MongoClient is closed. Каждая попытка теперь создаёт новый клиент.

  • MongoDB: при update поля со значением undefined сохранялись в BSON и затирали существующие поля. Теперь фильтруются перед $set.

  • Models: ImageTokens.selectOne() и SoundTokens.selectOne() искали по первичному ключу (который null на новой модели) вместо задокументированного паттерна поиска по path. Кэш токенов платформ не работал.

  • CommandReg: removeCommand удалял команду до обновления regexp-группы, из-за чего команда оставалась внутри группы и создавалась "пустая" запись с ключом undefined.

  • Bot: gracefulShutdown падал при ошибке close(), не вызывая process.exit(0). Добавлен try/catch.

  • Bot: при невалидном webhook токене в лог-файл улетал весь IncomingMessage (IP, headers, sockets). Теперь только metadata.

  • AppContext: #errWarnLog при непрерывном потоке ошибок рос без ограничений. Добавлен cap в 1000 записей с немедленным flush.

  • AppContext: setAppConfig({db: ...}) затирал вложенный db.options. Теперь выполняется глубокое слияние.

  • RateLimiter: при переполнении stateMap новые пользователи проходили без ограничений (fail-open). Заменено на LRU eviction.

  • Utils: fwriteSync/fwrite при параллельной записи одного файла конфликтовали на одном .tmp имени. Добавлен уникальный суффикс {pid}.{timestamp}.{rand}.tmp.

  • Utils: similarText при длинных строках (>2000 суммарно) блокировал event loop из-за O(n·m). Добавлен быстрый fallback по префиксу/длине.

  • MAX: лимит текста был 4096, но API отклоняет >4000. Исправлено.

  • VK: random_id выходил за диапазон int32 (Date.now()*1000). Ограничен до 2^31-1.

  • VK: при отправке по screen_name в тело запроса уходил peer_id=undefined. Поле удаляется вместо присваивания.

  • VK: signatureName = 'x-vk-signature' ломал webhook при заданном token — у VK другая модель безопасности (через secret_key в теле запроса), HTTP-подписи нет. Теперь signatureName = undefined, чтобы базовая HMAC-проверка не блокировала webhook.

  • VK: добавлена опциональная проверка secret из тела запроса VK Callback API. Если задан vk_secret_key в конструкторе адаптера или переменная VK_SECRET_KEY в .env, адаптер сверяет поле secret каждого запроса с сохранённым значением. Не влияет на confirmation событие.

  • VK: getContent() для message_event теперь при ошибке бизнес-логики отправляет sendMessageEvent с show_snackbar и текстом ошибки, а не пустое подтверждение.

  • Viber: действие message без sender больше не приводит к TypeError: Cannot read properties of undefined — адаптер корректно отклоняет malformed-запрос через platformOptions.error.

  • AppContext: маскировка метаданных логгера больше не помечает повторяющиеся (не циклические) объекты как [Circular] — маркер срабатывает только при реальной циклической ссылке.

  • AppContext: overflow-flush #errWarnLog сбрасывает только переполненную очередь (errors или warnings), не затирая вторую и не прерывая debounce.

  • Платформы: устранены несовместимости с актуальными API: MAX использует raw X-Max-Bot-Api-Secret, user_id/chat_id в query, двухшаговую загрузку файлов, матрицу inline-клавиатуры и answers для callback; VK отвечает в peer_id беседы; Viber не отправляет пустые сообщения на служебные события; Алиса ограничивает state 1 КБ; Telegram не включает HTML-разметку без явной настройки и отвечает на inline-запросы через answerInlineQuery.

  • Платформы: Telegram направляет ответ на callback-кнопку в исходный чат, VK сериализует кнопки карусели плоским массивом, SmartApp использует массив actions и документированный формат server_action, а Viber не вызывает send_message с пустым текстом перед Rich Media.

  • Платформы: устранены дополнительные несовместимости форматов: MAX соблюдает лимит 2 сообщения и 2 callback-ответа/с на диалог, ограничивает клавиатуру 30 рядами, отправляет изображения отдельными attachments и принимает служебные webhook-события; Viber передаёт обязательный sender и реальные метаданные файлов; SmartApp формирует актуальный left_right_cell_view; Алиса ограничивает ImageGallery десятью элементами и BigImage.description 1024 символами.

  • Платформы: адаптеры больше не подставляют пользовательские реплики «Ответ готов», «Ответ бота», «Смотрите на экране», «Выберите действие», «Изображение» и «Открыть». Пустой ответ Алисы/Маруси сохраняется без подстановки и исключения с диагностическим предупреждением; Telegram/MAX не отправляют недопустимое пустое сообщение.

  • Алиса: start_account_linking перенесён из недопустимого верхнего уровня в response.directives; ответ авторизации теперь содержит обычный response согласно протоколу.

  • MAX: пустой callback корректно подтверждается через POST /answers без придуманного сообщения; webhook URL с нестандартным портом отклоняется до HTTP-запроса.

  • Card: addImage() и addOneImage() больше не подставляют пробел в отсутствующие title/description; используются честные пустые строки.

  • Кнопки платформ: циклические и иные несериализуемые payload больше не обрушивают обработку запроса; невалидная кнопка пропускается без подмены данных.

  • CLI: генератор больше не вставляет произвольные значения flow в TypeScript: для set_variable доступна ограниченная арифметика, HTTP-методы и границы случайного числа валидируются, а пустой успешный HTTP-ответ корректно сохраняется как null.

  • CLI: hostname и port из пользовательской конфигурации безопасно сериализуются перед вставкой в шаблон; CLI на JavaScript теперь проходит ESLint.

  • CLI: зависимости генерируемых проектов зафиксированы, а package-lock.json больше не исключается из Git.

  • CLI: версия umbot в flow-генераторе синхронизирована с production-шаблоном 3.1.0.

  • CLI: flow-генератор теперь нормализует имена блоков в безопасные TypeScript-идентификаторы, сохраняет HTTP-метод у запросов без body и корректно обрабатывает проверки isSay* по текущему вводу.

  • CLI: Cloud Functions-проект больше не запускает локальный HTTP-сервер, использует compiled entrypoint dist/index.handler и актуальный runtime Node.js 22.

  • CLI: шаблоны Docker и GitHub Actions больше не требуют отсутствующий package-lock.json; опасные перезаписи .env, Dockerfile и workflow требуют явный --force.

  • Регистрация более 300 pattern-команд теперь поддерживает произвольные имена команд, включая дефис.

  • AppContext: пользовательские логгеры теперь маскируют секреты в сообщении и вложенных метаданных по умолчанию.

Документация и внутренние улучшения (Chore)

  • Исправлены некорректные форматы дат в истории изменений (например, 2025-30-112025-11-30).
  • Внутренний тип TPatternRegExp заменён на (string | RegExp)[] в api-reference.md.
  • Относительные импорты ../../src заменены на алиас umbot в next-release.md.
  • Исправлены неточности в JSDoc: описание @param controller (Alisa), неверное название платформы в Marusia Card, описание конструктора "ВКонтакте" → "Max" в MaxRequest, добавлены пропущенные описания @param.
  • Удалено избыточное описание "кнопки для кнопок" в Alisa/Card.ts.
  • Исправлено форматирование кода (4 блока) в adapter/platformAdapter.md и обновлена таблица производительности в FAQ.md.
  • Устранена циклическая ссылка в middleware.md (заменена на ссылку на GUIDE.md), скорректирована иерархия заголовков в FAQ.md.
  • Тест MarusiaRequest обновлён для соответствия новой версии VK API, добавлены тесты на критический функционал.
  • Перефразирована META-фраза в GUIDE.md (убраны прямые упоминания LLM), сокращена секция плагинов в FAQ.md с добавлением абсолютной ссылки на документацию.

ma.mochalov added 8 commits July 8, 2026 14:00
Исправлены ошибки
Исправлены ошибки, выявленные в ходе анализа проекта
Также исправлена проблема с race condition
Исправлены проблемы с документаций
Исправлены проблемы в адаптерах и утилитах
Исправлены найденные ошибки
Немного повышена производительность
Исправлены найденные ошибки
Немного повышена производительность
Исправлены найденные ошибки
Доработана документация
Исправлены ошибки
Повыше стабильность
Больше правок в CHANGELOG.md
Comment on lines +9 to +59
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '24'
cache: 'npm'

- name: Install dependencies
run: npm ci

- name: Build project
run: npm run build

- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ secrets.DOCKERHUB_USERNAME }}/my-umbot-bot:latest

- name: Deploy to server via SSH
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
key: ${{ secrets.SSH_KEY }}
script: |
docker stop my-umbot-bot || true
docker rm my-umbot-bot || true
docker pull ${{ secrets.DOCKERHUB_USERNAME }}/my-umbot-bot:latest
docker run -d \
--name my-umbot-bot \
-p 3000:3000 \
-e YANDEX_TOKEN=${{ secrets.YANDEX_TOKEN }} \
-e TELEGRAM_TOKEN=${{ secrets.TELEGRAM_TOKEN }} \
-e VK_TOKEN=${{ secrets.VK_TOKEN }} \
-e VK_CONFIRMATION_TOKEN=${{ secrets.VK_CONFIRMATION_TOKEN }} \
-e VIBER_TOKEN=${{ secrets.VIBER_TOKEN }} \
-e MARUSIA_TOKEN=${{ secrets.MARUSIA_TOKEN }} \
-e MAX_TOKEN=${{ secrets.MAX_TOKEN }} \
${{ secrets.DOCKERHUB_USERNAME }}/my-umbot-bot:latest
Comment thread src/components/nlu/Nlu.ts
public static getLink(query: string): INluResult<string[] | null> {
const matches = [...query.matchAll(Nlu.LINK_REGEX)].map((m) => m[0]);
const matches: string[] = [];
for (const m of query.matchAll(Nlu.LINK_REGEX)) {

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9f5b05bd2a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +90 to +92
if (!Array.isArray(reg) && isRegex(reg) && !customReg) {
return reg;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Compile RegExp objects through the configured safe engine

When re2 is installed and a command uses a RegExp object as a slot, this branch returns the native object instead of compiling its source through Re2; Text.#isSayPattern then executes that native expression directly. A vulnerable expression such as (a+)+$ can therefore block the event loop despite the application having installed the safe engine, unless strict mode happens to reject it during registration. Preserve the original direct object only when useDirectRegExp was explicitly requested.

AGENTS.md reference: AGENTS.md:L68-L69

Useful? React with 👍 / 👎.

Comment thread cli/flowGenerator.js
` const content = typeof event.body === 'string' ? event.body : JSON.stringify(event.body);`,
);
lines.push(` bot.setContent(content);`);
lines.push(` const result = await bot.run();`);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve webhook authentication in the cloud handler

For generated Yandex Cloud projects, calling bot.run() directly bypasses the isCorrectQuery() check performed by webhookHandle(). Consequently, Telegram or VK deployments that configure a webhook secret still accept forged request bodies at the public function endpoint, allowing arbitrary commands and their side effects to run. The generated handler needs to retain the event headers and route through the authenticated webhook path, or explicitly perform the same adapter verification before run().

AGENTS.md reference: AGENTS.md:L99-L106

Useful? React with 👍 / 👎.

Comment thread src/core/AppContext.ts
...config.tokens[platform],
};
}
delete config.tokens;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid deleting tokens from the caller's config

When setAppConfig() receives a config containing tokens, this deletes the property from the object supplied by the caller. Reusing that object later silently loses its token settings, and passing an Object.freeze()-ed configuration throws a TypeError under the emitted strict-mode JavaScript. Merge from a copied/destructured object instead of mutating this public method's input.

AGENTS.md reference: AGENTS.md:L50-L53

Useful? React with 👍 / 👎.

Comment thread cli/flowGenerator.js
Comment on lines +189 to +190
n.actions.forEach((a) => {
if (a.field && !isSystemVar(a.field)) vars.add(a.field);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Collect HTTP response variables for later expressions

The variable collector records only action.field, even though HTTP actions write their result through saveResponseTo. If a flow saves an HTTP response as result and later uses result in arithmetic or as the right-hand side of a condition, the generator does not recognize it as a known variable and emits a string literal such as 'result + 1' or 'result' instead of a ctrl.userData.result expression. Add saveResponseTo to the collected names.

AGENTS.md reference: cli/AGENTS.md:L100-L101

Useful? React with 👍 / 👎.

Comment on lines +425 to +430
normalizedParams.correct_option_ids === undefined &&
normalizedParams.correct_option_id !== undefined
) {
normalizedParams.correct_option_ids = [normalizedParams.correct_option_id];
}
delete normalizedParams.correct_option_id;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep Telegram's singular quiz answer parameter

For every quiz poll that supplies correct_option_id, this converts the documented singular integer into correct_option_ids and then removes the valid field. Telegram's sendPoll method requires correct_option_id; the plural option_ids concept belongs to poll answers, not quiz creation. The resulting request is rejected for lacking the required correct option, so quiz polls consistently return an API error.

Useful? React with 👍 / 👎.

Comment on lines +131 to +139
// Проверяем только начало и конец
const prefixLen = Math.min(50, Math.min(first.length, second.length));
const firstPrefix = first.slice(0, prefixLen);
const secondPrefix = second.slice(0, prefixLen);
if (firstPrefix !== secondPrefix) {
return 0;
}
// Если префиксы совпали — возвращаем примерную оценку по длине
return Math.round(((maxLen - lengthDiff) / maxLen) * 100);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid reporting prefix-only matches as identical

For inputs whose combined length exceeds 2,000 characters, two strings of equal length receive 100% similarity whenever only their first 50 characters match, regardless of the remaining content. For example, a common 50-character prefix followed by 1,000 entirely different characters is reported as identical and passes the default 80% threshold in Text.textSimilarity. Keep the bounded-cost fallback, but sample or compare enough of the full strings that unrelated suffixes cannot produce a perfect match.

Useful? React with 👍 / 👎.

Comment on lines +20 to +21
- name: Install dependencies
run: npm ci

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Install dependencies without requiring an absent lockfile

The newly added deploy workflow runs npm ci, but this commit does not contain a package-lock.json and .gitignore explicitly excludes it. Checked the repository tree at the reviewed commit: no supported npm lockfile is present, so every push to main stops in this install step before the referenced build or deployment sections run. Either commit a lockfile or use an installation command that supports this repository's lockfile policy.

Useful? React with 👍 / 👎.

Comment on lines +32 to +35
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Provide a Dockerfile to the root image build

After the install stage is fixed, the docker/build-push-action step still uses repository root as its context without specifying file. That action therefore looks for the root Dockerfile, but a repo-wide file search shows that the only Dockerfile-like artifact is cli/template/docker/DockerFile.text; there is no buildable root Dockerfile. Every execution reaching this section fails before pushing or deploying the image, so add a root Dockerfile or point file at an actual generated build definition.

Useful? React with 👍 / 👎.

Comment thread cli/flowGenerator.js
Comment on lines +702 to +705
for (const block of blocks) {
const needsAsync = blockNeedsAsync(block, doc, connectedBlocks);
lines.push(`${indent}${needsAsync ? 'await ' : ''}${getBlockFunctionName(block)}(ctrl);`);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Prevent recursive block cycles in generated flows

The validator explicitly accepts graph cycles, but every outgoing action/condition/response edge is emitted as an immediate function call with no runtime cycle guard. A valid graph such as command → action A → action B → action A therefore generates mutually recursive functions and throws RangeError: Maximum call stack size exceeded whenever the command runs. Either reject cycles composed solely of executable blocks or translate them into bounded navigation rather than direct recursion.

AGENTS.md reference: cli/AGENTS.md:L7-L8

Useful? React with 👍 / 👎.

Comment on lines 261 to +264
const stateMap = new Map<string, PlatformState>();
moduleStateMap = stateMap;
let cleanupInterval: ReturnType<typeof setInterval> | null = null;
moduleCleanupInterval = null;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Track every rate-limiter instance during cleanup

Each call to rateLimiter() overwrites the module-level state map and resets the stored interval before registering its own cleanup timer. If two bots or two independently configured limiter instances exist, destroyRateLimiter() can clear only the most recently created one; earlier intervals and their per-user maps remain reachable and continue running for the lifetime of the process. Store cleanup handles per instance, or maintain a collection that destroyRateLimiter() drains.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants