勤務先では日報をメール(Gmail)で出すこととなっているのですが、いちいちメール画面を開いて記録つけてると気が散るしいまいち編集画面が使いやすくない(※個人の感想です)ので、自分は下書きは自動バックアップ機能のあるVisual Studio Codeで書いていたり。
それだと書式が付けられないので、Markdownで清書するアプリを書いてみます。
ソースはこちら。
https://github.com/kuinaein/md-gmail/compare/0b4913e...3d66c13
なお、Node.jsでのGmail APIの呼び出し方は下記の記事にも書いたのでそのあたりは割愛します。
- Electron単体でGoogle APIを叩く
https://qiita.com/kuinaein/items/7bece719d8cd8624460c - NodeでGmailの下書きにパスワードをかけた添付ファイルを突っ込む
https://qiita.com/kuinaein/items/dd3a072f322b7b50ebdc
ライブラリのインストール
Markdownライブラリはmarkedを使うことにします。
vue init simulatedgreg/electron-vue md-gmail
cd md-gmail
yarn add --dev pug-html-loader
yarn add googleapis urlsafe-base64 moment marked html-escaper
yarn add bootstrap-honoka
Markdownプレビュー画面
Googleアカウントの認証については別の記事で書いたのでざっくり飛ばします。
Markdownの整形自体はmarkedがやってくれるので画面自体はシンプルなものです。
ただmarkedはテーブルタグに枠線を付けてくれないので、表示画面ではひとまずスタイルシートで補っています。vue-loaderは画面にない要素へのスタイル定義を削ろうとするので、「>>>」で子要素へ影響するものと指示しておきます。
<template lang="pug">
.container
textarea(v-model="md" rows="15" style="width:90vw")
p プレビュー:
div(id="formatted" v-html="formatted")
button.btn.btn-primary(type="button" @click="saveAsDraft") Gmail下書きに保存
</template>
<style scoped>
# formatted >>> table {
border-collapse: collapse;
}
# formatted >>> th, #formatted >>> td {
border: solid 1px black;
}
</style>
<script>
import moment from 'moment'
import URLSafeBase64 from 'urlsafe-base64'
import { google } from 'googleapis'
import marked from 'marked'
import { escape as escapeHtml } from 'html-escaper'
const BOUNDARY = 'MY_BOUNDARY_GX9900'
export default {
data () {
return {
md: ''
}
},
computed: {
formatted () {
return marked(this.md)
}
},
// 後略
}
</script>
Gmailの下書きへ保存
markedの出力結果を保存するだけ……なのですがいくつか注意点があります。
- Gmail APIにはRFC822に沿った生のメールデータをURLセーフなBase64形式にして渡す必要がある。
- HTMLメールの場合は
Content-Type: multipart/alternative
として送る必要がある。全体をtext/html
にしてもダメ。- ただし、テキスト形式の本文を含める必要はない。
- HTMLメールにしても<head>タグの内容は無視される。style属性等に書き換える必要がある。
また、本文のMarkdown形式でのソースも残しておきたいので、それも文中に含めることとします。
const body = this.formatted.replace(/<table>/g,
'<table border="1" style="border-collapse:collapse">') + `
<br/><br/>
<small>Markdownソース:
<textarea readonly rows="1" cols="1">${escapeHtml(this.md)}</textarea></small>
`
const subject = '【日報】' + moment().format('YYMMDD')
const email = `Subject: =?UTF-8?B?${URLSafeBase64.encode(Buffer.from(subject))}?=
Content-Type: multipart/alternative; boundary="${BOUNDARY}"
--${BOUNDARY}
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: base64
${Buffer.from(body).toString('base64')}
--${BOUNDARY}--`
gmail.users.drafts.create({
userId: 'me',
resource: { message: { raw: URLSafeBase64.encode(Buffer.from(email)) } }
})