Skip to content

Instantly share code, notes, and snippets.

@crrobinson14
Created October 5, 2016 15:57
Show Gist options
  • Save crrobinson14/b93431c5f2a3a337693ffa5d2f9e9eb5 to your computer and use it in GitHub Desktop.
Save crrobinson14/b93431c5f2a3a337693ffa5d2f9e9eb5 to your computer and use it in GitHub Desktop.
var pendingNotifications = {},
publicApi = {},
api;
// Dummy function to avoid an Unhandled Rejection error if the user is offline. Prevents us
// from needing to .catch() the notification result in every spot where we call it. Also a
// useful debugging point, so we don't just null it out.
function userNotOnline() {
}
/**
* Send a message to a specific user by their user ID. Optionally falls back to push notifications
* if the user is not online in the cluster.
* @param {Number} userId The ID of the user to reach
* @param {String} room The room to send the message to, typically 'system'
* @param {*} message The message to send.
* @
*/
publicApi.notifyUser = function(userId, room, message) {
var notificationId = Math.uuid(12);
api.log('NOTIFICATIONS: Notifying user {0} with message'.format(userId), 'info', message);
// Yes, yes, we know this pattern is deprecated
return new api.Promise(function(resolve) {
pendingNotifications[notificationId] = {
posted: Date.now(),
userId: userId,
room: room,
message: message,
resolve: resolve,
reject: userNotOnline
};
api.redis.doCluster('api.notifications.handleNotifyUser', [notificationId, userId, room, message]);
});
};
// @private, should only be called via notifyUser.
publicApi.handleNotifyUser = function(notificationId, userId, room, message) {
Object.keys(api.connections.connections).map(function(connectionId) {
var connection = api.connections.connections[connectionId];
if (connection.session && connection.session.userId === userId && connection.type === 'websocket') {
api.log('NOTIFICATIONS: [{0}] has user {1} for {2}'.format(api.uniqueWorkerId, userId, notificationId), 'info', message);
connection.sendMessage({ context: 'user', room: room, message: message });
api.redis.doCluster('api.notifications.handleNotificationSent', [notificationId]);
}
});
};
// @private, should only be called via notifyUser.
publicApi.handleNotificationSent = function(notificationId) {
var pending = pendingNotifications[notificationId];
if (pending) {
pending.resolve(notificationId);
delete pendingNotifications[notificationId];
}
};
/**
* Send an array of messages to a specific user
* @param {Object} connection The user to send to
* @param {String} room The name of the room to which the messages should be sent
* @param {Array} messages The array of messages to transmit
*/
publicApi.sendMessagesToConnection = function(connection, room, messages) {
(messages || []).map(function(message) {
connection.sendMessage({
context: 'user',
room: room,
message: message
});
});
};
module.exports = {
initialize: function(_api, next) {
api = _api;
api.notifications = publicApi;
next();
},
start: function(_api, next) {
var notificationTimeoutCheck = setInterval(function() {
var now = Date.now();
Object.keys(pendingNotifications).map(function(notificationId) {
var notification = pendingNotifications[notificationId];
if (notification.posted + 5000 < now) {
notification.reject(api.responses.notFoundError());
delete pendingNotifications[notificationId];
}
});
}, 5000);
notificationTimeoutCheck.unref();
next();
},
stop: function(_api, next) {
next();
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment