Cree una aplicación de preguntas frecuentes en Slack con funciones de Netlify y FaunaDB
A veces, cuando buscas una respuesta rápida, es realmente útil contar con un sistema de preguntas frecuentes, en lugar de esperar a que alguien responda una pregunta. ¿No sería fantástico si Slack pudiera responder estas preguntas frecuentes por nosotros? En este tutorial, vamos a crear precisamente eso: un comando de barra diagonal para Slack que responderá las preguntas frecuentes de los usuarios. Almacenaremos nuestras respuestas en FaunaDB, usaremos FQL para buscar en la base de datos y utilizaremos una función Netlify para proporcionar un punto final sin servidor para conectar Slack y FaunaDB.
Requisitos previos
Este tutorial asume que tiene los siguientes requisitos:
- Cuenta de Github, utilizada para iniciar sesión en Netlify y Fauna, además de almacenar nuestro código.
- Espacio de trabajo de Slack con permiso para crear e instalar nuevas aplicaciones
- Nodo.js v12
Crear paquete npm
Para comenzar, cree una nueva carpeta e inicialice un paquete npm utilizando el administrador de paquetes de su elección y ejecútelo npm init -y
desde dentro de la carpeta. Una vez creado el paquete, tenemos algunos paquetes npm para instalar.
Ejecute esto para instalar todos los paquetes que necesitaremos para este tutorial:
npm install express body-parser faunadb encoding serverless-http netlify-lambda
Estos paquetes se explican a continuación, pero si ya estás familiarizado con ellos, no dudes en seguir adelante.
La codificación se instaló debido a un error de complemento que ocurrió en @netlify/plugin-functions-core al momento de escribir este artículo y es posible que no sea necesario cuando siga este tutorial.
Paquetes
Express es un marco de aplicación web que nos permitirá simplificar la escritura de múltiples puntos finales para nuestra función. Las funciones de Netlify requieren controladores para cada punto final, pero Express combinado con http sin servidor nos permitirá escribir todos los puntos finales en un solo lugar.
Body-parser es un middleware rápido que se encargará de los application/x-www-form-urlencoded
datos que Slack enviará a nuestra función.
Faunadb es un módulo npm que nos permite interactuar con la base de datos a través del controlador Javascript de FaunaDB. Nos permite pasar consultas de nuestra función a la base de datos para obtener las respuestas.
Serverless-http es un módulo que empaqueta las aplicaciones Express al formato esperado por las funciones de Netlify, lo que significa que no tendremos que reescribir nuestro código cuando pasemos del desarrollo local a Netlify.
Netlify-lambda es una herramienta que nos permitirá construir y servir nuestras funciones localmente, de la misma manera que se construirán e implementarán en Netlify. Esto significa que podemos desarrollar localmente antes de enviar nuestro código a Netlify, aumentando la velocidad de nuestro flujo de trabajo.
Crear una función
Con nuestros paquetes npm instalados, es hora de comenzar a trabajar en la función. Usaremos la tecnología sin servidor para empaquetar una aplicación rápida, lo que nos permitirá implementarla en Netlify más adelante. Para comenzar, cree un archivo llamado netlify.toml
y agregue lo siguiente:
[build] functions = "functions"
Usaremos un archivo .gitignore para evitar que nuestras carpetas node_modules y funciones se agreguen a git más adelante. Cree un archivo llamado .gitignore y agregue lo siguiente:
funciones/
módulos_nodo/
También necesitaremos una carpeta llamada src y un archivo dentro de ella llamado server.js. Su estructura de archivos final debería verse así:
Una vez implementado esto, cree una aplicación Express básica insertando el siguiente código en server.js:
const express = require("express");const bodyParser = require("body-parser");const fauna = require("faunadb");const serverless = require("serverless-http"); const app = express(); module.exports.handler = serverless(app);
Mira la línea final; Se ve un poco diferente a una aplicación Express normal. En lugar de escuchar en un puerto, pasamos nuestra aplicación sin servidor y la usamos como nuestro controlador, para que Netlify pueda invocar nuestra función.
Configuramos nuestro analizador corporal para usar application/x-www-form-urlencoded
datos, además de instalar un enrutador. Agregue lo siguiente a server.js después de definir la aplicación:
const router = express.Router();app.use(bodyParser.json());app.use(bodyParser.urlencoded({ extended: true }));app.use("/.netlify/functions/server", router);
Observe que el enrutador lo está utilizando /.netlify/functions/server
como punto final. Esto es para que Netlify pueda implementar correctamente la función más adelante en el tutorial. Esto significa que necesitaremos agregar esto a cualquier URL base para poder invocar la función.
Crear una ruta de prueba
Con una aplicación básica implementada, creemos una ruta de prueba para comprobar que todo funciona. Inserte el siguiente código para crear una ruta GET simple, que devuelva un objeto json simple:
router.get("/test", (req, res) = { res.json({ hello: "world" });});
Con esta ruta implementada, activamos nuestra función en localhost y verificamos que obtenemos una respuesta. Usaremos netlify-lambda para servir nuestra aplicación, de modo que podamos imitar una función de Netlify localmente en el puerto 9000. En nuestro package.json
, agregue las siguientes líneas en la sección de scripts:
"start": "./node_modules/.bin/netlify-lambda serve src", "build": "./node_modules/.bin/netlify-lambda build src"
Con esto en su lugar, después de guardar el archivo, podemos ejecutar npm start
para iniciar netlify-lambda en el puerto 9000.
El comando de compilación se utilizará cuando implementemos en Netlify más adelante.
Una vez que esté en funcionamiento, podemos visitarlo http://localhost:9000/.netlify/functions/server/test
para comprobar que nuestra función funciona como se esperaba.
Lo mejor de netlify-lambda es que escuchará los cambios en nuestro código y lo recompilará automáticamente cada vez que actualicemos algo, por lo que podremos dejarlo ejecutándose mientras dure este tutorial.
Iniciar URL de ngrok
Ahora que tenemos una ruta de prueba funcionando en nuestra máquina local, la hagámosla disponible en línea. Para hacer esto, usaremos ngrok, un paquete npm que proporciona una URL pública para nuestra función. Si aún no tiene ngrok instalado, primero ejecútelo npm install -g ngrok
para instalarlo globalmente en su máquina. Luego ejecutar ngrok http 9000
, que dirigirá automáticamente el tráfico a nuestra función que se ejecuta en el puerto 9000.
Después de iniciar ngrok, debería ver una URL de reenvío en la terminal, que podemos visitar para confirmar que nuestro servidor está disponible en línea. Copie esta URL base en su navegador y sígala con /.netlify/functions/server/test
. Deberías ver el mismo resultado que cuando hicimos nuestras llamadas en localhost, lo que significa que ahora podemos usar esta URL como punto final para Slack.
Cada vez que reinicia ngrok, crea una nueva URL, por lo que si necesita detenerlo en algún momento, deberá actualizar su punto final de URL en Slack.
Configurar holgura
Ahora que tenemos una función implementada, es hora de pasar a Slack para crear la aplicación y el comando de barra diagonal. Tendremos que implementar esta aplicación en nuestro espacio de trabajo, además de realizar algunas actualizaciones en nuestro código para conectar nuestra función. Para obtener un conjunto de instrucciones más detalladas sobre cómo crear un nuevo comando de barra diagonal, puedes seguir la documentación oficial de Slack. Para obtener un conjunto simplificado de instrucciones, siga las siguientes instrucciones:
Crea una nueva aplicación de Slack
En primer lugar, creemos nuestra nueva aplicación Slack para estas preguntas frecuentes. Visita https://api.slack.com/apps y selecciona Crear nueva aplicación para comenzar. Asigne un nombre a su aplicación (usó Fauna FAQ) y seleccione un espacio de trabajo de desarrollo para la aplicación.
Crear un comando de barra diagonal
Después de crear la aplicación, debemos agregarle un comando de barra diagonal para que podamos interactuar con la aplicación. Seleccione los comandos de barra diagonal del menú después de que se haya creado la aplicación y luego cree un nuevo comando. Complete el siguiente formulario con el nombre de su comando (yo usé /faq) y proporcione la URL de ngrok. ¡No olvides agregar /.netlify/functions/server/
al final!
Instalar la aplicación en el espacio de trabajo.
Una vez que haya creado su comando de barra diagonal, haga clic en información básica en la barra lateral de la izquierda para regresar a la página principal de la aplicación. Desde aquí, seleccione el menú desplegable “Instalar aplicación en su espacio de trabajo” y haga clic en el botón para instalarla.
Una vez que haya permitido el acceso, la aplicación se instalará y podrá comenzar a usar el comando de barra diagonal en su espacio de trabajo.
Actualizar la función
Con nuestra nueva aplicación implementada, necesitaremos crear un nuevo punto final al que Slack envíe las solicitudes. Para esto, usaremos el punto final raíz por simplicidad. El punto final deberá poder aceptar una solicitud de publicación con application/x-www-form-urlencoded
datos y luego devolver una respuesta de estado 200 con un mensaje. Para hacer esto, creemos una nueva ruta de publicación en la raíz agregando el siguiente código a server.js
:
router.post("/", async (req, res) = { });
Ahora que tenemos nuestro punto final, también podemos extraer y ver el texto enviado por Slack agregando la siguiente línea antes de establecer el estado:
const text = req.body.text;console.log(`Input text: ${text}`);
Por ahora, simplemente pasaremos este texto a la respuesta y lo enviaremos de vuelta al instante para garantizar que la aplicación y la función de Slack se comuniquen.
res.status(200);res.send(text);
Ahora, cuando escribe /faq alguna pregunta en un canal flojo, debería recibir el mismo mensaje del comando de barra diagonal floja.
Formatear la respuesta
En lugar de simplemente enviar texto sin formato, podemos utilizar el Block Kit de Slack para utilizar elementos de interfaz de usuario especializados para mejorar el aspecto de nuestras respuestas. Si deseas crear un diseño más complejo, Slack proporciona un creador de kit de bloques para diseñar visualmente su diseño.
Por ahora, mantendremos las cosas simples y solo proporcionaremos una respuesta donde cada respuesta esté separada por un divisor. Agregue la siguiente función a su archivo server.js después de la ruta de publicación:
const format = (answers) = { if (answers.length == 0) { answers = ["No answers found"]; } let formatted = { blocks: [], }; for (answer of answers) { formatted["blocks"].push({ type: "divider", }); formatted["blocks"].push({ type: "section", text: { type: "mrkdwn", text: answer, }, }); } return formatted;};
Una vez implementado esto, ahora debemos pasar nuestras respuestas a esta función para formatearlas antes de devolverlas a Slack. Actualice lo siguiente en la ruta de la publicación raíz:
let answers = text;const formattedAnswers = format(answers);
Ahora, cuando ingresamos el mismo comando en la aplicación de barra diagonal, deberíamos recibir el mismo mensaje, ¡pero esta vez en una versión formateada!
Configuración de fauna
Con nuestra aplicación Slack implementada y una función para conectarnos a ella, ahora necesitamos comenzar a trabajar en la base de datos para almacenar nuestras respuestas. Si nunca antes ha configurado una base de datos con FaunaDB, existe excelente documentación sobre cómo comenzar rápidamente. A continuación se incluye una breve descripción general paso a paso de la base de datos y la colección:
Crear base de datos
Primero, necesitaremos crear una nueva base de datos. Después de iniciar sesión en el panel de Fauna en línea, haga clic en Nueva base de datos. Asigne a su nueva base de datos un nombre que recuerde (yo usé "slack-faq") y guarde la base de datos.
Crear colección
Con esta base de datos implementada, ahora necesitamos una colección. Haga clic en el botón "Nueva colección" que debería aparecer en su panel de control y asigne un nombre a su colección (yo usé "preguntas frecuentes"). Los días del historial y los valores TTL se pueden dejar como predeterminados, pero debes asegurarte de no agregar un valor al campo TTL, ya que no queremos que nuestros documentos se eliminen automáticamente después de un tiempo determinado.
Agregar documentos de preguntas/respuestas
Ahora que tenemos una base de datos y una colección, podemos comenzar a agregarle algunos documentos. Cada documento debe seguir la estructura:
{ question: "a question string", answer: "an answer string", qTokens: [ "first token", "second token", "third token" ]}
Los valores de qToken deben ser términos clave en la pregunta, ya que los usaremos para una búsqueda tokenizada cuando no podamos hacer coincidir una pregunta exactamente. Puedes agregar tantos qTokens como quieras para cada pregunta. Cuanto más relevantes sean los tokens, más precisos serán los resultados. Por ejemplo, si nuestra pregunta es "¿dónde están los baños?", debemos incluir los qTokens "baño", "baños", "inodoro", "inodoros" y cualquier otro término que crea que la gente buscará cuando intente encontrar información. sobre un baño.
Las preguntas que utilicé para desarrollar una prueba de concepto son las siguientes:
{ question: "where is the lobby", answer: "On the third floor", qTokens: ["lobby", "reception"],},{ question: "when is payday", answer: "On the first Monday of each month", qTokens: ["payday", "pay", "paid"],},{ question: "when is lunch", answer: "Lunch break is *12 - 1pm*", qTokens: ["lunch", "break", "eat"],},{ question: "where are the bathrooms", answer: "Next to the elevators on each floor", qTokens: ["toilet", "bathroom", "toilets", "bathrooms"],},{ question: "when are my breaks", answer: "You can take a break whenever you want", qTokens: ["break", "breaks"],}
Siéntase libre de tomar este tiempo para agregar tantos documentos como desee y tantos qTokens como crea que necesita cada pregunta; luego pasaremos al siguiente paso.
Creando índices
Con estas preguntas en su lugar, crearemos dos índices que nos permitirán buscar en la base de datos. Primero, cree un índice llamado "respuestas_por_pregunta", seleccionando pregunta como término y respuesta como valor. Esto nos permitirá buscar todas las respuestas por su pregunta asociada.
Luego, cree un índice llamado "answers_by_qTokens", seleccionando qTokens como término y respuesta como valor. Usaremos este índice para permitirnos buscar en los qTokens de todos los elementos de la base de datos.
Buscando en la base de datos
Para realizar una búsqueda en nuestra base de datos haremos dos cosas. Primero, realizaremos una búsqueda de una coincidencia exacta con la pregunta, para poder proporcionar una única respuesta al usuario. En segundo lugar, si esta búsqueda no encuentra un resultado, haremos una búsqueda en los qTokens que tiene cada respuesta y devolveremos cualquier resultado que proporcione una coincidencia. Usaremos el shell en línea de Fauna para demostrar y explicar estas consultas, antes de usarlas en nuestra función.
Coincidencia exacta
Antes de buscar los tokens, probaremos si podemos hacer coincidir exactamente la pregunta de entrada, ya que esto permitirá obtener la mejor respuesta a lo que preguntó el usuario. Para buscar nuestras preguntas, las compararemos con el índice "answers_by_question" y luego paginaremos nuestras respuestas. Copie el siguiente código en el shell de Fauna en línea para verlo en acción:
q.Paginate(q.Match(q.Index("answers_by_question"), "where is the lobby"))
Si tiene una pregunta que coincide con el ejemplo anterior "¿dónde está el vestíbulo?", como resultado debería ver la respuesta esperada "En el tercer piso".
Buscando las fichas
Para los casos en los que no haya una coincidencia exacta en la base de datos, tendremos que usar nuestros qTokens para encontrar respuestas relevantes. Para ello, compararemos el índice "answers_by_qTokens" que creamos y paginaremos nuevamente nuestras respuestas. Copie lo siguiente en el shell en línea para ver cómo funciona:
q.Paginate(q.Match(q.Index("answers_by_qTokens"), "break"))
Si tiene alguna pregunta con el "descanso" de qToken de las preguntas de ejemplo, debería ver todas las respuestas como resultado.
Función Conectar a Fauna
Tenemos nuestras búsquedas resueltas, pero actualmente solo podemos ejecutarlas desde el shell en línea. Para usarlos en nuestra función, se requiere cierta configuración, así como una actualización del código de nuestra función.
Configuración de funciones
Para conectarnos a Fauna desde nuestra función, necesitaremos crear una clave de servidor. Desde el panel de su base de datos, seleccione seguridad en la barra lateral izquierda y cree una nueva clave. Asigne a su nueva clave un nombre que reconozca y asegúrese de que el menú desplegable tenga seleccionado Servidor, no Administrador. Finalmente, una vez creada la clave, agrega el siguiente código server.js
antes de la ruta de prueba, reemplazando el secretKey
valor con el secreto proporcionado por Fauna.
const q = fauna.query;const client = new fauna.Client({ secret: "secretKey",});
Sería preferible almacenar esta clave en una variable de entorno en Netlify, en lugar de directamente en el código, pero eso está fuera del alcance de este tutorial. Si desea utilizar variables de entorno, esta publicación de Netlify explica cómo hacerlo.
Actualizar código de función
Para incluir nuestras nuevas consultas de búsqueda en la función, copie el siguiente código server.js
después de la ruta de publicación:
const searchText = async (text) = { console.log("Beginning searchText"); const answer = await client.query( q.Paginate(q.Match(q.Index("answers_by_question"), text)) ); console.log(`searchText response: ${answer.data}`); return answer.data;}; const getTokenResponse = async (text) = { console.log("Beginning getTokenResponse"); let answers = []; const questionTokens = text.split(/[ ]+/); console.log(`Tokens: ${questionTokens}`); for (token of questionTokens) { const tokenResponse = await client.query( q.Paginate(q.Match(q.Index("answers_by_qTokens"), text)) ); answers = [...answers, ...tokenResponse.data]; } console.log(`Token answers: ${answers}`); return answers;};
Estas funciones replican la misma funcionalidad que las consultas que ejecutamos anteriormente en el shell de Fauna en línea, pero ahora podemos utilizarlas desde nuestra función.
Implementar en Netlify
Ahora la función busca en la base de datos, lo único que queda por hacer es ponerla en la nube, en lugar de en una máquina local. Para hacer esto, usaremos una función Netlify implementada desde un repositorio de GitHub.
Lo primero es lo primero, agregue un nuevo repositorio en Github y envíele su código. Una vez que el código esté allí, vaya a Netlify y regístrese o inicie sesión usando su perfil de Github. Desde la página de inicio de Netlify, seleccione "Nuevo sitio desde git" para implementar un nuevo sitio, utilizando el repositorio que acaba de crear en Github.
Si nunca antes ha implementado un sitio en Netlify, esta publicación explica el proceso para implementar desde git.
Mientras crea el nuevo sitio, asegúrese de que su comando de compilación esté configurado en npm run build, para que Netlify cree la función antes de la implementación. El directorio de publicación se puede dejar en blanco, ya que solo estamos implementando una función, en lugar de páginas.
Netlify ahora construirá e implementará su repositorio, generando una URL única para la implementación del sitio. Podemos usar esta URL base para acceder al punto final de prueba de nuestra función anterior, para asegurarnos de que todo esté funcionando.
¡Lo último que debe hacer es actualizar el punto final de Slack a nuestra nueva URL! Navegue hasta su aplicación, luego seleccione "comandos de barra diagonal" en la barra lateral izquierda. Haga clic en el ícono de lápiz para editar el comando de barra diagonal y pegue la nueva URL para la función. ¡Finalmente, puedes usar tu nuevo comando de barra diagonal en cualquier canal autorizado de Slack!
Conclusión
Ahí lo tiene, un comando de barra holgada funcional y completamente sin servidor. Hemos utilizado FaunaDB para almacenar nuestras respuestas y nos hemos conectado a través de una función Netlify. Además, al usar Express, tenemos la flexibilidad de agregar más puntos finales a la función para agregar nuevas preguntas o cualquier otra cosa que se te ocurra para ampliar aún más este proyecto. Con suerte, ahora, en lugar de esperar a que alguien responda tus preguntas, puedes usar /faq y obtener la respuesta al instante.
Matthew Williams es un ingeniero de software de Melbourne, Australia, que cree que el futuro de la tecnología no tiene servidores. Si está interesado en saber más de él, consulte sus artículos de Medium o sus repositorios de GitHub .
Deja un comentario