Espera un momento ...
¿Te gustaría llevar mi curso de Laravel, gratis?
Sólo debes ingresar tus datos:
En la actualidad existen muchas herramientas para crear chatbots, sin la necesidad de programarlos.
Algunas herramientas son buenas y otras no lo son tanto. Esto se determina principalmente por la facilidad de uso, y los distintos escenarios que cubren.
¿Pero qué pasa si queremos desarrollar nuestro propio chatbot desde cero, sin depender de herramientas de terceros?
¿Es acaso eso posible? ¿Realmente podemos construir un chatbot que nos resulte útil?
La respuesta es sí. Es posible, y hoy veremos cómo hacerlo.
En resumen:
Existen aplicaciones, que cuentan con una interfaz gráfica de usuario, y que nos permiten crear nuestros propios bots. ¿Por qué hemos de desarrollar uno desde cero?
Tenemos 3 razones principales:
Podemos construir un bot para distintos canales. Entre los canales más populares tenemos Facebook Messenger, Slack, Twitter y Telegram.
En este artículo vamos a hablar de forma específica acerca del desarrollo de chatbots para Facebook Messenger.
¿Por qué? Principalmente porque Messenger es la plataforma más popular para chatbots. Casi todas las herramientas para construir chatbots se centran en Messenger, y algunas incluso sólo soportan Messenger. Y existe una buena razón para ello: presenta 2.167 millones de usuarios activos al mes (dato correspondiente a inicios del 2018).
Otra razón por la que se tiende a priorizar Messenger: botones de respuesta rápida.
Hay botones que nuestro chatbot puede ofrecer a los usuarios como un atajo y ahorrar que ellos lo tengan que escribir. Esto no sólo hace que el bot sea más atractivo (¿a quién le gusta tener que escribir desde un móvil?), también hace que nuestro trabajo como desarrolladores de chatbots sea mucho más sencillo.
Si ofrecemos botones a los usuarios, entonces ellos los usarán. Esto significa que no debemos preocuparnos por "parsear" consultas arbitrarias, que un usuario podría escribir (en lenguaje natural y probablemente fuera de contexto).
Guiar a nuestros usuarios es bueno para ellos, pero lo es también para nosotros.
Vamos a diseñar nuestro bot basados en un árbol de nodos.
Los posibles estados del bot se determinan en función a este árbol.
Los nodos representan:
El siguiente árbol representa entonces cada flujo de conversación posible.
Dedica unos segundos para analizarlo.
say: "Hola! Por favor selecciona una opción para poder ayudarte."
answers:
Cursos disponibles:
say: Tenemos varios cursos! Todos ellos son muy interesantes y totalmente prácticos. Por favor selecciona la opción que te resulte más interesante.
answers:
Dominar Javascript:
say: https://www.udemy.com/javascript-curso-practico-y-completo/?couponCode=INVITACION
Aprender Laravel:
say: https://www.udemy.com/curso-laravel-5-5-desde-cero-desarrolla-publica-una-app-pedidos/?couponCode=INVITACION https://www.udemy.com/laravel-y-oauth-2-facebook-twitter-google/?couponCode=INVITACION
Integrar Laravel+Vue:
say: https://www.udemy.com/realtime-messenger-usando-laravel-vue-bootstrap-pusher/?couponCode=PROMOCION
Tengo una duda:
say: ¿Es una duda sobre un video que viste en el canal de Youtube? ¿O necesitas ayuda personalizada?
answers:
Es sobre un video:
say: Gracias por visitar el canal. Por favor escribe tu duda en un comentario, y te responderé por allí tan pronto como pueda.
Busco asesoría:
say: Gracias por tu interés. Por favor visita este enlace, donde verás cómo puedo ayudarte https://programacionymas.com/asesoria
Solicitar desarrollo:
say: ¿Tienes definido formalmente el proyecto y estás dispuesto a invertir en él?
answers:
Aún no está definido:
say: Por favor cuéntame más y entonces te diré cómo puedo ayudarte.
No tengo presupuesto:
say: Entiendo. En tal caso te recomiendo inscribirte a mis cursos sobre programación. Puedes aprender mucho con los tutoriales, y además siempre atiendo todas las dudas.
Sí y sí:
say: Genial. Por favor envíame un documento con los requerimientos y te contactaré tan pronto como tenga una propuesta.
Como ves, el árbol de conversación es bastante sencillo. Está escrito en formato YAML (Yet Another Markup Language), lo que facilita su lectura.
El nodo raíz especifica el primer mensaje que el bot envía al usuario. En este caso el mensaje inicial es "Hola. ¿En qué te puedo ayudar?", y según la respuesta del usuario, la conversación con el bot fluye.
Para desarrollar nuestro bot, tenemos primero que configurar un par de cosas en Facebook.
Las instrucciones oficiales se pueden encontrar aquí, pero en resumen, vamos a necesitar:
secret access token
(lo necesitaremos posteriormente).Los bots para Facebook funcionan a través de webhooks, que son URLs que nosotros definimos y que Facebook Messenger usará para interactuar con nuestro bot.
Para publicar nuestro webhook usaremos Google App Engine. La ventaja de esto es que resulta gratuito para bajos volúmenes de tráfico, y escala automáticamente si necesitamos más. Así mismo usaremos Python como lenguaje de programación, pero en realidad se puede lograr también con cualquier otro lenguaje.
Vamos a necesitar descargar el Python SDK y crear un proyecto de Google Cloud si aún no tenemos uno.
La primera misión de nuestro webhook es permitirle a Facebook verificar que realmente se trata de un webhook auténtico. Para ello simplemente tenemos que gestionar una petición GET, que contiene un token de verificación (es una cadena secreta y aleatoria, que debemos definir en Facebook).
La verificación es posible usando el siguiente código:
class MainPage(webapp2.RequestHandler):
def get(self):
self.response.headers['Content-Type'] = 'text/plain'
mode = self.request.get("hub.mode")
if mode == "subscribe":
challenge = self.request.get("hub.challenge")
verify_token = self.request.get("hub.verify_token")
if verify_token == VERIFY_TOKEN:
self.response.write(challenge)
else:
self.response.write("Ok")
Es así como definimos una clase para gestionar peticiones (usando el framework webapp2
).
Aunque no se muestra en el fragmento anterior, esta clase presenta también un constructor que se encarga de inicializar nuestra clase bot.
El código completo está disponible en Github, pero te recomiendo seguir el curso para desarrollar este chatbot desde cero y paso a paso.
Necesitamos interpretar los mensajes que escriben los usuarios. Para ello primero necesitamos capturar estos mensajes.
Estos son enviados por Facebook a nuestro webhook, a través de peticiones POST.
def post(self):
logging.info("Data obtenida desde Messenger: %s", self.request.body)
data = json.loads(self.request.body)
if data["object"] == "page":
for entry in data["entry"]:
for messaging_event in entry["messaging"]:
sender_id = messaging_event["sender"]["id"]
recipient_id = messaging_event["recipient"]["id"]
if messaging_event.get("message"):
# Eventos de tipo message
if messaging_event.get("postback"):
# Eventos de tipo postback
Aquí "parseamos" la información que recibimos en formato JSON desde Facebook, para que posteriormense se pueda analizar y procesar.
Básicamente Facebook Messenger nos permite suscribirnos a eventos de 2 tipos: eventos message
(cuando el usuario escribe) y eventos postback
(que son enviados cuando un usuario hace clic en un botón de respuesta).
Entonces iteramos sobre los messaging events en general, y dependiendo el tipo decidimos cómo actuar.
Luego de recibir la información que nos envía Facebook e identificar los mensajes, invocamos al método handle
para que nuestra clase Bot se encargue de su procesamiento.
El fragmento anterior sólo muestra la estructura general.
Cuando instanciamos nuestra clase Bot, enviamos la función send_message
al constructor.
Esta función permite a nuestro bot devolver mensajes de respuesta a los usuarios. Y su definición es la siguiente:
def send_message(recipient_id, message_text, possible_answers):
headers = {
"Content-Type": "application/json"
}
message = get_postback_buttons_message(message_text, possible_answers)
if message is None:
message = {"text": message_text}
raw_data = {
"recipient": {
"id": recipient_id
},
"message": message
}
data = json.dumps(raw_data)
logging.info("Enviando mensaje a %r: %s", recipient_id, message_text)
r = urlfetch.fetch("https://graph.facebook.com/v2.6/me/messages?access_token=%s" % ACCESS_TOKEN,
method=urlfetch.POST, headers=headers, payload=data)
if r.status_code != 200:
logging.error("Error %r enviando mensaje: %s", r.status_code, r.content)
La variable recipient_id
que esta función recibe se corresponde con el identificador del usuario al que vamos a responder.
Junto a esta variable tenemos como parámetros: el texto a enviar, y algunos botones de respuesta rápida (que el usuario podrá presionar).
Primero nos aseguramos que las cabeceras de nuestra petición especifiquen el formato a usar (JSON), y entonces agregamos nuestros botones postback como parte del mensaje.
Estos botones no estarán presentes siempres, sólo en algunos casos, dependiendo de lo que se defina en nuestro árbol. De todas formas, para los casos en que están presentes, la función get_postback_buttons_message
es la encargada de dar a estos botones el formato adecuado (según lo exige Facebook).
Finalmente hacemos nuestra petición al Facebook Graph API, enviando el access token
que Facebook nos dio cuando registramos nuestra app.
El código final de nuestro archivo principal, contiene lo siguiente, que es necesario para construir la clase principal y ejecutar el webhook que va a representar a nuestro bot:
app = webapp2.WSGIApplication([
('/', MainPage),
], debug=True)
Y ahora llegamos a un punto interesante.
¿Cómo sabe el bot qué decir? El cerebro de nuestro bot se corresponde con el archivo bot.py
.
class Bot(object):
def __init__(self, send_callback, users_dao, tree):
self.send_callback = send_callback
self.users_dao = users_dao
self.tree = tree
def handle(self, user_id, user_message, is_admin=False):
# lógica del bot
La clase es inicializada con 3 parámetros:
Como es de notarse, la clase handle
es la que contiene la lógica principal del bot.
El código anterior sólo muestra un fragmento del método. Pero en resumen aquí:
DAO
(data access object
). Esto nos permitirá reproducir las acciones del usuario, para descubrir dónde es que nos encontramos según el árbol.
Se definen un mensaje y unos botones por defecto, que serán devueltos en caso que el usuario diga algo que el bot no entiende.
El historial de mensajes es muy importante. Estos mensajes guardan los textos enviados tanto por el usuario como por el bot.
Finalmente, tras recorrer todo el historial, escribimos nuestra respuesta (en un log y en una base de datos propia), y enviamos el mensaje de respuesta al usuario.
Lo último pero no menos importante es la definición de un data access object
y un modelo que represente a los eventos notificados por Messenger.
Estas clases en conjunto nos permiten gestionar toda la información relacionada con los mensajes intercambiados (considerando 3 actores: usuarios, bot y administrador).
class UserEvent(ndb.Model):
user_id = ndb.StringProperty()
author = ndb.StringProperty()
message = ndb.StringProperty()
date = ndb.DateTimeProperty(auto_now_add=True)
class UserEventsDao(object):
def add_user_event(self, user_id, author, message):
event = UserEvent()
event.user_id = user_id
event.author = author
event.message = message
event.put()
logging.info("Evento registrado: %r", event)
def get_user_events(self, user_id):
events = UserEvent.query(UserEvent.user_id == user_id).order(UserEvent.date)
return [(event.message, event.author) for event in events]
def remove_user_events(self, user_id):
events = UserEvent.query(UserEvent.user_id == user_id)
quantity = events.count()
for event in events:
event.key.delete()
logging.info("Se eliminaron %r eventos", quantity)
def admin_messages_exist(self, user_id):
events = UserEvent.query(UserEvent.user_id == user_id, UserEvent.author == 'admin')
return events.count() > 0
Nuestro DAO hace uso de Google Datastore. Y la API de Python hace que el uso de Datastore sea muy fácil.
En el fragmento anterior:
Primero creamos una clase modelo UserEvent
, que especifica los campos y sus tipos.
En nuestro caso, el user ID, el autor del mensaje, y el mensaje mismo son String
, y finalmente la fecha del evento es de tipo DateTime
.
Para crear y almacenar un nuevo evento de usuario, simplemente instanciamos esta clase, fijamos las propiedades, y llamamos al método put()
desde el objeto.
query()
y usamos como filtro el user ID
.
Aquí ordenamos los eventos por fecha, y devolvemos una lista de tuplas.Hemos dado un vistazo al código que compone nuestro bot! Ahora corresponde realizar el proceso de despliegue, para finalmente conectarlo con Messenger.
Para desplegar nuestra aplicación sobre App Engine, usamos el comando gcloud
que viene con el App Engine SDK:
gcloud app deploy
Una vez realizado el proceso de deployment, la URL de nuestro webhook será:
http://[PROJECT_ID].appspot.com/
Entonces actualizamos nuestra app Facebook con esta URL de webhook y estaremos listos!
Recuerda que puedes desarrollar todo tipo de bots tomando como base lo aprendido en este artículo/curso.
Si te has perdido en algún punto, te sugiero (y te agradecería mucho) que te inscribas al curso "Desarrolla tu primer Chatbot para Messenger usando Python".
En el curso vemos todo lo expuesto en este artículo, pero paso a paso, y con un mayor nivel de detalle. Además, si te inscribes tendrás acceso a una sección de preguntas y respuestas donde resolveré todas tus dudas.
Comparte este post si te fue de ayuda 🙂.
Regístrate
Accede a todos los cursos, y resuelve todas tus dudas.
Cursos Recomendados
Desarrolla tu primer Chatbot para Facebook Messenger sobre Google Cloud, y aprende Python en el camino!
Iniciar cursoCurso intensivo. Incluye el desarrollo de una API, su consumo, y autenticación vía JWT. También vemos Kotlin desde 0.
Iniciar cursoAprende por qué es importante y cómo funciona Docker, con este nuevo curso práctico!
Iniciar cursoEspera un momento ...
¿Te gustaría llevar mi curso de Laravel, gratis?
Sólo debes ingresar tus datos: