Slack es una herramienta de comunicación en equipo que se lanzó al mercado en agosto del 2013. Ofrece salas de chat organizadas por temas, así como grupos privados y mensajes directos. Posee un cuadro de búsqueda que permite acceder a todo el contenido de la aplicación y, además, integra una gran cantidad de servicios a terceros y respalda las integraciones hechas por la comunidad.
Como decíamos, las integraciones de la comunidad de servicios a terceros son una de las funcionalidades más exitosas de Slack, sin embargo, si tuviéramos que destacar dos de ellas, nos quedaríamos con los bots y los slash commands. Los primeros son programas que se conectan a Slack igual que tú (Slack ve a los bots como usuarios), tienen acceso a los mensajes que pasan a través de los servidores de Slack e interactúan con ellos. Los bots son excelentes si necesitas desarrollar una integración con Slack con un alto grado de interactividad, o si deseas poder procesar el flujo de mensajes. Los slash commands, por su lado, son comandos de Slack invocados con el carácter “/” seguido del nombre del comando. Éstos son excelentes cuando tenemos un conjunto fijo de interacciones, y deben iniciarse en nombre del usuario o informar al mismo.
Slack y Go
Evidentemente, tanto los bots como los slash commands, al ser programas que se integran con Slack, son programas que tendremos que desarrollar nosotros con nuestra propia lógica de negocio. Por lo tanto, lo primero será escoger un lenguaje de programación para dichos desarrollos. Como no podía ser de otra forma, para esta ocasión usaremos Go. Otro caso de uso del mundo real para el que la elección de Go es una buena elección.
En particular usaremos el paquete “nlopes/slack” de Norberto Lopes, cuya documentación la podemos encontrar aquí. Y más concretamente, lo que vamos a desarrollar, por un lado, es una API. Por eso, si aún no habéis desarrollado ninguna API en Go, os recomendamos encarecidamente leer nuestro artículo sobre como crear una API REST en Go.
De hecho, el funcionamiento de ambos es mediante una API. Por un lado, los slash commands no dejan de ser un trigger que harán una petición a la URL que nosotros indiquemos. Y, por otro lado, los bots se suscribirán a eventos (mensaje enviado, mención, canal creado, etc) que, una vez sucedan, también se triggerearan a la URL indicada para actuar en consecuencia.
Creando una Slack app
Como es habitual, la forma de desarrollar una integración con una aplicación web del estilo (Facebook, Twitter, etc) es mediante la creación de una aplicación en dicha plataforma, lo cuál suele ir asociado a la obtención de las credenciales necesarias para nuestros desarrollos.
En Slack, concretamente, lo que primero que deberemos hacer para empezar a crear nuestros bots y nuestros slash commands es acceder a la web de Slack API y rellenar el formulario de creación de una nueva aplicación:
Una vez creada, tendremos acceso a las credenciales de la misma:
¡Ahora ya estamos listos para empezar!
Creando un slash command
Como decíamos, para habilitar un nuevo slash command en nuestra aplicación, lo que deberemos hacer es deployar una API capaz de reaccionar a la misma, posteriormente configuraremos dicho comando en la web de la Slack API.
Entonces, ¿qué pinta tendrá el handler que responderá a nuestros comandos?
Vamos a suponer que queremos crear un comando /last
que nos devuelva el enlace al último articulo publicado en nuestro blog.
Para ello vamos a suponer que disponemos de un método (func getLastBlogPost() string
) para obtener dicho enlace.
func lastHandler(response http.ResponseWriter, request *http.Request) {
slashCommand, err := processIncomingRequest(request, response, signingSecret)
if err != nil {
return
}
// See which slash command the message contains
switch slashCommand.Command {
case "/last":
response.Write([]byte(getLastBlogPost()))
default:
return
}
}
Como podemos ver, lo que hacemos básicamente es:
- Identificar el comando solicitado con los datos de la petición HTTP.
- En caso de que sea el comando
/last
, entonces respondemos con el enlace.
Si nos fijamos, al método processIncomingRequest
le pasamos la signingSecret
. Ésta la podemos obtener del apartado
que veíamos anteriormente, el cuál contiene todas las credenciales de nuestra aplicación.
Pero, ¿qué pinta tiene el método processIncomingRequest
?
func processIncomingRequest(r *http.Request, w http.ResponseWriter, signingSecret string) (slashCommand slack.SlashCommand, errs error) {
verifier, err := slack.NewSecretsVerifier(r.Header, signingSecret)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
r.Body = ioutil.NopCloser(io.TeeReader(r.Body, &verifier))
slashCommand, err = slack.SlashCommandParse(r)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return slashCommand, err
}
// Verify that the request came from Slack
if err = verifier.Ensure(); err != nil {
w.WriteHeader(http.StatusUnauthorized)
return slashCommand, err
}
return slashCommand, nil
}
Como podemos ver, lo que hacemos básicamente es una pequeña gestión de errores para los casos en los que las credenciales son incorrectas o en los que la petición no nos llega desde Slack y, en caso contrario, devolvemos el slash command que nos ha llegado en la petición.
Una vez tenemos deployada la API en nuestro servidor, solo nos falta ir al apartado de Slash Commands de la Slack API y crear un nuevo comando:
Como podemos ver, en dicha sección podremos configurar el comando en cuestión. Es importante recordar que en SERVER_URL
pondríamos
el enlace al servidor que respondería con el handler que desarrollamos previamente, y que, en caso de necesidad, podríamos ajustar
las configuraciones de hint y autocompletado.
Creando un bot
Vale, ya hemos creado nuestro primer comando de Slack, pero ahora queremos ir un poco más allá, y queremos ver como desarrollar nuestro primer bot. Para desarrollar nuestro bot también tenemos que desarrollar una API. Con el pequeño matiz, como comentábamos anteriormente, de que nuestro bot, lo que hará será responder a eventos que sucedan en el workspace dónde tengamos nuestra aplicación instalada.
Para ello, deberemos crear un bot asociado a los eventos que queramos que responda. Lo primero será configurar el nombre y el identificador de usuario de nuestro bot:
Una vez configurado nuestro usuario bot, lo que haremos será realizar la subscripción a los eventos a los que queramos que éste responda:
De nuevo, deberemos configurar una URL (SERVER_URL
) que se corresponderá con la del servidor que contenga el handler que de respuesta a dichos eventos.
En esta ocasión la lógica cambiará un poco, pues, por un lado, tendremos que satisfacer un pequeño challenge que nos propone la gente de Slack
para validar nuestra URL y, por otro lado, deberemos inicializar una sesión de nuestro bot vía protocolo RTM.
Además, en esta ocasión, en lugar de identificar el slash command recibido, deberemos detectar cuál es el evento que se ha producido.
Veamos como hacerlo:
func eventsHandler(response http.ResponseWriter, request *http.Request) {
buf := new(bytes.Buffer)
buf.ReadFrom(request.Body)
body := buf.String()
eventsAPIEvent, err := slackevents.ParseEvent(
json.RawMessage(body),
slackevents.OptionVerifyToken(&slackevents.TokenComparator{VerificationToken: verificationToken}),
)
if err != nil {
response.WriteHeader(http.StatusInternalServerError)
}
if eventsAPIEvent.Type == slackevents.URLVerification {
var r *slackevents.ChallengeResponse
err := json.Unmarshal([]byte(body), &r)
if err != nil {
response.WriteHeader(http.StatusInternalServerError)
}
response.Header().Set("Content-Type", "text")
response.Write([]byte(r.Challenge))
}
if eventsAPIEvent.Type == slackevents.CallbackEvent {
innerEvent := eventsAPIEvent.InnerEvent
switch ev := innerEvent.Data.(type) {
case *slackevents.AppMentionEvent:
api.PostMessage(ev.Channel, slack.MsgOptionText("Friends of Go is the best #golang community ever.", false))
}
}
}
Si lo analizamos en detalle, veremos que en el bloque:
eventsAPIEvent.Type == slackevents.URLVerification
devolvemos la respuesta que nos permite superar el challenge de verificación de la URL, y en el bloque:
eventsAPIEvent.Type == slackevents.CallbackEvent
actuamos en consecuencia en función del evento que se ha producido. En esta ocasión, concretamente, al evento que
se produce cuándo hacemos una mención (slackevents.AppMentionEvent
).
Como podemos ver, el paquete “nlopes/slack” nos proporciona toda la magia necesaria para desarrollar nuestros bots y nuestros slash commands. Sin embargo, hay algo de magia que aún no hemos explicado.
¿Os habíais fijado en la siguiente línea?
api.PostMessage(ev.Channel, slack.MsgOptionText("Friends of Go is the best #golang community ever.", false))
¿De dónde surge dicha api
? ¿Y cómo indicamos que es nuestro bot el que publica el mensaje?
Pues bien, dicha variable surge de la inicialización que realizamos unas líneas más arriba, fuera scope del handler:
var api = slack.New(oAuthToken)
dónde la constante oAuthToken
se corresponde con los datos que podemos obtener en la sección OAuth de nuestra aplicación de Slack,
y que más concretamente se corresponden con el OAuthToken de nuestro bot:
Instalando la aplicación
Ya casi lo tenemos. Ya hemos desarrollado nuestro primer bot y nuestro primer slash command y ahora solo nos falta instalar dicha aplicación en nuestro workspace para poder empezar a usar ambas funcionalidades.
Para ello, solo tendremos que ir al menú de “Install App” e instalarla:
Y ¡voilà! ¡Ya lo tendremos! Ya tendremos en funcionamiento nuestro primer bot y nuestro primer slash command desarrollados en Go.
Y vosotros, ¿os animáis a desarrollar vuestros propios bots o slash commands? Y, ¿a compartirlos con nosotros?
Como siempre, estaremos encantados de recibir vuestro feedback en los comentarios del blog o vía Twitter.