Merge branch 'hotfix/21.56.9' into master
[gitter.git] / server / services / notifications / push-notification-postbox.js
blobdf0c30bce85833bc2d81e950c9ba531d235d1d00
1 'use strict';
3 var env = require('gitter-web-env');
4 var errorReporter = env.errorReporter;
5 var winston = env.logger;
6 var nconf = env.config;
7 var pushNotificationFilter = require('gitter-web-push-notification-filter');
8 var workerQueue = require('gitter-web-utils/lib/worker-queue-redis');
9 var debug = require('debug')('gitter:app:push-notification-postbox');
10 var mongoUtils = require('gitter-web-persistence-utils/lib/mongo-utils');
11 var Promise = require('bluebird');
13 var notificationWindowPeriods = [
14   nconf.get('notifications:notificationDelay') * 1000,
15   nconf.get('notifications:notificationDelay2') * 1000
18 /* 10 second window for users on mention */
19 var mentionNotificationWindowPeriod = 10000;
20 var maxNotificationsForMentions = 10;
21 var maxNotificationsForNonMentions = notificationWindowPeriods.length;
23 // This queue is responsible to taking notifications and deciding which users to forward them on to
24 var pushNotificationFilterQueue = workerQueue.queue('push-notifications-filter', {}, function() {
25   return function(data, done) {
26     var troupeId = data.troupeId;
27     var chatId = data.chatId;
28     var userIds = data.userIds;
29     var mentioned = data.mentioned;
31     return filterNotificationsForPush(troupeId, chatId, userIds, mentioned)
32       .then(function() {
33         debug('filterNotificationsForPush complete');
34       })
35       .catch(function(err) {
36         winston.error('Unable to queue notification: ' + err, { exception: err });
37       })
38       .nodeify(done);
39   };
40 });
42 // This queue is responsible to generating the actual content of the push notification and sending it to users
43 var pushNotificationGeneratorQueue = workerQueue.queue(
44   'push-notifications-generate',
45   {},
46   function() {
47     var pushNotificationGenerator = require('./push-notification-generator');
49     return function(data, done) {
50       var userId = data.userId;
51       var troupeId = data.troupeId;
52       var notificationNumber = data.notificationNumber;
54       debug('Spooling push notification for %s in %s, #%s', userId, troupeId, notificationNumber);
56       if (!userId || !troupeId || !notificationNumber) return done();
58       return pushNotificationGenerator
59         .sendUserTroupeNotification(userId, troupeId, notificationNumber)
60         .catch(function(err) {
61           winston.error('Failed to send notifications: ' + err + '. Failing silently.', {
62             exception: err
63           });
64           errorReporter(
65             err,
66             { userId: userId, troupeId: troupeId },
67             { module: 'push-notification-postbox' }
68           );
69         })
70         .nodeify(done);
71     };
72   }
75 function filterNotificationsForPush(troupeId, chatId, userIds, mentioned) {
76   var chatTime = mongoUtils.getTimestampFromObjectId(chatId);
77   debug('filterNotificationsForPush for %s users', userIds.length);
79   return Promise.map(userIds, function(userId) {
80     var maxLocks = mentioned ? maxNotificationsForMentions : maxNotificationsForNonMentions;
82     // TODO: bulk version of this method please
83     return pushNotificationFilter
84       .canLockForNotification(userId, troupeId, chatTime, maxLocks)
85       .then(function(notificationNumber) {
86         if (!notificationNumber) {
87           // TODO: For mentions: consider cancelling the current lock on mentions and creating a
88           // new one as if we're in the 60 second window period, we'll need to
89           // wait until the end of the window before sending the mention
90           debug(
91             'Unable to obtain a lock (max=%s) for user %s in troupe %s. Will not notify',
92             maxLocks,
93             userId,
94             troupeId
95           );
96           return;
97         }
99         var delay;
100         if (mentioned) {
101           /* Send the notification to the user very shortly */
102           delay = mentionNotificationWindowPeriod;
103         } else {
104           delay = notificationWindowPeriods[notificationNumber - 1];
105           if (!delay) {
106             debug('Obtained a lock in excess of the maximum lock number of %s', maxLocks);
107             return;
108           }
109         }
111         debug(
112           'Queuing notification %s to be send to user %s in %sms',
113           notificationNumber,
114           userId,
115           delay
116         );
118         return pushNotificationGeneratorQueue.invoke(
119           {
120             userId: userId,
121             troupeId: troupeId,
122             notificationNumber: notificationNumber
123           },
124           { delay: delay }
125         );
126       });
127   });
130 exports.queueNotificationsForChat = function(troupeId, chatId, userIds, mentioned) {
131   debug('queueNotificationsForChat for %s users', userIds.length);
133   return pushNotificationFilterQueue.invoke({
134     troupeId: troupeId,
135     chatId: chatId,
136     userIds: userIds,
137     mentioned: mentioned
138   });
141 exports.listen = function() {
142   pushNotificationGeneratorQueue.listen();
143   pushNotificationFilterQueue.listen();