Enseñándole a un bot a ser como tú

Luciano Adonis
devsChile
Published in
7 min readFeb 15, 2019

Vine buscando cobre y encontré oro.

Portada volteada del libro “La Saga de los Worthing” de Orson Scott Card.

En una tranquila tarde de verano, recordaba haber leído durante la semana pasada en devsChile un comentario sobre que el 🤖 Huemulcontase la cantidad de emojis en un mensaje”. En un principio puede parecer latero de implementar, aunque también puede ser de utilidad, si detestas a Slackbot y prefieres hacer tus polls con emojis.

Esta línea de pensamiento me recordó que no exploré completamente la documentación de la API y los alcances del Enerbot, un bot recanchero, escrito en Ruby.

En este post resolveré un par de misterios no tan explícitos, entre ellos, el personificar a un usuario Slack usando su token de acceso, permitiéndote adquirir permisos que de otra forma solo conseguirías a través de una solicitud tediosa.

Pero antes llegar a eso, hay que agregarle emojis a Enerbot.

Reactions 🤖

El poder “Reaccionar” agregando emojis, se encuentra dentro de los alcances de permisos para un bot común. Lo que implica que puedes obtener estadísticas de mensajes.

Para conseguir esto, se debe utilizar el método reactions.add, donde debes declarar el “quiero este emoji, en este canal y en este comentario”, en base a los siguientes argumentos:

  • name: nombre del emoji.
  • channel: canal donde esta el mensaje.
  • timestamp: timestamp.

Si no sabes como obtener dicha información, puedes verla fácilmente usando Slack desde el navegador, buscas el comentario y clickeas “Start a thread”, lo cual cambiará la URL, mostrándote algo así:

https://SPACE.slack.com/messages/example_chan/convo/example_chan-1123581321.345589/

Ejemplo rápido

Para utilizar los métodos que aparecen dentro de la documentación, se debe reemplazar el “.” por “_”. Para el siguiente ejemplo el método para agregar reacciones reactions.add queda de la siguiente forma:

require 'slack-ruby-client'Slack.configure do |config|
config.token = ENV['SLACK_API_TOKEN']
config
.raise 'Missing ENV[SLACK_API_TOKEN]!' unless config.token
end
client = Slack::Web::Client.new
client.reactions_add(channel: 'example_chan', name: 'joy', timestamp: '1123581321.345589')

Al ejecutar esto, reaccionará en el comentario seleccionado con el emoji “😂”.

Cabe mencionar que este método solo se encuentra disponible para la Web API. Una buena estrategia es que el bot cuente con ambas definiciones, así puedes extender sus funcionalidades. Quedando algo así:

client = Slack::RealTime::Client.new
web_client = Slack::Web::Client.new

En el post de Probando un bot de Slack, sin Slack, mencionaba la distinción que existe entre las distintas APIs y como el Slack Ruby Client trabaja con estas. Un resumen de sus diferencias:

  • Web API: puedes utilizarla para obtener información del workspace y es la que utilizarías para notificar algo en Slack, disparado por una App o un cron que envíe cierto tipo de info a un canal determinado.
  • Real Time Messaging API: recibes y respondes eventos en tiempo real. Un evento corresponde a lo que se escriba en un determinado canal, dicha información será utilizada y si aplica, se usa.
  • Events API: es el “no nos llames, nosotros te llamamos” de las APIs, menos “invasivo” que la forma con RTM. Lamentablemente, no he jugado lo suficiente con la Events API.

⁉️ Dato curioso: los bots no pueden tener status. 🔗 Info.

El límite que no rompen

Varios métodos interesantes requieren de permisos que no incluyen los tokens generados por las integraciones comunes como las de “Hubot” y “Bot”. El tipo de información que puedes obtener de Slack también es limitada, utilizando la Web API, pero vale la pena considerarlas.

El Token type muestra en el primer ejemplo que solo funciona con “user”, mientras que el segundo indica que tanto token de “bot” y “user” pueden utilizar el método.

Ejemplos destacables:

  • Obtener información de todos los usuarios del Workspace. No muy distinta a la que puedes conseguir al ver el profile de un usuario como nombres, correo y status. Mejor de los casos un número ☎️. Método: user.list.
  • Obtener información de un usuario especifico, la misma que user.list. Método: user.info.
  • Obtener información de todos los canales públicos. Se extiende a los privados donde el bot forma parte. Cuyo método destacable es channel.list. El resto pide más permisos 😅.
  • Reaccionar con emojis, cuyos métodos son; reactions.add, reactions.get, reactions.list y reaction.remove.
  • Manejar elementos pinned, cuyos métodos son; pins.add, pins.list y pins.remove.

¿Qué no puede hacer?

Bastantes cosas, algunas como crear, editar una que otra cosa y kickear gente, lo que puede hacer un usuario normal. Lo que más duele es no poder buscar archivos y mensajes con los métodos search.files, search.messages y search.all.

Personificación

Dentro de las opciones de chat.postMessage, puedes definir que el bot use la identidad del dueño de la integración. Así:

