Contenu

Ecrire un bot pour Slack en Rust 🤖

Introduction

Slack est un outil utilisé dans de nombreuses entreprises pour faciliter la communication au sein des équipes. Et il est possible d’augmenter les fonctionnalités de Slack via des commandes personnalisées. Parmi les plus utilisées, on retrouve la commande /polly pour créer des sondages, ainsi que la commande /giphy pour aller chercher des gifs et participer de façon constructive à une conversation. Dans cet article, nous allons présenter une façon d’ajouter ses propres commandes via la création d’un bot Slack fait en Rust. (Aucune connaissance de Rust n’est requise pour lire cet article).

Fonctionnement global

Le bot est un service-web REST déployé sur le produit Cloud Run de GCP (Google Cloud Platform) et qui expose le endpoint /commands (POST).

Slack va envoyer la ligne de commande entière au webservice, avec une url de callback et un token d’authentification, ainsi que des informations sur l’utilisateur et le channel.

Le bot s’occupera d’interpréter la commande et de renvoyer une réponse au format d’un message Slack classique.

Paramétrage côté Slack

Pour attaquer notre bot, il nous faut d’abord configurer notre Slack pour lui ajouter une application, pour cela, direction le site api.slack.com/apps

Il existe ensuite différents types de features qu’on peut ajouter, ici celle qui nous intéresse est le fait d’ajouter un slash command

/rust-slack-bot/max-bot-slash.jpg

Ici on peut définir de nouvelle commande Slack, comme par exemple /maxbot pour utiliser notre bot, ainsi que l’url à utiliser.

Il est aussi possible d’avoir un hook sur différents types d’évènements, par exemple à chaque fois qu’une personne rejoint un channel, pour lancer automatiquement un message de bienvenue.

Côté webservice

Afin de communiquer avec l’api Slack, on utilise une librairie qui définit déjà toutes les structures utilisées par l’api, il s’agit de slack_api

1
2
[dependencies]
slack_api = "0.23.0"

Infrastructure

Notre webservice est déployé dans un conteneur docker sur GCP, et Slack envoie chaque commande /maxbot au webservice avec une url de callback( même chose pour les évènements)

/rust-slack-bot/diag.png

Implémentation du bot en Rust

Exposition

Pour la partie webservice et exposition du endpoint, on utilise la librairie Rocket.

Création des endpoints :

1
2
3
4
5
pub fn create_routes(sender: Sender<ExchangeInChannel>) -> rocket::Rocket {
    Rocket::ignite()
        .manage(sender)
        .mount("/commands", rocket::routes![process_all_commands])
        .mount(...)
1
2
#[post("/", data = "<command_input>")]
pub fn process_all_commands( ... ) 

Interprétation de la commande

Pour savoir quel traitement doit être effectué, il faut parser la ligne de commande. Exemple de commande à parser :

/maxbot météo , /maxbot help , /maxbot csgo stats.

Dans la structure que l’on reçoit, le nom de la commande et le texte qui suit sont deux champs séparés.

1
2
3
4
5
6
7
8
9
pub fn process_maxbot_command(input_cmd: CommandInput, sender: State<Sender<ExchangeInChannel>>) -> EventResponse {
    let cmd = &input_cmd.text.url_decode().unwrap();
    let cmd_params: Vec<&str> = cmd.trim().split(' ').collect::<Vec<&str>>();
    match cmd_params[0] {
        "météo" | "meteo" => process_meteo_command(...),
        "csgo" => process_command_csgo(...),
        "random" => process_random_command(...),
        "" | "help" => process_help_command(...),
        // et autres commandes

Ensuite le traitement à faire est à adapter selon les besoins, pour l’une des commandes les plus utilisées sur notre bot, il s’agit de la commande /maxbot csgo stat qui s’occupe d’aller se connecter via ssh sur un VPS où tourne un serveur privé CSGO, et d’effectuer une requête sql pour la récupération des statistiques de tous nos participants.

Ici on présentera plutôt ensuite la réponse de la commande random qui permet d’effectuer un jet de pièce ou de dés, afin d’éviter le stat shaming auprès de nos collègues.

Répondre à Slack.

Une fois le traitement effectué, il nous reste à renvoyer la réponse à Slack.

Pour cela on utilise tout ce qui nous a été transmis en même temps que la requête, à savoir une url de callback et un token d’authentification :

1
2
3
4
5
6
/**
* Appel de l'url sur https://hooks.slack.com/commands/XXX/ZZZZZZZZZZZZZZZ fourni dans l'url passée en paramètre
*/
fn post_hooks(client: &Reqwest_client, token: &str, url: String, text: String, response_type: String) {
    client.post(url.as_str()).bearer_auth(token).json(&Hook { text, response_type }).send().unwrap();
}

Exemple de réponse pour la commande /maxbot random coin :

/rust-slack-bot/result-random.png

Conclusion

Dans cet article, on a présenté une façon de personnaliser Slack et son utilisation via une application simple. Ce projet était aussi l’occasion d’avoir un terrain de jeu pour monter en compétence sur Rust, mais ce sera pour un prochain article.