Instructivo de Slack: comandos de barra

En este instructivo, se muestra cómo usar Cloud Functions para implementar un comando de barra de Slack que realiza búsquedas en la API de Gráfico de conocimiento de Google.

Objetivos

  • Crear un comando de barra en Slack
  • Escribir y, luego, implementar una función de HTTP de Cloud Functions
  • Buscar en la API de Gráfico de conocimiento de Google con el comando de barra

Costos

En este instructivo, se usan componentes facturables de Cloud Platform, entre los que se incluyen estos:

  • Google Cloud Functions

Usa la calculadora de precios para generar una estimación de los costos según el uso previsto.

Antes de comenzar

  1. Accede a tu Cuenta de Google.

    Si todavía no tienes una cuenta, regístrate para obtener una nueva.

  2. Selecciona o crea un proyecto de GCP.

    Ir a la página Administrar recursos

  3. Asegúrate de tener habilitada la facturación para tu proyecto.

    Aprende a habilitar la facturación

  4. Habilita las Cloud Functions and Google Knowledge Graph Search API necesarias.

    Habilita las API

  5. Actualiza los componentes de gcloud:
    gcloud components update
  6. Prepara tu entorno de desarrollo.

Visualiza el flujo de datos

El flujo de datos en la aplicación del instructivo del comando de barra de Slack incluye varios pasos, como se muestra a continuación:

  1. El usuario ejecuta el comando de barra /kg <search_query> en un canal de Slack.
  2. Slack envía la carga útil del comando al extremo de activación de Cloud Functions.
  3. La función de Cloud Functions envía una solicitud con la búsqueda del usuario a la API de Gráfico de conocimiento.
  4. La API de Gráfico de conocimiento responde con todos los resultados coincidentes.
  5. La función de Cloud Functions da formato a la respuesta y la transforma en un mensaje de Slack.
  6. La función de Cloud Functions envía el mensaje de vuelta a Slack.
  7. El usuario ve la respuesta con formato en el canal de Slack.

El siguiente gráfico puede ayudarte a visualizar los pasos:

Prepara la función

  1. Clona el repositorio de la app de muestra en tu máquina local:

    Node.js

    git clone https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git

    De manera opcional, puedes descargar la muestra como un archivo ZIP y extraerla.

    Python

    git clone https://github.com/GoogleCloudPlatform/python-docs-samples.git

    De manera opcional, puedes descargar la muestra como un archivo ZIP y extraerla.

  2. Ve al directorio que contiene el código de muestra de Cloud Functions, como sigue:

    Node.js

    cd nodejs-docs-samples/functions/slack/

    Python

    cd python-docs-samples/functions/slack/

  3. Configura la aplicación de la siguiente forma:

    Node.js

    Usa el archivo config.default.json como plantilla y crea un archivo config.json en el directorio app con el siguiente contenido:
    {
    "SLACK_TOKEN": "YOUR_SLACK_TOKEN",
    "KG_API_KEY": "YOUR_KG_API_KEY",
    }
    • Reemplaza YOUR_SLACK_TOKEN por el token de verificación proporcionado por Slack en la página Información básica de la configuración de tu aplicación.
    • Reemplaza YOUR_KG_API_KEY por la clave de API de Gráfico de conocimiento que acabas de crear.

    Python

    Edita el archivo config.json en el directorio app para que tenga los siguientes contenidos:
    {
    "SLACK_TOKEN": "YOUR_SLACK_TOKEN",
    "KG_API_KEY": "YOUR_KG_API_KEY",
    }
    • Reemplaza YOUR_SLACK_TOKEN por el token de verificación proporcionado por Slack en la página Información básica de la configuración de tu aplicación.
    • Reemplaza YOUR_KG_API_KEY por la clave de API de Gráfico de conocimiento que acabas de crear.

Implementa la función

Para implementar la función que se ejecuta cuando tú (o Slack) hacen una solicitud HTTP POST al extremo de la función, ejecuta el siguiente comando en el directorio que contiene el código de muestra de Cloud Functions:

Node.js 8

gcloud functions deploy kgSearch --runtime nodejs8 --trigger-http

Node.js 10 (Beta)

gcloud functions deploy kgSearch --runtime nodejs10 --trigger-http

Node.js 6 (obsoleto)

gcloud functions deploy kgSearch --runtime nodejs6 --trigger-http

Python

gcloud functions deploy kg_search --runtime python37 --trigger-http

Configura la aplicación