client.web_client.chat_postMessage channel: channel,
text: text,
icon_url: @bot_icon,
username: @bot_name,
attachments: find,
as_user: true

Lamentablemente eso no funciona tan bien, debido a que requieres un par de permisos extra, que puedes solicitar declarando la app, accesos y más, para su posterior instalación.

Pensando en una posible solución, sobre cómo saltarme las limitantes del bot al no poder extraer información ya que este debía ser mas “humano”, mejor dicho, un usuario. En ese punto, recordé un post que habían compartido hace mucho tiempo, sobre un script en Python que utilizaba el token de usuario para autenticarse… ahí estaba la respuesta.

TL;DR: puedes extraer información “sensible” mediante el SlackPirate, utilizando el Token de tu usuario, el cual no es difícil de obtener.

Si el bot no poseía los permisos para reemplazarme y para intrusear. Simple, el bot debe convertirse en mí, con mi Token.

Yo, Enerbot

Para obtener el Token solo necesitas buscar XOX dentro del código de Slack en tu navegador. Es parecido al utilizado por bots, pero más largo:

xoxs-0000000000-1111111111-0000000000-abcde12345abcde12345abcde12345abcde12345abcde12345abcde12345

Reciclando el ejemplo inicial, lo único que tendrás que cambiar es el token de acceso:

require 'slack-ruby-client'Slack.configure do |config|
config.token = 'YOUR_TOKEN_HERE'
config
.raise 'Missing ENV[SLACK_API_TOKEN]!' unless config.token
end
client = Slack::Web::Client.new
client.reactions_add(channel: 'example_chan', name: 'joy', timestamp: '1123581321.345589')

Para validar si funcionó correctamente, verás que se agregó el emoji 😂 utilizando tu usuario. De aquí en adelante es averiguar que funciones te pueden ser útiles de la API de Slack.

Si funciona con Web API, entonces: ¿Con la RTM API funciona? Sí y mejor de lo que imaginaba

Para pruebas rápidas, removí el segmento que requería que solamente hiciera match con /(ener[brs])/i por /.*/. Poco elegante, pero útil para pruebas rápidas, lo malo es que si alguien usaba Enerbot, saldría mi usuario respondiendo, junto a este en paralelo.

Casi ocurre, pero una de las reglas para escribir en canales privados es que se debe pertenecer, lo cual no aplica a canales públicos. Por lo cual solamente Enerbot pudo realizar una acción sobre ese canal.

Respecto a la velocidad de respuesta, es inmediata, lo cual sería sospechoso si tratas de hacerlo pasar por ti. Con un sleep(3) y el client.typing de la RTM API, cuando haga match las palabras adecuadas con tu case, verán el usuario del Token como “USER is typing”.

¿Puede hacerse pasar por alguien mas?

Asumía que Enerbot solo podía usar mi identidad con la forma as_user: true y que al hacerle click al nombre de usuario se vería que era Enerbot o poseería algún elemento diferenciador como lo tiene el bot con el APP seguido de su nombre. Pero no, como usa mi token, es mi usuario en toda su gloría y majestad. Por lo cual pensé: ¿Sirve con los tokens de otros?

Gracias al sacrificio de un colaborador, pude comprobar que el bot puede suplantar perfectamente a alguien. Permitiéndote usar la RTM API para adquirir todos los mensajes que este reciba en todos los canales pertenecientes y si cuentas con una función para mensajes “personalizados” hablar por dicha persona.

Las búsquedas son algo que mostraba el SlackPirate, si revisas la API hay distintos métodos que te dejan buscar información entre los distintos mensajes y archivos que puedas tener dentro de Slack. Cuyos métodos mencione al comienzo, los search.

Por cosa de tipo solo probé con mi Token el search.files y funciona de maravilla, entre toda la información que te entrega incluye el link de descarga para cada archivo.

require 'slack-ruby-client'Slack.configure do |config|
config.token = ENV[SLACK_API_TOKEN]
config.raise 'Missing ENV[SLACK_API_TOKEN]!' unless config.token
end
client = Slack::Web::Client.new
p client.search_files(query: 'energon')

Reflexión final

Si quieres que tu bot responda Slack por ti, debes tener cuidado con la velocidad de respuesta, prepararlo para distintos casos, generar un log en un canal privado para mantenerte al tanto de sus acciones y que desde ahí te avise ante posibles casos que este no pueda manejar. De lo contrario todo lo que se te diga será manejado por el bot, valga decir, saldrá como visto y eventualmente manejará más de lo que puedas imaginarte, en Slack.

Una de mis dudas, es que no tenía conciencia de ninguna herramienta, estilo auditoría que usase los permisos para revisar los logs y uno que otro canal en busca de información sensible. Lo cual sería bastante interesante. Bueno si hubiese buscado y encontrado algo así, posiblemente hubiese llegado a la misma conclusión: que el bot puede sustituirte en Slack al menos.

--

--

devsChile
devsChile

Published in devsChile

La mejor y más activa comunidad en Slack para Desarrolladores en Chile.

Luciano Adonis
Luciano Adonis

Written by Luciano Adonis

Un joven DevOps y arquitecto ☁️

No responses yet