make it send emails

This commit is contained in:
John McLear 2013-01-31 01:57:12 +00:00
parent 9cd41efb1d
commit 36d10a5010
4 changed files with 172 additions and 112 deletions

View file

@ -2,6 +2,7 @@
* a point to unsubscribe * a point to unsubscribe
* stop the ui prompting if already subscribed * stop the ui prompting if already subscribed
* send actual emails with content * move settings to settings.json
* Clean up all code * 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

View file

@ -4,7 +4,7 @@
"name": "ep_email_notifications", "name": "ep_email_notifications",
"hooks": { "hooks": {
"padUpdate": "ep_email_notifications/update", "padUpdate": "ep_email_notifications/update",
"handleMessage": "ep_email_notifications/update", "handleMessage": "ep_email_notifications/handleMessage",
"eejsBlock_scripts": "ep_email_notifications/client", "eejsBlock_scripts": "ep_email_notifications/client",
"eejsBlock_editbarMenuRight": "ep_email_notifications/client:eejsBlock_toolbarRight", "eejsBlock_editbarMenuRight": "ep_email_notifications/client:eejsBlock_toolbarRight",
"eejsBlock_embedPopup": "ep_email_notifications/client:eejsBlock_embedPopup" "eejsBlock_embedPopup": "ep_email_notifications/client:eejsBlock_embedPopup"

108
handleMessage.js Normal file
View file

@ -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
});
}

167
update.js
View file

@ -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, var db = require('../../src/node/db/DB').db,
API = require('../../src/node/db/API.js'), API = require('../../src/node/db/API.js'),
async = require('../../src/node_modules/async'), async = require('../../src/node_modules/async'),
check = require('validator').check, check = require('validator').check,
email = require('emailjs'),
settings = require('../../src/node/utils/Settings'); settings = require('../../src/node/utils/Settings');
var pluginSettings = settings.ep_email_notifications; var pluginSettings = settings.ep_email_notifications;
var checkInterval = 60000; // Move me to the settings file var checkInterval = 3000; // How frequently to check for pad updates -- Move me to the settings file
var staleTime = 3600000; // Move me to settings var staleTime = 3000; // How stale does a pad need to be before notifying subscribers? Move me to settings
var timers = {}; 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 var server = email.server.connect({
exports.handleMessage = function(hook_name, context, callback){ user: "username",
if (context.message && context.message.data){ password:"password",
if (context.message.data.type == 'USERINFO_UPDATE' ) { // if it's a request to update an authors email host: "smtp.gmail.com",
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;
});
}
exports.padUpdate = function (hook_name, _pad) { exports.padUpdate = function (hook_name, _pad) {
@ -102,6 +29,8 @@ exports.padUpdate = function (hook_name, _pad) {
// does an interval not exist for this pad? // does an interval not exist for this pad?
if(!timers[padId]){ if(!timers[padId]){
console.warn("Someone started editing "+padId);
exports.notifyBegin(padId);
console.debug("Created an interval time check for "+padId); console.debug("Created an interval time check for "+padId);
// if not then create one and write it to the timers object // if not then create one and write it to the timers object
timers[padId] = exports.createInterval(padId, checkInterval); 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){ exports.sendUpdates = function(padId){
// check to see if we can delete this interval // 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. // we delete an interval if a pad hasn't been edited in X seconds.
var currTS = new Date().getTime(); var currTS = new Date().getTime();
if(currTS - message.lastEdited > staleTime){ if(currTS - message.lastEdited > staleTime){
exports.notifyEnd(padId);
console.warn("Interval went stale so deleting it from object and timer"); console.warn("Interval went stale so deleting it from object and timer");
var interval = timers[padId]; var interval = timers[padId];
clearInterval(timers[padId]); // remove the interval timer clearInterval(timers[padId]); // remove the interval timer
delete timers[padId]; // remove the entry from the padId delete timers[padId]; // remove the entry from the padId
}else{ }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
});
}