node-red
Node-REDDay 16

超初心者がNode-REDでnodered.orgの更新情報をSlackに通知するアプリを作った

More than 3 years have passed since last update.


はじめに


誰向けの記事か

この記事は、Node-RED超初心者が作成しており、今からNode-REDを始めようとしている人向けです。


この記事で使用している環境


  • Mac OS X Yosemite

  • Node-RED version: v0.12.0

  • Node.js version: v0.12.7


具体的にどういうアプリを作ったのか

http://nodered.org/ の1日分の更新情報をLocalのNode-REDから取得し、更新日時と更新タイトルを、

スクリーンショット 2015-12-16 14.03.44.png

のようにSlackの#generalチャンネルに通知するアプリを作成しました。


完成形

スクリーンショット 2015-12-16 14.31.55.png


作成手順

作成手順はおおまかに以下です。


  1. 前回の最終更新日時をファイルから取得する

  2. master.atomから各エントリーの更新日時を取得する

  3. 前回の最終更新日時以降の更新エントリを抽出しSlackに投げる

  4. 今回の最終更新日時をファイルに保存する


1. 前回の最終更新日時をファイルから取得する

このアプリでは前日のチェック以降、本日までに更新があったかどうかを通知するため、前日チェック時の更新情報と現在の更新情報との差分を見る必要があります。

手順1ではファイルにすでに前日チェック時の最終更新日時が記入されているとします。手順4にて現在の最終更新日時に上書きするフローを作成します。

スクリーンショット 2015-12-16 15.41.35.png

最終更新日時は'last_updated'というファイルに保存します。このファイルには、


2015-12-14T10:03:52Z


の形で日時が保存されています。

この日時を取得し、node-red-contrib-momentでDate形式に変換します。変換した日時をmsg.lastupdatedに収納します。


2. master.atomから各エントリーの更新日時を取得する

スクリーンショット 2015-12-16 15.55.30.png

次に、http://nodered.org/ の本体 https://github.com/node-red/node-red.github.io の更新履歴が保存されているURLからxmlを取得します。

xmlノードでJSONに変換しvalidatorで中身を確認すると、各更新記事がentryキーに紐付いていることがわかるので、splitterノードでentryをキーとして各記事を分けます。

分けた更新記事それぞれの更新日時を文字列の形でmsg.updatedに入れた後、Date型に変換します。


3. 最終更新日時以降の更新エントリを抽出しSlackに投げる

スクリーンショット 2015-12-16 16.17.40.png

各更新の更新日時が分かったところで、最終更新日時との比較を行います。Date形式で日時を持っているので、単純に比較するだけでOKです。最終更新日時以降に更新された記事のみswitchノードで抽出します。

Slackには、更新日時と更新の簡単な内容を通知したいので、functionノードでメッセージを加工してSlackに投げます。


4. 今回の最終更新日時をファイルに保存する

スクリーンショット 2015-12-16 16.21.27.png

最後に、今回チェックした最終更新日時をファイルに記入します。次回はここで記入した日時を使って更新の差分を抽出することになります。

手順2で取得したJSONの中に、全体の最終更新日時の記述がありますので、それを取得したのち文字列に変換して'last_updated'のファイルを上書きします。

これで次回は、今回取得した最終更新日時以降の更新が抽出できるようになりました。


最後に

このアプリは、nodered.orgの和訳を行う中で必要になったもので、更新を見逃して古い記事を和訳してしまった苦い経験から生まれたアプリです。

作成する中で、超初心者が落ちやすい穴にたくさんハマりました。次回はその落とし穴と回避方法について記事にしたいと考えています。


ソースコード

全体のソースコードは以下です。


flow

