Genesys Cloud integrazione con ChatBot esterno

Con l’introduzione in Genesys Cloud delle WebChat APIs è possibile integrare un proprio ChatBot esterno con la piattaforma Genesys offrendo un’interessante user experience per gli utenti. Sfruttando le WebChat APIs si può ad esempio sviluppare un proprio ChatBot che accoglie gli utenti con la possibilità di switching tra ChatBot e Agente reale su Genesys Cloud, rendendo anche disponibile all’agente l’history della conversazione che l’utente ha avuto con il BOT prima di essere trasferito.

Qui andremo a sperimentare questo tipo di integrazione realizzando un nostro ChatBot con l’aiuto di Microsoft Bot Framework Emulator e codice Javascript disponibile in rete. Ovviamente avremo bisogno di un accesso alla piattaforma Genesys Cloud, per configurare una coda sulla quale registrare un agente che dovrà interagire via chat con l’utente una volta che quest’ultimo sarà trasferito dal nostro ChatBot alla coda.

La figura che segue rappresenta (in modo molto semplificato) lo schema architetturale.

genesys-cloud-ChatBot

Cosa ci serve

Per realizzare una semplice demo dello scenario appena descritto ci occorrono:

  1. Un accesso valido a Genesys Cloud, con le WebCaht APIs abilitate
  2. Una coda sulla quale trasferire il contatto
  3. Microsoft Bot Framework Emulator
  4. Microsoft Bot Framework SDK
  5. Node.js
  6. Il codice javascript di esempio per il ChatBot

Configurazione Genesys Cloud

Per prima cosa assicuriamoci di avere tutto predisposto sulla piattaforma Genesys Cloud, quindi API accesso abilitato, una coda ed un agente disponibile sulla coda. Nel mio caso di test ho la coda ChatAssistant configurata ed il mio Utente in coda pronto a rispondere.

genesys-cloud-queue

genesys-cloud-agent

Microsoft Bot Framework Emulator

Con il Bot Framework SDK di Microsoft possiamo costruire il nostro ChatBot e tramite il Bot Framework Emulator lo possiamo testare localmente sul nostro laptop.
Io ho utilizzato l’SDK per costruire un bot in JavaScript seguendo la documentazione Microsoft. Ho predisposto l’ambiente installando i vari moduli per Node.js e poi generato un echo bot.

	PS C:\temp\simplebot> yo botbuilder      

	Welcome to the Microsoft Bot Builder generator v4.20.0.

	Detailed documentation can be found at https://aka.ms/botbuilder-generator

	? What's the name of your bot? my-chat-bot
	? What will your bot do? Demonstrate the core capabilities of the Microsoft Bot Framework
	? What programming language do you want to use? JavaScript     
	? Which template would you like to start with? Echo Bot - https://aka.ms/bot-template-echo
	? Looking good.  Shall I go ahead and create your new bot? Yes
	simplebot
	my-chat-bot
	Your bot should be in a directory named my-chat-bot
	I'll automatically create this folder.
		create my-chat-bot\package.json
		create my-chat-bot\.gitignore
		create my-chat-bot\.env
		create my-chat-bot\.eslintrc.js
		create my-chat-bot\README.md
		create my-chat-bot\deploymentTemplates\deployUseExistResourceGroup\parameters-for-template-AzureBot-with-rg.json
		create my-chat-bot\deploymentTemplates\deployUseExistResourceGroup\parameters-for-template-BotApp-with-rg.json
		create my-chat-bot\deploymentTemplates\deployUseExistResourceGroup\readme.md
		create my-chat-bot\deploymentTemplates\deployUseExistResourceGroup\template-AzureBot-with-rg.json
		create my-chat-bot\deploymentTemplates\deployUseExistResourceGroup\template-BotApp-with-rg.json
		create my-chat-bot\deploymentTemplates\deployWithNewResourceGroup\parameters-for-template-AzureBot-new-rg.json
		create my-chat-bot\deploymentTemplates\deployWithNewResourceGroup\parameters-for-template-BotApp-new-rg.json
		create my-chat-bot\deploymentTemplates\deployWithNewResourceGroup\readme.md
		create my-chat-bot\deploymentTemplates\deployWithNewResourceGroup\template-AzureBot-new-rg.json
		create my-chat-bot\deploymentTemplates\deployWithNewResourceGroup\template-BotApp-new-rg.json
		create my-chat-bot\index.js
		create my-chat-bot\bot.js

	I'm all done. Running npm install for you to install the required dependencies. If this fails, try running the command yourself.

Bene, ora abbiamo un echo bot pronto all’uso che possiamo lanciare sull’emulatore, ma in realtà a noi interessa modificarlo con le parti necessarie all’integrazione con Genesys Cloud tramite WebChat Guest API. Il codice sorgente per integrare il bot con Genesys è disponibile gratuitamente su repository BitBucket e per il nostro scopo ci basta prendere i files bot.js, index.js e purecloud-chat.js riadattandoli un minimo. Vediamo come.

