diff options
-rw-r--r-- | modules/changes/changes.js | 149 |
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; |