LINE Messaging APIとGoogleAppsScriptとCOTOHA APIで「会話するLINEチャットボット」を作ってみました。

記事構成 ①オウム返しボットを作る ②COTOHA APIでテキストをタイプ別に分けてみる ③COTOHA APIでテキストから感情を判別してみる ④文タイプ別に返信するチャットボットを作る←イマココ

完成画像↓ S__2670611.jpg S__2687110.jpg

文タイプ別の返信をスプレットシートに以降する

今回はシンプルに文タイプのみで返信を考えてみようと思います。

まずはコメントアウトでメソッドを切り替えます。

  // main.gs
  //COTOHA APIでメッセージの文タイプを判定
  const message_type = getSentenceType(AccessToken, message);
  // const message_type = getSentimentType(AccessToken, message);
  // main.gs
  // 返信するメッセージを選択する処理
  const reply_messages = typeChoiceReply(message_type.modality, message_type.dialog_act);
  // const reply_messages = emotionChoiceReply(message_type.sentiment, message_type.emotion);

として、「感情判別」から「文タイプ判別」にメソッドを変更します。

ここで一度デプロイして、チャットの返信が「文タイプ判別」になっているか確認しても良いでしょう。 68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f3236343838342f63663638313633662d323065392d633732642d646633342d6430.jpg

返信内容をスプレットシートに移行する

このままハードコーディングなままなのも微妙ですし、せっかくGASを使っているのでスプレットシートに返信内容を保管してみましょう。 ↓こんな感じでスプレットシートから値を取得します。

// (例
function myFunction() {
  //1. 現在のスプレッドシートを取得
  var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
  //2. 現在のシートを取得
  var sheet = spreadsheet.getActiveSheet();
  //3. 指定するセルの範囲(A1)を取得
  var range = sheet.getRange("A1");
  //4. 値を取得する
  var value = range.getValue();
 
  //ログに出力
  Logger.log(value);
}

スプレットシートにこんな感じで値を入れてみました。(コピペできないので気合で入力していきます) セルに返信用のメッセージを書き込みます。 配列にしてランダムに取り出したいので「,(カンマ)」でメッセージを区切っています。 68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f3236343838342f31396436353864312d363537652d353564372d656436612d6239.jpg

次に、type.gsの最後尾に関数を追加します。


// type.gs

// 文タイプ別にスプレットシートから返信候補を取得しランダムに返す
function getReply(area) {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sheet = ss.getActiveSheet();
  const reply_messages = sheet.getRange(area).getValue();
  const reply_array = reply_messages.split(',');
  return reply_array[Math.floor(Math.random() * reply_array.length)]
}

そして、文タイプごとに関数を実行するため、typeChoiceReply関数を書き換えます。

// type.gs

//文タイプから返信メッセージを選択する関数
function typeChoiceReply(modality, dialog_act) {
  //挨拶タイプ(greeting)のメッセージを受信した場合の返信
  if (dialog_act == 'greeting') {
    return getReply('B1');

  //挨拶(別れ)(goodbye)のメッセージを受信した場合の返信
  } else if (dialog_act == 'goodbye') {
    return getReply('B2');
 
 //情報提供タイプ(information-providing)のメッセージを受信した場合の返信
  } else if (dialog_act == 'information-providing') {
    return getReply('B3');
 
 //フィードバック/相槌(feedback)のメッセージを受信した場合の返信
  } else if (dialog_act == 'feedback') {
    return getReply('B4');
 
 //情報獲得タイプ(information-seeking)のメッセージを受信した場合の返信
  } else if (dialog_act == 'information-seeking') {
    return getReply('B5');
 
 //同意タイプ(agreement)のメッセージを受信した場合の返信
  } else if (dialog_act == 'agreement') {
    return getReply('B6');
 
 //理解確認タイプ(feedbackElicitation)のメッセージを受信した場合の返信
  } else if (dialog_act == 'feedbackElicitation') {
    return getReply('B7');
 
 //約束タイプ(commissive)のメッセージを受信した場合の返信
  } else if (dialog_act == 'commissive') {
    return getReply('B8');
 
 //受領タイプ(acceptOffer)のメッセージを受信した場合の返信
  } else if (dialog_act == 'acceptOffer') {
    return getReply('B9');
 
 //言い直しタイプ(selfCorrection)のメッセージを受信した場合の返信
  } else if (dialog_act == 'selfCorrection') {
    return getReply('B10');
 
 //感謝タイプ(thanking)のメッセージを受信した場合の返信
  } else if (dialog_act == 'thanking') {
    return getReply('B11');
 
 //謝罪タイプ(apology)のメッセージを受信した場合の返信
  } else if (dialog_act == 'apology') {
    return getReply('B12');
 
 //時間埋めタイプ(stalling)のメッセージを受信した場合の返信
  } else if (dialog_act == 'stalling') {
    return getReply('B13');
 
 //指示タイプ(directive)のメッセージを受信した場合の返信
  } else if (dialog_act == 'directive') {
    return getReply('B14');
 
 //否認タイプ(declineOffer)のメッセージを受信した場合の返信
  } else if (dialog_act == 'declineOffer') {
    return getReply('B15');
 
 //ターン譲渡タイプ(turnAssign)のメッセージを受信した場合の返信
  } else if (dialog_act == 'turnAssign') {
    return getReply('B16');
 
 //中断タイプ(pausing)のメッセージを受信した場合の返信
  } else if (dialog_act == 'pausing') {
    return getReply('B17');
 
 //謝罪受領タイプ(acceptApology)のメッセージを受信した場合の返信
  } else if (dialog_act == 'acceptApology') {
    return getReply('B18');
 
 //感謝受領タイプ(acceptThanking)のメッセージを受信した場合の返信
  } else if (dialog_act == 'acceptThanking') {
    return getReply('B19');
 
 //それ以外の文タイプのメッセージを受信した場合の返信
  } else {
    return getReply('B20');
  }
}

