ファイルが更新されたらメールで教えてくれる助手を作ってみた(nodemon)

# はじめに
家のPCで”あるパラメータ”を探していて、日中は講義やら研究室やらにこもってしまうので、その間に家のPCが**パラメータを探し終えたかどうか**そわそわして中々集中できませんでした。
<img width="500" alt="abs1.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398510/f2d680bd-e4fe-6c01-ff01-fe756b1cc44b.png">
そこで、パラメータが見つかり次第ファイルを更新するので、その**ファイルが更新されたらメールで教えてくれる助手**を作ってみました。
<img width="500" alt="abs2.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398510/de7c001b-01e7-3d69-0d08-cf10f6792ff1.png">


# 大体の流れ
上で説明しているように、ファイル更新をトリガーにしてメールを送信するのを目的としています。また外出時にもファイルの中身が見れるように、メール送信時にファイルをクラウド上にあげてもらいます。

<img width="100%" alt="constitution.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398510/bab08db5-90d5-6d3c-88d2-68cba9bef4e9.png">


# 使用したツール当
使用したツール等を簡単に説明します。

### 1. nodemon (公式: https://nodemon.io/)

以前も紹介しました (以前: [ホットリロードで毎回ビルドする方法(nodemon)](https://qiita.com/syakoo/items/86f80b823bad019e2083))。
簡単に言うと、フォルダを監視して変更され次第コマンドを叩いてくれます。

今回はこれを**監視役**として使います。

### 2. fs

これはJS(TS)のファイルを操作するライブラリです。フォルダの中のファイルを拾ってもらいます。

### 3. Firebase (Cloud Storage)

更新されたファイルの転送先のクラウドストレージです。選んだ理由としては、個人的に慣れていたからです。今回はストレージのみを使用します。

### 4. nodemailer

Gmailを送信するためのライブラリです。非常に楽に送信できたので助かりました。


# ディレクトリ構成とソースコード

実装したコード等を具体的に紹介しますが、firebaseのconfigファイル等のファイルは見せられないので、**Githubには公開していません。**

ディレクトリ構成

```
|
|--node_modules
|--src
|  |--utils
|  |  |--firebase.ts
|  |  |--config.json
|  |
|  |--assistant.ts
|  |--getFile.ts
|  |--mailer.ts
|  |--uploader.ts
|--nodemon.json
|--package.json 
```

各コードを紹介していきます。

### firebase.ts

firebaseのCloud Storageにアクセスする為のtsファイルです。`storageBucket`に自分のCloudStorageのフォルダパスを入力します

```ts:./src/utils/firebase.ts
import * as firebase from 'firebase-admin'

const serviceAccount = require('./config.json')

firebase.initializeApp({
  credential: firebase.credential.cert(serviceAccount),
  storageBucket: 'XXXXXXXXXX.appspot.com'
})

export const bucket = firebase.storage().bucket()

```

### getFile.ts
対称のディレクトリにあるファイル名を全て取得してくれるtsファイルです。
取得したいファイル(例：`.txt`のみにしたい等)が具体的に決まっている場合はここでフィルターをかけるといいかもしれません。

```ts:./src/getFile.ts
import * as fs from 'fs'

export const getFileList = (folderPath: string) => {
  let fileList: string[] = []
  fileList = fs.readdirSync(folderPath)
  fileList = fileList.map(file => folderPath + file)
  return fileList
}
```


### mailer.ts

メールタイトルと本文を入力するだけでメールを送信してくれるtsファイルです。
**sender側のアカウントにここでログインしているので、アカウントの「安全性の低いアプリのアクセス」をオンにしておく必要があります。**

```ts:./src/mailer.ts
import * as mailer from 'nodemailer'

const senderEmail = 'SENDER_EMAIL@gmail.com'
const senderPass = 'SENDERPASS'
const receiverEmail = 'RECEIVER_EMAIL@gmail.com'

const smtpConfig = {
  host: 'smtp.gmail.com',
  port: 465,
  secure: true,
  auth: {
    user: senderEmail,
    pass: senderPass
  }
}

export const mailSend = (title: string, text: string) => {
  const smtp = mailer.createTransport(smtpConfig)
  const mailOptions = {
    from: senderEmail,
    to: receiverEmail,
    subject: title,
    html: text
  }

  smtp.sendMail(mailOptions, (err, info) => {
    if (err) {
      console.log(err)
    } else {
      console.log(`Message sent: ${mailOptions.subject}`)
    }
    smtp.close()
  })
}

```

### uploader.ts

さっき設定したCloud Storageに保存するtsファイルです。アップロードするファイルパスを送るだけで送ってくれます。

```ts:./src/uploader.ts
import { bucket } from './utils/firebase'

export const uploadFile = (filePath: string) => {
  try {
    bucket.upload(filePath)
    console.log('The file was updated successfully')
  } catch (err) {
    console.log(err)
  }
}

```

### assistant.ts

全体の処理をしてくれるメインファイルです。gmailには簡単なHTMLが埋め込めるとの事なので、アップロードしたファイルをリスト形式`<li>`でおくってます。

```ts:./src/assistant.ts
import { mailSend } from './mailer'
import { getFileList } from './getFile'
import { uploadFile } from './uploader'

const message = (fileList: string[]) => {
  const filesText = fileList.map(file => {
    return `<li>${file}</li>`
  })

  return `
    <h1>ファイル更新されました</h1>
    <h3>以下のファイルが更新されました。https://console.firebase.google.com/u/0/project/XXXXXXXXXXX/storage/XXXXXXXXXXX.appspot.com/files~2F を確認してください。</h3>
    <div style="margin: 0.5rem;">
        ${filesText}
    </div>
    `
}

const report = () => {
  // 監視対象のフォルダ
  const targetFolder = './targetFolder/'
  // 対称のフォルダにあるファイルを取得
  const fileList = getFileList(targetFolder)
  // ファイルを全てCould Storageにアップロード
  fileList.forEach(file => {
    console.log({ file })
    uploadFile(file)
  })
  // 完了メールを送る。
  mailSend('ファイルが更新されました。', message(fileList))
}

report()

```

`report()`で対称のフォルダにあるファイルを取得 -> Could Storageにアップロード -> メールを送信をしています。

つまり、**監視対象のフォルダが変更され次第**このファイルを呼び出せばいいわけですね。


### package.json

意外と大事なファイルで、`scripts`にコマンドを設定しておくとこの環境元で実行してくれます。

```json:./package.json
{
  "name": "myAssistant",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "report": "ts-node ./src/assistant.ts",
    "start": "nodemon"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@types/nodemailer": "^6.2.2",
    "firebase-admin": "^8.8.0",
    "nodemailer": "^6.3.1",
    "nodemon": "^2.0.1",
    "ts-node": "^8.5.4",
    "typescript": "^3.7.2"
  }
}
```

`scripts`を見るとわかりますが、`report`でさっきの`assistant.ts`を呼び出して、`start`で`nodemon`を呼び出すことができるようにしています。


### nodemon.json

フォルダーを監視して実行してくれる設定ファイルです。

```json:./nodemon.json
{
    "watch": ["./targetFolder/"],
    "ext": "*",
    "exec": "npm run report"
}
```

`watch`に監視対象のフォルダ、`exec`に変更時に実行するコマンドを設定します。



# 実行例

監視対象フォルダは**相対パスでも絶対パスでも可能**です。今回はCドライブ直下に`target`というフォルダを作成してやってみます。

`./nodemon.json`と`assistant.ts`の監視対象フォルダに設定している箇所を`C:\\target\\`に変更します。

そして、コンソールでnodemonを実行します。`./package.json`で設定している`start`を叩きます。

```console
$ npm run start
```

するとnodemonで`assistant.ts`が一度呼び出されます。次に試しに監視対象フォルダである`C:\\target\\`に新しく`testfile.txt`というファイルを作成すると...

```
> myAssistant@1.0.0 report C:\Users\hogehoge\Documents\workspace\myAssistant
> ts-node ./src/assistant.ts

{ file: 'C:\\target\\testfile.txt' }
The file was updated successfully
Message sent: ファイルが更新されました。
[nodemon] clean exit - waiting for changes before restart
```

そしてメールが届き
<img width="608" alt="testfilecreate.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398510/466aa3d8-b8f7-8cd6-ffad-9706b824d9da.png">
こんなふうに更新されたファイルと保存先のurlまで表示してくれます。


今はファイル作成のときに実行されましたが、もちろんファイル更新時にも実行してくれます。`testfile.txt`に"END"と文字を入力して保存すると、同様のメールが届きurlにあるCloud Storageを見てみると...
<img width="498" alt="end.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398510/7d3220b1-d815-a322-5307-5f3436e2b3a1.png">

変更が更新されているのが確認できましたので、これで終わります。

# さいごに

助手の名前や本文を変えたり、メールではなくLINEにしたり色々楽しめそうではありますが、時間に余裕がないのでこれで満足しておきます...

出力先のディレクトリを一般向けにしてオープンソースとして公開するという方法もありそうですね。

# 参考

[Qiita - Node.jsでGmailを送信 @hanuman6](https://qiita.com/hanuman6/items/4a5ba155037cab115fef)