Google Apps Scriptでフォームメールを送る【GAS】

Google Formはフォームとしてページを表示するだけでなく、メールの中にフォームを埋め込んで送ることが可能です。しかし、FormAppには、フォームを作成するメソッドは合っても、Google Apps Scriptからそれを埋め込んだメールを送るメソッドが存在しません。そのため、メールで送るには、1人ずつ手動で、Google Form上で作業をしなければならない。

そこでGoogle Formを使わずに、フォームメールを作り、データをスプレッドシートに直接格納することはできないだろうか?と考えたのが今回の手法です。尚、Google Formのフォームメールも、今回の手法もスマートフォンのGMailアプリ上では入力が出来ません(セキュリティの関係上)。PC上では問題なく入力が可能です。

図:きちんとValidationも働く

今回使用するスプレッドシート類

GMail上では、JavaScriptは動きませんが、CSSで装飾する事は可能です。また、今回、Submit先は別に用意したdoPostの受け皿のURLを指定しているので、そちらで値を取得することが可能です。

ソースコード

GAS側コード

var sheetid = "ここに書き込み先スプレッドシートのIDを入力する";

function onOpen(){
  var ui = SpreadsheetApp.getUi();
  ui.createMenu("Apps Script")
    .addItem("フォーム送信", "htmls_mail")
    .addItem("フォーム生成と送り付け", "test_sendForm")
    .addToUi();
}

//フォーム送信を受け付ける
function doPost(e){
  //パラメータを取得する
  var ret = makeContent(makeResponse(e,"POST"));
  
  //書き込み用配列を用意する
  var array = [];
  var params = e.parameter;
  
  //パラメータを配列に格納
  array.push(params.email);
  array.push(params.blood);
  array.push(params.name);
  array.push(params.sex);
  array.push(params.kanso);
  
  //appendRowで書き込み
  var ss = SpreadsheetApp.openById(sheetid);
  var sheet = ss.getSheetByName("Sheet1");
  sheet.appendRow(array);

  //取得結果をそのままリターン
  return ret;
}

//投稿内容をJSONで組み立てて返す
function makeResponse (e, type) {
  e.parameter.valid = "20202020";  //適当な数値いれてテスト
 
  var s  = JSON.stringify({type: type, params: e});
 
  if (!e.parameter.callback) {
    return {mime:ContentService.MimeType.JSON,  content: s};
  }
  else {
    return {mime: ContentService.MimeType.JAVASCRIPT, content: e.parameter.callback + "(" + s + ");" };
  }
}

//投稿コンテンツ内容をJSONで返す
function makeContent(content) {
  return ContentService.createTextOutput(content.content).setMimeType(content.mime);
}

//現在のユーザのアドレスを取得
function GetUser() {
  var objUser = Session.getActiveUser();
  return objUser.getEmail();
}


//HTML Serviceを使ったCSSメール送信
function htmls_mail(){
  //HTMLサービスでCSSを取得
  var css = HtmlService.createHtmlOutputFromFile('css')
            .setSandboxMode(HtmlService.SandboxMode.IFRAME).getContent();
  
  var html = HtmlService.createHtmlOutputFromFile('index')
            .setSandboxMode(HtmlService.SandboxMode.IFRAME).getContent();
  
  //ヘッダー部分
  var formbody = "";
  var csshead = "<head><style>";
  var cssfoot = "</style></head>";

  formbody = csshead + css + cssfoot + "<body>" + html + "</body>";
  
  //メールを送信
  MailApp.sendEmail({
    to: GetUser(),
    subject: "CSSメールテスト",
    htmlBody: formbody,
    name: "HTML ServiceなCSSメール",
    noReply: true
  });   

}
  • 利用する場合は、必ず一度ウェブアプリケーションとして公開し、公開URLを取得する必要があります。
  • 公開URLは次のHTML内(index.html)で、FORMの送信先URLとして利用するので、最低でも2回は、ウェブアプリケーションとして版を重ねて公開が必要になります。
  • ボタンやテキストボックス等は、CSSで装飾ができるので、別途css.html内に装飾用のCSSコードを記述してあります。
  • makeResponsemakeContentはJSON形式に加工するために利用しています。
  • doPostで受け取って、スプレッドシートに書き込みをしています。また、この時、returnではJSONの内容を返していますが、別のHTMLファイルを用意して、返して上げれば「ありがとう表示」をできるようになるので、実運用時にはもうひとつHTMLを用意して、HTML Serviceで表示してあげましょう。

HTML側コード

