添付ファイル付きのGmailメッセージをkintoneに自動的にアップロード

著者名:Mamoru Fujinoki(Fuji Business International)

目次

はじめに

外部からの問い合わせメールを kintone で管理するには、以下の記事で紹介されているように Zapier (External link) 等のサービスを利用することでコードなしで実現できます。
コーディングなしで超簡単!kintoneのWebhookでGmailに通知する

しかし、添付ファイルが送られて来た場合に、そういったサービスだけでは添付ファイルを kintone にアップロードできません。
今回は、Google Apps Script(以下 GAS)を使って、定期的に Gmail に送られて来たメールの内容と添付ファイルを kintone に記録するアプリの作成をします。
この方法を用いれば、Web サイトの問い合わせフォームの送信先を Gmail に設定したい場合、問い合わせの内容を添付ファイルも含め自動的に kintone へ記録管理できます。

1. 事前に必要なもの

  • Google アカウント
  • kintone アカウント 開発者ライセンスの取得方法は こちら

2. 開発の流れ

以上の手順で開発していきます。

3. kintoneアプリの作成

ステップ1

kintone でメール問い合わせのアプリを作成します。
以下のテーブルおよび画像を参照して、新規アプリを作成します。

フィールドの種類 フィールド名 フィールドコード
文字列(1行) 氏名 name
文字列(1行) Eメール email
文字列(1行) 件名 subject
文字列(複数行) 内容 message
添付ファイル 添付ファイル Attachment

ステップ2

フォーム保存後、「アプリの設定」タブに移動し、「API トークン」をクリックします。

「生成する」ボタンをクリックして、API トークンを生成し、「レコード追加」オプションをチェックした後、「保存」ボタンをクリックして設定を保存します。

最後に「アプリの設定」画面に戻り、「アプリを更新」ボタンをクリックして、設定を有効にします。
生成した「API トークン」、「アプリ ID」(URL 記載の番号)、「アプリ名」はあとあとコード内で使用しますので、メモしておいてください。

4. Gmail API の GAS エディタの設定

ステップ1

Google.com (External link) にてログインし、Google アプリアイコンより、Google ドライブを選択します。

次に左上の「新規」メニューより、「その他」→「Google Apps Script」を選択します。
「Google Apps Script」がメニューにない場合、「アプリを追加」より、「Google Apps Script」を追加します。

GAS のエディタが表示されますので、適当なプロジェクト名を入力します。

ステップ2

kintone API の呼び出しには、GitHub に公開されている Google Apps Script Library for kintone (External link) のライブラリを使用します。
エディタのメニューより、「ライブラリ」の横の「+」ボタンを選択します。

Google Apps Script Library for kintone (External link) の README.md に記載されている「Script ID(For New editor)」の値を「スクリプト ID」欄に入力します。
スクリプト ID を入力したら「検索」ボタンでライブラリを検索します。
ライブラリが表示されたら、最新のバージョンを選択し、「追加」をクリックします。

これでライブラリがプロジェクトに追加されました。

ステップ3

Gmail API を有効にします。
「リソース」メニューより、「サービス」を選択します。

一覧より、「Gmail API」を探し、サービスを追加します。

5. GAS のコーディング

GAS スクリプトエディタにコードを書き込みます。以下がサンプルコードです。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
/*
* 添付ファイル付のGmailメッセージをkintoneに自動的にアップロード
* Copyright (c) 2018 Cybozu
*
* Licensed under the MIT License
*/

'use strict';
const domain = '{サブドメイン名}.cybozu.com';
const appId = {アプリID};
const apiToken = '{APIトークン}';
const appName = '{アプリ名}';

// 添付ファイルをアップロードする
function uploadAttachment(attachment) {
  const blob = attachment.copyBlob();
  const formData = {
    file: blob
  };
  const formHeader = {
    'X-Requested-With': 'XMLHttpRequest',
    'X-Cybozu-API-Token': apiToken
  };
  const options = {
    method: 'post',
    headers: formHeader,
    payload: formData
  };
  const res = UrlFetchApp.fetch('https://' + domain + '/k/v1/file.json', options);
  return JSON.parse(res.getContentText());
}

