RubyとSlackBotを使って、誰でもIssueを作成できるBotを作ってみた。

## はじめに
先日、このTweetを見かけてしまいました。

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">Slackで絵文字つけるだけでGitHubにissue化できるようにするとめっちゃ便利…<br>スマホからでもいつでもすぐissue作れて、あとから文脈をSlackのリンクから辿れるので内容を思い出せる <a href="https://t.co/LWsy4pXkZg">pic.twitter.com/LWsy4pXkZg</a></p>&mdash; uiu (@uiu______) <a href="https://twitter.com/uiu______/status/1118667737104916480?ref_src=twsrc%5Etfw">2019年4月18日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

**「これは...圧倒的に便利すぎる！」** と衝撃を受け、有り余る好奇心で開発を試みたのが始まりです。(しかもGWで割と時間もあったので)

今回はRubyとSlack Bot、Github APIを使用して、Slackのリアクションでissueを作成してみました。ちなみにSlack Botほぼ初心者でも2日くらいでだいたい動くものは完成したので、サクッと作れます。

### 完成イメージ
<img width="642" alt="slack_bot.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/168550/2e546527-6bbc-75ba-1dc2-4e7bdd246ec2.png">


## この記事を読んでできること
* RubyとSlack Botを使って、SlackのリアクションでGithubのissueが作成できる。
* Slack上の特定のメッセージ、リアクションに対して、決まった返答を返すことができる。




### 準備
> 言語：Ruby
API：Github API
Bot：Slack

Slack Botを使ったことのない方推奨の記事：
http://studio-andy.hatenablog.com/entry/ruby-bot
一度手を動かしてみると挙動が把握で切るのでオススメです！

### SlackBotを作成しよう

https://amdlaboratory.com/amdblog/slack-bot%E3%81%AE%E4%BD%9C%E3%82%8A%E6%96%B9%E3%80%82/
こちらのSlack Botの作り方を参考にBotを作成してください。

APIの挙動確認、パラメータ、レスポンスなどはこちらからデモ操作できます。
https://api.slack.com/methods/chat.postMessage/test



### Github APIを取得
こちらも公式APIドキュメントから細かいパラメータの設定や、レスポンスなどが確認できます。
https://developer.github.com

今回はissueを作成したいので、
https://developer.github.com/v3/issues/#create-an-issue


### Rubyファイルを作成

作成するファイル構成
slack_bot
┣ slack_bot.rb
┣ Gemfile
┗ Gemfile.lock

```ruby:Gemfile
# frozen_string_literal: true
source "https://rubygems.org"
ruby "2.6.3"

gem 'http'
gem 'json'
# websocketを使用する
gem 'faye-websocket'
gem 'eventmachine'
```

`bundle install`
しておく。

```ruby:slack_bot.rb

require 'http'
require 'json'
require 'eventmachine'
require 'faye/websocket'
require 'uri'
require 'net/http'
require 'net/https'


# 環境変数を設定する
# export SLACK_API_TOKEN=xoxo-hogehoge-api
# export GITHUB_USERNAME=hoge
# export GITHUB_PASSWORD=hogepass

SLACK_RTM_URL="https://slack.com/api/rtm.start"
SLACK_REACTION_URL="https://slack.com/api/reactions.get"


response = HTTP.post(SLACK_RTM_URL, params: {
    token: ENV['SLACK_API_TOKEN']
})

rc = JSON.parse(response.body)
url = rc['url']

EM.run do
    # Web Socketインスタンスの立ち上げ
    ws = Faye::WebSocket::Client.new(url)

    #  接続が確立した時の処理
    ws.on :open do
        p [:open]
    end
    # RTM APIから情報を受け取った時の処理
    ws.on :message do |event|
        data = JSON.parse(event.data)
        p [:message, data]
        if data['text'] == '疲れた'
            ws.send({
                type: 'message',
                text: "お疲れ様です！ <@#{data['user']}> さん",
                channel: data['channel']
            }.to_json)
        elsif data['text'] == ':github_issue:'
            ws.send({
                type: 'message',
                text: "<@#{data['user']}> さん！メッセージに、:github_issue:リアクションをするとissueが簡単に作成できますよ。",
                channel: data['channel']
            }.to_json)
        elsif data['text'] == 'グッときた'
            ws.send({
                type: 'message',
                text: "<@#{data['user']}> さん、:最高かよ:",
                channel: data['channel']
            }.to_json)
        end

        if data['reaction'] == 'github_issue'

            slack_response = HTTP.post(SLACK_REACTION_URL, params: {
                    token: ENV['SLACK_API_TOKEN'],
                    channel: data['item']['channel'],
                    timestamp: data['item']['ts']
                })
            slack_response = JSON.parse(slack_response)

            https = Net::HTTP.new('api.github.com', '443')
            https.use_ssl = true
            https.start do |https|
                # Issue を作る API: http://developer.github.com/v3/issues/#create-an-issue
                req = Net::HTTP::Post.new('/repos/githubアカウント名/リポジトリ名/issues')
                req.basic_auth ENV['GITHUB_USERNAME'],ENV['GITHUB_PASSWORD']
                issue_info = {
                'title': "#{slack_response['message']["text"]}",
                'body': "ヘルプです",
                "labels": [
                        "help wanted"
                    ]
                }

                # githubAPIを叩いて、issue作成
                req.body = JSON.generate issue_info
                github_response = https.request(req)

                # JSON.parseとは、JSON形式の文字列をRubyのHash形式に変換するためのメソッド
                github_response = JSON.parse(github_response.body)

                ws.send({
                    type: 'message',
                    text: "<@#{data['user']}> さんのために、issueを作成しました！issueボードを確認してください。 #{github_response["html_url"]}",
                    channel: data['item']['channel']
                }.to_json)
            end
        elsif  data['reaction'] == 'fish'
            slack_response = HTTP.post(SLACK_REACTION_URL, params: {
                    token: ENV['SLACK_API_TOKEN'],
                    channel: data['item']['channel'],
                    timestamp: data['item']['ts']
                })
            slack_response = JSON.parse(slack_response)

            ws.send({
                type: 'message',
                text: "魚のリアクションしましたね？",
                # text: "#{slack_response['message']['text']}に、魚のリアクションしましたね？",
                channel: data['item']['channel']
            }.to_json)
        elsif data['reaction'] == 'gyozabu'
            ws.send({
                type: 'message',
                text: "なるほど！<@#{data['user']}> さんは震えるほど餃子が食べたいみたいですよ！ <@here> 今日は、餃子活動しないんですか？",
                channel: data['item']['channel']
            }.to_json)
        end
    end
    # 接続が切断した時の処理
    ws.on :close do
        p [:close, event.code]
        ws = nil
        EM.stop
    end

end

```

