SlideShare a Scribd company logo
1 of 79
Download to read offline
UNIVERSIT`A DEGLI STUDI DI TRIESTE
DIPARTIMENTO DI INGEGNERIA ELETTRONICA E
INFORMATICA
Tesi di Laurea Triennale in Ingegneria Elettronica e Informatica
Realizzazione di un ChatBot sulla
piattaforma Messenger di Facebook per
l’informatizzazione di una bacheca
annunci
Candidato: Xhoi Kerbizi
Relatore: Eric Medvet
A.A 2016-2017
Abstract
Nella tesi `e stato studiato un modo per realizzare un Chatbot che informatizza una comune
bacheca di annunci. Tale applicazione, che `e stata collegata alla piattaforma Messenger di Face-
book, d`a la possibilit`a agli utenti che si connettono di inserire e gestire i propri annunci; inoltre
una sezione `e stata riservata alla ricerca degli annunci pubblicati da altri utilizzatori della chat.
Per rendere la ricerca pi`u incline alle esigenze degli utenti, `e stato progettato, oltre al comune
motore di ricerca, anche una conversazione in cui il bot con una serie di domande comprende
i loro interessi e in seguito cerca l’annuncio corrispondente. Infine, la comunicazione tra bot e
utente `e semplificato con una procedura di dialoghi guidati, minimizzando l’incomprensione tra
le due parti. In ogni momento l’utente pu`o fermare la conversazione, tornare indietro e chiedere
aiuto al bot sul suo funzionamento e informazioni sulle opportunit`a offerte.
Indice
Abstract i
Introduzione iii
1 Informazioni generali sui Chatbot 1
1.1 Come si realizza un Bot . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
2 Creazione del bot 3
2.1 Elementi fondamentali dell’SDK per .NET . . . . . . . . . . . . . . . . . . . . . . 3
2.1.1 Il Connettore . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
2.1.2 Le Attivit`a . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
2.1.3 Gli Allegati . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
2.1.4 I Dialoghi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
2.1.5 Il ciclo di vita dei dialoghi . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.1.6 Il FormFlow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2.1.7 Gestire i dati di una conversazione . . . . . . . . . . . . . . . . . . . . . . 10
2.1.8 Aggiungere azioni suggerite . . . . . . . . . . . . . . . . . . . . . . . . . . 11
3 Progettazione del bot 14
3.1 Dialogo Principale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
3.2 Controllore dei messaggi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
3.3 Gestore globale dei messaggi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
3.4 Dialoghi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
3.4.1 Mettere un annuncio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
3.4.2 Gestire gli annunci . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
3.4.3 Trovare un annuncio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
3.4.4 Vetrina bacheca . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
4 Pubblicazione del bot 61
5 Conclusione 63
A Codici di alcuni dialoghi 64
Bibliografia 74
ii
Introduzione
Quando adoperiamo il motore di ricerca di un qualsiasi sito specializzato nella pubblicazione di
diversi tipi di annunci realizzati da utenti, digitiamo ci`o che stiamo cercando e premiamo il tasto
invio per visualizzarne i risultati. Spesso per`o non abbiamo le idee chiare e quindi `e comodo
avere un’applicazione, che attraverso una serie di domande, possa comprendere i nostri interessi
e poi cercare se ci sono annunci adatti.
Lo scopo primario della tesi `e di realizzare questo scenario tramite un Chatbot, un’applicazione
web che interagisce con gli utenti connessi attraverso delle semplici conversazioni. Il dialogo,
che si instaura tra l’utente e il bot `e, rispetto al classico motore di ricerca, pi`u adatto alla
comprensione delle esigenze e si comporta come una vera e propria consulenza sempre attiva
e semplice da usare. La chat fa da tramite tra pi`u utenti che intendono vendere o comprare,
per cui `e necessario collegarla ad una piattaforma web che sostiene le comunicazioni personali.
Attualmente, uno dei siti web che favorisce le relazioni umane `e il noto social network Facebook,
esso gode di un’applicazione di messaggistica istantanea chiamata Messenger, per il quale negli
ultimi anni sta promuovendo largamente la campagna dei bot. Anche la nostra chat gode del
servizio di questo sito.
La tesi `e suddivisa in quattro capitoli: il primo introduce il concetto di Chatbot e si mostrano
due modi per realizzarlo.
Nel secondo capitolo `e discussa la creazione di un primo semplice bot, tramite una combinazione
delle due tecniche descritte nel capitolo precedente, e termina con la presentazione di tutti i
concetti base necessari per la sua costruzione.
Infine, a partire dal codice del bot iniziale ottenuto nel secondo capitolo, nel terzo capitolo
vengono implementate le classi necessarie per la realizzazione della bacheca di annunci. Tale
procedura `e accompagnata da test con emulatori locali, per verificarne il corretto funzionamento.
In particolare, `e stata implementata una conversazione che, con una serie di domande, aiuta gli
utenti a scegliere un oggetto e, in seguito, verifica la disponibilit`a di suoi annunci in bacheca.
In pi`u, sono state progettate diverse sezioni in cui questi possono visualizzare tutti gli annunci
presenti in bacheca, inserirne di nuovi, modificarli o eliminarli, oppure ricercare quelli a cui sono
interessati, come in un motore di ricerca. Inoltre, attraverso la registrazione di un modulo,
l’utente pu`o interrompere la conversazione in ogni momento, per chiedere delle informazioni sul
funzionamento e funzionalit`a del bot, oppure per ritornare semplicemente al main.
Per fare in modo che pi`u utenti possano usufruire del servizio del Chatbot, `e stato, inoltre,
caricato sul cloud di Azure e collegato alla piattaforma Messenger di Facebook, seguita da
ulteriori verifiche per il corretto funzionamento e sistemati eventuali problemi. Infine, diamo le
nostre conclusioni e alcuni spunti di miglioramento per un lavoro futuro.
Capitolo 1
Informazioni generali sui Chatbot
I Chatbot sono delle applicazioni web con cui utenti diversi possono interagire attraverso delle
conversazioni. La comunicazione tra le due parti pu`o avvenire attraverso messaggi di testo, carte
pi`u complesse (messaggi contenenti bottoni, immagini e testi), o addirittura con l’uso della voce
(ad esempio Siri di Apple e Cortana di Windows).
I bot possono essere liberi, ovvero gestiscono diversi tipi di input degli user, grazie a tecniche
sofisticate di riconoscimento del linguaggio naturale (come il servizio offerto, ad esempio, dal sito
web LUIS), oppure possono avere pi`u interazioni guidate, dove esso stesso fornisce all’utente le
scelte e le azioni che `e in grado di accettare.
Come per le applicazioni dei siti web pi`u conosciuti `e importante offrire una buona esperienza
utente ai loro utilizzatori, lo stesso vale anche per i Chatbot. Dunque, quando si progetta un
bot, bisogna fare in modo che esso risolva le problematiche dei suoi utenti:
• con il minor numero di passaggi possibili
• pi`u facilmente e pi`u velocemente di quanto non facciano i suoi competitori
• facendo in modo che funzioni su tutti i dispositivi e piattaforme informatiche richieste
• facendo in modo che sia accessibile a tutti, ovvero facile da usare
Molto spesso, ottenere una buona esperienza utente non significa necessariamente rendere pi`u
intelligente il bot, pi`u abile nella comprensione automatica del linguaggio naturale o pi`u capace
nell’uso della voce. E’ preferibile un bot semplice e funzionante, che uno molto sofisticato e che
non risolve le problematiche dei suoi utenti.
1.1 Come si realizza un Bot
Ci sono essenzialmente due modi per costruire un Chatbot, tramite
• Servizio Bot di Azure
1
CAPITOLO 1. INFORMAZIONI GENERALI SUI CHATBOT 2
• SDK (Kit per sviluppo dei software) di Bot Builder
Il Servizio Bot di Microsoft Azure offre la possibilit`a di creare un Chatbot di default, ospitato
direttamente sul sito, che pu`o essere modificato con l’editor di codice online, oppure scaricando
e poi elaborando il file .zip in locale con Visual Studio. Al termine delle modifiche occorre ri-
pubblicarlo nuovamente su Azure, in modo tale che sia utilizzabile da terzi. Pi`u precisamente,
il pacchetto di Azure presenta sia l’SDK di Bot Builder, che serve per la progettazione del
bot, che la libreria Bot Framework, per connetterlo ai canali, come Facebook, Skype, Twillio
e molti altri.
La seconda tecnica, invece, prevede l’utilizzo dell’SDK di Bot Builder, tramite l’ambiente di
sviluppo Microsoft Visual Studio (quindi si tratta di un’implementazione locale). E’ anche di-
sponibile un emulatore per testare il bot e il servizio web di Channel Inspector per visualizzare
in anteprima il suo funzionamento nel canale specificato. Quando `e pronto, viene dapprima pub-
blicato su Azure e poi pu`o essere collegato eventualmente ad un canale.
E’ importante notare che l’SDK di Bot Builder pu`o essere usato con supporto per .NET o No-
de.js, le quali sono due importanti piattaforme utilizzate per costruire diversi tipi di applicazioni,
tra cui quelle web.
Nella tesi `e stata adoperata in un primo momento un’implementazione locale con Visual Studio,
sfruttando le funzionalit`a dell’SDK di Bot Builder per .NET (quindi la seconda tecnica e per
la scrittura del programma `e stato utilizzato il linguaggio C#, moderno, orientato agli oggetti
e di ”tipo sicuro”). In questa fase sono state create tutte le classi necessarie per dare vita alla
bacheca di annunci, e tramite l’emulatore di Bot Framework, che `e un’applicazione desktop, `e
stata testata e condotta la messa a punto del bot in locale.
Successivamente, usando la prima tecnica, `e stato creato un servizio bot di Azure, scaricato
il file in locale e modificato inserendo le classi gi`a create nella fase precedente; infine, `e stato
ripubblicato nuovamente sul sito.
Sarebbe stato possibile fare direttamente tutto su Azure, ma sono state incontrate diverse diffi-
colt`a: l’editor di codice online non permetteva grossi cambiamenti di codice a partire da quello
iniziale ricevuto al momento dell’attivazione del servizio e la procedura di download del file .zip
- modifica - upload sul sito comportava molte pubblicazioni che richiedevano del tempo.
In seguito alla pubblicazione del bot su Azure, `e stato collegato anche al canale Messenger di
Facebook, seguendo un procedimento che ha comportato la realizzazione di una pagina web sul
social network, impostata come gestore della chat.
I dettagli sui passaggi seguiti per l’implementazione del Chatbot sono presenti nel prossimo
capitolo, dove si discutono anche gli ingredienti principali per la comprensione del suo funziona-
mento.
Capitolo 2
Creazione del bot
Per la creazione del bot `e stata utilizzata come riferimento la guida disponibile al link 2 della
Bibliografia. Per cominciare, i passi da seguire sono:
1. Installazione di Visual Studio 2017.
2. Aggiornamento di tutte le estensioni.
3. Scaricamento dei file .zip Bot Application, Bot Controller e Bot Dialog, ottenuti dai
rispettivi siti elencati nella guida.
4. Estrazione dei file e copiatura nella directory dei modelli di elementi di Visual Studio 2017,
in questo caso si ha:
C:UsersXhoiDocumentsVisual Studio 2017 TemplatesProjectTemplatesVisual C#.
5. Apertura di Visual Studio, creazione di un nuovo progetto C#, e selezione del modello
”Applicazione Bot”, presente tra le scelte, poich´e installato nel passo precedente.
Arrivati a questo punto, si ottiene un progetto che contiene tutti i componenti necessari per
un semplice bot, compreso un riferimento all’SDK di Bot Builder per .NET; si verifica ora che
esso sia dell’ultima versione rilasciata, andando ad aggiornare nella sezione ”Gestisci pacchetti
NuGet”, a cui si accede cliccando tasto destro sul nome del progetto, il pacchetto con nome
Microsoft.Bot.Builder. Prima di visualizzare e modificare il codice ottenuto, `e necessario fare
un approfondimento sui concetti chiavi dell’SDK.
2.1 Elementi fondamentali dell’SDK per .NET
2.1.1 Il Connettore
Il framework con nome Bot.Framework.Connector fornisce un ”connettore”, cio`e una singola
API REST, il quale consente ad un bot di comunicare attraverso pi`u canali, come Facebook,
Skype, Slack, con uno o pi`u utenti. Esso facilita la trasmissione dei messaggi tra le due parti.
3
CAPITOLO 2. CREAZIONE DEL BOT 4
REST `e il principio architettonico di base del web, nel quale i client (browser) e i server possono
interagire in modi complessi senza che i primi sappiano nulla in anticipo sui secondi e le risorse
che ospitano. Il vincolo principale `e che essi devono essere entrambi d’accordo sul supporto
utilizzato per i documenti scambiati, che nel caso del web `e HTML.
Un’API che aderisce ai principi di REST non richiede che il client conosca qualcosa sulla struttura
dell’API stessa. Piuttosto, il server deve fornire tutte le informazioni necessarie al client per
interagire con il servizio che offre. Ad esempio, lo pu`o fare attraverso un modulo HTML, nel
quale esso specifica la posizione della risorsa e i campi richiesti.
Il browser non sa in anticipo dove inviare le informazioni e quali trasmettere, la cui conoscenza,
invece, `e interamente fornita dal server.
2.1.2 Le Attivit`a
Il connettore utilizza un oggetto di tipo Activity per passare informazioni avanti e indietro tra
bot e canale. Il tipo pi`u comune di attivit`a `e il messaggio, ma ce ne sono di altri. Alcuni mes-
saggi possono semplicemente essere costituiti da testo normale, mentre altri possono presentare
contenuti pi`u ricchi, come testo parlato, azioni suggerite, allegati multimediali, schede ricche e
dati specifici per i canali. `E possibile creare un messaggio personalizzato, utilizzando un oggetto
di tipo Activity, ed impostare le propriet`a necessarie prima di inviarlo all’utente, un esempio in
C# `e il seguente:
IMessageActivity messaggio = Activity.CreateMessageActivity();
messaggio.Text = "Ciao!";
messaggio.TextFormat = "plain";
messaggio.Locale = "it-IT";
2.1.3 Gli Allegati
La propriet`a Attachments di un oggetto di tipo Activity, si consideri per esempio un messaggio,
contiene una serie di oggetti di tipo Attachment che rappresentano le carte ricche ed elementi
multimediali, che possono essere immagini, audio, video e file.
Le seguenti righe di codice C# mostrano un esempio per l’inserimento di un allegato:
var messaggio = await risultato as Activity; // il bot attende un messaggio,
var risposta = messaggio.CreateReply(); // genera poi una risposta,
risposta.Attachments = new List<Attachment>(); // pone la propriet`a Attachments uguale a una lista di allegati,
risposta.Attachments.Add(new Attachment() // aggiunge a questa lista l’allegato sotto definito
{
ContentUrl = "https://upload.wikimedia.org/wikipedia/en/a/a6/Bender_Rodriguez.png",
ContentType = "image/png",
Name = "Bender_Rodriguez.png"
});
Se un allegato `e un’immagine, un audio o un video, il servizio Connettore comunicher`a i
suoi dati al canale in modo tale che esso li renda visualizzabili all’interno della conversazione. Se
l’allegato `e un file, l’URL corrispondente verr`a reso come un collegamento ipertestuale all’interno
della conversazione.
CAPITOLO 2. CREAZIONE DEL BOT 5
Le carte ricche sono dei messaggi che non contengono solo testo o immagini, ma anche dei bottoni
cliccabili, che producono degli eventi. Per inserire quest’ultimi, bisogna definire un oggetto della
classe CardAction e specificare l’azione prodotta, quando l’utente esegue il click.
Ogni oggetto della classe CardAction pu`o presentare le propriet`a seguenti:
• Type: di tipo stringa che specifica il genere di azione prodotta.
• Title: di tipo stringa che specifica il nome del bottone.
• Image: di tipo stringa che specifica l’URL dell’immagine per il bottone.
• Value: stringa che l’utente deve inserire per realizzare il tipo dell’azione presente nella
propriet`a Type.
Nella tesi `e stata adoperata molte volte la combinazione delle propriet`a: Type, Title e Value.
Ci sono diversi esempi di Type come openUrl, imBack, postBack e molti altri. In seguito
alla pressione del bottone che li contiene, il primo consente di aprire un url specificato nel value,
il secondo chiede all’utente di digitare il testo contenuto in value. Il terzo `e analogo al secondo,
con la differenza che il messaggio di risposta `e nascosto agli altri membri della conversazione.
Per la somiglianza dei due, alcuni canali lo considerano come se fosse imBack.
La tabella 2.1 contiene un riassunto delle carte ricche con i loro nomi e funzioni.
Tabella 2.1: Carte ricche
Tipo Descrizione
Adaptive Card Una carta modificabile che contiene ogni com-
binazione di testo, parole, immagini, bottoni e
campi di input.
Animation Card Una carta che pu`o eseguire gif animati o video
corti.
Audio Card Una carta che pu`o eseguire un file audio.
Hero Card Una carta che contiene tipicamente una singo-
la ma grande immagine, uno o pi`u bottoni e un
testo.
Thumbnail Card Una carta che contiene tipicamente una singo-
la immagine thumbnail, uno o pi`u bottoni e un
testo.
Receipt Card Una carta che consente a un bot di fornire una
carta recipiente allo user. Esso contiene tipica-
mente la lista degli oggetti da includere nel reci-
piente, il carico e l’informazione complessiva e un
altro testo.
SignIn Card Una carta che consente a un bot di chiedere allo
user di collegarsi. Esso contiene tipicamente un
testo e uno o pi`u bottoni cliccabili dallo user per
iniziare il processo di collegamento.
Video Card Una carta che pu`o eseguire video.
CAPITOLO 2. CREAZIONE DEL BOT 6
2.1.4 I Dialoghi
In una comune applicazione web o sito web si usano immagini singole o multiple per permettere
lo scambio di informazioni con l’utente; queste costituiscono ci`o che viene chiamata User In-
terface (UI). In modo simile si pu`o dire che i bot hanno una UI fatta di dialoghi.
Essenzialmente, un dialogo `e una vera e propria astrazione che incapsula il proprio stato in una
classe C#, la quale eredita dalla superclasse IDialog. Dunque, quando si crea un bot utilizzando
l’SDK per .NET di Bot Builder, `e possibile utilizzare delle finestre di dialogo per modellare e
gestire il flusso di conversazione con uno o pi`u utenti. Questo genera una separazione logica del
Chatbot in diverse aree.
I dialoghi possono essere in forma di testo, bottoni, conversazioni vocali o altri elementi. Po-
trebbero avere o no interfacce grafiche. Essi sono usati per invocare altri dialoghi, oppure per
gestire l’input degli utenti.
Il contesto di un dialogo `e un oggetto della classe IDialogContext che mantiene la pila dei
dialoghi attivi nella conversazione in qualsiasi momento.
Ogni bot deve avere un dialogo principale, che `e quello invocato nel metodo ”post” dalla classe,
che funge da controllore dei messaggi, e da cui possono, eventualmente, partire suoi figli. Si
mostra ora un esempio in C# di un possibile dialogo principale:
[Serializable]
public class DialogoPrincipale : IDialog<object>
{
public async Task StartAsync(IDialogContext contesto)
{
contesto.PostAsync("Ciao! Io sono il dialogo principale");
contesto.Wait(MessaggioRicevutoAsync);
}
private async Task MessaggioRicevutoAsync(IDialogContext contesto,IAwaitable<object> risul)
{
contesto.PostAsync("Per favore, digita un messaggio!");
var messaggio = await risul as Activity;
await contesto.PostAsync($"Hai inviato {messaggio.Text}");
contesto.Wait(MessaggioRicevutoAsync);
}
}
}
$
Ogni dialogo, tra cui anche quello appena visto, poich´e `e una classe che eredita dalla madre
IDialog, deve implementare l’interfaccia di quest’ultima, che presenta le seguenti caratteristiche:
• Deve usare l’attributo [Serializable], per permettere la serializzazione degli oggetti della
sua classe. Il termine ”serializzazione” in C# indica il processo di conversione di un oggetto
in un flusso di byte allo scopo di archiviarlo o trasmetterlo alla memoria, ad un database
o ad un file. Esso consente allo sviluppatore di salvare lo stato di un oggetto per ricrear-
lo quando necessario (il processo inverso `e denominato deserializzazione), consentendo sia
l’archiviazione di oggetti che lo scambio di dati. Grazie alla serializzazione, uno svilup-
patore pu`o eseguire azioni come l’invio dell’oggetto ad un’applicazione remota tramite un
servizio Web (questo `e il caso corrente, in quanto, come gi`a detto, l’applicazione bot deve
CAPITOLO 2. CREAZIONE DEL BOT 7
essere pubblicata sul sito di Microsoft Azure), il passaggio di un oggetto da un dominio ad
un altro, il passaggio di un oggetto attraverso un firewall come stringa XML o la gestione
su diverse applicazioni di informazioni che hanno a che fare con la sicurezza o specifiche
dell’utente.
• Utilizza le parole chiave async ed await, che in C# sono il punto centrale della program-
mazione asincrona. Quest’ultima `e essenziale per tutte quelle operazioni che interrompono
l’esecuzione del programma in cui sono svolte. Per esempio, quella riguardante l’accesso
ad una risorsa Web pu`o risultare talvolta lenta o ritardata. Se questa operazione viene
bloccata in un processo sincrono, l’intera applicazione deve attendere la sua terminazione
prima di ripartire, mentre, in uno asincrono, essa pu`o continuare a fare altro.
Tramite le parole chiave async e await, `e possibile usare le risorse del framework .NET per
creare un metodo asincrono con la stessa facilit`a con cui `e possibile crearne uno sincrono.
La modalit`a asincrona `e molto importante per le applicazioni che accedono al thread del-
l’interfaccia utente (UI), poich´e tutte le operazioni di quest’ultima, in genere, condividono
un solo thread. Se si specifica un metodo asincrono, usando il modificatore async, esso pu`o
usare l’operatore await per definire i suoi punti di sospensione, indicando al compilatore
che non pu`o continuare oltre a questi, prima del completamento dei corrispondenti processi
asincroni in attesa; nel frattempo, il controllo viene restituito al suo chiamante. E’ doveroso
informare che la sospensione di un metodo asincrono in corrispondenza di un’espressione
await non costituisce la sua uscita.
Inoltre, il metodo asincrono contrassegnato con async pu`o essere atteso da quelli che lo
chiamano. Esso contiene, in genere, una o pi`u occorrenze di un operatore await, mentre
l’assenza di quest’ultimo non determina un errore del compilatore, ma lo fa agire come un
metodo sincrono, nonostante la presenza del modificatore async; in questo caso, il com-
pilatore genera un avviso (warning). Un metodo asincrono ritorna, in genere, un oggetto
della classe Task o Task<TResult>, che rappresentano un’istruzione return (nel secondo
si specifica anche il tipo, per esempio si potrebbe avere Task<string>).
• Possiede un metodo, che pu`o essere asincrono o no a seconda dei casi, di nome StartAsync,
che riceve come parametro il contesto del dialogo e ne chiama un altro. Quest’ultimo,
asincrono, ha come parametri il contesto del dialogo attuale e un oggetto della classe
IWaitable, che pu`o essere di diverso tipo, tra cui anche IActivity. Esso ha la funzione
di gestire le azioni successive all’invio del messaggio da parte dell’utente. Nell’esempio del
dialogo principale, tale metodo chiede a quest’ultimo di digitare un messaggio e gli risponde
con il suo testo. In altri casi, potrebbe non rimanere in attesa e rispondere subito, per
esempio, con un messaggio o una Hero Card; ci sono pi`u implementazioni possibili, a
seconda delle esigenze. Inoltre, entrambi i metodi hanno come tipo restituito un oggetto
della classe Task, che, in questo caso, `e una rappresentazione asincrona dell’invio del
messaggio di risposta del bot.
Inoltre, sempre dal codice, `e possibile vedere un modo per inviare e ricevere messaggi, ad esempio
con l’istruzione:
var messaggio = await risul as Activity;
CAPITOLO 2. CREAZIONE DEL BOT 8
Il bot attende un messaggio da parte dell’utente. Inoltre, con il comando seguente esso invia il
testo del messaggio ricevuto precedentemente:
await contesto.PostAsync($"Hai inviato {messaggio.Text}");
Il metodo PostAsync restituisce un oggetto della classe Task ed ha come parametro, in generale,
un oggetto della classe IMessageActivity; in questo caso, `e usato il valore del suo campo Text.
Inoltre, con il comando: contesto.Wait(MessaggioRicevutoAsync) il bot si mette in attesa
di un altro messaggio, richiamando nuovamente il metodo di prima.
Il seguente codice, invece, rappresenta un esempio di Controllore:
[BotAuthentication]
public class MessagesController : ApiController
{
public async Task<HttpResponseMessage> Post([FromBody]Activity attivit`a)
{
if (attivit`a.Type == ActivityTypes.Message)
{
await Conversation.SendAsync(attivit`a, () => new DialogoPrincipale());
}
else
{
// gestisce le attivit`a che non sono messaggi
// GestioneMessaggiDiSistema(attivit`a);
}
var risposta = Request.CreateResponse(HttpStatusCode.OK);
return risposta;
}
}
Per quando riguarda l’interfaccia di un controllore, notiamo che: nel metodo post, se al bot ar-
riva un’attivit`a di tipo messaggio, viene chiamato il dialogo principale, altrimenti si definiscono
nel metodo GestioneMessaggiDiSistema le operazioni da eseguire per le attivit`a differenti.
Per garantire che l’accesso al bot sia possibile, per motivi di sicurezza, solo tramite il connettore
di Bot Framework, bisogna configurare l’endpoint del Chatbot, in modo tale che, registrandola
sul cloud di Azure, permetta solo traffico HTTPS (crittato) e che si abiliti la sua autenticazione,
tramite l’identificatore e la password dell’applicazione (prelevati sempre dal sito appena citato).
Normalmente, si mette l’attributo [BotAuthentication] senza parametri, per usare le cre-
denziali di autenticazione del bot immagazzinate nel file Web.config, che `e fatto in questo
modo:
<appSettings>
<add key="MicrosoftAppId" value="_appId_" />
<add key="MicrosoftAppPassword" value="_password_" />
</appSettings>
altrimenti, si adopera l’attributo specificandone i parametri:
[BotAuthentication(MicrosoftAppId = "_appId_", MicrosoftAppPassword="_password_")]
public class MessagesController : ApiController
{
}
CAPITOLO 2. CREAZIONE DEL BOT 9
2.1.5 Il ciclo di vita dei dialoghi
Quando una finestra di dialogo viene chiamata, essa prende il controllo del flusso di conversazione,
cio`e si colloca nella prima posizione dello stack (pila) dei dialoghi attivi; ogni nuovo messaggio
che arriva al bot viene elaborato da essa, finch´e non viene chiusa o reindirizzata ad un’altra con
opportuni comandi. Per chiudere una finestra di dialogo e rimuoverla dallo stack, instradando cos`ı
l’utente verso quella della posizione precedente, si pu`o utilizzare il comando context.Done().
Per evitare errori nell’esecuzione del bot, oltre a quello appena visto, `e necessario terminare
ogni metodo di dialogo con uno dei seguenti comandi, altrimenti, la stessa infrastruttura di Bot
Framework non sa quale azione intraprendere quando gli arriva un nuovo messaggio:
• context.Wait(): definisce quale metodo, il cui nome `e specificato nei parametri, deve
essere chiamato dal dialogo corrente, al prossimo messaggio digitato dall’utente.
• context.Forward() o context.Call(): chiamano un dialogo figlio e lo aggiungono nella
prima posizione dello stack, lasciandogli cos`ı il comando. Entrambi hanno come primo
parametro il nome del dialogo figlio, come secondo il metodo da eseguire dopo la sua
terminazione, mentre differiscono nel terzo e quarto. Nel caso di context.Forward() viene
passato al figlio anche un oggetto e c’`e la possibilit`a di gestire la cancellazione dei token,
mentre in context.Call() non ci sono questi ultimi due parametri. Il funzionamento di
entrambi ricorda molto la ”redirection” (indirizzamento) dei siti web. Il dialogo chiamato
usando context.Call() deve presentare, nel metodo invocato da StartAsync, un’istruzione
con cui si aspetta un’attivit`a, mentre ci`o non vale per context.Forward().
Muniti delle conoscenze sui dialoghi, consideriamo ora l’implementazione e le funzionalit`a di una
carta ricca, in particolare una Hero Card su Messenger.
Figura 2.1: Un esempio di Hero Card.
Il codice C# corrispondente `e il seguente:
var risposta = contesto.MakeMessage();
risposta.Attachments = new List<Attachment>();
HeroCard hc = new HeroCard()
CAPITOLO 2. CREAZIONE DEL BOT 10
{
Title = "Benvenuto nella mia applicazione",
Subtitle = "Io sono il bot degli annunci!"
};
List<CardImage> immagine = new List<CardImage>();
CardImage ci = new CardImage("" +
"https://medias2.prestastore.com/835054-pbig/chat-bot-for-social-networking.jpg" +
"");
immagine.Add(ci);
hc.Images = immagine;
risposta.Attachments.Add(hc.ToAttachment());
await contesto.PostAsync(risposta);
Il bot in questo caso, per generare una risposta, usa il metodo contesto.MakeMessage(). A
questo punto imposta la propriet`a Attachments del messaggio di risposta uguale ad una nuova
lista di allegati e definisce la Hero Card (settandone le propriet`a). Successivamente, crea una
nuova lista di carte immagini, in cui inserisce una con l’URL di un documento immagine ed
uguaglia la propriet`a Images della Hero Card con tale lista. Infine, aggiunge alla lista degli
allegati, creata all’inizio, la Hero Card trasformandola con il metodo ToAttachment() in un
allegato, per poi inviare la risposta.
Quindi complessivamente, il bot attende un messaggio da parte dell’utente e risponde con un
carta ricca contenente un’immagine e un testo.
2.1.6 Il FormFlow
Il formflow `e una conversazione guidata tra bot e utenti, che avviene in modo automatico, poich´e
le sue linee guida sono stabilite in un primo momento di settaggio. Quest’ultimo contribuisce
all’utilizzo di poche righe di codice, dal momento che il grosso del lavoro lo conduce la libre-
ria Microsoft.Bot.Builder.FormFlow. Il formflow pu`o essere adoperato in collaborazione a
finestre di dialogo, rispetto a cui per`o `e meno flessibile, per aumentare la propria funzionalit`a.
Inoltre, pu`o semplificare la creazione di quei Chatbot adibiti al raccoglimento delle informazioni
dell’utente. Ad esempio, si potrebbe pensare ad un bot specializzato nel prendere ordini per
dei sandwich, il quale deve ottenere diversi dati dall’utente come il tipo di pane che vuole, il
condimento, la dimensione e cos`ı via.
2.1.7 Gestire i dati di una conversazione
Poich´e, per raggiungere l’obbiettivo della tesi, `e necessario che utenti diversi possano collegarsi al
Chatbot contemporaneamente, senza influenzarsi e disturbarsi a vicenda, occorre illustrare una
serie di tecniche che consentono di memorizzare e recuperare i dati di quest’ultimi all’interno
della conversazione. Pi`u specificatamente, sono stati utilizzati nella tesi PrivateConversation-
DataTryGetValue e PrivateConversationDataSetValue. Il primo consente di recuperare
i dati dell’utente precedentemente salvati all’interno della conversazione sul canale specificato,
mentre il secondo di immagazzinarli all’interno della conversazione sul canale specificato.
CAPITOLO 2. CREAZIONE DEL BOT 11
Il recupero e l’immagazzinamento delle informazioni avvengono tramite delle variabili che sono
uniche per ogni utente, con cui di fatto il bot conduce una conversazione privata. Un esempio
scritto in codice C# `e il seguente:
private async Task MessaggioRicevutoAsync(IDialogContext contesto, IAwaitable<object> ris)
{
var messaggio = await ris as Activity;
string frase = messaggio.Text;
contesto.PrivateConversationData.SetValue<string>("Testo", frase);
await contesto.PostAsync($"Mi hai inviato {frase}");
contesto.Wait(MessaggioRicevutoAsync2);
}
private async Task MessaggioRicevutoAsync2(IDialogContext contesto, IAwaitable<object> ris)
{
string frase = " ";
contesto.PrivateConversationData.TryGetValue("Testo", out frase);
await context.PostAsync($"Prima mi hai inviato {frase}");
}
L’istruzione: contesto.PrivateConversationData.SetValue<string>(”Testo”, frase) realizza il fat-
to che il bot, privatamente con ogni utente, crea una variabile stringa di nome ”Testo” in cui
memorizza il testo del messaggio atteso.
Con l’istruzione: contesto.PrivateConversationData.TryGetValue<string>(”Testo”, out frase) il
bot ricava il valore della variabile ”Testo” memorizzato nel metodo precedente, per comunicarlo
all’utente corrispondente.
Si nota, inoltre, che: i prefissi di SetValue e TryGetValue devono essere gli stessi. Non si pu`o
usare, ad esempio, PrivateConversationData.SetValue con ConversationData.TryGetValue, o vi-
ceversa.
Se fosse stato adoperato l’istruzione con ConversationData in tutti e due i metodi, si avrebbe
avuto un’unica variabile ”Testo” condivisa da tutti gli utenti nella stessa conversazione, modifi-
cabile da chiunque, e il cui valore ottenuto nel metodo con ”TryGetValue” sarebbe stato quello
dell’utente che aveva modificato per ultimo. Pertanto, si avrebbe avuto un effetto non desidera-
to, in quanto gli utenti avrebbero rischiato di ricevere risultati per scelte che non avrebbero mai
fatto.
2.1.8 Aggiungere azioni suggerite
Per fare in modo che il Chatbot interagisca con i suoi utenti attraverso conversazioni guidate, in
cui `e lui stesso a proporre le possibili risposte che `e in grado di accettare, `e necessario fare una
breve descrizione dei metodi necessari a tale scopo. Solitamente, sono utilizzate le funzioni della
classe PromptDialog, elencate brevemente nella tabella 2.2.
Tabella 2.2: Funzioni della classe PromptDialog
CAPITOLO 2. CREAZIONE DEL BOT 12
Metodo della classe PromptDialog Descrizione
Text Chiede all’utente di digitare una stringa di testo.
Confirm Chiede all’utente una conferma (S`ı o No)
Number Chiede all’utente di inserire un numero
Choice Chiede all’utente di scegliere tra diverse possibi-
lit`a.
Attachment Chiede all’utente di caricare un file.
Un esempio di PromptDialog.Choice `e il seguente:
private async Task OrdinaCibo(IDialogContext contesto, IAwaitable<object> risultato)
{
List<string> listaCibo = new List<string>{
"Pizza",
"Pasta",
};
PromptOptions<string> opzioni = new PromptOptions<string>("Che cibo vorresti mangiare?","Per favore, riprova di nuovo!",
"Tentativi finiti!", listaCibo, 2);
PromptDialog.Choice<string>(contesto, metodoSuccessivoAllaSceltaAsync, opzioni);
}
private async Task metodoSuccessivoAllaSceltaAsync(IDialogContesto contesto, IAwaitable<string> ris)
{
string risposta = await ris; // aspetto che l’utente scelga tra pizza e pasta
if(risposta == "pizza") await contesto.PostAsync("Hai scelto la pizza");
else if (risposta == "pasta") await contesto.PostAsync("Hai scelto la pasta");
}
I parametri del metodo sono il contesto attuale del dialogo, il metodo successivo da eseguire in
caso di procedura corretta, una lista di stringhe che identificano le opzioni da scegliere, un testo
per far capire all’utente che deve operare una decisione, un messaggio che esce quando si sbaglia
a digitare e un numero di tentavi massimo. In questo semplice estratto di dialogo, il bot propone
all’utente di scegliere tra pizza e pasta e gli mostra la decisione effettuata.
Come esempio di PromptDialog.Text, consideriamo le seguenti righe di codice:
private async Task InserisciTesto(IDialogContext contesto, IAwaitable<object> ris)
{
PromptDialog.Text(contesto, TestoSceltoAsync, "Inserisci un testo");
}
private async Task TestoSceltoAsync(IDialogContext contesto, IAwaitable<string> ris)
{
string risposta = await ris;
await contesto.PostAsync($"Hai inviato il testo seguente: {risposta}");
}
$
I parametri del metodo sono il contesto attuale del dialogo, un metodo da eseguire una volta che
`e stato inserito il testo e un messaggio per far capire all’utente che deve inserirlo. Se la procedura
non avviene correttamente, esce un messaggio di errore che invita a ripetere. In questa sezione
di dialogo illustrativo, il bot chiede all’utente di inserire un testo che poi gli mostra.
Infine, mostriamo un esempio di PromptDialog.Number e PromptDialog.Confirm, i cui
parametri sono uguali ai metodi precedenti. In questo esempio, Il bot aspetta l’inserimento di
un numero e ne chiede la conferma. Il codice corrispondente `e il seguente:
CAPITOLO 2. CREAZIONE DEL BOT 13
private async Task InserisciNumero(IDialogContext contesto, IAwaitable<object> ris)
{
PromptDialog.Number(contesto, NumeroSceltoAsync, "Inserisci un numero qualsiasi!");
}
private async Task NumeroSceltoAsync(IDialogContext contesto, IAwaitable<long> ris)
{
long risposta = await ris;
await contesto.PostAsync($"Hai inviato il numero seguente: {risposta}");
PromptDialog.Confirm(contesto, confermaNumeroAsync, "Vuoi confermare il numero inserito?");
}
private async Task confermaNumeroAsync(IDialogContext contesto, IAwaitable<bool> ris)
{
var conferma = await ris;
if (conferma == true) await contesto.PostAsync("Numero confermato");
else await contesto.PostAsync("Numero non confermato");
}
$
Sommario
In questo capitolo `e stato creato un primo semplice bot usando una guida presente sul sito di
Microsoft, che ha comportato il coinvolgimento dell’ambiente di sviluppo di Microsoft Visual
Studio, in cui sono state sfruttate le funzionalit`a dell’SDK di Bot Builder per .NET.
In seguito, allo scopo di comprendere il suo funzionamento, `e stato condotto un approfondimento
di tutti i concetti base del framework appena citato, organizzati tramite una lista riassuntiva.
Ed `e a questo punto che `e stato introdotto il concetto di Connettore, che permette l’interazione
tra bot e uno o pi`u canali, a cui si collegano gli utenti, trasportando da una parte all’altra gli
oggetti della classe Activity. Quest’ultimi sono di diversi tipi, tra cui i messaggi, che possono
essere composti di solo testo, oppure possono possedere eventualmente degli allegati. Questi
possono essere elementi multimediali (come immagini, audio e file), oppure carte ricche, cio`e
messaggi composti da testo, immagini, e anche bottoni, la cui pressione genera degli eventi.
Successivamente, `e stata vista l’importanza dei dialoghi, che permettono di creare le conversazioni
con gli utenti. Siccome `e stato ritenuto necessario per l’implementazione del bot prefissato che
queste ultime siano private per ogni utente e guidate, sono stati descritti i metodi e le funzioni
che consentono ci`o.
Nel prossimo capitolo si parte dal codice del bot ottenuto prima e si implementano e aggiungono
le classi per realizzare la bacheca di annunci, di cui si verifica poi il corretto funzionamento e
funzionalit`a mediante opportuni emulatori.
Capitolo 3
Progettazione del bot
Per visualizzare il codice C# dell’applicazione bot creata nel capitolo precedente, andiamo nella
sezione di nome Esplora soluzioni di Visual Studio, dove si vedono all’inizio questi elementi:
Figura 3.1: Esplora soluzioni di Visual Studio.
• Properties: contiene tutte le impostazioni di base dell’applicazione bot.
• Riferimenti: contiene tutti i pacchetti NuGet installati.
• AppStart: contiene la classe WebApiConfig.cs, la quale, usando il framework di nome
WebApiConfig, serve per generare connessioni HTTP con il bot, che, essendo un’appli-
cazione web che offre un servizio, `e in attesa su un certo numero di porta. In un primo
momento, poich´e esso non `e ospitato su Azure, `e accessibile localmente, in genere, sul
numero di porta: 3979. Quando viene caricato sul sito, `e accessibile su un altro numero.
• Controllers: in questa cartella c’`e il Controllore dei messaggi, che `e una classe ereditata
dalla madre ApiController e contiene un metodo post che serve per chiamare il dialogo
principale quando l’utente scrive al bot la prima volta.
14
CAPITOLO 3. PROGETTAZIONE DEL BOT 15
• Dialogs: in questa cartella vengono inseriti tutti i dialoghi, tra cui anche quello principale
chiamato per primo.
• default.htm: rappresenta la pagina web che si apre per mostrare la connessione effettuata
con il bot.
• Global.asax: `e una classe che consente di scrivere del codice che viene eseguito in risposta
ad alcuni eventi dei diversi livelli di sistema, come l’inizio dell’applicazione, terminazione
di una sessione, l’occorrenza di un errore; esso `e composto essenzialmente da registrazioni
di moduli con valenza globale in tutto il programma, che evitano l’inserimento di righe di
codice per la gestione degli eventi in ogni dialogo.
• package.config: questo file `e amministrato dall’infrastruttura NuGet. E’ usato per
visualizzare i diversi pacchetti installati con le rispettive versioni.
• Web.config: presenta le impostazioni principali e il file di configurazione dell’applicazione
web. Si tratta di un documento XML che risiede nella directory radice del programma e
contiene dati sul comportamento della app. Queste informazioni controllano il caricamento
del modulo, la configurazione della sicurezza, la configurazione dello stato della sessione e le
impostazioni della lingua e della compilazione dell’applicazione. I file Web.config possono
anche contenere elementi specifici come stringhe di connessione ad un database.
I procedimenti seguiti nel secondo capitolo hanno permesso di ottenere un semplice e funzionante
bot, composto da un dialogo principale, che viene richiamato alla prima interazione dell’utente
da un controllore di messaggi. Quindi, per progettare il Chatbot prefissato, si `e partiti dal
codice attuale e sono state apportate successivamente alcune modifiche. Come prima cosa, `e
stato cambiato il file Web.config:
1 <appSettings>
2 <!-- update these with your Microsoft App Id and your Microsoft App Password-->
3 <add key="MicrosoftAppId" value="0dec18cd-2c4e-4d1f-83f9-ba255c018b2f" />
4 <add key="MicrosoftAppPassword" value="rhtiUOT0:=(dkxGAPO0604+" />
5 <add key="ChiaveArchiviazione" value="DefaultEndpointsProtocol=https;AccountName=bacheca;
6 AccountKey=0+mBwlgqV9US1LGh2WjFwFfo4hrk7z2HMK+eF fwuXs8m/MxOipBOJ+4gfwtHkLmQRhT8/1YcGhuBRXoFIY0kmA==" />
7 </appSettings>
8 <connectionStrings>
9 <add name="stringaConnessione"
10 connectionString="Server=tcp:bacheca.database.windows.net,1433;Initial Catalog=BachecaDB;
11 Persist Security Info=False; User ID=Xhoi ;Password=Albi4061992;MultipleActiveResultSets=False;
12 Encrypt=True;TrustServerCertificate=False; Connection Timeout=30"providerName="System.Data.SqlClient" />
13 </connectionStrings>
In ”appSettings” sono state aggiunte tre chiavi (da riga 3 a 5): i valori delle prime due sono stati
inseriti quando il Chatbot `e stato pubblicato su Azure e servono per garantire la sua sicurezza.
Mentre, il terzo `e necessario per collegarsi ad un account di archiviazione, che ha luogo sempre
su Azure, nel quale vengono caricati degli oggetti chiamati ”Blob”, che permettono l’inserimento
delle immagini negli annunci.
Infine, `e stata aggiunta una stringa per l’apertura di connessioni sql verso un database (da riga
8 a 13), ospitato in un server creato sul sito appena citato. Poich´e essa `e di lunghe dimensioni e
CAPITOLO 3. PROGETTAZIONE DEL BOT 16
mirando ad una buona leggibilit`a del codice, `e stato preferito porla in questo file, anzich´e inserirla
in ogni dialogo in cui si conducono delle interrogazioni, che necessitano, appunto, dell’apertura
della connessione.
3.1 Dialogo Principale
Come prima cosa, `e stato creato un nuovo dialogo radice, con nome DialogoPrincipale, il quale
presenta questo codice:
1 using System;
2 using System.Threading.Tasks;
3 using Microsoft.Bot.Builder.Dialogs;
4 using Microsoft.Bot.Connector;
5 using System.Collections.Generic;
6 using System.Threading;
7
8 namespace BachecaAnnunci.Dialogs
9 {
10 [Serializable]
11 public class DialogoPrincipale : IDialog<object>
12 {
13 public async Task StartAsync(IDialogContext contesto)
14 {
15 contesto.Wait(MessaggioRicevutoAsync);
16 }
17
18 private async Task MessaggioRicevutoAsync(IDialogContext contesto, IAwaitable<object> risultato)
19 {
20 var risposta = contesto.MakeMessage();
21 risposta.Attachments = new List<Attachment>();
22 HeroCard hc = new HeroCard()
23 {
24 Title = "Benvenuto nella mia applicazione",
25 Subtitle = "Io sono il bot degli annunci!"
26 };
27 List<CardImage> immagine = new List<CardImage>();
28 CardImage ci = new CardImage("https://medias2.prestastore.com/835054-pbig/chat-bot-for-social-networking.jpg");
29 immagine.Add(ci);
30 hc.Images = immagine;
31 risposta.Attachments.Add(hc.ToAttachment());
32 await contesto.PostAsync(risposta);
33 List<string> scelta = new List<string>
34 {
35 "Trovare un annuncio",
36 "Mettere un annuncio",
37 "Vetrina Bacheca",
38 "Gestire gli annunci",
39 "indietro"
40 };
41 PromptDialog.Choice(
42 contesto,
43 OperSceltaAsync,
44 scelta,
45 "Che operazione vorresti fare?",
46 retry: "Per favore, riprova di nuovo",
47 attempts: 2,
48 promptStyle: PromptStyle.Auto
49 );
50 }
51 private async Task OperSceltaAsync(IDialogContext contesto, IAwaitable<string> risultato)
CAPITOLO 3. PROGETTAZIONE DEL BOT 17
52 {
53 string messaggio = await risultato;
54 if (messaggio == "Trovare un annuncio")
55 {
56 string verifica = " ";
57 contesto.PrivateConversationData.SetValue("VerificaAnnullamento", verifica);
58 await contesto.Forward(new Comprare(), this.MetodoSuccAllaSceltaAsync, messaggio, CancellationToken.None);
59 }
60 else if (messaggio == "Mettere un annuncio")
61 {
62 string aggiorna = " ";
63 contesto.PrivateConversationData.SetValue("AggiornaAnnuncio", aggiorna);
64 await contesto.PostAsync("Ora ti chiedo di inserire una serie di informazioni!");
65 await contesto.Forward(new VendereScegliereIdAnnuncio(), this.MetodoSuccAllaSceltaAsync, messaggio, CancellationToken.None);
66 }
67 else if (messaggio == "Vetrina Bacheca")
68 {
69 await contesto.Forward(new Dialogs.VetrinaBacheca(), this.MetodoSuccAllaSceltaAsync, messaggio, CancellationToken.None);
70 }
71 else if (messaggio == "Gestire gli annunci")
72 {
73 await contesto.Forward(new Dialogs.VisualizzaAnnunciNome(), this.MetodoSuccAllaSceltaAsync, messaggio, CancellationToken.None);
74 }
75 else if (messaggio == "indietro")
76 {
77 var risposta = contesto.MakeMessage();
78 risposta.Attachments = new List<Attachment>();
79 HeroCard hc = new HeroCard()
80 {
81 Title = "Grazie per aver usufruito del mio servizio! ",
82 Subtitle = "Per riattivarmi digita qualcosa...."
83 };
84 List<CardImage> immagine = new List<CardImage>();
85 CardImage ci = new CardImage("https://medias2.prestastore.com/835054-pbig/chat-bot-for-social-networking.jpg");
86 immagine.Add(ci);
87 hc.Images = immagine;
88 risposta.Attachments.Add(hc.ToAttachment());
89 await contesto.PostAsync(risposta);
90 }
91 }
92 private async Task MetodoSuccAllaSceltaAsync(IDialogContext contesto, IAwaitable<object> risultato)
93 {
94 await contesto.PostAsync("Problema interno! Sei stato rediretto al Dialogo Principale, " +
95 "per favore digita qualcosa per riattivarmi!");
96 }
97 }
98 }
In questo dialogo, quando l’utente digita qualcosa, il bot risponde con una Hero Card di benve-
nuto, a cui segue una lista di opzioni, di cui egli ne deve scegliere una per proseguire nella conver-
sazione. Quest’ultima `e guidata, effetto reso grazie all’utilizzo del metodo PromptDialog.Choice
della classe PromptDialog, la cui funzionalit`a `e stata discussa nella sezione ”Aggiungere azioni
suggerite” del capitolo 2. Perci`o, quando l’utente digita correttamente, oppure preme semplice-
mente sul bottone d’interesse, si attiva il metodo OperSceltaAsync (riga 51 del codice), il quale
gestisce il testo del messaggio in arrivo e, a seconda della scelta effettuata, pu`o attivare i dialoghi
per trovare un annuncio, mettere un annuncio, vedere gli annunci disponibili al momento, ovvero
la vetrina della bacheca, e gestire gli annunci (modificarli o eliminarli). Il reindirizzamento a
questi avviene tramite il comando: await contesto.Forward(...), poich´e, nei metodi chiamati da
quello con nome StartAsync di ciascuno di essi, il bot non deve mettersi inizialmente in attesa di
CAPITOLO 3. PROGETTAZIONE DEL BOT 18
ricevere un messaggio, ma invia una lista di opzioni e si aspetta che l’utente ne scelga uno. Nelle
figure 3.2 e 3.3 si pu`o vedere la schermata grafica del dialogo, rispettivamente, nell’emulatore e
su Messenger.
Figura 3.2: Schermata del dialogo principale nell’emulatore di Bot Framework.
Figura 3.3: Schermata del dialogo principale su Messenger.
3.2 Controllore dei messaggi
Come gi`a detto nel capitolo precedente, il controllore dei messaggi possiede un metodo post, che,
se il bot riceve un’attivit`a di tipo messaggio, chiama il dialogo principale, altrimenti invoca il
metodo GestioneMessaggiDiSistema. Per garantire la sicurezza del bot, `e stato scelto di usare
l’attributo [BotAuthentication], attraverso cui si prendono le informazioni per l’autenticazione
dal file WebApi.Config. Un esempio di codice in C# per il Controllore `e il seguente:
CAPITOLO 3. PROGETTAZIONE DEL BOT 19
1 using System.Net;
2 using System.Net.Http;
3 using System.Threading.Tasks;
4 using System.Web.Http;
5 using Microsoft.Bot.Builder.Dialogs;
6 using Microsoft.Bot.Connector;
7
8 namespace BachecaAnnunci
9 {
10 [BotAuthentication]
11 public class MessagesController : ApiController
12 {
13 public async Task<HttpResponseMessage> Post([FromBody]Activity attivit`a)
14 {
15 if (attivit`a.Type == ActivityTypes.Message)
16 {
17 attivit`a.Locale = "it-IT";
18 await Conversation.SendAsync(attivit`a, () => new Dialogs.DialogoPrincipale());
19 }
20 else
21 {
22 GestioneMessaggiDiSistema(attivit`a);
23 }
24 var risposta = Request.CreateResponse(HttpStatusCode.OK);
25 return risposta;
26 }
27
28 private Activity GestioneMessaggiDiSistema(Activity messaggio)
29 {
30 if (messaggio.Type == ActivityTypes.DeleteUserData)
31 {
32
33 }
34 else if (messaggio.Type == ActivityTypes.ConversationUpdate)
35 {
36
37 }
38 else if (messaggio.Type == ActivityTypes.ContactRelationUpdate)
39 {
40
41 }
42 else if (messaggio.Type == ActivityTypes.Typing)
43 {
44
45 }
46 else if (messaggio.Type == ActivityTypes.Ping)
47 {
48 }
49
50 return null;
51 }
52 }
53 }
Si pone ora l’attenzione sul metodo GestioneMessaggiDiSistema (riga 28): esso riceve come pa-
rametro un’attivit`a che non `e di tipo messaggio e, a seconda del Type, restituisce un oggetto
della classe Activity. Con il primo if, il bot gestisce un’attivit`a in ingresso di tipo cancellazio-
ne utente, con il secondo, invece, gestisce il cambiamento dello stato di conversazione, dovuto
all’inserimento o eliminazione di membri; per ottenere informazioni, si usano in questo blocco
le propriet`a Activity.MembersAdded, Activity.MembersRemoved e Activity.Action della classe
Activity. Tale possibilit`a di gestione non `e presente per tutti i canali. Con il terzo if, il bot
CAPITOLO 3. PROGETTAZIONE DEL BOT 20
gestisce l’inserimento e rimozione di elementi dalla lista dei contatti. Le propriet`a Activity.From
e Activity.Action danno informazioni su cosa succede.
Con il penultimo if, il bot gestisce la conoscenza di quello che lo user sta scrivendo;
3.3 Gestore globale dei messaggi
Prima di discutere di tutti i dialoghi implementati per dare vita alla bacheca di annunci, `e ne-
cessario informare che `e stato registrato un modulo nel file Global.asax, che serve per gestire in
un certo modo alcuni tipi di messaggi in input, indipendentemente dal punto della conversazione
in cui ci si trova. Un esempio in C# del modulo `e il seguente:
using Autofac;
using BachecaAnnunci.Dialogs;
using Microsoft.Bot.Builder.Dialogs.Internals;
using Microsoft.Bot.Builder.Scorables;
using Microsoft.Bot.Connector;
namespace BachecaAnnunci
{
public class GlobalMessageHandlersBotModule : Module
{
protected override void Load(ContainerBuilder builder)
{
base.Load(builder);
builder
.Register(c => new DialogoScrollabile(c.Resolve<IDialogTask>()))
.As<IScorable<IActivity, double>>()
.InstancePerLifetimeScope();
builder
.Register(c => new AnnullaDialogo(c.Resolve<IDialogTask>()))
.As<IScorable<IActivity, double>>()
.InstancePerLifetimeScope();
}
}
}
Come si pu`o vedere dal codice, il modulo usufruisce di due ulteriori dialoghi, presenti nella
cartella Dialogs: DialogoScrollabile e AnnullaDialogo.
Il primo concede la possibilit`a all’utente di interrompere il dialogo corrente ogni volta che digita
la parola ”aiuto”, alla cui scrittura si attiva quello con nome ImpostazioniDialogo (Figura 3.4),
attraverso cui egli pu`o ricevere informazioni sul funzionamento e funzionalit`a del bot (Figure 3.5
e 3.6). Quando il servizio di supporto `e terminato, si ritorna al punto prima dell’interruzione.
Il secondo d`a la facolt`a all’utente, ogni volta che egli digita la parola ”annulla”, di interrompere
il dialogo corrente e poi, scrivendo un messaggio qualsiasi, di ripartire da quello principale.
Nell’esempio di Figura 3.7, viene interrotto il dialogo della ricerca libera, la cui implementazione
si vedr`a in seguito.
CAPITOLO 3. PROGETTAZIONE DEL BOT 21
Figura 3.4: Scelta dell’aiuto.
Figura 3.5: Come funziona il bot.
CAPITOLO 3. PROGETTAZIONE DEL BOT 22
Figura 3.6: Cosa pu`o fare il bot.
Figura 3.7: Annulla dialogo.
3.4 Dialoghi
In questa sezione si parla delle finestre di dialogo usate per dare vita alla bacheca di annunci.
La caratteristica comune di queste `e il fatto che siano guidate, ovvero il bot fa una serie di
domande all’utente e ne propone le risposte ammissibili, che possono essere digitate in forma
di testo, oppure premendo i bottoni presenti. Dunque, esso offre la possibilit`a di avanzare
nella conversazione con il modo appena descritto, oppure di retrocedere o attraverso dei bottoni
chiamati ”indietro”, oppure con altre modalit`a spiegate da esso stesso.
CAPITOLO 3. PROGETTAZIONE DEL BOT 23
3.4.1 Mettere un annuncio
Dal dialogo principale si pu`o inserire un annuncio tramite il bottone ”Mettere un annuncio” (Fig.
3.9). Per svolgere l’operazione, il bot chiede all’utente innanzitutto di scegliere un identificatore
per l’annuncio, poi di inserire alcune informazioni di carattere personale per la reperibilit`a come
un nome, un cognome, una residenza, un numero di cellulare; in seguito `e richiesta una breve
descrizione, una eventuale immagine, ed infine una password che gli serve per modifiche succes-
sive, effettuabili nella sezione ”Gestire gli annunci”, di cui si parler`a nella prossima sezione.
Per memorizzare i dati, `e stato creato un server sql su Microsoft Azure, in cui `e stato inserito
un database, formato dalle tabelle rappresentate in Figura 3.8.
Figura 3.8: Oggetti server sql su Visual Studio.
Le tabelle IdAnnunci, Chiavi, PasswordAnnunci contengono, rispettivamente, gli identifi-
catori, i nomi delle eventuali immagini e le password usate dagli utenti al momento per i loro
annunci. Ognuna di queste possiede un unico attributo di tipo stringa, ovvero una sola colonna,
che `e anche chiave primaria, contenente i valori citati prima.
La tabella ListaAnnunci, come suggerisce il nome stesso, contiene tutti gli annunci disponibili
in bacheca, distinti univocamente dal loro identificatore, che `e una stringa di dieci caratteri ed
`e anche la chiave primaria. Per ognuno di essi, oltre al campo appena citato, ci sono altri che
contengono il nome, cognome, luogo di residenza, numero di cellulare del proprietario corrispon-
dente, descrizione, ed, infine, un nome per un’eventuale immagine e una password. I valori degli
identificatori, chiavi delle immagini e password sono unici per costruzione, poich´e prima di es-
sere inseriti in ListaAnnunci, viene verificata la loro univocit`a nelle tabelle IdAnnunci, Chiavi e
PasswordAnnunci (ci`o sar`a visto in seguito).
CAPITOLO 3. PROGETTAZIONE DEL BOT 24
Figura 3.9: Mettere un annuncio.
Si descrive ora la sequenza delle finestre di dialogo progettate per l’inserimento di un annuncio;
ognuna di queste svolge la sua funzione e al suo termine chiama quella successiva:
• La procedura inizia con VendereScegliereIdAnnuncio, in cui il bot chiede all’utente
di scegliere un identificatore per il proprio annuncio, poi controlla con una query se `e gi`a
presente nella tabella di nome IdAnnunci. In caso affermativo, fa sceglierne un altro, fino
a quando non gli arriva uno non ancora presente nel sistema; in caso negativo lo inserisce
nella tabella con un’istruzione ”INSERT”. Per esempio, si vuole pubblicare un annuncio
di vendita per un cellulare ”Samsung galaxy s6” e si sceglie come identificatore ”galaxys6”
(Fig. 3.10). Poich´e quest’ultimo non `e ancora stato usato da altri utenti, viene inserito
nella tabella in Figura 3.11.
CAPITOLO 3. PROGETTAZIONE DEL BOT 25
Figura 3.10: Scelta dell’identificatore.
Figura 3.11: Tabella con gli identificatori degli annunci.
• Nei successivi dialoghi VendereNome, VendereCognome, VendereLuogo, Vendere-
NumCell, VendereDescrizione, il bot chiede all’utente di inserire rispettivamente un
nome, un cognome, un luogo di residenza, un numero di cellulare e una breve descrizione;
per ognuno chiede la conferma, dando la possibilit`a di rimediare ad eventuali errori. Ad
esempio, supponiamo di scegliere i dati presenti nelle figure seguenti.
CAPITOLO 3. PROGETTAZIONE DEL BOT 26
Figura 3.12: Inserimento nome e cognome.
Figura 3.13: Inserimento residenza e numero di cellulare.
Figura 3.14: Inserimento descrizione.
• A questo punto viene chiamato il dialogo VendereScegliereChiave, in cui il bot chiede
all’utente se vuole allegare un’immagine al proprio annuncio. In caso negativo, pone la
chiave per l’immagine uguale alla stringa ”nessuna immagine” e invoca direttamente il
dialogo per la scelta della password. Questo procedimento serve per evitare che ci siano
errori nel metodo per recuperare le immagini, quando vengono visualizzati gli annunci che
le contengono (si vedr`a meglio nella sezione ”Trovare un annuncio”).
In caso affermativo, lo invita a scegliere un nome univoco (Fig. 3.15), che, se non `e gi`a
stato scelto da un altro utente, viene inserito nella tabella Chiavi, altrimenti viene richiesto
un altro; in questo caso quest’ultima viene aggiornata come in Figura 3.16.
CAPITOLO 3. PROGETTAZIONE DEL BOT 27
Figura 3.15: Aggiunta di un’immagine.
Figura 3.16: Tabella con le chiavi delle immagini.
• Se nella finestra precedente `e stato scelto di inserire un’immagine, viene invocato ora
il dialogo VendereAggiungiImmagine (Fig. 3.15), in cui il bot aspetta che gli arrivi un
allegato, poi lo carica su un oggetto di tipo blob, situato su uno spazio di archiviazione
creato su Azure, che denomina con la chiave univoca decisa prima. Ogni utente ha la
propria immagine distinguibile da tutte le altre.
• A questo punto si arriva al dialogo VendereSceglierePassword, in cui il bot invita a scegliere
una password, che serve per consentire modifiche successive agli utenti che ne sono detentori
(Fig. 3.18). Anche qui, c’`e lo stesso procedimento visto nelle finestre di dialogo per la
scelta dell’identificatore e nome dell’immagine dell’annuncio. La scelta viene aggiunta
nella tabella PasswordAnnunci (Fig. 3.17).
Figura 3.17: Tabella con le password degli annunci.
• Infine, si arriva a ConfermaVendita, in cui l’utente `e costretto a decidere tra completare
o annullare la pubblicazione dell’annuncio (Fig. 3.18). Nel primo caso, il bot recupera
tutti i dati digitati nei passi precedenti e li inserisce nella tabella ListaAnnunci con una
istruzione SQL di tipo ”INSERT”. In questo esempio, la tabella viene aggiornata come in
Figura 3.19.
CAPITOLO 3. PROGETTAZIONE DEL BOT 28
Nel secondo caso, esso invoca il dialogo di nome RecIdAnnNomeImmaginePass, presente in
appendice, che rimuove dalle tabelle IdAnnunci, Chiavi, PasswordAnnunci, rispettivamen-
te, l’identificatore, il nome dell’immagine e la password dell’annuncio stabiliti precedente-
mente, in modo tale che possano essere usati da altri utenti. Infine, chiede all’utente di
scegliere tra iniziare un’altra procedura di pubblicazione, indirizzandolo al primo dialogo
della sequenza, ovvero VendereNome, oppure tornare semplicemente al main.
Figura 3.18: Completamento dell’inserimento dell’annuncio.
Figura 3.19: Tabella con la lista degli annunci.
Facciamo ora alcune considerazioni generali sui dialoghi appena descritti: in ognuno di loro si
memorizza l’informazione da pubblicare in una variabile di conversazione attraverso il meto-
do PrivateConversationData.SetValue(), il quale viene poi recuperato in ConfermaVendita con
PrivateConversationData.TryGetValue(). Ad esempio, in VendereNome si usa PrivateConver-
sationData.SetValue(”Nome”, nomeUser), dove nomeUser `e una stringa contenente il testo del
messaggio inserito dall’utente, il cui valore viene immagazzinato nella variabile ”Nome”. Que-
st’ultimo viene poi recuperato in ConfermaVendita, attraverso il metodo PrivateConversation-
Data.TryGetValue(”Nome”, out nomeUser), dove nomeUser `e una variabile di tipo stringa in
cui viene memorizzato il valore contenuto in ”Nome”). In pi`u, `e importante ricordare che si usa
PrivateConversationData e non ConversationData, perch´e si vuole che il bot offra il servizio a
pi`u utenti contemporaneamente, senza che si disturbino a vicenda.
Inoltre, in tutti `e presente una variabile di tipo stringa di nome verificaAgg, che serve per
distinguere l’inserimento di un annuncio dall’aggiornamento. I dialoghi appena descritti possono
essere, infatti, usati sia per pubblicare che per aggiornare; sono stati preferiti dei dialoghi ”poli-
valenti”, piuttosto che un numero maggiore di essi che fanno le stesse cose, dal momento che le
due operazioni sono quasi simili. Di questo se ne parler`a in modo pi`u approfondito nella sezione
”Gestire gli annunci”.
E’ importante osservare che la struttura dei dialoghi VendereNome, VendereCognome, Ven-
dereLuogo, VendereNumCell, VendereDescrizione `e pressoch´e la stessa, cambia solo il tipo di
informazione che deve essere memorizzata (in VendereNome viene registrato il nome, Vendere-
Cognome il cognome e cos`ı via per gli altri). Si riporta in Appendice il codice C# del primo.
CAPITOLO 3. PROGETTAZIONE DEL BOT 29
Inoltre, per il motivo precedente, tra VendereScegliereIdAnnuncio, VendereScegliereChiave e
VendereSceglierePassword `e stato deciso di inserire in Appendice il primo di essi.
3.4.2 Gestire gli annunci
Dal dialogo principale, attraverso il bottone ”Gestire gli annunci” (Fig. 3.20), `e possibile far
partire una conversazione guidata, costituita da una serie di finestre di dialogo, che permette
agli utenti di modificare i propri annunci o di eliminarli dalla bacheca.
Per garantire la sicurezza dei dati, `e stata realizzata una procedura di autenticazione, attraverso
cui il bot chiede all’utente, intenzionato a usufruire del servizio di gestione, il nome, il cognome
e il numero di cellulare che ha utilizzato per i suoi annunci nella fase di inserimento. Se questi
sono corretti, il bot ne mostra la lista e, per autorizzare la gestione di un suo elemento, richiede
la password scelta al momento della pubblicazione. Potenzialmente, ognuno pu`o conoscere il
nome, il cognome e il numero di cellulare del proprietario di un annuncio, poich´e questi dati
sono accessibili a tutti. Ma soltanto il reale possessore ne conosce la password e l’autenticazione
fallisce quando questa non viene digitata correttamente, impedendo ogni attivit`a di modifica o
di cancellazione alle persone estranee.
Figura 3.20: Gestione degli annunci.
Vediamo ora in dettaglio quali sono le classi che consentono di fornire tale servizio.
Dal punto di vista funzionale, i dialoghi, che realizzano la conversazione per la gestione degli
annunci si possono suddividere in due gruppi: quelli di autenticazione e quelli di modifica.
Autenticazione
E’ stato pensato di usare il nome, il cognome e il numero di cellulare, per identificare univoca-
mente un proprietario dell’annuncio. Dunque, in questa fase la sequenza `e formata da:
CAPITOLO 3. PROGETTAZIONE DEL BOT 30
• In VisualizzaAnnunciNome, VisualizzaAnnunciCognome, VisualizzaAnnunciNum-
Cell il bot chiede all’utente, rispettivamente, il nome , il cognome (entrambi visibili in Fig.
3.21) e il numero di cellulare (Fig. 3.22) che aveva messo nell’annuncio.
• VisualizzaAnnunci (Fig. 3.23 e 3.24): con questo dialogo il bot mostra tutti gli annunci
della persona con il nome, cognome e numero di cellulare corrispondenti ai valori dei punti
precedenti.
Figura 3.21: Autenticazione: richiesta del nome e cognome.
Figura 3.22: Autenticazione: richiesta del numero cellulare.
CAPITOLO 3. PROGETTAZIONE DEL BOT 31
Figura 3.23: Visualizzazione del primo annuncio.
Figura 3.24: Visualizzazione del secondo annuncio.
A questo punto, l’utente pu`o scegliere l’annuncio che vuole gestire. Per esempio, se seleziona
il primo dei due, viene rediretto al dialogo VerificaPassword, nel quale, per poterlo
modificare o eliminare, viene chiesta la password stabilita al momento della pubblicazione.
Se quest’ultima `e errata, allora scrive un messaggio di autenticazione fallita e si ritorna al
dialogo precedente. Altrimenti, il bot propone tre attivit`a, come mostrato in Figura 3.25:
aggiornare l’annuncio, eliminarlo, oppure tornare indietro nella sezione visualizzazione degli
annunci. Inoltre, `e importante far notare che, per scegliere un annuncio da gestire, `e
necessario premere il bottone ”Scegli”, il quale fa in modo che l’utente invi un messaggio
successivo contenente il suo identificatore; questa tecnica serve al bot per capire quale
CAPITOLO 3. PROGETTAZIONE DEL BOT 32
`e stato selezionato. Tutto ci`o non `e visibile in Fig. 3.25, perch´e Messenger presenta
un bug per i bottoni con Type ”ImBack”; Di fatto, viene inviato, come appena detto,
l’identificatore e non la stringa ”Scegli”.
Figura 3.25: Gestione annuncio: scelta dell’operazione.
Modifica
Se l’utente decide di eliminare, allora viene chiamato il dialogo EliminaAnnuncio, il quale
semplicemente rimuove l’annuncio e invoca VisualizzaAnnunci.
Se, invece, decide di aggiornare, si imposta, come prima cosa, con il metodo PrivateConver-
sationData.SetValue() la variabile di conversazione ”AggiornaAnnuncio” uguale ad ”Aggiorna”,
poi si attiva la sequenza di chiamate di dialoghi seguente:
• Si inizia con AggiornaAnnuncioNome, in cui il bot chiede all’utente se desidera modi-
ficare il nome nell’annuncio. In caso negativo, il nome vecchio viene recuperato tramite il
commando contesto.PrivateConversationDataTryGetValue(”NomeUser”, out nomeUser),
dove il valore di ”NomeUser” `e gi`a stato settato precedentemente in VisualizzaAnnunci-
Nome, e viene memorizzato nella variabile di conversazione ”Nome” con PrivateConversa-
tionData.SetValue(). Infine, viene invocato il dialogo successivo.
Se acconsente a modificare, allora viene chiamato il dialogo VendereNome, gi`a trattato
nella sezione ”Mettere un annuncio”, che stabilisce nuovamente il nome ed, essendo stata
la variabile ”AggiornaAnnuncio” precedentemente posta uguale alla stringa ”Aggiorna”,
termina con la chiamata al dialogo AggiornaAnnuncioCognome. Infatti, se si osserva il
codice C# di VendereNome posto in Appendice, nel metodo confermaNomeAsync (ri-
ga 27) si recupera il valore della variabile ”AggiornaAnnuncio” e lo si memorizza nella
stringa verificaAgg. Se tale valore `e uguale ad ”Aggiorna”, allora viene chiamato Aggior-
naAnnuncioCognome. Altrimenti, viene invocato VendereCognome, che appartiene alla
sequenza usata per l’inserimento. A seconda del valore di questa variabile si attiva o meno
la funzione di aggiornamento di VendereNome.
CAPITOLO 3. PROGETTAZIONE DEL BOT 33
• In AggiornaAnnuncioCognome il bot si comporta in modo analogo a prima, con la
differenza che d`a la possibilit`a qui di modificare il cognome. Se l’utente accetta, viene
chiamato dapprima VendereCognome e poi AggiornaAnnuncioLuogo. Se, invece, vuole
mantenere il valore attuale, esso viene recuperato e memorizzato con la stessa procedura
del passo precedente.
• In AggiornaAnnuncioLuogo il bot domanda all’utente se vuole cambiare la residenza. Se
egli risponde in modo negativo, il luogo vecchio viene recuperato con una query sulla tabella
ListaAnnunci, con cui si estrae il valore dell’attributo ”Luogo” del record corrispondente
all’identificatore dell’annuncio scelto per la gestione. In seguito, come `e stato fatto per i
punti precedenti, esso viene memorizzato in una variabile.
Se egli risponde in modo affermativo, allora viene chiamata la finestra VendereLuogo e si
opera come nei passi precedenti.
• AggiornaAnnuncioCellulare: il bot chiede allo user se vuole modificare il numero di
cellulare dell’annuncio. Se egli rifiuta, il valore vecchio viene recuperato e memorizzato co-
me in AggiornaAnnuncioCognome. Se accetta, viene chiamato il dialogo VendereNumCell
e si opera come nei passi precedenti;
• AggiornaAnnuncioDescrizione: il bot concede di cambiare la descrizione dell’annuncio.
Se l’utente `e contrario, allora il valore vecchio viene recuperato e memorizzato come in
AggiornaAnnuncioLuogo e AggiornaAnnuncioCellulare. Se `e favorevole, viene chiamato il
dialogo VendereDescrizione e si opera come nei passi precedenti;
• In AggiornaAnnuncioImmagine: domanda all’utente se vuole cambiare l’immagine del-
l’annuncio. In caso negativo, per recuperare la chiave e la password vecchia, si comporta
come nei punti precedenti. Infine, invoca AggiornaAnnuncio, concludendo cos`ı il proce-
dimento. In caso affermativo, viene chiamato questa volta VendereScegliereChiave, che
chiede all’utente se vuole caricare una nuova immagine, oppure togliere quella gi`a presen-
te. Se sceglie la prima delle due opzioni, allora viene aggiornato il nome dell’immagine
con uno nuovo e infine, a causa dell’impostazione della variabile ”AggiornaAnnuncio” fatta
prima, viene chiamato il dialogo AggiornaChiave, che aggiorna la tabella Chiavi con il
nuovo valore e termina chiamando VendereAggiungiImmagine; quest’ultimo carica l’imma-
gine e conclude instradando verso VendereSceglierePassword. Una volta scelta la password,
sempre per il fatto che la variabile di conversazione ”AggiornaAnnuncio” `e posta uguale
ad ”Aggiorna”, viene invocato l’ultimo dialogo della sequenza, ovvero quello con nome Ag-
giornaAnnuncio. Se sceglie la seconda opzione, si imposta il nome immagine uguale alla
stringa ”nessuna immagine” e viene chiamato VendereSceglierePassword, da cui si arriva
direttamente ad AggiornaAnnuncio;
• Infine, in AggiornaAnnuncio il bot effettua un’istruzione SQL di tipo ”UPDATE”, ag-
giornando i vecchi valori con quelli nuovi, che recupera, usando il comando PrivateCon-
versationData.TryGetValue(), dalle variabili di conversazione impostate nei dialoghi prece-
denti. Al suo termine, viene invocato nuovamente VisualizzaAnnunci, in cui l’utente vede
la lista dei suoi annunci con le eventuali modifiche.
CAPITOLO 3. PROGETTAZIONE DEL BOT 34
A titolo di esempio, si modifica l’annuncio considerato fino ad ora, lasciando invariati il nome,
il cognome, il luogo di residenza, il numero di cellulare e la descrizione (per questi si vedano
le figure 3.26 e 3.27), e rimuovendo solo l’immagine (Fig. 3.28). Successivamente, `e richiesto
l’aggiornamento della password (Fig. 3.29) e, infine, viene mostrata all’utente proprietario la
lista degli annunci con la modifica appena effettuata (Fig. 3.30).
Figura 3.26: Aggiornamento: nome, cognome, residenza.
Figura 3.27: Aggiornamento: numero cellulare e descrizione.
Figura 3.28: Aggiornamento: immagine.
CAPITOLO 3. PROGETTAZIONE DEL BOT 35
Figura 3.29: Completamento aggiornamento annuncio con la scelta della password.
Figura 3.30: Visualizzazione delle modifiche.
Poich´e la struttura dei dialoghi AggiornaAnnuncioNome, AggiornaAnnuncioCognome,
AggiornaAnnuncioCellulare `e la stessa, presentiamo qui il codice C# del primo:
CAPITOLO 3. PROGETTAZIONE DEL BOT 36
namespace BachecaAnnunci.Dialogs
{
public class AggiornaAnnuncioNome : IDialog<object>
{
public async Task StartAsync(IDialogContext contesto)
{
contesto.Wait(MessaggioRicevutoAsync);
}
private async Task MessaggioRicevutoAsync(IDialogContext contesto, IAwaitable<object> risultato)
{
PromptDialog.Confirm(contesto, aggiornareNome, "Vorresti aggiornare il tuo nome nell’annuncio?");
}
private async Task aggiornareNome(IDialogContext contesto, IAwaitable<bool> risultato)
{
bool conferma = await risultato;
if(conferma == true)
{
await contesto.Forward(new Dialogs.VendereNome(), this.metodosucc, conferma, CancellationToken.None);
}
else
{
string nomeUser = "";
contesto.PrivateConversationData.TryGetValue("NomeUser", out nomeUser);
contesto.PrivateConversationData.SetValue("Nome", nomeUser);
await contesto.Forward(new Dialogs.AggiornaAnnuncioCognome(), this.metodosucc, conferma, CancellationToken.None);
}
}
private async Task metodosucc(IDialogContext contesto, IAwaitable<object> risultato)
{
await contesto.PostAsync("Problema interno! Sei ritornato al dialogo precedente, per ripartire da esso digita qualcosa");
}
}
}
Un discorso analogo vale anche per AggiornaAnnuncioLuogo, AggiornaAnnuncioDescri-
zione e AggiornaAnnuncioImmagine, per cui mostriamo il codice C# del primo di questi:
namespace BachecaAnnunci.Dialogs
{
public class AggiornaAnnuncioLuogo : IDialog<object>
{
public async Task StartAsync(IDialogContext contesto)
{
contesto.Wait(MessaggioRicevutoAsync);
}
private async Task MessaggioRicevutoAsync(IDialogContext contesto, IAwaitable<object> risultato)
{
PromptDialog.Confirm(contesto, aggiornareLuogo, "Vorresti aggiornare il luogo dell’annuncio");
}
private async Task aggiornareLuogo(IDialogContext contesto, IAwaitable<bool> risultato)
{
string idAnnuncio = "";
contesto.PrivateConversationData.TryGetValue("IdAnnuncio", out idAnnuncio);
bool conferma = await risultato;
if (conferma == true)
{
await contesto.Forward(new Dialogs.VendereLuogo(), this.metodosucc, conferma, CancellationToken.None);
}
CAPITOLO 3. PROGETTAZIONE DEL BOT 37
else
{
string luogo = "";
SqlConnection connessione =
new SqlConnection(WebConFigurationManager.ConnectionStrings["stringaConnessione"].ConnectionString);
connessione.Open();
SqlCommand cmd = new SqlCommand(@"SELECT Luogo FROM ListaAnnunci WHERE IdAnnuncio = @IdAnnuncio", connessione);
cmd.Parameters.Add(new SqlParameter("@IdAnnuncio", idAnnuncio));
SqlDataReader leggi = cmd.ExecuteReader();
while (leggi.Read())
{
luogo = leggi["Luogo"].ToString();
}
contesto.PrivateConversationData.SetValue("Luogo", luogo);
connessione.Close();
await contesto.Forward(new Dialogs.AggiornaAnnuncioCellulare(), this.metodosucc, conferma, CancellationToken.None);
}
}
private async Task metodosucc(IDialogContext contesto, IAwaitable<object> risult)
{
await contesto.PostAsync("Problema interno! Sei ritornato al dialogo precedente, per ripartire da esso digita qualcosa");
}
}
}
Il codice di AggiornaAnnuncio `e simile a quello di ConfermaVendita, cambia solo l’istruzione
sql, che prevede qui l’utilizzo di ”UPDATE” al posto di ”INSERT”.
3.4.3 Trovare un annuncio
Dal dialogo principale, tramite il bottone ”Trovare un annuncio”, il bot indirizza l’utente al
dialogo di nome Comprare (Fig. 3.31); qui viene fatta una domanda sul tipo di ricerca che si
vuole fare, che `e di due modalit`a: libera o guidata.
Figura 3.31: Selezione del tipo di ricerca.
CAPITOLO 3. PROGETTAZIONE DEL BOT 38
Ricerca libera
In questa sezione del bot si cerca di costruire un semplice motore di ricerca, che ha lo scopo di
verificare la disponibilit`a in bacheca degli annunci richiesti dagli utenti. Tutto ci`o `e realizzato
dalla finestra di dialogo con nome RicercaLibera, che `e accessibile dal dialogo Comprare tramite
il bottone ”Ricerca libera”. Nella Figura 3.32 si pu`o vedere una rappresentazione grafica della
barra di ricerca.
Figura 3.32: Ricerca libera.
Il codice in C# che implementa il motore di ricerca `e molto corposo, dunque si preferisce spezzarlo
in pi`u parti, di cui si espone il contenuto e si mostra con delle immagini il funzionamento. La
parte di codice riguardante la selezione del testo dell’annuncio `e:
1 [Serializable]
2 public class RicercaLibera : IDialog<object>
3 {
4 public async Task StartAsync(IDialogContext contesto)
5 {
6 contesto.Wait(MessaggioRicevutoAsync);
7 }
8
9 private async Task MessaggioRicevutoAsync(IDialogContext contesto, IAwaitable<IActivity> risultato)
10 {
11 string idDest = "";
12 string nomeDest = "";
13 string idMitt = "";
14 string nomeMitt = "";
15 string urlServizio = "";
16 string idCanale = "";
17 string idConversazione = "";
18 string testo = "";
19 var attivit`a = await risultato as Activity;
20 if (!attivit`a.Text.Equals("indietro", StringComparison.InvariantCultureIgnoreCase))
21 {
22 contesto.PrivateConversationData.SetValue("Activity", attivit`a);
CAPITOLO 3. PROGETTAZIONE DEL BOT 39
23 testo = attivit`a.Text;
24 idDest = attivit`a.From.Id;
25 nomeDest = attivit`a.From.Name;
26 idMitt = attivit`a.Recipient.Id;
27 nomeMitt = attivit`a.Recipient.Name;
28 urlServizio = attivit`a.ServiceUrl;
29 idCanale = attivit`a.ChannelId;
30 idConversazione = attivit`a.Conversation.Id;
31 contesto.PrivateConversationData.SetValue<string>("Interesse", testo);
32 contesto.PrivateConversationData.SetValue<string>("IdDest", idDest);
33 contesto.PrivateConversationData.SetValue<string>("NomeDest", nomeDest);
34 contesto.PrivateConversationData.SetValue<string>("IdMitt", idMitt);
35 contesto.PrivateConversationData.SetValue<string>("NomeMitt", nomeMitt);
36 contesto.PrivateConversationData.SetValue<string>("UrlServizio", urlServizio);
37 contesto.PrivateConversationData.SetValue<string>("IdCanale", idCanale);
38 contesto.PrivateConversationData.SetValue<string>("IdConversazione", idConversazione);
39 await contesto.PostAsync($"Sei interessato al seguente annuncio: {testo}");
40 PromptDialog.Confirm(contesto, confermaAsync, "Confermi l’annuncio?");
41 }
42 else if (attivit`a.Text.Equals("indietro", StringComparison.InvariantCultureIgnoreCase))
43 {
44 await contesto.Forward(new Dialogs.Comprare(), this.metodosucc, testo, CancellationToken.None);
45 }
46 }
47 $
Dal codice `e possibile vedere che il bot aspetta che l’utente ricerchi l’annuncio, digitando un
messaggio (riga 19), dal quale, come prima cosa, ricava alcune informazioni (da riga 23 a 30:
come l’identificatore di conversazione e canale, e altro ancora) che sono necessarie per creare un
promemoria nel caso in cui l’oggetto desiderato non si trovi gi`a in bacheca.
In seguito, se l’utente `e d’accordo con il testo scelto, fa la ricerca e ne mostra i risultati (Figure
3.33, 3.34 e 3.35), altrimenti lo invita ad inserire uno nuovo.
Figura 3.33: Scelta del testo dell’annuncio.
Figura 3.34: Visualizzazione del primo risultato.
CAPITOLO 3. PROGETTAZIONE DEL BOT 40
Figura 3.35: Visualizzazione del secondo risultato.
Un esempio di codice in C# adoperato per la ricerca e la visualizzazione degli annunci `e il
seguente:
1 private async Task confermaAsync(IDialogContext contesto, IAwaitable<bool> risultato)
2 {
3 string testo = "";
4 string idConversazione = "";
5 string query = "";
6 var attivit`a = new Activity();
7 bool conferma = await risultato;
8 if (conferma == true)
9 {
10 contesto.PrivateConversationData.TryGetValue("Interesse", out testo);
11 contesto.PrivateConversationData.TryGetValue("IdConversazione", out idConversazione);
12 contesto.PrivateConversationData.TryGetValue("Activity", out attivit`a);
13 SqlConnection connessione = new
14 SqlConnection(WebConFigurationManager.ConnectionStrings["stringaConnessione"].ConnectionString);
15 connessione.Open();
16 SqlCommand cmd =
17 new SqlCommand(@"SELECT * FROM ListaAnnunci WHERE REPLACE (REPLACE(REPLACE(Descrizione, ’,’, ’’), ’)’, ’’), ’(’, ’’) LIKE ’%’
18 + @Testo + ’%’", connessione);
19 cmd.Parameters.Add(new SqlParameter("@Testo", testo));
20 query = cmd.CommandText;
21 var risposta = contesto.MakeMessage();
22 risposta.AttachmentLayout = AttachmentLayoutTypes.Carousel;
23 risposta.Attachments = new List<Attachment>();
24 List<string> annunci = new List<string>();
25 contesto.PrivateConversationData.SetValue("Query", query);
26 SqlDataReader leggi = cmd.ExecuteReader();
27 while (leggi.Read())
28 {
29 annunci.Add(leggi["IdAnnuncio"].ToString());
30 string nomeImmagine = leggi["NomeImmagine"].ToString();
31 List<CardImage> immagini = new List<CardImage>();
32 CloudStorageAccount accountArchiviazione = CloudStorageAccount.Parse(ConFigurationManager.AppSettings["ChiaveArchiviazione"]);
33 CloudBlobClient clientBlob = accountArchiviazione.CreateCloudBlobClient();
34 CloudBlobContainer container = clientBlob.GetContainerReference("immagini");
35 if (nomeImmagine != "nessuna immagine")
36 {
37 CloudBlockBlob bloccoBlob = container.GetBlockBlobReference(nomeImmagine);
38 var urlBlob = bloccoBlob.Uri.AbsoluteUri;
39 immagini.Add(new CardImage(urlBlob));
40 }
41
42 List<CardAction> bottoni = new List<CardAction>()
43 {
44 new CardAction(){ Title = "Compra", Type=ActionTypes.ImBack, Value=leggi["IdAnnuncio"].ToString() },
45 new CardAction(){ Title = "Chiama", Type=ActionTypes.OpenUrl, Value=leggi["Cellulare"].ToString() },
46
47 };
48
49 var heroCard = new HeroCard
50 {
51 Title = leggi["Nome"].ToString() + " " + leggi["Cognome"].ToString() + ", " + leggi["Luogo"].ToString(),
CAPITOLO 3. PROGETTAZIONE DEL BOT 41
52 Text = leggi["Descrizione"].ToString(),
53 Images = immagini,
54 Buttons = bottoni
55
56 };
57 risposta.Attachments.Add(heroCard.ToAttachment());
58 }
59 if (!leggi.HasRows)
60 {
61 await contesto.PostAsync("Nessun record trovato!");
62 await contesto.Forward(new tabellaConversazione(), this.metodosucc, risposta, CancellationToken.None);
63 connessione.Close();
64 }
65 else if (leggi.HasRows)
66 {
67 contesto.PrivateConversationData.SetValue("ListaAnnunci", annunci);
68 await contesto.PostAsync("Ecco qua il risultato della ricerca! Puoi scegliere tra " +
69 "uno degli annunci solo premendo sugli appositi tasti, " +
70 "oppure annullare la ricerca digitando la parola ’indietro’");
71 contesto.PrivateConversationData.SetValue("ListaAnnunci", annunci);
72 await contesto.PostAsync(risposta);
73 connessione.Close();
74 contesto.Wait(comprareAsync);
75 }
76 }
77 else if (conferma == false)
78 {
79 await contesto.PostAsync("Scrivi un altro annuncio");
80 contesto.Wait(MessaggioRicevutoAsync);
81 }
82 }
Vediamo ora in dettaglio le operazioni condotte dal bot per la ricerca e visualizzazione degli an-
nunci. Come prima cosa, esso apre una connessione tcp sulla porta 1433 con il server su Azure e
interroga la tabella ListaAnnunci del database con la query alla riga 16 del codice. Quest’ultima,
essenzialmente, controlla se il testo digitato dall’utente `e presente in qualche descrizione degli
annunci in bacheca. In caso di esito positivo, inizializza una lista di stringhe, in cui inserisce
gli identificatori risultanti, che passa poi al metodo successivo. In seguito, crea un messaggio
di risposta, di cui pone la propriet`a Attachments uguale ad una nuova lista di allegati, in cui
inserisce, per ogni risultato che trova, una Hero Card con testo le informazioni degli annunci,
come il nome, il cognome, il luogo di residenza del proprietario e la descrizione, due bottoni ed
una eventuale immagine. Viene settato anche il layout della lista, preferendo una visualizzazione
orizzontale degli annunci (carosello). Infine, invia la risposta con tutti gli allegati.
I dati personali citati prima e la chiave dell’immagine sono ottenuti, creando un oggetto della
classe SqlDataReader, in questo caso di nome ”leggi”, e sui cui viene usato il metodo leg-
gi[”NomeAttributo”].ToString(). Per estrarre l’immagine, si usa un ulteriore comando, che
preleva dall’account di archiviazione su Azure l’oggetto ”blob” corrispondente al nome ottenuto
prima. Nella sezione ”Mettere un annuncio” era stato detto che gli annunci senza immagini
presentano nel database, come valore dell’attributo ”NomeImmagine”, la stringa ”nessuna im-
magine”. Se al posto di quest’ultimo fosse stato lasciato NULL, per via del metodo usato per
estrarre i dati di prima, si avrebbe avuto, alla riga 30 del codice, un oggetto di tipo stringa,
ovvero nomeImmagine, uguagliato ad un valore NULL di database, le quali sono due entit`a
completamente diverse (una stringa nulla `e diversa da un valore NULL di un DB) e ci`o avrebbe
prodotto degli errori durante la fase di compilazione del programma.
CAPITOLO 3. PROGETTAZIONE DEL BOT 42
Riprendendo la descrizione del funzionamento del dialogo, come suggerisce anche il bot nel
messaggio prima di mostrare i risultati, si nota che `e possibile scegliere uno degli annunci esclu-
sivamente premendo il bottone intitolato ”Compra” corrispondente, che genera una risposta
dell’utente contenente l’identificatore dell’annuncio deciso. In questo modo il bot lo distingue da
tutti gli altri. Tale procedura non `e visibile in Figura 3.36, perch´e Messenger presenta un bug
per le carte d’azione con tipo ”ImBack”, viene in realt`a inviato l’identificatore dell’annuncio (in
questo esempio, ”galaxys6”) e non la stringa ”Compra”. Infatti, si nota che, con l’utilizzo dell’e-
mulatore, la procedura appena descritta `e visibile chiaramente, confermando in questo modo la
presenza del bug (Fig. 3.37).
Figura 3.36: Scelta dell’annuncio con Messenger.
Figura 3.37: Scelta dell’annuncio con l’emulatore.
Un esempio di codice C# per scegliere un annuncio `e:
CAPITOLO 3. PROGETTAZIONE DEL BOT 43
private async Task comprareAsync(IDialogContext contesto, IAwaitable<IActivity> risultato)
{
List<string> annunci = new List<string>();
string idAnnuncio = "";
contesto.PrivateConversationData.TryGetValue("ListaAnnunci", out annunci);
var attivit`a = await risultato as Activity;
if (annunci.Contains(attivit`a.Text))
{
idAnnuncio = attivit`a.Text;
contesto.PrivateConversationData.SetValue("IdAnnuncio", idAnnuncio);
await contesto.Forward(new Dialogs.AnnuncioAcquistato(), this.metodosucc, attivit`a, CancellationToken.None);
}
else if (!annunci.Contains(attivit`a.Text) && !attivit`a.Text.Equals("indietro", StringComparison.InvariantCultureIgnoreCase))
{
await contesto.PostAsync("Ricordati: puoi scegliere tra uno degli annunci " +
"solo premendo sugli appositi tasti oppure annullare la ricerca digitando" +
" la parola ’indietro’");
contesto.Wait(comprareAsync);
}
else if (attivit`a.Text.Equals("indietro", StringComparison.InvariantCultureIgnoreCase))
{
await contesto.PostAsync("Sei stato rediretto al main, digita qualcosa per riattivarmi!");
contesto.Call(new Dialogs.DialogoPrincipale(), this.metodosucc);
}
}
A questo punto, il bot recupera la lista con gli identificatori degli annunci riempita precedente-
mente, e se quello scelto dall’utente non vi appartiene, allora lo invita a ripetere correttamente
la procedura. Altrimenti lo salva e procede con la chiamata del dialogo AnnuncioAcquistato,
che, come suggerisce il nome stesso, gestisce le operazioni successive al suo acquisto. Qui, il bot
concede due possibilit`a (vedere Fig. 3.36): visualizzare eventuali annunci pubblicati dallo stes-
so proprietario di quello appena comprato, oppure tornare semplicemente al dialogo principale.
Scegliendo la prima opzione, attraverso la chiamata della finestra di dialogo con nome RecI-
dAnnNomeImmPass, gi`a introdotto nella sezione ”Mettere un annuncio”, vengono aggiornate
le tabelle IdAnnunci, Chiavi, PasswordAnnunci, eliminando tutti i record che fanno riferimento
all’annuncio comprato, cio`e il suo identificatore, il nome dell’eventuale immagine allegata e la
password. Poi, per vedere se il proprietario ne ha pubblicati di altri, prima di rimuoverlo defi-
nitivamente dal sistema, si recuperano con una query il nome e cognome contenuti in esso, con
cui si interroga successivamente la tabella ListaAnnunci e si mostrano gli eventuali risultati.
Scegliendo la seconda opzione, viene utilizzato nuovamente la finestra di dialogo citata prima,
per aggiornare le tabelle, e subito dopo viene eliminato l’annuncio.
E’ importante far notare che il dialogo in questione ha una natura polivalente, perch´e `e usato
per svolgere il suo compito sia quando si decide di annullare la pubblicazione di un annuncio,
che quando si vuole eliminarlo dalla bacheca. Tale effetto `e reso grazie ad una variabile, che
specifica quale delle due operazioni effettuare (vedere Appendice per maggiori dettagli).
Oltre al bottone ”Compra”, si potrebbe premere quello con nome ”Chiama”, il quale permette
di contattare il proprietario dell’annuncio. Se si utilizza il Chatbot con un PC, si apre di default
l’applicazione Skype, mentre, se lo si adopera con un cellulare, si apre il tasto di chiamata stan-
dard. Ad esempio, scegliendo di contattare il proprietario del primo annuncio in Figura 3.36, si
apre la schermata seguente:
CAPITOLO 3. PROGETTAZIONE DEL BOT 44
Figura 3.38: Contattare il proprietario tramite cellulare.
Creazione di un promemoria
Quando nel dialogo della ricerca libera non c’`e nessun risultato per l’annuncio richiesto, si viene
indirizzati al dialogo di nome tabellaConversazione, nel quale all’utente viene chiesto se vuole
creare un promemoria, per essere avvisato nel caso in cui l’oggetto richiesto diventi disponibile
in bacheca. In una conversazione normale, i bot attendono un messaggio da parte degli utenti e
non succede quasi mai che scrivano per primi. L’infrastruttura Bot Framework `e fatta in questo
modo, per evitare che vengano mandati dei messaggi ritenuti ”spam”. Uno dei pochi casi in cui
ci`o `e consentito `e quando si inviano dei promemoria (sempre chiedendo prima il consenso all’u-
tente), e anche qui alcuni canali impongono delle restrizioni sul numero massimo che `e possibile
trasmettere.
Esempio di funzionamento: si suppone di voler trovare degli annunci in cui si offrono lezioni
per la lingua inglese (Fig. 3.39). Dal momento che in bacheca non c’`e nulla a riguardo, il bot
chiede all’utente se vuole creare un promemoria. In caso negativo, egli viene semplicemente
riportato al dialogo principale. In caso positivo, invece il bot gli risponde come in Figura 3.40
e nel frattempo esso ha gi`a recuperato le informazioni dell’utente salvate prima e inserite con
un’istruzione SQL di tipo ”INSERT” nella tabella ConversazioneUtente (Fig. 3.41).
CAPITOLO 3. PROGETTAZIONE DEL BOT 45
Figura 3.39: Ricerca di lezioni di lingua inglese.
Figura 3.40: Attivazione di un promemoria.
Figura 3.41: Tabella con i dati degli utenti.
Tale elenco `e in continuo aggiornamento grazie all’utilizzo di un’applicazione distribuita su Azure,
ovvero il servizio di nome webjob, che conduce in modo ricorrente le seguenti attivit`a: come prima
cosa, preleva da tutti i record della tabella ConversazioneUtente il valore degli attributi di
CAPITOLO 3. PROGETTAZIONE DEL BOT 46
nome Query e Parametro, che usa per generare una nuova interrogazione sulla ListaAnnunci.
Se il risultato della query `e negativo, cio`e l’annuncio cercato prima non `e ancora stato pubblicato,
passa al record successivo. Se il risultato della query `e positivo, inserisce in una nuova tabella di
nome TabellaAvvisi le informazioni degli utenti che devono essere avvisati della disponibilit`a
in bacheca dell’annuncio richiesto prima. Infine, per maggiore scalabilit`a del servizio, elimina
dalla tabella ConversazioneUtente i record relativi agli utenti gi`a posti in fase di avviso.
Si aggiunge ora a scopo illustrativo un annuncio con descrizione ”lezioni di inglese” e si vede come
l’azione del web job aggiorni le tabelle sopra citate. ConversazioneUtente prima `e costituita come
in Figura 3.42, poi come in 3.44. TabellaAvvisi, dall’essere come in Figura 3.43, diventa come in
3.45.
Figura 3.42: ConversazioneUtente (prima di azionare il webjob).
Figura 3.43: TabellaAvvisi (prima di azionare il webjob).
Figura 3.44: ConversazioneUtente (dopo aver azionato il webjob).
Figura 3.45: TabellaAvvisi (dopo aver azionato il webjob).
Nel frattempo che avvengono tali modifiche, il bot ha gi`a chiamato la finestra MessaggioProat-
tivo, che resta in attesa per un certo periodo di tempo, lasciando l’utente libero di fare altro al
momento, poi si attiva, interropendo il dialogo corrente, e interroga TabellaAvvisi (dove ci sono
le informazioni degli utenti da avvisare). Per ogni record di quest’ultima ricava il valore degli
attributi IdConversazione e idCanale, che passa come parametri alla successiva chiamata del
dialogo IniziaConversazione. Quest’ultimo ha la funzione di comunicare agli user la disponi-
bilit`a dell’annuncio cercato prima; infine, li elimina dalla tabella degli avvisi per comunicazioni
future. Normalmente, si pone il tempo di attesa di MessaggioProattivo di qualche giorno, dando
CAPITOLO 3. PROGETTAZIONE DEL BOT 47
cos`ı tempo a sufficienza perch´e si pubblichi l’annuncio richiesto.
Quest’ultimo `e fatto in modo tale che, se uno stesso utente attiva pi`u di un promemoria per il
medesimo oggetto, lo avvisa solo una volta quando l’annuncio corrispondente viene pubblicato
nella bacheca.
Riprendendo l’esempio di prima, poich´e ora in bacheca `e presente un annuncio in cui si offrono
lezioni di inglese, a noi, che lo avevamo richiesto, arriva un messaggio di avviso (Fig. 3.46), che
interrompe il dialogo corrente fino a quando la sua azione non `e terminata.
Figura 3.46: Messaggio proattivo.
Ricerca guidata
Dal dialogo Comprare, di cui si `e discusso nelle sezioni precedenti, tramite il bottone ”Ricerca
guidata” (Fig. 3.47), si attiva la conversazione guidata per aiutare l’utente a scegliere un oggetto
tra un PC, un Cellulare e Altro, di cui verifica successivamente se ci sono annunci in bacheca.
CAPITOLO 3. PROGETTAZIONE DEL BOT 48
Figura 3.47: Ricerca guidata.
Figura 3.48: Dialogo per consigliare oggetti della categoria Altro.
Per esempio, se l’utente desidera comprare un oggetto della categoria ”Altro” (Fig. 3.48), si
avvia la seguente sequenza di finestre di dialogo:
• In ComprareOggettoCategoria, ComprareOggettoDimensione, ComprareOgget-
toPeso e ComprareOggettoPrezzo l’utente, rispettivamente, opera una scelta sulla
categoria, dimensione e prezzo di interesse.
• In ComprareOggettoNome, a seconda delle decisioni fatte nei passi precedenti, il bot
propone un lista di nomi di oggetti possibili, di cui egli ne deve decidere una, per poi
cercare suoi eventuali annunci nel dialogo figlio ComprareOggettoRicercaNome; in
CAPITOLO 3. PROGETTAZIONE DEL BOT 49
caso di esito positivo, l’utente pu`o scegliere di acquistare uno di essi (analogamente a
quanto visto per la ricerca libera, solo premendo il bottone ”Compra” corrispondente),
oppure pu`o ritornare al dialogo iniziale (scelta della ”Categoria”). Se opta per la prima
opzione, viene rediretto al dialogo AnnuncioAcquistato, e da qui si procede come visto
per la ricerca libera.
In ognuna di queste finestre, per mostrare la lista delle scelte ammissibili, si interroga la tabella
di nome Oggetto, estraendo da essa i valori degli attributi corrispondenti alla funzione del dia-
logo in questione. Per esempio, in ComprareOggettoCategoria si estraggono tutti i valori, senza
ripetizioni, dell’attributo ”Categoria”, dal momento che la funzione del dialogo `e di far scegliere
una categoria di interesse. In Figura 3.49 `e possibile vedere la struttura della suddetta tabella,
di cui sono rappresentati solo i primi 13 record dei totali 73.
Per popolare la tabella sono stati inseriti i nomi degli oggetti con le seguenti informazioni
ammesse:
• Categorie: Casa e cucina, Elettronica, Fai da te e giardinaggio, Libri, Sport e
tempo libero.
• Dimensioni: grande, media, piccola.
• Pesi: minore di 1 Kg, tra 1 e 3 Kg, maggiore di 3 Kg.
• Range di prezzo: minore di 20 euro, maggiore di 20 euro
• Per ogni oggetto, nella colonna ”UrlInfo”, `e stato deciso anche l’url di un sito web, dove
l’utente pu`o trovare informazioni a riguardo. La tabella creata rappresenta una possibile
soluzione del modo in cui il Chatbot prende i dati, la quale non `e completa; l’ideale sarebbe
avere a disposizione un database pronto e contenente pi`u elementi, per esempio fornito da
un’azienda intenzionata ad investire sui bot. Nel mio caso ho dovuto scegliere personal-
mente i dati da inserire, andando a fare delle ricerche approfondite per capire a cosa una
persona poteva essere interessata.
Figura 3.49: Tabella con gli oggetti di categorie diverse.
In ognuno, ad eccezione di quello per stabilire la categoria dell’oggetto, l’interrogazione `e in-
fluenzata dalle scelte dei dialoghi precedenti. Per esempio, in ComprareOggettoPeso si mostra il
Realizzazione di un ChatBot sulla piattaforma Messenger di Facebook per l'informatizzazione di una bacheca annunci-tesi
Realizzazione di un ChatBot sulla piattaforma Messenger di Facebook per l'informatizzazione di una bacheca annunci-tesi
Realizzazione di un ChatBot sulla piattaforma Messenger di Facebook per l'informatizzazione di una bacheca annunci-tesi
Realizzazione di un ChatBot sulla piattaforma Messenger di Facebook per l'informatizzazione di una bacheca annunci-tesi
Realizzazione di un ChatBot sulla piattaforma Messenger di Facebook per l'informatizzazione di una bacheca annunci-tesi
Realizzazione di un ChatBot sulla piattaforma Messenger di Facebook per l'informatizzazione di una bacheca annunci-tesi
Realizzazione di un ChatBot sulla piattaforma Messenger di Facebook per l'informatizzazione di una bacheca annunci-tesi
Realizzazione di un ChatBot sulla piattaforma Messenger di Facebook per l'informatizzazione di una bacheca annunci-tesi
Realizzazione di un ChatBot sulla piattaforma Messenger di Facebook per l'informatizzazione di una bacheca annunci-tesi
Realizzazione di un ChatBot sulla piattaforma Messenger di Facebook per l'informatizzazione di una bacheca annunci-tesi
Realizzazione di un ChatBot sulla piattaforma Messenger di Facebook per l'informatizzazione di una bacheca annunci-tesi
Realizzazione di un ChatBot sulla piattaforma Messenger di Facebook per l'informatizzazione di una bacheca annunci-tesi
Realizzazione di un ChatBot sulla piattaforma Messenger di Facebook per l'informatizzazione di una bacheca annunci-tesi
Realizzazione di un ChatBot sulla piattaforma Messenger di Facebook per l'informatizzazione di una bacheca annunci-tesi
Realizzazione di un ChatBot sulla piattaforma Messenger di Facebook per l'informatizzazione di una bacheca annunci-tesi
Realizzazione di un ChatBot sulla piattaforma Messenger di Facebook per l'informatizzazione di una bacheca annunci-tesi
Realizzazione di un ChatBot sulla piattaforma Messenger di Facebook per l'informatizzazione di una bacheca annunci-tesi
Realizzazione di un ChatBot sulla piattaforma Messenger di Facebook per l'informatizzazione di una bacheca annunci-tesi
Realizzazione di un ChatBot sulla piattaforma Messenger di Facebook per l'informatizzazione di una bacheca annunci-tesi
Realizzazione di un ChatBot sulla piattaforma Messenger di Facebook per l'informatizzazione di una bacheca annunci-tesi
Realizzazione di un ChatBot sulla piattaforma Messenger di Facebook per l'informatizzazione di una bacheca annunci-tesi
Realizzazione di un ChatBot sulla piattaforma Messenger di Facebook per l'informatizzazione di una bacheca annunci-tesi
Realizzazione di un ChatBot sulla piattaforma Messenger di Facebook per l'informatizzazione di una bacheca annunci-tesi
Realizzazione di un ChatBot sulla piattaforma Messenger di Facebook per l'informatizzazione di una bacheca annunci-tesi
Realizzazione di un ChatBot sulla piattaforma Messenger di Facebook per l'informatizzazione di una bacheca annunci-tesi
Realizzazione di un ChatBot sulla piattaforma Messenger di Facebook per l'informatizzazione di una bacheca annunci-tesi

More Related Content

Similar to Realizzazione di un ChatBot sulla piattaforma Messenger di Facebook per l'informatizzazione di una bacheca annunci-tesi

Manuale Aula Virtuale
Manuale Aula VirtualeManuale Aula Virtuale
Manuale Aula VirtualeFormaLms
 
Generazione automatica diagrammi di rete con template pptx
Generazione automatica diagrammi di rete con template pptxGenerazione automatica diagrammi di rete con template pptx
Generazione automatica diagrammi di rete con template pptxGiacomoZorzin
 
Progettazione e realizzazione di un nodo di elaborazione per il rilevamento d...
Progettazione e realizzazione di un nodo di elaborazione per il rilevamento d...Progettazione e realizzazione di un nodo di elaborazione per il rilevamento d...
Progettazione e realizzazione di un nodo di elaborazione per il rilevamento d...ciakana
 
Realizzazione di un ChatBot sulla piattaforma Messenger di Facebook per l'inf...
Realizzazione di un ChatBot sulla piattaforma Messenger di Facebook per l'inf...Realizzazione di un ChatBot sulla piattaforma Messenger di Facebook per l'inf...
Realizzazione di un ChatBot sulla piattaforma Messenger di Facebook per l'inf...Xhoi Kerbizi
 
Laboratorio internet 6: Piano di qualità
Laboratorio internet 6: Piano di qualitàLaboratorio internet 6: Piano di qualità
Laboratorio internet 6: Piano di qualitàRoberto Polillo
 
Guida autovalutazione-3-0
Guida autovalutazione-3-0Guida autovalutazione-3-0
Guida autovalutazione-3-0Paola Severi
 
Angular kit e Design system del Paese - Meetup ngRome 30 Gennaio 2023
Angular kit e Design system del Paese - Meetup ngRome 30 Gennaio 2023Angular kit e Design system del Paese - Meetup ngRome 30 Gennaio 2023
Angular kit e Design system del Paese - Meetup ngRome 30 Gennaio 2023AndreaStagi3
 
Web designer vs Web developer
Web designer vs Web developerWeb designer vs Web developer
Web designer vs Web developerGiuseppe Vizzari
 
Costruire un client .NET per SugarCRM
Costruire un client .NET per SugarCRMCostruire un client .NET per SugarCRM
Costruire un client .NET per SugarCRMAntonio Musarra
 
Analisi e sviluppo di un sistema collaborativo simultaneo per la modifica di ...
Analisi e sviluppo di un sistema collaborativo simultaneo per la modifica di ...Analisi e sviluppo di un sistema collaborativo simultaneo per la modifica di ...
Analisi e sviluppo di un sistema collaborativo simultaneo per la modifica di ...Filippo Muscolino
 
Liferay: Esporre Web Services Custom
Liferay: Esporre Web Services CustomLiferay: Esporre Web Services Custom
Liferay: Esporre Web Services CustomAntonio Musarra
 
Scelta Software Elearning
Scelta Software ElearningScelta Software Elearning
Scelta Software ElearningFormaLms
 
Alessio Iafrate - Utilizziamo il Bot Framework per realizzare il nostro primo...
Alessio Iafrate - Utilizziamo il Bot Framework per realizzare il nostro primo...Alessio Iafrate - Utilizziamo il Bot Framework per realizzare il nostro primo...
Alessio Iafrate - Utilizziamo il Bot Framework per realizzare il nostro primo...Codemotion
 
Come utilizzare il bot framework
Come utilizzare il bot frameworkCome utilizzare il bot framework
Come utilizzare il bot frameworkAlessio Iafrate
 
15 - Web designer vs Web developer
15 - Web designer vs Web developer15 - Web designer vs Web developer
15 - Web designer vs Web developerGiuseppe Vizzari
 
B Human Progetti di Stage 2009
B Human Progetti di Stage 2009B Human Progetti di Stage 2009
B Human Progetti di Stage 2009B Human Srl
 

Similar to Realizzazione di un ChatBot sulla piattaforma Messenger di Facebook per l'informatizzazione di una bacheca annunci-tesi (20)

Manuale Aula Virtuale
Manuale Aula VirtualeManuale Aula Virtuale
Manuale Aula Virtuale
 
Generazione automatica diagrammi di rete con template pptx
Generazione automatica diagrammi di rete con template pptxGenerazione automatica diagrammi di rete con template pptx
Generazione automatica diagrammi di rete con template pptx
 
Web20
Web20Web20
Web20
 
Progettazione e realizzazione di un nodo di elaborazione per il rilevamento d...
Progettazione e realizzazione di un nodo di elaborazione per il rilevamento d...Progettazione e realizzazione di un nodo di elaborazione per il rilevamento d...
Progettazione e realizzazione di un nodo di elaborazione per il rilevamento d...
 
Realizzazione di un ChatBot sulla piattaforma Messenger di Facebook per l'inf...
Realizzazione di un ChatBot sulla piattaforma Messenger di Facebook per l'inf...Realizzazione di un ChatBot sulla piattaforma Messenger di Facebook per l'inf...
Realizzazione di un ChatBot sulla piattaforma Messenger di Facebook per l'inf...
 
Tesi Forcolin Fabio
Tesi Forcolin FabioTesi Forcolin Fabio
Tesi Forcolin Fabio
 
Laboratorio internet 6: Piano di qualità
Laboratorio internet 6: Piano di qualitàLaboratorio internet 6: Piano di qualità
Laboratorio internet 6: Piano di qualità
 
Guida autovalutazione-3-0
Guida autovalutazione-3-0Guida autovalutazione-3-0
Guida autovalutazione-3-0
 
Angular kit e Design system del Paese - Meetup ngRome 30 Gennaio 2023
Angular kit e Design system del Paese - Meetup ngRome 30 Gennaio 2023Angular kit e Design system del Paese - Meetup ngRome 30 Gennaio 2023
Angular kit e Design system del Paese - Meetup ngRome 30 Gennaio 2023
 
Web designer vs Web developer
Web designer vs Web developerWeb designer vs Web developer
Web designer vs Web developer
 
Costruire un client .NET per SugarCRM
Costruire un client .NET per SugarCRMCostruire un client .NET per SugarCRM
Costruire un client .NET per SugarCRM
 
Analisi e sviluppo di un sistema collaborativo simultaneo per la modifica di ...
Analisi e sviluppo di un sistema collaborativo simultaneo per la modifica di ...Analisi e sviluppo di un sistema collaborativo simultaneo per la modifica di ...
Analisi e sviluppo di un sistema collaborativo simultaneo per la modifica di ...
 
Liferay: Esporre Web Services Custom
Liferay: Esporre Web Services CustomLiferay: Esporre Web Services Custom
Liferay: Esporre Web Services Custom
 
Scelta Software Elearning
Scelta Software ElearningScelta Software Elearning
Scelta Software Elearning
 
Web2.0.2008
Web2.0.2008Web2.0.2008
Web2.0.2008
 
Alessio Iafrate - Utilizziamo il Bot Framework per realizzare il nostro primo...
Alessio Iafrate - Utilizziamo il Bot Framework per realizzare il nostro primo...Alessio Iafrate - Utilizziamo il Bot Framework per realizzare il nostro primo...
Alessio Iafrate - Utilizziamo il Bot Framework per realizzare il nostro primo...
 
Come utilizzare il bot framework
Come utilizzare il bot frameworkCome utilizzare il bot framework
Come utilizzare il bot framework
 
15 - Web designer vs Web developer
15 - Web designer vs Web developer15 - Web designer vs Web developer
15 - Web designer vs Web developer
 
Web 2.0 2010 Istat
Web 2.0 2010 IstatWeb 2.0 2010 Istat
Web 2.0 2010 Istat
 
B Human Progetti di Stage 2009
B Human Progetti di Stage 2009B Human Progetti di Stage 2009
B Human Progetti di Stage 2009
 

Realizzazione di un ChatBot sulla piattaforma Messenger di Facebook per l'informatizzazione di una bacheca annunci-tesi

  • 1. UNIVERSIT`A DEGLI STUDI DI TRIESTE DIPARTIMENTO DI INGEGNERIA ELETTRONICA E INFORMATICA Tesi di Laurea Triennale in Ingegneria Elettronica e Informatica Realizzazione di un ChatBot sulla piattaforma Messenger di Facebook per l’informatizzazione di una bacheca annunci Candidato: Xhoi Kerbizi Relatore: Eric Medvet A.A 2016-2017
  • 2. Abstract Nella tesi `e stato studiato un modo per realizzare un Chatbot che informatizza una comune bacheca di annunci. Tale applicazione, che `e stata collegata alla piattaforma Messenger di Face- book, d`a la possibilit`a agli utenti che si connettono di inserire e gestire i propri annunci; inoltre una sezione `e stata riservata alla ricerca degli annunci pubblicati da altri utilizzatori della chat. Per rendere la ricerca pi`u incline alle esigenze degli utenti, `e stato progettato, oltre al comune motore di ricerca, anche una conversazione in cui il bot con una serie di domande comprende i loro interessi e in seguito cerca l’annuncio corrispondente. Infine, la comunicazione tra bot e utente `e semplificato con una procedura di dialoghi guidati, minimizzando l’incomprensione tra le due parti. In ogni momento l’utente pu`o fermare la conversazione, tornare indietro e chiedere aiuto al bot sul suo funzionamento e informazioni sulle opportunit`a offerte.
  • 3. Indice Abstract i Introduzione iii 1 Informazioni generali sui Chatbot 1 1.1 Come si realizza un Bot . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 2 Creazione del bot 3 2.1 Elementi fondamentali dell’SDK per .NET . . . . . . . . . . . . . . . . . . . . . . 3 2.1.1 Il Connettore . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 2.1.2 Le Attivit`a . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 2.1.3 Gli Allegati . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 2.1.4 I Dialoghi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 2.1.5 Il ciclo di vita dei dialoghi . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 2.1.6 Il FormFlow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 2.1.7 Gestire i dati di una conversazione . . . . . . . . . . . . . . . . . . . . . . 10 2.1.8 Aggiungere azioni suggerite . . . . . . . . . . . . . . . . . . . . . . . . . . 11 3 Progettazione del bot 14 3.1 Dialogo Principale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 3.2 Controllore dei messaggi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 3.3 Gestore globale dei messaggi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 3.4 Dialoghi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 3.4.1 Mettere un annuncio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 3.4.2 Gestire gli annunci . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 3.4.3 Trovare un annuncio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 3.4.4 Vetrina bacheca . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 4 Pubblicazione del bot 61 5 Conclusione 63 A Codici di alcuni dialoghi 64 Bibliografia 74 ii
  • 4. Introduzione Quando adoperiamo il motore di ricerca di un qualsiasi sito specializzato nella pubblicazione di diversi tipi di annunci realizzati da utenti, digitiamo ci`o che stiamo cercando e premiamo il tasto invio per visualizzarne i risultati. Spesso per`o non abbiamo le idee chiare e quindi `e comodo avere un’applicazione, che attraverso una serie di domande, possa comprendere i nostri interessi e poi cercare se ci sono annunci adatti. Lo scopo primario della tesi `e di realizzare questo scenario tramite un Chatbot, un’applicazione web che interagisce con gli utenti connessi attraverso delle semplici conversazioni. Il dialogo, che si instaura tra l’utente e il bot `e, rispetto al classico motore di ricerca, pi`u adatto alla comprensione delle esigenze e si comporta come una vera e propria consulenza sempre attiva e semplice da usare. La chat fa da tramite tra pi`u utenti che intendono vendere o comprare, per cui `e necessario collegarla ad una piattaforma web che sostiene le comunicazioni personali. Attualmente, uno dei siti web che favorisce le relazioni umane `e il noto social network Facebook, esso gode di un’applicazione di messaggistica istantanea chiamata Messenger, per il quale negli ultimi anni sta promuovendo largamente la campagna dei bot. Anche la nostra chat gode del servizio di questo sito. La tesi `e suddivisa in quattro capitoli: il primo introduce il concetto di Chatbot e si mostrano due modi per realizzarlo. Nel secondo capitolo `e discussa la creazione di un primo semplice bot, tramite una combinazione delle due tecniche descritte nel capitolo precedente, e termina con la presentazione di tutti i concetti base necessari per la sua costruzione. Infine, a partire dal codice del bot iniziale ottenuto nel secondo capitolo, nel terzo capitolo vengono implementate le classi necessarie per la realizzazione della bacheca di annunci. Tale procedura `e accompagnata da test con emulatori locali, per verificarne il corretto funzionamento. In particolare, `e stata implementata una conversazione che, con una serie di domande, aiuta gli utenti a scegliere un oggetto e, in seguito, verifica la disponibilit`a di suoi annunci in bacheca. In pi`u, sono state progettate diverse sezioni in cui questi possono visualizzare tutti gli annunci presenti in bacheca, inserirne di nuovi, modificarli o eliminarli, oppure ricercare quelli a cui sono interessati, come in un motore di ricerca. Inoltre, attraverso la registrazione di un modulo, l’utente pu`o interrompere la conversazione in ogni momento, per chiedere delle informazioni sul funzionamento e funzionalit`a del bot, oppure per ritornare semplicemente al main. Per fare in modo che pi`u utenti possano usufruire del servizio del Chatbot, `e stato, inoltre, caricato sul cloud di Azure e collegato alla piattaforma Messenger di Facebook, seguita da ulteriori verifiche per il corretto funzionamento e sistemati eventuali problemi. Infine, diamo le nostre conclusioni e alcuni spunti di miglioramento per un lavoro futuro.
  • 5. Capitolo 1 Informazioni generali sui Chatbot I Chatbot sono delle applicazioni web con cui utenti diversi possono interagire attraverso delle conversazioni. La comunicazione tra le due parti pu`o avvenire attraverso messaggi di testo, carte pi`u complesse (messaggi contenenti bottoni, immagini e testi), o addirittura con l’uso della voce (ad esempio Siri di Apple e Cortana di Windows). I bot possono essere liberi, ovvero gestiscono diversi tipi di input degli user, grazie a tecniche sofisticate di riconoscimento del linguaggio naturale (come il servizio offerto, ad esempio, dal sito web LUIS), oppure possono avere pi`u interazioni guidate, dove esso stesso fornisce all’utente le scelte e le azioni che `e in grado di accettare. Come per le applicazioni dei siti web pi`u conosciuti `e importante offrire una buona esperienza utente ai loro utilizzatori, lo stesso vale anche per i Chatbot. Dunque, quando si progetta un bot, bisogna fare in modo che esso risolva le problematiche dei suoi utenti: • con il minor numero di passaggi possibili • pi`u facilmente e pi`u velocemente di quanto non facciano i suoi competitori • facendo in modo che funzioni su tutti i dispositivi e piattaforme informatiche richieste • facendo in modo che sia accessibile a tutti, ovvero facile da usare Molto spesso, ottenere una buona esperienza utente non significa necessariamente rendere pi`u intelligente il bot, pi`u abile nella comprensione automatica del linguaggio naturale o pi`u capace nell’uso della voce. E’ preferibile un bot semplice e funzionante, che uno molto sofisticato e che non risolve le problematiche dei suoi utenti. 1.1 Come si realizza un Bot Ci sono essenzialmente due modi per costruire un Chatbot, tramite • Servizio Bot di Azure 1
  • 6. CAPITOLO 1. INFORMAZIONI GENERALI SUI CHATBOT 2 • SDK (Kit per sviluppo dei software) di Bot Builder Il Servizio Bot di Microsoft Azure offre la possibilit`a di creare un Chatbot di default, ospitato direttamente sul sito, che pu`o essere modificato con l’editor di codice online, oppure scaricando e poi elaborando il file .zip in locale con Visual Studio. Al termine delle modifiche occorre ri- pubblicarlo nuovamente su Azure, in modo tale che sia utilizzabile da terzi. Pi`u precisamente, il pacchetto di Azure presenta sia l’SDK di Bot Builder, che serve per la progettazione del bot, che la libreria Bot Framework, per connetterlo ai canali, come Facebook, Skype, Twillio e molti altri. La seconda tecnica, invece, prevede l’utilizzo dell’SDK di Bot Builder, tramite l’ambiente di sviluppo Microsoft Visual Studio (quindi si tratta di un’implementazione locale). E’ anche di- sponibile un emulatore per testare il bot e il servizio web di Channel Inspector per visualizzare in anteprima il suo funzionamento nel canale specificato. Quando `e pronto, viene dapprima pub- blicato su Azure e poi pu`o essere collegato eventualmente ad un canale. E’ importante notare che l’SDK di Bot Builder pu`o essere usato con supporto per .NET o No- de.js, le quali sono due importanti piattaforme utilizzate per costruire diversi tipi di applicazioni, tra cui quelle web. Nella tesi `e stata adoperata in un primo momento un’implementazione locale con Visual Studio, sfruttando le funzionalit`a dell’SDK di Bot Builder per .NET (quindi la seconda tecnica e per la scrittura del programma `e stato utilizzato il linguaggio C#, moderno, orientato agli oggetti e di ”tipo sicuro”). In questa fase sono state create tutte le classi necessarie per dare vita alla bacheca di annunci, e tramite l’emulatore di Bot Framework, che `e un’applicazione desktop, `e stata testata e condotta la messa a punto del bot in locale. Successivamente, usando la prima tecnica, `e stato creato un servizio bot di Azure, scaricato il file in locale e modificato inserendo le classi gi`a create nella fase precedente; infine, `e stato ripubblicato nuovamente sul sito. Sarebbe stato possibile fare direttamente tutto su Azure, ma sono state incontrate diverse diffi- colt`a: l’editor di codice online non permetteva grossi cambiamenti di codice a partire da quello iniziale ricevuto al momento dell’attivazione del servizio e la procedura di download del file .zip - modifica - upload sul sito comportava molte pubblicazioni che richiedevano del tempo. In seguito alla pubblicazione del bot su Azure, `e stato collegato anche al canale Messenger di Facebook, seguendo un procedimento che ha comportato la realizzazione di una pagina web sul social network, impostata come gestore della chat. I dettagli sui passaggi seguiti per l’implementazione del Chatbot sono presenti nel prossimo capitolo, dove si discutono anche gli ingredienti principali per la comprensione del suo funziona- mento.
  • 7. Capitolo 2 Creazione del bot Per la creazione del bot `e stata utilizzata come riferimento la guida disponibile al link 2 della Bibliografia. Per cominciare, i passi da seguire sono: 1. Installazione di Visual Studio 2017. 2. Aggiornamento di tutte le estensioni. 3. Scaricamento dei file .zip Bot Application, Bot Controller e Bot Dialog, ottenuti dai rispettivi siti elencati nella guida. 4. Estrazione dei file e copiatura nella directory dei modelli di elementi di Visual Studio 2017, in questo caso si ha: C:UsersXhoiDocumentsVisual Studio 2017 TemplatesProjectTemplatesVisual C#. 5. Apertura di Visual Studio, creazione di un nuovo progetto C#, e selezione del modello ”Applicazione Bot”, presente tra le scelte, poich´e installato nel passo precedente. Arrivati a questo punto, si ottiene un progetto che contiene tutti i componenti necessari per un semplice bot, compreso un riferimento all’SDK di Bot Builder per .NET; si verifica ora che esso sia dell’ultima versione rilasciata, andando ad aggiornare nella sezione ”Gestisci pacchetti NuGet”, a cui si accede cliccando tasto destro sul nome del progetto, il pacchetto con nome Microsoft.Bot.Builder. Prima di visualizzare e modificare il codice ottenuto, `e necessario fare un approfondimento sui concetti chiavi dell’SDK. 2.1 Elementi fondamentali dell’SDK per .NET 2.1.1 Il Connettore Il framework con nome Bot.Framework.Connector fornisce un ”connettore”, cio`e una singola API REST, il quale consente ad un bot di comunicare attraverso pi`u canali, come Facebook, Skype, Slack, con uno o pi`u utenti. Esso facilita la trasmissione dei messaggi tra le due parti. 3
  • 8. CAPITOLO 2. CREAZIONE DEL BOT 4 REST `e il principio architettonico di base del web, nel quale i client (browser) e i server possono interagire in modi complessi senza che i primi sappiano nulla in anticipo sui secondi e le risorse che ospitano. Il vincolo principale `e che essi devono essere entrambi d’accordo sul supporto utilizzato per i documenti scambiati, che nel caso del web `e HTML. Un’API che aderisce ai principi di REST non richiede che il client conosca qualcosa sulla struttura dell’API stessa. Piuttosto, il server deve fornire tutte le informazioni necessarie al client per interagire con il servizio che offre. Ad esempio, lo pu`o fare attraverso un modulo HTML, nel quale esso specifica la posizione della risorsa e i campi richiesti. Il browser non sa in anticipo dove inviare le informazioni e quali trasmettere, la cui conoscenza, invece, `e interamente fornita dal server. 2.1.2 Le Attivit`a Il connettore utilizza un oggetto di tipo Activity per passare informazioni avanti e indietro tra bot e canale. Il tipo pi`u comune di attivit`a `e il messaggio, ma ce ne sono di altri. Alcuni mes- saggi possono semplicemente essere costituiti da testo normale, mentre altri possono presentare contenuti pi`u ricchi, come testo parlato, azioni suggerite, allegati multimediali, schede ricche e dati specifici per i canali. `E possibile creare un messaggio personalizzato, utilizzando un oggetto di tipo Activity, ed impostare le propriet`a necessarie prima di inviarlo all’utente, un esempio in C# `e il seguente: IMessageActivity messaggio = Activity.CreateMessageActivity(); messaggio.Text = "Ciao!"; messaggio.TextFormat = "plain"; messaggio.Locale = "it-IT"; 2.1.3 Gli Allegati La propriet`a Attachments di un oggetto di tipo Activity, si consideri per esempio un messaggio, contiene una serie di oggetti di tipo Attachment che rappresentano le carte ricche ed elementi multimediali, che possono essere immagini, audio, video e file. Le seguenti righe di codice C# mostrano un esempio per l’inserimento di un allegato: var messaggio = await risultato as Activity; // il bot attende un messaggio, var risposta = messaggio.CreateReply(); // genera poi una risposta, risposta.Attachments = new List<Attachment>(); // pone la propriet`a Attachments uguale a una lista di allegati, risposta.Attachments.Add(new Attachment() // aggiunge a questa lista l’allegato sotto definito { ContentUrl = "https://upload.wikimedia.org/wikipedia/en/a/a6/Bender_Rodriguez.png", ContentType = "image/png", Name = "Bender_Rodriguez.png" }); Se un allegato `e un’immagine, un audio o un video, il servizio Connettore comunicher`a i suoi dati al canale in modo tale che esso li renda visualizzabili all’interno della conversazione. Se l’allegato `e un file, l’URL corrispondente verr`a reso come un collegamento ipertestuale all’interno della conversazione.
  • 9. CAPITOLO 2. CREAZIONE DEL BOT 5 Le carte ricche sono dei messaggi che non contengono solo testo o immagini, ma anche dei bottoni cliccabili, che producono degli eventi. Per inserire quest’ultimi, bisogna definire un oggetto della classe CardAction e specificare l’azione prodotta, quando l’utente esegue il click. Ogni oggetto della classe CardAction pu`o presentare le propriet`a seguenti: • Type: di tipo stringa che specifica il genere di azione prodotta. • Title: di tipo stringa che specifica il nome del bottone. • Image: di tipo stringa che specifica l’URL dell’immagine per il bottone. • Value: stringa che l’utente deve inserire per realizzare il tipo dell’azione presente nella propriet`a Type. Nella tesi `e stata adoperata molte volte la combinazione delle propriet`a: Type, Title e Value. Ci sono diversi esempi di Type come openUrl, imBack, postBack e molti altri. In seguito alla pressione del bottone che li contiene, il primo consente di aprire un url specificato nel value, il secondo chiede all’utente di digitare il testo contenuto in value. Il terzo `e analogo al secondo, con la differenza che il messaggio di risposta `e nascosto agli altri membri della conversazione. Per la somiglianza dei due, alcuni canali lo considerano come se fosse imBack. La tabella 2.1 contiene un riassunto delle carte ricche con i loro nomi e funzioni. Tabella 2.1: Carte ricche Tipo Descrizione Adaptive Card Una carta modificabile che contiene ogni com- binazione di testo, parole, immagini, bottoni e campi di input. Animation Card Una carta che pu`o eseguire gif animati o video corti. Audio Card Una carta che pu`o eseguire un file audio. Hero Card Una carta che contiene tipicamente una singo- la ma grande immagine, uno o pi`u bottoni e un testo. Thumbnail Card Una carta che contiene tipicamente una singo- la immagine thumbnail, uno o pi`u bottoni e un testo. Receipt Card Una carta che consente a un bot di fornire una carta recipiente allo user. Esso contiene tipica- mente la lista degli oggetti da includere nel reci- piente, il carico e l’informazione complessiva e un altro testo. SignIn Card Una carta che consente a un bot di chiedere allo user di collegarsi. Esso contiene tipicamente un testo e uno o pi`u bottoni cliccabili dallo user per iniziare il processo di collegamento. Video Card Una carta che pu`o eseguire video.
  • 10. CAPITOLO 2. CREAZIONE DEL BOT 6 2.1.4 I Dialoghi In una comune applicazione web o sito web si usano immagini singole o multiple per permettere lo scambio di informazioni con l’utente; queste costituiscono ci`o che viene chiamata User In- terface (UI). In modo simile si pu`o dire che i bot hanno una UI fatta di dialoghi. Essenzialmente, un dialogo `e una vera e propria astrazione che incapsula il proprio stato in una classe C#, la quale eredita dalla superclasse IDialog. Dunque, quando si crea un bot utilizzando l’SDK per .NET di Bot Builder, `e possibile utilizzare delle finestre di dialogo per modellare e gestire il flusso di conversazione con uno o pi`u utenti. Questo genera una separazione logica del Chatbot in diverse aree. I dialoghi possono essere in forma di testo, bottoni, conversazioni vocali o altri elementi. Po- trebbero avere o no interfacce grafiche. Essi sono usati per invocare altri dialoghi, oppure per gestire l’input degli utenti. Il contesto di un dialogo `e un oggetto della classe IDialogContext che mantiene la pila dei dialoghi attivi nella conversazione in qualsiasi momento. Ogni bot deve avere un dialogo principale, che `e quello invocato nel metodo ”post” dalla classe, che funge da controllore dei messaggi, e da cui possono, eventualmente, partire suoi figli. Si mostra ora un esempio in C# di un possibile dialogo principale: [Serializable] public class DialogoPrincipale : IDialog<object> { public async Task StartAsync(IDialogContext contesto) { contesto.PostAsync("Ciao! Io sono il dialogo principale"); contesto.Wait(MessaggioRicevutoAsync); } private async Task MessaggioRicevutoAsync(IDialogContext contesto,IAwaitable<object> risul) { contesto.PostAsync("Per favore, digita un messaggio!"); var messaggio = await risul as Activity; await contesto.PostAsync($"Hai inviato {messaggio.Text}"); contesto.Wait(MessaggioRicevutoAsync); } } } $ Ogni dialogo, tra cui anche quello appena visto, poich´e `e una classe che eredita dalla madre IDialog, deve implementare l’interfaccia di quest’ultima, che presenta le seguenti caratteristiche: • Deve usare l’attributo [Serializable], per permettere la serializzazione degli oggetti della sua classe. Il termine ”serializzazione” in C# indica il processo di conversione di un oggetto in un flusso di byte allo scopo di archiviarlo o trasmetterlo alla memoria, ad un database o ad un file. Esso consente allo sviluppatore di salvare lo stato di un oggetto per ricrear- lo quando necessario (il processo inverso `e denominato deserializzazione), consentendo sia l’archiviazione di oggetti che lo scambio di dati. Grazie alla serializzazione, uno svilup- patore pu`o eseguire azioni come l’invio dell’oggetto ad un’applicazione remota tramite un servizio Web (questo `e il caso corrente, in quanto, come gi`a detto, l’applicazione bot deve
  • 11. CAPITOLO 2. CREAZIONE DEL BOT 7 essere pubblicata sul sito di Microsoft Azure), il passaggio di un oggetto da un dominio ad un altro, il passaggio di un oggetto attraverso un firewall come stringa XML o la gestione su diverse applicazioni di informazioni che hanno a che fare con la sicurezza o specifiche dell’utente. • Utilizza le parole chiave async ed await, che in C# sono il punto centrale della program- mazione asincrona. Quest’ultima `e essenziale per tutte quelle operazioni che interrompono l’esecuzione del programma in cui sono svolte. Per esempio, quella riguardante l’accesso ad una risorsa Web pu`o risultare talvolta lenta o ritardata. Se questa operazione viene bloccata in un processo sincrono, l’intera applicazione deve attendere la sua terminazione prima di ripartire, mentre, in uno asincrono, essa pu`o continuare a fare altro. Tramite le parole chiave async e await, `e possibile usare le risorse del framework .NET per creare un metodo asincrono con la stessa facilit`a con cui `e possibile crearne uno sincrono. La modalit`a asincrona `e molto importante per le applicazioni che accedono al thread del- l’interfaccia utente (UI), poich´e tutte le operazioni di quest’ultima, in genere, condividono un solo thread. Se si specifica un metodo asincrono, usando il modificatore async, esso pu`o usare l’operatore await per definire i suoi punti di sospensione, indicando al compilatore che non pu`o continuare oltre a questi, prima del completamento dei corrispondenti processi asincroni in attesa; nel frattempo, il controllo viene restituito al suo chiamante. E’ doveroso informare che la sospensione di un metodo asincrono in corrispondenza di un’espressione await non costituisce la sua uscita. Inoltre, il metodo asincrono contrassegnato con async pu`o essere atteso da quelli che lo chiamano. Esso contiene, in genere, una o pi`u occorrenze di un operatore await, mentre l’assenza di quest’ultimo non determina un errore del compilatore, ma lo fa agire come un metodo sincrono, nonostante la presenza del modificatore async; in questo caso, il com- pilatore genera un avviso (warning). Un metodo asincrono ritorna, in genere, un oggetto della classe Task o Task<TResult>, che rappresentano un’istruzione return (nel secondo si specifica anche il tipo, per esempio si potrebbe avere Task<string>). • Possiede un metodo, che pu`o essere asincrono o no a seconda dei casi, di nome StartAsync, che riceve come parametro il contesto del dialogo e ne chiama un altro. Quest’ultimo, asincrono, ha come parametri il contesto del dialogo attuale e un oggetto della classe IWaitable, che pu`o essere di diverso tipo, tra cui anche IActivity. Esso ha la funzione di gestire le azioni successive all’invio del messaggio da parte dell’utente. Nell’esempio del dialogo principale, tale metodo chiede a quest’ultimo di digitare un messaggio e gli risponde con il suo testo. In altri casi, potrebbe non rimanere in attesa e rispondere subito, per esempio, con un messaggio o una Hero Card; ci sono pi`u implementazioni possibili, a seconda delle esigenze. Inoltre, entrambi i metodi hanno come tipo restituito un oggetto della classe Task, che, in questo caso, `e una rappresentazione asincrona dell’invio del messaggio di risposta del bot. Inoltre, sempre dal codice, `e possibile vedere un modo per inviare e ricevere messaggi, ad esempio con l’istruzione: var messaggio = await risul as Activity;
  • 12. CAPITOLO 2. CREAZIONE DEL BOT 8 Il bot attende un messaggio da parte dell’utente. Inoltre, con il comando seguente esso invia il testo del messaggio ricevuto precedentemente: await contesto.PostAsync($"Hai inviato {messaggio.Text}"); Il metodo PostAsync restituisce un oggetto della classe Task ed ha come parametro, in generale, un oggetto della classe IMessageActivity; in questo caso, `e usato il valore del suo campo Text. Inoltre, con il comando: contesto.Wait(MessaggioRicevutoAsync) il bot si mette in attesa di un altro messaggio, richiamando nuovamente il metodo di prima. Il seguente codice, invece, rappresenta un esempio di Controllore: [BotAuthentication] public class MessagesController : ApiController { public async Task<HttpResponseMessage> Post([FromBody]Activity attivit`a) { if (attivit`a.Type == ActivityTypes.Message) { await Conversation.SendAsync(attivit`a, () => new DialogoPrincipale()); } else { // gestisce le attivit`a che non sono messaggi // GestioneMessaggiDiSistema(attivit`a); } var risposta = Request.CreateResponse(HttpStatusCode.OK); return risposta; } } Per quando riguarda l’interfaccia di un controllore, notiamo che: nel metodo post, se al bot ar- riva un’attivit`a di tipo messaggio, viene chiamato il dialogo principale, altrimenti si definiscono nel metodo GestioneMessaggiDiSistema le operazioni da eseguire per le attivit`a differenti. Per garantire che l’accesso al bot sia possibile, per motivi di sicurezza, solo tramite il connettore di Bot Framework, bisogna configurare l’endpoint del Chatbot, in modo tale che, registrandola sul cloud di Azure, permetta solo traffico HTTPS (crittato) e che si abiliti la sua autenticazione, tramite l’identificatore e la password dell’applicazione (prelevati sempre dal sito appena citato). Normalmente, si mette l’attributo [BotAuthentication] senza parametri, per usare le cre- denziali di autenticazione del bot immagazzinate nel file Web.config, che `e fatto in questo modo: <appSettings> <add key="MicrosoftAppId" value="_appId_" /> <add key="MicrosoftAppPassword" value="_password_" /> </appSettings> altrimenti, si adopera l’attributo specificandone i parametri: [BotAuthentication(MicrosoftAppId = "_appId_", MicrosoftAppPassword="_password_")] public class MessagesController : ApiController { }
  • 13. CAPITOLO 2. CREAZIONE DEL BOT 9 2.1.5 Il ciclo di vita dei dialoghi Quando una finestra di dialogo viene chiamata, essa prende il controllo del flusso di conversazione, cio`e si colloca nella prima posizione dello stack (pila) dei dialoghi attivi; ogni nuovo messaggio che arriva al bot viene elaborato da essa, finch´e non viene chiusa o reindirizzata ad un’altra con opportuni comandi. Per chiudere una finestra di dialogo e rimuoverla dallo stack, instradando cos`ı l’utente verso quella della posizione precedente, si pu`o utilizzare il comando context.Done(). Per evitare errori nell’esecuzione del bot, oltre a quello appena visto, `e necessario terminare ogni metodo di dialogo con uno dei seguenti comandi, altrimenti, la stessa infrastruttura di Bot Framework non sa quale azione intraprendere quando gli arriva un nuovo messaggio: • context.Wait(): definisce quale metodo, il cui nome `e specificato nei parametri, deve essere chiamato dal dialogo corrente, al prossimo messaggio digitato dall’utente. • context.Forward() o context.Call(): chiamano un dialogo figlio e lo aggiungono nella prima posizione dello stack, lasciandogli cos`ı il comando. Entrambi hanno come primo parametro il nome del dialogo figlio, come secondo il metodo da eseguire dopo la sua terminazione, mentre differiscono nel terzo e quarto. Nel caso di context.Forward() viene passato al figlio anche un oggetto e c’`e la possibilit`a di gestire la cancellazione dei token, mentre in context.Call() non ci sono questi ultimi due parametri. Il funzionamento di entrambi ricorda molto la ”redirection” (indirizzamento) dei siti web. Il dialogo chiamato usando context.Call() deve presentare, nel metodo invocato da StartAsync, un’istruzione con cui si aspetta un’attivit`a, mentre ci`o non vale per context.Forward(). Muniti delle conoscenze sui dialoghi, consideriamo ora l’implementazione e le funzionalit`a di una carta ricca, in particolare una Hero Card su Messenger. Figura 2.1: Un esempio di Hero Card. Il codice C# corrispondente `e il seguente: var risposta = contesto.MakeMessage(); risposta.Attachments = new List<Attachment>(); HeroCard hc = new HeroCard()
  • 14. CAPITOLO 2. CREAZIONE DEL BOT 10 { Title = "Benvenuto nella mia applicazione", Subtitle = "Io sono il bot degli annunci!" }; List<CardImage> immagine = new List<CardImage>(); CardImage ci = new CardImage("" + "https://medias2.prestastore.com/835054-pbig/chat-bot-for-social-networking.jpg" + ""); immagine.Add(ci); hc.Images = immagine; risposta.Attachments.Add(hc.ToAttachment()); await contesto.PostAsync(risposta); Il bot in questo caso, per generare una risposta, usa il metodo contesto.MakeMessage(). A questo punto imposta la propriet`a Attachments del messaggio di risposta uguale ad una nuova lista di allegati e definisce la Hero Card (settandone le propriet`a). Successivamente, crea una nuova lista di carte immagini, in cui inserisce una con l’URL di un documento immagine ed uguaglia la propriet`a Images della Hero Card con tale lista. Infine, aggiunge alla lista degli allegati, creata all’inizio, la Hero Card trasformandola con il metodo ToAttachment() in un allegato, per poi inviare la risposta. Quindi complessivamente, il bot attende un messaggio da parte dell’utente e risponde con un carta ricca contenente un’immagine e un testo. 2.1.6 Il FormFlow Il formflow `e una conversazione guidata tra bot e utenti, che avviene in modo automatico, poich´e le sue linee guida sono stabilite in un primo momento di settaggio. Quest’ultimo contribuisce all’utilizzo di poche righe di codice, dal momento che il grosso del lavoro lo conduce la libre- ria Microsoft.Bot.Builder.FormFlow. Il formflow pu`o essere adoperato in collaborazione a finestre di dialogo, rispetto a cui per`o `e meno flessibile, per aumentare la propria funzionalit`a. Inoltre, pu`o semplificare la creazione di quei Chatbot adibiti al raccoglimento delle informazioni dell’utente. Ad esempio, si potrebbe pensare ad un bot specializzato nel prendere ordini per dei sandwich, il quale deve ottenere diversi dati dall’utente come il tipo di pane che vuole, il condimento, la dimensione e cos`ı via. 2.1.7 Gestire i dati di una conversazione Poich´e, per raggiungere l’obbiettivo della tesi, `e necessario che utenti diversi possano collegarsi al Chatbot contemporaneamente, senza influenzarsi e disturbarsi a vicenda, occorre illustrare una serie di tecniche che consentono di memorizzare e recuperare i dati di quest’ultimi all’interno della conversazione. Pi`u specificatamente, sono stati utilizzati nella tesi PrivateConversation- DataTryGetValue e PrivateConversationDataSetValue. Il primo consente di recuperare i dati dell’utente precedentemente salvati all’interno della conversazione sul canale specificato, mentre il secondo di immagazzinarli all’interno della conversazione sul canale specificato.
  • 15. CAPITOLO 2. CREAZIONE DEL BOT 11 Il recupero e l’immagazzinamento delle informazioni avvengono tramite delle variabili che sono uniche per ogni utente, con cui di fatto il bot conduce una conversazione privata. Un esempio scritto in codice C# `e il seguente: private async Task MessaggioRicevutoAsync(IDialogContext contesto, IAwaitable<object> ris) { var messaggio = await ris as Activity; string frase = messaggio.Text; contesto.PrivateConversationData.SetValue<string>("Testo", frase); await contesto.PostAsync($"Mi hai inviato {frase}"); contesto.Wait(MessaggioRicevutoAsync2); } private async Task MessaggioRicevutoAsync2(IDialogContext contesto, IAwaitable<object> ris) { string frase = " "; contesto.PrivateConversationData.TryGetValue("Testo", out frase); await context.PostAsync($"Prima mi hai inviato {frase}"); } L’istruzione: contesto.PrivateConversationData.SetValue<string>(”Testo”, frase) realizza il fat- to che il bot, privatamente con ogni utente, crea una variabile stringa di nome ”Testo” in cui memorizza il testo del messaggio atteso. Con l’istruzione: contesto.PrivateConversationData.TryGetValue<string>(”Testo”, out frase) il bot ricava il valore della variabile ”Testo” memorizzato nel metodo precedente, per comunicarlo all’utente corrispondente. Si nota, inoltre, che: i prefissi di SetValue e TryGetValue devono essere gli stessi. Non si pu`o usare, ad esempio, PrivateConversationData.SetValue con ConversationData.TryGetValue, o vi- ceversa. Se fosse stato adoperato l’istruzione con ConversationData in tutti e due i metodi, si avrebbe avuto un’unica variabile ”Testo” condivisa da tutti gli utenti nella stessa conversazione, modifi- cabile da chiunque, e il cui valore ottenuto nel metodo con ”TryGetValue” sarebbe stato quello dell’utente che aveva modificato per ultimo. Pertanto, si avrebbe avuto un effetto non desidera- to, in quanto gli utenti avrebbero rischiato di ricevere risultati per scelte che non avrebbero mai fatto. 2.1.8 Aggiungere azioni suggerite Per fare in modo che il Chatbot interagisca con i suoi utenti attraverso conversazioni guidate, in cui `e lui stesso a proporre le possibili risposte che `e in grado di accettare, `e necessario fare una breve descrizione dei metodi necessari a tale scopo. Solitamente, sono utilizzate le funzioni della classe PromptDialog, elencate brevemente nella tabella 2.2. Tabella 2.2: Funzioni della classe PromptDialog
  • 16. CAPITOLO 2. CREAZIONE DEL BOT 12 Metodo della classe PromptDialog Descrizione Text Chiede all’utente di digitare una stringa di testo. Confirm Chiede all’utente una conferma (S`ı o No) Number Chiede all’utente di inserire un numero Choice Chiede all’utente di scegliere tra diverse possibi- lit`a. Attachment Chiede all’utente di caricare un file. Un esempio di PromptDialog.Choice `e il seguente: private async Task OrdinaCibo(IDialogContext contesto, IAwaitable<object> risultato) { List<string> listaCibo = new List<string>{ "Pizza", "Pasta", }; PromptOptions<string> opzioni = new PromptOptions<string>("Che cibo vorresti mangiare?","Per favore, riprova di nuovo!", "Tentativi finiti!", listaCibo, 2); PromptDialog.Choice<string>(contesto, metodoSuccessivoAllaSceltaAsync, opzioni); } private async Task metodoSuccessivoAllaSceltaAsync(IDialogContesto contesto, IAwaitable<string> ris) { string risposta = await ris; // aspetto che l’utente scelga tra pizza e pasta if(risposta == "pizza") await contesto.PostAsync("Hai scelto la pizza"); else if (risposta == "pasta") await contesto.PostAsync("Hai scelto la pasta"); } I parametri del metodo sono il contesto attuale del dialogo, il metodo successivo da eseguire in caso di procedura corretta, una lista di stringhe che identificano le opzioni da scegliere, un testo per far capire all’utente che deve operare una decisione, un messaggio che esce quando si sbaglia a digitare e un numero di tentavi massimo. In questo semplice estratto di dialogo, il bot propone all’utente di scegliere tra pizza e pasta e gli mostra la decisione effettuata. Come esempio di PromptDialog.Text, consideriamo le seguenti righe di codice: private async Task InserisciTesto(IDialogContext contesto, IAwaitable<object> ris) { PromptDialog.Text(contesto, TestoSceltoAsync, "Inserisci un testo"); } private async Task TestoSceltoAsync(IDialogContext contesto, IAwaitable<string> ris) { string risposta = await ris; await contesto.PostAsync($"Hai inviato il testo seguente: {risposta}"); } $ I parametri del metodo sono il contesto attuale del dialogo, un metodo da eseguire una volta che `e stato inserito il testo e un messaggio per far capire all’utente che deve inserirlo. Se la procedura non avviene correttamente, esce un messaggio di errore che invita a ripetere. In questa sezione di dialogo illustrativo, il bot chiede all’utente di inserire un testo che poi gli mostra. Infine, mostriamo un esempio di PromptDialog.Number e PromptDialog.Confirm, i cui parametri sono uguali ai metodi precedenti. In questo esempio, Il bot aspetta l’inserimento di un numero e ne chiede la conferma. Il codice corrispondente `e il seguente:
  • 17. CAPITOLO 2. CREAZIONE DEL BOT 13 private async Task InserisciNumero(IDialogContext contesto, IAwaitable<object> ris) { PromptDialog.Number(contesto, NumeroSceltoAsync, "Inserisci un numero qualsiasi!"); } private async Task NumeroSceltoAsync(IDialogContext contesto, IAwaitable<long> ris) { long risposta = await ris; await contesto.PostAsync($"Hai inviato il numero seguente: {risposta}"); PromptDialog.Confirm(contesto, confermaNumeroAsync, "Vuoi confermare il numero inserito?"); } private async Task confermaNumeroAsync(IDialogContext contesto, IAwaitable<bool> ris) { var conferma = await ris; if (conferma == true) await contesto.PostAsync("Numero confermato"); else await contesto.PostAsync("Numero non confermato"); } $ Sommario In questo capitolo `e stato creato un primo semplice bot usando una guida presente sul sito di Microsoft, che ha comportato il coinvolgimento dell’ambiente di sviluppo di Microsoft Visual Studio, in cui sono state sfruttate le funzionalit`a dell’SDK di Bot Builder per .NET. In seguito, allo scopo di comprendere il suo funzionamento, `e stato condotto un approfondimento di tutti i concetti base del framework appena citato, organizzati tramite una lista riassuntiva. Ed `e a questo punto che `e stato introdotto il concetto di Connettore, che permette l’interazione tra bot e uno o pi`u canali, a cui si collegano gli utenti, trasportando da una parte all’altra gli oggetti della classe Activity. Quest’ultimi sono di diversi tipi, tra cui i messaggi, che possono essere composti di solo testo, oppure possono possedere eventualmente degli allegati. Questi possono essere elementi multimediali (come immagini, audio e file), oppure carte ricche, cio`e messaggi composti da testo, immagini, e anche bottoni, la cui pressione genera degli eventi. Successivamente, `e stata vista l’importanza dei dialoghi, che permettono di creare le conversazioni con gli utenti. Siccome `e stato ritenuto necessario per l’implementazione del bot prefissato che queste ultime siano private per ogni utente e guidate, sono stati descritti i metodi e le funzioni che consentono ci`o. Nel prossimo capitolo si parte dal codice del bot ottenuto prima e si implementano e aggiungono le classi per realizzare la bacheca di annunci, di cui si verifica poi il corretto funzionamento e funzionalit`a mediante opportuni emulatori.
  • 18. Capitolo 3 Progettazione del bot Per visualizzare il codice C# dell’applicazione bot creata nel capitolo precedente, andiamo nella sezione di nome Esplora soluzioni di Visual Studio, dove si vedono all’inizio questi elementi: Figura 3.1: Esplora soluzioni di Visual Studio. • Properties: contiene tutte le impostazioni di base dell’applicazione bot. • Riferimenti: contiene tutti i pacchetti NuGet installati. • AppStart: contiene la classe WebApiConfig.cs, la quale, usando il framework di nome WebApiConfig, serve per generare connessioni HTTP con il bot, che, essendo un’appli- cazione web che offre un servizio, `e in attesa su un certo numero di porta. In un primo momento, poich´e esso non `e ospitato su Azure, `e accessibile localmente, in genere, sul numero di porta: 3979. Quando viene caricato sul sito, `e accessibile su un altro numero. • Controllers: in questa cartella c’`e il Controllore dei messaggi, che `e una classe ereditata dalla madre ApiController e contiene un metodo post che serve per chiamare il dialogo principale quando l’utente scrive al bot la prima volta. 14
  • 19. CAPITOLO 3. PROGETTAZIONE DEL BOT 15 • Dialogs: in questa cartella vengono inseriti tutti i dialoghi, tra cui anche quello principale chiamato per primo. • default.htm: rappresenta la pagina web che si apre per mostrare la connessione effettuata con il bot. • Global.asax: `e una classe che consente di scrivere del codice che viene eseguito in risposta ad alcuni eventi dei diversi livelli di sistema, come l’inizio dell’applicazione, terminazione di una sessione, l’occorrenza di un errore; esso `e composto essenzialmente da registrazioni di moduli con valenza globale in tutto il programma, che evitano l’inserimento di righe di codice per la gestione degli eventi in ogni dialogo. • package.config: questo file `e amministrato dall’infrastruttura NuGet. E’ usato per visualizzare i diversi pacchetti installati con le rispettive versioni. • Web.config: presenta le impostazioni principali e il file di configurazione dell’applicazione web. Si tratta di un documento XML che risiede nella directory radice del programma e contiene dati sul comportamento della app. Queste informazioni controllano il caricamento del modulo, la configurazione della sicurezza, la configurazione dello stato della sessione e le impostazioni della lingua e della compilazione dell’applicazione. I file Web.config possono anche contenere elementi specifici come stringhe di connessione ad un database. I procedimenti seguiti nel secondo capitolo hanno permesso di ottenere un semplice e funzionante bot, composto da un dialogo principale, che viene richiamato alla prima interazione dell’utente da un controllore di messaggi. Quindi, per progettare il Chatbot prefissato, si `e partiti dal codice attuale e sono state apportate successivamente alcune modifiche. Come prima cosa, `e stato cambiato il file Web.config: 1 <appSettings> 2 <!-- update these with your Microsoft App Id and your Microsoft App Password--> 3 <add key="MicrosoftAppId" value="0dec18cd-2c4e-4d1f-83f9-ba255c018b2f" /> 4 <add key="MicrosoftAppPassword" value="rhtiUOT0:=(dkxGAPO0604+" /> 5 <add key="ChiaveArchiviazione" value="DefaultEndpointsProtocol=https;AccountName=bacheca; 6 AccountKey=0+mBwlgqV9US1LGh2WjFwFfo4hrk7z2HMK+eF fwuXs8m/MxOipBOJ+4gfwtHkLmQRhT8/1YcGhuBRXoFIY0kmA==" /> 7 </appSettings> 8 <connectionStrings> 9 <add name="stringaConnessione" 10 connectionString="Server=tcp:bacheca.database.windows.net,1433;Initial Catalog=BachecaDB; 11 Persist Security Info=False; User ID=Xhoi ;Password=Albi4061992;MultipleActiveResultSets=False; 12 Encrypt=True;TrustServerCertificate=False; Connection Timeout=30"providerName="System.Data.SqlClient" /> 13 </connectionStrings> In ”appSettings” sono state aggiunte tre chiavi (da riga 3 a 5): i valori delle prime due sono stati inseriti quando il Chatbot `e stato pubblicato su Azure e servono per garantire la sua sicurezza. Mentre, il terzo `e necessario per collegarsi ad un account di archiviazione, che ha luogo sempre su Azure, nel quale vengono caricati degli oggetti chiamati ”Blob”, che permettono l’inserimento delle immagini negli annunci. Infine, `e stata aggiunta una stringa per l’apertura di connessioni sql verso un database (da riga 8 a 13), ospitato in un server creato sul sito appena citato. Poich´e essa `e di lunghe dimensioni e
  • 20. CAPITOLO 3. PROGETTAZIONE DEL BOT 16 mirando ad una buona leggibilit`a del codice, `e stato preferito porla in questo file, anzich´e inserirla in ogni dialogo in cui si conducono delle interrogazioni, che necessitano, appunto, dell’apertura della connessione. 3.1 Dialogo Principale Come prima cosa, `e stato creato un nuovo dialogo radice, con nome DialogoPrincipale, il quale presenta questo codice: 1 using System; 2 using System.Threading.Tasks; 3 using Microsoft.Bot.Builder.Dialogs; 4 using Microsoft.Bot.Connector; 5 using System.Collections.Generic; 6 using System.Threading; 7 8 namespace BachecaAnnunci.Dialogs 9 { 10 [Serializable] 11 public class DialogoPrincipale : IDialog<object> 12 { 13 public async Task StartAsync(IDialogContext contesto) 14 { 15 contesto.Wait(MessaggioRicevutoAsync); 16 } 17 18 private async Task MessaggioRicevutoAsync(IDialogContext contesto, IAwaitable<object> risultato) 19 { 20 var risposta = contesto.MakeMessage(); 21 risposta.Attachments = new List<Attachment>(); 22 HeroCard hc = new HeroCard() 23 { 24 Title = "Benvenuto nella mia applicazione", 25 Subtitle = "Io sono il bot degli annunci!" 26 }; 27 List<CardImage> immagine = new List<CardImage>(); 28 CardImage ci = new CardImage("https://medias2.prestastore.com/835054-pbig/chat-bot-for-social-networking.jpg"); 29 immagine.Add(ci); 30 hc.Images = immagine; 31 risposta.Attachments.Add(hc.ToAttachment()); 32 await contesto.PostAsync(risposta); 33 List<string> scelta = new List<string> 34 { 35 "Trovare un annuncio", 36 "Mettere un annuncio", 37 "Vetrina Bacheca", 38 "Gestire gli annunci", 39 "indietro" 40 }; 41 PromptDialog.Choice( 42 contesto, 43 OperSceltaAsync, 44 scelta, 45 "Che operazione vorresti fare?", 46 retry: "Per favore, riprova di nuovo", 47 attempts: 2, 48 promptStyle: PromptStyle.Auto 49 ); 50 } 51 private async Task OperSceltaAsync(IDialogContext contesto, IAwaitable<string> risultato)
  • 21. CAPITOLO 3. PROGETTAZIONE DEL BOT 17 52 { 53 string messaggio = await risultato; 54 if (messaggio == "Trovare un annuncio") 55 { 56 string verifica = " "; 57 contesto.PrivateConversationData.SetValue("VerificaAnnullamento", verifica); 58 await contesto.Forward(new Comprare(), this.MetodoSuccAllaSceltaAsync, messaggio, CancellationToken.None); 59 } 60 else if (messaggio == "Mettere un annuncio") 61 { 62 string aggiorna = " "; 63 contesto.PrivateConversationData.SetValue("AggiornaAnnuncio", aggiorna); 64 await contesto.PostAsync("Ora ti chiedo di inserire una serie di informazioni!"); 65 await contesto.Forward(new VendereScegliereIdAnnuncio(), this.MetodoSuccAllaSceltaAsync, messaggio, CancellationToken.None); 66 } 67 else if (messaggio == "Vetrina Bacheca") 68 { 69 await contesto.Forward(new Dialogs.VetrinaBacheca(), this.MetodoSuccAllaSceltaAsync, messaggio, CancellationToken.None); 70 } 71 else if (messaggio == "Gestire gli annunci") 72 { 73 await contesto.Forward(new Dialogs.VisualizzaAnnunciNome(), this.MetodoSuccAllaSceltaAsync, messaggio, CancellationToken.None); 74 } 75 else if (messaggio == "indietro") 76 { 77 var risposta = contesto.MakeMessage(); 78 risposta.Attachments = new List<Attachment>(); 79 HeroCard hc = new HeroCard() 80 { 81 Title = "Grazie per aver usufruito del mio servizio! ", 82 Subtitle = "Per riattivarmi digita qualcosa...." 83 }; 84 List<CardImage> immagine = new List<CardImage>(); 85 CardImage ci = new CardImage("https://medias2.prestastore.com/835054-pbig/chat-bot-for-social-networking.jpg"); 86 immagine.Add(ci); 87 hc.Images = immagine; 88 risposta.Attachments.Add(hc.ToAttachment()); 89 await contesto.PostAsync(risposta); 90 } 91 } 92 private async Task MetodoSuccAllaSceltaAsync(IDialogContext contesto, IAwaitable<object> risultato) 93 { 94 await contesto.PostAsync("Problema interno! Sei stato rediretto al Dialogo Principale, " + 95 "per favore digita qualcosa per riattivarmi!"); 96 } 97 } 98 } In questo dialogo, quando l’utente digita qualcosa, il bot risponde con una Hero Card di benve- nuto, a cui segue una lista di opzioni, di cui egli ne deve scegliere una per proseguire nella conver- sazione. Quest’ultima `e guidata, effetto reso grazie all’utilizzo del metodo PromptDialog.Choice della classe PromptDialog, la cui funzionalit`a `e stata discussa nella sezione ”Aggiungere azioni suggerite” del capitolo 2. Perci`o, quando l’utente digita correttamente, oppure preme semplice- mente sul bottone d’interesse, si attiva il metodo OperSceltaAsync (riga 51 del codice), il quale gestisce il testo del messaggio in arrivo e, a seconda della scelta effettuata, pu`o attivare i dialoghi per trovare un annuncio, mettere un annuncio, vedere gli annunci disponibili al momento, ovvero la vetrina della bacheca, e gestire gli annunci (modificarli o eliminarli). Il reindirizzamento a questi avviene tramite il comando: await contesto.Forward(...), poich´e, nei metodi chiamati da quello con nome StartAsync di ciascuno di essi, il bot non deve mettersi inizialmente in attesa di
  • 22. CAPITOLO 3. PROGETTAZIONE DEL BOT 18 ricevere un messaggio, ma invia una lista di opzioni e si aspetta che l’utente ne scelga uno. Nelle figure 3.2 e 3.3 si pu`o vedere la schermata grafica del dialogo, rispettivamente, nell’emulatore e su Messenger. Figura 3.2: Schermata del dialogo principale nell’emulatore di Bot Framework. Figura 3.3: Schermata del dialogo principale su Messenger. 3.2 Controllore dei messaggi Come gi`a detto nel capitolo precedente, il controllore dei messaggi possiede un metodo post, che, se il bot riceve un’attivit`a di tipo messaggio, chiama il dialogo principale, altrimenti invoca il metodo GestioneMessaggiDiSistema. Per garantire la sicurezza del bot, `e stato scelto di usare l’attributo [BotAuthentication], attraverso cui si prendono le informazioni per l’autenticazione dal file WebApi.Config. Un esempio di codice in C# per il Controllore `e il seguente:
  • 23. CAPITOLO 3. PROGETTAZIONE DEL BOT 19 1 using System.Net; 2 using System.Net.Http; 3 using System.Threading.Tasks; 4 using System.Web.Http; 5 using Microsoft.Bot.Builder.Dialogs; 6 using Microsoft.Bot.Connector; 7 8 namespace BachecaAnnunci 9 { 10 [BotAuthentication] 11 public class MessagesController : ApiController 12 { 13 public async Task<HttpResponseMessage> Post([FromBody]Activity attivit`a) 14 { 15 if (attivit`a.Type == ActivityTypes.Message) 16 { 17 attivit`a.Locale = "it-IT"; 18 await Conversation.SendAsync(attivit`a, () => new Dialogs.DialogoPrincipale()); 19 } 20 else 21 { 22 GestioneMessaggiDiSistema(attivit`a); 23 } 24 var risposta = Request.CreateResponse(HttpStatusCode.OK); 25 return risposta; 26 } 27 28 private Activity GestioneMessaggiDiSistema(Activity messaggio) 29 { 30 if (messaggio.Type == ActivityTypes.DeleteUserData) 31 { 32 33 } 34 else if (messaggio.Type == ActivityTypes.ConversationUpdate) 35 { 36 37 } 38 else if (messaggio.Type == ActivityTypes.ContactRelationUpdate) 39 { 40 41 } 42 else if (messaggio.Type == ActivityTypes.Typing) 43 { 44 45 } 46 else if (messaggio.Type == ActivityTypes.Ping) 47 { 48 } 49 50 return null; 51 } 52 } 53 } Si pone ora l’attenzione sul metodo GestioneMessaggiDiSistema (riga 28): esso riceve come pa- rametro un’attivit`a che non `e di tipo messaggio e, a seconda del Type, restituisce un oggetto della classe Activity. Con il primo if, il bot gestisce un’attivit`a in ingresso di tipo cancellazio- ne utente, con il secondo, invece, gestisce il cambiamento dello stato di conversazione, dovuto all’inserimento o eliminazione di membri; per ottenere informazioni, si usano in questo blocco le propriet`a Activity.MembersAdded, Activity.MembersRemoved e Activity.Action della classe Activity. Tale possibilit`a di gestione non `e presente per tutti i canali. Con il terzo if, il bot
  • 24. CAPITOLO 3. PROGETTAZIONE DEL BOT 20 gestisce l’inserimento e rimozione di elementi dalla lista dei contatti. Le propriet`a Activity.From e Activity.Action danno informazioni su cosa succede. Con il penultimo if, il bot gestisce la conoscenza di quello che lo user sta scrivendo; 3.3 Gestore globale dei messaggi Prima di discutere di tutti i dialoghi implementati per dare vita alla bacheca di annunci, `e ne- cessario informare che `e stato registrato un modulo nel file Global.asax, che serve per gestire in un certo modo alcuni tipi di messaggi in input, indipendentemente dal punto della conversazione in cui ci si trova. Un esempio in C# del modulo `e il seguente: using Autofac; using BachecaAnnunci.Dialogs; using Microsoft.Bot.Builder.Dialogs.Internals; using Microsoft.Bot.Builder.Scorables; using Microsoft.Bot.Connector; namespace BachecaAnnunci { public class GlobalMessageHandlersBotModule : Module { protected override void Load(ContainerBuilder builder) { base.Load(builder); builder .Register(c => new DialogoScrollabile(c.Resolve<IDialogTask>())) .As<IScorable<IActivity, double>>() .InstancePerLifetimeScope(); builder .Register(c => new AnnullaDialogo(c.Resolve<IDialogTask>())) .As<IScorable<IActivity, double>>() .InstancePerLifetimeScope(); } } } Come si pu`o vedere dal codice, il modulo usufruisce di due ulteriori dialoghi, presenti nella cartella Dialogs: DialogoScrollabile e AnnullaDialogo. Il primo concede la possibilit`a all’utente di interrompere il dialogo corrente ogni volta che digita la parola ”aiuto”, alla cui scrittura si attiva quello con nome ImpostazioniDialogo (Figura 3.4), attraverso cui egli pu`o ricevere informazioni sul funzionamento e funzionalit`a del bot (Figure 3.5 e 3.6). Quando il servizio di supporto `e terminato, si ritorna al punto prima dell’interruzione. Il secondo d`a la facolt`a all’utente, ogni volta che egli digita la parola ”annulla”, di interrompere il dialogo corrente e poi, scrivendo un messaggio qualsiasi, di ripartire da quello principale. Nell’esempio di Figura 3.7, viene interrotto il dialogo della ricerca libera, la cui implementazione si vedr`a in seguito.
  • 25. CAPITOLO 3. PROGETTAZIONE DEL BOT 21 Figura 3.4: Scelta dell’aiuto. Figura 3.5: Come funziona il bot.
  • 26. CAPITOLO 3. PROGETTAZIONE DEL BOT 22 Figura 3.6: Cosa pu`o fare il bot. Figura 3.7: Annulla dialogo. 3.4 Dialoghi In questa sezione si parla delle finestre di dialogo usate per dare vita alla bacheca di annunci. La caratteristica comune di queste `e il fatto che siano guidate, ovvero il bot fa una serie di domande all’utente e ne propone le risposte ammissibili, che possono essere digitate in forma di testo, oppure premendo i bottoni presenti. Dunque, esso offre la possibilit`a di avanzare nella conversazione con il modo appena descritto, oppure di retrocedere o attraverso dei bottoni chiamati ”indietro”, oppure con altre modalit`a spiegate da esso stesso.
  • 27. CAPITOLO 3. PROGETTAZIONE DEL BOT 23 3.4.1 Mettere un annuncio Dal dialogo principale si pu`o inserire un annuncio tramite il bottone ”Mettere un annuncio” (Fig. 3.9). Per svolgere l’operazione, il bot chiede all’utente innanzitutto di scegliere un identificatore per l’annuncio, poi di inserire alcune informazioni di carattere personale per la reperibilit`a come un nome, un cognome, una residenza, un numero di cellulare; in seguito `e richiesta una breve descrizione, una eventuale immagine, ed infine una password che gli serve per modifiche succes- sive, effettuabili nella sezione ”Gestire gli annunci”, di cui si parler`a nella prossima sezione. Per memorizzare i dati, `e stato creato un server sql su Microsoft Azure, in cui `e stato inserito un database, formato dalle tabelle rappresentate in Figura 3.8. Figura 3.8: Oggetti server sql su Visual Studio. Le tabelle IdAnnunci, Chiavi, PasswordAnnunci contengono, rispettivamente, gli identifi- catori, i nomi delle eventuali immagini e le password usate dagli utenti al momento per i loro annunci. Ognuna di queste possiede un unico attributo di tipo stringa, ovvero una sola colonna, che `e anche chiave primaria, contenente i valori citati prima. La tabella ListaAnnunci, come suggerisce il nome stesso, contiene tutti gli annunci disponibili in bacheca, distinti univocamente dal loro identificatore, che `e una stringa di dieci caratteri ed `e anche la chiave primaria. Per ognuno di essi, oltre al campo appena citato, ci sono altri che contengono il nome, cognome, luogo di residenza, numero di cellulare del proprietario corrispon- dente, descrizione, ed, infine, un nome per un’eventuale immagine e una password. I valori degli identificatori, chiavi delle immagini e password sono unici per costruzione, poich´e prima di es- sere inseriti in ListaAnnunci, viene verificata la loro univocit`a nelle tabelle IdAnnunci, Chiavi e PasswordAnnunci (ci`o sar`a visto in seguito).
  • 28. CAPITOLO 3. PROGETTAZIONE DEL BOT 24 Figura 3.9: Mettere un annuncio. Si descrive ora la sequenza delle finestre di dialogo progettate per l’inserimento di un annuncio; ognuna di queste svolge la sua funzione e al suo termine chiama quella successiva: • La procedura inizia con VendereScegliereIdAnnuncio, in cui il bot chiede all’utente di scegliere un identificatore per il proprio annuncio, poi controlla con una query se `e gi`a presente nella tabella di nome IdAnnunci. In caso affermativo, fa sceglierne un altro, fino a quando non gli arriva uno non ancora presente nel sistema; in caso negativo lo inserisce nella tabella con un’istruzione ”INSERT”. Per esempio, si vuole pubblicare un annuncio di vendita per un cellulare ”Samsung galaxy s6” e si sceglie come identificatore ”galaxys6” (Fig. 3.10). Poich´e quest’ultimo non `e ancora stato usato da altri utenti, viene inserito nella tabella in Figura 3.11.
  • 29. CAPITOLO 3. PROGETTAZIONE DEL BOT 25 Figura 3.10: Scelta dell’identificatore. Figura 3.11: Tabella con gli identificatori degli annunci. • Nei successivi dialoghi VendereNome, VendereCognome, VendereLuogo, Vendere- NumCell, VendereDescrizione, il bot chiede all’utente di inserire rispettivamente un nome, un cognome, un luogo di residenza, un numero di cellulare e una breve descrizione; per ognuno chiede la conferma, dando la possibilit`a di rimediare ad eventuali errori. Ad esempio, supponiamo di scegliere i dati presenti nelle figure seguenti.
  • 30. CAPITOLO 3. PROGETTAZIONE DEL BOT 26 Figura 3.12: Inserimento nome e cognome. Figura 3.13: Inserimento residenza e numero di cellulare. Figura 3.14: Inserimento descrizione. • A questo punto viene chiamato il dialogo VendereScegliereChiave, in cui il bot chiede all’utente se vuole allegare un’immagine al proprio annuncio. In caso negativo, pone la chiave per l’immagine uguale alla stringa ”nessuna immagine” e invoca direttamente il dialogo per la scelta della password. Questo procedimento serve per evitare che ci siano errori nel metodo per recuperare le immagini, quando vengono visualizzati gli annunci che le contengono (si vedr`a meglio nella sezione ”Trovare un annuncio”). In caso affermativo, lo invita a scegliere un nome univoco (Fig. 3.15), che, se non `e gi`a stato scelto da un altro utente, viene inserito nella tabella Chiavi, altrimenti viene richiesto un altro; in questo caso quest’ultima viene aggiornata come in Figura 3.16.
  • 31. CAPITOLO 3. PROGETTAZIONE DEL BOT 27 Figura 3.15: Aggiunta di un’immagine. Figura 3.16: Tabella con le chiavi delle immagini. • Se nella finestra precedente `e stato scelto di inserire un’immagine, viene invocato ora il dialogo VendereAggiungiImmagine (Fig. 3.15), in cui il bot aspetta che gli arrivi un allegato, poi lo carica su un oggetto di tipo blob, situato su uno spazio di archiviazione creato su Azure, che denomina con la chiave univoca decisa prima. Ogni utente ha la propria immagine distinguibile da tutte le altre. • A questo punto si arriva al dialogo VendereSceglierePassword, in cui il bot invita a scegliere una password, che serve per consentire modifiche successive agli utenti che ne sono detentori (Fig. 3.18). Anche qui, c’`e lo stesso procedimento visto nelle finestre di dialogo per la scelta dell’identificatore e nome dell’immagine dell’annuncio. La scelta viene aggiunta nella tabella PasswordAnnunci (Fig. 3.17). Figura 3.17: Tabella con le password degli annunci. • Infine, si arriva a ConfermaVendita, in cui l’utente `e costretto a decidere tra completare o annullare la pubblicazione dell’annuncio (Fig. 3.18). Nel primo caso, il bot recupera tutti i dati digitati nei passi precedenti e li inserisce nella tabella ListaAnnunci con una istruzione SQL di tipo ”INSERT”. In questo esempio, la tabella viene aggiornata come in Figura 3.19.
  • 32. CAPITOLO 3. PROGETTAZIONE DEL BOT 28 Nel secondo caso, esso invoca il dialogo di nome RecIdAnnNomeImmaginePass, presente in appendice, che rimuove dalle tabelle IdAnnunci, Chiavi, PasswordAnnunci, rispettivamen- te, l’identificatore, il nome dell’immagine e la password dell’annuncio stabiliti precedente- mente, in modo tale che possano essere usati da altri utenti. Infine, chiede all’utente di scegliere tra iniziare un’altra procedura di pubblicazione, indirizzandolo al primo dialogo della sequenza, ovvero VendereNome, oppure tornare semplicemente al main. Figura 3.18: Completamento dell’inserimento dell’annuncio. Figura 3.19: Tabella con la lista degli annunci. Facciamo ora alcune considerazioni generali sui dialoghi appena descritti: in ognuno di loro si memorizza l’informazione da pubblicare in una variabile di conversazione attraverso il meto- do PrivateConversationData.SetValue(), il quale viene poi recuperato in ConfermaVendita con PrivateConversationData.TryGetValue(). Ad esempio, in VendereNome si usa PrivateConver- sationData.SetValue(”Nome”, nomeUser), dove nomeUser `e una stringa contenente il testo del messaggio inserito dall’utente, il cui valore viene immagazzinato nella variabile ”Nome”. Que- st’ultimo viene poi recuperato in ConfermaVendita, attraverso il metodo PrivateConversation- Data.TryGetValue(”Nome”, out nomeUser), dove nomeUser `e una variabile di tipo stringa in cui viene memorizzato il valore contenuto in ”Nome”). In pi`u, `e importante ricordare che si usa PrivateConversationData e non ConversationData, perch´e si vuole che il bot offra il servizio a pi`u utenti contemporaneamente, senza che si disturbino a vicenda. Inoltre, in tutti `e presente una variabile di tipo stringa di nome verificaAgg, che serve per distinguere l’inserimento di un annuncio dall’aggiornamento. I dialoghi appena descritti possono essere, infatti, usati sia per pubblicare che per aggiornare; sono stati preferiti dei dialoghi ”poli- valenti”, piuttosto che un numero maggiore di essi che fanno le stesse cose, dal momento che le due operazioni sono quasi simili. Di questo se ne parler`a in modo pi`u approfondito nella sezione ”Gestire gli annunci”. E’ importante osservare che la struttura dei dialoghi VendereNome, VendereCognome, Ven- dereLuogo, VendereNumCell, VendereDescrizione `e pressoch´e la stessa, cambia solo il tipo di informazione che deve essere memorizzata (in VendereNome viene registrato il nome, Vendere- Cognome il cognome e cos`ı via per gli altri). Si riporta in Appendice il codice C# del primo.
  • 33. CAPITOLO 3. PROGETTAZIONE DEL BOT 29 Inoltre, per il motivo precedente, tra VendereScegliereIdAnnuncio, VendereScegliereChiave e VendereSceglierePassword `e stato deciso di inserire in Appendice il primo di essi. 3.4.2 Gestire gli annunci Dal dialogo principale, attraverso il bottone ”Gestire gli annunci” (Fig. 3.20), `e possibile far partire una conversazione guidata, costituita da una serie di finestre di dialogo, che permette agli utenti di modificare i propri annunci o di eliminarli dalla bacheca. Per garantire la sicurezza dei dati, `e stata realizzata una procedura di autenticazione, attraverso cui il bot chiede all’utente, intenzionato a usufruire del servizio di gestione, il nome, il cognome e il numero di cellulare che ha utilizzato per i suoi annunci nella fase di inserimento. Se questi sono corretti, il bot ne mostra la lista e, per autorizzare la gestione di un suo elemento, richiede la password scelta al momento della pubblicazione. Potenzialmente, ognuno pu`o conoscere il nome, il cognome e il numero di cellulare del proprietario di un annuncio, poich´e questi dati sono accessibili a tutti. Ma soltanto il reale possessore ne conosce la password e l’autenticazione fallisce quando questa non viene digitata correttamente, impedendo ogni attivit`a di modifica o di cancellazione alle persone estranee. Figura 3.20: Gestione degli annunci. Vediamo ora in dettaglio quali sono le classi che consentono di fornire tale servizio. Dal punto di vista funzionale, i dialoghi, che realizzano la conversazione per la gestione degli annunci si possono suddividere in due gruppi: quelli di autenticazione e quelli di modifica. Autenticazione E’ stato pensato di usare il nome, il cognome e il numero di cellulare, per identificare univoca- mente un proprietario dell’annuncio. Dunque, in questa fase la sequenza `e formata da:
  • 34. CAPITOLO 3. PROGETTAZIONE DEL BOT 30 • In VisualizzaAnnunciNome, VisualizzaAnnunciCognome, VisualizzaAnnunciNum- Cell il bot chiede all’utente, rispettivamente, il nome , il cognome (entrambi visibili in Fig. 3.21) e il numero di cellulare (Fig. 3.22) che aveva messo nell’annuncio. • VisualizzaAnnunci (Fig. 3.23 e 3.24): con questo dialogo il bot mostra tutti gli annunci della persona con il nome, cognome e numero di cellulare corrispondenti ai valori dei punti precedenti. Figura 3.21: Autenticazione: richiesta del nome e cognome. Figura 3.22: Autenticazione: richiesta del numero cellulare.
  • 35. CAPITOLO 3. PROGETTAZIONE DEL BOT 31 Figura 3.23: Visualizzazione del primo annuncio. Figura 3.24: Visualizzazione del secondo annuncio. A questo punto, l’utente pu`o scegliere l’annuncio che vuole gestire. Per esempio, se seleziona il primo dei due, viene rediretto al dialogo VerificaPassword, nel quale, per poterlo modificare o eliminare, viene chiesta la password stabilita al momento della pubblicazione. Se quest’ultima `e errata, allora scrive un messaggio di autenticazione fallita e si ritorna al dialogo precedente. Altrimenti, il bot propone tre attivit`a, come mostrato in Figura 3.25: aggiornare l’annuncio, eliminarlo, oppure tornare indietro nella sezione visualizzazione degli annunci. Inoltre, `e importante far notare che, per scegliere un annuncio da gestire, `e necessario premere il bottone ”Scegli”, il quale fa in modo che l’utente invi un messaggio successivo contenente il suo identificatore; questa tecnica serve al bot per capire quale
  • 36. CAPITOLO 3. PROGETTAZIONE DEL BOT 32 `e stato selezionato. Tutto ci`o non `e visibile in Fig. 3.25, perch´e Messenger presenta un bug per i bottoni con Type ”ImBack”; Di fatto, viene inviato, come appena detto, l’identificatore e non la stringa ”Scegli”. Figura 3.25: Gestione annuncio: scelta dell’operazione. Modifica Se l’utente decide di eliminare, allora viene chiamato il dialogo EliminaAnnuncio, il quale semplicemente rimuove l’annuncio e invoca VisualizzaAnnunci. Se, invece, decide di aggiornare, si imposta, come prima cosa, con il metodo PrivateConver- sationData.SetValue() la variabile di conversazione ”AggiornaAnnuncio” uguale ad ”Aggiorna”, poi si attiva la sequenza di chiamate di dialoghi seguente: • Si inizia con AggiornaAnnuncioNome, in cui il bot chiede all’utente se desidera modi- ficare il nome nell’annuncio. In caso negativo, il nome vecchio viene recuperato tramite il commando contesto.PrivateConversationDataTryGetValue(”NomeUser”, out nomeUser), dove il valore di ”NomeUser” `e gi`a stato settato precedentemente in VisualizzaAnnunci- Nome, e viene memorizzato nella variabile di conversazione ”Nome” con PrivateConversa- tionData.SetValue(). Infine, viene invocato il dialogo successivo. Se acconsente a modificare, allora viene chiamato il dialogo VendereNome, gi`a trattato nella sezione ”Mettere un annuncio”, che stabilisce nuovamente il nome ed, essendo stata la variabile ”AggiornaAnnuncio” precedentemente posta uguale alla stringa ”Aggiorna”, termina con la chiamata al dialogo AggiornaAnnuncioCognome. Infatti, se si osserva il codice C# di VendereNome posto in Appendice, nel metodo confermaNomeAsync (ri- ga 27) si recupera il valore della variabile ”AggiornaAnnuncio” e lo si memorizza nella stringa verificaAgg. Se tale valore `e uguale ad ”Aggiorna”, allora viene chiamato Aggior- naAnnuncioCognome. Altrimenti, viene invocato VendereCognome, che appartiene alla sequenza usata per l’inserimento. A seconda del valore di questa variabile si attiva o meno la funzione di aggiornamento di VendereNome.
  • 37. CAPITOLO 3. PROGETTAZIONE DEL BOT 33 • In AggiornaAnnuncioCognome il bot si comporta in modo analogo a prima, con la differenza che d`a la possibilit`a qui di modificare il cognome. Se l’utente accetta, viene chiamato dapprima VendereCognome e poi AggiornaAnnuncioLuogo. Se, invece, vuole mantenere il valore attuale, esso viene recuperato e memorizzato con la stessa procedura del passo precedente. • In AggiornaAnnuncioLuogo il bot domanda all’utente se vuole cambiare la residenza. Se egli risponde in modo negativo, il luogo vecchio viene recuperato con una query sulla tabella ListaAnnunci, con cui si estrae il valore dell’attributo ”Luogo” del record corrispondente all’identificatore dell’annuncio scelto per la gestione. In seguito, come `e stato fatto per i punti precedenti, esso viene memorizzato in una variabile. Se egli risponde in modo affermativo, allora viene chiamata la finestra VendereLuogo e si opera come nei passi precedenti. • AggiornaAnnuncioCellulare: il bot chiede allo user se vuole modificare il numero di cellulare dell’annuncio. Se egli rifiuta, il valore vecchio viene recuperato e memorizzato co- me in AggiornaAnnuncioCognome. Se accetta, viene chiamato il dialogo VendereNumCell e si opera come nei passi precedenti; • AggiornaAnnuncioDescrizione: il bot concede di cambiare la descrizione dell’annuncio. Se l’utente `e contrario, allora il valore vecchio viene recuperato e memorizzato come in AggiornaAnnuncioLuogo e AggiornaAnnuncioCellulare. Se `e favorevole, viene chiamato il dialogo VendereDescrizione e si opera come nei passi precedenti; • In AggiornaAnnuncioImmagine: domanda all’utente se vuole cambiare l’immagine del- l’annuncio. In caso negativo, per recuperare la chiave e la password vecchia, si comporta come nei punti precedenti. Infine, invoca AggiornaAnnuncio, concludendo cos`ı il proce- dimento. In caso affermativo, viene chiamato questa volta VendereScegliereChiave, che chiede all’utente se vuole caricare una nuova immagine, oppure togliere quella gi`a presen- te. Se sceglie la prima delle due opzioni, allora viene aggiornato il nome dell’immagine con uno nuovo e infine, a causa dell’impostazione della variabile ”AggiornaAnnuncio” fatta prima, viene chiamato il dialogo AggiornaChiave, che aggiorna la tabella Chiavi con il nuovo valore e termina chiamando VendereAggiungiImmagine; quest’ultimo carica l’imma- gine e conclude instradando verso VendereSceglierePassword. Una volta scelta la password, sempre per il fatto che la variabile di conversazione ”AggiornaAnnuncio” `e posta uguale ad ”Aggiorna”, viene invocato l’ultimo dialogo della sequenza, ovvero quello con nome Ag- giornaAnnuncio. Se sceglie la seconda opzione, si imposta il nome immagine uguale alla stringa ”nessuna immagine” e viene chiamato VendereSceglierePassword, da cui si arriva direttamente ad AggiornaAnnuncio; • Infine, in AggiornaAnnuncio il bot effettua un’istruzione SQL di tipo ”UPDATE”, ag- giornando i vecchi valori con quelli nuovi, che recupera, usando il comando PrivateCon- versationData.TryGetValue(), dalle variabili di conversazione impostate nei dialoghi prece- denti. Al suo termine, viene invocato nuovamente VisualizzaAnnunci, in cui l’utente vede la lista dei suoi annunci con le eventuali modifiche.
  • 38. CAPITOLO 3. PROGETTAZIONE DEL BOT 34 A titolo di esempio, si modifica l’annuncio considerato fino ad ora, lasciando invariati il nome, il cognome, il luogo di residenza, il numero di cellulare e la descrizione (per questi si vedano le figure 3.26 e 3.27), e rimuovendo solo l’immagine (Fig. 3.28). Successivamente, `e richiesto l’aggiornamento della password (Fig. 3.29) e, infine, viene mostrata all’utente proprietario la lista degli annunci con la modifica appena effettuata (Fig. 3.30). Figura 3.26: Aggiornamento: nome, cognome, residenza. Figura 3.27: Aggiornamento: numero cellulare e descrizione. Figura 3.28: Aggiornamento: immagine.
  • 39. CAPITOLO 3. PROGETTAZIONE DEL BOT 35 Figura 3.29: Completamento aggiornamento annuncio con la scelta della password. Figura 3.30: Visualizzazione delle modifiche. Poich´e la struttura dei dialoghi AggiornaAnnuncioNome, AggiornaAnnuncioCognome, AggiornaAnnuncioCellulare `e la stessa, presentiamo qui il codice C# del primo:
  • 40. CAPITOLO 3. PROGETTAZIONE DEL BOT 36 namespace BachecaAnnunci.Dialogs { public class AggiornaAnnuncioNome : IDialog<object> { public async Task StartAsync(IDialogContext contesto) { contesto.Wait(MessaggioRicevutoAsync); } private async Task MessaggioRicevutoAsync(IDialogContext contesto, IAwaitable<object> risultato) { PromptDialog.Confirm(contesto, aggiornareNome, "Vorresti aggiornare il tuo nome nell’annuncio?"); } private async Task aggiornareNome(IDialogContext contesto, IAwaitable<bool> risultato) { bool conferma = await risultato; if(conferma == true) { await contesto.Forward(new Dialogs.VendereNome(), this.metodosucc, conferma, CancellationToken.None); } else { string nomeUser = ""; contesto.PrivateConversationData.TryGetValue("NomeUser", out nomeUser); contesto.PrivateConversationData.SetValue("Nome", nomeUser); await contesto.Forward(new Dialogs.AggiornaAnnuncioCognome(), this.metodosucc, conferma, CancellationToken.None); } } private async Task metodosucc(IDialogContext contesto, IAwaitable<object> risultato) { await contesto.PostAsync("Problema interno! Sei ritornato al dialogo precedente, per ripartire da esso digita qualcosa"); } } } Un discorso analogo vale anche per AggiornaAnnuncioLuogo, AggiornaAnnuncioDescri- zione e AggiornaAnnuncioImmagine, per cui mostriamo il codice C# del primo di questi: namespace BachecaAnnunci.Dialogs { public class AggiornaAnnuncioLuogo : IDialog<object> { public async Task StartAsync(IDialogContext contesto) { contesto.Wait(MessaggioRicevutoAsync); } private async Task MessaggioRicevutoAsync(IDialogContext contesto, IAwaitable<object> risultato) { PromptDialog.Confirm(contesto, aggiornareLuogo, "Vorresti aggiornare il luogo dell’annuncio"); } private async Task aggiornareLuogo(IDialogContext contesto, IAwaitable<bool> risultato) { string idAnnuncio = ""; contesto.PrivateConversationData.TryGetValue("IdAnnuncio", out idAnnuncio); bool conferma = await risultato; if (conferma == true) { await contesto.Forward(new Dialogs.VendereLuogo(), this.metodosucc, conferma, CancellationToken.None); }
  • 41. CAPITOLO 3. PROGETTAZIONE DEL BOT 37 else { string luogo = ""; SqlConnection connessione = new SqlConnection(WebConFigurationManager.ConnectionStrings["stringaConnessione"].ConnectionString); connessione.Open(); SqlCommand cmd = new SqlCommand(@"SELECT Luogo FROM ListaAnnunci WHERE IdAnnuncio = @IdAnnuncio", connessione); cmd.Parameters.Add(new SqlParameter("@IdAnnuncio", idAnnuncio)); SqlDataReader leggi = cmd.ExecuteReader(); while (leggi.Read()) { luogo = leggi["Luogo"].ToString(); } contesto.PrivateConversationData.SetValue("Luogo", luogo); connessione.Close(); await contesto.Forward(new Dialogs.AggiornaAnnuncioCellulare(), this.metodosucc, conferma, CancellationToken.None); } } private async Task metodosucc(IDialogContext contesto, IAwaitable<object> risult) { await contesto.PostAsync("Problema interno! Sei ritornato al dialogo precedente, per ripartire da esso digita qualcosa"); } } } Il codice di AggiornaAnnuncio `e simile a quello di ConfermaVendita, cambia solo l’istruzione sql, che prevede qui l’utilizzo di ”UPDATE” al posto di ”INSERT”. 3.4.3 Trovare un annuncio Dal dialogo principale, tramite il bottone ”Trovare un annuncio”, il bot indirizza l’utente al dialogo di nome Comprare (Fig. 3.31); qui viene fatta una domanda sul tipo di ricerca che si vuole fare, che `e di due modalit`a: libera o guidata. Figura 3.31: Selezione del tipo di ricerca.
  • 42. CAPITOLO 3. PROGETTAZIONE DEL BOT 38 Ricerca libera In questa sezione del bot si cerca di costruire un semplice motore di ricerca, che ha lo scopo di verificare la disponibilit`a in bacheca degli annunci richiesti dagli utenti. Tutto ci`o `e realizzato dalla finestra di dialogo con nome RicercaLibera, che `e accessibile dal dialogo Comprare tramite il bottone ”Ricerca libera”. Nella Figura 3.32 si pu`o vedere una rappresentazione grafica della barra di ricerca. Figura 3.32: Ricerca libera. Il codice in C# che implementa il motore di ricerca `e molto corposo, dunque si preferisce spezzarlo in pi`u parti, di cui si espone il contenuto e si mostra con delle immagini il funzionamento. La parte di codice riguardante la selezione del testo dell’annuncio `e: 1 [Serializable] 2 public class RicercaLibera : IDialog<object> 3 { 4 public async Task StartAsync(IDialogContext contesto) 5 { 6 contesto.Wait(MessaggioRicevutoAsync); 7 } 8 9 private async Task MessaggioRicevutoAsync(IDialogContext contesto, IAwaitable<IActivity> risultato) 10 { 11 string idDest = ""; 12 string nomeDest = ""; 13 string idMitt = ""; 14 string nomeMitt = ""; 15 string urlServizio = ""; 16 string idCanale = ""; 17 string idConversazione = ""; 18 string testo = ""; 19 var attivit`a = await risultato as Activity; 20 if (!attivit`a.Text.Equals("indietro", StringComparison.InvariantCultureIgnoreCase)) 21 { 22 contesto.PrivateConversationData.SetValue("Activity", attivit`a);
  • 43. CAPITOLO 3. PROGETTAZIONE DEL BOT 39 23 testo = attivit`a.Text; 24 idDest = attivit`a.From.Id; 25 nomeDest = attivit`a.From.Name; 26 idMitt = attivit`a.Recipient.Id; 27 nomeMitt = attivit`a.Recipient.Name; 28 urlServizio = attivit`a.ServiceUrl; 29 idCanale = attivit`a.ChannelId; 30 idConversazione = attivit`a.Conversation.Id; 31 contesto.PrivateConversationData.SetValue<string>("Interesse", testo); 32 contesto.PrivateConversationData.SetValue<string>("IdDest", idDest); 33 contesto.PrivateConversationData.SetValue<string>("NomeDest", nomeDest); 34 contesto.PrivateConversationData.SetValue<string>("IdMitt", idMitt); 35 contesto.PrivateConversationData.SetValue<string>("NomeMitt", nomeMitt); 36 contesto.PrivateConversationData.SetValue<string>("UrlServizio", urlServizio); 37 contesto.PrivateConversationData.SetValue<string>("IdCanale", idCanale); 38 contesto.PrivateConversationData.SetValue<string>("IdConversazione", idConversazione); 39 await contesto.PostAsync($"Sei interessato al seguente annuncio: {testo}"); 40 PromptDialog.Confirm(contesto, confermaAsync, "Confermi l’annuncio?"); 41 } 42 else if (attivit`a.Text.Equals("indietro", StringComparison.InvariantCultureIgnoreCase)) 43 { 44 await contesto.Forward(new Dialogs.Comprare(), this.metodosucc, testo, CancellationToken.None); 45 } 46 } 47 $ Dal codice `e possibile vedere che il bot aspetta che l’utente ricerchi l’annuncio, digitando un messaggio (riga 19), dal quale, come prima cosa, ricava alcune informazioni (da riga 23 a 30: come l’identificatore di conversazione e canale, e altro ancora) che sono necessarie per creare un promemoria nel caso in cui l’oggetto desiderato non si trovi gi`a in bacheca. In seguito, se l’utente `e d’accordo con il testo scelto, fa la ricerca e ne mostra i risultati (Figure 3.33, 3.34 e 3.35), altrimenti lo invita ad inserire uno nuovo. Figura 3.33: Scelta del testo dell’annuncio. Figura 3.34: Visualizzazione del primo risultato.
  • 44. CAPITOLO 3. PROGETTAZIONE DEL BOT 40 Figura 3.35: Visualizzazione del secondo risultato. Un esempio di codice in C# adoperato per la ricerca e la visualizzazione degli annunci `e il seguente: 1 private async Task confermaAsync(IDialogContext contesto, IAwaitable<bool> risultato) 2 { 3 string testo = ""; 4 string idConversazione = ""; 5 string query = ""; 6 var attivit`a = new Activity(); 7 bool conferma = await risultato; 8 if (conferma == true) 9 { 10 contesto.PrivateConversationData.TryGetValue("Interesse", out testo); 11 contesto.PrivateConversationData.TryGetValue("IdConversazione", out idConversazione); 12 contesto.PrivateConversationData.TryGetValue("Activity", out attivit`a); 13 SqlConnection connessione = new 14 SqlConnection(WebConFigurationManager.ConnectionStrings["stringaConnessione"].ConnectionString); 15 connessione.Open(); 16 SqlCommand cmd = 17 new SqlCommand(@"SELECT * FROM ListaAnnunci WHERE REPLACE (REPLACE(REPLACE(Descrizione, ’,’, ’’), ’)’, ’’), ’(’, ’’) LIKE ’%’ 18 + @Testo + ’%’", connessione); 19 cmd.Parameters.Add(new SqlParameter("@Testo", testo)); 20 query = cmd.CommandText; 21 var risposta = contesto.MakeMessage(); 22 risposta.AttachmentLayout = AttachmentLayoutTypes.Carousel; 23 risposta.Attachments = new List<Attachment>(); 24 List<string> annunci = new List<string>(); 25 contesto.PrivateConversationData.SetValue("Query", query); 26 SqlDataReader leggi = cmd.ExecuteReader(); 27 while (leggi.Read()) 28 { 29 annunci.Add(leggi["IdAnnuncio"].ToString()); 30 string nomeImmagine = leggi["NomeImmagine"].ToString(); 31 List<CardImage> immagini = new List<CardImage>(); 32 CloudStorageAccount accountArchiviazione = CloudStorageAccount.Parse(ConFigurationManager.AppSettings["ChiaveArchiviazione"]); 33 CloudBlobClient clientBlob = accountArchiviazione.CreateCloudBlobClient(); 34 CloudBlobContainer container = clientBlob.GetContainerReference("immagini"); 35 if (nomeImmagine != "nessuna immagine") 36 { 37 CloudBlockBlob bloccoBlob = container.GetBlockBlobReference(nomeImmagine); 38 var urlBlob = bloccoBlob.Uri.AbsoluteUri; 39 immagini.Add(new CardImage(urlBlob)); 40 } 41 42 List<CardAction> bottoni = new List<CardAction>() 43 { 44 new CardAction(){ Title = "Compra", Type=ActionTypes.ImBack, Value=leggi["IdAnnuncio"].ToString() }, 45 new CardAction(){ Title = "Chiama", Type=ActionTypes.OpenUrl, Value=leggi["Cellulare"].ToString() }, 46 47 }; 48 49 var heroCard = new HeroCard 50 { 51 Title = leggi["Nome"].ToString() + " " + leggi["Cognome"].ToString() + ", " + leggi["Luogo"].ToString(),
  • 45. CAPITOLO 3. PROGETTAZIONE DEL BOT 41 52 Text = leggi["Descrizione"].ToString(), 53 Images = immagini, 54 Buttons = bottoni 55 56 }; 57 risposta.Attachments.Add(heroCard.ToAttachment()); 58 } 59 if (!leggi.HasRows) 60 { 61 await contesto.PostAsync("Nessun record trovato!"); 62 await contesto.Forward(new tabellaConversazione(), this.metodosucc, risposta, CancellationToken.None); 63 connessione.Close(); 64 } 65 else if (leggi.HasRows) 66 { 67 contesto.PrivateConversationData.SetValue("ListaAnnunci", annunci); 68 await contesto.PostAsync("Ecco qua il risultato della ricerca! Puoi scegliere tra " + 69 "uno degli annunci solo premendo sugli appositi tasti, " + 70 "oppure annullare la ricerca digitando la parola ’indietro’"); 71 contesto.PrivateConversationData.SetValue("ListaAnnunci", annunci); 72 await contesto.PostAsync(risposta); 73 connessione.Close(); 74 contesto.Wait(comprareAsync); 75 } 76 } 77 else if (conferma == false) 78 { 79 await contesto.PostAsync("Scrivi un altro annuncio"); 80 contesto.Wait(MessaggioRicevutoAsync); 81 } 82 } Vediamo ora in dettaglio le operazioni condotte dal bot per la ricerca e visualizzazione degli an- nunci. Come prima cosa, esso apre una connessione tcp sulla porta 1433 con il server su Azure e interroga la tabella ListaAnnunci del database con la query alla riga 16 del codice. Quest’ultima, essenzialmente, controlla se il testo digitato dall’utente `e presente in qualche descrizione degli annunci in bacheca. In caso di esito positivo, inizializza una lista di stringhe, in cui inserisce gli identificatori risultanti, che passa poi al metodo successivo. In seguito, crea un messaggio di risposta, di cui pone la propriet`a Attachments uguale ad una nuova lista di allegati, in cui inserisce, per ogni risultato che trova, una Hero Card con testo le informazioni degli annunci, come il nome, il cognome, il luogo di residenza del proprietario e la descrizione, due bottoni ed una eventuale immagine. Viene settato anche il layout della lista, preferendo una visualizzazione orizzontale degli annunci (carosello). Infine, invia la risposta con tutti gli allegati. I dati personali citati prima e la chiave dell’immagine sono ottenuti, creando un oggetto della classe SqlDataReader, in questo caso di nome ”leggi”, e sui cui viene usato il metodo leg- gi[”NomeAttributo”].ToString(). Per estrarre l’immagine, si usa un ulteriore comando, che preleva dall’account di archiviazione su Azure l’oggetto ”blob” corrispondente al nome ottenuto prima. Nella sezione ”Mettere un annuncio” era stato detto che gli annunci senza immagini presentano nel database, come valore dell’attributo ”NomeImmagine”, la stringa ”nessuna im- magine”. Se al posto di quest’ultimo fosse stato lasciato NULL, per via del metodo usato per estrarre i dati di prima, si avrebbe avuto, alla riga 30 del codice, un oggetto di tipo stringa, ovvero nomeImmagine, uguagliato ad un valore NULL di database, le quali sono due entit`a completamente diverse (una stringa nulla `e diversa da un valore NULL di un DB) e ci`o avrebbe prodotto degli errori durante la fase di compilazione del programma.
  • 46. CAPITOLO 3. PROGETTAZIONE DEL BOT 42 Riprendendo la descrizione del funzionamento del dialogo, come suggerisce anche il bot nel messaggio prima di mostrare i risultati, si nota che `e possibile scegliere uno degli annunci esclu- sivamente premendo il bottone intitolato ”Compra” corrispondente, che genera una risposta dell’utente contenente l’identificatore dell’annuncio deciso. In questo modo il bot lo distingue da tutti gli altri. Tale procedura non `e visibile in Figura 3.36, perch´e Messenger presenta un bug per le carte d’azione con tipo ”ImBack”, viene in realt`a inviato l’identificatore dell’annuncio (in questo esempio, ”galaxys6”) e non la stringa ”Compra”. Infatti, si nota che, con l’utilizzo dell’e- mulatore, la procedura appena descritta `e visibile chiaramente, confermando in questo modo la presenza del bug (Fig. 3.37). Figura 3.36: Scelta dell’annuncio con Messenger. Figura 3.37: Scelta dell’annuncio con l’emulatore. Un esempio di codice C# per scegliere un annuncio `e:
  • 47. CAPITOLO 3. PROGETTAZIONE DEL BOT 43 private async Task comprareAsync(IDialogContext contesto, IAwaitable<IActivity> risultato) { List<string> annunci = new List<string>(); string idAnnuncio = ""; contesto.PrivateConversationData.TryGetValue("ListaAnnunci", out annunci); var attivit`a = await risultato as Activity; if (annunci.Contains(attivit`a.Text)) { idAnnuncio = attivit`a.Text; contesto.PrivateConversationData.SetValue("IdAnnuncio", idAnnuncio); await contesto.Forward(new Dialogs.AnnuncioAcquistato(), this.metodosucc, attivit`a, CancellationToken.None); } else if (!annunci.Contains(attivit`a.Text) && !attivit`a.Text.Equals("indietro", StringComparison.InvariantCultureIgnoreCase)) { await contesto.PostAsync("Ricordati: puoi scegliere tra uno degli annunci " + "solo premendo sugli appositi tasti oppure annullare la ricerca digitando" + " la parola ’indietro’"); contesto.Wait(comprareAsync); } else if (attivit`a.Text.Equals("indietro", StringComparison.InvariantCultureIgnoreCase)) { await contesto.PostAsync("Sei stato rediretto al main, digita qualcosa per riattivarmi!"); contesto.Call(new Dialogs.DialogoPrincipale(), this.metodosucc); } } A questo punto, il bot recupera la lista con gli identificatori degli annunci riempita precedente- mente, e se quello scelto dall’utente non vi appartiene, allora lo invita a ripetere correttamente la procedura. Altrimenti lo salva e procede con la chiamata del dialogo AnnuncioAcquistato, che, come suggerisce il nome stesso, gestisce le operazioni successive al suo acquisto. Qui, il bot concede due possibilit`a (vedere Fig. 3.36): visualizzare eventuali annunci pubblicati dallo stes- so proprietario di quello appena comprato, oppure tornare semplicemente al dialogo principale. Scegliendo la prima opzione, attraverso la chiamata della finestra di dialogo con nome RecI- dAnnNomeImmPass, gi`a introdotto nella sezione ”Mettere un annuncio”, vengono aggiornate le tabelle IdAnnunci, Chiavi, PasswordAnnunci, eliminando tutti i record che fanno riferimento all’annuncio comprato, cio`e il suo identificatore, il nome dell’eventuale immagine allegata e la password. Poi, per vedere se il proprietario ne ha pubblicati di altri, prima di rimuoverlo defi- nitivamente dal sistema, si recuperano con una query il nome e cognome contenuti in esso, con cui si interroga successivamente la tabella ListaAnnunci e si mostrano gli eventuali risultati. Scegliendo la seconda opzione, viene utilizzato nuovamente la finestra di dialogo citata prima, per aggiornare le tabelle, e subito dopo viene eliminato l’annuncio. E’ importante far notare che il dialogo in questione ha una natura polivalente, perch´e `e usato per svolgere il suo compito sia quando si decide di annullare la pubblicazione di un annuncio, che quando si vuole eliminarlo dalla bacheca. Tale effetto `e reso grazie ad una variabile, che specifica quale delle due operazioni effettuare (vedere Appendice per maggiori dettagli). Oltre al bottone ”Compra”, si potrebbe premere quello con nome ”Chiama”, il quale permette di contattare il proprietario dell’annuncio. Se si utilizza il Chatbot con un PC, si apre di default l’applicazione Skype, mentre, se lo si adopera con un cellulare, si apre il tasto di chiamata stan- dard. Ad esempio, scegliendo di contattare il proprietario del primo annuncio in Figura 3.36, si apre la schermata seguente:
  • 48. CAPITOLO 3. PROGETTAZIONE DEL BOT 44 Figura 3.38: Contattare il proprietario tramite cellulare. Creazione di un promemoria Quando nel dialogo della ricerca libera non c’`e nessun risultato per l’annuncio richiesto, si viene indirizzati al dialogo di nome tabellaConversazione, nel quale all’utente viene chiesto se vuole creare un promemoria, per essere avvisato nel caso in cui l’oggetto richiesto diventi disponibile in bacheca. In una conversazione normale, i bot attendono un messaggio da parte degli utenti e non succede quasi mai che scrivano per primi. L’infrastruttura Bot Framework `e fatta in questo modo, per evitare che vengano mandati dei messaggi ritenuti ”spam”. Uno dei pochi casi in cui ci`o `e consentito `e quando si inviano dei promemoria (sempre chiedendo prima il consenso all’u- tente), e anche qui alcuni canali impongono delle restrizioni sul numero massimo che `e possibile trasmettere. Esempio di funzionamento: si suppone di voler trovare degli annunci in cui si offrono lezioni per la lingua inglese (Fig. 3.39). Dal momento che in bacheca non c’`e nulla a riguardo, il bot chiede all’utente se vuole creare un promemoria. In caso negativo, egli viene semplicemente riportato al dialogo principale. In caso positivo, invece il bot gli risponde come in Figura 3.40 e nel frattempo esso ha gi`a recuperato le informazioni dell’utente salvate prima e inserite con un’istruzione SQL di tipo ”INSERT” nella tabella ConversazioneUtente (Fig. 3.41).
  • 49. CAPITOLO 3. PROGETTAZIONE DEL BOT 45 Figura 3.39: Ricerca di lezioni di lingua inglese. Figura 3.40: Attivazione di un promemoria. Figura 3.41: Tabella con i dati degli utenti. Tale elenco `e in continuo aggiornamento grazie all’utilizzo di un’applicazione distribuita su Azure, ovvero il servizio di nome webjob, che conduce in modo ricorrente le seguenti attivit`a: come prima cosa, preleva da tutti i record della tabella ConversazioneUtente il valore degli attributi di
  • 50. CAPITOLO 3. PROGETTAZIONE DEL BOT 46 nome Query e Parametro, che usa per generare una nuova interrogazione sulla ListaAnnunci. Se il risultato della query `e negativo, cio`e l’annuncio cercato prima non `e ancora stato pubblicato, passa al record successivo. Se il risultato della query `e positivo, inserisce in una nuova tabella di nome TabellaAvvisi le informazioni degli utenti che devono essere avvisati della disponibilit`a in bacheca dell’annuncio richiesto prima. Infine, per maggiore scalabilit`a del servizio, elimina dalla tabella ConversazioneUtente i record relativi agli utenti gi`a posti in fase di avviso. Si aggiunge ora a scopo illustrativo un annuncio con descrizione ”lezioni di inglese” e si vede come l’azione del web job aggiorni le tabelle sopra citate. ConversazioneUtente prima `e costituita come in Figura 3.42, poi come in 3.44. TabellaAvvisi, dall’essere come in Figura 3.43, diventa come in 3.45. Figura 3.42: ConversazioneUtente (prima di azionare il webjob). Figura 3.43: TabellaAvvisi (prima di azionare il webjob). Figura 3.44: ConversazioneUtente (dopo aver azionato il webjob). Figura 3.45: TabellaAvvisi (dopo aver azionato il webjob). Nel frattempo che avvengono tali modifiche, il bot ha gi`a chiamato la finestra MessaggioProat- tivo, che resta in attesa per un certo periodo di tempo, lasciando l’utente libero di fare altro al momento, poi si attiva, interropendo il dialogo corrente, e interroga TabellaAvvisi (dove ci sono le informazioni degli utenti da avvisare). Per ogni record di quest’ultima ricava il valore degli attributi IdConversazione e idCanale, che passa come parametri alla successiva chiamata del dialogo IniziaConversazione. Quest’ultimo ha la funzione di comunicare agli user la disponi- bilit`a dell’annuncio cercato prima; infine, li elimina dalla tabella degli avvisi per comunicazioni future. Normalmente, si pone il tempo di attesa di MessaggioProattivo di qualche giorno, dando
  • 51. CAPITOLO 3. PROGETTAZIONE DEL BOT 47 cos`ı tempo a sufficienza perch´e si pubblichi l’annuncio richiesto. Quest’ultimo `e fatto in modo tale che, se uno stesso utente attiva pi`u di un promemoria per il medesimo oggetto, lo avvisa solo una volta quando l’annuncio corrispondente viene pubblicato nella bacheca. Riprendendo l’esempio di prima, poich´e ora in bacheca `e presente un annuncio in cui si offrono lezioni di inglese, a noi, che lo avevamo richiesto, arriva un messaggio di avviso (Fig. 3.46), che interrompe il dialogo corrente fino a quando la sua azione non `e terminata. Figura 3.46: Messaggio proattivo. Ricerca guidata Dal dialogo Comprare, di cui si `e discusso nelle sezioni precedenti, tramite il bottone ”Ricerca guidata” (Fig. 3.47), si attiva la conversazione guidata per aiutare l’utente a scegliere un oggetto tra un PC, un Cellulare e Altro, di cui verifica successivamente se ci sono annunci in bacheca.
  • 52. CAPITOLO 3. PROGETTAZIONE DEL BOT 48 Figura 3.47: Ricerca guidata. Figura 3.48: Dialogo per consigliare oggetti della categoria Altro. Per esempio, se l’utente desidera comprare un oggetto della categoria ”Altro” (Fig. 3.48), si avvia la seguente sequenza di finestre di dialogo: • In ComprareOggettoCategoria, ComprareOggettoDimensione, ComprareOgget- toPeso e ComprareOggettoPrezzo l’utente, rispettivamente, opera una scelta sulla categoria, dimensione e prezzo di interesse. • In ComprareOggettoNome, a seconda delle decisioni fatte nei passi precedenti, il bot propone un lista di nomi di oggetti possibili, di cui egli ne deve decidere una, per poi cercare suoi eventuali annunci nel dialogo figlio ComprareOggettoRicercaNome; in
  • 53. CAPITOLO 3. PROGETTAZIONE DEL BOT 49 caso di esito positivo, l’utente pu`o scegliere di acquistare uno di essi (analogamente a quanto visto per la ricerca libera, solo premendo il bottone ”Compra” corrispondente), oppure pu`o ritornare al dialogo iniziale (scelta della ”Categoria”). Se opta per la prima opzione, viene rediretto al dialogo AnnuncioAcquistato, e da qui si procede come visto per la ricerca libera. In ognuna di queste finestre, per mostrare la lista delle scelte ammissibili, si interroga la tabella di nome Oggetto, estraendo da essa i valori degli attributi corrispondenti alla funzione del dia- logo in questione. Per esempio, in ComprareOggettoCategoria si estraggono tutti i valori, senza ripetizioni, dell’attributo ”Categoria”, dal momento che la funzione del dialogo `e di far scegliere una categoria di interesse. In Figura 3.49 `e possibile vedere la struttura della suddetta tabella, di cui sono rappresentati solo i primi 13 record dei totali 73. Per popolare la tabella sono stati inseriti i nomi degli oggetti con le seguenti informazioni ammesse: • Categorie: Casa e cucina, Elettronica, Fai da te e giardinaggio, Libri, Sport e tempo libero. • Dimensioni: grande, media, piccola. • Pesi: minore di 1 Kg, tra 1 e 3 Kg, maggiore di 3 Kg. • Range di prezzo: minore di 20 euro, maggiore di 20 euro • Per ogni oggetto, nella colonna ”UrlInfo”, `e stato deciso anche l’url di un sito web, dove l’utente pu`o trovare informazioni a riguardo. La tabella creata rappresenta una possibile soluzione del modo in cui il Chatbot prende i dati, la quale non `e completa; l’ideale sarebbe avere a disposizione un database pronto e contenente pi`u elementi, per esempio fornito da un’azienda intenzionata ad investire sui bot. Nel mio caso ho dovuto scegliere personal- mente i dati da inserire, andando a fare delle ricerche approfondite per capire a cosa una persona poteva essere interessata. Figura 3.49: Tabella con gli oggetti di categorie diverse. In ognuno, ad eccezione di quello per stabilire la categoria dell’oggetto, l’interrogazione `e in- fluenzata dalle scelte dei dialoghi precedenti. Per esempio, in ComprareOggettoPeso si mostra il