How to send structured email markup using Gmail AP

2019-02-28 15:54发布

问题:

I'm trying to build a Javascript app that uses Gmail's API to send emails (to myself) including the structured data needed for Gmail's Inbox to recognise a hotel reservation. The goal is to be able to enter details about the reservation in an HTML form and have the app send me an email than Inbox then recognises as a hotel reservation and adds a Trip bundle to my inbox.

I followed a worked example, here, that uses Google Apps Scripts to send emails from my account to myself. The script pulls the body from a file as html, including the necessary structured data in JSON format. This works fine, and Inbox recognises the hotel reservation.

Here's the Apps Script code:

function manuallyCreateTrips() {
  var htmlBody = HtmlService.createHtmlOutputFromFile('hotel').getContent();

  MailApp.sendEmail({
    to: Session.getActiveUser().getEmail(),
    subject: 'Somewhere ' + new Date(),
    htmlBody: htmlBody,
  });
}

And here's the html content of the email (hotel.html):

<html>
  <body>
    <script type="application/ld+json">
{
  "@context": "http://schema.org",
  "@type": "LodgingReservation",
  "reservationNumber": "None",
  "reservationStatus": "http://schema.org/Confirmed",
  "underName": {
    "@type": "Person",
    "name": "Richard Guy"
  },
  "reservationFor": {
    "@type": "LodgingBusiness",
    "name": "Somewhere",
    "address": {
      "@type": "PostalAddress",
      "streetAddress": "Street",
      "addressLocality": "Locality",
      "addressRegion": "Region",
      "postalCode": "Postcode",
      "addressCountry": "UK"
    },
    "telephone": "+44 1234 123123"
  },
  "checkinDate": "2016-04-27T13:00:00+01:00",
  "checkoutDate": "2016-04-28T12:00:00+01:00"
}
</script>


    <p>
      This is a hotel reservation at Somewhere.
    </p>
  </body>
</html>

But rather than type the JSON manually, I'd like to have a prettier interface. So I've started by building a simple email client in Javascript using Gmail's API, following the worked example here. The client works OK to send emails which I then receive in my Gmail account.

Here are the functions that assemble and send the email:

function sendEmail()
{
  $('#send-button').addClass('disabled');

  sendMessage(
    {
      'To': $('#compose-to').val(),
      'Subject': $('#compose-subject').val(),
      'Content-Type': 'text/html; charset=utf-8',
    },
    $('#compose-message').val(),
    composeTidy
  );

  return false;
}

