3
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

gitbucketからslackへ通知する

Last updated at Posted at 2017-07-30

はじめに

職場では、社内サーバーにgitbucketを立てており、slackによりメンバー間のコミュニケーションを取っています。
以下のような流れでレビューを実施しているのですが、「プルリクエストへのコメントにmentionがつかない」ので、
誰がプルリクエストへコメントしたかどうかが判断しにくいと、チームから意見がでました。

そこでrustを使用して、プルリクエストへコメントをmention付きでslackへ通知するプログラムを作成しました。

  1. プルリクエストを作成
  2. コードレビューを実施し、gitbucketでコメントを記載
  3. slackへ2で記載したコメントを通知(slack-github連携
    • ここでコメントは指定したチャンネルに通知されるが、mentionがつかない!

インストール

Macではhomebrewでインストールします。

brew install rust

Slack Token生成

rustプログラムからslackへ通知できるよう、アプリケーションを作成し、tokenを生成します。

slack app(チーム名の下矢印クリック) → Customize Slack → API(左サイドメニュー)→ Your Apps(右上メニュー)

Legacy Tokenを使用するので、ここから取得。

docker環境構築

Docker上で動作させる為、Dockerfileを作成します。

FROM ubuntu:16.04

RUN apt-get -y update
RUN apt-get install -y vim curl less
RUN apt-get install -y build-essential
RUN apt-get install -y libtool
RUN apt-get install -y zlib1g-dev
RUN apt-get install -y openssl libssl-dev libcrypto++-dev
RUN apt-get install -y sudo
RUN apt-get install -y pkg-config
RUN curl -sSf https://static.rust-lang.org/rustup.sh | sh

RUN mkdir -p /var/slack
WORKDIR /var/slack
ADD ./source/Cargo.toml /var/slack
ADD ./source/Cargo.lock /var/slack
ADD ./source/src/ /var/slack/src
ADD ./source/target/ /var/slack/target
RUN cargo build --release

CMD cargo run --release

ironサーバー構築

gitbucketからの通知を受け付けるために、ironサーバーを構築します。
Cargoを使用して、新しいプロジェクトを作成します(参考:cargoの使い方)。

cargo new notification

Cargo.tomlを修正し、iron等必要なcrateをインストールします。

[package]
name = "slack"
version = "0.1.0"
authors = ["developer"]

[dependencies]
iron = "*"
params = "*"
slack_api = "0.17.0"
reqwest = "*"
crate名 説明 参考
params リクエストパラメータをパースするためのironプラグイン https://github.com/iron/params
slack_api Slackへ通知を送るcrate https://github.com/slack-rs/slack-rs-api
reqwest HTTPクライアント(?) https://github.com/seanmonstar/reqwest

Slack通知処理

rustからSlackへ通知します。
流れ的には、

  1. gitbucketのプルリクエストへコメント記載
  2. gitbucketからslackへWebohook機能を使用して、ironサーバーへコメント情報をJSONで通知
  3. ironサーバーでJSONをパースし、特定チャンネルへmention付きでコメント通知

以下、コードになります。
gitbucketユーザーとslackユーザーが異なるので、紐付けるため「create_table」でテーブルを作成しています。
各種tokenやユーザー名/チャンネル名は、任意のものを指定してください。

main.rs
extern crate iron;
extern crate params;
extern crate slack_api as slack;
extern crate reqwest;

use iron::prelude::*;
use iron::status;
use std::collections::HashMap;
use std::default::Default;

static TOKEN: &'static str = [slack legacy token];

// git/slackアカウント構造体.
struct Account {
    git_name: String,  // gitbucket上のユーザー名.
    slack_name: String // slackユーザー名.
}

impl Account {
    fn new(git: &str, slack: &str) -> Account {
        Account{ git_name: git.to_string(), slack_name: slack.to_string() }
    }
}

// slack-gitテーブル作成.
fn create_table() -> HashMap<i32, Account> {
    // gitbucketユーザーからslackユーザーを検索できるよう、ここでテーブルを作成しています。
    let mut git_slack_hash = HashMap::<i32, Account>::new();
    git_slack_hash.insert(1, Account::new("gitbucket_test", "slack_test"));
    git_slack_hash
}

// search slack user info.
fn get_slack_userid(_u: &Account) -> Result<String, String> {
    let client = reqwest::Client::new().unwrap();
    let request = slack::rtm::StartRequest::default();
    let response = slack::rtm::start(&client, TOKEN, &request);
    if let Ok(response) = response {
        if let Some(users) = response.users {
            // search user id.
            let account = users.iter()
                               .filter(|u| u.name.clone().unwrap() == _u.slack_name)
                               .next()
                               .unwrap();
            return Ok(account.clone().id.unwrap());
        }
    }
    Err("Failed".to_string())
}

// send message to slack.
fn send_to_slack(id: &str, mention: &str, msg: &str) -> Result<String, String> {
    // send to slack.
    let req_info = slack::chat::PostMessageRequest{
        channel: [チャンネル名],
        text: &format!("<@{}|{}>\n{}", id, mention, msg),
        username: Some([ユーザー名]),
        parse: None,
        link_names: None,
        attachments: None,
        unfurl_links: None,
        as_user: None,
        icon_emoji: None,
        icon_url: None,
        reply_broadcast: None,
        thread_ts: None,
        unfurl_media: None
    };
    let client = reqwest::Client::new().unwrap();
    match slack::chat::post_message(&client, TOKEN, &req_info) {
        Ok(_) => return Ok("Send Success".to_string()),
        Err(_) => return Err("Send Error".to_string())
    };
}

// main function.
fn main() {
    //-------------------------------.
    // slackへ通知を流す.
    //-------------------------------.
    fn notification(req: &mut Request) -> IronResult<Response> {
        // slack hash table from gitbucket account.
        let table = create_table();
        use params::{Params, Value};
        let data = req.get_ref::<Params>().unwrap();

        // gitユーザー名取得.
        let _name = match data.find(&["issue", "user", "login"]) {
            Some(&Value::String(ref name)) => name,
            _ => ""
        };
        // gitユーザ名からslackユーザー名検索.
        let user = table.iter()
                        .filter(|&(_, val)| &val.git_name == _name)
                        .next()
                        .unwrap();

        // コメント取得.
        let msg = match data.find(&["comment", "body"]) {
            Some(&Value::String(ref body)) => body,
            _ => ""
        };

        // send to slack.
        if let Ok(id) = get_slack_userid(&user.1) {
            match send_to_slack(&id, &user.1.slack_name, &msg) {
                Ok(_) => return Ok(Response::with((status::Ok, "Success"))),
                Err(_) => return Ok(Response::with((status::Ok, "Success")))
            };
        }
        else {
            return Ok(Response::with((status::Ok, "Success")))
        }
    }
    Iron::new(notification).http("0.0.0.0:8080").unwrap();
}

ironサーバー起動

docker build && docker upにて、ironサーバーを起動します。
また、gitbucketサーバーからironサーバーへ通知がいくように設定を行います。
(gitbucketのwebhook設定は割愛します)

まとめ

ひとまずはgitbucketから受け取った情報をrustにてパースし、slackへ通知することができました。
ただし、unwrapで直接取り出していたりとまだまだレベルが低いので、エラーハンドリングをもう少し勉強したいと思います。

課題

このプログラムで、githubからslackへmention付きでコメントが通知されるようになりましたが、プルリクエストを作成した人がコメントすると、その人自身にmentionがついてしまいます。
例えば、Aさんがプルリクエストを作成しコメントを行うと、Aさん自身にmentionが付き、コメントが記載されます。
このあたりを、例えば「プルリクエストを許可する人」対してmentionがつくよう、改良していきたいと思います(できるのかな?)。

3
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?