Google App Scriptを使って無料で簡単にサーバーレスアーキテクトを実現するハンズオン!

Google App Scriptを使って無料で簡単にサーバーレスアーキテクトを実現するハンズオン!

2019, Oct 29

アジェンダ

Google App Script(以下、GAS。ガス?)を使って色々なWebAPIを連携してみましょう。 真っ先に候補に上がるのはLINEやSLACKなどのいわゆるDevOpsでしょうか。 それだけだと面白くないので、送ったメッセージの履歴をサーバー側1で保管・監視できるようにしましょう。 なんとなく高尚なシステムっぽいですね。

ハンズオン:LINE - GAS連携

まずは気軽に送ったメッセージをオウム返ししつつ、ログを取ってみましょう。 ログは可視化したいのでSpread Sheetを使います。 当然、無料です。

必要なのは、Google App ScriptとGoogle Spread Sheetを使うためのGoogleアカウント、 LINE BOTを使うためのLINEアカウントです。

ポイント

学校でプログラミングやったことあるよ!っていう人ぐらいに閾値を下げてお届けします。 アカウント登録は他で解説している通り、ソースについても他で解説している通りですが、 私がハマったところをメインにします。

GASのソース全文は執筆現在、公開していないので部分的に切り出します。 エッセンスは全部詰まっています。

LINE BOTから受け取ったメッセージから解析する

まず、LINE BOTから送られてきた=ユーザーがLINE BOTにメッセージを送った場合の受け取り方です。 便宜的にincomming.gsとでも呼びます。

設定はよそさんが超わかりやすいのでそちらで。 こちらでは、主にソースコードの解説を重視します。

// eはdoPostで受け取ったpostオブジェクトそのもの
function get_post_for_Line(e) {
  return JSON.parse(e.postData).events[0];
}

日本語で説明すると、doPostで受け取ったオブジェクトにpostDataというキーがあります。この内容がベタ書きのテキストなので、こいつをJSONにparseしないと内容を読み取れません。23 学校ではParserを教えてくれない4ので、思わず文字列処理をゴリゴリ書いてしまいたくなりますが、修行僧みたいな事をするならJSON.parseを使ったほうが早いです。

これを丁寧に書くと、

// 上と同じ動作をする関数です。
function get_post_for_Line(e) {
  var json_string_key = "postData;
  var json_string_value = e["postData"];
  var json_object = JSON.parse(json_string_value);
  return json_object.events[0];
}

となります。 json_objectのeventsは複数取れるみたいですが、実データが入っているのは0だけでした。 ここは必ず配列です。

復習

これをより実践的に書くとこのようになります。 内容がほとんど別物みたいに見えますが、やっていることは同じです。

// オブジェクトが指定したキーを持っているか判定する
var haskey = function(object, key) {
  return object
  ? object.hasOwnProperty(key)
    ? true
    : false
  : false;
}

var isLine = function(e, key) {
  target = "postData";
  if(!haskey(e, target)) return null;  // doPostがLineか以外だとnullを返す
  post = e[target];
  target = "contents";
  if(!haskey(post, target)) return null;  // doPostがLineか以外だとnullを返す

  try {  // doPostを経由
    p = JSON.parse(post[target]).events[0];
  } catch(error) {  // 何らかの理由でevents[0]が取得できない場合はnullを返す
    return null;
  }

  // ついでに要素を取り出して後で使いやすいよう共通のオブジェクトを作って返す
  return (p, haskey(p, "source") && haskey(p, "message"))
  ? get(
    PROPERTIES['LINE_ID_' + p.source.userId],
    p.message.text,
    key,
    p.source.groupId
  )
  : null;
}

5

doPostはとりあえず飛んできたものを全部受取るので、Lineから飛んできたかどうかチェックしてから値を返すようにしています。 受け取った値が正しいかどうかは、次のステップで標準化した時にチェックするようにします。

GASからLINE NOTIFYに返す

LINEからLINEに返す方法です。 便宜的にoutgoing.gsとでも呼びます。 事前にLINE NOTIFYを使えるようにしておきましょう。

const ENDPOINT = "(エンドポイント)";
const TOKEN = "(トークン)";
var headers = {"Authorization": "Bearer " + TOKEN};
var option = {
  "method": "post",
  "payload": "メッセージ",
  "headers":
};
UrlFetchApp.fetch(ENDPOINT, option);

短縮すると

return UrlFetchApp.fetch(
  "(エンドポイント)",
  {
    "method": "post",
    "payload": "メッセージ",
    "headers":
    {
      "Authorization": "Bearer " + TOKEN
    }
  }
) ? true : false;
// 未検証だけど、常にtrueが返りそう

個人的にはかえって読みにくいかなぁ、と思います…。

オウム返しをさせるなら、メッセージに受け取ったメッセージを渡すようにすると良いです。 p.messageになんかそれっぽいのがいっぱいあるので、オウム返しはp.message.textでできます。

詳細はconsole.logとかで掘り返してみましょう。

SpreadSheetに送る

LINE-LINEでメッセージのやりとりができるようになったので、メッセージをSpreadSheetにも送りましょう。 outgoing.gsの処理が無事に終わったら、その内容をシートにも送ります。

LINEに送る方法が分かれば、今度はSpreadSheetに送る方法も確かめましょう。 LINEから送られてきた各情報はp.message以下を見ればなんとなく使えそうな値がいっぱいあるので、これをSpreadSheetでも使えるように加工してあげればいいです。 IDを知りたい場合、GDrive上からファイルのプロパティとかでURLを探しましょう。

手引

var id= "https://docs.google.com/spreadsheets/d/(ここ)/..."
var File = SpreadsheetApp.openById(id);
var baseSheet = File.getSheetByName("シート名");

で取れるので、空白セルを探してデータを投入し、保存をするとできます。 なお、シート情報を取得する方法は色々ありますが、ここでは一番分かりやすい(と私が感じた)IDを使っています。

注釈

  1. 【サーバー側でログを取る】今回はサーバーレスアーキテクトですが、サーバーのような振る舞いをする存在は必要です。 

  2. 【Parser】パーサーとは、たとえばJavaScriptでオブジェクトとして扱いたい何かを解析してくれるもの、です。 

  3. 【JSON.parse】Lineから送られてくるPOSTデータ(e)の”PostData”にはJSONの記述がStringで入っているので、これをJavaScriptがJSONオブジェクトだと認識する必要があります。 

  4. 【学校ではParserを教えてくれない】私が教えてきた生徒さんたちは存在を知らなかったので教えてないと勝手に思ってます。 

  5. 【三項演算子使いまくり問題】このコードが読みやすいか、直感的かは人によって意見が分かれますね。当時は隙間なく行を追えば結果が追えるから楽だと思ってましたが、こうして見直してみると素直にif-elseでよくね?っていう気もします。特にhaskeyのreturn文中