GmailのAPIを触ってみてますが、メールによって取得できるパターン取得できないパターンがありました。
まずはどんなコードか
"Google公式ライブラリを利用してNode.jsからGmailの送受信をしてみよう"などを参考にコードを書いてメール取得をしてみています。
users.messages.list()
でメールを検索し、users.messages.get()
でメール本文を取得します。
//省略
const gmail = google.gmail({version: 'v1', auth});
//1件検索
const resList = await gmail.users.messages.list({userId: 'me', q: 'from:hoge@hoge.com'});
const lastMessage = resList.data.messages[0];
//検索したメールIDで中身を取得
const res = await gmail.users.messages.get({
userId: 'me',
id: lastMessage.id,
format: 'FULL'
});
const buf = new Buffer.from(res.data.payload.body.data, 'base64');
const str = buf.toString();
console.log(str,str.length);
//省略
通常だと、res.data.payload.body.data
にメール本文が入ってきて、メール本文がコンソールに表示されます。
メールが空の場合がありました。
Gmailなどのメーラー的にはちゃんと表示されてるのですが、このコードのconsole.log
で表示がされないケースがありました。
こんな感じのエラーです。
TypeError [ERR_INVALID_ARG_TYPE]: The first argument must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object. Received undefined
new Buffer.from(res.data.payload.body.data, 'base64');
と指定している第1引数に文字列を入れろと怒られてます。
console.log(resMes.data.payload);
で調べてみると{ size: 0 }
となっていました。
body: { size: 0 },
parts: [
{
partId: '0',
mimeType: 'text/plain',
filename: '',
headers: [Array],
body: [Object]
},
{
partId: '1',
mimeType: 'text/html',
filename: '',
headers: [Array],
body: [Object]
}
]
さらにbodyではなく、partsの方にBodyが入ってそうです。
console.log(resMes.data.payload.parts[0]);
などでみてみるとやはり入ってます。
{
partId: '0',
mimeType: 'text/plain',
filename: '',
headers: [
{ name: 'Content-Type', value: 'text/plain; charset=utf-8' },
{ name: 'Content-Transfer-Encoding', value: 'base64' }
],
body: {
size: 11661,
data: '44GT44KT44Gr44Gh44Gv44CB44G144KL44GV44Go44OB44On44Kk44K544...
payload.bodyではなくpayload.partsに本文がいるケースがある
ということで、bodyではなくparts側に本文のbase64文字列が格納されている場合があります。
何個か試してみましたが、HTMLメールなどはそうなってるのかもしれません。
GoogleのAPI側でその辺吸収してくれても良いのになぁ...
ということで、bodyが空の場合はpartsからbase64文字列をchunkとして取得して、partsの数だけ結合するようにしました。
let mailData = '';
//bodyが場合
if(resMes.data.payload.body.size !== 0){
mailData = resMes.data.payload.body.data;
}
//bodyが空の場合はpartsから取得
else{
const parts = resMes.data.payload.parts;
for (let i = 0, len=parts.length; i < len; i++) {
if(parts[i].body.size === 0) continue;
const chunk = parts[i].body.data;
mailData += chunk;
}
}
const buf = new Buffer.from(mailData, 'base64');
const str = buf.toString();
これで今のところbodyが空の場合のメール本文も取得することができています。
まとめ
メールの仕様なんですかね?
APIの返却値がpayload.parts
の奥にある場合と、payload.data
に存在する場合があるという話でした。
Gmail API触ってる記事はまだまだ少ない気がするので参考になれば幸いです。