function sendMessage(headers_obj, message, callback)
{
  var email = '';

  for(var header in headers_obj)
    email += header += ": "+headers_obj[header]+"\r\n";

  email += "\r\n" + message;

  var sendRequest = gapi.client.gmail.users.messages.send({
    'userId': 'me',
    'resource': {
      'raw': window.btoa(email).replace(/\+/g, '-').replace(/\//g, '_')
    }
  });

  return sendRequest.execute(callback);
}

The function sendEmail() calls sendMessage(), passing an object containing the header lines, a string containing the (html formatted) content of the email, and a callback function to tidy up afterwards. sendMessage() combines the headers with the content and encodes the lot as base64. I send the same html content of the email as before, and the email arrives in my inbox. Any html formatting in the body text appears correctly in my email viewer. But Gmail's Inbox doesn't seem to pick up the structured data, so the reservation isn't added to a new trip.

When I compare the source of the two emails I can see significant differences in the headers.

This was the successful email:

Delivered-To: my@gmail.com
Received: by 10.79.103.71 with SMTP id b68csp826548ivc;
        Fri, 22 Apr 2016 09:09:32 -0700 (PDT)
X-Received: by 10.60.21.33 with SMTP id s1mr8900353oee.74.1461341372514;
        Fri, 22 Apr 2016 09:09:32 -0700 (PDT)
Return-Path: <3vEwaVwsJC30sjdibseuhvzhnbjm.dpnsjdibseuhvzhnbjm.dpn@maestro.bounces.google.com>
Received: from mail-ob0-x248.google.com (mail-ob0-x248.google.com. [2607:f8b0:4003:c01::248])
        by mx.google.com with ESMTPS id b134si2357523oih.30.2016.04.22.09.09.32
        for <my@gmail.com>
        (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128);
        Fri, 22 Apr 2016 09:09:32 -0700 (PDT)
Received-SPF: pass (google.com: domain of 3vEwaVwsJC30sjdibseuhvzhnbjm.dpnsjdibseuhvzhnbjm.dpn@maestro.bounces.google.com designates 2607:f8b0:4003:c01::248 as permitted sender) client-ip=2607:f8b0:4003:c01::248;
Authentication-Results: mx.google.com;
       dkim=pass header.i=@gmail.com;
       spf=pass (google.com: domain of 3vEwaVwsJC30sjdibseuhvzhnbjm.dpnsjdibseuhvzhnbjm.dpn@maestro.bounces.google.com designates 2607:f8b0:4003:c01::248 as permitted sender) smtp.mailfrom=3vEwaVwsJC30sjdibseuhvzhnbjm.dpnsjdibseuhvzhnbjm.dpn@maestro.bounces.google.com;
       dmarc=pass (p=NONE dis=NONE) header.from=gmail.com
Received: by mail-ob0-x248.google.com with SMTP id js7so145982383obc.0
        for <richardtguy@gmail.com>; Fri, 22 Apr 2016 09:09:32 -0700 (PDT)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
        d=gmail.com; s=20120113;
        h=mime-version:message-id:date:subject:from:to;
        bh=L6yElux8juWH24FvYKyFSYm7hSo/j0YatA7rHQ62QZ4=;
        b=iUcZ/k5ctSENTTcliXUd1jyE8FnHigru+fx97U26V4ppudjHaWF5tgTXhHD9di+qu3
         dCTG/5uXRDZq/9lXox+zLGn1CUJv8otDjzyu4zQQzMCgFWkrlPvzauPCxmWMeqKBpsEN
         sbipWbMvTPMSLbUkzWNmC7aDqHEQffdlTu69+oEidkxBVGYYGHO6XWBNT78O9owYLUXD
         +7KzpEwciGDmdXkN+bFf9kFXsIapq7kHja3o3Y56Xz/lEeZDOYfOF211IhQ/ALWKEzpe
         uL3iOu1GItLVC6oUVt46d8qYxHfNtP0qmQXzNjuL4YC/XuFeR6eJQ8mXBj8pM8YkIfst
         1GYg==
X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
        d=1e100.net; s=20130820;
        h=x-gm-message-state:mime-version:message-id:date:subject:from:to;
        bh=L6yElux8juWH24FvYKyFSYm7hSo/j0YatA7rHQ62QZ4=;
        b=QOSVkBTnwapvWbnMDhcvjyvrS84JL0QsG2vbckfUMLpFgjNcA5uw0QxZYRBLgYEjkT
         r3IvezvZqgtXES6QU6XUBnZQ8h7sVhgKGvB5t/b4BZbKbnkImGAhIhSQDqFtlY0+ZgqS
         GcuWs1eacvCKMfi5RKLvH6O1Bn63gEfdYtz3EjwfZdle1lvg9WvU3GSWt8G2Hw/Bb8Z2
         sYo3Ok4jwLFgdguCsJG8CK6eUKwAdLvgmrfU1oh0UJVDVGbWallEsVJPXW/iqAYvgOVr
         0BFsTMxKheruSLeKgx5PbUYYfOul8mLbYZz/NkLxU+hHuUd+zVvIvIPmidK+0CvMyGUB
         YFeA==
X-Gm-Message-State: AOPr4FVRMkINNmOkf3rYdLZspc+99SeliqYmKfw/6w4zvXITcXqVY/CP1fJO86bNJRLEPwl2jO4UMroedpRj6A==
MIME-Version: 1.0
X-Received: by 10.182.250.201 with SMTP id ze9mr14627612obc.0.1461341372044;
 Fri, 22 Apr 2016 09:09:32 -0700 (PDT)
Message-ID: <001a11c1fabe608d4f0531150d8c@google.com>
Date: Fri, 22 Apr 2016 16:09:32 +0000
Subject: Somewhere Fri Apr 22 2016 17:09:31 GMT+0100 (BST)
From: my@gmail.com
To: my@gmail.com
Content-Type: multipart/alternative; boundary=001a11c1fabe608d370531150d89

--001a11c1fabe608d370531150d89
Content-Type: text/plain; charset=ISO-8859-1; format=flowed; delsp=yes

Somewhere

--001a11c1fabe608d370531150d89
Content-Type: text/html; charset=ISO-8859-1

<html>
  <body>
    <script type="application/ld+json">
{
  "@context": "http://schema.org",
  "@type": "LodgingReservation",
  "reservationNumber": "None",
  "reservationStatus": "http://schema.org/Confirmed",
  "underName": {
    "@type": "Person",
    "name": "Richard Guy"
  },
  "reservationFor": {
    "@type": "LodgingBusiness",
    "name": "Somewhere,
    "address": {
      "@type": "PostalAddress",
      "streetAddress": "Address",
      "addressLocality": "Locality",
      "addressRegion": "Region",
      "postalCode": "Postcode",
      "addressCountry": "Country"
    },
    "telephone": "+12 1234 606630"
  },
  "checkinDate": "2016-04-23T13:00:00+01:00",
  "checkoutDate": "2016-04-24T12:00:00+01:00"
}
</script>


    <p>
      Somewhere
    </p>
  </body>
</html>
--001a11c1fabe608d370531150d89--

And here's the email sent by the Javascript app using the Gmail API:

Received: from 760084270114
    named unknown
    by gmailapi.google.com
    with HTTPREST;
    Sat, 23 Apr 2016 09:43:29 -0400
To: my@gmail.com
Subject: Test reservation
Content-Type: text/html; charset=utf-8
Date: Sat, 23 Apr 2016 09:43:29 -0400
Message-Id: <CAD=04YmCHxH6mPY6FgoKLKFXd=C5sTLjQeo-9Q70jWKe4wVsRQ@mail.gmail.com>
From: my@gmail.com

<html>
  <body>
    <script type="application/ld+json">
{
  "@context": "http://schema.org",
  "@type": "LodgingReservation",
  "reservationNumber": "None",
  "reservationStatus": "http://schema.org/Confirmed",
  "underName": {
    "@type": "Person",
    "name": "Richard Guy"
  },
  "reservationFor": {
    "@type": "LodgingBusiness",
    "name": "Somewhere,
    "address": {
      "@type": "PostalAddress",
      "streetAddress": "Address,
      "addressLocality": "Locality",
      "addressRegion": "Region",
      "postalCode": "Postcode",
      "addressCountry": "UK"
    },
    "telephone": "+12 3456 606630"
  },
  "checkinDate": "2016-04-23T13:00:00+01:00",
  "checkoutDate": "2016-04-24T12:00:00+01:00"
}
</script>


    <p>
      Somewhere
    </p>
  </body>
</html>

Any ideas??

回答1:

In the end I found it was quite simple to implement a reasonably polished front-end form using client-side JavaScript and Bootstrap styling in a Google Apps Script (GAS) web app. Server-side functions handle sending the email with the structured data embedded. As GAS's MailApp.sendEmail() function digitally signs emails, this was successful. I've put the resulting code on GitHub and published the web app here. Thanks for the suggestions!