IMAPでGmailを受信する(Node.js編)

ユーザ登録のフロー（メアド入力、メールのURL開く、ユーザ情報入力）をE2Eテストしたかったのですが、gmailをプログラムで受信する検証をしていました。

## 要件
- 2017-01-01以降に送信され、送信者に「corec.jp」を含むメール
- gmailアカウントやパスワードは .env ファイルから読み込む
- vscodeでdebugできる

## 準備
- gmailの設定で「IMAPを有効」及び「安全性の低いアプリからのアクセスの許可」をオンにしておく

## 検証環境

- Windows10
- chocolateyでnode10系をインストール

```
node -v
v10.16.0
```

- 文字エンコーディングがiso-2022-jpのメールをUTF-8に変換するのに iconv を使った。
  - simpleParserはiso-2022-jpに対応していないiconv-liteを使っている。
- Windowsでiconvをインストールするには下記のビルドツールを事前にインストールする必要があった。
  - python2.7がインストールされる。python3系がすでにインストール済みの場合は注意が必要かも。

```sh
npm install --global --production windows-build-tools
npm install -g node-gyp
```

## ソースコード

```json:package.json
{
  "name": "imap-gmail-sample",
  "version": "1.0.0",
  "description": "imap-gmail-sample",
  "main": "gmail.js",
  "scripts": {
    "start": "env-cmd node gmail.js"
  },
  "keywords": [],
  "author": "oharato",
  "license": "MIT",
  "dependencies": {
    "env-cmd": "^9.0.3",
    "iconv": "^2.3.4",
    "imap-simple": "^4.3.0",
    "mailparser": "^2.7.1"
  }
}
```

```json:.vscode/launch.json
{
    "version": "0.2.0",
    "configurations": [
        {
            "type": "node",
            "request": "launch",
            "name": "nodejs",
            "envFile": "${workspaceFolder}/.env",
            "program": "${workspaceFolder}/gmail.js"
        }
    ]
}
```

```:.env.sample
ACCOUNT=example@gmail.com
PASSWORD=example
```

```javascript:gmail.js
// https://github.com/chadxz/imap-simple
const imaps = require('imap-simple');
const simpleParser = require('mailparser').simpleParser;
const iconv = require('iconv');

const config = {
  imap: {
    user: process.env.ACCOUNT,
    password: process.env.PASSWORD,
    host: 'imap.gmail.com',
    port: 993,
    tls: true,
  }
};
// https://github.com/mscdex/node-imap#connection-instance-methods
const searchCriteria = [['FROM', 'corec.jp'],['SINCE', '2017-01-01']];
// https://github.com/chadxz/imap-simple#usage-of-mailparser-in-combination-with-imap-simple
const fetchOptions = {bodies: ['']};

(async()=>{
  const connection = await imaps.connect(config);
  await connection.openBox('INBOX');
  const messages = await connection.search(searchCriteria, fetchOptions);
  console.log('search results: '+messages.length);
  for(let message of messages){
    const part = message.parts.find(part => part.which === '');
    // https://github.com/chadxz/imap-simple#usage-of-mailparser-in-combination-with-imap-simple
    const mail = await simpleParser(`Imap-Id: ${message.attributes.uid}\r\n${part.body}`);
    // simpleParserはiconv-liteを使っているのでiso-2022-jpを正しく変換できない
    // charsetを見てiconvで変換
    const match = mail.headerLines.find(o => o.key === "content-type").line.match(/charset="(.+)"/);
    const charset = match ? match[1] : null;
    if(charset){
      const conv = new iconv.Iconv(charset, "UTF-8");
      if(mail.subject) console.log('subject: ' + conv.convert(mail.subject).toString());
      if(mail.text) console.log(conv.convert(mail.text).toString());
      // if(mail.html) console.log(conv.convert(mail.html).toString());
    }else{
      console.log('subject: ' + mail.subject);
      console.log(mail.text);
      // console.log(mail.html);
    }
    // break;//テスト用に1件だけで止めたいときにコメントアウト外す
  }
  await connection.end();
})();
```


## 実行

```sh
cp .env.sample .env
# .env を任意のエディタで開き、ACCOUNTとPASSWORDを編集する

npm install
npm start
```

## 参照

さくっと手元で試したい人はこちらをgit cloneしてどうぞ
https://github.com/oharato/imap-gmail-sample