今さら聞けない、雑談LINE BOTの作り方（２）「おじさんだって、ボットをつくってもいいじゃないか」

## ドコモ雑談会話API でつくった関西女子高生風LINE Bot に、「会話継続モード」と「しりとり機能」を追加する


[前回、インフラSEだった私が、偶然LINE BOTのことを知り、20年ぶりにプログラミングをすることになった経緯をお伝えした。](http://qiita.com/ponjopi/items/913ae7f5b1117cee4860)

Google Apps Script環境を利用することで、言語習得やPaaS環境の構築などに手間がかからず、ダイレクトに APIをテストできる。私もそうだが開発言語・スクリプト言語にあまり詳しくない方が、最短最速で LINE Botをつくって遊びたい場合には便利だ。

大晦日と正月を費やして開発した関西女子高生風・雑談ボット「りの」は、私の趣味で様々な機能を増やしながら、現在も活発に（？）活動中である（LINE ID: @lmj1644w または下記QRコード）。
![rino.jpg](https://qiita-image-store.s3.amazonaws.com/0/157507/569f113c-9ebf-8123-012a-fd7ac47503df.jpeg)


マイクロソフトをはじめとする各社APIを使って実装したそれら機能については、機会とご要望があれば、また改めてご紹介しよう。

まずは前回の記事でお約束しながら、書きそびれた部分の説明をしておきたい。具体的には以下の機能だ。

* Templateメッセージなど、LINE Messaging APIが提供する機能
* ドコモ雑談会話APIで「会話を継続」させる機能
* ドコモ雑談会話APIで「しりとり」をする機能


# LINE Messaging APIは豊富なメッセージ種別を扱える

LINE Messaging APIについては、API Referenceが用意されているので、それを読めば基本的な仕様は理解できる。また、SDKも様々な言語向けに提供されているので、あなたが得意な言語で書かれたサンプルプログラムを見れば、すぐにコードを書くこともできるだろう。

LINE API Reference https://devdocs.line.me/ja/#messaging-api

まず、チャット相手のユーザが送ってくるメッセージを受け取るWebhook Event Objectの構造を見てみる。LINEユーザ側からのメッセージは、以下のようなJSON形式で送られてくる。

~~~ javascript
{
  "events": [
    {
      "replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA", // 例
      "type": "message",
      "timestamp": 1462629479859,
      "source": {
        "type": "user",
        "userId": "U206d25c2ea6bd87c17655609a1c37cb8" // 会話している相手
      },
      "message": {
        "id": "325708", // 受け取ったメッセージ固有の識別子
        "type": "text", // 受け取ったメッセージのデータ型
        "text": "Hello, world" // 受け取ったメッセージの内容
      }
    },
    {
      "replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
      "type": "follow", // 相手が友達登録をした
      "timestamp": 1462629479859,
      "source": {
        "type": "user",
        "userId": "U206d25c2ea6bd87c17655609a1c37cb8"
      }
    }
  ]
}
~~~

events[ ].typeには、テキスト・画像・スタンプといったMessageのほか、友達登録時のFollowやテンプレートメッセージに対する返信であるPostbackなど、いくつかのデータ型がある。

通常の会話だけをカバーするなら、Messageイベントをつかまえればいいが、たとえば友達登録時に自動返信メッセージを送ろうとしたり、ボットがグループに参加した場合にあいさつを送りたいなら、それらに対応するFollowやJoinといったイベントも識別する必要がある。

LINE BOTの場合、もっとも多く利用されるのは間違いなくReply Message APIだと思われるが、ここでさきほど紹介したWebhook Event Objectに含まれるreplyTokenが必要になる。

~~~ javascript
// LINEにメッセージを投げる関数（Reply Message）
function SendMessageToLINE(reply_token, replay_text){

var CHANNEL_ACCESS_TOKEN = 'xxxxxxx';  //ここにアクセストークン
    
  UrlFetchApp.fetch(line_endpoint, {
    'headers': {
      'Content-Type': 'application/json; charset=UTF-8',
      'Authorization': 'Bearer ' + CHANNEL_ACCESS_TOKEN,
    },
    'method': 'post',
    'payload': JSON.stringify({
      'replyToken': reply_token,
      'messages': [
        {'type': 'text', 'text': replay_text},
      ],
    }),
  });  
  return ContentService.createTextOutput(JSON.stringify({'content': 'post ok'})).setMimeType(ContentService.MimeType.JSON);  
}
~~~

上の例ではLINEへのメッセージ送付処理を関数として切り出しているが、ただメッセージ送信をテストするだけなら、UrlFetchApp.fetch() をメインのdoPost()関数にベタ打ちでも構わない。

いずれにしても、LINE へのメッセージ送付はこんなに単純な仕組みだということだ。上記は、テキストメッセージを送る例だが、このほか画像・動画・音声・位置情報・スタンプなど、実に様々なデータを送ることができる。 データ種別の指定は、上記のJSON型データのPayload. messages.typeの部分でおこなう。

LINE Messaging APIを使った場合の、LINEの特徴ともいえるスタンプ送受信処理については、機会があればまた書いてみたいが、今回はユーザをシナリオどおりに誘導したい場合などに便利なTemplateを利用する例を紹介しておく。

Templateメッセージは、テキストや画像など通常のメッセージと異なり、選択式ボタンがついており、相手からの回答を選択肢で受け取ることを目的としている。たとえば、「はい」「いいえ」で次の処理を決めたり、相手の回答でその後の処理を振り分けたりするときに便利だ。


LINE がMessaging API公開時にリリースした動画で紹介されているような、オススメのお店を紹介したりするような業務ボットをつくる場合は、このTemplateメッセージがかなり活躍するだろうと予測できる。

* 参考動画 [【公式】BOT & THE NEW WORLD by LINE](https://youtu.be/C7ZuzJe24y4)


私が作成したLINE Bot「りの」の例で恐縮だが、LINE友達登録時にTemplateメッセージのひとつ Buttons Templateを利用している（将来的に、友達登録時のこのメニュー表示は中止するかもしれないが、その場合でも「メニュー」と入力すれば表示するようになっている）。

![buttons_Template.png](https://qiita-image-store.s3.amazonaws.com/0/157507/8723b047-421c-8b99-33a2-2e4d8cba697d.png)


実際のコーディングでは、以下のようにテンプレートメッセージ送信用の関数を作成している。

~~~ javascript
// buttonテンプレートメッセージを送る関数
function SendButtonTemplateToLINE(reply_token, MessageTitle, MessageBody){

  UrlFetchApp.fetch(line_endpoint, {
    'headers': {
      'Content-Type': 'application/json; charset=UTF-8',
      'Authorization': 'Bearer ' + CHANNEL_ACCESS_TOKEN,
    },
    'method': 'post',
    'payload': JSON.stringify({
      'replyToken': reply_token,
      'messages': [ {
        "type": "template",
        "altText": "this is a buttons template",
        "template": {
        "type": "buttons",
        "thumbnailImageUrl": " https://xxxxx/xxx.jpg ",
        "title": MessageTitle,
        "text": MessageBody,
        "actions": [
        {
            "type": "postback",
            "label": "しりとり",
            "data": "action=shiritori"
          },
        {
            "type": "postback",
            "label": "ふつーに雑談",
            "data": "action=smallTalk"
          }          
      ]
  }
}],
    }),
  });  
  return ContentService.createTextOutput(JSON.stringify({'content': 'post ok'})).setMimeType(ContentService.MimeType.JSON);  
}

~~~ 

JSONデータ型に変換したメッセージを、UrlFetchApp.fetchメソッドでLINEエンドポイントに送るのは、Textメッセージの場合と変わらないが、messages[ ].actions[ ].typeとして「postback」を指定することで、相手からの回答に応じて後続処理を連携させることが容易になる。シナリオに沿った会話を展開することができるようになるわけである。

以下は「postbasck」を受け取って、処理を振り分ける例である。

~~~ javascript
case 'postback':  // テンプレートメッセージからの返信の場合
// 回答メッセージを取得
switch(JSON.parse(e.postData.contents).events[0].postback.data){
                
// 回答により、処理を分岐する
case 'action=shiritori':
SendMessageToLINE(reply_token, "じゃあ、はじめるで。「しりとり」ってメッセージしてや。やめたくなったら「やめる」ってメッセージして");    //テスト用エコー  
                    
break;
~~~
                          

上手に「postback」を拾う処理をコーディングできれば、ユーザを一連のシナリオに載せて誘導し、ミニゲームや診断クイズ、アンケートなどをLINE上でおこなうことができるだろう。


2.	ドコモ雑談会話APIで「会話を継続」させる・「しりとり」をする
ドコモのAPIを利用すれば、カンタンに雑談Botができあがる。が… パラメータの設定で若干の落とし穴があるのと、AIとの会話に人間味を出す小技があるので、紹介しておく。

~~~ javascript
// ドコモ雑談APIで会話を生成・取得する        
function getDialogueMessage(userId, mes) {
  var dialogueUrl = 'https: //api.apigw.smt.docomo.ne.jp/ dialogue/v1/dialogue?APIKEY=’xxxxx';
  
  var contextId = 'context' + userId;
  var dialogue_options = {
    't': 20,   // 関西女子高生風の設定
    'utt': mes // ここにユーザからのメッセージを設定
  };
  var props = PropertiesService.getScriptProperties();
  var context = props.getProperty(contextId);
  var dialoguemode = props.getProperty("DIALOGUE_MODE");
  var username = props.getProperty("USER_NAME");

  if (context) {dialogue_options.context = context;};
  if (dialoguemode){ dialogue_options.mode = dialoguemode;};
  if (username){ dialogue_options.nickname = username;};

  var options = {
    'method': 'POST',
    'contentType': 'text/json',
    'payload': JSON.stringify(dialogue_options)
  };
  var response = UrlFetchApp.fetch(dialogueUrl, options);
  var content = JSON.parse(response.getContentText());
  props.setProperty(contextId, content.context);
  props.setProperty("DIALOGUE_MODE", content.mode);
  props.setProperty("USER_NAME", content.nickname);
  return content.utt;
}
~~~


まず「会話を継続」させるコツから説明する。

上のコードでは「dialogue_options」に、ユーザから受け取ったメッセージや関西女子高生風会話モード設定などを詰め込んで、ドコモAPIに渡している。

ここで「dialogue_options.context」というパラメータに各ユーザごとの識別子を設定しておくと、ドコモAIは「同一人物に対する継続した会話」として認識する。この例では、'context' という文字列+ LINEのuserIdを組み合わせたものを送っている。

ここに何も設定しないで投げると、毎回、バラバラで関連性のない回答をよこすので、会話が成り立たない。ドコモ雑談会話APIを利用して雑談Botを制作される方は、気を付けた方がいいだろう。

またAIに「しりとり」をさせるには「dialogue_options.mode」に「srtr」という値を設定してドコモAPIに送る必要がある。

【参考】docomo Developer support 雑談対話API
https://dev.smt.docomo.ne.jp/?p=docs.api.page&api_name=dialogue&p_name=api_1#tag01

ただしドコモAPIは「しりとり」というキーワードを受け取ると、メッセージと一緒に返信してくるパラメータに「srtr」という値を設定してくれるので、それをどこかに保管しておいて、次回ユーザからの「しりとり」回答を送るときに読みだして、パラメータに設定すればよい。

データベースを使うなどやり方はいろいろと考えられると思うが、「りの」の場合はGoogle Apps Scriptで記述しているので、setPropertyというメソッドを使って、key-valueストアに保管している（以下の部分）。

setProperty("DIALOGUE_MODE", content.mode);

で、ドコモAPIエンドポイントにデータを送る前にこれを毎回読みだして、パラメータに設定している。これで「しりとり」が継続するわけだ。

最後に小技をひとつ。ドコモに引き渡すデータの中に「nickname」というパラメータがある。これを設定しておくと、会話の中でときどき「xxxさん」とユーザの名前を呼んでくれるらしい。どのタイミングかはドコモAI任せになる。

そのnicknameはどこから持ってくるのか、という疑問が湧いてくるかもしれないが、それはアイデア次第だと思う。ユーザとの会話の中で拾うとか、LINEのプロフィールから表示名を持ってくるなどの方法が思いつく（LINEのGet Profile APIでユーザの表示名を取得できる）。

私も自分で実際に「りの」とチャットして試してみたが、いくら会話しても全然、nicknameを呼んでくれないこともあれば、短い会話の中でもしつこいほどnicknameを繰り返すこともあった。

もしあなたがnicknameパラメータを使うときは、考えた方がいいかもしれない。あまりしつこいと、せっかく友達になってくれたユーザにブロックされてしまう可能性もある。


以上、ここまで前回、お約束した機能の紹介をしてみた。その後もいろんなAPIを試しながら、「りの」に付け加えているので、また機会をみてご紹介したいと思っている。

では、あなたがご自身のLINE BOTと楽しい時間を過ごされることを心からお祈りしている。