Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

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

More than 3 years have passed since last update.

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の参考にしたり、是非チーム開発で使ってみてください!

yuzushioh
20歳です!!
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away