Google Apps ScriptでGmail APIにてメールデータを変換して追加する【GAS】
以前、NotesのメールをOutlookのEML形式に変換して取り込むのを作成したのですが、今回ちょっとこれをGMailに取り込めないか?と考えてる途中で、メールを分解し一覧表にし、添付ファイルはGoogle Driveにアップロードしてある状態で、この2つからメールを構築して受信トレイに入れられないか?と考え、GASで作ってみました。
Gmailは他の環境からのメールの移行はThunderbirdを使うケースはよくあるのですが、これをプログラムで行えたら便利だと思ったので、まだ変換して取り込む部分だけですが、これで他の環境のメールの移行の一助になるのではないかと思います。
目次
今回利用するファイル
- データからメールデータに変換 - Google Spreadsheet
今回はGmailを操作する為、Google Apps ScriptからGmail APIを叩いて変換をしています。添付するファイルは2つ以下のファイルにしてあります。
図:こんな感じに構築して受信トレイに入れる
事前準備と仕組み
拡張サービスを有効化する
今回、GASからGmail APIを直接利用するので、以下の作業が必要になります。
- スクリプトエディタを開く
- 左サイドバーのサービスにある+をクリックする
- Gmail APIを選択して、追加をクリック
これで、GASからREST APIであるGmail APIを叩く事が可能になります。GASのメソッドであるMailAppやGmailAppとは異なるので注意。
図:拡張サービスを利用する
Gmailの仕組み
GmailはOutlookとは異なり、メールボックスは1個しかなく、各メールのエントリーに対して標準で用意されてるラベル(受信トレイやスター付きといったもの)を付与する事で分類しています。送信済みであったりもそのうちの1つで、個別にメールボックスの区分が存在するわけではありません。
また、このラベルはラベル名とそれぞれIDが付与されており、受信トレイの場合はIDはINBOXというものが割当られており、ユーザが自分で作成したラベルも作成時に独自にラベルIDが付与されています。このラベルが1個も無い状態のメールは、「すべてのメール」にしか表示されません。
今回はtomatoというラベルを用意し、受信トレイとtomatoにメールが表示されるように、メールデータを作成したらラベルも同時に付与しています。
図:独自のラベルを作成しておいた
ソースコード
対象のメールのラベルIDを取得する
1 2 3 4 5 6 7 8 9 10 11 12 13 |
//ユーザラベルリストを取得 function listLabels(target) { var request = Gmail.Users.Labels.list('me'); var name, id; for (var l = 0 ; l < request.labels.length; l++) { name = request.labels[l].name; id = request.labels[l].id; if(name == target){ return id; } } } |
- targetに自分の作成したラベル名を入れる
- ラベル一覧から合致するものがヒットしたら、そのラベルIDを返す
図:ラベルIDが取得できた
添付ファイル部分を構築する処理
Gmail APIで添付ファイル部分を構築するには、ちょっと複雑な処理が必要で、JavaScriptでファイルのアップロードをする時にBoundaryというものを使って区切り、データの詳細な内容を構築し、1まとめにしたものを返す必要があります。
今回は複数添付出来るように、配列で渡して変換し、呼び出し元のContent-Typeに返すように構築した関数を用いています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
//添付ファイルの処理 function getAttachments(ids) { //boundary let boundary = "multipart/mixed; boundary=##########\r\n\r\n" let parts = "--##########\r\n" //添付ファイル格納用 let content = boundary; for (let i in ids) { let file = DriveApp.getFileById(ids[i]); let attach = 'Content-Type: ' + file.getMimeType() + '; charset=UTF-8; name="' + file.getName() + '"\r\n' + 'Content-Disposition: attachment; filename="' + file.getName() + '"\r\n' + 'Content-Transfer-Encoding: base64\r\n\r\n' + Utilities.base64Encode(file.getBlob().getBytes()) //添付ファイルを作成する content = content + parts + attach + '\r\n' + parts + parts; } return content; } |
- 配列の中にはファイルIDだけが入ってる
- それをidsで受け取って、mimetypeやfilename, base64でエンコードしたblobデータをattachにひとまとめに格納
- 格納したものにboundary文字列を前後に結合して返しています。変換結果は以下のようなものになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
multipart/mixed; boundary=########## --########## Content-Type: text/plain; charset=UTF-8; name="testtext1.txt" Content-Disposition: attachment; filename="testtext1.txt" Content-Transfer-Encoding: base64 44Go44G+44Go44G+44KT --########## --########## --########## Content-Type: text/plain; charset=UTF-8; name="testtext2.txt" Content-Disposition: attachment; filename="testtext2.txt" Content-Transfer-Encoding: base64 44Go44G+44Go44G+44KT --########## --########## |
メールデータを構築して受信トレイに入れるメインコード
前述の2つの関数を使って、メールデータに必要なデータに変換し、このメインの処理で1つのメールデータに構築し直しています。ただ、添付ファイルはGoogle Driveにアップロードしてるわけですから、そのファイルへの直リンクでもいいんじゃないかなと個人的には思うので、メールコンバータを作る際には、添付ファイルではなく、本文にファイルへのリンクを添付するだけにしようかと考えています。
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 |
//メールを生成する function usermsginsert(){ var frommail = "送信元のメールアドレス"; var tomail = "送信先のメールアドレス"; var msg_text = "ここにメール本文" //添付ファイル var attach = ["19ERTEfmJrodd9-pfhD_6mOJ8-lWt1zgv","1nE_XRB5hnBkcos-zU_VqtUgiHv4M1vMQ"] var content = getAttachments(attach); //ユーザが作成したラベルのIDを取得する var ulabel = listLabels("tomato") //メールの内容を構築する var encodedMessage = Utilities.base64EncodeWebSafe([ 'From: ' + frommail + '\r\n', 'To: ' + tomail + '\r\n', 'Subject: ' + "testman" + '\r\n', 'Content-Type: ' + content, msg_text ].join(""),Utilities.Charset.UTF_8).replace(/\+/g, '-').replace(/\//g, '_'); //新規メールを作成する var messageResource = Gmail.newMessage(); //本文等を追加する messageResource.raw = encodedMessage; //ラベルを追加する messageResource.labelIds = ["INBOX",ulabel] //メールを追加する Gmail.Users.Messages.insert(messageResource, "me"); } |
- attachには複数のファイルIDを入れて、複数添付させています。
- encodedMessageではUtilities.base64EncodeWebSafeというメソッドを使って、エンコードしたものを構築します。
- その際にメール本文は一番最後の、msg_textとして格納しておき、添付ファイルはcontent-typeに変換したデータを格納
- Gmail.newMessageで新規のメッセージを作成し、前述のencodedMessageの中身をrawに追加。
- 最後に受信トレイとtomatoというラベルを付与。ここはラベル名ではなくラベルIDなので注意。
- メール追加はGmail.Users.Messages.insertにて行い、meは自身のメールボックスの意味で指定。
- Utilities.Charset.UTF_8を指定しないと、日本語が文字化けしてしまうので、必ず追加しましょう。
メールを追加後に受信トレイで更新をクリックすると、冒頭のような自分で構築したメールがボックスに入ります。予め別のメール環境からSpreadsheetにデータ類を出力、添付ファイルはDriveにアップしてIDだけを追記、その一覧からGmailに連続的にこの処理で追加処理をすれば事実上のメールボックスの変換処理が可能になります。
※この処理を前述のNotes=>Outlookへ新機能としてNotes=>Gmailとして装備しようかなと考えています。
関連リンク
- Method: users.labels.get - Gmail API
- REST Resource: users.messages - Gmail API
- Managing Labels - Gmail API
- Google、Gmailへの古いメールのインポート・ツール2種類をオープンソース化してGitHubに公開
- google/import-mailbox-to-gmail
- google/mail-importer
- base64DecodeWebSafe(encoded) - Google Apps Script
- [Google Apps Script]メールをemlとして保存する
- ISO-2022-JP(7bit JIS)のeml形式データをApps Scriptで解読する
- gmail.users.messages.import - postman
- How to send a Gmail message using Advanced Gmail Service in GAS?
- Google GMail API w G Suite - Can I insert directly a message into a gmail user mailbox?
- Get Label ID to get a List of Messages with GMAIL API
- How to send an email with attachment using Gmail API in Node.js?
- Adding attachment to Gmail draft with Google Apps Script and Gmail API
- How can I get the body of a gmail email with an attatchment gmail python API
- Use the Gmail API to Send Emails with Attachments