summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
authortomsmeding <tom.smeding@gmail.com>2016-11-08 11:00:51 +0100
committertomsmeding <tom.smeding@gmail.com>2016-11-08 11:00:51 +0100
commitb72261cd8fae5d522308a53bc09bdfb6e18a4629 (patch)
tree044b31cba28fb24bf455469e35909e2ef91051ee /modules
parent6c38ff04312e9cbed38e5397769157b0c8e6fce1 (diff)
changes: Add e-mail notifications
Diffstat (limited to 'modules')
-rw-r--r--modules/changes/changes.js149
1 files changed, 136 insertions, 13 deletions
diff --git a/modules/changes/changes.js b/modules/changes/changes.js
index 04b7f6e..67e4e43 100644
--- a/modules/changes/changes.js
+++ b/modules/changes/changes.js
@@ -5,7 +5,8 @@ var cmn=require("../$common.js"),
crypto=require("crypto"),
http=require("http"),
https=require("https"),
- URL=require("url");
+ URL=require("url"),
+ child_process=require("child_process");
var moddir=null;
@@ -20,19 +21,53 @@ persist.initSync();
//url: String
//URLobject: {url, timeline: [[Date, hash, contents]]}
var urls=persist.getItemSync("urls");
-(function(){
- if(!urls){
- urls={};
- persist.setItemSync("urls",urls);
- } else {
+if(!urls){
+ urls={};
+ persist.setItemSync("urls",urls);
+} else {
+ (function(){
var url,i;
for(url in urls){
for(i=0;i<urls[url].timeline.length;i++){
urls[url].timeline[i][0]=new Date(urls[url].timeline[i][0]);
}
}
+ })();
+}
+//gsettings: {notify: null/{to: "email", interval: ms}}
+var gsettings=persist.getItemSync("gsettings");
+if(!gsettings){
+ gsettings={notify: null};
+ persist.setItemSync("gsettings",gsettings);
+}
+
+if(gsettings.notify){
+ scheduleNotify();
+}
+
+var notifyIsScheduled=false;
+function scheduleNotify(){
+ if(notifyIsScheduled){
+ console.log("WARNING: requesting notify schedule when already scheduled");
+ return;
}
-})();
+ notifyIsScheduled=true;
+ var timeoutFunc=function(){
+ if(!gsettings.notify){
+ notifyIsScheduled=false;
+ return;
+ }
+ performNotify(gsettings.notify,function(){
+ performCleanup();
+ if(gsettings.notify){
+ setTimeout(timeoutFunc,gsettings.notify.interval);
+ } else {
+ notifyIsScheduled=false;
+ }
+ });
+ };
+ setTimeout(timeoutFunc,gsettings.notify.interval);
+}
function URLobject(url){
@@ -92,13 +127,16 @@ function fetch(method,url,data/*?*/,cb){
var refresh_persist_timeout=null;
-function refreshURLs(){
+function refreshURLs(cb){
var hashes={};
var i;
var url;
+ var nwaiting=0;
for(url in urls){
console.log("Fetching <"+url+">");
+ nwaiting++;
fetch("GET",url,function(url,status,body){
+ nwaiting--;
console.log("Got <"+url+">; status = "+status);
if(status==-1){
hashes[url]=[new Date(),null,null];
@@ -110,12 +148,14 @@ function refreshURLs(){
if(!urls[url]){
console.log("WARNING: url <"+url+"> from hashes not found in urls!");
- return;
+ } else {
+ //var last=urls[url].timeline[urls[url].timeline.length-1];
+ //if(last==undefined||hashes[url][1]==null||hashes[url][1]!=last[1]){
+ urls[url].timeline.push(hashes[url]);
+ //}
}
- //var last=urls[url].timeline[urls[url].timeline.length-1];
- //if(last==undefined||hashes[url][1]==null||hashes[url][1]!=last[1]){
- urls[url].timeline.push(hashes[url]);
- //}
+
+ if(nwaiting==0&&cb)process.nextTick(cb);
if(refresh_persist_timeout!=null)clearTimeout(refresh_persist_timeout);
refresh_persist_timeout=setTimeout(function(){
@@ -127,6 +167,89 @@ function refreshURLs(){
}
}
+function collectLastHashes(){
+ var lasthashes={};
+ for(url in urls){
+ if(urls[url].timeline.length==0){
+ lasthashes[url]=null;
+ } else {
+ lasthashes[url]=urls[url].timeline[urls[url].timeline.length-1][1];
+ }
+ }
+ return lasthashes;
+}
+
+function sendMail(tomail,body,cb){
+ var proc=child_process.spawn("sendmail",[tomail],{stdio:["pipe",process.stdout,process.stderr]});
+ proc.stdin.error(function(e){
+ console.log(e);
+ cb(e);
+ });
+ proc.stdin.write(body,function(){
+ proc.stdin.end();
+ cb();
+ });
+}
+
+function constructMailBody(tomail,diffs){
+ var body="To: "+tomail+"\n"+
+ "From: changes <changes_module@tomsmeding.com>\n"+
+ "Subject: changes notification mail\n\n"+
+ "The <a href=\"http://tomsmeding.com/changes\">changes</a> module recorded "+
+ "some changes in watched webpages.\n"+
+ "The URLs and their old and new content hashes are shown below.\n\n";
+ var i;
+ for(i=0;i<diffs.length;i++){
+ body+="- <a href=\""+diffs[i].url+"\">"+diffs[i].url+"</a>\n"+
+ " was: "+diffs[i].oldhash+"\n"+
+ " now: "+diffs[i].newhash+"\n";
+ }
+ body+="\n\n"+
+ "- webserver\n";
+ return body;
+}
+
+function performNotify(settings,cb){
+ var oldhashes=collectLastHashes();
+ refreshURLs(function(){
+ var newhashes=collectLastHashes();
+ var diffs=[]; // [{url, oldhash, newhash}]
+ for(url in newhashes){
+ if(newhashes[url]==oldhashes[url])continue;
+ diffs.push({
+ url: url,
+ oldhash: oldhashes[url],
+ newhash: newhashes[url]
+ });
+ }
+ sendMail(settings.to,constructMailBody(settings.to,diffs),cb);
+ });
+}
+
+function performCleanup(){
+ //keep only the last hash and the last different hash intact
+ //(if there is no different hash, delete everything but the last)
+ for(url in urls){
+ if(urls[url].timeline.length<=2)continue;
+ var i,lasthash,prevhash=null,prevhashidx=-1,tllen;
+ tllen=urls[url].timeline.length;
+ lasthash=urls[url].timeline[tllen-1][1];
+ for(i=tllen-2;i>=0;i--){
+ if(urls[url].timeline[i][1]!=lasthash){
+ prevhash=urls[url].timeline[i][1];
+ prevhashidx=i;
+ break;
+ }
+ }
+ if(prevhash==null){
+ urls[url].timeline.splice(0,tllen-1);
+ } else {
+ urls[url].timeline.splice(prevhashidx+1,tllen-prevhashidx-2);
+ urls[url].timeline.splice(0,prevhashidx);
+ }
+ }
+}
+
module.exports=function(app,io,_moddir){
moddir=_moddir;