Después de implementar la función, es necesario que crees un comando de barra de Slack que envíe la consulta a tu función de Cloud Functions cada vez que se activa el comando.

  1. Crea una aplicación de Slack para alojar el comando de barra de Slack. Asóciala con un equipo de Slack en el que tengas permisos para instalar integraciones.

  2. Ve a Comandos de barra y haz clic en el botón Crear comando nuevo.

  3. Ingresa /kg como el nombre del comando.

  4. Ingresa la URL del comando, como en el ejemplo:

    Node.js

    https://YOUR_REGION-YOUR_PROJECT_ID.cloudfunctions.net/kgSearch

    Python

    https://YOUR_REGION-YOUR_PROJECT_ID.cloudfunctions.net/kg_search

    donde YOUR_REGION es la región en la cual se implementa tu función de Cloud Functions y YOUR_PROJECT_ID es el ID del proyecto de Cloud.

    Ambos valores pueden verse en la terminal cuando la función termina de implementarse.

  5. Haz clic en Guardar.

  6. Ve a Información básica.

  7. Haz clic en Instala la app en tu lugar de trabajo y sigue las instrucciones en pantalla para habilitar la aplicación en tu lugar de trabajo.

    Tu comando de barra de Slack debería estar en línea en breve.

Comprende el código

Importa dependencias

La aplicación debe importar varias dependencias con el fin de comunicarse con los servicios de Google Cloud Platform:

Node.js

const config = require('./config.json');
const {google} = require('googleapis');

// Get a reference to the Knowledge Graph Search component
const kgsearch = google.kgsearch('v1');

Python

import json

import apiclient
from flask import jsonify

with open('config.json', 'r') as f:
    data = f.read()
config = json.loads(data)

kgsearch = apiclient.discovery.build('kgsearch', 'v1',
                                     developerKey=config['KG_API_KEY'])

Recibe el webhook

La siguiente función se ejecuta cuando tú (o Slack) haces una solicitud HTTP POST al extremo de la función:

Node.js

/**
 * Receive a Slash Command request from Slack.
 *
 * Trigger this function by making a POST request with a payload to:
 * https://[YOUR_REGION].[YOUR_PROJECT_ID].cloudfunctions.net/kgsearch
 *
 * @example
 * curl -X POST "https://us-central1.your-project-id.cloudfunctions.net/kgSearch" --data '{"token":"[YOUR_SLACK_TOKEN]","text":"giraffe"}'
 *
 * @param {object} req Cloud Function request object.
 * @param {object} req.body The request payload.
 * @param {string} req.body.token Slack's verification token.
 * @param {string} req.body.text The user's search query.
 * @param {object} res Cloud Function response object.
 */
exports.kgSearch = (req, res) => {
  return Promise.resolve()
    .then(() => {
      if (req.method !== 'POST') {
        const error = new Error('Only POST requests are accepted');
        error.code = 405;
        throw error;
      }

      // Verify that this request came from Slack
      verifyWebhook(req.body);

      // Make the request to the Knowledge Graph Search API
      return makeSearchRequest(req.body.text);
    })
    .then(response => {
      // Send the formatted message back to Slack
      res.json(response);
    })
    .catch(err => {
      console.error(err);
      res.status(err.code || 500).send(err);
      return Promise.reject(err);
    });
};

Python

def kg_search(request):
    if request.method != 'POST':
        return 'Only POST requests are accepted', 405

    verify_web_hook(request.form)
    kg_search_response = make_search_request(request.form['text'])
    return jsonify(kg_search_response)

La siguiente función autentica la solicitud entrante a través de la comprobación de tu token de verificación que generó Slack.

Node.js

/**
 * Verify that the webhook request came from Slack.
 *
 * @param {object} body The body of the request.
 * @param {string} body.token The Slack token to be verified.
 */
function verifyWebhook(body) {
  if (!body || body.token !== config.SLACK_TOKEN) {
    const error = new Error('Invalid credentials');
    error.code = 401;
    throw error;
  }
}

Python

def verify_web_hook(form):
    if not form or form.get('token') != config['SLACK_TOKEN']:
        raise ValueError('Invalid request/credentials.')

Consulta a la API de Gráfico de conocimiento

La siguiente función envía una solicitud con la búsqueda del usuario a la API de Gráfico de conocimiento.

Node.js

/**
 * Send the user's search query to the Knowledge Graph API.
 *
 * @param {string} query The user's search query.
 */
function makeSearchRequest(query) {
  return new Promise((resolve, reject) => {
    kgsearch.entities.search(
      {
        auth: config.KG_API_KEY,
        query: query,
        limit: 1,
      },
      (err, response) => {
        console.log(err);
        if (err) {
          reject(err);
          return;
        }

        // Return a formatted message
        resolve(formatSlackMessage(query, response));
      }
    );
  });
}

Python

def make_search_request(query):
    req = kgsearch.entities().search(query=query, limit=1)
    res = req.execute()
    return format_slack_message(query, res)

Formato de mensaje de Slack

Finalmente, la siguiente función da formato al resultado de Gráfico de conocimiento y lo transforma en un mensaje de Slack con formato enriquecido que se le mostrará al usuario.

Node.js

/**
 * Format the Knowledge Graph API response into a richly formatted Slack message.
 *
 * @param {string} query The user's search query.
 * @param {object} response The response from the Knowledge Graph API.
 * @returns {object} The formatted message.
 */
