Webの勉強はじめてみた その21 〜Slack上で動くbot作成1〜

N予備校の「プログラミング入門Webアプリ」を受講しています。
今回は第3章7〜9節です。
講義内ではTODOのbotでしたが、復習も兼ねて自分で作りました。

<h2>学んだ用語</h2>
<h3>CRUD クラッド</h3>

:::note
ソフトウェアが情報の永続化をしようとしたときに出てくる操作、Create, Read, Update, Deleteの頭文字をとったもの。
:::

<h3>リファクタリング</h3>
:::note
ソースコードを見易くする。可読性の向上。
:::

<h3>テスト駆動開発</h3>
:::note
実装や修正よりも先にテストを用意し、テストに適合するようにリファクタリングを繰り返すことでコードの品質を高めていく開発手法
:::

<h2>とにかくほめてくれるbotを作る</h2>
<h3>要件</h3>
1. 「ほめて」 : あらかじめ用意された配列からランダムで発言
2. 「もっと ~」 : ~という内容を配列に登録。 
3. 「ほめないで ~」 : ~(インデックス)番のメッセージを削除
4. 「見せて」 : 登録されているメッセージを一覧で表示 

<h3>手順</h3>
`praise-bot-test`ディレクトリを作成
1. yeomanのインストール(Dockerfileに記述)
2. `yarn init`で package.json を作成
3. index.js(モジュールを記述するファイル)とtest.js(テストを実行するファイル)
4. モジュールの実装
5. リファクタリング
`praise-bot`ディレクトリ作成
1. yeomanのインストール(Dockerfile)
2. `cp -r` で`praise-bot-test`をコピー
3. `yo hubot --adapter=slack` でbot作成
4. `scripts`ディレクトリに実行ファイル(index.js)を作成
5. 実装 
6. slack上でhubotの作成
7. コンソール上で環境変数の指定と実行

<h3>Docker</h3>
           
```Dockerfile
FROM --platform=linux/x86_64 node:14.15.4

RUN apt-get update
RUN apt-get install -y locales
RUN locale-gen ja_JP.UTF-8
RUN localedef -f UTF-8 -i ja_JP ja_JP
ENV LANG ja_JP.UTF-8
ENV TZ Asia/Tokyo
RUN yarn global add yo@3.0.0
RUN yarn global add generator-hubot@1.1.0
RUN yarn global add coffeescript@1.12.7
RUN useradd praise-bot -m
USER praise-bot
WORKDIR /home/praise-bot
```

```yml
version: '3'
services:
  app:
    build: .
    tty: true
    volumes:
      - .:/home/praise-bot
```

<h3>モジュール</h3>

```javascript
'use strict';
/**
 * slack上で欲しい言葉をくれるボット
 */
const praiseWords = [
    {index:1, msg:"今日はよくがんばりました。すごいです。"},
    {index:2, msg:"生きていてくれて嬉しいです。ありがとう。"},
    {index:3, msg:"あなたの頑張りは私だけが知っています。お疲れ様でした。"},
    {index:4, msg:"あなたの優しさはきっと届いています。"},
    {index:5, msg:"今のあなたはイケてます。すごくいい感じです。"},
    {index:6, msg:"たくさんがんばりましたね。今日もお疲れ様でした。"},        
];
/**
 * インデックスの最大値にプラス1をする
 * @returns 最大値
 */
 function maxIndex(){
    let newInt = 1;
    //配列のindexのみ取得
    const maxArray = praiseWords.map(m => m.index);
    //最大値
    for(let max of maxArray){
        if(max >= newInt){
            newInt = max + 1;
        }
    }
    return newInt;
}
/**
 * メッセージの追加
 * @param {integer} index  
 * @param {string} msg  メンションするメッセージ
 */
function addPraise(msg){
    //indexの最大値取得
    const newIndex = maxIndex();
    praiseWords.push({index:newIndex, msg:msg});
}
/**
 * 指定したインデックスでメッセージの削除
 * @param {integer} index 
 */
function delPraise(index){
    //指定されたインデックスの存在チェック
    const indx = praiseWords.findIndex(p => p.index === index);
    if(indx !== -1){
        praiseWords.splice(indx, 1);
    }
}
/**
 * 一覧を表示
 * @returns index : メッセージ
 */
function listPraise(){
    return praiseWords.map(w => w.index + " : " + w.msg);            
}
/**
 * ランダムにメッセージを返す
 * @returns {string} メッセージ
 */
function praise(){
    if(praiseWords.length > 0){
        //ランダムな添字の取得
        const randomIndex = Math.floor(Math.random() * praiseWords.length);
        return praiseWords[randomIndex].msg;
    }else{
        return "ほめてもらいたい言葉が見つかりませんでした";
    }
}
module.exports = {
    praise,
    add:addPraise, 
    del:delPraise, 
    list:listPraise
}
```

```javascript
'use strict';
// Description
//  「ほめて」というとほめてくれるbot
// Commands:
//   ほめて                        - 登録された言葉をランダムに発言する
//   もっと メッセージ内容           - ほめて欲しい言葉を登録する
//   ほめないで index番号           - 指定された番号の言葉を削除する
//   見せて                        - 登録されている言葉を一覧表示
const praise = require('praise-bot');

module.exports = robot =>{
    robot.hear(/ほめて/i, msg =>{
        const words = praise.praise();
        msg.send(words);
    });
    robot.hear(/もっと (.+)/i, msg =>{
        const words = msg.match[1].trim();
        praise.add(words);
        msg.send("登録しました。もっとほめますね♪");
    });
    robot.hear(/ほめないで (.+)/i, msg =>{
        const index = msg.match[1].trim();
        praise.del(parseInt(index));
        msg.send("削除しました。他の言葉でほめますね。");
    });
    robot.hear(/見せて/i, msg =>{
        const words = praise.list().join('\n');
        if(words.length === 0){
            msg.send("登録されているメッセージがありません。")
        }else{
            msg.send(words);
        }
    });
}
```

:::note warn
コンソール上で実行するとき、APIトークンだけではうまくいかなかった
:::

`HUBOT_SLACK_RTM_CLIENT_OPTS='{"dataStore":"false","useRtmConnect":"true"}'`

これを一緒に実行すると大丈夫みたい。
HubotのRTM？ちょっと詳しいことはわからないけれど後で調べたい。
最終的に下で実行するとよい。

`env HUBOT_SLACK_TOKEN=xoxb-xxxxxxx  HUBOT_SLACK_RTM_CLIENT_OPTS='{"dataStore":"false","useRtmConnect":"true"}' bin/hubot --adapter slack
`

<h2>まとめ</h2>
実際に自分で考えて作って、ようやく全体の流れみたいなものが理解できた。作業量が増えてきてるので難しい。
次はbotの永続化とのことなので、とにかくほめるbotも引き続き改良したい。入力チェックも特にしてない状態なので。