Compartir a través de


El programador políglota

Háblame, cuarta parte: Feliza recibe su voz

Ted Neward

 

Ted NewardAhora nos encontramos en la recta final. En el primer artículo vimos Tropo (consulte msdn.microsoft.com/magazine/hh781028). En la segunda parte vimos Feliza, un ensamblado en F# que practica, cual prestidigitador, un simple análisis de cadenas para sonar como un psiquiatra de pacotilla (consultar msdn.microsoft.com/magazine/hh852597). En la tercera parte conectamos Tropo con el sitio web Feliza (para que Tropo recibiera los comandos del sitio web en vez de un script hospedado en el mismo servidor) y así pudimos terminar el cableado para obtener Feliza (vea msdn.microsoft.com/magazine/hh975378). Y ahora completaremos el trabajo de conexión y lanzaremos Feliza al mundo.

Recuerde del último artículo, que nuestro sueño de que Feliza pudiera atender llamadas telefónicas está limitado, momentáneamente, por la API de Tropo, pero Feliza no tiene problemas con los SMS —al 425-247-3096— o con la mensajería instantánea (mediante el controlador registrado de Jabber de feliza@tropo.im), así que seguiremos con eso. Recuerde también, que Tropo puede controlar fácilmente las entradas de voz si hay un conjunto de opciones enlazado del cual elegir, de modo que si quisiéramos limitar Feliza a entradas fijas, podríamos fácilmente volver a la voz. Sin embargo, en el caso de Feliza, no me parece que eso otorgue una buena experiencia de usuario: “¡Hola! Si estás deprimido, di ‘deprimido’; si te sientes mal, di ‘me siento mal’; si sientes ganas de liquidar un sistema de terapia automatizado, di ‘muere, muere, muere’…”

A pesar de esta limitación, Feliza puede ayudar a las personas (bueno, más o menos), así que prosigamos. Recuerde que necesitamos ubicar Feliza detrás de un sitio web que sepa cómo responder a las solicitudes REST/JSON que Tropo nos entrega, así que debemos saber interpretar el JSON entrante, pasarlo a Feliza y luego generar la respuesta. Esto significa que necesitamos una estructura ASP.NET MVC que represente el JSON entrante y otra que represente la respuesta saliente.

La creación de una clase de Microsoft .NET Framework que coincida con esto (para que ASP.NET MVC pueda hacer el trabajo pesado de analizar el JSON) es bastante fácil, como podemos apreciar en la Ilustración 1.

Ilustración 1 Clase .NET con estructuras Tropo que coinciden con los objetos JSON

public class Session
{
  public string accountId { get; set; }
  public string callId { get; set; }
  public class From
  {
    public string id { get; set; }
    public string name { get; set; }
    public string channel { get; set; }
    public string network { get; set; }
  }
  public From from { get; set; }
  public class To
  {
    public string id { get; set; }
    public string name { get; set; }
    public string channel { get; set; }
    public string network { get; set; }
  }
  public To to { get; set; }
  public string id { get; set; }
  public string initialText { get; set; }
  public string timestamp { get; set; }
  public string userType { get; set; }
};

No todas las propiedades que vemos en la Ilustración 1 estarán siempre rellenas, pero esto brinda un ejemplo de trabajo de cómo asignar la estructura Tropo con un objeto JSON.

De este modo, parecería ser bastante sencillo analizar el JSON entrante (un objeto de “sesión”), extraer el texto del campo initialText, generar un objeto de respuesta y devolverlo, tal como se aprecia en la Ilustración 2.

Ilustración 2 Análisis de JSON

[AcceptVerbs("GET", "POST")]
public ActionResult Index(Tropo.JSON.Session session)
{
  string incoming = session.initialText;
  object felizaResponse = new
  {
    tropo = new object[]
    {
      new
      {
        ask = new Tropo.JSON.Ask()
        {
          say = new Tropo.JSON.Say() {
          value = "I'm Feliza. What about '" + incoming +
            "' would you like to talk about?"
          },
          choices = new Tropo.JSON.Ask.Choices() {
            value = "[ANY]"
          }
        }
      }
    }
  };
  return Json(felizaResponse);
}

Desafortunadamente, nos topamos con una limitación del serializador JSON de ASP.NET MVC, ya que todos los valores nulos del objeto devuelto se convertirán en valores “null” en la respuesta serializada JSON, y Tropo tiene problemas con los campos JSON de valor nulo. Afortunadamente, podemos solucionar esto con un serializador JSON personalizado (en este caso, el excelente serializador Json.NET de James Newton-King, disponible en bit.ly/a27Ou), que se puede configurar para que no serialice los valores nulos. Esto cambia un poco el código para devolver un ActionResult personalizado (estamos muy agradecidos de obtenerlo de bit.ly/1DVucR) en lugar del estándar (ver la Ilustración 3).

Ilustración 3 ActionResult personalizado

public class JsonNetResult : ActionResult
{
  public Encoding ContentEncoding { get; set; }
  public string ContentType { get; set; }
  public object Data { get; set; }
  public JsonSerializerSettings SerializerSettings { get; set; }
  public Formatting Formatting { get; set; }
  public JsonNetResult() { SerializerSettings =
    new JsonSerializerSettings(); }
  public override void ExecuteResult(ControllerContext context)
  {     
    if (context == null)
      throw new ArgumentNullException("context");
    HttpResponseBase response = context.HttpContext.Response;
    response.ContentType = !string.IsNullOrEmpty(ContentType) ?
      ContentType : "application/json";
    if (ContentEncoding != null)
      response.ContentEncoding = ContentEncoding;
    if (Data != null)
    {
      JsonTextWriter writer =
        new JsonTextWriter(response.Output) 
        { Formatting = Formatting };
      JsonSerializer serializer =
        JsonSerializer.Create(SerializerSettings);
      serializer.Serialize(writer, Data);
      writer.Flush();
    }
  }
}

