Node-REDからGmail送信する方法(GoogleアカウントのGmail送信/G SuiteでのGmail送信)

Node-RED経由でGmailを送信しようとしましたが色々と苦しんだので忘れないために記事を投稿します。

# はじめに
「Node-RED Gmail」とググって出てくる記事では、

- 無償のGoogleアカウントのGmail送信(G SuiteではないGmailからのメール送信)がほとんど
- 有償のG Suiteを利用した場合のGmail送信についてはほぼなし
    - JavaScriptなど他のプログラムを利用した内容はありますがNode-REDでの記事はほぼない

といった状況でした。
そのため上記2点についてのメール送信方法をまとめて整理します。
また有償のG SuiteアカウントでのGmail送信と無料のGoogleアカウントでのGmail送信と言葉が頻繁に出てきてややこしいため、以降の記事では文言を下記のように統一します。

有償のG SuiteアカウントでのGmail送信　→　G SuiteのGmail送信
無償のGoogleアカウントでのGmail送信　→　通常利用のGmail送信

# 双方の比較
G SuiteでないGmailのメール送信とG SuiteでのGmail送信では色々と手段が違います。
簡単にですが表にしてまとめました。

| 比較内容 | G SuiteのGmail送信 | 通常利用のGmail送信 |
|:-:|:-:|:-:|
| Node-REDで利用するノード  | httpノード  | emailノード  |
| メール送信方法  | Gmail API  | アプリパスワード利用 or 外部アプリ利用許可 |
| Googleアカウントでの作業 | Google Developersへの登録  | アプリパスワードの取得(2段階認証の場合)  |

またG SuiteのGmail送信の場合は通常利用のGmail送信で行う方法ではメール送信できませんが、その逆は可能となります。

そのため、よく紹介されるアプリパスワードを利用したメール送信ではなくGmail APIでのメール送信するほうが双方利用できます。
ただし、Gmailでの送信上限があるため確認が必要です。上限については[こちら](https://support.google.com/a/answer/166852?hl=ja)を参考にしてください。


# 通常利用のGmail送信
通常利用のGmail送信については、emailノードを利用してメール送信することが可能です。
サンプルフローについては以下の通りです。

フロー図
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283423/0590ebb3-26c2-99b2-8274-badc95c9305a.png)

ソースコード

```
[{"id":"99691d2e.cc8c6","type":"inject","z":"c457baa8.e7a2c8","name":"メール送信ノード","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":170,"y":360,"wires":[["d67f8a69.ebe6c8"]]},{"id":"d67f8a69.ebe6c8","type":"function","z":"c457baa8.e7a2c8","name":"メール設定","func":"msg.topic=\"メール件名\"\nmsg.payload=\"メール本文\"\nreturn msg;","outputs":1,"noerr":0,"x":370,"y":360,"wires":[["3a25c51c.292cca"]]},{"id":"3a25c51c.292cca","type":"e-mail","z":"c457baa8.e7a2c8","server":"smtp.gmail.com","port":"465","secure":true,"tls":true,"name":"example_from@gmail.com","dname":"メール送信","x":550,"y":360,"wires":[]}]
```

構成は非常に簡単で、functionノードにはmsg.topicとmsg.payloadしか設定していません。

functionノード設定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283423/371bc67e-4d6b-ad66-8234-8f807f5f1f95.png)

emailノードについては以下の設定を行っています。

- 宛先
- ユーザID
- パスワード

上記設定以外はデフォルトの設定となります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283423/8077a8f0-64a8-6e6a-d90a-85dcd4b516eb.png)

ここのユーザIDとパスワードをGoogleアカウントのアドレス、パスワードを指定します。
しかし正しいパスワードを設定して実行しても以下のエラーが出力されます。

```
Error: Invalid login: 535-5.7.8 Username and Password not accepted. Learn more at535 5.7.8  https://support.google.com/mail/?p=BadCredentials i9sm105976223pgo.46 - gsmtp
```

Node-REDで実行したフローが設定したGoogleアカウントへログインしてメール送信しようとしますが、GoogleアカウントからはNode-REDのフローが安全性の低いアプリとして認識され、ログインが許可されないためエラーが表示されます。

エラー解決方法については、基本的には[メール送信の問題解決方法](https://support.google.com/mail/?p=BadCredentialsGoogle)の内容を実行すれば解決します。
基本的にはアカウントで2段階認証を設定しているかで変わります。
2段階認証を設定している場合はアプリパスワードを設定し、2段階認証を設定していない場合は、安全性の低いアプリがアカウントにアクセスすることを許可します。

ただしGoogleアカウントからすれば上記方法はセキュリティ的によろしくないらしく、アカウント設定ページ上では「セキュリティの問題が見つかりました」と表示されます。

ちなみにですが、G Suiteアカウントの場合でもアプリパスワードは表示されますが、なぜかメール送信ができません。実際に試してみると上記エラーが表示されたまま終了します。
そのためG Suiteアカウントでは次のメール送信方法を利用します。


# G SuiteのGmail送信
G SuiteのGmail送信の場合httpノードを利用しますが、emailノードに比べて設定が色々と必要になります。

フロー図
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283423/0d0e4c71-c666-2aca-3f5d-526fe4617d8a.png)