rubyスクリプト実行。
`bundle exec ruby slack_bot.rb`

<img width="789" alt="rubyスクリプト起動.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/168550/51d93bca-e101-5aa0-bc1d-f1af24b6d200.png">



作成したSlackBotの入っているチャンネルで、メッセージを送信したり、リアクションしてみるとBotが動いて、挙動が確認できると思われます。

### まとめ
これでエンジニアではなくても、困っているメンバーが簡単にIssueを作成できるようになりました。(使われると嬉しい。。)Issue Boadは毎週エンジニアMTGで確認して、社内メンバーの困っているIssueを改修して参ります。

### おまけ🥟
折角SlackBotを作ったので、多少ユーモアを入れてみました。
(割と開発の醍醐味だと思っています)
非常に共感したので引用させて頂きました。↓

>くだらないBotを運用する一番のメリットは、楽しく技術の勉強ができるという点です。
>失敗をおそれず色々な技術を試せる
>工夫次第でどんな技術でも自由に取り入れられる
>得た知見を業務に役立てられる
>車輪の再開発/overkillが気にならない
>フロント側のコードを書かなくても目に見える楽しいアウトプットがある
>https://tech.dely.jp/entry/slack_bot_philosophy



今回は餃子が震えているリアクションをするとBotが反応してくれます。

<img width="740" alt="gyozabu.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/168550/b2218acb-8470-23de-6e26-78624e0a7d2e.png">
(`@here` がメンションになっていない部分の課題感は残りつつ)
**SlackBotはアイディア次第でもっと面白くできそうです！**

折角なので、社内の餃子部イベントでLTした内容もよかったらどうぞ！

<iframe src="//www.slideshare.net/slideshow/embed_code/key/BqYh3WtjpNSs2n" width="595" height="485" frameborder="0" marginwidth="0" marginheight="0" scrolling="no" style="border:1px solid #CCC; border-width:1px; margin-bottom:5px; max-width: 100%;" allowfullscreen> </iframe> <div style="margin-bottom:5px"> <strong> <a href="//www.slideshare.net/secret/BqYh3WtjpNSs2n" title="Slackで餃子マスターを召喚しよう〜餃子Bash 2019/08〜" target="_blank">Slackで餃子マスターを召喚しよう〜餃子Bash 2019/08〜</a> </strong><strong><a href="https://www.slideshare.net/watanaberina" target="_blank"></a></strong> </div>



何か間違えている部分など気になる点があれば、優しく教えて頂けると励みになります。

#### 推奨記事
* Slack API
https://qiita.com/namutaka/items/233a83100c94af033575

* WebSocketについて
https://qiita.com/south37/items/6f92d4268fe676347160

### デプロイ編
[SlackBotをHerokuでデプロイ](https://qiita.com/naberina/items/da4a6d3c480aa7a62b06)