LoginSignup
7
2

More than 1 year has passed since last update.

GASとMessaging APIで自分専用辞書を作る

Posted at

はじめに

語彙力を増やすために、自分用の辞書を作ろうとしたが、せっかくなのでGASとLINEのMessaging API、どこからでも辞書に登録できるようにする。

前提

javascriptの基本的な文法を理解していること。

内容

完成物のイメージ

LINEで単語と説明を送信すると、それをスプレッドシートの最終行に登録してくれる。単語のみを入れると、単語の説明を返してくれる。
説明に改行を入れたいので、単語と説明は、改行二回で区切る。以下のような感じ。

単語

説明だよ。
これだと単語=説明みたいになってややこしいね。

また、スプレッドシ-トには、1列目に単語、2列目に説明を記録する。

単語を探す関数、単語を追加する関数、メッセージを送信する関数、LINEから受け取った情報を処理して、単語を探すのか、追加するのかを判別する関数を作ればよいだろう。関数をどの程度細かく分ければよいかが、まだあまり分かっていないので、是非コメントで教えてほしい。

作業

前準備

はじめに、Google ドライブを開いて、新規からGoogle スプレッドシートを作成する。拡張機能からApps Scriptを選んで、GASのエディタを開く。プログラムはここに書いていく。

次に、Messaging APIの準備を行う。ここに行って、今すぐ始めようと書かれたボタンを押す。流れに沿って必要事項の入力をしていき、チャンネルを作成する。

グローバルで行うこと

いくつかの関数でスプレッドシートのデータを扱うので、グローバルで取得しておく。

//gasと関連付いたスプレッドシートを取得する。
let spreadSheet=SpreadsheetApp.getActiveSpreadsheet();

//スプレッドシートの開いているシートを取得する。
let sheet=spreadSheet.getActiveSheet();

//シートで、データが入っているセルの範囲を取得する。
let range=sheet.getDataRange();

//範囲のセルの情報を2次配列にして取得。
let values=range.getValues();

単語を探す関数

最初から順番に値を見ていき、単語と合うものがあればその場所を返す。無ければ-1を返す。

function searchWord(word){
  for(let i=0;i<values.length;i++){
    if(values[i][0]==word)
      return i;
  }
  return -1;
}

単語を追加する関数

単語は、既に存在する場合は上書き、しない場合は最終行に追加することにする。
最終行を取得するには、getLastRowを使えばよいそうだが(参考)、今回は先にvaluesを取得しているので、その長さを使って最終行を取得する。

function addDictionary(word,description){
  let wordIndex=searchWord(word);
  if(wordIndex==-1){
    let lastRow=values.length+1;
    sheet.getRange(lastRow,1).setValue(word);
    sheet.getRange(lastRow,2).setValue(description);
  }else{
    sheet.getRange(wordIndex+1,2).setValue(description);
  }
}

getRangeでは、セルの位置が(1,1)から始まっていることに注意する。

メッセージを送信する関数

Messaging APIを使って、ユーザーにメッセージを送信する。

function sendMessage(messageText,userId){
  let url="https://api.line.me/v2/bot/message/push";
  let token="チャンネルのアクセストークン"

  let message={
    "type":"text",
    "text":messageText
  };

  let params={
    "headers":{
      "Content-Type":"application/json",
      "Authorization":"Bearer "+token
    },
    "method":"post",
    "payload":JSON.stringify({
      "to":userId,
      "messages":[message]
    })
  };

  UrlFetchApp.fetch(url,params);
}

チャンネルのトークンは、作ったチャンネルのMessaging API設定チャネルアクセストークンと書かれたところから発行する。表示された文字列がトークンとなるので、それをコピペして貼り付けよう。

メッセージを受け取った時の処理

プログラムを書く前に、いくつかやることがある。

はじめに、Messaging API設定から応答メッセージを編集する。
応答モードをBOTに、応答メッセージをオフに、Webhookをオンにする。応答モードをオンにすると、BOTへメッセージを送った時に、自動で返信してしまう。WebhookはLINEでメッセージ等を受け取った時、それをGASへ送信するのに必要となる。

次に、LINEから送信された情報を受け取るために、プロジェクトを公開したい。そのためには、デプロイから新しいデプロイを作成する。デプロイタイプはウェブアプリに、アクセスできるユーザーを全員にして(LINEもアクセスできるようにするため)、デプロイする。そして、ウェブアプリのURLをコピーしておく。

次に、Messaging API設定Webhook 設定で、さっきコピーしたURLを貼り付け、Webhookの利用をオンにする。

ここからやっとプログラムを書いていく。

