>El SDK

>

Como había dicho en mi entrada anterior, el proyecto está construido en C# usando el perfil NET_2 para hacerlo compatible con aplicaciones viejas hechas en C#1, C#2 y VisualBasic.NET 7 y 8 pero, lo más importante, para ser usable sobre Linux y MacOS usando el compilador de C# implementado por Mono a partir del ECMA-CLI.

Básicamente, el SDK consiste en una serie de clases especializadas para realizar el envío y recepción de mensajes de texto conectándose a un servidor externo (mi servidor) a través de Internet. La autenticación se realiza por medio de HTTP BASIC AUTHENTICATION por lo que conectarse a mi servidor no debería ser un problema ya que solo se utiliza el protocolo HTTP, sencillo y fácil de entender.

En este caso, tengo las clases escritas en C# pero la lógica puede ser perfectamente reimplementada en PHP o Java (por ejemplo) si es que existe alguna necesidad de usar esto desde otra tecnología de desarrollo.

No voy a profundizar mucho en el funcionamiento interno del SDK ya que voy a publicar el código, asi que si están interesados en cómo funciona, pueden descargar los fuentes y echarle una leída :).

Para que vean que realmente es fácil, pongo el diagrama de las principales clases. El código está en castellano para que sea apto para todo público, es decir, no hace falta que sepas inglés para usarlo.

El código

Originalmente lo escribí completamente en Linux usando Monodevelop, pero para asegurarme de que puede ser abierto por Visual Studio, lo llevé a Windows y confirmo que sí se puede.

En las siguientes entradas se vienen los ejemplos de uso.

Descarga

>Kit de desarrollo para Envío/Recepción de SMS

>

Muchas veces, durante el transcurso de mi vida como programador, quise agregarle funcionalidades de envío de mensajes de texto a los varios programas que había desarrollado pero esto casi nunca fue posible y cuando lo fue, tenía que enviarlos a través de mails que no siempre llegaban y que cuando llegaban, no podías responderlos con facilidad, eran difíciles de leer o de identificar al remitente pero lo peor de todo esto, era que al poco tiempo, la IP de mi servidor de correo era bloqueada por la operadora haciendo inservible todo el trabajo que había hecho.

El producto: smsSDK versión 0.1

Este SDK permite el envío y recepción de mensajes de texto a través de un número real, no hay emails ni algún otro truco raro. Es un número de teléfono como el que tenés en tu aparato celular y podes usarlo como tal. Los mensajes que salen por este servicio LLEGAN, por el siempre hecho de que son enviados usando el servicio de SMS de la operadora, pagando un saldo en guaraníes y remitidos desde una cuenta (número de teléfono) comprado y pagado mensualmente por mi grupo.

El hecho de que puedas enviar usando un número real, te permite poder recibir una respuesta. Por ejemplo: si envias el texto “hola mundo” desde el 09xx111222 al celular 09xx222333, este último puede perfectamente responder tu mensaje y vos capturar su respuesta utilizando el SDK.

¿A quiénes va dirigido ésto?

Por tratarse de un kit de desarrollo, es de esperarse que vaya dirigido a los programadores. La idea es que cualquiera que necesite agregarle este tipo de funcionalidad a su trabajo, lo haga sin mayores complicaciones y en un tiempo bajísimo ya que la curva de aprendizaje del SDK es casi una recta 🙂

¿Para qué sirve?

Le podés dar muchísimas utilidades. A continuación te cito algunas ideas:

  • Si tenés una tienda de comercio, podés enviar mensajes de texto a tus clientes informando de nuevos productos, ofertas o promociones.
  • Si tenés o formás parte de un grupo que involucre un alto número de personas, como una cooperativa, club social o fraternidad, podés enviar mensajes masivos avisando de reuniones y como el SDK te permite leer las respuestas de cada uno, sabés quiénes van y quiénes no.
  • Si tenés un consultorio médico podés hacer confirmación automatizada de citas, cosa que las secretarias siempre hacen, pero a mano.
  • Podés hacer un recordatorio que te envíe un mensaje de texto al celular de acuerdo a fechas importantes dentro de tu agenda.
