From 8fc8553d232a9229db2d91688b2216ac3de17e1f Mon Sep 17 00:00:00 2001 From: max36895 Date: Thu, 12 Jun 2025 19:49:01 +0300 Subject: [PATCH 1/2] doc fix doc --- README.md | 10 ++-------- package.json | 2 +- src/docs/getting-started.md | 5 +---- 3 files changed, 4 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index b01508d..329c23c 100644 --- a/README.md +++ b/README.md @@ -89,10 +89,7 @@ bot.initParams({ const logic = new EchoController(); bot.initBotController(logic); -// Экспорт обработчика для serverless -module.exports = async (req: IncomingMessage, res: ServerResponse) => { - bot.start(req, res); -}; +bot.start('localhost', 3000); ``` #### Логика приложения (EchoController.ts) @@ -232,10 +229,7 @@ bot.initParams({ const controller = new MyController(); bot.initBotController(controller); -// Экспорт обработчика для serverless -module.exports = async (req: IncomingMessage, res: ServerResponse) => { - bot.start(req, res); -}; +bot.start('localhost', 3000); ``` ##### Работа с кнопками diff --git a/package.json b/package.json index 7018644..281d88c 100644 --- a/package.json +++ b/package.json @@ -70,5 +70,5 @@ "files": [ "dist" ], - "version": "2.0.6" + "version": "2.0.8" } diff --git a/src/docs/getting-started.md b/src/docs/getting-started.md index 23e2651..faea9fd 100644 --- a/src/docs/getting-started.md +++ b/src/docs/getting-started.md @@ -66,10 +66,7 @@ bot.initParams({ const controller = new MyController(); bot.initBotController(controller); -// Экспортируем обработчик -export default async (req, res) => { - bot.start(req, res); -}; +bot.start('localhost', 3000); ``` ## Основные концепции From ae8cd5377fa6dbe3d7a31f96e733cc03652305b9 Mon Sep 17 00:00:00 2001 From: max36895 Date: Sat, 14 Jun 2025 18:01:47 +0300 Subject: [PATCH 2/2] fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit добавлен store дописаны юниты --- README.md | 3 +- src/controller/BotController.ts | 8 +++++ src/core/Bot.ts | 6 ++++ tests/Bot/bot.test.ts | 57 ++++++++++++++++++++++++++++++++- 4 files changed, 72 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 329c23c..eacf76a 100644 --- a/README.md +++ b/README.md @@ -423,11 +423,12 @@ acme.sh --install-cert -d {{domain}} --key-file {{key file}} --fullchain-file {{ С той лишь разницей, что нужно использовать класс `BotTest` вместо `Bot`. ```typescript -import { BotTest } from "umbot/dist/test"; +import { BotTest } from 'umbot/dist/test'; const bot = new BotTest(); bot.test(); ``` + Запуск будет выглядеть следующим образом: ```bash diff --git a/src/controller/BotController.ts b/src/controller/BotController.ts index 61fff9f..9de8409 100644 --- a/src/controller/BotController.ts +++ b/src/controller/BotController.ts @@ -258,6 +258,11 @@ export interface IUserData { * @template TUserData Тип пользовательских данных, по умолчанию {@link IUserData} */ export abstract class BotController { + /** + * Локальное хранилище с данными. Используется в случаях, когда нужно сохранить данные пользователя, но userData приложением не поддерживается. + * В случае если данные хранятся в usetData и store, пользователю вернятеся информация из userData. + */ + public store: Record | undefined; /** * Компонент для отображения кнопок пользователю * Позволяет создавать интерактивные элементы управления @@ -601,6 +606,9 @@ export abstract class BotController { this.nlu = new Nlu(); } + /** + * Очищает все временные данные необходимы для отправки ответа. + */ public clearStoreData(): void { this.buttons.clear(); this.card.clear(); diff --git a/src/core/Bot.ts b/src/core/Bot.ts index 261ee97..23d975f 100644 --- a/src/core/Bot.ts +++ b/src/core/Bot.ts @@ -539,6 +539,12 @@ export class Bot { if (this._botController.isSendRating) { content = await botClass.getRatingContext(); } else { + if ( + this._botController.store && + JSON.stringify(this._botController.userData) === '{}' + ) { + this._botController.userData = this._botController.store as TUserData; + } content = await botClass.getContext(); } if (!isLocalStorage) { diff --git a/tests/Bot/bot.test.ts b/tests/Bot/bot.test.ts index a7ddf10..e8e70d2 100644 --- a/tests/Bot/bot.test.ts +++ b/tests/Bot/bot.test.ts @@ -17,6 +17,7 @@ import { T_SMARTAPP, T_USER_APP, TemplateTypeModel, + IAlisaWebhookResponse, } from '../../src'; import { Server } from 'http'; @@ -26,15 +27,24 @@ class TestBotController extends BotController { } action(intentName: string | null, isCommand?: boolean) { + if (isCommand) { + this.userData.cool = true; + return; + } if (intentName === 'btn') { this.buttons.addBtn('1'); this.tts = 'btn'; } else if (intentName === 'card') { this.card.addImage('', 'Header'); this.tts = 'card'; + } else if (intentName === 'setStore') { + this.store = { + data: 'test', + }; + return; } this.text = 'test'; - return 'test'; + //return 'test'; } } @@ -227,6 +237,51 @@ describe('Bot', () => { await expect(bot.run(botClass)).rejects.toThrow(error); }); + it('added user command', async () => { + bot.initBotController(botController); + mmApp.appType = T_USER_APP; + const botClass = new Alisa(); + jest.spyOn(botClass, 'setLocalStorage').mockResolvedValue(undefined); + jest.spyOn(botClass, 'getError').mockReturnValue(null); + + jest.spyOn(usersData, 'whereOne').mockResolvedValue(Promise.resolve(true)); + jest.spyOn(usersData, 'save').mockResolvedValue(Promise.resolve(true)); + jest.spyOn(usersData, 'update').mockResolvedValue(Promise.resolve(true)); + mmApp.addCommand('cool', ['cool'], (_, botC) => { + botC.text = 'cool'; + botC.userData.cool = true; + }); + + bot.setContent(getContent('cool', 2)); + let res = (await bot.run(botClass)) as IAlisaWebhookResponse; + expect(res.response?.text).toBe('cool'); + // Убеждаемся что пользовательские данные скинулись, так как они хранятся в сессии. + expect(botController.userData.cool).toBe(undefined); + + mmApp.removeCommand('cool'); + res = (await bot.run(botClass)) as IAlisaWebhookResponse; + expect(res.response?.text).toBe('test'); + }); + + it('local store', async () => { + bot.initBotController(botController); + mmApp.appType = T_USER_APP; + const botClass = new Alisa(); + mmApp.setParams({ + intents: [{ name: 'setStore', slots: ['сохранить'] }], + }); + mmApp.setConfig({ isLocalStorage: true }); + jest.spyOn(botClass, 'getError').mockReturnValue(null); + + jest.spyOn(usersData, 'whereOne').mockResolvedValue(Promise.resolve(true)); + jest.spyOn(usersData, 'save').mockResolvedValue(Promise.resolve(true)); + jest.spyOn(usersData, 'update').mockResolvedValue(Promise.resolve(true)); + + bot.setContent(getContent('сохранить', 2)); + const res = (await bot.run(botClass)) as IAlisaWebhookResponse; + expect(res.session_state).toEqual({ data: 'test' }); + }); + it('skill started', async () => { bot.initBotController(botController); mmApp.appType = T_ALISA;