Nel file bot.js eliminiamo tutti i riferimenti a Watson.

Inoltre modifichiamo il file index.js in questo modo.

	// Copyright (c) Microsoft Corporation. All rights reserved.
	// Licensed under the MIT License.

	const path = require('path');

	const dotenv = require('dotenv');
	// Import required bot configuration.
	const ENV_FILE = path.join(__dirname, '.env');
	dotenv.config({ path: ENV_FILE });

	const restify = require('restify');

	// Import required bot services.
	// See https://aka.ms/bot-services to learn more about the different parts of a bot.
	const {
		CloudAdapter,
		ConversationState,
		UserState,
		MemoryStorage,
		ConfigurationServiceClientCredentialFactory,
		createBotFrameworkAuthenticationFromConfiguration
	} = require('botbuilder');

	// This bot's main dialog.
	const { EchoBot } = require('./bot');

	// Create HTTP server
	const server = restify.createServer();
	server.use(restify.plugins.bodyParser());

	server.listen(process.env.port || process.env.PORT || 3978, () => {
		console.log(`\n${ server.name } listening to ${ server.url }`);
		console.log('\nGet Bot Framework Emulator: https://aka.ms/botframework-emulator');
		console.log('\nTo talk to your bot, open the emulator select "Open Bot"');
	});

	const credentialsFactory = new ConfigurationServiceClientCredentialFactory({
		MicrosoftAppId: process.env.MicrosoftAppId,
		MicrosoftAppPassword: process.env.MicrosoftAppPassword,
		MicrosoftAppType: process.env.MicrosoftAppType,
		MicrosoftAppTenantId: process.env.MicrosoftAppTenantId
	});

	const botFrameworkAuthentication = createBotFrameworkAuthenticationFromConfiguration(null, credentialsFactory);

	// Create adapter.
	// See https://aka.ms/about-bot-adapter to learn more about adapters.
	const adapter = new CloudAdapter(botFrameworkAuthentication);

	// Catch-all for errors.
	const onTurnErrorHandler = async (context, error) => {
		// This check writes out errors to console log .vs. app insights.
		// NOTE: In production environment, you should consider logging this to Azure
		//       application insights.
		console.error(`\n [onTurnError] unhandled error: ${ error }`);
		// Clear out state
		await conversationState.clear(context);
		// Save state changes.
		await conversationState.saveChanges(context);
		// Send a trace activity, which will be displayed in Bot Framework Emulator
		await context.sendTraceActivity(
			'OnTurnError Trace',
			`${ error }`,
			'https://www.botframework.com/schemas/error',
			'TurnError'
		);

		// Send a message to the user
		await context.sendActivity('The bot encountered an error or bug.');
		await context.sendActivity('To continue to run this bot, please fix the bot source code.');
	};

	// Set the onTurnError for the singleton CloudAdapter.
	adapter.onTurnError = onTurnErrorHandler;

	// Define a state store for your bot. See https://aka.ms/about-bot-state to learn more about using MemoryStorage.
	// A bot requires a state store to persist the dialog and user state between messages.
	let conversationState;
	let userState;

	// For local development, in-memory storage is used.
	// CAUTION: The Memory Storage used here is for local bot debugging only. When the bot
	// is restarted, anything stored in memory will be gone.
	const memoryStorage = new MemoryStorage();
	conversationState = new ConversationState(memoryStorage);
	userState = new UserState(memoryStorage);

	// Create the main dialog.
	const myBot = new EchoBot(conversationState, userState);

	// Listen for incoming requests.
	server.post('/api/messages', async (req, res) => {
		// Route received a request to adapter for processing
		await adapter.process(req, res, (context) => myBot.onTurn(adapter, context));
	});

Modifichiamo il file purecloud-chat.js inserendo i dati richiesti per l’integrazione Genesys.

	const organizationId = "";                    // organizationId
	const deploymentId = "";                      // deploymentId from PureCloud org definition
	const queueName = "ChatAssistant";            // queueName where Chat will be routed
	const env = '';                               // PureCloud environment (mypurecloud.com / mypurecloud.ie / mypurecloud.de)
  • organizationId è l’identificativo della subscription Genesys Cloud
  • deploymentId è il Deployment Key del Contact Center Integration Widget
  • queueName è la coda sulla quale trasferire la chat
  • env è la region PureCloud su cui è attiva la propria subscription (nel mio caso mypurecloud.de)