Luego, en el Controlador, usamos este ActionResult en lugar del basado en JSON, asegurándonos de configurarlo para que no envíe los valores nulos:

[AcceptVerbs("GET", "POST")]
public ActionResult Index(Tropo.JSON.Session session)
{
  string incoming = session.initialText;
  object felizaResponse = // ...;
  JsonNetResult jsonNetResult = new JsonNetResult();
  jsonNetResult.SerializerSettings.NullValueHandling =
    NullValueHandling.Ignore;
  jsonNetResult.Data = felizaResponse;
  return jsonNetResult;
}

Entonces, para los lectores que juegan en casa con el código, a estas alturas el servidor puede recibir mensajes SMS entrantes, seleccionar el texto entrante y generar una respuesta. Probablemente ya adivinó lo que sigue.

Encantado de conocerte, Feliza

Ha llegado el momento de completar el circuito e incorporar los binarios de F# de Feliza del segundo artículo de esta serie. Copie los binarios compilados del código antiguo o, si lo prefiere, cree un (segundo) proyecto de Biblioteca de F# en la solución Tropo de ASP.NET MVC, marque el proyecto TropoApp como dependiente del proyecto de Feliza y copie el código. (Asegúrese de que el proyecto ASP.NET MVC también esté informado sobre las dependencias de F#, específicamente de FSharp.Core.dll.)

Ahora generar la respuesta es tan trivial como tomar el texto entrante, pasarlo al método respond de Feliza1 y capturar los resultados en el JSON devuelto, tal como se aprecia en la Ilustración 4.

Ilustración 4 Generación de una respuesta

[AcceptVerbs("GET", "POST")]
public ActionResult Index(Tropo.JSON.Session session)
{
  object felizaResponse = new
  {
    tropo = new object[]
    {
      new
      {
        ask = new Tropo.JSON.Ask()
        {
          say = new Tropo.JSON.Say() {
            value = Feliza1.respond(session.initialText)
          },
          choices = new Tropo.JSON.Choices() {
            value = "[ANY]"
          }
        }
      }
    }
  };
  JsonNetResult jsonNetResult = new JsonNetResult();
  jsonNetResult.SerializerSettings.NullValueHandling =
    NullValueHandling.Ignore;
  jsonNetResult.Data = felizaResponse;
  return jsonNetResult;
}

Realmente es así de fácil: pruébelo, si tiene un teléfono a mano, y verá que puede entablar una especie de conversación por texto con Feliza. No es el chat-bot más inteligente del mundo, pero con el núcleo en F# que lo respalda, podremos hacer mucho para mejorar sus respuestas.

Incorporación de voz o SMS en las aplicaciones

Terminamos Feliza, y ahora el mundo puede descansar tranquilo, sabiendo que si alguien está despierto a altas horas de la noche, anhelando que alguien le envíe un mensaje de texto, Feliza está preparada y esperando a esa persona. Claro que no será una conversación muy satisfactoria porque, la mayoría de las veces, no tendrá ni la menor idea de lo que está leyendo.

La API de Tropo presenta algunas excentricidades interesantes, pero sin duda que tiene un potencial enorme. Los modelos gemelos (los scripts hospedados así como la “API de web”) ofrecen una flexibilidad interesante cuando se piensa en cómo incorporar voz, SMS o mensajería instantánea en las aplicaciones.

El sitio web de Feliza también podría mejorarse de muchas maneras. Por ejemplo, los que tienen más afinidad con la web móvil, podrían crear un conjunto de páginas HTML5 estáticas y usar jQuery para consultar Feliza con el texto escrito y agregar las respuestas a la página. Básicamente, Tropo ofrece un nuevo conjunto de “canales” para recibir las entradas e, idealmente, esos canales normalizarán las entradas en un único conjunto de estructuras JSON para simplificar el código extremo de ASP.NET MVC. Y claramente, la historia de Tropo combinaría muy bien con otros medios de comunicación con los clientes o usuarios. Otros canales interesantes que podríamos agregar son Twitter o Facebook, por mencionar dos ejemplos obvios. (Dicho sea de paso, Tropo ofrece la posibilidad de enlazarse con Twitter, lo que simplificaría ese canal puntual, pero en el caso de Facebook deberíamos crear el canal a mano).

Por ahora, hay otros temas urgentes, así que es hora de despedirnos de Feliza y pasar a otro capítulo. Pero no se preocupe, ella estará ahí cuando se sienta solo. Mientras tanto…

¡Feliz codificación!

Ted Neward es asesor arquitectónico en Neudesic LLC. Ha escrito más de 100 artículos, es un MVP de C# y orador de INETA, y ha sido autor y coautor de aproximadamente una docena de libros, incluido el recientemente publicado “Professional F# 2.0” (Wrox, 2010). Realiza asesorías con regularidad y asiste como mentor. Puede comunicarse con él en ted@tedneward.com, si está interesado en recibir ayuda suya para su equipo, o puede leer su blog en blogs.tedneward.com.