function formatSlackMessage(query, response) {
  let entity;

  // Extract the first entity from the result list, if any
  if (
    response &&
    response.data &&
    response.data.itemListElement &&
    response.data.itemListElement.length > 0
  ) {
    entity = response.data.itemListElement[0].result;
  }

  // Prepare a rich Slack message
  // See https://api.slack.com/docs/message-formatting
  const slackMessage = {
    response_type: 'in_channel',
    text: `Query: ${query}`,
    attachments: [],
  };

  if (entity) {
    const attachment = {
      color: '#3367d6',
    };
    if (entity.name) {
      attachment.title = entity.name;
      if (entity.description) {
        attachment.title = `${attachment.title}: ${entity.description}`;
      }
    }
    if (entity.detailedDescription) {
      if (entity.detailedDescription.url) {
        attachment.title_link = entity.detailedDescription.url;
      }
      if (entity.detailedDescription.articleBody) {
        attachment.text = entity.detailedDescription.articleBody;
      }
    }
    if (entity.image && entity.image.contentUrl) {
      attachment.image_url = entity.image.contentUrl;
    }
    slackMessage.attachments.push(attachment);
  } else {
    slackMessage.attachments.push({
      text: 'No results match your query...',
    });
  }

  return slackMessage;
}

Python

def format_slack_message(query, response):
    entity = None
    if response and response.get('itemListElement') is not None and \
       len(response['itemListElement']) > 0:
        entity = response['itemListElement'][0]['result']

    message = {
        'response_type': 'in_channel',
        'text': 'Query: {}'.format(query),
        'attachments': []
    }

    attachment = {}
    if entity:
        name = entity.get('name', '')
        description = entity.get('description', '')
        detailed_desc = entity.get('detailedDescription', {})
        url = detailed_desc.get('url')
        article = detailed_desc.get('articleBody')
        image_url = entity.get('image', {}).get('contentUrl')

        attachment['color'] = '#3367d6'
        if name and description:
            attachment['title'] = '{}: {}'.format(entity["name"],
                                                  entity["description"])
        elif name:
            attachment['title'] = name
        if url:
            attachment['title_link'] = url
        if article:
            attachment['text'] = article
        if image_url:
            attachment['image_url'] = image_url
    else:
        attachment['text'] = 'No results match your query.'
    message['attachments'].append(attachment)

    return message

Tiempos de espera de la API de Slack

La API de Slack espera que tu función responda dentro de 3 segundos después de recibir una solicitud de webhook.

Los comandos que aparecen en este instructivo, por lo general, tardan menos de 3 segundos en responder. En el caso de los comandos con tiempos de respuesta más largos, se recomienda configurar una función para enviar solicitudes (incluida la response_url) a un tema de Pub/Sub que actúa como una lista de tareas en cola.

Después, puedes crear una segunda función activada por Pub/Sub que procese esas tareas y envíe resultados de vuelta a la response_url de Slack.

Usa el comando de barra

  1. Prueba el comando manualmente:

    Node.js

    curl -X POST "https://YOUR_REGION-YOUR_PROJECT_ID.cloudfunctions.net/kgSearch" -H "Content-Type: application/json" --data '{"token":"YOUR_SLACK_TOKEN","text":"giraffe"}'

    Python

    curl -X POST "https://YOUR_REGION-YOUR_PROJECT_ID.cloudfunctions.net/kg_search" --data 'token=YOUR_SLACK_TOKEN&text=giraffe'

    donde:

    • YOUR_REGION es la región en la que se implementa tu función. Esta se puede ver en tu terminal cuando la función termina de implementarse.
    • YOUR_PROJECT_ID es el ID del proyecto de Cloud. Este se puede ver en tu terminal cuando la función termina de implementarse.
    • YOUR_SLACK_TOKEN es el token que proporciona Slack en la configuración del comando de barra.
  2. Revisa los registros para asegurarte de que las ejecuciones se completaron:

    gcloud functions logs read --limit 100
    
  3. Escribe el comando en el canal de Slack:

    /kg giraffe

Limpieza

Sigue estos pasos para evitar que se apliquen cargos a tu cuenta de Google Cloud Platform por los recursos que usaste en este instructivo:

Borra el proyecto

La manera más fácil de eliminar la facturación es borrar el proyecto que creaste para el instructivo.

Para borrar el proyecto, sigue estos pasos:

  1. En la GCP Console, dirígete a la página Proyectos.

    Ir a la página Proyectos

  2. En la lista de proyectos, selecciona el proyecto que deseas borrar y haz clic en Borrar.
  3. En el cuadro de diálogo, escribe el ID del proyecto y, luego, haz clic en Cerrar para borrar el proyecto.

Borra la función de Cloud Functions

Para borrar la función de Cloud Functions que implementaste en este instructivo, ejecuta el siguiente comando:

Node.js

gcloud functions delete kgSearch 

Python

gcloud functions delete kg_search 

También puedes borrar las funciones de Cloud Functions desde Google Cloud Platform Console.

¿Te sirvió esta página? Envíanos tu opinión:

Enviar comentarios sobre…