添付ファイル付きの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で動作を確認しています。