【slack】 ちゃんと声を出して返答してくれるbotをrubyで作ろう

# はじめに

皆さん、1週間後の12/25は何の日か分かりますか？
そうですね、Ruby2.3のリリース予定日ですね！
[サンプルコードでわかる！Ruby 2.3の主な新機能](http://qiita.com/jnchito/items/0faac073cb77417d61c7)

世間一般ではクリスマスです。
残り一週間もあればslackの中に簡単に彼女を作ることが出来ます。

というわけで、Rubyを使って、slack上のメッセージ「Merry Christmas！」に対して「Merry Christmas！」とちゃんと声で返答してくれる彼女(bot)を作ります。

今回、以下の「slack-ruby-bot」というgemのお力をお借りして作成していきます。
https://github.com/dblock/slack-ruby-bot

こちらのtutorialを参考にしているので合わせてお読み頂ければと思います。
https://github.com/dblock/slack-ruby-bot/blob/master/TUTORIAL.md

＊ ローカル環境で立ち上げたときだけ声が返ってくるのでサーバーにアップはしません。
＊ 完成版はこちら -> https://github.com/kuboshizuma/nami

それでは作っていきましょう！

# 実装

## Gemfile
今回は「nami」という名前のbotを作成します。名前は好みの名前に変えて下さい。

まずはアプリケーションのディレクトリを作成します。

```
$ mkdir nami
```

ではこのフォルダの中にファイルを作成していきます。
まず、Gemfileを作成しましょう。

```ruby:Gemfile
source 'http://rubygems.org'

gem 'slack-ruby-bot'
gem 'puma'
gem 'sinatra'
gem 'rake'
gem 'foreman'
```

その後にbundle installをしましょう。

```
$ bundle install
```

## Application
「slack-speakbot」というフォルダを作成し、その中で「app.rb」を作成します。
以下を書くだけでオーケーです。

```ruby:slack-speakbot/app.rb
module SlackSpeakbot
  class App < SlackRubyBot::App
  end
end
```

## Commands

コマンドを作成します。
Aloudというクラスを作ってその中に処理を書いていきます。
Aloudクラスの中の記述の意味ですが、「Merry Christmas!」って言われたら(slack上でコメントしたら)、「speak」というメソッドを実行するよ、という意味です。


```ruby:slack-speakbot/commands/aloud.rb
module SlackSpeakBot
  module Commands
   class Aloud < SlackRubyBot::Commands::Base

      match "Merry Christmas!" do |client, data, match|
        speak
      end

    end
  end
end
```

それでは、speakメソッドのほうも作っていきたいと思います。
slack上で喋ってくれているように声をだすのにはMacではsayコマンド、linuxではespeckを使います。(今回はこれだけの対応です)
speakメソッドが呼び出されたら、rubyのsystemコマンドで「sayコマンド(Mac)またはespeak」を実行するようにします。optionはいい感じに設定していますが、適当に変えていいです。

```ruby:slack-speakbot/commands/aloud.rb
module SlackSpeakBot
  module Commands
   class Aloud < SlackRubyBot::Commands::Base

      def self.speak
        speak_text = "\"Merry Christmas!\""
        command = if /darwin/ === RUBY_PLATFORM
                    "say -v Agnes -r 130 #{speak_text}"
                  else
                    "echo #{speak_text} | espeak -s 120 -p 99"
                  end
        system(command)
      end

      match "Merry Christmas!" do |client, data, match|
        speak
      end

    end
  end
end
```

声を出したタイミングでbotがメッセージでも返事するようにspeakメソッドの実行前に一行記述を付け足します。

```ruby:slack-speakbot/commands/aloud.rb
module SlackSpeakBot
  module Commands
   class Aloud < SlackRubyBot::Commands::Base

      def self.speak
        speak_text = "\"Merry Christmas!\""
        command = if /darwin/ === RUBY_PLATFORM
                    "say -v Agnes -r 130 #{speak_text}"
                  else
                    "echo #{speak_text} | espeak -s 120 -p 99"
                  end
        system(command)
      end

      match "Merry Christmas!" do |client, data, match|
        send_message client, data.channel, 'Merry Christmas!'
        speak
      end

    end
  end
end
```



## Require Everything

root直下に```slack-speakbot.rb``` を作成し、これまでの作成ファイル等を読み込みます。

```ruby:slack-speakbot.rb
require 'slack-ruby-bot'
require 'slack-speakbot/commands/aloud'
require 'slack-speakbot/app'
```

## Web server

ルート直下に。そのままどうぞ。

```ruby:web.rb
require 'sinatra/base'

module SlackSpeakbot
  class Web < Sinatra::Base
    get '/' do
      "I'd like to talk to you."
    end
  end
end
```

## Config.ru

これまた、ルート直下にどうぞ。

```rb:config.ru
$LOAD_PATH.unshift(File.dirname(__FILE__))

require 'slack-speakbot'
require 'web'

Thread.abort_on_exception = true

Thread.new do
  begin
    SlackSpeakbot::App.instance.run
  rescue Exception => e
    STDERR.puts "ERROR: #{e}"
    STDERR.puts e.backtrace
    raise e
  end
end

run SlackSpeakbot::Web
```

## Create a Bot User

ここで、slackでbotを登録しましょう。

slack名の右の「下向き矢印のような部分」をクリックし、その中の「Configure Integrations」を選択して下さい。

![スクリーンショット_2015-12-14_22_15_17.png](https://qiita-image-store.s3.amazonaws.com/0/40796/c6bcfc8c-11c0-6b7c-4880-ca317e751356.png)

表示された画面の検索で「bots」とかやると出てきます。そしてクリック。

![スクリーンショット 2015-12-14 22.15.50.png](https://qiita-image-store.s3.amazonaws.com/0/40796/d81eea31-9938-3213-8855-b5e2194598f9.png)

Usernameを決めてあげて「Add Bot Integration」とすると登録完了です。

![スクリーンショット 2015-12-14 22.16.44.png](https://qiita-image-store.s3.amazonaws.com/0/40796/2989977a-b27c-2a40-e8b1-d5c093a5b609.png)

詳細でavatar画像も設定出来るのでいい感じの写真を使って下さい。そしてAPIトークンを取得して下さい。

## .env

環境変数を設定するために```.env```を作成しましょう。

```rb:.env
# 値は自分のものを使う
SLACK_API_TOKEN=xoxb-erfijadjsvkdaKJDFSJOWf049
```

環境変数が記述されたこのファイルは```.gitignore```に追加しておきましょう。(.gitignoreファイルを作成)

```rb:.gitignore
.env
```

## Procfile
Procfileにforeman起動時に実行するコマンドを記述します。

```rb:Procfile
web: bundle exec puma -p $PORT
```

## Run the Bot

下記コマンドでbotを起動させて見て下さい！

```
$ foreman start
```

これで、slackのチャンネルのどこかにbotを招待して、```Merry Christmas!```とコメントしてみましょう！きっと返事をくれるはずです！

# Only for you

これでもいいんですけど、これだとどのchannelの誰が「Merry Christmas!」って言っても返してくれるのでちょっとさびしいです。
そこで、特定のチャンネルで自分に対してのみ返してくれるようにします。

以下で、新しく ```set_env?``` というメソッドと```watching?``` というメソッドを作り、それがtrueならば speak メソッドを実行します。channelとuserは後で環境変数の設定をするので```ENV['WATCHING_USER']```と```ENV['WATCHING_CHANNELL']```にそれぞれuserとchannelのidがセットされているとして作成します。
```set_env?```が環境変数に上記の環境変数がセットされているか確認するメソッドで、```watching?```がuserとchannelが指定したものか判断するメソッドです。

```ruby:slack-speakbot/commands/aloud.rb
module SlackSpeakBot
  module Commands
    class Aloud < SlackRubyBot::Commands::Base

      def self.speak
        speak_text = "\"Merry Christmas!\""
        command = if /darwin/ === RUBY_PLATFORM
                    "say -v Agnes -r 130 #{speak_text}"
                  else
                    "echo #{speak_text} | espeak -s 120 -p 99"
                  end
        system(command)
      end

      def self.set_env?(client, data)
        if ENV['WATCHING_USER'] && ENV['WATCHING_CHANNELL']
          true
        else
          send_message client, data.channel, "'watching_user' or 'watching_channel' is not defined in slack-speakbot."
          false
        end
      end

      def self.watching?(user, channel)
        user == ENV['WATCHING_USER'] && channel == ENV['WATCHING_CHANNEL']
      end

      match "Merry Christmas!" do |client, data, match|
        if set_env?(client, data) && watching?(data.user, data.channel)
          send_message client, data.channel, 'Merry Christmas!'
          speak if set_env?(client, data) && watching?(data.user, data.channel)
        end
      end

    end
  end
end
```

ここで、userとchannelのidを取得するための実行ファイルを作成しておきます。

```exec``` というフォルダを作成してそのフォルダ内にファイルを作成していきます。

```rb:exec/check-userid.rb
require 'slack-ruby-client'
require 'dotenv'
require_relative '../config/initialize.rb'

client = Slack::Web::Client.new

user = client.users_list['members'].detect{ |u| u['name'] == ARGV[0] }

if user.nil?
  puts "I can not find this user. Please confirm that this user exist."
else
  puts user['id']
end
```

```rb:exec/check-channel.rb
require 'slack-ruby-client'
require 'dotenv'
require_relative '../config/initialize.rb'

client = Slack::Web::Client.new

channel = client.channels_list['channels'].detect{ |u| u['name'] == ARGV[0] }

if channel.nil?
  puts "I can not find this channel. Please confirm that this channel exist."
else
  puts channel['id']
end
```

また、下記ファイルを用意します。

```rb:config/initialize.rb
Dotenv.load

Slack.configure do |config|
    config.token = ENV['SLACK_API_TOKEN']
    fail 'Missing ENV[SLACK_API_TOKEN]! Please set up .env.' unless config.token
end
```

その後に、botから返事がほしいチャンネル名と自分のユーザー名を用意します。

第一引数にチャンネル名で、

```
$ ruby exec/check-channel.rb test
```

第一引数にユーザー名で、

```
$ ruby exec/check-userid.rb shizuma
```

返り値として、それぞれidが返ってくるので、それを```.env``` に記述します。

```rb:.env
# 値は自分のものを使う
SLACK_API_TOKEN=xoxb-erfijadjsvkdaKJDFSJOWf049
WATCHED_USER=SDR43SF
WATCHED_CHANNEL=SEW3295D
```

その後、起動し直して見て下さい。

```
$ foreman start
```

これで、特定のチャンネルのあなただけに返事をするようになりました！