【Linebot】ピストルズと自然な会話をできるbotを作った話【初心者向け】

#概要

少し前に毎日天気予報を通知したり、結婚記念日などの特定の日付にメッセージを送るbotを作りました。
今回はこのbotを改良し、自然な会話ができるようにしてみました。
(参考)
https://qiita.com/ko_seven/items/66cbccced520e0530cdf

会話のロジック自体は自作ではなく、A3RTのSmall Talk APIというリクルートが開発しているAPIサービスを活用します。
利用にお金はかからず、APIキーの発行だけで良いので非常に手軽です。デモ環境もあるので興味ある方はお試しあれ。
(参考)
https://a3rt.recruit-tech.co.jp/product/talkAPI/

## Herokuの環境構築とLineアカウントの用意について

以前記念日を通知してくれるbotを作った際のことを以下の記事にまとめています。
こちらを参照ください。
https://qiita.com/ko_seven/items/66cbccced520e0530cdf

## 返信する部分のロジックについて
webhookを通して返信をする部分をIndex.js
smallTalkのAPIを使う部分をsmalltalk.js
としてファイルを分ける。

それぞれのソースは以下の通り。

以下のサイトを参考にさせていただきました。
(参考)
https://qiita.com/Illly/items/350d6631495ef8aea652

```js:index.js

// -----------------------------------------------------------------------------
// モジュールのインポート
const server = require("express")();
const line = require("@line/bot-sdk"); // Messaging APIのSDKをインポート
var request = require('request');
const smallTalk = require('./smalltalk'); // smallTalk.js 後述

// -----------------------------------------------------------------------------
// パラメータ設定
const line_config = {
    channelAccessToken: process.env.LINE_ACCESS_TOKEN, // 環境変数からアクセストークンをセットしています
    channelSecret: process.env.LINE_CHANNEL_SECRET // 環境変数からChannel Secretをセットしています
};
const actoken = process.env.LINE_ACCESS_TOKEN;

// -----------------------------------------------------------------------------
// Webサーバー設定
server.listen(process.env.PORT || 3000);
// -----------------------------------------------------------------------------

// APIコールのためのクライアントインスタンスを作成
const bot = new line.Client(line_config);

// ルーター設定
server.post('/bot/webhook', line.middleware(line_config), (req, res, next) => {
    // 先行してLINE側にステータスコード200でレスポンスする。
    res.sendStatus(200);

    Promise
      .all(req.body.events.map(handleEvent))
      .then((result) => res.json(result));
});

//イベント処理関数
function handleEvent(event){
  if (event.type !== 'message' || event.message.type !== 'text') {
    return bot.replyMessage(event.replyToken, {
        type: "text",
        text: "ちょっと何をいっているかわからないです"
    });

  }else if (event.type == "message" && event.message.type == "text") {
    console.log(event.message.text);

    //返信処理
    const userText = event.message.text; // ユーザーの送ってくれたテキスト
    smallTalk(userText) //ユーザが送ったテキストを引数にいれてsmallTalkを呼ぶ
      .then((smallTalkRes) => {
        return bot.replyMessage(event.replyToken, {
            type: "text",
            text: smallTalkRes
        });
      })
      .catch((err) => {
        return bot.replyMessage(event.replyToken, {
            type: "text",
            text: 'err'
        });
      })
  }else{
    return bot.replyMessage(event.replyToken, {
        type: "text",
        text: "ちょっと何をいっているかわからないです"
    });
  }
}

```


```js:index.js
// パッケージのインポート
const request = require('request');


// Talk APIを利用するためにPOSTするURLとAPI Key
const smallTalkApiKey     = '******';
const smallTalkRequestUrl = 'https://api.a3rt.recruit-tech.co.jp/talk/v1/smalltalk';


// 以下のfunctionをモジュール化
// 引数はユーザーからのテキスト
module.exports = function(userText){

  // Talk APIが受け付けるリクエスト情報
  const smallTalkRequestOption = {
    url: smallTalkRequestUrl,
    form: {
      apikey: smallTalkApiKey,
      query: userText
    }
  };

  // Promiseを返す
  return new Promise((resolve, reject) => {
    // POST リクエストを送信する
    request.post(smallTalkRequestOption, function(err, res, body){
      if(err){
        reject(err);
      }

      // Talk APIから受け取ったstring型のレスポンスをJavaScript Objectにする
      const bodyObj = JSON.parse(body);

      // .results[0].replyはTalk APIからの返信
      if(bodyObj.results[0].reply){
        resolve(bodyObj.results[0].reply);          // 返信がある場合はそれをresolve
      } else {
        reject('Invalid response from smallTalk');  // ない場合はreject
      }
    });
  });
}

```

これだけです。超絶簡単ですね。

## 完成したもの

Lineのアイコンは個人的に好きなジョジョ5部よりピストルズを採用。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635205/1c9b6b08-c68f-4f68-fcba-7a36c42a229c.png)


ちょっと後半の会話は変な空気になってしまいましたが、まぁ自然な会話ができているようですね。
ピストルズが結構礼儀正しい子になっちゃったのは違和感ありますが、そこはご愛嬌ということで。。。

## まとめ
自然言語処理を活用したbotって作る前はすごく難しいそうなイメージを持っていましたが、いざやってみると超簡単でした。
会話を行う機械学習の部分を自作するとなるともっと大変なんでしょうが、ある程度のレベルであれば適切に外部サービスを利用するほうがコスパがよいですね。
ちなみにAPIの処理上仕方ないのですが、画像やスタンプなどでは反応できませんし、英語でも会話という会話はできません。
そのうちマルチ言語対応したモデルも広く普及するのかしら。