// 検索条件に合った受信メールを取得する
function getGmailMessage() {
  // 指定した件名で添付ファイル付未読メールのみ取得
  const threads = GmailApp.search('is:unread subject:(任意のメールの件名) has:attachment');

  const recordObjs = [];
  for (let i = 0; i < threads.length; i++) {
    const messages = threads[i].getMessages();// Get messages
    for (let j = 0; j < messages.length; j++) {
      const recordObj = {};
      const message = messages[j];
      if (message.isUnread()) { // スレッド内の未読メールのみ対象
        const attachments = message.getAttachments();
        const fileKeysArray = [];
        for (let k = 0; k < attachments.length; k++) {
          fileKeysArray.push(uploadAttachment(attachments[k]));
        }
        if (fileKeysArray.length > 0) {
          recordObj.Attachment = {value: fileKeysArray};
        }

        // メールの送信者
        recordObj.name = {value: message.getFrom()};
        // 送信元メールアドレス
        recordObj.email = {value: message.getReplyTo()};
        // メールの件名
        recordObj.subject = {value: message.getSubject()};
        // メールの本文
        recordObj.message = {value: message.getPlainBody()};

        // 既読にする
        message.markRead();
      }
      recordObjs.push(recordObj);
    }
  }
  return recordObjs;
}

function sendToKintone() {
  const apps = {
    YOUR_APPLICATION1: {
      appid: appId,
      name: appName,
      token: apiToken
    }
  };

  const manager = new KintoneManager.KintoneManager(domain, apps);
  const records = getGmailMessage();

  // recordsにメールの情報が取得できた場合にのみkintoneにPOSTする
  if (records.length > 0) {
    const response = manager.create('YOUR_APPLICATION1', records);
    // 成功すればcodeに200が返る
    const code = response.getResponseCode();
    Logger.log('Response code is "%s"', code);
  } else {
    Logger.log('No record found');
  }
}

コードの解説

変数の定義

複数の関数で使用する変数を最初に定義します。
domain には、次のようにご使用のアカウントのフルドメイン名を指定します。(海外アカウントの場合、たとえば US だと{サブドメイン名}.kintone.com のように指定します)
また、上記で作成したアプリ ID、API トークン、アプリ名を指定します。

 9
10
11
12
const domain = '{サブドメイン名}.cybozu.com';
const appId = {アプリID};
const apiToken = '{APIトークン}';
const appName = '{アプリ名}';
sendTokintone 関数

Google Apps Script Library for kintone(kintoneManager)を使って、kintone API でレコードを作成します。

72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
function sendToKintone() {
  const apps = {
    YOUR_APPLICATION1: {
      appid: appId,
      name: appName,
      token: apiToken
    }
  };

  const manager = new KintoneManager.KintoneManager(domain, apps);
  const records = getGmailMessage();

  // recordsにメールの情報が取得できた場合にのみkintoneにPOSTする
  if (records.length > 0) {
    const response = manager.create('YOUR_APPLICATION1', records);
    // 成功すればcodeに200が返る
    const code = response.getResponseCode();
    Logger.log('Response code is "%s"', code);
  } else {
    Logger.log('No record found');
  }
}
getGmailMessage 関数

Gmail のメッセージデータを取得し、kintone へ送信するレコードを生成します。

指定した件名で添付ファイル付の未読のすべてのメッセージのスレッドを取得します。
検索の条件のクエリは、画面のように Gmail の検索画面にて検索オプション表示し、条件設定した後、検索すると表示されるので、これをコピーしてコード内に設定します。

35
36
// 指定した件名で添付ファイル付未読メールのみ取得
const threads = GmailApp.search('is:unread subject:(任意のメールの件名) has:attachment');

取得したスレッドのすべてのメッセージを取得します。
次に個々のメッセージをループし、送信するレコードのデータを取得して JSON 形式のレコードを生成します。
未読を含むスレッド内のメールすべてを取得しているため、個々のメッセージが未読かどうかをチェックします。

