【GAS/LINE WORKS】一問一答のBotを作る(MessageAPI)

GAS(Google Application Script)に戻る

目次

概要

いくつかユーザーに聞きたいものがある時は、

ソースコード

const HeaderString = {
  messageId: "MessageID",
  userId: "UserId",
  messageType: "MessageType",
  dateTime: "StartDateTime",
  question1: "Question1",
  question2: "Question2",
  question3: "Question3"
}

const SheetNames = {
  main: "Tamura",
  shopList: "shop_list"
}

const postBack = "ButtonTemplate_Shop_List";
const configureData = {
  clientiD: [クライアントID],
  clientSecret: [クライアントシークレット],
  serviceAccount: [サービスアカウント],
  privateKey: [privateKey],
  botId: [botId],
  channelId: [チャンネルID]
}

const getAccessToken = () => {
  const time = Date.now();
  const header = Utilities.base64Encode(JSON.stringify({ 'alg': 'RS256', 'typ': 'JWT' }));
  const claimSet = Utilities.base64Encode(JSON.stringify({
    'iss': configureData.clientiD,
    'sub': configureData.serviceAccount,
    'iat': Math.floor(time / 1000),
    'exp': Math.floor(time / 1000 + 3600)
  }));

  const privateKey = configureData.privateKey;
  
  const signature = Utilities.base64Encode(
    Utilities.computeRsaSha256Signature(
      `${header}.${claimSet}`,
      privateKey
      )
    );
  const jwt = `${header}.${claimSet}.${signature}`;

  const endpoint = 'https://auth.worksmobile.com/oauth2/v2.0/token';
  const options = {
    method: 'post',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    payload: {
      'assertion': jwt,
      'grant_type': 'urn:ietf:params:oauth:grant-type:jwt-bearer',
      'client_id': configureData.clientiD,
      'client_secret': configureData.clientSecret,
      'scope': 'bot,bot.read'
    }
  }
  try {
    const res = JSON.parse(UrlFetchApp.fetch(endpoint, options));
    Logger.log(res)
    return res;
  } catch(e) {
    // 例外エラー処理
    Logger.log('Error(token):')
    Logger.log(e)
    return "";
  }
}
// POSTリクエストに対する処理
function doPost(e) {
  const ss = SpreadsheetApp.getActive()
  const sheet = ss.getSheetByName(SheetNames.main);
  var lastRow = sheet.getLastRow()
  var lastColumn = sheet.getRange(lastRow, sheet.getMaxColumns()).getNextDataCell(SpreadsheetApp.Direction.PREVIOUS).getColumn();

  const headers = sheet.getRange(1,1,1,sheet.getLastColumn()).getValues()[0];

  if (lastColumn >= headers.length) {
    lastRow += 1
    lastColumn = 1
  }

  // JSONをパース
  if (e == null || e.postData == null || e.postData.contents == null) {
    console.log("error");
    return;
  }
  
  const requestJSON = e.postData.contents;
  const requestObj = JSON.parse(requestJSON);

  switch(headers[lastColumn]) {
    case HeaderString.messageId: 
    case HeaderString.userId:
    case HeaderString.messageType:
    case HeaderString.dateTime:
      var values = [];
      for (i in headers){
        var val = "";
        switch(headers[i]) {
          case HeaderString.messageId:
            val = lastRow - 1;
            break;
          case HeaderString.userId:
            // val = requestObj.source[header];
            val = "373737";
            break;
          case HeaderString.messageType:
            val = requestObj.type;
            break;
          case HeaderString.dateTime:
            val = new Date();
            break;
          default:
            break;
        }
        values.push(val);
      }
      sheet.appendRow(values);
      sendMessage("所属を教えてください");
      break;
    case HeaderString.question1:
      sheet.getRange(lastRow, lastColumn + 1).setValue(requestObj.content.text);
      sendMessage("あなたの名前を教えてください。");
      break;
    case HeaderString.question2:
      sheet.getRange(lastRow, lastColumn + 1).setValue(requestObj.content.text);
      sendMessage("どのようなことでお困りでしょうか?");
      break;
    case HeaderString.question3:
      sheet.getRange(lastRow, lastColumn + 1).setValue(requestObj.content.text);
      sheet.getRange(lastRow, 8).setValue("質問完了");
      sendMessage("どうもありがとうございました。");
      return;
  }
}

function sendMessage(message) {
  const accessToken = getAccessToken().access_token;
  const url = `https://www.worksapis.com/v1.0/bots/${configureData.botId}/channels/${configureData.channelId}/messages`
  if (typeof message == 'string') {
    const options = {
      method: 'post',
      headers: {
        'Authorization': `Bearer ${accessToken}`,
        'Content-Type': 'application/json'
      },
      payload: JSON.stringify({
        'content': {
          'type': 'text',
          "text": message
        }
      })
    }

    try {
      const response = UrlFetchApp.fetch(url, options);
      Logger.log(response)
    } catch(e) {
      // 例外エラー処理
      Logger.log('Error(messages):')
      Logger.log(e)
    }
  }
}

