Javascript知らない男のGAS 2


さて書くべきコードは大まかに、GASにLINEからデータが来た時の処理、返信内容、返信をするために必要な認証情報の付与 といったところです。実際にコードを見ていきましょう。割と最低限のコードです。コメント無しをGithubにも載せておきます。
https://github.com/hinawakasonoko/desktop-tutorial/blob/master/Code.gs
#コード
```
//具体的な値は事前に指定しています 説明はコード後に
var CHANNEL_ACCESS_TOKEN = PropertiesService.getScriptProperties().getProperty("token");
var id = PropertiesService.getScriptProperties().getProperty("doc");
//LINEから来たデータから使うものを取り出す部分 
function doPost(post) {
  var obj = JSON.parse(post.postData.contents);//HTTPSに来たPOSTリクエストのJSONをパース
  var events = obj["events"];JSON内、リクエストボディのeventsを取り出す
      reply_message(events[0]);//ここは１つのメッセージごとに返信を返す仕様に甘えたコードかも?
}

//ラインのサーバから画像データを取得する部分
function getImg(messageId) {   
  var url = "https://api-data.line.me/v2/bot/message/" + messageId + "/content"; 
 //このURLは変数に入れといたほうが今後機能追加するうえで便利
  var options = { 
    "method" : "get",
    "headers" : {
    "Content-Type" : "application/json", 
    'Authorization': "Bearer " + CHANNEL_ACCESS_TOKEN
    }
  }; 
  return UrlFetchApp.fetch(url, options);   
 }

//ドキュメントへの動作と、LINEへの返信内容のコード 
function reply_message(data) 
{
  var return_text;
  if(data.message.type=="text") //文字
  {
    var input_text = data.message.text;
      //指定したIDを参考にドキュメントを開き、新しい行に追記
    var document = DocumentApp.openById(id).appendParagraph(input_text);
    return_text = "追加しました";   
  }
  else if(data.message.type=="image")//画像
  {
    var input_img = getImg(data.message.id);//画像データを得ます
    var document = DocumentApp.openById(id).appendImage(input_img);
    return_text = "追加しました";
  }
  else//スタンプなど
  {
    return_text = "非対応の形式です"
  }
  
//以下、返信データ用のコード いわばおまじない 
  var postData = {
    "replyToken" : data.replyToken,
    "messages" : [
      {
        "type" : "text",
        "text" : return_text
      }
    ]
  };
  
  var options = {
    "method" : "post",
    "headers" : {
      "Content-Type" : "application/json",//一応ここで送信データの文字コードも指定できます
      "Authorization" : "Bearer " + CHANNEL_ACCESS_TOKEN
    },
    "payload" : JSON.stringify(postData)
  };
  UrlFetchApp.fetch("https://api.line.me/v2/bot/message/reply", options);//ここにしか使わないのでURL直書き
}
```
アクセストークンとドキュメントのidはプロパティとして事前に登録しているので、コード内には記載していません。
DriveでGASを編集している画面で、右上の ファイル>プロジェクトのプロパティ>スクリプトのプロパティ に登録できます。
プロパティの名前はコード内で使用するものと一致していればなんでもOK
値にアクセストークンやドキュメントidを入れて、保存します。
一応比較しましたが、コード内に書いた場合と処理速度は同じか誤差レベルと思われます。

恐らく解説が一番必要なのは画像データを取得する部分かと思います。
文字はdata.message.textとして簡単に取り出せていますが、
それ以外の、画像や動画は同じようにはいかないのです。
画像を送ったとて、画像を送ったという事実はGASに送られてきますが画像データそのものは送られてきていません。
そのため、送られてきたメッセージのIDを用いて、該当画像をLINEのサーバーから直に拾ってきています。
ちなみに、文字をsetText(input_text)で書き込むことはできますが、前のメモを上書きされかねないので、新しい行に書き込んでくれるappendメソッドを使っています。
新しい行に書き込むという性質上か、ドキュメントの内容を取得するコードは省略できていますね

#アプリとして公開
さて、コードがOKなら、上の公開からウェブアプリケーションとして導入を押します
公開範囲を匿名含む全員に指定し、公開します。
初めはGoogleのセキュリティ確認が入ると思います。
公開するとURLが発行されるので、これをLINE developのWebhook URLに貼り付けます。
これで、作成した公式アカウントに送ったメッセージが自動的にドキュメントに追記されます。完成！

これは、他のサービスのAPIを用いれば他のメッセンジャーにも応用できますし(トークンの使い方など違いがあるので流用はできません)、Evernoteなどは投稿用アドレスにメールを送れば追加してくれるサービスがあります。
# セキュリティ面の懸念
さて、こうして作った公式アカウントは他人に公開しない事を強く勧めます。
というのも、他人が入力したデータが簡単に見れるからです。
今回のケースは誰が入力しようが自分のドキュメントにメモされるので公開されることはないと思いますが、問題はEvernoteと組み合わせた場合です。
例えば、ユーザーにEvernoteの投稿用アドレスを登録してもらい、そこへメモを投げるBOTを私が作ったとします。
当然logに流せば投稿用アドレスも私に見えますし、メモの内容も覗けます。
テキストを直接取得するAPIがないのは、APIを使うまでもないからです。
私はWebhookどころかJSONすら知らなかった初学者なので、なにかやり方があるのかなと、BOTを公開していた方に興味本位でデータのセキュリティについて質問したところ、見事に記事全消し＆Twitterに鍵をかけるという感じで答えももらえず逃げられましたので.....
その方はデバッグ用にスプレッドシートにログを残すコードを入れていましたので、気付いていながら運用していた可能性が高いです。
##感想
このコードの課題は、画像サイズがそのままなのでドキュメント上で大きすぎることです。
とはいえリサイズする関数を書けば解決します。どのサイズにリサイズするか画像投稿時に一緒に指定することも可能ですね。
まだ試せていませんが、最終的には非公式のGoogle keep APIを活用してKeepにメモを残せるようにするつもりです。
普段コードを書くことがないので新鮮でしたが、英語が分かれば分かるもんですね
LINEに関しては日本語での説明が豊富ですし、初学者にとって易しかったです。

##参考資料
LINE Messaging APIリファレンス LINE Developersより
GASに来るデータの中身
https://developers.line.biz/ja/reference/messaging-api/#message-event
画像データの取得
https://developers.line.biz/ja/reference/messaging-api/#get-content

Google GAS Developersより
ドキュメントに対する処理記述
https://developers.google.com/apps-script/reference/document/body#methods