¿Cómo funciona?

Es siempre mejor explicar algo con un ejemplo y así lo voy a hacer, pero en mis siguientes posts. Mientras, lo voy a describir en palabras sencillas.

El SDK esta hecho en C#, lo podés usar desde cualquier proyecto .NET. Necesitas incluir el ensamblado DLL como referencia, importar los espacios de nombre, tener una conexión decente a Internet para conectarte a mis servidores y ya está, solo eso.

¿Cuesta algo?

El SDK no cuesta nada, voy a liberar el código fuente para que cualquiera pueda usarlo, cambiarlo o estudiarlo.

El saldo para enviar mensajes sí cuesta, pero esa es otra historia que la vamos a ir cubriendo más adelante.

Si te interesa, fijate en lo que viene muy próximamente en las siguientes entradas.

>Delivery, un enfoque informático

>Yo personalmente uso mucho el delivery de comidas de cierto conocido local comercial que no vale la pena mencionar. El problema con esto es que después de convertirte en un cliente frecuente que llama siempre a pedir lo mismo o que si cambia sus gustos sabe bien que pedir, que te atienda la operadora con ese infernal ruido, te pregunte tu número de teléfono (what, again?!) y te haga repetir mil veces lo que pediste se vuelve algo tedioso y con consecuencias nerviosas difíciles de ocultar.

Que tal si te atiende un operador inteligente que te liste los menús mas comunes y te pase con la operadora ruidosa solo cuando lo que escuchaste no esta ahí (poco probable) ?. Bueno, a mi me encanta la idea.

¿Cómo solucionamos esto?

La verdad que es un poco complicado pero básicamente necesitamos lo siguiente:

  1. Una entrada de línea telefónica a través de un media gateway que nos haga de pasarela entre la red celular (GSM) y/o fija (PSTN).
  2. Un PBX, pero no cualquier PBX, tiene que ser un IPPBX porque queremos poder programarlo y no gastar ni un guaraní en adquirirlo e instalarlo. Para esto tenemos varias opciones pero mi favorita es Asterisk .
  3. Las voces del IVR . Son las grabaciones que se reproducirán cuando alguien llame e interactúe con la central.Enlace
  4. Nuestro cerebro e Internet para programar el dialplan que básicamente, es lo más dificil en todo esto.

Necesitamos saber quién es el cliente que llama, para esto tenemos su caller-id. Con ese dato podemos ir a tratar de identificarlo en una base de datos (MySQL en mi caso) buscando su nombre y dirección particular. Si no lo encontramos, lo derivamos directamente con la operadora para que le tome sus datos y lo dé de alta en el sistema, cosa que se hace siempre cuando llamas por primera vez.

Si el cliente ya está registrado, le reproducimos el menú estadísticamente más pedido o todo el menú si quieren (no es buena idea). Por ejemplo:

  • Llamo y me atiende una IVR deciéndome: “Bienvenido al bar X” y me reproduce el menú.
  • “Presione 1 para pizzas, 2 para lomitos, 3 para bebidas, 0 (cero) para hablar con un operador”.
  • Me gustaría comer pizza y presiono 1 reproduciéndome…
  • “Presione 1 para napolitana, 2 para cuatro quesos, 3 para muzarela, 4 para peperoni.”
  • Me gusta la de 4 quesos y presiono 2 y me reproduce…
  • “Seleccione la cantidad que desea”
  • Bueno, estoy con algunas personas y quiero tres, entonces aprieto 3…
  • “Usted ha pedido 3 pizzas de cuatro quesos, el precio total es, 90 mil guaraníes, presione 1 para confirmar, 2 para cambiar el pedido, 3 para salir”
  • Quiero confirmar, presiono 1…
  • “Su pedido ha sido procesado con éxito, adiós”, y termina la transacción. Opcionalmente, recibís un SMS que te confirma que tu pedido fue atendido y otro mensaje cuando se encuentra en tránsito, es decir, cuando el delivery guy se está yendo a tu casa en su moto.

