RubyでRettyからランチ候補をオススメしてくれるSlackのBotを作りました

# はじめに

クラウドワークス1年生の@yizknnです。2018年8月にエンジニアとして入社しました。
外部向けに投稿するのは初めてなので部屋の隅で震えながら書いています。

## 前置き

ある時、先輩がこう仰りました。
「ランチは毎日違うお店に行きたい。エンジニアたる者、未知への探究心を忘れたくない」
そんな訳で、修行も兼ねて社内版OSS Gateの時間を利用して、ランチ候補を調べてくれるbotを作成してみました。

クラウドワークスのOSSへの取り組みは、@minamijoyoさんの記事で詳しく紹介していますので宜しければ併せてご覧ください。
[OSS開発に参加してみたい人の背中を押して回る - クラウドワークス エンジニアブログ](http://engineer.crowdworks.jp/entry/2018/10/17/111454)

# どんなBot？

:octocat: GitHub: https://github.com/yizknn/slack-lunch-bot

## `@bot lunch`

Botユーザーにメンションを付けて「lunch」と発言すると、恵比寿のランキングからランダムに3件のお店を提案してくれます。
<img width="777" alt="slack_lunch_bot_sample.png" src="https://qiita-image-store.s3.amazonaws.com/0/276959/74d550c3-fe3d-99cf-c082-3140b028f3dc.png">


## 3点仕様

コードを読みはすれど碌に書いた事が無く、記事のネタにするには時間も少なかったので仕様は小さくしました。

- Slackでお手軽に使える
  - 弊社はSlackBotが多いので、同じような使用感だと良い
- まとめサイトからデータを取得する
  - 未知のお店を知りたいので、Rettyからスクレイピングする
  - サイトに負荷を掛けたくないので、データをDBに保存するようにアップデート予定
- サーバーにお金を掛けない
  - GASは権限の問題で断念
  - 勉強目的でもあるので、自分でHerokuに立てる

## 動作環境

- MacBook Pro (Mojave 10.14.1)
- Ruby 2.5.1
- Heroku
- Slack

# 各コードの働き

とても簡単なコードですが、処理の流れを追って説明します。
`slack-ruby-bot`と`mechanize`をミニマムに利用しているので、行数は抑えられているような気がします。
 
## ページ情報の取得

`mechanize`の`get`メソッドで指定ページの情報を丸ごと取得します。
Rettyは検索フォームから絞り込むと、動的に表示している為かデータが取得できません。
「恵比寿の美味しいランチ20選」のように、データセットが固定されているページを指定します。
  
```ruby:lunchbot.rb
query = 'https://retty.me/area/PRE13/PUR1/city/131131500013/'

agent = Mechanize.new
page = agent.get(query)
```

## データの選別

続けて`mechanize`の`.search`メソッドでページから指定したクラスの要素だけ抜き出します。
店名とリンクが欲しかったので`<a>`タグにしています。
Rettyは店舗情報なども取れてしまうので、改行で分けるという荒技で対応しました。

```ruby:lunchbot.rb
restaurants = {}
elements = page.search('a.restaurant__block-link')

elements.each do |element|
  info = element.inner_text.split("\n")
  restaurants.merge!(info[1].strip => element[:href])
end

restaurants = restaurants.to_a.sample(3)
```

## Slackへの送信

あとは適当に発言内容を整形して、`slack-ruby-bot`の`say`メソッドでSlackに送ります。

```
client.say(text: recommend_comment, channel: data.channel)
```

# お使いになるには

## STEP 1: SlackAPIを作る

1. [公式サイト](https://api.slack.com)の「Start Building」ボタンをクリックしてAPIを作成します
<img width="569" alt="usage_lunch_bot_step1-1.png" src="https://qiita-image-store.s3.amazonaws.com/0/276959/c4e478ec-1317-1de9-2ad4-76588f34f243.png">


2.  [Bot User]ページでBotを作成します
<img width="745" alt="usage_lunch_bot_step1-2.png" src="https://qiita-image-store.s3.amazonaws.com/0/276959/84776961-f63c-90f6-545b-5ea876d2c71c.png">

3. [OAuth & Permissions]ページの「Scopes」でBotがメッセージを送信できる権限を許可します
<img width="742" alt="usage_lunch_bot_step1-3.png" src="https://qiita-image-store.s3.amazonaws.com/0/276959/a73b2853-7f25-7109-f018-6bee52d189ac.png">

4. 同じページの「Install App to Workspace」ボタンをクリックしてBotの利用を許可します
<img width="486" alt="usage_lunch_bot_step1-4.png" src="https://qiita-image-store.s3.amazonaws.com/0/276959/e3078b2c-35fb-a0d5-003e-ae6000ad1efc.png">

5. アプリの認証に必要な「Bot User OAth Access Token」を控えておきましょう
<img width="751" alt="usage_lunch_bot_step1-5.png" src="https://qiita-image-store.s3.amazonaws.com/0/276959/75385b12-91ca-8e99-029f-d127b6ca2c1b.png">

## STEP 2: Herokuで起動する

1. Herokuで新しくAppを作成します
2. Herokuにアプリをアップロードしてデプロイします
  - CLIでもGitHub連携でもお好みの方法でどうぞ
  - GitHubのアカウントがあれば、Forkして連携すると楽だと思います
3. SlackのTokenを環境変数に保存します
  - 変数名は「SLACK_API_TOKEN」としてください
  <img width="1229" alt="usage_lunch_bot_step2-3.png" src="https://qiita-image-store.s3.amazonaws.com/0/276959/a6790ac0-5f46-8f61-55ee-6d955278c736.png">


# 参考リンク
- :octocat: GitHub: https://github.com/slack-ruby/slack-ruby-bot
- Qiita: [Rails スクレイピング手法 Mechanizeの使い方 - Qiita](https://qiita.com/shizuma/items/d04facaa732f606f00ff)

# おわりに

今回は力量を超えた初挑戦を盛り込み過ぎて、これだけ小さくしても50時間くらい掛かりました。
改善の余地しかないので、これからも更新し続けていきたいと思います。
ゆくゆくは早くも先輩方から「機械学習でベストなランチを提案すると良いよね」「AWSも使えると良いね」と下知された機能まで搭載できると良いなと夢見ています。