[黒い画面不要] ブラウザだけで始めるSlack Bot開発(1) - 準備編

## はじめに
- 黒い画面を使わずにブラウザだけでSlack ChatBotの開発を行う方法を書いていきます
- クレジットカードの登録はありません、全て無料で出来ます
- JavaScript/Node.jsの知識があれば誰でもChatBotの開発ができます
- 具体的にはGlitch上でSlack Botkitを動かす手順を書いていきます
- 最終的にはスラッシュコマンドとボタンを利用したQiitaの記事検索、共有機能の開発を目指してやっていきます
  - こちら実装は[この記事](https://qiita.com/ishihamat/items/53f1364e4b0d4453dfca)に書いてあります
- まずはGlitchとBotkitの説明をした後にChatBot開発の準備をやります
- その後にBotkitのサンプルを簡単に説明していきます


### [Glitch](https://glitch.com/)
- node.jsでWebサーバを立てられるPaaSでオンラインコードエディタとコミュニティ機能が付いています
- 他の誰かが作ったアプリを環境ごとコピーすることができます
- 少々変わったWebデザインですがファウンダーの1人はJoel on Softwareで有名なJoel Spolskyさんです
- 元々はFog Creek Softwareという会社名だったのですがサービスに合わせてGlitchという会社名に変えたみたいです
  - Stack OverflowやTrelloを運営している会社でもあります

### [Botkit](https://botkit.ai/)
- チャットボットのためのフレームワークです
- Slackが公式に推奨しています
    - GitHub製のHubotより後に開発されました
- 具体的にはチャットツールのread/write権限などを委譲されたWebアプリケーションといったところです(Hubotなどもそうです)
- Expressで実装されています

---

## Glitch上でbotkit-slackをRemixする
- まずは[Glith](https://glitch.com/)にアクセスしてサインインを行います
  - GitHubかFacebookのアカウントでサインインできます
- https://glitch.com/~botkit-slack にアクセスします
- `Remix your own ボタン`をクリックしてbotkit-slackをRemixします
  - たったこれだけで自分の環境にコードとWebサーバのコピーができており実際にアクセスすることができます
  - 画面左上のShowをクリックするとremixしたWebアプリにアクセスできます
  - https://somber-forest.glitch.me/ 左のようなURLの`somber-forest`部分はランダム生成されておりremixするたびに変わります

- デフォルトだとコードが公開状態になっています
  - 非公開にしたい場合は鍵アイコンをクリックしてロック状態にしておきます
  - 公開状態でも`.env`ファイルだけは非公開状態になっています（後ほど、ここにアクセストークンなどの隠したい情報を設定していきます）
![Screenshot from 2018-11-19 22-32-54.png](https://qiita-image-store.s3.amazonaws.com/0/3507/362d3d33-fc6c-b0ba-12c4-3a2bd3e426f3.png)


- Slack側の設定に使うのでURLを控えておきます `https://xxxx-xxxx.glitch.me/`
- Glitchは一旦ここまでにします
- Slack側の設定をやってから戻ってきます

---

## Slack上でbotの設定を行う
- https://api.slack.com/ にアクセスします
- `Start Building ボタン`をクリック
  -  `App Name`: `qiita-search` （任意の名前でOK）
  -  `Workspace`を選択
  -  `Create App`をクリック

![Screenshot from 2018-11-19 22-48-58.png](https://qiita-image-store.s3.amazonaws.com/0/3507/2846e0af-6d59-eab9-7a86-223c02e21122.png)

### Interactive Components
- `Interactivity`: `On`
- `Request URL`: `https://xxxx-xxxx.glitch.me/slack/receive` (xxxx-xxxxはglitchのURLから取ってください)
- `Actions`:設定不要（PinやEdit messageのようなメニュー追加が必要であれば設定します）
- `Options Load URL`: `https://xxxx-xxxx.glitch.me/slack/receive`
![Screenshot from 2018-11-20 00-49-28.png](https://qiita-image-store.s3.amazonaws.com/0/3507/0ef8f89a-d9db-ff77-8608-c4b12e177249.png)


### Slash Commands
- `Create New Command ボタン`をクリック
- `Command` : `/qiita`
- `Request URL` : `https://xxxx-xxxx.glitch.me/slack/receive`
- `Short Description` : `Qiitaから検索できます`
- `Usage Hint` : `[検索ワード]`
![Screenshot from 2018-11-19 23-03-54.png](https://qiita-image-store.s3.amazonaws.com/0/3507/53a5ee69-0b6b-67d9-8016-38ca476017c2.png)

### Event Subscriptions
- `Enable Events` : `On`
- `Request URL` : `https://xxxx-xxxx.glitch.me/slack/receive`
- `Subscribe to Workspace Events`
  - `Add Workspace Event ボタン`をクリックして以下設定を追加
      - `message.channels`
      - `message.groups`
      - `message.im`
      - `message.mpim`
![Screenshot from 2018-11-20 00-47-24.png](https://qiita-image-store.s3.amazonaws.com/0/3507/bc00b6b6-3ec2-258f-40b2-7d0bf8991e4b.png)


### Bot User
- `Display name` : `qiita-search`
- `Default username` : `qiita-search`
- `Always Show My Bot as Online` : `On`

![Screenshot from 2018-11-20 00-46-38.png](https://qiita-image-store.s3.amazonaws.com/0/3507/82f96fad-faee-c226-90d1-35635aceb545.png)



### OAuth & Permissions
- `Add New Redirect URL ボタン`をクリック
- `Redirect URLs`: `https://xxxx-xxxx.glitch.me/oauth`
  - `Addボタン`をクリック後に`Save URLsボタン`で保存

- `Select Permission Scopes`に以下を設定
  - `Send messages as qiita-search`
  - `Send messages as user`
  - `Add slash commands and add actions to messages (and view related content)`
      - `Save Changesボタン`をクリックして保存
- `Install App to Workspaceボタン`をクリックし遷移先のページで`Authorizeボタン`をクリック

### Basic Information
- 左メニューの`Basic Information`をクリック
- Client IDとClient Secretを控えます
![Screenshot from 2018-11-20 01-08-16.png](https://qiita-image-store.s3.amazonaws.com/0/3507/89993517-7fd2-652a-2197-5f4f9d0f7355.png)

- Slackの設定は以上です
- Glithに戻りBotkitの設定を行います

----------

## Botkit設定の続き

- glitchのエディタから`.env`ファイルに設定を行います
    - 保存はctrl+S（放っといても勝手に保存されます）

```bash:.env
clientId=[Slack AppのBasic Informationでコピったもの]
clientSecret=[Slack AppのBasic Informationでコピったもの]
studio_token=[空でOK、Botkit Studio用の設定]
PORT=
```
- https://xxxx-xxxx.glitch.me/ にアクセスし`Add to Slack ボタン`をクリック
  - glitchのコードエディタ画面にあるShowからでもアクセスできます

![Screenshot from 2018-11-19 23-41-37.png](https://qiita-image-store.s3.amazonaws.com/0/3507/72e508b4-4d47-61d9-97da-c8167deffb0f.png)

- 以下メッセージが表示されればOKです
![Screenshot from 2018-11-20 00-22-31.png](https://qiita-image-store.s3.amazonaws.com/0/3507/c0352040-8b2f-3a21-367a-e284753e8f3b.png)

- これで全ての設定は終了です

----

## ChatBotの動作確認

- Botkitのbot.jsがskillsディレクトリ配下のファイルを読み込んでいます

```javascript:bot.js
  var normalizedPath = require("path").join(__dirname, "skills");
  require("fs").readdirSync(normalizedPath).forEach(function(file) {
    require("./skills/" + file)(controller);
  });
```

- skillsディレクトリ配下にsampleが置いてあるので処理の説明をしながら動作確認をやっていきます

### sample_hears.js
- botがメッセージを聞いて反応するサンプルコードです
- メンションかDMで`say hogehoge`のようなメッセージがあるとオウム返しをしてくれます
  - echo serverですね

```javascript:skills/sample_hears.js
    controller.hears(['^say (.*)','^say'], 'direct_message,direct_mention', function(bot, message) {
        if (message.match[1]) {

            if (!wordfilter.blacklisted(message.match[1])) {
                bot.reply(message, message.match[1]);
            } else {
                bot.reply(message, '_sigh_');
            }
        } else {
            bot.reply(message, 'I will repeat whatever you say.')
        }
    });
```

![Screenshot from 2018-11-20 01-01-13.png](https://qiita-image-store.s3.amazonaws.com/0/3507/80a29511-0a5b-57a5-249a-00ba0da25a56.png)

### sample_conversations.js
- botと連続したメッセージのやりとりを行うサンプルコードです
- botの質問に答えると、その答えを利用したメッセージを返してくれます

```javascript:skills/sample_conversations.js
    controller.hears(['color'], 'direct_message,direct_mention', function(bot, message) {

        bot.startConversation(message, function(err, convo) {
            convo.say('This is an example of using convo.ask with a single callback.');

            convo.ask('What is your favorite color?', function(response, convo) {

                convo.say('Cool, I like ' + response.text + ' too!');
                convo.next();
            });
        });
    });
```

![Screenshot from 2018-11-20 02-16-35.png](https://qiita-image-store.s3.amazonaws.com/0/3507/cddede0c-a1fc-ac39-9652-4b65777eebfa.png)

### sample_taskbot.js
- タスクの追加と一覧表示を行うサンプルコードです
- つまりデータの管理を行うことができます
  - jfs(JSON file store)でデータを保存しているっぽいです

```javascript:skills/sample_taskbot.js
    controller.hears(['add (.*)'],'direct_message,direct_mention,mention', function(bot, message) {

        var newtask = message.match[1];
        controller.storage.users.get(message.user, function(err, user) {

            if (!user) {
                user = {};
                user.id = message.user;
                user.tasks = [];
            }

            user.tasks.push(newtask);

            controller.storage.users.save(user, function(err,saved) {

                if (err) {
                    bot.reply(message, 'I experienced an error adding your task: ' + err);
                } else {
                    bot.api.reactions.add({
                        name: 'thumbsup',
                        channel: message.channel,
                        timestamp: message.ts
                    });
                }

            });
        });

    });
```
![Screenshot from 2018-11-20 02-34-44.png](https://qiita-image-store.s3.amazonaws.com/0/3507/5cc6205e-ba2b-b4a6-ec22-4af7fffb54d8.png)


```javascript:skills/sample_taskbot.js
    controller.hears(['tasks','todo'], 'direct_message', function(bot, message) {

        // load user from storage...
        controller.storage.users.get(message.user, function(err, user) {

            // user object can contain arbitary keys. we will store tasks in .tasks
            if (!user || !user.tasks || user.tasks.length == 0) {
                bot.reply(message, 'There are no tasks on your list. Say `add _task_` to add something.');
            } else {

                var text = 'Here are your current tasks: \n' +
                    generateTaskList(user) +
                    'Reply with `done _number_` to mark a task completed.';

                bot.reply(message, text);

            }

        });

    });
```
![Screenshot from 2018-11-20 02-30-27.png](https://qiita-image-store.s3.amazonaws.com/0/3507/b471bf46-e3b1-8380-04c8-8b7a02515f2b.png)

### sample_events.js
- channel joinに反応するサンプルコードです

```javascript:skills/sample_events.js
    controller.on('user_channel_join,user_group_join', function(bot, message) {

        bot.reply(message, 'Welcome, <@' + message.user + '>');

    });
```

![Screenshot from 2018-11-20 02-38-49.png](https://qiita-image-store.s3.amazonaws.com/0/3507/1f03a491-f63a-55e7-5f84-3aee8a0be739.png)

- Botkitサンプルの説明はだいたい出来たと思うので一旦ここまでにします
- Qiitaの記事検索の話は次の記事でやっていきたいと思います

---

## 終わりに
- Glitch、Slack設定、Botkit説明で結構なボリュームになってしまいましたが、これまでの説明でサンプルファイルを参考にbotの開発がある程度できる状態になっていると思います
  - 開発に必要なBotkitとSlackのドキュメントはこちらにあります
      - [Botkitドキュメント](https://botkit.ai/docs/readme-slack.html)
      - [Slackドキュメント](https://api.slack.com/#read_the_docs)
- 次回はスラッシュコマンドを使ったQiita検索を行う機能の開発を行う予定です
- ボタンを使ったインタラクティブ処理も行う予定なのでコードが少々複雑になってしまうと思います

## 追記: 実装編を書きました
[黒い画面不要] ブラウザだけで始めるSlack Bot開発(2) - 実装編]
https://qiita.com/ishihamat/items/53f1364e4b0d4453dfca