<form action="ここにウェブアプリケーションとして公開した時のURLを記述する" method="post" class="contact-form">
  <p>お客様の情報をご記載下さい。</p>
  <div class="item">
    名前:<input type="text" name="name" size="40">
  </div>
  <div class="item">
    メール:<input id="e-mail" type="email" name="email">
  </div>
  <div class="item">
    性別:<input type="radio" name="sex" value="male">男
    <input type="radio" name="sex" value="female">女
  </div>
  
  <div class="item">
    血液型:<select name="blood">
      <option value="A">A型</option>
      <option value="B">B型</option>
      <option value="O">O型</option>
      <option value="AB">AB型</option>
    </select>
  </div>
  <div class="item">
    ご感想:<br>
    <textarea name="kanso" rows="4" cols="40"></textarea>
  </div>
  <div class="item">
    <input type="submit" value="送信" class="btn-square-little-rich"><input type="reset" value="リセット" class="btn-square-little-rich">
  </div>
</form>

今回のサンプルは、リファレンスのコードに1つメールアドレスを足し、CSS装飾するようにしました。それぞれの項目のname属性はユニークな値を記述するようにしてください。また、formタグのactionには、前項で取得したウェブアプリケーションのURLを指定します。

CSS側コード

今回のコードは関連リンク先に公開されているCSSデザインを利用させていただきました。css.htmlに記述しています。

.contact-form {
  border: 1px solid #ccc;
  padding: 10px;
  font-size: 13px;
  font-family: sans-serif;
}
.contact-form .item {
  display: block;
  overflow: hidden;
  margin-bottom: 10px;
}
.contact-form .item.no-label {
  padding: 5px 0px 5px 60px;
}
.contact-form .item .label {
  float: left;
  padding: 5px;
  margin:0;
}
.contact-form .item .radio-group{
  padding: 5px 0px 5px 60px;
}
.contact-form .item input[type=text],
.contact-form .item input[type=email],
.contact-form .item textarea {
  display: block;
  margin-left: 60px;
  width: 200px;
  padding: 5px;
  border: 1px solid #ccc;
  box-sizing: border-box;
  font-size: 13px;
}
.contact-form .item ::placeholder {
  color: #ccc;
}
.contact-form .item textarea {
  outline: none;
  border: 1px solid #ccc;
  resize: vertical;
  background:#f1f442;
}
input[type=submit] {
  border: none;
  outline: none;
  display: block;
  line-height: 30px;
  width: 160px;
  text-align: center;
  font-size: 13px;
  color: #fff;
  background-color: #696;
  border-bottom: 4px solid #464;
  cursor:pointer;

  box-sizing: content-box;
  transition:0.1s ease all
}
input[type=submit]:hover{
  opacity:0.6;
}

.btn-square-little-rich {
  position: relative;
  display: inline-block;
  padding: 0.25em 0.5em;
  text-decoration: none;
  color: #FFF;
  background: #03A9F4;/*色*/
  border: solid 1px #0f9ada;/*線色*/
  border-radius: 4px;
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.2);
  text-shadow: 0 1px 0 rgba(0,0,0,0.2);
}

.btn-square-little-rich:active {
  /*押したとき*/
  border: solid 1px #03A9F4;
  box-shadow: none;
  text-shadow: none;
}

doPostで受信したデータ

上記のコードで発信したメール上から、フォーム送信をすると、doPostで受け取ります。そして、doPostはreturnでそれを加工して返すようにしていますが、その際に加工して返してあげています。素の状態でdoPostで受け取ったデータは以下のような感じ。