Nello stesso file modifichiamo la funzione startChat in questo modo.

	function startChat(_adapter, _context, _conversationState) {

		let myBody = {
			"organizationId": organizationId,
			"deploymentId": deploymentId,
			"routingTarget": {
				"targetType": "QUEUE",
				"targetAddress": queueName
			},
			"memberInfo": {
				"displayName": "Steve Jobs",
				"profileImageUrl": "https://banner2.kisspng.com/20181201/xes/kisspng-call-centre-customer-service-clip-art-computer-ico-homepage-vacuum-5c02fa4fe698b1.3075985415436990239445.jpg",
				"customFields": {
					"firstName": "Steve",
					"lastName": "Jobs"
				}
			}
		};

		let options = {
			url: 'https://api.' + env + '/api/v2/webchat/guest/conversations',
			method: 'POST',
			headers: {
				'Content-Type': 'application/json'
			},
			body: JSON.stringify(myBody)
		};

		request(options, function (error, response, body) {
			if (!error && response.statusCode == 200) {
				var info = JSON.parse(body);

				webSocket = new WebSocket(info.eventStreamUri);
				webSocket.on('open', function () {
					//Connection is open. Start the subscription(s)
					console.log('WebSocket opened');
				});

				webSocket.on('message', function (message) {
					var data = JSON.parse(message);

					// we do not care about channel.metadata informations. Ignore them
					if (data.topicName == 'channel.metadata') {
						return;
					}

					try {
						if (!_conversationState.purecloud || !_conversationState.purecloud.agentId) {
							// do only once after chat session initated. Save participant data and other required informations
							_conversationState.purecloud = {};
							_conversationState.purecloud.conversationId = data.eventBody.conversation.id;
							if (data.eventBody.hasOwnProperty('member')) {
								_conversationState.purecloud.agentId = data.eventBody.member.id;
								console.log('agentId', _conversationState.purecloud.agentId);
							}
							_conversationState.purecloud.botConversationId = _context.conversation.id;
							_conversationState.purecloud.chatId = info.jwt;

							console.log('conversationId', _conversationState.purecloud.conversationId);

							// Send history
							sendMessageToPureCloud(buildHistory(_conversationState.history), _conversationState.purecloud);
						}

						// new message from purecloud received
						if (data.metadata.type == 'message') {

							// We do not want to display message from the bot again (echo)
							// display only messages where senderId is different than current botId
							if (data.eventBody.sender.id != _conversationState.purecloud.agentId) {
								console.log('msg from pc', data.eventBody.body);
								sendMessageToBot(_adapter, _context, data.eventBody.body);
							}
							// member-change event (detect DISCONNECT event)
						} else if (data.metadata.type == 'member-change' && data.eventBody.member.id == _conversationState.purecloud.agentId && data.eventBody.member.state == 'DISCONNECTED') {
							console.log('# chat disconnected, clear bot session');
							_conversationState.purecloud = undefined;
							sendMessageToBot(_adapter, _context, '[purecloud disconnected]');
						}
					} catch (error) {
						console.log(error);
					}
				});
			}
		});
	};

Infine modifichiamo la funzione sendMessageToBot in questo modo.

	// send message to the bot
	async function sendMessageToBot(_adapter, _context, _msg) {
		await _adapter.continueConversationAsync( "fakeid", _context, async (proactiveTurnContext) => {
			await proactiveTurnContext.sendActivity(_msg);
		});
	}

Testiamo lo scenario

A questo punto possiamo testare il ChatBot locale e l’integrazione con Genesys Cloud. Per prima cosa avviamo il ChatBot appena creato.

	PS C:\temp\simplebot\my-chat-bot> npm start

	> my-chat-bot@1.0.0 start
	> node ./index.js

	(node:11544) [DEP0111] DeprecationWarning: Access to process.binding('http_parser') is deprecated.
	(Use `node --trace-deprecation ...` to show where the warning was created)

	restify listening to http://[::]:3978

	Get Bot Framework Emulator: https://aka.ms/botframework-emulator

	To talk to your bot, open the emulator select "Open Bot"

Avviamo poi il Bot Emulator è colleghiamolo al nostro ChatBot, selezionando Open Bot, inseriamo la URL del Bot locale http://localhost:3978/api/messages e quindi click su Connect.

genesys-cloud-bot-emulator

Ora siamo connessi con il ChatBot locale e possiamo interagire.

genesys-cloud-bot-emulator

Il flusso del ChatBot di test prevede che digitando “transfer” la conversazione venga trasferita sulla coda Genesys e l’agente, nel momento in cui risponde al contatto, riceve anche tutta l’history della chat tra l’utente ed il ChatBot e questo è esattamente quello che accade, come potete osservare nelle immagini che seguono.

genesys-cloud-bot-emulator

genesys-cloud-bot-emulator

Se volete divertirvi a modificare il ChatBot potete farlo modificando il dialogo a vostro piacimento nel file bot.js.