LoginSignup
14
5

More than 5 years have passed since last update.

コードレビューをチームにリクエストするSlack BotをSwift実装したお話🚢

Posted at

GithubでコードレビューをチームにリクエストするSlack BotをSwift実装したお話🚢

サーバーサイドSwiftで代表的なフレームワークVaporを使って、チーム開発をしているレポジトリーでPull Requestを出した時に同じチーム内の誰かにランダムでレビューをアサインするSlack Botを実装しました!

職人のように洗練されて行って欲しいのでGithubShokuninと名付けました👍

現状のGithubShokuninができること

現状のGithubShokuninができることは以下の一個だけです😇

  • Slackで以下のようにメンションすることでGithub上でメンバーにランダムでReviewをリクエストできる。
@Bot review #PR Number

こんな感じです!

Screen Shot 2016-12-24 at 15.25.05.png

GithubShokuninのセットアップ

Vaporのインストール方法はReadMeにまとめてあります!

  • Slack BotのTokenと名前をbot-config.jsonに書き込む
{
    "token": "xoxo-2h4j43n5g3i2mn24g",
    "bot_name": "GithubShokunin"
}
  • GithubのTokenやTeamIdをセットする

※レポジトリー上でReviewを行うチームを作成する必要があります!

{
    "owner_name": "yuzushioh",
    "repo_name": "GithubShokunin",
    "review_team_id": "12345",
    "github_token": "biub3h24vb53vjjv2v42v24g(sample)"
}

Vaporについて

Vapor

インストール ReadMe

以下のコードで、Hello World!を実装できます!

let drop = Droplet()

drop.get("/") { request in
    return "Hello World!"
}

drop.run()

GithubShokuninの実装

Hubotが使えないのでSlackのReal Time Messaging APIを使ってChatを監視しています。

VaporのWebSocket

以下のようにWebSocket Clientを作成できます。

import Engine
import WebSockets

try WebSocket.connect(to: url) { ws in
    print("Connected to \(url)")

    ws.onText = { ws, text in
        print("[event] - \(text)")
    }

    ws.onClose = { ws, _, _, _ in
        print("\n[CLOSED]\n")
    }
}

メンションされたユーザーがbot-config.jsonで指定したBotならば、そのチャットの内容に応じて処理を行うようになっています。

チャットの内容を元にContentを作成する

現状はチャットの内容をもとに以下の3つにわけている。
enum MessageContent {
    case requestReview(prNumber: String)
    case commandLine
    case unknown
}

Contentを元に必要なAPIを叩く

VaporのDropletを使ってGithubのAPIを叩く

let response = try drop.client.get("http://github.com", query: ["q": "test"])

ResponseのJSONをModelにマッピングする。

guard let json = response.json else {
    throw BotError.invalidResponse
}

let pullRequest = try PullRequest(node: json.node)

作成したModelを元にBotのレスポンスを作成する

  • SlackMessageというModelを用意している
struct SlackMessage {
    let id: UInt32
    let channel: String
    let text: String
    let type: String

    init(to channel: String, text: String) {
        self.id = UInt32.random()
        self.channel = channel
        self.text = text
        self.type = "message"
    }
}

このモデルはVaporのNodeConvertibleに準拠していて、モデルからNodeを作り出すことができます!

extension SlackMessage: NodeConvertible {
    func makeNode(context: Context) throws -> Node {
        return try Node(node: [
            "id": id,
            "channel": channel,
            "type": "message",
            "text": text]
        )
    }

    init(node: Node, in context: Context)  throws {
        id = UInt32.random()
        channel = try node.extract("channel")
        text = try node.extract("text")
        type = try node.extract("type")
    }
}

作成いたSlackMessageをメンションがあったSlack Channelに投げ返す!!

try WebSocket.connect(to: url) { ws in
    print("Connected to \(url)")

    ws.onText = { ws, text in
        ......
        let response = SlackMessage(to: message.channel, text: responseText)
        try socket.send(response)
    }

    ws.onClose = { ws, _, _, _ in
        print("\n[CLOSED]\n")
    }
}

 思ったこと

  • Hubotが使えないのでCoffee Scriptでかいた時より格段にコード量が増えてしまった。
  • Vaporは初めて2週間くらいだがなかなか使いやすいし書きやすい!

Vaporの参考にしたり、是非チーム開発で使ってみてください!

14
5
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
14
5