TL;DR
メールが届いたらこんな感じのメッセージをslack/mattermostにpostする仕組みを作りました.
はじめに
slack / mattermostを始めとするチャットツールは普及してきていますが,様々な事情により,完全にメールを使わないで仕事をすることは難しいと思います.とはいえ定期的にメーラをチェックするのもめんどくさいですね.なので,メールが届いたらその内容をslack / mattermostに通知し,通知内容を見て必要があれば返信すればよいのではと考えました.
何らかの事情によりメールを見る環境と普段作業をする環境が分断されている方にもおすすめです.1
前提
- メーラとしてThunderBirdを利用していること
具体的な手順
incoming webhookの取得
今回は届いたメールの内容をpostするため,incoming webhookを取得します.具体的な手順は既に解説記事がたくさんあるので省略します.
slackの場合はこちら,mattermostの場合はこちらをご参照ください.
FiltaQuillaの導入
ThunderBirdのアドオン,FiltaQuillaをインストールします.
FiltaQuillaは,メール受信時のフィルタやそれに対するアクションを拡張するアドオンです.フィルタ条件として正規表現が使えるため,その用途で利用されることが多いですが,今回はアクションとして任意のスクリプトを実行できる機能を主に利用します.
上記のアクション機能は,デフォルトではオフになっています.そのため, ハンバーガーメニュー > アドオン > FiltaQuilla
から,JavaScript Action with Body
にチェックを入れ,Thunderbirdを再起動します.
ハンバーガーメニュー > メッセージフィルター > メッセージフィルター
より,新しくフィルタを作り,以下の動作を実行する
の選択肢の中に Javascript Action with Body
があればOKです.アクション選択の右側にあるアイコンをクリックするとウインドウが開き,任意のjavascriptを書くことができます.
例えば,以下のようなプログラムを書くと,受信したメールのタイトルの前に [Hello, World]
とprefixをつけることができます.
for (let index = 0; index < msgHdrs.length; index++){
let hdr = msgHdrs.queryElementAt(index, Ci.nsIMsgDBHdr);
hdr.subject = "[Hello, World] " + hdr.subject;
}
ちなみに,アクションの選択肢の中には Run Program
というものがあり,ローカルにある任意の実行ファイルを実行することができます.しかしこの動作は,メールのヘッダを受信した段階で起動するようで,プログラムに対してヘッダに関する情報を渡すことができますが,本文等のヘッダに含まれない情報は渡すことができません 参考.
一方, JavaScript Action with Body
に関しては,公式サイトに以下のような記述があります.
The “Javascript Action with Body” delays the application of the filter until the body has been downloaded.
つまり,メールの受信が完了してからJavascriptが起動するようなので,こちらのアクションを使えばメール本文を取得することができます.
実行するjavascriptを書く
ここからは上記のアクションに指定するjavascriptを書いていきます.とりあえず動かしたい方は最終的なjavascriptコードまで読み飛ばしてください.
コードを実行して動かなくなった場合には,開発ツールボックス (Ctrl + Shift + I) の コンソール
タブを開くとデバッグしやすいかと思います.
postする要素の取得
まずhdrオブジェクトを取得します.
let hdr = msgHdrs.queryElementAt(index, Ci.nsIMsgDBHdr);
hdrオブジェクトにはメールヘッダに関する情報が含まれており,簡単にSubjectやAuthor, Cc, Bcc情報を取得することができます.これらの他に取得できる項目については公式ドキュメントを参照してください.
subject = hdr.mime2DecodedSubject
author = hdr.mime2DecodedAuthor
cc = hdr.ccList
bcc = hdr.bccList
hdrオブジェクトの要素として,hdr.subject
や hdr.author
がありますが,これらの要素にはMIMEエンコードされた文字列が格納されています.そのため,日本語が含まれる件名や長い件名を扱う場合には hdr.mime2DecodedSubject
, hdr.mime2DecodedAuthor
を使う点に注意してください.
何かの事情でMIMEデコードをする場合にはこちら や こちら を利用するのが良いと思います.
bodyについては,このサイトを参考に,以下のようにして取得しました.
let messenger = Components
.classes["@mozilla.org/messenger;1"]
.createInstance(Components.interfaces.nsIMessenger);
let listener = Components
.classes["@mozilla.org/network/sync-stream-listener;1"]
.createInstance(Components.interfaces
.nsISyncStreamListener);
let uri = hdr.folder.getUriForMsg(hdr);
messenger.messageServiceFromURI(uri)
.streamMessage(uri, listener, null, null, false, "");
let body = hdr.folder.getMsgTextFromStream(listener.inputStream,
hdr.Charset, 65536, 32768, false, true, {});
postするjsonの作成
取得した要素を並べてpostするjsonを作成します.mattermostの場合だとmarkdown記法を利用することもできます.
const data = {
"text": "件名:" + subject + "\n"
+ "送信元: " + author + "\n"
+ "Cc: " + cc + "\n"
+ "Bcc: " + bcc + "\n"
+ "本文\n"
+ body
};
postする
XMLHTTPRequestを用いてデータをpostします.mattermostでうまく動かない場合は,HTTPヘッダの content-type
に application/json
を指定していないのが原因かもしれません.
const xml = new XMLHttpRequest();
xml.open("POST", url, false);
xml.setRequestHeader("content-type", "application/json");
xml.send(`${JSON.stringify(data)}`)
最終的なjavascriptコード
最終的には下記のようなjavascriptコードになります.デバッグをしやすくするため,consoleへ出力するコードも残してあります. 利用される際は,1-2行目の url
を取得したwebhook urlに置き換えてください.
const url = "https://your.mattermost.url/hooks/xxxxxxxxxxx"; // mattermost
const url = "https://hooks.slack.com/services/xxxxxxxxxxx"; // slack
for (let index = 0; index < msgHdrs.length; index++) {
let hdr = msgHdrs.queryElementAt(index, Ci.nsIMsgDBHdr);
// subject
subject = hdr.mime2DecodedSubject
Cu.reportError(" flt log1: " + subject);
// message body
let messenger = Components
.classes["@mozilla.org/messenger;1"]
.createInstance(Components.interfaces.nsIMessenger);
let listener = Components
.classes["@mozilla.org/network/sync-stream-listener;1"]
.createInstance(Components.interfaces
.nsISyncStreamListener);
let uri = hdr.folder.getUriForMsg(hdr);
messenger.messageServiceFromURI(uri)
.streamMessage(uri, listener, null, null, false, "");
let body = hdr.folder.getMsgTextFromStream(listener.inputStream,
hdr.Charset, 65536, 32768, false, true, {});
Cu.reportError(" flt log2: " + body.length + " " + body);
// author
author = hdr.mime2DecodedAuthor
Cu.reportError(" flt log3: " + author);
// Cc, Bcc
cc = hdr.ccList
bcc = hdr.bccList
// formatting data
const data = {
"text": "## 件名:" + subject + "\n"
+ "## 送信元: " + author + "\n"
+ "## Cc: " + cc + "\n"
+ "## Bcc: " + bcc + "\n"
+ "## 本文\n"
+ body
};
// POST
const xml = new XMLHttpRequest();
xml.open("POST", url, false);
xml.setRequestHeader("content-type", "application/json");
xml.send(`${JSON.stringify(data)}`)
}
動かしてみる
プロジェクトA
が件名に含まれる場合にアクションを起こすよう設定して,メールを送ってみます.すると…
さらに,mattermostではmarkdown記法が使えるので,もうちょっとリッチな表現にすることができます.例えばpostするjsonを書き換えて,緊急
という文字列がタイトルに含まれているときにこのようなデータをpostする,という新しいルールを加えると...
const data = {
"attachments": [{
"color": "#FF8000",
"text": "件名: " + subject + "\n"
+ "送信元: " + author + "\n"
+ "Cc: " + cc + "\n"
+ "Bcc: " + bcc + "\n"
+ "本文\n"
+ body
}]
};
まとめ
ThunderbirdのプラグインFiltaQuillaを使って,slack/mattermostに新着メールの情報をpostする方法について説明しました.本記事では説明しませんでしたが,postするチャンネルを条件によって変えて,そのチャンネルへのpostだけスマホに通知するようにすれば,重要なメールが来たときにだけスマホを鳴らすようにすることもできます.また,今回はslack/mattermostへの通知でしたが,FiltaQuillaを使えば任意のjavascriptを動かすことができるため,もっと面白い使い方があるかもしれません.
参考サイト
- FiltaQuilla: https://addons.thunderbird.net/ja/thunderbird/addon/filtaquilla/
- https://superuser.com/questions/210047/script-thunderbird-filters
- https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIMsgDBHdr
-
自己責任でお願いします. ↩