{
  parameter={
    kanso=ガソリン, sex=male, name=キャラメルマン, blood=AB, email=turbo@penguin.jp
  }, 
  contextPath=, 
  contentLength=154, 
  queryString=,  
  parameters={
    kanso=[Ljava.lang.Object;@52fa97aa,
    sex=[Ljava.lang.Object;@72523afa, 
    name=[Ljava.lang.Object;@de431ca, 
    blood=[Ljava.lang.Object;@af2b550, 
    email=[Ljava.lang.Object;@cb32463
  },
  postData=FileUpload
}

parameterの項目だけ引ければ良いのですが、一応加工すると以下のような感じになります。

{
  "type":"POST",
  "params":
    {
      "parameter":
        {
          "kanso":"ガソリン",
          "sex":"male",
          "name":"キャラメルマン",
          "blood":"AB",
          "email":"turbo@penguin.jp",
          "valid":"20202020"
        },
      "contextPath":"",
      "contentLength":154,
      "queryString":"",
      "parameters":
        {
          "kanso":["ガソリン"],
          "sex":["male"],
          "name":["キャラメルマン"],
          "blood":["AB"],
          "email":["turbo@penguin.jp"]
        },
      "postData":
        {
          "type":"application/x-www-form-urlencoded",
          "length":154,
          "contents":"name=%E3%82%AD%E3%83%A3%E3%83%A9%E3%83%A1%E3%83%AB%E3%83%9E%E3%83%B3&email=turbo%40penguin.jp&sex=male&blood=AB&kanso=%E3%82%AC%E3%82%BD%E3%83%AA%E3%83%B3",
          "name":"postData"
        }
    }
}

強引にFormを生成してメールで送る

概要

FormAppにはフォームをメールで送るメソッドが存在しません。そこで、Stackoverflowにて紹介されていた手法がありますが、かなり強引な手法で、送れるには送れるのですが、CSSデザインなどが効いていない状態なので、デザイン的には残念なメールが送られてきました。ただ、GASでFormを生成して、続けてメールで送れるという自動化プロセスは非常に興味深いですね。使い捨てのFormを作って送れる点は、とても有意義だと思います。

このコードはフォームを生成⇒公開時URLにUrlfetchAppでスクレイピング⇒メール本文に貼り付けて送信という手段なので、CSSなどを別途準備できたら、もっと良いものになるのかなぁと思います。

ソースコード

//フォームを生成してメールで送る
function test_sendForm() {
  //新しくフォームを生成する
  var form = FormApp.create('フォームのファイル名');
  var formTitle = 'フォームの名前';
  
  //フォームにオプションを設定
  form.setTitle(formTitle)
      .setDescription('フォームの説明')
      .setConfirmationMessage('フォーム送信時のメッセージ')
      .setAllowResponseEdits(true)
      .setAcceptingResponses(true)

  //ログインを必須とする
  try { 
    form.setRequireLogin(true);
  } catch (e) {
    //ログインしていない場合にはエラーとなる
  }

  //1つだけ質問項目を生成する
  form.addTextItem().setTitle("Q1");

  //ユーザアドレスを取得してフォームを送信
  var email = GetUser();
  sendForm(form,email)
}

//生成したフォームをメール送信するルーチン
function sendForm(form,email) {
  //フォームの公開時アドレスを取得する
  var url = form.getPublishedUrl();

  //ログイン必須を解除しておく
  if (form.requiresLogin()) {
    var requiresLogin = true;
    //設定はfalseにしておく
    form.setRequireLogin(false);
  }

  //公開URLをスクレイピング
  var response = UrlFetchApp.fetch(url);
  var htmlBody = HtmlService.createHtmlOutput(response).getContent();

  //再度ログイン必須を設定する
  if (requiresLogin) {
    form.setRequireLogin(true);
  }
  
  //メールサブジェクトにフォームタイトルを利用する
  var subject = form.getTitle();
  
  //メールを送信する
  MailApp.sendEmail(
    email,
    subject,
    'このメールはHTMLメールでの表示が必須です。',
      {
        name: 'フォームメールスクリプトで送信テスト',
        htmlBody: htmlBody
      }
  );
}

受信した結果

送信ボタンは見当たらないのですが、Enterキーで送信することは可能です。ただ、スクレイピング内容を強引に貼り付けてるだけなので、CSSといったものが一切ありません。そのため下記の図のような表示になります。

図:なんだかオカシナ表示になってしまう。残念

強引にGoogle FormのデータをGASで送る事例の動画

GAS019 How to Embed Google Forms to Gmail

関連リンク

Google Apps Scriptでフォームメールを送る【GAS】” に対して2件のコメントがあります。

  1. Chihiro Fukazawa より:

    ありがとうございます!
    まさに、まさに欲しかった情報ですっっごく助かります!!!

    iPhone上のGmailアプリから実行すると「doGet関数がありません」と言われるので、doPostをdoGetにコピペし、gsの
    > var ret = makeContent(makeResponse(e,”POST”));
    のPOSTをGETに、htmlの
    > method=”post”
    のpostもgetにすると、携帯でもなんか動きました。

    これからも参考にさせていただきます。
    ありがとうございます!!!

    1. akanemaru2017 より:

      どうしても、Google Formのみだと限界がありますが、ちょっとだけそこを超えて自前でフォーム作れると世界が一気に広がりますね。
      自分もiPadのGmailで一度テストしてみようと思います。

Chihiro Fukazawa へ返信する コメントをキャンセル

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)