電話かFAXしか受け付けないペンションのためにFirebase + Google Apps Scriptで作った問い合わせフォームを作った話

#前置き

自分の居合道の師匠がペンションを運営しているのですが、予約ミスを防ぐために「電話かFAXのみ」しか予約を受け付けていませんでした。
さすがにそれはこの時代、使いにくいだろう、ということで、ウェブサイトの全面リニューアル時に予約フォームの仕組みを作った時の話です。

現物はこれです。
https://mugenkan.jp/inquiry/

<img width="552" alt="Screen Shot 2018-12-22 at 2.33.50 PM.png" src="https://qiita-image-store.s3.amazonaws.com/0/39529/dcc3f53d-daa9-ba29-3f09-3d3751f8831f.png">

ページを見ていただければわかりますが、Google Formを使ってます。

#なぜ電話とFaxだけなのか

師匠は80才ながらパソコンで文章を書いたり、居合の指導をしながら撮影した動画を編集してDVDを弟子に作ってあげたり、と、実はかなりハイスペック。
スマホもばっちり使いこなしています。
なんと「The Card」という20年以上前の顧客データベースをWindows 10でも現役で使いこなしている人。

ではなぜ電話とFaxだけなのかというと、**それは奥様のため**。

居合道界では有名な師匠でしたので、現役時代は国内・海外指導や大会などに出かけていたため
留守にすることも多く、その際にメールで予約があってしまうと、**奥様がパソコンを使えないので、ダブルブッキングが発生してしまう可能性があるから**です。

「メールを受け取ったら師匠が自宅に連絡すればいいじゃないか」と思うかもしれませんが、海外にいるときなどはそれほど自由が効きません。
なので長年、予約は電話かFAXのみでした。

長年なんとかしてあげたかったのですが、私の時間がなく、長年放置してました。
そんなときTwilioがFaxサービスを日本でも始めたことを知り、これを使って解決しようと重い腰を上げたのが、2018年5月です。

#システム構成

お金とメンテナンスの時間はかけたくなかったので、こんな感じの構成にしました。

![Screen Shot 2018-12-22 at 2.33.50 PM.png](https://qiita-image-store.s3.amazonaws.com/0/39529/cd2c8e15-ab08-9eaa-5a65-ba383eb36088.png)

コードは公開しませんが、システム構成だけ分かっていれば簡単に作れると思います。

以下、要点のみ書いていきます。

# Google Forms + Google Slides

Google Formsいいですよね〜。
項目も簡単に変更できるし、PCとモバイルと両方対応してくれるので、簡単に扱えます。

”Google Formsができることに合わせて使えば”、ストレスなく使えるでしょう。
（”Google Formsができないこと”をやろうとすると疲れるのでやめましょう）

Google Formsのもう一つの利点は、**Google Apps Scriptと組み合わせてコードを動かすことができる**、ということ。

FAXを送信してくれる[TwilioのFAX API](https://www.twilio.com/docs/fax)は高機能で、いくつもの使い方があります。
しかし、今回は決まった番号に送信をしたいだけなので、
Programmable Fax Quickstartに書いてある一番最初のコードしか使っていません。
下記のコードを見て分かる通り、PDFのURLを指定すればいいだけなので、なんとかしてPDFを生成する必要がありました。

```js
// Download the helper library from https://www.twilio.com/docs/node/install
// Your Account Sid and Auth Token from twilio.com/console
const accountSid = 'ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';
const authToken = 'your_auth_token';
const client = require('twilio')(accountSid, authToken);

client.fax.faxes
  .create({
     from: '+15017122661',
     to: '+15558675310',
     mediaUrl: 'https://www.twilio.com/docs/documents/25/justthefaxmaam.pdf'
   })
  .then(fax => console.log(fax.sid))
  .done();
```

# Google SlidesでPDFを作成

そこでGoogle Apps Scriptの出番です。

Google Apps ScriptからPDFを生成する方法としては、普通は「Google Docs」を使います。
私も最初はそうしようと思いました。

ただ今回は「今までのFax用紙のデザインをそのまま使いたい」という思惑もありました。
やはり80才の師匠が、小さい文字を読むのは大変です。
今まで使ってきたデザインをなんとかそのまま生かしたかったのですが、Google Docsだと何か問題があった気がします。

そこでGoogle SlidesにFax用紙の原版を背景画像としてセットし、その上にTextフィールドを作って、文字を置き換える方法にしました。

一言で言えば「**Google Slidesを使ってテンプレートシステムを自前で構築した**」ということです。
そんなに難しくはなく、"(変数名)"が置き換わるだけです。変数名は、フォームの名前と一致してます。
<img width="710" alt="Screen Shot 2018-12-22 at 3.25.06 PM.png" src="https://qiita-image-store.s3.amazonaws.com/0/39529/20a3b909-2cc0-6b87-f45c-5527fc435683.png">


## PDF作成の裏技!

テンプレートシステムが出来上がり、さてさてPDFを出力しようと思ったら、まさかのPDF保存の機能がGoogle Apps Scriptにありません！！

当然あるだろう！くらいに思ってたら実装されていなかったので悩んでいたら、「Download as ...の機能を使えばいい」というのを日本人の誰かが解説してくれていた記事を見つけ、これを使うことにしました。
https://www.virment.com/create-pdf-google-apps-script/

まずGoogle Apps Scriptで、テンプレートのGoogle Slideをコピーします。
次にオレオレテンプレートシステムで、フォームの内容を埋め込んでいきます。

で、最後に裏技なのですが、以下のようなURLにすると、Google SlidesがPDFをエクスポートしてくれます。

```js
// スプレッドシートをPDFにエクスポートするためのURL。このURLに色々なオプションを付けてPDFを作成
var url = "https://docs.google.com/presentation/u/0/d/<<ID>>/export/pdf?id=<<ID>>&pageid=p"
  .replace(/<<ID>>/g, slideFile.getId());
Logger.log(url);
```

# Firebase Functions経由で送信

ここまで読んだ方は、__その裏技Google SlidesのURLをTwilioに渡せばいいじゃないか__、と思うかもしれませんが、なぜか失敗します。

そこでFirebase Functionsで一度取得して、Google Cloud Storageに保存します。

Google Cloud Storageのいいところは、**保存したファイルに公開URLを割り当てることができる**、ということ。
ちょっとしたファイルサーバーとして使えます。価格は激安。

このURLをTwilioのFax APIに送ると、Twilioのbotが取得しに来て、それをFaxとして送信してくれます。

# テストはどうやったのか

Faxなんて超アナログなものは持ってない私なので、テストには[e-fax](http://www.efax.com/‎)を利用させてもらいました。無料試用期間があるので、数日間の開発には十分です。

解約がちょっと面倒（らしい）です。日本語のウェブサイトから申し込んでも、解約時にはアメリカに転送されて、英語で言わなければならないらしいです。
私は面倒なので、最初からアメリカのサポートに電話して解約しました。「ありがとう、もういらない」って言ったら、素直に解約してくれました。

# 結果どうなった？

この予約フォームを作ってから、**問い合わせが今まで体験したことがないくらい増えた**とのことでした。

良かった。良かった。ちゃんちゃん♪