Conversation
| 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 |
| 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)) { |
There was a problem hiding this comment.
💡 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".
| if (!Array.isArray(reg) && isRegex(reg) && !customReg) { | ||
| return reg; | ||
| } |
There was a problem hiding this comment.
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 👍 / 👎.
| ` const content = typeof event.body === 'string' ? event.body : JSON.stringify(event.body);`, | ||
| ); | ||
| lines.push(` bot.setContent(content);`); | ||
| lines.push(` const result = await bot.run();`); |
There was a problem hiding this comment.
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 👍 / 👎.
| ...config.tokens[platform], | ||
| }; | ||
| } | ||
| delete config.tokens; |
There was a problem hiding this comment.
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 👍 / 👎.
| n.actions.forEach((a) => { | ||
| if (a.field && !isSystemVar(a.field)) vars.add(a.field); |
There was a problem hiding this comment.
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 👍 / 👎.
| normalizedParams.correct_option_ids === undefined && | ||
| normalizedParams.correct_option_id !== undefined | ||
| ) { | ||
| normalizedParams.correct_option_ids = [normalizedParams.correct_option_id]; | ||
| } | ||
| delete normalizedParams.correct_option_id; |
There was a problem hiding this comment.
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 👍 / 👎.
| // Проверяем только начало и конец | ||
| 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); |
There was a problem hiding this comment.
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 👍 / 👎.
| - name: Install dependencies | ||
| run: npm ci |
There was a problem hiding this comment.
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 👍 / 👎.
| - name: Build and push Docker image | ||
| uses: docker/build-push-action@v5 | ||
| with: | ||
| context: . |
There was a problem hiding this comment.
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 👍 / 👎.
| for (const block of blocks) { | ||
| const needsAsync = blockNeedsAsync(block, doc, connectedBlocks); | ||
| lines.push(`${indent}${needsAsync ? 'await ' : ''}${getBlockFunctionName(block)}(ctrl);`); | ||
| } |
There was a problem hiding this comment.
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 👍 / 👎.
| const stateMap = new Map<string, PlatformState>(); | ||
| moduleStateMap = stateMap; | ||
| let cleanupInterval: ReturnType<typeof setInterval> | null = null; | ||
| moduleCleanupInterval = null; |
There was a problem hiding this comment.
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 👍 / 👎.
Добавлено
ALISA_TOKENдля Алисы. Старое имяYANDEX_TOKENсохраняется для обратной совместимости — при одновременном задании обеих приоритет уALISA_TOKEN.npx umbot validate <flow.json>для проверки корректности flow.json перед генерацией (битые ссылки вedges, дубликаты id, невалидные идентификаторы, невалидный JSON). Циклы в графе допустимы и ошибкой не считаются.npx umbot stats --log <path>для агрегации метрик из лога (счётчики строк/ошибок/ворнингов, топ команд, p50/p95/p99 latency).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.addForm(formName, options)для многошаговых форм (опросников). Реализован поверх существующегоaddStep— для каждого поля регистрируется отдельный шаг__form_<formName>_<idx>, состояние ответа сохраняется вuserData.__formdata_<formName>. Поддерживаются синхронные и асинхронные валидаторы, prompt-функция, команды отмены с кастомным текстом.Botтеперь принимает дополнительный genericTPlatformState. Пример:new Bot<MyUserData, MyState>(). Это даёт типобезопасный доступ кctx.state.Bot— второй genericTPlatformStateдля типизации локального хранилища платформы (state).oldIntentNameдля восстановления шагов диалога через локальное хранилище платформы.bot.simulate(query, { platform, userId, count, state })— сам генерирует webhook payload черезgetQueryExampleплатформы и возвращает структурированный ответ. Упрощает написание интеграционных тестов.sendMessageEvent()вVkRequestдля корректного подтверждения callback-кнопок.eventIdвIPlatformOptionsдля хранения ID события callback-кнопки VK.ImageGallery.user_id.account_linking_complete_eventсbooleanдоobject | boolean.--forceдляcreate from-flow, чтобы перезапись непустой папки была явным действием.createPlugin(fn)(экспортируется изumbot) — оборачивает функцию-плагин и автоматически выставляет маркерisPlugin = true. Убирает частую ошибку, когда забытый флаг заставлялbot.use()трактовать плагин как middleware.Изменено
parse_modeотключён. Разметка применяется только при явномtelegram_parse_mode; разработчик сам отвечает за экранирование динамических данных.markdownзаменён наMarkdownV2.sendPollприведён к Bot API 10.2: 1–12 объектовInputPollOption, актуальное полеcorrect_option_idsи проверка индексов без незаметного удаления вариантов.sendMediaGroupсохраняетmediaпри передаче дополнительных параметров черезFormDataи отклоняет группы вне диапазона 2–10 без молчаливой потери элементов.VIBER_DEFAULT_API_VERSION).en_ENна стандартныйen-US. Добавлены новые голоса:kirkorov,lera,madusa.welcome,help), добавленные черезaddCommandбез явного указания слотов, теперь автоматически регистрируются со слотами по умолчанию.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.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()в JSDocBotController,UsersDataиGUIDE.mdпереписаны (асинхронная логика показана черезaddCommand/fire-and-forget), в JSDocaction()добавлено явное предупреждение.[Критично] Ядро: устранена циклическая зависимость
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 платформы. Установлен безопасный default2000 мс(переопределяется через свойство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.description1024 символами.Платформы: адаптеры больше не подставляют пользовательские реплики «Ответ готов», «Ответ бота», «Смотрите на экране», «Выберите действие», «Изображение» и «Открыть». Пустой ответ Алисы/Маруси сохраняется без подстановки и исключения с диагностическим предупреждением; 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-11→2025-11-30).TPatternRegExpзаменён на(string | RegExp)[]вapi-reference.md.../../srcзаменены на алиасumbotвnext-release.md.@param controller(Alisa), неверное название платформы в Marusia Card, описание конструктора "ВКонтакте" → "Max" в MaxRequest, добавлены пропущенные описания@param.adapter/platformAdapter.mdи обновлена таблица производительности вFAQ.md.middleware.md(заменена на ссылку наGUIDE.md), скорректирована иерархия заголовков вFAQ.md.MarusiaRequestобновлён для соответствия новой версии VK API, добавлены тесты на критический функционал.GUIDE.md(убраны прямые упоминания LLM), сокращена секция плагинов вFAQ.mdс добавлением абсолютной ссылки на документацию.