function doPost(e){
  let userId="自分のユーザーid";
  let contents=JSON.parse(e.postData.contents);
  let text=contents.events[0].message.text;

  //送ってきたのが自分でなければ、終了する。
  if(userId!=contents.events[0].source.userId)
    return;

  //改行二回で分割してる。もし、この配列の長さが1ならば検索、2ならば登録とわかる。
  let textArr=text.split("\n\n");
  let sendText;
  if(textArr.length==1){
    let index=searchWord(textArr[0]);
    if(index==-1){
      sendText="単語が見つかりませんでした。"
    }else{
      sendText=sheet.getRange(index+1,2).getValue();
    }
  }else{
    addDictionary(textArr[0],textArr[1]);
    sendText="追加しました。"
  }
  sendMessage(sendText,userId);
}

doPostは、LINEからメッセージを受け取った時に勝手に実行される。

メッセージを送ってきた相手が自分の時のみ、動かしたいので、ユーザーidを照合している。

自身のユーザーidはチャネル基本設定あなたのユーザーIDから見られる(参考)。

バグの修正

プログラムを保存した後、デプロイ->デプロイの管理->編集からバージョンを新しくし、デプロイすることで、更新することができる。
実際にアカウントを友達登録し、辞書の登録を行ってみると、スプレッドシートが少しおかしなことになっている。
2021-11-22 (2).png

このように、最初の行が空いてしまっているのである。
原因は、スプレッドシートにデータの入ったセルが全くないときも、valuesが[[""]]になってしまうからだ。
動作はするので特に問題はないが、少し気持ち悪いので、修正する。

function addDictionary(word,description){
  let wordIndex=searchWord(word);
  if(wordIndex==-1){
    let lastRow;

    //最初の要素の長さが1,つまりvaluesが[[""]]のとき
    if(values[0].length==1){
      lastRow=1;
    }else{
      lastRow=values.length+1;
    }
    sheet.getRange(lastRow,1).setValue(word);
    sheet.getRange(lastRow,2).setValue(description);
  }else{
    sheet.getRange(wordIndex+1,2).setValue(description);
  }
}

これで、無事にバグは治った。

さいごに

最終的に、このような感じになった。

LINEでの表示
2021-11-22 (3).png

スプレッドシートでの表示
2021-11-22 (4).png

すべてのコードを乗せておく。早く動かしたい人は、これをコピペして使えばいい。

"use strict"

let spreadSheet=SpreadsheetApp.getActiveSpreadsheet(),
sheet=spreadSheet.getActiveSheet(),
range=sheet.getDataRange(),
values=range.getValues();

function searchWord(word){
  for(let i=0;i<values.length;i++){
    if(values[i][0]==word)
      return i;
  }
  return -1;
}

function addDictionary(word,description){
  let wordIndex=searchWord(word);
  if(wordIndex==-1){
    let lastRow;

    //最初の要素の長さが1,つまりvaluesが[[""]]のとき
    if(values[0].length==1){
      lastRow=1;
    }else{
      lastRow=values.length+1;
    }
    sheet.getRange(lastRow,1).setValue(word);
    sheet.getRange(lastRow,2).setValue(description);
  }else{
    sheet.getRange(wordIndex+1,2).setValue(description);
  }
}

function sendMessage(messageText,userId){
  let url="https://api.line.me/v2/bot/message/push";
  let token="チャンネルのアクセストークン"
  let message={
    "type":"text",
    "text":messageText
  };
  let params={
    "headers":{
      "Content-Type":"application/json",
      "Authorization":"Bearer "+token
    },
    "method":"post",
    "payload":JSON.stringify({
      "to":userId,
      "messages":[message]
    })
  };
  UrlFetchApp.fetch(url,params);
}

function doPost(e){
  let userId="自分のユーザーid";
  let contents=JSON.parse(e.postData.contents);
  let text=contents.events[0].message.text;

  //送ってきたのが自分でなければ、終了する。
  if(userId!=contents.events[0].source.userId)
    return;

  //改行二回で分割してる。もし、この配列の長さが1ならば検索、2ならば登録とわかる。
  let textArr=text.split("\n\n");
  let sendText;
  if(textArr.length==1){
    let index=searchWord(textArr[0]);
    if(index==-1){
      sendText="単語が見つかりませんでした。"
    }else{
      sendText=sheet.getRange(index+1,2).getValue();
    }
  }else{
    addDictionary(textArr[0],textArr[1]);
    sendText="追加しました。"
  }
  sendMessage(sendText,userId);
}

最後まで読んでくれてありがとう。
分かりにくかったところや、改善できるところなどは、是非コメントで教えてほしい。

7
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
2