ソースコード

```
[{"id":"ecff67e9.e347b8","type":"function","z":"c457baa8.e7a2c8","name":"メール設定","func":"// 件名を日本語にするための関数\nfunction btoa(str) {  \n  var buffer;\n  if (Buffer.isBuffer(str)) {\n    buffer = str;\n  }\n  else {\n    buffer = new Buffer(str.toString(), 'binary');\n  }\n\n  return buffer.toString('base64');\n}\n\n// メールヘッダー設定\nmsg.headers  = {\n    \"Accept\": \"application/json\",\n    \"Content-Type\": \"application/json\",\n    \"Authorization\" : \"Bearer <Access Tokenキーを設定>\"\n}\n\n// メール本文の設定\nmsg.raw = \n[\n            \"Content-Type: text/plain; charset=UTF-8\",\n            \"MIME-Version: 1.0\",\n            \"from: example_from@gmail.com\",\n            \"Content-Transfer-Encoding: 7bit\",\n            \"Subject: =?utf-8?B?\" + btoa(unescape(encodeURIComponent(メール件名))) + \"?=\",\n            \"\",\n            \"メール本文\"\n].join('\\n').trim();\n\nlet buffer = new Buffer(msg.raw);\n// メール本文をbase64に変換\nmsg.raw = buffer.toString('base64').replace(/\\+/g, '-').replace(/\\//g, '_');\n\nmsg.payload = {\n    \"raw\" : msg.raw\n}\nreturn msg","outputs":1,"noerr":0,"x":370,"y":460,"wires":[["fb990f99.73716"]]},{"id":"fb990f99.73716","type":"http request","z":"c457baa8.e7a2c8","name":"メール送信","method":"POST","ret":"txt","paytoqs":false,"url":"https://www.googleapis.com/gmail/v1/users/me/messages/send","tls":"","proxy":"","authType":"","x":550,"y":460,"wires":[["ef7ec299.df8a9"]]},{"id":"fb520313.d2367","type":"inject","z":"c457baa8.e7a2c8","name":"メール送信ノード","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":170,"y":460,"wires":[["ecff67e9.e347b8"]]},{"id":"ef7ec299.df8a9","type":"debug","z":"c457baa8.e7a2c8","name":"メール送信結果出力","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":760,"y":460,"wires":[]}]
```

functionノードの設定がかなり多くなっています。実際に利用するのはmsg.headerとmsg.payloadのみですが、色々と工夫が必要となります。

functionノード設定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283423/4639ea1d-cd3a-aef4-4e44-31fd46592921.png)


httpノードを利用するため、ヘッダー情報をmsg.headerで定義しています。
アクセストークンについてはこの方のブログでわかりやすくまとめられています
[Google APIを使用するためにGoogle OAuth認証をしようよ - ひま缶](https://himakan.net/websites/how_to_google_oauth)

msg.rawでGmail APIでメールを送信する際の設定を記載しています。
基本的にはfrom,Subject(件名)とメールヘッダーの設定のみで送信可能となります。
ただしSubjectについては日本語が文字化けしてしまうため、`btoa(unescape(encodeURIComponent`の部分で日本語化しています。
画像では省略していますが上部に関数を設定していますのでその内容を参考にしてください。

またメール送信形式はbase64でないといけないため変換をかけています。
変換後msg.payload内部のrawでメール設定を記載しています。この辺りは公式リファレンスのJavaScript項目と同様となります。

httpノードについては特段認証設定をしていません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283423/05e43342-b3e1-ab2d-0844-b4c4bdb5ff2d.png)


設定をPOST、URL欄を`https://www.googleapis.com/gmail/v1/users/me/messages/send`としています。
実行すると、msg.payload.labelIdsにSENTといったパラメータが表示されます。

失敗するとerrorとしてエラー内容が表示されます。よくあるエラーについてはAccess Tokenキーの期限が切れているため送信できないパターンとなります。

# 最後に
上記内容でGmailのメール送信については可能となります。
もちろん内容はサンプルプログラムですので、その都度修正して利用ください。
Node-REDでemailフローが利用できるため、ここの箇所で引っかかる人が多いと思いますので、是非参考にしていただけるとありがたいです。