デモ動画

スプレッドシートの結果

詳細

結論から言うと、一問一答の形式は

セルの入力状況を見て、その入力状況に応じて送信するメッセージを変える

ということをしている。
コードを見ていこう。

データがどこまで入力されているか検知する

const ss = SpreadsheetApp.getActive()
const sheet = ss.getSheetByName(SheetNames.main);
var lastRow = sheet.getLastRow()
var lastColumn = sheet.getRange(lastRow, sheet.getMaxColumns()).getNextDataCell(SpreadsheetApp.Direction.PREVIOUS).getColumn();

まず、上記のコードは、入力されている最後の行「lastRow」を取得し、
その行の最後に書かれている列のセル「lastColumn」を取得する。
このlastColumnに応じてメッセージを変えている。

どこまで入力されているかヘッダーで判別する。

const headers = sheet.getRange(1,1,1,sheet.getLastColumn()).getValues()[0];

まず、上記で一行目、つまり項目が並んでる行で、lastColumnの文字列を取得することで
次に欲しい情報がなんなのかを知る。
例として、最初の場合、「StartDateTime」まで記入が完了されているはずなので、次の項目は「Question1」になる。

項目に応じて質問の内容を変える

switch(headers[lastColumn]) {
  case HeaderString.messageId: 
  case HeaderString.userId:
  case HeaderString.messageType:
  case HeaderString.dateTime:
    var values = [];
    for (i in headers){
      var val = "";
      switch(headers[i]) {
        case HeaderString.messageId:
          val = lastRow - 1;
          break;
        case HeaderString.userId:
          val = "373737";
          break;
        case HeaderString.messageType:
          val = requestObj.type;
          break;
        case HeaderString.dateTime:
          val = new Date();
          break;
        default:
          break;
      }
      values.push(val);
    }
    sheet.appendRow(values);
    sendMessage("所属を教えてください");
    break;
  case HeaderString.question1:
    sheet.getRange(lastRow, lastColumn + 1).setValue(requestObj.content.text);
    sendMessage("あなたの名前を教えてください。");
    break;
  case HeaderString.question2:
    sheet.getRange(lastRow, lastColumn + 1).setValue(requestObj.content.text);
    sendMessage("どのようなことでお困りでしょうか?");
    break;
  case HeaderString.question3:
    sheet.getRange(lastRow, lastColumn + 1).setValue(requestObj.content.text);
    sheet.getRange(lastRow, 8).setValue("質問完了");
    sendMessage("どうもありがとうございました。");
    return;
}

こうすることで、どこまで項目の記入が完了しているかで質問の内容を変えることができる。

sheet.getRange(lastRow, lastColumn + 1).setValue(requestObj.content.text);

そして、質問に対して回答、つまりメッセージを送ったら、上記のコードで指定された行の項目の列にメッセージの文字列を記入する。
そして、それが終わったらsendMessageメソッドで質問のメッセージを送る。
これの繰り返しだ。

最後は、HeaderString.question3の記入が完了したら、「どうもありがとうございました。」のメッセージを送ろう。

メッセージの送り方

メッセージを送信するにはLINE WORKSから提供されているAPIを呼び出す必要がある。
今回使用したのはトークルーム指定したやり方

実装箇所は以下

function sendMessage(message) {
  const accessToken = getAccessToken().access_token;
  const url = `https://www.worksapis.com/v1.0/bots/${configureData.botId}/channels/${configureData.channelId}/messages`
  if (typeof message == 'string') {
    const options = {
      method: 'post',
      headers: {
        'Authorization': `Bearer ${accessToken}`,
        'Content-Type': 'application/json'
      },
      payload: JSON.stringify({
        'content': {
          'type': 'text',
          "text": message
        }
      })
    }

    try {
      const response = UrlFetchApp.fetch(url, options);
      Logger.log(response)
    } catch(e) {
      // 例外エラー処理
      Logger.log('Error(messages):')
      Logger.log(e)
    }
  }
}

まず、アクセストークンを取得する。
そして、URLの形式はこちらのように書かれている。

botIDはボットID、channnelIDは以下からわかる。
右上の3点リーダーをクリックすると、チャンネルIDというのが表示される。

method: 'post',

また、「POST」と指定されているので、上記のようにmethodは「post」としている。

ヘッダーはこちらのように定義されている。

ともに「required」と記載されている。

headers: {
  'Authorization': `Bearer ${accessToken}`,
  'Content-Type': 'application/json'
},

headersのパラメータに上記のように設定している。

テキストのメッセージを送る場合はこちらこちらを参照する。

payload: JSON.stringify({
  'content': {
    'type': 'text',
    "text": message
  }
}

上記のように、テキストメッセージなので「type」に「text」、「text」に送りたいメッセージの文字列を格納する。

const response = UrlFetchApp.fetch(url, options);

最後は上記のコードでAPIを呼び出す処理を行うことでメッセージを送信している。

参考ページ

GAS(Google Application Script)に戻る