はじめに
前回は、Gmail APIを利用してメールデータ取得してみました。
今回は、ここから件名と本文を取得してみたいと思います。
メールの内容取得
早速、メールの内容取得してみたいと思います。
使用しているAPIについては公式ドキュメントを参照してみてください。
users_messages->get()
で取得します。
1 2 3 4 5 6 7 8 9 10 11 12 13 | public function index(Request $request) { $client = $this->getClient(); $service = new Gmail($client); $user = 'me'; $messages = $service->users_messages->listUsersMessages($user); foreach ($messages->getMessages() as $message) { $message_id = $message->getID(); $message_contents = $service->users_messages->get($user, $message_id); } } |
取得したレスポンスの中身は、以下の様な構成になっています。
件名や本文は、
payload
キーで取得するMessagePartオブジェクトの中に中に入っています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | { "id": string, "threadId": string, "labelIds": [ string ], "snippet": string, "historyId": string, "internalDate": string, "payload": { object (MessagePart) }, "sizeEstimate": integer, "raw": string } |
MessagePartオブジェクト
MessagePartオブジェクトは以下の様に構成されています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | { "partId": string, "mimeType": string, "filename": string, "headers": [ { object (Header) } ], "body": { object (MessagePartBody) }, "parts": [ { object (MessagePart) } ] } |
件名
件名は
headers
キーで取得するHeaderオブジェクトに含まれています。
様々な値がオブジェクト形式で保管されているので、件名のオブジェクト取得してその値(実際の件名)を取得します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | "headers": [ { "name": "Delivered-To", "value": "sample@test.com" }, { "name": "Received", "value": "by 2002:a05:6a10:c706:b0:2f4:9a11:6104 with SMTP id m6csp843972pxw; Fri, 23 Sep 2022 20:50:08 -0700 (PDT)" }, { "name": "X-Google-Smtp-Source", "value": "AMsMyM6bfA8EtKcfhKA16TuTXF6LwqO0APXe8tjgzkYSAEGuZSpJf6djgjkZ8bNwoU0ZYKU1gt+9" }, { "name": "Subject", "value": "DX\u30bd\u30ea\u30e5\u30fc\u30b7\u30e7\u30f3\u55b6\u696d\u8077\u30fbSES~~" } ] |
1 2 3 4 | // ヘッダーの取得 $headers = $message_contents['payload']['headers']; // ヘッダーオブジェクトの配列 $subject_key = array_search('Subject', array_column($headers, 'name')); // ヘッダーオブジェクトの配列から件名オブジェクトの連番キーを取得 $subject = $headers[$subject_key]->value; // 件名のオブジェクトからvalueプロパティの値を取得(件名の取得) |
本文
本文は、
body
キーで取得する
MessagePartBody
オブジェクトの中の
data
プロパティに入っています。
1 2 3 4 5 | { "attachmentId": string, "size": integer, "data": string } |
1 2 3 | $url_safe_data = $message_contents['payload']['body']['data']; // 本文の取得 $data = str_replace(array('-', '_'), array('+', '/'), $url_safe_data); // // URL用のBase64エンコーディングされているため、文字置換 $decoded_data = base64_decode($data); // Base64デコードする |
multipartの場合
単純な構成のメールであれば上記の方法で本文取得出来ますが、Content-typeがmultipartになっている場合は取得方法が変わります。
multipartの構成については、こちらの解説が分かりやすかったです。
要はメール構成が複雑になると(画像や絵文字や添付ファイルなど)それぞれのデータ取得までの階層が深くなるようです。
MessagePartBody
も同様に、Content-typeがmultipartになっている場合
data
は空になっています。
その代わり、
MessagePart
の
parts
キーの中に、配列が格納されています。
この配列の中に、更に
MessagePart
が格納されています。
メール構成によってはこの様に、ひたすら
parts
キーの中に配列が含まれ階層が深くなる可能性があります。
通常は多くても3~4階層までいけば本文取得は出来ると思います。
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 | "body": { "attachmentId": null, "data": null, "size": 0 }, "parts": [ { "filename": "", "mimeType": "text\/plain", "partId": "0", "headers": [ { "name": "Content-Type", "value": "text\/plain; charset=UTF-8" }, { "name": "Content-Transfer-Encoding", "value": "7bit" } ], "body": { "attachmentId": null, "data": "TWVyZ2VkICM4MTMgaW50byBkZXZlbG9wLg0KDQotLSANClJlcGx5IHRvIHRoaXMgZW1haWwgZGlyZWN0bHkgb3IgdmlldyBpdCBvbiBHaXRIdWI6DQpodHRwczovL2dp~~", "size": 283 } }, { "filename": "", "mimeType": "text\/html", "partId": "1", "headers": [ { "name": "Content-Type", "value": "text\/html; charset=UTF-8" }, { "name": "Content-Transfer-Encoding", "value": "7bit" } ], "body": { "attachmentId": null, "data": "PHA-PC9wPg0KPHAgZGlyPSJhdXRvIj5NZXJnZWQgPGEgY2xhc3M9Imlzc3VlLWxpbms~~", "size": 1876 } } ] |
本文の取得
階層が深くても本文取得出来るように配列の中の
MessagePart
のmimeTypeを見て本文取得出来るまで再帰処理を行います。
(他にもっと良い方法あるかもしれません)
これでmultipartメールの本文取得が出来ました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | $body_parts = $message_contents['payload']['parts']; $body = $this->get_body($body_parts); private function get_body($body_parts) { foreach ($body_parts as $body_part) { // text/htmlなら、本文取得 if ($body_part->mimeType == 'text/html') { $url_safe_data = $body_part->body->data; $data = str_replace(array('-', '_'), array('+', '/'), $url_safe_data); return base64_decode($data); } } return $this->get_body($body_part->parts); } |
全文
最後に全文載せておきます。
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 | public function index(Request $request) { $client = $this->getClient(); $service = new Gmail($client); $user = 'me'; $messages = $service->users_messages->listUsersMessages($user); foreach ($messages->getMessages() as $message) { $message_id = $message->getID(); $message_contents = $service->users_messages->get($user, $message_id); // メッセージ内容の取得 // ヘッダーの取得 $headers = $message_contents['payload']['headers']; // ヘッダーオブジェクトの配列 $subject_key = array_search('Subject', array_column($headers, 'name')); // ヘッダーオブジェクトの配列から件名オブジェクトの連番キーを取得 $subject = $headers[$subject_key]->value; // 件名のオブジェクトからvalueプロパティの値を取得(件名の取得) // Body $size = $message_contents['payload']['body']['size']; if (!empty($size)) { $url_safe_data = $message_contents['payload']['body']['data']; // 本文の取得 $data = str_replace(array('-', '_'), array('+', '/'), $url_safe_data); // URL用のBase64エンコーディングされているため、文字置換 $decoded_data = base64_decode($data); // Base64デコードする } else { $body_parts = $message_contents['payload']['parts']; $body = $this->get_body($body_parts); } } } private function get_body($body_parts) { foreach ($body_parts as $body_part) { // text/htmlなら、本文取得 if ($body_part->mimeType == 'text/html') { $url_safe_data = $body_part->body->data; $data = str_replace(array('-', '_'), array('+', '/'), $url_safe_data); return base64_decode($data); } } return $this->get_body($body_part->parts); } |
さいごに
Gmail API を使う機会はあまりないかもしれませんが、自分が実装した時に困った部分なので、だれかの参考になれば。