38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
const recordObjs = [];
for (let i = 0; i < threads.length; i++) {
  const messages = threads[i].getMessages();// Get messages
  for (let j = 0; j < messages.length; j++) {
    const recordObj = {};
    const message = messages[j];
    if (message.isUnread()) { // スレッド内の未読メールのみ対象
      const attachments = message.getAttachments();
      const fileKeysArray = [];
      for (let k = 0; k < attachments.length; k++) {
        fileKeysArray.push(uploadAttachment(attachments[k]));
      }
      if (fileKeysArray.length > 0) {
        recordObj.Attachment = {value: fileKeysArray};
      }

      // メールの送信者
      recordObj.name = {value: message.getFrom()};
      // 送信元メールアドレス
      recordObj.email = {value: message.getReplyTo()};
      // メールの件名
      recordObj.subject = {value: message.getSubject()};
      // メールの本文
      recordObj.message = {value: message.getPlainBody()};

      // 既読にする
      message.markRead();
    }
    recordObjs.push(recordObj);
  }
}

添付ファイルを取得します。
「uploadAttachment」関数でファイルを kintone にアップロードし、その際返却される「FileKey」を fileKeysArray に push します。

45
46
47
48
49
50
51
52
const attachments = message.getAttachments();
const fileKeysArray = [];
for (let k = 0; k < attachments.length; k++) {
  fileKeysArray.push(uploadAttachment(attachments[k]));
}
if (fileKeysArray.length > 0) {
  recordObj.Attachment = {value: fileKeysArray};
}

メッセージ処理後は既読に設定します。

63
64
// 既読にする
message.markRead();
uploadAttachment 関数

Gmail から取得した添付ファイルを kintone にアップロードします。戻り値として「fileKey」が返されます。
ファイルのデータは blob 形式でコピーし、XMLHttpRequest を使って multipart/form-data 形式で POST します。また、ヘッダーに kintone アプリで生成した API トークンを設定します。

14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 添付ファイルをアップロードする
function uploadAttachment(attachment) {
  const blob = attachment.copyBlob();
  const formData = {
    file: blob
  };
  const formHeader = {
    'X-Requested-With': 'XMLHttpRequest',
    'X-Cybozu-API-Token': apiToken
  };
  const options = {
    method: 'post',
    headers: formHeader,
    payload: formData
  };
  const res = UrlFetchApp.fetch('https://' + domain + '/k/v1/file.json', options);
  return JSON.parse(res.getContentText());
}

動作確認

ステップ1

画面のようにお使いの E メールクライアントより、Gmail 宛先、返信先、件名、メッセージを入力し、添付ファイルを添えて、送信します。(「返信先」を指定しないと E メール欄が空欄となります)

宛先の Gmail アカウントにメールが受信されました。(送信後、未読の状態を保つため、該当の Gmail メッセージは開かないでください)

ステップ2

次にエディタの上部のメニューより、「sendTokintone」を選択し「実行」をクリックすると、作成したプログラムが実行されます。

初めて実行すると Gmail への承認が要求されますので、許可し、該当の Gmail アカウントを選択します。

以下のような警告が表示された場合は、「cybozu.com(安全ではないページ)に移動」を選択してください。

さらにアクセスするスコープを許可します。

するとプログラムが実行されます。
エラーがなく「G メール問い合わせ」アプリに添付ファイル付でレコード追加されていれば成功です。

トリガーの設定

最後にプログラムを定期的に実行するためにトリガーを設定します。
エディタのメニューより、「トリガー」を選択します。

トリガーを新規に追加し、sendTokintone 関数を指定し、1 分毎にプログラムを実行します。

設定後、保存します。

注意事項

  • 添付ファイル名に日本語が含まれている場合、kintone 側で文字化けしてしまうことがあります。
  • 上記サンプルコード内の検索クエリをそのままお使いいただくと Gmail のメッセージを複数返信した場合、添付ファイルがなくても、kintone にレコード登録されてしまう場合がございますのでご注意ください。

まとめ

外部サービスからレコードを追加するには、kintone API で比較的容易に実現できますが、添付ファイルの追加には一工夫必要となります。
今回のように Gmail を介して添付ファイルを kintone にアップロードすれば、外部からメールで送られて来た添付ファイルも容易に管理できるようになるのではないでしょうか。
今回の方法を応用すれば、メール添付ファイル以外にもさまざまなケースで外部から kintone へファイルをアップロードできます。

参照

information

この Tips は、2023 年 4 月版 kintone で動作を確認しています。