From 36d10a5010f8d93f4d244b394cd2d20666a1b7df Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 31 Jan 2013 01:57:12 +0000 Subject: [PATCH] make it send emails --- README.md | 5 +- ep.json | 2 +- handleMessage.js | 108 ++++++++++++++++++++++++++++++ update.js | 169 +++++++++++++++++------------------------------ 4 files changed, 172 insertions(+), 112 deletions(-) create mode 100644 handleMessage.js diff --git a/README.md b/README.md index c2b67c4..5de6bc9 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ * a point to unsubscribe * stop the ui prompting if already subscribed -* send actual emails with content +* move settings to settings.json * Clean up all code - +* Get the modified contents from the API HTML diff +* Keep a record of when a user was last on a pad diff --git a/ep.json b/ep.json index 9ee23fc..37af7f6 100644 --- a/ep.json +++ b/ep.json @@ -4,7 +4,7 @@ "name": "ep_email_notifications", "hooks": { "padUpdate": "ep_email_notifications/update", - "handleMessage": "ep_email_notifications/update", + "handleMessage": "ep_email_notifications/handleMessage", "eejsBlock_scripts": "ep_email_notifications/client", "eejsBlock_editbarMenuRight": "ep_email_notifications/client:eejsBlock_toolbarRight", "eejsBlock_embedPopup": "ep_email_notifications/client:eejsBlock_embedPopup" diff --git a/handleMessage.js b/handleMessage.js new file mode 100644 index 0000000..4121976 --- /dev/null +++ b/handleMessage.js @@ -0,0 +1,108 @@ + var db = require('../../src/node/db/DB').db, + API = require('../../src/node/db/API.js'), + async = require('../../src/node_modules/async'), + check = require('validator').check, +settings = require('../../src/node/utils/Settings'); + +var pluginSettings = settings.ep_email_notifications; + +// When a new message comes in from the client - FML this is ugly +exports.handleMessage = function(hook_name, context, callback){ + if (context.message && context.message.data){ + if (context.message.data.type == 'USERINFO_UPDATE' ) { // if it's a request to update an authors email + if (context.message.data.userInfo){ + if(context.message.data.userInfo.email){ // it contains email + console.debug(context.message); + + // does email Subscription already exist for this email address? + db.get("emailSubscription:"+context.message.data.padId, function(err, userIds){ + + var alreadyExists = false; + if(userIds){ + async.forEach(Object.keys(userIds), function(user, cb){ + console.debug("UserIds subscribed by email to this pad:", userIds); + if(user == context.message.data.userInfo.email){ // If we already have this email registered for this pad + + // This user ID is already assigned to this padId so don't do anything except tell the user they are already subscribed somehow.. + alreadyExists = true; + console.debug("email ", user, "already subscribed to ", context.message.data.padId, " so sending message to client"); + + context.client.json.send({ type: "COLLABROOM", + data:{ + type: "emailSubscriptionSuccess", + payload: false + } + }); + } + cb(); + }, + + function(err){ + // There should be something in here! + }); // end async for each + } + var validatesAsEmail = check(context.message.data.userInfo.email).isEmail(); + if(!validatesAsEmail){ // send validation failed if it's malformed.. y'know in general fuck em! + console.warn("Dropped email subscription due to malformed email address"); + context.client.json.send({ type: "COLLABROOM", + data:{ + type: "emailSubscriptionSuccess", + payload: false + } + }); + } + if(alreadyExists == false && validatesAsEmail){ + console.debug ("Wrote to the database and sent client a positive response ",context.message.data.userInfo.email); + + exports.setAuthorEmail( + context.message.data.userInfo.userId, + context.message.data.userInfo.email, callback + ); + + exports.setAuthorEmailRegistered( + context.message.data.userInfo.email, + context.message.data.userInfo.userId, + context.message.data.padId + ); + + context.client.json.send({ type: "COLLABROOM", + data:{ + type: "emailSubscriptionSuccess", + payload: true + } + }); + } + }); // close db get + + callback(null); // don't run onto passing colorId or anything else to the message handler + + } + } + } + } + callback(); +} + +// Updates the database with the email record +exports.setAuthorEmail = function (author, email, callback){ + db.setSub("globalAuthor:" + author, ["email"], email, callback); +} + +// Write email and padId to the database +exports.setAuthorEmailRegistered = function(email, authorId, padId){ + var timestamp = new Date().getTime(); + var registered = { + authorId: authorId, + timestamp: timestamp + }; + console.debug("registered", registered, " to ", padId); + + // Here we have to basically hack a new value into the database, this isn't clean or polite. + db.get("emailSubscription:" + padId, function(err, value){ // get the current value + if(!value){value = {};} // if an emailSubscription doesnt exist yet for this padId don't panic + value[email] = registered; // add the registered values to the object + db.set("emailSubscription:" + padId, value); // stick it in the database + }); + +} + diff --git a/update.js b/update.js index e0ec591..c0901fc 100644 --- a/update.js +++ b/update.js @@ -1,98 +1,25 @@ +// We check pads periodically for activity and notify owners when someone begins editing and when someone finishes. + var db = require('../../src/node/db/DB').db, API = require('../../src/node/db/API.js'), async = require('../../src/node_modules/async'), check = require('validator').check, + email = require('emailjs'), settings = require('../../src/node/utils/Settings'); var pluginSettings = settings.ep_email_notifications; -var checkInterval = 60000; // Move me to the settings file -var staleTime = 3600000; // Move me to settings +var checkInterval = 3000; // How frequently to check for pad updates -- Move me to the settings file +var staleTime = 3000; // How stale does a pad need to be before notifying subscribers? Move me to settings var timers = {}; +var fromName = "John McLear"; +var fromEmail = "john@mclear.co.uk"; +var urlToPads = "http://beta.etherpad.org/p/"; -// When a new message comes in from the client -exports.handleMessage = function(hook_name, context, callback){ - if (context.message && context.message.data){ - if (context.message.data.type == 'USERINFO_UPDATE' ) { // if it's a request to update an authors email - if (context.message.data.userInfo){ - if(context.message.data.userInfo.email){ // it contains email - console.debug(context.message); - - // does email Subscription already exist for this email address? - db.get("emailSubscription:"+context.message.data.padId, function(err, userIds){ - - var alreadyExists = false; - if(userIds){ - async.forEach(Object.keys(userIds), function(user, cb){ - console.debug("UserIds subscribed by email to this pad:", userIds); - if(user == context.message.data.userInfo.email){ // If we already have this email registered for this pad - - // This user ID is already assigned to this padId so don't do anything except tell the user they are already subscribed somehow.. - alreadyExists = true; - console.debug("email ", user, "already subscribed to ", context.message.data.padId, " so sending message to client"); - - context.client.json.send({ type: "COLLABROOM", - data:{ - type: "emailSubscriptionSuccess", - payload: false - } - }); - } - cb(); - }, - - function(err){ - // There should be something in here! - }); // end async for each - } - - var validatesAsEmail = check(context.message.data.userInfo.email).isEmail(); - if(!validatesAsEmail){ // send validation failed if it's malformed.. y'know in general fuck em! - console.warn("Dropped email subscription due to malformed email address"); - context.client.json.send({ type: "COLLABROOM", - data:{ - type: "emailSubscriptionSuccess", - payload: false - } - }); - } - if(alreadyExists == false && validatesAsEmail){ - console.warn ("Wrote to the database and sent client a positive response ",context.message.data.userInfo.email); - - exports.setAuthorEmail( - context.message.data.userInfo.userId, - context.message.data.userInfo.email, callback - ); - - exports.setAuthorEmailRegistered( - context.message.data.userInfo.email, - context.message.data.userInfo.userId, - context.message.data.padId - ); - - context.client.json.send({ type: "COLLABROOM", - data:{ - type: "emailSubscriptionSuccess", - payload: true - } - }); - } - }); // close db get - - callback(null); // don't run onto passing colorId or anything else to the message handler - - } - } - } - } - callback(); -} - -exports.doesPadIdEmailAssociationAlreadyExist = function (padId, email){ - var found = false; - db.get("emailSubscription:"+padId, function(err, value){ - return value; - }); -} +var server = email.server.connect({ + user: "username", + password:"password", + host: "smtp.gmail.com", +}); exports.padUpdate = function (hook_name, _pad) { @@ -102,6 +29,8 @@ exports.padUpdate = function (hook_name, _pad) { // does an interval not exist for this pad? if(!timers[padId]){ + console.warn("Someone started editing "+padId); + exports.notifyBegin(padId); console.debug("Created an interval time check for "+padId); // if not then create one and write it to the timers object timers[padId] = exports.createInterval(padId, checkInterval); @@ -111,6 +40,49 @@ exports.padUpdate = function (hook_name, _pad) { }; +exports.notifyBegin = function(padId){ + db.get("emailSubscription:" + padId, function(err, recipients){ // get everyone we need to email + async.forEach(Object.keys(recipients), function(recipient, cb){ + console.debug("Emailing "+recipient +" about a new begin update"); + + server.send({ + text: "Your pad at "+urlToPads+"/p/"+padId +" is being edited, we're just emailing you let you know :)", + from: fromName+ "<"+fromEmail+">", + to: recipient, + subject: "Someone begin editing "+padId + }, function(err, message) { console.log(err || message); }); + + cb(); // finish each user + }, + function(err){ + + }); + }); +} + +exports.notifyEnd = function(padId){ + // get the modified contents... + + + db.get("emailSubscription:" + padId, function(err, recipients){ // get everyone we need to email + async.forEach(Object.keys(recipients), function(recipient, cb){ + console.debug("Emailing "+recipient +" about a new begin update"); + + server.send({ + text: "Your pad at "+urlToPads+"/p/"+padId +" has finished being edited, we're just emailing you let you know :) The changes look like this:" + changesToPad, + from: fromName+ "<"+fromEmail+">", + to: recipient, + subject: "Someone begin editing "+padId + }, function(err, message) { console.log(err || message); }); + + cb(); // finish each user + }, + function(err){ + + }); + }); +} + exports.sendUpdates = function(padId){ // check to see if we can delete this interval @@ -119,13 +91,14 @@ exports.sendUpdates = function(padId){ // we delete an interval if a pad hasn't been edited in X seconds. var currTS = new Date().getTime(); if(currTS - message.lastEdited > staleTime){ + exports.notifyEnd(padId); console.warn("Interval went stale so deleting it from object and timer"); var interval = timers[padId]; clearInterval(timers[padId]); // remove the interval timer delete timers[padId]; // remove the entry from the padId }else{ - console.debug("email timeotu not stale so not deleting"); + console.debug("email timeout not stale so not deleting"); } }); @@ -186,25 +159,3 @@ exports.createInterval = function(padId){ }); } -// Updates the database with the email record -exports.setAuthorEmail = function (author, email, callback){ - db.setSub("globalAuthor:" + author, ["email"], email, callback); -} - -// Write email and padId to the database -exports.setAuthorEmailRegistered = function(email, authorId, padId){ - var timestamp = new Date().getTime(); - var registered = { - authorId: authorId, - timestamp: timestamp - }; - console.debug("registered", registered, " to ", padId); - - // Here we have to basically hack a new value into the database, this isn't clean or polite. - db.get("emailSubscription:" + padId, function(err, value){ // get the current value - if(!value){value = {};} // if an emailSubscription doesnt exist yet for this padId don't panic - value[email] = registered; // add the registered values to the object - db.set("emailSubscription:" + padId, value); // stick it in the database - }); - -}