/*****************************************************************************\
|                                               ( )_  _                       |
|    _ _    _ __   _ _    __    ___ ___     _ _ | ,_)(_)  ___   ___     _     |
|   ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| |  | |/',__)/' v `\ /'_`\   |
|   | (_) )| |  ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) )  |
|   | ,__/'(_)  `\__,_)`\__  |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/'  |
|   | |                ( )_) |                                                |
|   (_)                 \___/'                                                |
|                                                                             |
| General Bots Copyright (c) Pragmatismo.io. All rights reserved.             |
| Licensed under the AGPL-3.0.                                                |
|                                                                             |
| According to our dual licensing model, this program can be used either      |
| under the terms of the GNU Affero General Public License, version 3,        |
| or under a proprietary license.                                             |
|                                                                             |
| The texts of the GNU Affero General Public License with an additional       |
| permission and of our proprietary license can be found at and               |
| in the LICENSE file you have received along with this program.              |
|                                                                             |
| This program is distributed in the hope that it will be useful,             |
| but WITHOUT ANY WARRANTY, without even the implied warranty of              |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the                |
| GNU Affero General Public License for more details.                         |
|                                                                             |
| "General Bots" is a registered trademark of Pragmatismo.io.                 |
| The licensing of the program under the AGPLv3 does not imply a              |
| trademark license. Therefore any rights, title and interest in              |
| our trademarks remain entirely with us.                                     |
|                                                                             |
\*****************************************************************************/
/**
 * @fileoverview General Bots server core.
 */
'use strict';
Object.defineProperty(exports, "__esModule", { value: true });
exports.AskDialog = exports.AskDialogArgs = void 0;
const app_1 = require("../../../src/app");
const botbuilder_dialogs_1 = require("botbuilder-dialogs");
const botlib_1 = require("botlib");
const strings_1 = require("../strings");
const KBService_1 = require("./../services/KBService");
const SecService_1 = require("../../security.gbapp/services/SecService");
const pragmatismo_io_framework_1 = require("pragmatismo-io-framework");
const GBVMService_1 = require("../../basic.gblib/services/GBVMService");
const GBImporterService_1 = require("../../core.gbapp/services/GBImporterService");
const GBDeployer_1 = require("../../core.gbapp/services/GBDeployer");
/**
 * Dialog arguments.
 */
class AskDialogArgs {
}
exports.AskDialogArgs = AskDialogArgs;
/**
 * Handle the ask loop on knowledge base data or delegate to other services.
 */