解説

type.gsには「getSentenceType関数」「typeChoiceReply関数」「getReply関数」が存在します。

typeChoiceReply関数にCOTOHA APIから返ってきた判定を引数としてわたし、分岐します。

getReply関数で文タイプ別にスプレットシートから取得した「メッセージ候補」を配列に変換し、ランダムで抽出してリターンしています。

↓type.gs全文

// type.gs
//受信したメッセージをCOTOHA APIに送り、文タイプの判定の結果を取得する関数
function getSentenceType(AccessToken, message){
  const url = "Developer API Base URL/nlp/v1/sentence_type"; // ←「Developer API Base URL」からエンドポイントURLを作成

  const headers = {
    "Authorization": "Bearer " + AccessToken,
    "Content-Type": "application/json; charset=UTF-8",
  }

  const data = {
    "sentence": message,
    "type": "kuzure"
  }

  const options = {
    "method": "post",
    "payload": JSON.stringify(data),
    "headers": headers
  }

  const responce = UrlFetchApp.fetch(url, options);
  const json = JSON.parse(responce.getContentText());
  const dialog_act = json["result"]["dialog_act"];
  const modality = json["result"]["modality"];
  const type_data = {
    modality: modality,
    dialog_act: dialog_act.toString()
  };

  return type_data;
}

//文タイプから返信メッセージを選択する関数
function typeChoiceReply(modality, dialog_act) {
  //挨拶タイプ(greeting)のメッセージを受信した場合の返信
  if (dialog_act == 'greeting') {
    return getReply('B1');

  //挨拶(別れ)(goodbye)のメッセージを受信した場合の返信
  } else if (dialog_act == 'goodbye') {
    return getReply('B2');
 
 //情報提供タイプ(information-providing)のメッセージを受信した場合の返信
  } else if (dialog_act == 'information-providing') {
    return getReply('B3');
 
 //フィードバック/相槌(feedback)のメッセージを受信した場合の返信
  } else if (dialog_act == 'feedback') {
    return getReply('B4');
 
 //情報獲得タイプ(information-seeking)のメッセージを受信した場合の返信
  } else if (dialog_act == 'information-seeking') {
    return getReply('B5');
 
 //同意タイプ(agreement)のメッセージを受信した場合の返信
  } else if (dialog_act == 'agreement') {
    return getReply('B6');
 
 //理解確認タイプ(feedbackElicitation)のメッセージを受信した場合の返信
  } else if (dialog_act == 'feedbackElicitation') {
    return getReply('B7');
 
 //約束タイプ(commissive)のメッセージを受信した場合の返信
  } else if (dialog_act == 'commissive') {
    return getReply('B8');
 
 //受領タイプ(acceptOffer)のメッセージを受信した場合の返信
  } else if (dialog_act == 'acceptOffer') {
    return getReply('B9');
 
 //言い直しタイプ(selfCorrection)のメッセージを受信した場合の返信
  } else if (dialog_act == 'selfCorrection') {
    return getReply('B10');
 
 //感謝タイプ(thanking)のメッセージを受信した場合の返信
  } else if (dialog_act == 'thanking') {
    return getReply('B11');
 
 //謝罪タイプ(apology)のメッセージを受信した場合の返信
  } else if (dialog_act == 'apology') {
    return getReply('B12');
 
 //時間埋めタイプ(stalling)のメッセージを受信した場合の返信
  } else if (dialog_act == 'stalling') {
    return getReply('B13');
 
 //指示タイプ(directive)のメッセージを受信した場合の返信
  } else if (dialog_act == 'directive') {
    return getReply('B14');
 
 //否認タイプ(declineOffer)のメッセージを受信した場合の返信
  } else if (dialog_act == 'declineOffer') {
    return getReply('B15');
 
 //ターン譲渡タイプ(turnAssign)のメッセージを受信した場合の返信
  } else if (dialog_act == 'turnAssign') {
    return getReply('B16');
 
 //中断タイプ(pausing)のメッセージを受信した場合の返信
  } else if (dialog_act == 'pausing') {
    return getReply('B17');
 
 //謝罪受領タイプ(acceptApology)のメッセージを受信した場合の返信
  } else if (dialog_act == 'acceptApology') {
    return getReply('B18');
 
 //感謝受領タイプ(acceptThanking)のメッセージを受信した場合の返信
  } else if (dialog_act == 'acceptThanking') {
    return getReply('B19');
 
 //それ以外の文タイプのメッセージを受信した場合の返信
  } else {
    return getReply('B20');
  }
}


// 文タイプ別にスプレットシートから返信候補を取得しランダムに返す
function getReply(area) {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sheet = ss.getActiveSheet();
  const reply_messages = sheet.getRange(area).getValue();
  const reply_array = reply_messages.split(',');
  return reply_array[Math.floor(Math.random() * reply_array.length)]
}

デプロイしておしゃべりしてみる

68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f3236343838342f30306339373737302d653639642d383265642d613036662d3738.jpg

どうでしょうか? さらに「情報提供タイプ」「情報獲得タイプ」などを感情判別など工夫したらちょっと面白いチャットボットができるかもしれません。 2年ぶりくらいに触ったGASの学習のため、記事にしてみました。 お粗末さまでしたm(_ _)m

記事構成 ①オウム返しボットを作る ②COTOHA APIでテキストをタイプ別に分けてみる ③COTOHA APIでテキストから感情を判別してみる ④文タイプ別に返信するチャットボットを作る←イマココ