Probando un bot de Slack, sin Slack

Luciano Adonis
devsChile
Published in
6 min readJan 21, 2019

Tuve que ir a la playa el finde 🏖

Una tranquila mañana de sábado para un jovenzuelo arquitecto que se encontraba esperando el bus, soñando despierto sobre el refactoring del Enerbot que le traería la tan esperada paz mental. Sin sospechar, de lo que le iba a ocurrir al llegar.

Antes de contar la historia de mi fin de semana, hablemos sobre Enerbot.

La historia de un pequeño bot

Pariente lejano de Talus y Huemul.

Hace algunos meses, en un espacio de Slack muy, muy lejano, en un canal bastante seguro, surge Enerbot, un 🤖 fuerte e independiente que no necesita un framework como Hubot, Errbot o Slack Ruby Bot para funcionar la mayor parte de las veces.

Este utiliza la gema de Slack Ruby Client para interactuar con la API de Real Time Messaging, lo cual implica que el bot “lee” todo lo escrito en los canales que este se encuentre.

A varios puede levantarles una 🚩, a mi gusto, eso es compensado con la naturalidad y cierto grado de carisma que le entrega, al no requerir necesariamente a una invocación estilo “@otherbots” pasando a ser un montón de REGEX, pero con carisma.

Algo así como el Albert Eistein de La Saga de los Heechee.

Continuando con la historia… el tiempo pasó, le fuimos incorporando nuevas funcionalidades, validaciones, mejoras, permitiendo adaptarlo a las necesidades que van surgiendo en la comunidad. Todo esto claramente en nuestro tiempo libre, lo cual provocó que algunos cambios fueran hechos a la rápida o simplemente tener que alterar la estructura en vuelo, haciendo que recurriésemos a workarounds, convirtiéndose tan paulatinamente en un Legacy que ni nos dimos cuenta.

De ahí la necesidad de mejorarlo o al menos empezar a avanzar en ello, cosa que había planeado hacer este finde.

Volviendo a mi fin de semana

La cobertura de cierta compañía telefónica*, no me facilitó el poder conectarme y si me llegaba señal, no era suficiente para que el Enerbot local se conectase, por lo tanto, nada de pruebas.

Tenía dos opciones, encontrar otra forma de pasar el tiempo hasta el domingo en la noche o ver una forma rebuscada para conversar con el bot.

Your friendy local neighbor Enerbot

Las desopilantes aventuras de mi vida con Enerbot.

Para utilizarlo localmente, hay que reemplazar los elementos de la gema que permiten recibir y envían información a Slack.

En su estructura más simple, se vería algo como esto:

require 'slack-ruby-client'

Slack.configure do |config|
config.token = ''
config.raise 'Missing token!' unless config.token
end

client = Slack::RealTime::Client.new

client.on :message do |data|
text = data.text
chan = data.channel

case text
when /^enerborg hola/
client.web_client.chat_postMessage channel: chan,
text: 'Holiwis'
end


end

client.start!

Realiza lo siguiente:

  • Carga la gema de ‘slack-ruby-client’ que contiene toda la magia para interactuar con Slack.
  • Realiza la configuración del token de autenticación.
  • Define la inicialización del cliente para ser utilizada en el hermoso loop al recibir un mensaje client.on :message, que es básicamente “lee todo lo que escriba en los canales donde te encuentres”.
  • Toda la información recibida se asigna a data, del cual se obtiene información sobre quién escribió algo, en qué canal y más.
  • Ante algún match con el Case responderá el mensaje mediante chat_postMessage entregando el valor asignado.

En este post se llamará Enerborg por temas de Compliance.

Recibiendo y enviando un mensaje

Al escribir en Slack, el bot lo lee de la siguiente forma:

#<Slack::Messages::Message channel="###" client_msg_id="###" event_ts="###" team="###" text="enerborg hola" ts="###" type="message" user="###">

Toda esta información se define en data de la cual puedes obtener información como de donde se invoca al bot, si es desde un thread, que usuario, incluye un archivo, etc.

Por ejemplo, un respuesta dentro del mismo canal, el método de respuesta, normalmente esperaría el valor data.channel.

