"use strict";
/*****************************************************************************\
|                                               ( )_  _                       |
|    _ _    _ __   _ _    __    ___ ___     _ _ | ,_)(_)  ___   ___     _     |
|   ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| |  | |/',__)/' 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.                                     |
|                                                                             |
\*****************************************************************************/
Object.defineProperty(exports, "__esModule", { value: true });
const urlJoin = require('url-join');
const Swagger = require('swagger-client');
const fs = require('fs');
const Path = require('path');
const botlib_1 = require("botlib");
const pragmatismo_io_framework_1 = require("pragmatismo-io-framework");
const request = require("request-promise-native");
const app_1 = require("../../../src/app");
const GBConversationalService_1 = require("../../core.gbapp/services/GBConversationalService");
const SecService_1 = require("../../security.gbapp/services/SecService");
const strings_1 = require("../strings");
const DialogKeywords_1 = require("../../basic.gblib/services/DialogKeywords");
const GBAdminService_1 = require("../../admin.gbapp/services/GBAdminService");
const GBMinService_1 = require("../../core.gbapp/services/GBMinService");
const GBConfigService_1 = require("../../core.gbapp/services/GBConfigService");
const puppeteer = require('puppeteer');
const { MessageMedia, Client, LocalAuth } = require('whatsapp-web.js');
const qrcode = require('qrcode-terminal');
/**
 * Support for Whatsapp.
 */
