Help us understand the problem. What is going on with this article?

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

はじめに

家のPCで”あるパラメータ”を探していて、日中は講義やら研究室やらにこもってしまうので、その間に家のPCがパラメータを探し終えたかどうかそわそわして中々集中できませんでした。
abs1.PNG
そこで、パラメータが見つかり次第ファイルを更新するので、そのファイルが更新されたらメールで教えてくれる助手を作ってみました。
abs2.PNG

大体の流れ

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

constitution.PNG

使用したツール当

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

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

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

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

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のフォルダパスを入力します

./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のみにしたい等)が具体的に決まっている場合はここでフィルターをかけるといいかもしれません。

./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側のアカウントにここでログインしているので、アカウントの「安全性の低いアプリのアクセス」をオンにしておく必要があります。

./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ファイルです。アップロードするファイルパスを送るだけで送ってくれます。

./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>でおくってます。

./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にコマンドを設定しておくとこの環境元で実行してくれます。

./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を呼び出して、startnodemonを呼び出すことができるようにしています。

nodemon.json

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

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

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

実行例

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

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

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

$ 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

そしてメールが届き
testfilecreate.PNG
こんなふうに更新されたファイルと保存先のurlまで表示してくれます。

今はファイル作成のときに実行されましたが、もちろんファイル更新時にも実行してくれます。testfile.txtに"END"と文字を入力して保存すると、同様のメールが届きurlにあるCloud Storageを見てみると...
end.PNG

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

さいごに

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

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

参考

Qiita - Node.jsでGmailを送信 @hanuman6

syakoo
M1/Webフロント/符号・暗号/将棋
https://syakoo-lab.work/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away