Cuando llega un nuevo pedido se levanta un ticket o alerta que la cajera o algún encargado puede ver en su pantalla y éste se procesa como cualquier otro pedido hecho físicamente en la caja.

¿Cuánto tiempo tardé en pedir?

Escuchar toda la grabación me tomó 30 segundos o menos, tiempo decente pero, cuando me sepa de memoria el menú, al llamar puedo presionar 123 (1 + 2 + 3) de seguido y lo siguiente que escucharé será: “Usted ha pedido 3 pizzas de cuatro quesos, el precio total es, 90 mil guaraníes, presione 1 para confirmar, 2 para cambiar el pedido, 3 para salir”, entonces, presiono 1 y listo. Cuánto tardé?, menos de 10 segundos.

Como había dicho, lo más difícil de programar fue el dialplan. Tenía 4 opciones para esto, usar el seudolenguaje de programación de extensiones (no!!!), AEL (tampoco, pero lo usé solo para algunas operaciones de muy bajo nivel), AGI (mucho mejor, pero no) y FastAGI, el ganador.

Por qué fastAGI? porque utiliza sockets sobre IP lo que me permite manejar la carga en una suerte de computación distribuida, mucho mas escalable y encima elegante.

Hay demasiadas cosas por explicar sobre cómo funciona Asterisk y fastAGI. tal vez en otra ocasión me siente a hacer un ensayo, pero por ahora les dejo un snippet esencial para el manejo de pedidos, hecho en C#, para que se den una idea.


public override void Service(AGIRequest param1, AGIChannel param2)
{
try
{
Answer();
StreamFile("pizza/bienvenidos");
if (!IsClientRegistered(param1.CallerId))
{
Console.WriteLine("CallerID {0} is not registered, transferring to operator.", param1.CallerId);
StreamFile("pizza/numero_desconocido");
SetVariable("GO_TO_OPERATOR", "yes");
AddCustomer(param1.CallerId);
return;
}

SetVariable("GO_TO_OPERATOR", "no");

if (CustomerHasPendingOrders(param1.CallerId))
{
StreamOrderStatus();
if (Ask("13", "pizza/1_nuevaorden_3_salir") == ConfirmationRequest.Exit)
throw new Exception("User exited transaction");
}

MenuLevel menu;
PhoneKey pressedKey = '';
ConfirmationRequest confimation;
List customerChoice = new List();
bool makeMoreOrders = false;
ulong total = 0;
/*
* Say menu options
*/
while(true)
{
menu = _rootMenu;
makeMoreOrders = false;
while(true)
{
pressedKey = StreamMenu(menu);

if (menu.IsRoot && pressedKey.IsGoToOperator)
{
SetVariable("GO_TO_OPERATOR", "yes");
return;
}

if (!menu.IsRoot && pressedKey.IsGoBack)
{
menu = menu.Parent;
continue;
}

if (!menu.HasSubLevels)
{
MenuOption option = menu[pressedKey];
int quantity = AskForQuantity();

StreamCustomerOrder(option, quantity);

confimation = Ask("123", "pizza/1_confirmar_2_ordenar_3_salir");

if (confimation == ConfirmationRequest.Exit)
throw new Exception("User canceled the transaction");
else if(confimation == ConfirmationRequest.OrderMore)
makeMoreOrders = true;

customerChoice.Add(new SelectedProduct(menu[pressedKey], quantity));
total += option.Price * (ulong) quantity;

break;
}

menu = menu.GetSubMenu(pressedKey);
}

if (makeMoreOrders)
continue;

StreamFile("pizza/costo_total");
SayNumber(total.ToString());
StreamFile("pizza/guaranies");

if (Ask("13", "pizza/1_confirmar_3_salir") == ConfirmationRequest.Exit)
throw new Exception("User canceled transaction in the last minute");

foreach(SelectedProduct selection in customerChoice)
AddOrder(param1.CallerId, selection.SelectedOption.ProductID, selection.Quantity, selection.TotalPrice);

StreamFile("pizza/pedido_procesado_adios");

break;
}
}
catch(Exception ex)
{
Console.WriteLine("PizzaIVR.Service(): {0}", ex.Message);

}

Hangup();
}