SlackAPIを使って宅配ランチを注文できるbotを作ってみた

# なぜ作ったか
皆さん平日のランチはどうしてますか？
僕は極度の出不精かつ面倒くさがりなので、**ランチに出かけたりメニューに悩んだりするのが好きではない**です。

**Slackに3つくらいおすすめのメニューが配信されて、ワンクリックで注文できたらいいのに**と思いついたので**1年半かけてbotを開発**しました。

現在はプロダクト化して一般公開しています。
[recoben レコベン | Slackで宅配ランチ注文](https://obento.chat/)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364049/b3f42574-6ff5-3fc4-0dd3-e8bd37c29dab.png)

# どのように動くか
主な仕様は以下の通りです
- Slackから住所を登録
- 朝10時に近隣の飲食店からいい感じにチョイスされた3メニューが届く
- ワンクリックで注文可能
- 12時すぎのお昼時に商品が届く

Slackとの連携は、npmの[@slack/web-api](https://www.npmjs.com/package/@slack/web-api)モジュールを使用しました。

## インストール
[Webページ](https://obento.chat/)のAdd to Slackボタンを押すと、Slackのページにリダイレクトします。その画面でインストールしたいチャンネルを選択すれば完了です。
インストール処理時に、前述したモジュールを使用してOAuth認証を通しています。

## 住所登録
インストールしたSlackのチャンネルにbotから住所登録を促すメッセージが届くので、手順に従ってSlack上で住所を登録します。
ボタンを押して文字を入力するだけです。
![スクリーンショット 2021-02-01 23.31.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364049/1ea86b13-aaf4-0275-9833-f1ccfa19de4b.png)
Slackにモーダルを表示するAPIがありますので、OAuth認証通過時に発行されたトークン情報を使用してモーダル表示リクエストをチャンネルに対して行っています。

## メニュー配信
毎朝10:00にbotがそのチャンネルに適した3つのメニューを選定して、投稿します。レコメンドエンジンを本当はこだわりたかったですが、時間が足りなかったので限りなくランダムに近い選定ロジックです😇
将来的にはチャンネルごとのニーズに合わせて選定するようにロジックを構築する予定です。
![slack.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364049/c47e9acb-3ef7-5c58-7897-375dea4fcc60.png)
[node-cron](https://www.npmjs.com/package/node-cron)を使用して、決められた時間になったらメニューの生成と配信を自動実行するようになっています。

## 注文
初回注文時のみSlack上でモーダルが開いて、カード情報を入力します。Stripeを利用して認証と決済を行います。
カード情報の認証が通ると、注文が登録されます。
![スクリーンショット 2021-02-01 23.32.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364049/04208bb3-011d-6f4f-ccc1-a7ee1c621363.png)
npmの[stripe](https://www.npmjs.com/package/stripe)モジュールを使用して、カードの有効性確認・決済・キャンセルなどあらゆる注文系の処理をStripe APIで行っています。カード情報はもちろん非保持です。

## 細かいUX
小さなこだわりとして、ランチごとに注文したユーザーがサムネイルで可視化されるようにしました。「あいつはコレ頼んだのか〜、じゃあオレはコレにしよ」という意思決定ができると面白いかないう軽い気持ちで作ったのですが、リアルタイムに反映していくためのSlackAPIを絡めた実装が地味に複雑化して大変でした。
![スクリーンショット 2021-01-29 20.26.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364049/ba4881ca-7e56-cd30-c472-382026270e73.png)
Slackに特定の投稿を上書きするAPIがありますので、主にそれを使用しています。ただし、Slack側にドキュメントで明文化されていない連続受付の制限やキューの順番制御があり、同時注文や連打には対策が必要です。（ここは相当骨が折れました）

## 注文達成〜飲食店に注文
チャンネル内の合計注文数がめでたく最低注文数を超えると、以下のようなメッセージが届き、飲食店に注文が飛びます。
![スクリーンショット 2021-01-29 20.26.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364049/4d895676-3eac-883a-9741-4fdaae76191b.png)


# ざっくりシステム構成
Kubernetesを使ってネットワークでブリッジされたDockerコンテナを構築してます。APIはNode.JSで実装しており、外部のSlackAPIや決済モジュールと接続してインタラクティブな機能を実現しています。決済モジュールはStripeを使用しました。
内部の運営者が注文情報等を確認するための管理画面はVue.jsで実装しています。Pythonは、飲食店に代理注文を行うため部分特化的な機能の実装に使用しています。
![kousei.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364049/0379c54f-dbdd-5c41-3032-54b3f2684089.png)



# 自分たちで使ってみた感想〜飛行機の機内食のUXを目標に〜
UXとして目指したのは**「飛行機の機内食」**です。「Beaf or Chiken」くらい簡単な意思決定で脳のリソースを使わないし、味も悪くなく、効率よく食事の満足感を得られるので僕は最高のUXだと思っています。

COMPのような完全食をヘビロテしていたこともあったのですが、味のある固形物を摂取する快楽は捨てがたく、自分たちでこのようなサービスを作るに至りました。

手前味噌ですが、弊社では全員毎日使っており、ユーザービリティは最高です。ただ、たまに食べたいメニューが出てこないことがあります。それは将来的にランチメニュー選定のアルゴリズム改善で解決するつもりです。

# 今後のアップデート予定
- メニュー配信時刻をユーザーのニーズに合わせる
- メニューの選定ロジックをユーザーのニーズに合わせる（金額・好みのカテゴリ・カロリー等）
- 飲食店・お弁当屋さんを増やしたい（募集中）→自社で配送もやっている+Webで注文を受け付けている飲食店がかなり少ない状況…
- Slack以外のプラットフォームと連携？（ニーズを探りながら...）

などなど、様々なアップデートを予定しています。

# Slack×Eコマースの可能性
今回はランチに焦点を当ててサービスを開発しましたが、ネットで販売可能なものであれば何にでも流用できます。
- 毎週末にオススメの映画情報が送られてきて、Slack上で座席の予約が完了する
- 飲み会の会場の候補が送られてきて、参加者の可視化と割り勘がSlack上で完結する→幹事の仕事が楽になる

などなど、色々なことに応用できそうです。
「これは」というアイデアがありましたら、ぜひお気軽にご連絡ください！

# さいごに
この記事に反響があったら、Slack APIの使い方などもう少しソースコードレベルに踏み込んだ内容も記事化していきたいと思います。1年間以上SlackのAPIを使い倒した&調べまくったので、Slackで実現可能なアプリケーションについて国内でここまで詳しい人はいないのでは？と（過剰な）自信がありますw

また、その他サービスに関するご意見・要望、アイデア、なんでも構いませんので、何かありましたら以下からお気軽にご連絡ください！

[Webサイト問い合わせフォーム](https://obento.chat/)
[Facebookグループ](https://www.facebook.com/recoben.by.OneStone)
[Twitter（公式）](https://twitter.com/recoben_slack)
[Twitter（個人用）](https://twitter.com/knob3110)＊公式だと気が重いという方はお気軽に私にどうぞ