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:
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:
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.
La verdad que es un poco complicado pero básicamente necesitamos lo siguiente:
- 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).
- 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 .
- Las voces del IVR . Son las grabaciones que se reproducirán cuando alguien llame e interactúe con la central.
- Nuestro cerebro e Internet para programar el dialplan que básicamente, es lo más dificil en todo esto.
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.
¿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 = '\0';
ConfirmationRequest confimation;
ListcustomerChoice = 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();
}
MMm...Interesante idea pero para los q hacemos pedidos de empanadas seria medio complicado.
ResponderEliminarA quiere 1 jamon y queso, 2 de palmitos y una de roquefort.
B quiere 2 de huevo, 1 de roquefort y una de carne
C quiere 1 de pollo, 1 de carne, 1 de choclo y 1 de roquefort.
Todos queremos papas grandes y una coca grande
Tenemos:
2 palmitos
1 jamon y queso
2 huevo
2 roquefort
2 carne
1 pollo
1 choclo
3 papas grandes
1 coca
La serie de botones seria un poco mas compleja para un pedido tipico en casa.
Pero ya es cuestion de interface, me sigue gustando :D
jajaja. En ese caso, entras al menu de empanadas y haces todos tus pedidos, despues confirmas y elegis la opcion de hacer otro pedido, cosa que olvide agregarle al post pero que esta disponible.
ResponderEliminarAca se puede ver:
http://1.bp.blogspot.com/_qQ2i8b8SKWI/TE4lnuHi8wI/AAAAAAAAABI/65KAryI_Ysg/s1600/delivery-part-2
Linea:
confimation = Ask("123", "pizza/1_confirmar_2_ordenar_3_salir");
Es demasiado largo pedir asi por lo que es mejor usar la version para el celular, que también hice y voy a estar posteando proximamente :D
Ciertamente para pedidos muy largos derepente va a ser hincha, pero imaginate si el lugar tiene una web (hablamos de donvito, pancholos, macdonalds, o por que no algun lomitero consciente de que tiene que tener presencia en la web) entonces entrás, y te suscribís para cliente, y podés hacer tu "pedido frecuente" por ejemplo, que lo configuraste vos por una interfaz más amigable, la idea es ofrecer una alternativa para agilizar lo que quieras pedir.
ResponderEliminarClaro, esas son variantes de la misma idea y son perfectamente realizables. Solo falta que alguien quiera adoptar la idea y hacerla realidad en un ambiente real.
ResponderEliminar