[{

"id": "cf66d21b.30993",
"type": "slack",
"z": "c7e3292f.381cd8",
"name": "",
"channelURL": "",
"username": "node-red-github",
"emojiIcon": "",
"channel": "",
"x": 778.9999847412109,
"y": 434,
"wires": []
}, {
"id": "14a4f46b.eb5b0c",
"type": "http request",
"z": "c7e3292f.381cd8",
"name": "",
"method": "GET",
"ret": "txt",
"url": "https://github.com/node-red/node-red.github.io/commits/master.atom",
"x": 101.5,
"y": 127,
"wires": [
["c2dc5cbb.3d23a"]
]
}, {
"id": "c2dc5cbb.3d23a",
"type": "xml",
"z": "c7e3292f.381cd8",
"name": "",
"attr": "",
"chr": "",
"x": 251.89573669433594,
"y": 126.88888549804688,
"wires": [
["63c4b6d8.9c3b48", "f6890586.0976f8"]
]
}, {
"id": "63c4b6d8.9c3b48",
"type": "splitter",
"z": "c7e3292f.381cd8",
"name": "",
"property": "payload.feed.entry",
"x": 286.8956756591797,
"y": 214.88888549804688,
"wires": [
["f45f4b56.0ba0b8"]
]
}, {
"id": "f45f4b56.0ba0b8",
"type": "function",
"z": "c7e3292f.381cd8",
"name": "entry updated",
"func": "msg.updated = String(msg.payload.updated);\n\nreturn msg;",
"outputs": 1,
"noerr": 0,
"x": 445.89573669433594,
"y": 214.88888549804688,
"wires": [
["5993f252.a66c0c"]
]
}, {
"id": "f5dabfa1.0a254",
"type": "file in",
"z": "c7e3292f.381cd8",
"name": "",
"filename": "last_updated",
"format": "utf8",
"x": 303.9999694824219,
"y": 49,
"wires": [
["1e6fc179.e1903f"]
]
}, {
"id": "75b24a5c.8a4db4",
"type": "change",
"z": "c7e3292f.381cd8",
"name": "",
"rules": [{
"t": "set",
"p": "lastupdated",
"to": "msg.payload"
}],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 733.8957366943359,
"y": 49,
"wires": [
["14a4f46b.eb5b0c"]
]
}, {
"id": "1e6fc179.e1903f",
"type": "moment",
"z": "c7e3292f.381cd8",
"name": "",
"topic": "",
"input": "payload",
"format": "",
"output": "payload",
"x": 506.8956756591797,
"y": 49,
"wires": [
["75b24a5c.8a4db4"]
]
}, {
"id": "5993f252.a66c0c",
"type": "moment",
"z": "c7e3292f.381cd8",
"name": "",
"topic": "",
"input": "updated",
"format": "",
"output": "updated",
"x": 641.8957977294922,
"y": 215,
"wires": [
["1e11bad6.e1ee45"]
]
}, {
"id": "1e11bad6.e1ee45",
"type": "function",
"z": "c7e3292f.381cd8",
"name": "updated>lastupdated?",
"func": "if(msg.updated > msg.lastupdated){\n msg.payload.title = String(msg.payload.title);\n return msg;\n}\nelse\nmsg.payload = \"no update\";\nreturn msg;",
"outputs": "1",
"noerr": 0,
"x": 210.49998474121094,
"y": 433.8888854980469,
"wires": [
["c08d4380.3f72c"]
]
}, {
"id": "c08d4380.3f72c",
"type": "switch",
"z": "c7e3292f.381cd8",
"name": "",
"property": "payload",
"rules": [{
"t": "neq",
"v": "no update"
}],
"checkall": "true",
"outputs": 1,
"x": 396.89564514160156,
"y": 433.8888854980469,
"wires": [
["8ed5fbfd.712a08"]
]
}, {
"id": "8ed5fbfd.712a08",
"type": "function",
"z": "c7e3292f.381cd8",
"name": "create bot message",
"func": "msg.payload = msg.updated +'\\n' + msg.payload.title;\nreturn msg;",
"outputs": 1,
"noerr": 0,
"x": 585.8957366943359,
"y": 433.8888854980469,
"wires": [
["cf66d21b.30993"]
]
}, {
"id": "e2d9d039.1d263",
"type": "file",
"z": "c7e3292f.381cd8",
"name": "",
"filename": "last_updated",
"appendNewline": false,
"createDir": false,
"overwriteFile": "true",
"x": 758.8957366943359,
"y": 124.88888549804688,
"wires": []
}, {
"id": "f6890586.0976f8",
"type": "change",
"z": "c7e3292f.381cd8",
"name": "",
"rules": [{
"t": "set",
"p": "payload",
"to": "msg.payload.feed.updated"
}],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 429.89573669433594,
"y": 127.88888549804688,
"wires": [
["93ea179c.6c15e8"]
]
}, {
"id": "93ea179c.6c15e8",
"type": "function",
"z": "c7e3292f.381cd8",
"name": "to String",
"func": "msg.payload = String(msg.payload);\nreturn msg;",
"outputs": 1,
"noerr": 0,
"x": 602.8957366943359,
"y": 124.88888549804688,
"wires": [
["e2d9d039.1d263"]
]
}, {
"id": "53249f21.acdb6",
"type": "inject",
"z": "c7e3292f.381cd8",
"name": "",
"topic": "",
"payload": "",
"payloadType": "date",
"repeat": "",
"crontab": "00 10 * * *",
"once": false,
"x": 113,
"y": 49,
"wires": [
["f5dabfa1.0a254"]
]
}]