⭐️ TIP: si quieres responder y/o enviar a algún otro canal, puedes utilizar el ID o nomenclatura en vez del valor de data.channel en el chat.postMessage.

Para el case del ejemplo, este evalúa el data.text y el REGEX hace el match con el “hola” dentro del texto “enerborg hola”.

Seguir usando esta forma de recibir información evitará tener que reestructurar posibles cambios debido a qué tal método, esperaba que data incluyese .channel y no el valor de #party_parrot_squad directamente.

⭐️ TIP: para una mejor troubleshooting, un p data dentro del case o en el chat.postMessage ante algún match. Hará tu día mejor.

Ya teniendo todo eso en mente, comencemos por la definición para el valor donde obtendrá la info, será del modulo SlackIn:

module SlackIn
def self.channel
'#party_parrot_squad'
end

def self
.text
'enerborg hola'
end

def self
.user
'lucio'
end
end

Para obtener el valor de cada uno de los métodos utilizados por este ejemplo, de la siguiente forma:

text = SlackIn.text
chan = SlackIn.channel
user = SlackIn.user

Ya que tenemos el módulo que imita un mensaje escrito en Slack, esto nos libra de utilizar:

require 'slack-ruby-client'

Slack.configure do |config|
config.token = ''
config.raise 'Missing token!' unless config.token
end

client = Slack::RealTime::Client.new
client.on :message do |data|
end
client.start!

Ahora solamente queda el Case, el cual podemos mantenerlo, ya que no se encuentra sujeto a la gema de Slack y el chat.postMessage, que define los parámetros de cómo y dónde se escribirá el bot en Slack.

Este puede incluir valores que sobre-escriban los por default como el ícono y nombre del bot.

client.web_client.chat_postMessage channel: chan,
text: 'Holiwis',
icon_emoji: ':enerbot:',
username: 'ENERBORG'

⭐️ TIP: como lifehack, si quieres escribir a través del bot en un thread, solamente debes agregarle el parámetro y valor del Time Stamp (ts) al chat.postMessage.

Lo cual perfectamente queda como:

p "Reply on channel: #{chan} | With Text: Hola #{user}"

Recibiendo y enviando un mensaje

Bien, del primer ejemplo pasamos a lo siguiente:

module SlackIn
def self.channel
'#party_parrot_squad'
end

def self
.text
'enerborg hola'
end

def self
.user
'lucio'
end
end

text = SlackIn.text
chan = SlackIn.channel
user = SlackIn.user

case text
when /^enerborg hola/
p "Reply on channel: #{chan} | With Text: Hola #{user}"
end

Al ejecutar, nos entrega lo siguiente:

“Reply on channel: #party_parrot_squad | With Text: Hola lucio”

Equivalente al bot escribiendo "Hola #{tu_saurio}" en Slack.

¿No salía mejor hacer un script?

SNO, si fuesen funcionalidades (scripts) sería mejor. En este caso se apuntaba a modificar gran parte de la estructura que maneja la interacción con Slack, cosa de utilizar clases y módulos para jugar con las evaluaciones de permisos, entre otras cosas.

Lo siguiente es parte de los cambios que quería y del repositorio que estaba usando para pruebas sin Slack.

La inicializar la clase Reply, esta recibe los valores de data (SlackIn) y el reply (data.text). De ahí surge la necesidad de usar la definición engorrosa del modulo, debido a que al definir variables individuales, no representaría bien la forma en que recibe información el bot desde Slack.

Resumiendo

Con un módulo puedes hacer pruebas simples sobre tu bot sin conexión a internet, lo cual es poco probable que ocurra en esta época, en cualquier caso, fuiste advertido.

La documentación de la gema de Slack Ruby Client le faltan algunas cosas, ya que el resto se basa en que leas la documentación de la API Slack. Con eso, tienes un montón de cosas entretenidas por hacer.

Un ⭐️ TIP, podría ser que para obtener información de usuarios, canales y otras cosas, debes utilizar el cliente Web y no el RealTime Messaging.

Dejo el repo del Enerbot y del Enerborg local. Dudas, comentarios, sugerencias, siempre bienvenidas en este post o en los repositorios respectivos.

Aguante Ruby.

--

--

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