class WhatsappDirectLine extends botlib_1.GBService {
    constructor(min, botId, directLineSecret, whatsappServiceKey, whatsappServiceNumber, whatsappServiceUrl) {
        super();
        this.pollInterval = 3000;
        this.directLineClientName = 'DirectLineClient';
        this.locale = 'pt-BR';
        this.INSTANCE_URL = 'https://api.maytapi.com/api';
        this.min = min;
        this.botId = botId;
        this.directLineSecret = directLineSecret;
        this.whatsappServiceKey = whatsappServiceKey;
        this.whatsappServiceNumber = whatsappServiceNumber;
        this.whatsappServiceUrl = whatsappServiceUrl;
        this.provider = whatsappServiceKey === "internal" ?
            'GeneralBots' : whatsappServiceNumber.indexOf(';') > -1 ? 'maytapi' : 'chatapi';
    }
    static async asyncForEach(array, callback) {
        for (let index = 0; index < array.length; index++) {
            await callback(array[index], index, array);
        }
    }
    async setup(setUrl) {
        this.directLineClient =
            new Swagger({
                spec: JSON.parse(fs.readFileSync('directline-3.0.json', 'utf8')), usePromise: true
            });
        const client = await this.directLineClient;
        client.clientAuthorizations.add('AuthorizationBotConnector', new Swagger.ApiKeyAuthorization('Authorization', `Bearer ${this.directLineSecret}`, 'header'));
        let options;
        switch (this.provider) {
            case 'GeneralBots':
                const minBoot = app_1.GBServer.globals.minBoot;
                if (minBoot.botId !== this.botId) {
                    this.customClient = minBoot.whatsAppDirectLine.customClient;
                }
                else {
                    // Initialize the browser using a local profile for each bot.
                    const gbaiName = `${this.min.botId}.gbai`;
                    const localName = Path.join('work', gbaiName, 'profile');
                    const createClient = async (browserWSEndpoint) => {
                        let puppeteer = {
                            headless: false, args: ['--disable-features=site-per-process',
                                `--user-data-dir=${localName}`]
                        };
                        if (browserWSEndpoint) {
                            puppeteer = { browserWSEndpoint: browserWSEndpoint };
                        }
                        const client = this.customClient = new Client({
                            authStrategy: new LocalAuth({
                                clientId: this.min.botId,
                                dataPath: localName
                            }),
                            puppeteer: puppeteer
                        });
                        await client.initialize();
                        this.browserWSEndpoint = client.pupBrowser.wsEndpoint();
                        client.on('message', (async (message) => {
                            await this.WhatsAppCallback(message, null);
                        }).bind(this));
                        client.on('qr', (async (qr) => {
                            const adminNumber = this.min.core.getParam(this.min.instance, 'Bot Admin Number', null);
                            const adminEmail = this.min.core.getParam(this.min.instance, 'Bot Admin E-mail', null);
                            // Sends QR Code to boot bot admin.
                            const msg = `Please, scan QR Code with for bot ${this.botId}.`;
                            botlib_1.GBLog.info(msg);
                            qrcode.generate(qr, { small: true, scale: 0.5 });
                            // While handling other bots uses boot instance of this class to send QR Codes.
                            if (this.botId !== app_1.GBServer.globals.minBoot.botId) {
                                const s = new DialogKeywords_1.DialogKeywords(null, null, null, null);
                                const qrBuf = await s.getQRCode(qr);
                                const gbaiName = `${this.min.botId}.gbai`;
                                const localName = Path.join('work', gbaiName, 'cache', `qr${GBAdminService_1.GBAdminService.getRndReadableIdentifier()}.png`);
                                fs.writeFileSync(localName, qrBuf);
                                const url = urlJoin(app_1.GBServer.globals.publicAddress, this.min.botId, 'cache', Path.basename(localName));
                                app_1.GBServer.globals.minBoot.whatsAppDirectLine.sendFileToDevice(adminNumber, url, Path.basename(localName), msg);
                                s.sendEmail(adminEmail, `Check your WhatsApp for bot ${this.botId}`, msg);
                            }
                        }).bind(this));
                        client.on('authenticated', () => {
                            this.browserWSEndpoint = client.pupBrowser.wsEndpoint();
                            client.pupPage.on('error', (async () => {
                                botlib_1.GBLog.info(`Page crashed. Restarting ${this.min.botId} WhatsApp native provider.`);
                                if (!client.pupPage.isClosed()) {
                                    client.pupPage.close();
                                }
                                await (createClient.bind(this))(null);
                            }).bind(this));
                            botlib_1.GBLog.info(`WhatsApp QR Code authenticated for ${this.botId}.`);
                        });
                        client.pupBrowser.on('disconnected', (async () => {
                            botlib_1.GBLog.info(`Browser crashed. Restarting ${this.min.botId} WhatsApp native provider.`);
                            await (createClient.bind(this))(null);
                        }).bind(this));
                    };
                    await (createClient.bind(this))(this.browserWSEndpoint);
                    setUrl = false;
                }
                break;
            case 'chatapi':
                options = {
                    method: 'POST',
                    url: urlJoin(this.whatsappServiceUrl, 'webhook'),
                    timeout: 10000,
                    qs: {
                        token: this.whatsappServiceKey,
                        webhookUrl: `${app_1.GBServer.globals.publicAddress}/webhooks/whatsapp/${this.botId}`,
                        set: true
                    },
                    headers: {
                        'cache-control': 'no-cache'
                    }
                };
                break;
            case 'maytapi':
                let phoneId = this.whatsappServiceNumber.split(';')[0];
                let productId = this.whatsappServiceNumber.split(';')[1];
                let url = `${this.INSTANCE_URL}/${productId}/${phoneId}/config`;
                WhatsappDirectLine.phones[phoneId] = this.botId;
                options = {
                    url: url,
                    method: 'POST',
                    body: {
                        webhook: `${app_1.GBServer.globals.publicAddress}/webhooks/whatsapp/${this.botId}`,
                        "ack_delivery": false
                    },
                    headers: {
                        'x-maytapi-key': this.whatsappServiceKey,
                        'Content-Type': 'application/json',
                    },
                    json: true,
                };
                break;
        }
        if (setUrl && options) {
            const express = require('express');
            app_1.GBServer.globals.server.use(`/audios`, express.static('work'));
            if (options) {
                try {
                    await request.post(options);
                }
                catch (error) {
                    botlib_1.GBLog.error(`Error initializing 3rd party Whatsapp provider(1) ${error.message}`);
                }
            }
        }
    }
    async resetConversationId(botId, number, group = '') {
        WhatsappDirectLine.conversationIds[botId + number + group] = undefined;
    }
    async check() {
        botlib_1.GBLog.info(`GBWhatsapp: Checking server...`);
        const options = {
            url: urlJoin(this.whatsappServiceUrl, 'status') + `?token=${this.min.instance.whatsappServiceKey}`,
            method: 'GET'
        };
        const res = await request(options);
        const json = JSON.parse(res);
        return json.accountStatus === 'authenticated';
    }
    static providerFromRequest(req) {
        return req.body.messages ? 'chatapi' :
            req.body.message ? 'maytapi' : 'GeneralBots';
    }
    async received(req, res) {
        const provider = WhatsappDirectLine.providerFromRequest(req);
        let message, from, fromName, text;
        let group = "";
        let answerText = null;
        let attachments = null;
        switch (provider) {
            case 'GeneralBots':
                message = req;
                text = message.body;
                from = message.from.split('@')[0];
                fromName = message._data.notifyName;
                if (message.hasMedia) {
                    const base64Image = await message.downloadMedia();
                    attachments = [];
                    attachments.push({
                        name: 'uploaded.png',
                        contentType: base64Image.mimetype,
                        contentUrl: `data:${base64Image.mimetype};base64,${base64Image.data}`
                    });
                }
                break;
            case 'chatapi':
                message = req.body.messages[0];
                text = message.body;
                from = req.body.messages[0].author.split('@')[0];
                fromName = req.body.messages[0].senderName;
                if (message.type !== 'chat') {
                    attachments = [];
                    attachments.push({
                        name: 'uploaded',
                        contentType: 'application/octet-stream',
                        contentUrl: message.body
                    });
                }
                if (req.body.messages[0].fromMe) {
                    res.end();
                    return; // Exit here.
                }
                break;
            case 'maytapi':
                message = req.body.message;
                text = message.text;
                from = req.body.user.phone;
                fromName = req.body.user.name;
                if (req.body.message.fromMe) {
                    res.end();
                    return; // Exit here.
                }
                break;
        }
        text = text.replace(/\@\d+ /gi, '');
        botlib_1.GBLog.info(`GBWhatsapp: RCV ${from}(${fromName}): ${text})`);
        if (provider === "chatapi") {
            if (message.chatName.charAt(0) !== '+') {
                group = message.chatName;
                let botGroupName = this.min.core.getParam(this.min.instance, 'WhatsApp Group Name', null);
                let botShortcuts = this.min.core.getParam(this.min.instance, 'WhatsApp Group Shortcuts', null);
                if (!botShortcuts) {
                    botShortcuts = new Array();
                }
                else {
                    botShortcuts = botShortcuts.split(' ');
                }
                const parts = text.split(' ');
                // Bot name must be specified on config.
                if (botGroupName === group) {
                    // Shortcut has been mentioned?
                    let found = false;
                    parts.forEach(e1 => {
                        botShortcuts.forEach(e2 => {
                            if (e1 === e2 && !found) {
                                found = true;
                                text = text.replace(e2, '');
                            }
                        });
                        // Verify if it is a group cache answer.
                        const questions = this.min['groupCache'];
                        if (questions && questions.length > 0) {
                            questions.forEach(q => {
                                if (q.content === e1 && !found) {
                                    const answer = this.min.kbService['getAnswerById'](this.min.instance.instanceId, q.answerId);
                                    answerText = answer.content;
                                }
                            });
                        }
                        // Ignore group messages without the mention to Bot.
                        let smsServiceNumber = this.min.core.getParam(this.min.instance, 'whatsappServiceNumber', null);
                        if (smsServiceNumber && !answerText) {
                            smsServiceNumber = smsServiceNumber.replace('+', '');
                            if (!message.body.startsWith('@' + smsServiceNumber)) {
                                return;
                            }
                        }
                    });
                }
            }
        }
        const botId = this.min.instance.botId;
        const state = WhatsappDirectLine.state[botId + from];
        if (state) {
            WhatsappDirectLine.state[botId + from] = null;
            await state.promise(null, message.text);
            return; // Exit here.
        }
        ;
        // Processes .gbapp message interception.
        await pragmatismo_io_framework_1.CollectionUtil.asyncForEach(this.min.appPackages, async (e) => {
            await e.onExchangeData(this.min, 'whatsappMessage', { from, fromName });
        });
        const sec = new SecService_1.SecService();
        const user = await sec.ensureUser(this.min.instance.instanceId, from, fromName, '', 'whatsapp', fromName, null);
        const locale = user.locale ? user.locale : 'pt';
        if (answerText) {
            await this.sendToDeviceEx(user.userSystemId, answerText, locale, null);
            return; // Exit here.
        }
        if (message.type === 'ptt') {
            if (process.env.AUDIO_DISABLED !== 'true') {
                const options = {
                    url: provider ? message.body : message.text,
                    method: 'GET',
                    encoding: 'binary'
                };
                const res = await request(options);
                const buf = Buffer.from(res, 'binary');
                text = await GBConversationalService_1.GBConversationalService.getTextFromAudioBuffer(this.min.instance.speechKey, this.min.instance.cloudLocation, buf, locale);
            }
            else {
                await this.sendToDevice(user.userSystemId, `No momento estou apenas conseguindo ler mensagens de texto.`, null);
            }
        }
        const conversationId = WhatsappDirectLine.conversationIds[botId + from + group];
        const client = await this.directLineClient;
        WhatsappDirectLine.lastMessage[botId + from] = message;
        // Check if this message is from a Human Agent itself.
        if (user.agentMode === 'self') {
            // Check if there is someone being handled by this Human Agent.
            const manualUser = await sec.getUserFromAgentSystemId(from);
            if (manualUser === null) {
                await sec.updateHumanAgent(from, this.min.instance.instanceId, null);
            }
            else {
                const agent = await sec.getUserFromSystemId(user.agentSystemId);
                const cmd = '/reply ';
                if (text.startsWith(cmd)) {
                    const filename = text.substr(cmd.length);
                    const message = await this.min.kbService.getAnswerTextByMediaName(this.min.instance.instanceId, filename);
                    if (message === null) {
                        await this.sendToDeviceEx(user.userSystemId, `File ${filename} not found in any .gbkb published. Check the name or publish again the associated .gbkb.`, locale, null);
                    }
                    else {
                        await this.min.conversationalService.sendMarkdownToMobile(this.min, null, user.userSystemId, message);
                    }
                }
                else if (text === '/qt') {
                    // TODO: Transfers only in pt-br for now.
                    await this.sendToDeviceEx(manualUser.userSystemId, strings_1.Messages[this.locale].notify_end_transfer(this.min.instance.botId), locale, null);
                    if (user.agentSystemId.charAt(2) === ":") { // Agent is from Teams.
                        await this.min.conversationalService['sendOnConversation'](this.min, agent, strings_1.Messages[this.locale].notify_end_transfer(this.min.instance.botId));
                    }
                    else {
                        await this.sendToDeviceEx(user.agentSystemId, strings_1.Messages[this.locale].notify_end_transfer(this.min.instance.botId), locale, null);
                    }
                    await sec.updateHumanAgent(manualUser.userSystemId, this.min.instance.instanceId, null);
                    await sec.updateHumanAgent(user.agentSystemId, this.min.instance.instanceId, null);
                }
                else {
                    botlib_1.GBLog.info(`HUMAN AGENT (${manualUser.agentSystemId}) TO USER ${manualUser.userSystemId}: ${text}`);
                    await this.sendToDeviceEx(manualUser.userSystemId, `AGENTE: *${text}*`, locale, null);
                }
            }
        }
        else if (user.agentMode === 'human') {
            const agent = await sec.getUserFromSystemId(user.agentSystemId);
            if (text === '/t') {
                await this.sendToDeviceEx(user.userSystemId, `Você já está sendo atendido por ${agent.userSystemId}.`, locale, null);
            }
            else if (text === '/qt' || text === 'Sair' || text === 'Fechar') {
                // TODO: Transfers only in pt-br for now.
                await this.endTransfer(from, locale, user, agent, sec);
            }
            else {
                botlib_1.GBLog.info(`USER (${from}) TO AGENT ${agent.userSystemId}: ${text}`);
                if (user.agentSystemId.charAt(2) === ":" || agent.userSystemId.indexOf("@") > -1) { // Agent is from Teams or Google Chat.
                    await this.min.conversationalService['sendOnConversation'](this.min, agent, text);
                }
                else {
                    await this.sendToDeviceEx(user.agentSystemId, `Bot: ${this.min.instance.botId}\n${from}: ${text}`, locale, null);
                }
            }
        }
        else if (user.agentMode === 'bot' || user.agentMode === null || user.agentMode === undefined) {
            if (WhatsappDirectLine.conversationIds[botId + from + group] === undefined) {
                botlib_1.GBLog.info(`GBWhatsapp: Starting new conversation on Bot.`);
                const response = await client.Conversations.Conversations_StartConversation();
                const generatedConversationId = response.obj.conversationId;
                WhatsappDirectLine.conversationIds[botId + from + group] = generatedConversationId;
                if (provider === "GeneralBots") {
                    WhatsappDirectLine.chatIds[generatedConversationId] = message.from;
                }
                WhatsappDirectLine.mobiles[generatedConversationId] = from;
                WhatsappDirectLine.usernames[from] = fromName;
                WhatsappDirectLine.chatIds[generatedConversationId] = message.chatId;
                this.pollMessages(client, generatedConversationId, from, fromName);
                this.inputMessage(client, generatedConversationId, text, from, fromName, group, attachments);
            }
            else {
                this.inputMessage(client, conversationId, text, from, fromName, group, attachments);
            }
        }
        else {
            botlib_1.GBLog.warn(`Inconsistencty found: Invalid agentMode on User Table: ${user.agentMode}`);
        }
        if (res) {
            res.end();
        }
    }
    async endTransfer(id, locale, user, agent, sec) {
        await this.sendToDeviceEx(id, strings_1.Messages[this.locale].notify_end_transfer(this.min.instance.botId), locale, null);
        if (user.agentSystemId.charAt(2) === ":") { // Agent is from Teams.
            await this.min.conversationalService['sendOnConversation'](this.min, agent, strings_1.Messages[this.locale].notify_end_transfer(this.min.instance.botId));
        }
        else {
            await this.sendToDeviceEx(user.agentSystemId, strings_1.Messages[this.locale].notify_end_transfer(this.min.instance.botId), locale, null);
        }
        await sec.updateHumanAgent(id, this.min.instance.instanceId, null);
    }
    inputMessage(client, conversationId, text, from, fromName, group, attachments) {
        return client.Conversations.Conversations_PostActivity({
            conversationId: conversationId,
            activity: {
                textFormat: 'plain',
                text: text,
                type: 'message',
                mobile: from,
                group: group,
                attachments: attachments,
                from: {
                    id: from,
                    name: fromName
                },
                replyToId: from
            }
        });
    }
    pollMessages(client, conversationId, from, fromName) {
        botlib_1.GBLog.info(`GBWhatsapp: Starting message polling(${from}, ${conversationId}).`);
        let watermark;
        const worker = async () => {
            try {
                const response = await client.Conversations.Conversations_GetActivities({
                    conversationId: conversationId,
                    watermark: watermark
                });
                watermark = response.obj.watermark;
                await this.printMessages(response.obj.activities, conversationId, from, fromName);
            }
            catch (err) {
                botlib_1.GBLog.error(`Error calling printMessages on Whatsapp channel ${err.data === undefined ? err : err.data}`);
            }
        };
        setInterval(worker, this.pollInterval);
    }
    async printMessages(activities, conversationId, from, fromName) {
        if (activities && activities.length) {
            // Ignore own messages.
            activities = activities.filter(m => m.from.id === this.botId && m.type === 'message');
            if (activities.length) {
                // Print other messages.
                await WhatsappDirectLine.asyncForEach(activities, async (activity) => {
                    await this.printMessage(activity, conversationId, from, fromName);
                });
            }
        }
    }
    async printMessage(activity, conversationId, from, fromName) {
        let output = '';
        if (activity.text) {
            botlib_1.GBLog.info(`GBWhatsapp: SND ${from}(${fromName}): ${activity.text}`);
            output = activity.text;
        }
        if (activity.attachments) {
            activity.attachments.forEach(attachment => {
                switch (attachment.contentType) {
                    case 'application/vnd.microsoft.card.hero':
                        output += `\n${this.renderHeroCard(attachment)}`;
                        break;
                    case 'image/png':
                        botlib_1.GBLog.info(`Opening the requested image ${attachment.contentUrl}`);
                        output += `\n${attachment.contentUrl}`;
                        break;
                    default:
                        botlib_1.GBLog.info(`Unknown content type: ${attachment.contentType}`);
                }
            });
        }
        await this.sendToDevice(from, output, conversationId);
    }
    renderHeroCard(attachment) {
        return `${attachment.content.title} - ${attachment.content.text}`;
    }
    async sendFileToDevice(to, url, filename, caption, chatId) {
        let options;
        switch (this.provider) {
            case 'GeneralBots':
                const attachment = await MessageMedia.fromUrl(url);
                await this.customClient.sendMessage(to + '@c.us', attachment, { caption: caption });
                break;
            case 'chatapi':
                options = {
                    method: 'POST',
                    url: urlJoin(this.whatsappServiceUrl, 'sendFile'),
                    qs: {
                        token: this.whatsappServiceKey
                    },
                    json: true,
                    body: {
                        phone: to,
                        body: url,
                        filename: filename,
                        caption: caption
                    },
                    headers: {
                        'cache-control': 'no-cache'
                    }
                };
                break;
            case 'maytapi':
                let contents = 0;
                let body = {
                    to_number: to,
                    type: "media",
                    message: url,
                    text: caption
                };
                let phoneId = this.whatsappServiceNumber.split(';')[0];
                let productId = this.whatsappServiceNumber.split(';')[1];
                options = {
                    url: `${this.INSTANCE_URL}/${productId}/${phoneId}/sendMessage`,
                    method: 'post',
                    json: true,
                    body,
                    headers: {
                        'Content-Type': 'application/json',
                        'x-maytapi-key': this.whatsappServiceKey,
                    },
                };
                break;
        }
        if (options) {
            try {
                // tslint:disable-next-line: await-promise
                const result = await request.post(options);
                botlib_1.GBLog.info(`File ${url} sent to ${to}: ${result}`);
            }
            catch (error) {
                botlib_1.GBLog.error(`Error sending file to Whatsapp provider ${error.message}`);
            }
        }
    }
    async sendAudioToDevice(to, url, chatId) {
        let options;
        switch (this.provider) {
            case 'GeneralBots':
                const attachment = MessageMedia.fromUrl(url);
                await this.customClient.sendMessage(to, attachment);
                break;
            case 'chatapi':
                options = {
                    method: 'POST',
                    url: urlJoin(this.whatsappServiceUrl, 'sendPTT'),
                    qs: {
                        token: this.whatsappServiceKey,
                        phone: chatId ? null : to,
                        chatId: chatId,
                        body: url
                    },
                    headers: {
                        'cache-control': 'no-cache'
                    }
                };
                break;
            case 'maytapi':
                options = {}; // TODO: Code Maytapi.
                break;
        }
        if (options) {
            try {
                const result = await request.post(options);
                botlib_1.GBLog.info(`Audio ${url} sent to ${to}: ${result}`);
            }
            catch (error) {
                botlib_1.GBLog.error(`Error sending audio message to Whatsapp provider ${error.message}`);
            }
        }
    }
    async sendTextAsAudioToDevice(to, msg, chatId) {
        const url = await GBConversationalService_1.GBConversationalService.getAudioBufferFromText(msg);
        await this.sendFileToDevice(to, url, 'Audio', msg, chatId);
    }
    async sendToDevice(to, msg, conversationId) {
        const cmd = '/audio ';
        let chatId = WhatsappDirectLine.chatIds[conversationId];
        if (msg.startsWith(cmd)) {
            msg = msg.substr(cmd.length);
            return await this.sendTextAsAudioToDevice(to, msg, chatId);
        }
        else {
            let options;
            switch (this.provider) {
                case 'GeneralBots':
                    this.customClient.sendMessage(to + '@c.us', msg);
                    break;
                case 'chatapi':
                    options = {
                        method: 'POST',
                        url: urlJoin(this.whatsappServiceUrl, 'message'),
                        qs: {
                            token: this.whatsappServiceKey,
                            phone: chatId ? null : to,
                            chatId: chatId,
                            body: msg
                        },
                        headers: {
                            'cache-control': 'no-cache'
                        }
                    };
                    break;
                case 'maytapi':
                    let phoneId = this.whatsappServiceNumber.split(';')[0];
                    let productId = this.whatsappServiceNumber.split(';')[1];
                    let url = `${this.INSTANCE_URL}/${productId}/${phoneId}/sendMessage`;
                    options = {
                        url: url,
                        method: 'post',
                        json: true,
                        body: { type: 'text', message: msg, to_number: to },
                        headers: {
                            'Content-Type': 'application/json',
                            'x-maytapi-key': this.whatsappServiceKey,
                        },
                    };
                    break;
            }
            if (options) {
                try {
                    botlib_1.GBLog.info(`Message [${msg}] is being sent to ${to}...`);
                    await request.post(options);
                }
                catch (error) {
                    botlib_1.GBLog.error(`Error sending message to Whatsapp provider ${error.message}`);
                    // TODO: Handle Error: socket hang up and retry.
                }
            }
        }
    }
    async sendToDeviceEx(to, text, locale, conversationId) {
        text = await this.min.conversationalService.translate(this.min, text, locale);
        await this.sendToDevice(to, text, conversationId);
    }
    async WhatsAppCallback(req, res) {
        try {
            if (req.body && req.body.webhook) {
                res.status(200);
                res.end();
                return;
            }
            let provider = GBMinService_1.GBMinService.isChatAPI(req, res);
            let id;
            let senderName;
            let botId;
            let text;
            switch (provider) {
                case "GeneralBots":
                    id = req.from.split('@')[0];
                    senderName = req._data.notifyName;
                    text = req.body;
                    break;
                case "chatapi":
                    if (req.body.ack) {
                        res.status(200);
                        res.end();
                        return;
                    }
                    if (req.body.messages[0].fromMe) {
                        res.end();
                        return; // Exit here.
                    }
                    id = req.body.messages[0].author.split('@')[0];
                    senderName = req.body.messages[0].senderName;
                    text = req.body.messages[0].body;
                    botId = req.params.botId;
                    if (botId === '[default]' || botId === undefined) {
                        botId = GBConfigService_1.GBConfigService.get('BOT_ID');
                    }
                    break;
                case "maytapi":
                    if (req.body.type !== 'message') {
                        res.status(200);
                        res.end();
                        return;
                    }
                    if (req.body.message.fromMe) {
                        res.end();
                        return; // Exit here.
                    }
                    id = req.body.user.phone;
                    senderName = req.body.user.name;
                    text = req.body.message.text;
                    botId = WhatsappDirectLine.phones[req.body.phoneId];
                    break;
            }
            const sec = new SecService_1.SecService();
            let user = await sec.getUserFromSystemId(id);
            botlib_1.GBLog.info(`A WhatsApp mobile requested instance for: ${botId}.`);
            let urlMin = app_1.GBServer.globals.minInstances.filter(p => p.instance.botId === botId)[0];
            const botNumber = urlMin ? urlMin.core.getParam(urlMin.instance, 'Bot Number', null) : null;
            let activeMin;
            // Processes group behaviour.
            text = text.replace(/\@\d+ /gi, '');
            if (provider === "chatapi") {
                // Ensures that the bot group is the active bot for the user (like switching).
                const message = req.body.messages[0];
                if (message.chatName.charAt(0) !== '+') {
                    const group = message.chatName;
                    const botGroup = await this.min.core.loadInstanceByBotId(group);
                    if (botGroup && user.instanceId !== botGroup.instanceId) {
                        await sec.updateUserInstance(id, botGroup.instanceId);
                    }
                    if (botGroup) {
                        activeMin = app_1.GBServer.globals.minInstances.filter(p => p.instance.instanceId === botGroup.instanceId)[0];
                        await activeMin.whatsAppDirectLine.received(req, res);
                        return; // EXIT HERE.
                    }
                }
            }
            // Detects if the welcome message is enabled.
            if (process.env.WHATSAPP_WELCOME_DISABLED !== 'true') {
                // Tries to find if user wants to switch bots.
                let toSwitchMin = app_1.GBServer.globals.minInstances.filter(p => p.instance.botId.toLowerCase() === text.toLowerCase())[0];
                if (!toSwitchMin) {
                    toSwitchMin = app_1.GBServer.globals.minInstances.filter(p => p.instance.activationCode ? p.instance.activationCode.toLowerCase() === text.toLowerCase() : false)[0];
                }
                // If bot has a fixed Find active bot instance.
                activeMin = botNumber ? urlMin :
                    toSwitchMin ? toSwitchMin : app_1.GBServer.globals.minBoot;
                // If it is the first time for the user, tries to auto-execute
                // start dialog if any is specified in Config.xlsx.
                if (user === null || user.hearOnDialog) {
                    user = await sec.ensureUser(activeMin.instance.instanceId, id, senderName, '', 'whatsapp', senderName, null);
                    const startDialog = user.hearOnDialog ?
                        user.hearOnDialog :
                        activeMin.core.getParam(activeMin.instance, 'Start Dialog', null);
                    if (startDialog) {
                        botlib_1.GBLog.info(`Calling /start to Auto start ${startDialog} for ${activeMin.instance.instanceId}...`);
                        if (provider === "chatapi") {
                            req.body.messages[0].body = `/start`;
                        }
                        else if (provider === "maytapi") {
                            req.body.message = `/start`;
                        }
                        else {
                            req.body = `/start`;
                        }
                        // Resets HEAR ON DIALOG value to none and passes
                        // current dialog to the direct line.
                        await sec.updateUserHearOnDialog(user.userId, null);
                        await activeMin.whatsAppDirectLine.received(req, res);
                    }
                    else {
                        await activeMin.whatsAppDirectLine.sendToDevice(id, `Olá! Seja bem-vinda(o)!\nMe chamo ${activeMin.instance.title}. Como posso ajudar? Pode me falar que eu te ouço, me manda um aúdio.`, null);
                        if (res) {
                            res.end();
                        }
                    }
                }
                else {
                    // User wants to switch bots.
                    if (toSwitchMin !== undefined) {
                        // So gets the new bot instance information and prepares to
                        // auto start dialog if any is specified.
                        const instance = await this.min.core.loadInstanceByBotId(activeMin.botId);
                        await sec.updateUserInstance(id, instance.instanceId);
                        await activeMin.whatsAppDirectLine.resetConversationId(activeMin.botId, id, '');
                        const startDialog = activeMin.core.getParam(activeMin.instance, 'Start Dialog', null);
                        if (startDialog) {
                            botlib_1.GBLog.info(`Calling /start for Auto start : ${startDialog} for ${activeMin.instance.botId}...`);
                            if (provider === "chatapi") {
                                req.body.messages[0].body = `/start`;
                            }
                            else if (provider === "maytapi") {
                                req.body.message = `/start`;
                            }
                            else {
                                req.body = `/start`;
                            }
                            await activeMin.whatsAppDirectLine.received(req, res);
                        }
                        else {
                            await activeMin.whatsAppDirectLine.sendToDevice(id, `Agora falando com ${activeMin.instance.title}...`, null);
                        }
                        if (res) {
                            res.end();
                        }
                    }
                    else {
                        activeMin = app_1.GBServer.globals.minInstances.filter(p => p.instance.instanceId === user.instanceId)[0];
                        if (activeMin === undefined) {
                            activeMin = app_1.GBServer.globals.minBoot;
                            await activeMin.whatsAppDirectLine.sendToDevice(id, `O outro Bot que você estava falando(${user.instanceId}), não está mais disponível. Agora você está falando comigo, ${activeMin.instance.title}...`);
                        }
                        await activeMin.whatsAppDirectLine.received(req, res);
                    }
                }
            }
            else {
                let minInstance = app_1.GBServer.globals.minInstances.filter(p => p.instance.botId.toLowerCase() === botId.toLowerCase())[0];
                // Just pass the message to the receiver.
                await minInstance.whatsAppDirectLine.received(req, res);
            }
        }
        catch (error) {
            botlib_1.GBLog.error(`Error on Whatsapp callback: ${error.data ? error.data : error} ${error.stack}`);
        }
    }
}
exports.WhatsappDirectLine = WhatsappDirectLine;
WhatsappDirectLine.conversationIds = {};
WhatsappDirectLine.mobiles = {};
WhatsappDirectLine.phones = {};
WhatsappDirectLine.chatIds = {};
WhatsappDirectLine.usernames = {};
WhatsappDirectLine.state = {}; // 2: Waiting, 3: MessageArrived.
WhatsappDirectLine.lastMessage = {}; // 2: Waiting, 3: MessageArrived.
//# sourceMappingURL=WhatsappDirectLine.js.map