class AskDialog extends botlib_1.IGBDialog {
    /**
     * Setup dialogs flows and define services call.
     *
     * @param bot The bot adapter.
     * @param min The minimal bot instance data.
     */
    static setup(bot, min) {
        const service = new KBService_1.KBService(min.core.sequelize);
        const importer = new GBImporterService_1.GBImporter(min.core);
        this.deployer = new GBDeployer_1.GBDeployer(min.core, importer);
        min.dialogs.add(new botbuilder_dialogs_1.WaterfallDialog('/answerEvent', AskDialog.getAnswerEventDialog(service, min)));
        min.dialogs.add(new botbuilder_dialogs_1.WaterfallDialog('/answer', AskDialog.getAnswerDialog(min, service)));
        min.dialogs.add(new botbuilder_dialogs_1.WaterfallDialog('/ask', AskDialog.getAskDialog(min)));
    }
    static getAskDialog(min) {
        return [
            async (step) => {
                if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) {
                    return await step.beginDialog('/auth');
                }
                else {
                    return await step.next(step.options);
                }
            },
            async (step) => {
                const locale = step.context.activity.locale;
                const user = await min.userProfile.get(step.context, {});
                user.isAsking = true;
                if (!user.subjects) {
                    user.subjects = [];
                }
                let text;
                // Three forms of asking.
                if (step.options && step.options.firstTime) {
                    text = strings_1.Messages[locale].ask_first_time;
                }
                else if (step.options && step.options.isReturning) {
                    text = ""; // REMOVED: Messages[locale].anything_else;
                }
                else if (step.options && step.options.emptyPrompt) {
                    text = "";
                }
                else if (user.subjects.length > 0) {
                    text = strings_1.Messages[locale].which_question;
                }
                else {
                    throw new Error('Invalid use of /ask');
                }
                return await min.conversationalService.prompt(min, step, text);
            },
            async (step) => {
                if (step.result) {
                    let text = step.result;
                    text = text.replace(/<([^>]+?)([^>]*?)>(.*?)<\/\1>/gi, '');
                    let sec = new SecService_1.SecService();
                    const member = step.context.activity.from;
                    const user = await sec.ensureUser(min.instance.instanceId, member.id, member.name, '', 'web', member.name, null);
                    let handled = false;
                    let nextDialog = null;
                    await pragmatismo_io_framework_1.CollectionUtil.asyncForEach(min.appPackages, async (e) => {
                        if (nextDialog = await e.onExchangeData(min, 'handleAnswer', {
                            query: text,
                            step: step,
                            message: text,
                            user: user ? user['dataValues'] : null
                        })) {
                            handled = true;
                        }
                    });
                    await step.beginDialog(nextDialog ? nextDialog : '/answer', {
                        query: text,
                        user: user ? user['dataValues'] : null,
                        message: text
                    });
                }
                else {
                    return await step.next();
                }
            }
        ];
    }
    static getAnswerDialog(min, service) {
        return [
            async (step) => {
                if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) {
                    return await step.beginDialog('/auth');
                }
                else {
                    return await step.next(step.options);
                }
            },
            async (step) => {
                let answer = null;
                const user = await min.userProfile.get(step.context, {});
                const minBoot = app_1.GBServer.globals.minBoot;
                let text = step.options.query;
                text = text.replace(/<([^>]+?)([^>]*?)>(.*?)<\/\1>/gi, '');
                // When no text is typed, the start dialog is invoked again
                // when people type just the @botName in MSTEAMS for example.
                if (!text) {
                    const startDialog = min.core.getParam(min.instance, 'Start Dialog', null);
                    if (startDialog) {
                        await GBVMService_1.GBVMService.callVM(startDialog.toLowerCase().trim(), min, step, this.deployer);
                    }
                    return step.endDialog();
                }
                const locale = step.context.activity.locale;
                // Stops any content on projector.
                await min.conversationalService.sendEvent(min, step, 'stop', undefined);
                // Handle extra text from FAQ.
                if (step.options && step.options.query) {
                    text = step.options.query;
                }
                else if (step.options && step.options.fromFaq) {
                    await min.conversationalService.sendText(min, step, strings_1.Messages[locale].going_answer);
                }
                // Searches KB for the first time.
                const searchScore = min.core.getParam(min.instance, 'Search Score', min.instance.searchScore ? min.instance.searchScore : minBoot.instance.searchScore);
                user.lastQuestion = text;
                await min.userProfile.set(step.context, user);
                const resultsA = await service.ask(min.instance, text, searchScore, user.subjects);
                // If there is some result, answer immediately.
                if (resultsA !== undefined && resultsA.answer !== undefined) {
                    // Saves some context info.
                    user.isAsking = false;
                    user.lastQuestionId = resultsA.questionId;
                    await min.userProfile.set(step.context, user);
                    // Sends the answer to all outputs, including projector.
                    answer = resultsA.answer;
                    // If this search was restricted to some subjects...
                }
                else if (user.subjects && user.subjects.length > 0) {
                    // ...second time running Search, now with no filter.
                    const resultsB = await service.ask(min.instance, text, searchScore, undefined);
                    // If there is some result, answer immediately.
                    if (resultsB !== undefined && resultsB.answer !== undefined) {
                        // Saves some context info.
                        const user2 = await min.userProfile.get(step.context, {});
                        user2.isAsking = false;
                        user2.lastQuestionId = resultsB.questionId;
                        await min.userProfile.set(step.context, user2);
                        // Informs user that a broader search will be used.
                        if (user2.subjects.length > 0) {
                            await min.conversationalService.sendText(min, step, strings_1.Messages[locale].wider_answer);
                        }
                        answer = resultsB.answer;
                    }
                }
                // Answers using Search or NLP responses.
                if (answer) {
                    return await AskDialog.handleAnswer(service, min, step, answer);
                }
                else if (!(await min.conversationalService.routeNLP(step, min, text))) {
                    const message = min.core.getParam(min.instance, 'Not Found Message', strings_1.Messages[locale].did_not_find);
                    await min.conversationalService.sendText(min, step, message);
                    return await step.replaceDialog('/ask', { isReturning: true });
                }
            }
        ];
    }
    static async handleAnswer(service, min, step, answer) {
        const text = answer.content;
        if (text.endsWith('.docx')) {
            const mainName = GBVMService_1.GBVMService.getMethodNameFromVBSFilename(text);
            return await GBVMService_1.GBVMService.callVM(mainName, min, step, this.deployer);
        }
        else {
            await service.sendAnswer(min, AskDialog.getChannel(step), step, answer);
            return await step.replaceDialog('/ask', { isReturning: true });
        }
    }
    static getChannel(step) {
        return !isNaN(step.context.activity['mobile']) ? 'whatsapp' : step.context.activity.channelId;
    }
    static getAnswerEventDialog(service, min) {
        return [
            async (step) => {
                if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) {
                    return await step.beginDialog('/auth');
                }
                else {
                    return await step.next(step.options);
                }
            },
            async (step) => {
                const data = step.options;
                if (data !== undefined && data.questionId !== undefined) {
                    const question = await service.getQuestionById(min.instance.instanceId, data.questionId);
                    const answer = await service.getAnswerById(min.instance.instanceId, question.answerId);
                    // Sends the answer to all outputs, including projector.
                    await service.sendAnswer(min, AskDialog.getChannel(step), step, answer);
                    await step.replaceDialog('/ask', { isReturning: true });
                }
                return await step.next();
            }
        ];
    }
}
exports.AskDialog = AskDialog;
//# sourceMappingURL=AskDialog.js.map