forestec

勉強した内容をつらつらと備忘録として記していきます。

RustでSlackにカスタム絵文字を登録するツールを作成してみた

Slackのカスタム絵文字

以下みたいなアクションで使う絵文字
f:id:forest_yuzuremon:20181128193054p:plain

このカスタム絵文字の登録をSlackから実施出来るようにRustでAPIを実装してみました。

このソースはgithubにもあげています。 github.com

概要

以下のようなAPIになっています。
・スラッシュコマンドで画像のURLと登録絵文字名をリクエストとして受け取る
・画像のURLからアップロードする画像を取得する
・Slackの絵文字アップロード画面をPOSTで叩く

Slackではカスタム絵文字登録を行うAPIは公開されていませんでした。
なので、Slackの画像アップロード画面を直接APIから叩くような仕様になっています。

api_tokenの取得方法について

Slackの画像アップロード画面を叩く際にapi_tokenが認証に必要になります。
api_tokenは以下から取得できます。
https://[自身のワークスペース].slack.com/customize/emoji
ページを開いたらF12を押してデベロッパーツールを開きます。
その状態でページを再読み込みし、上部のSourcesを選択します。
そこで表示されるHTML内のapi_tokenを使用しています。

実装

リクエストの分解

// 空白で文字列を分割する 絵文字画像URL 登録絵文字名
let v: Vec<&str> = params.get("text").unwrap().split(' ').collect();
let emoji_url: &str = v.get(0).unwrap();
let emoji_name: &str = v.get(1).unwrap();

スラッシュコマンドで送られてきたリクエストからparamsを取得、
そのparamsを空白で分割し、1つ目を画像URL、2つ目を絵文字登録名として取り出します。

画像の取得

// URLから画像を取得してローカルに保存する
fn download_image(url: &str) {
    // 画像データの取得
    let image = reqwest::get(url).unwrap();
    let file = File::create(FILE_NAME).unwrap();
    let mut br = BufWriter::new(&file);
    for byte in image.bytes() {
        br.write(&[byte.unwrap()]).unwrap();
    }
    br.flush().unwrap();

    // 保存した画像に読み取り権限を付与
    let mut perms = file.metadata()
        .expect("メタデータの取得に失敗しました。")
        .permissions();
    perms.set_readonly(true);
    file.set_permissions(perms)
        .expect("権限付与に失敗しました。");
}

リクエストから取得した画像URLで画像を取得し、
それを一時的にローカルに保存しています。 後でSlackにアップロードするために読み取り権限を付与しています。

絵文字アップロード

// 絵文字のアップロードを行う
fn upload_emoji(url: &str, form: Form, emoji_name: &str, api_token: &str)
-> impl Future<Item = String, Error = Error> {
    // カスタム絵文字アップロードリクエスト
    let mut res = Client::new()
        .post(url)
        .query(&[
            ("mode", "data"),
            ("name", emoji_name),
            ("token", api_token)])
        .multipart(form)
        .send()
        .unwrap();

    match res.text() {
        Err(e) => fut_ok(String::from(format!("アップロード失敗:{:?}", e))),
        Ok(result) => {
            let v: Value = serde_json::from_str(result.as_str()).unwrap();

            // エラー判定
            match v["ok"].as_bool() {
                Some(true) => fut_ok(String::from(format!("アップロード完了::{}:", emoji_name))),
                Some(false) => fut_ok(String::from(format!("アップロード失敗:{}", v["error"]))),
                None => fut_ok(String::from(format!("アップロード失敗:{}", v["error"]))),
            }
        }
    }
}

Slackへの絵文字のアップロードを行なっています。
crateはrequwestを使用しています。
ここで設定しているemoji_nameはスラッシュコマンドから、
api_tokenは上で記載した取得方法で取得したものを使用しています。
Slackからのレスポンスも判定しており、
正常に登録できた場合は応答として「アップロード完了:[登録した絵文字]」が、
登録に失敗した場合は「アップロード失敗:[エラーメッセージ]」が返却されるようになっています。

使用イメージ

addemojiというスラッシュコマンドを使用しています。 cat_iconという名前で登録してみます。 f:id:forest_yuzuremon:20181128232620p:plain
登録が完了すると以下のように表示されます。
f:id:forest_yuzuremon:20181128232850p:plain

これでスマホのSlackアプリからも登録出来るようになりました。