Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[NEW] Better Push and Email Notification logic #17357

Merged
merged 46 commits into from
Apr 21, 2020
Merged
Show file tree
Hide file tree
Changes from 35 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
d7638a5
Import push library
rodrigok Apr 17, 2020
df9003c
Simplify imported lib
rodrigok Apr 17, 2020
1c205c0
Move our custom push code to inside the library
rodrigok Apr 17, 2020
f7e9bc0
Remove unused methods
rodrigok Apr 17, 2020
d239f8e
Remove dependecy of push package
rodrigok Apr 17, 2020
4947f7b
Convert push methods to class
rodrigok Apr 17, 2020
5ae70d4
Improve GCM and APN codes
rodrigok Apr 17, 2020
16c0cd2
Update APN
rodrigok Apr 17, 2020
ca1a28e
Unify part of the gateway and native send logic
rodrigok Apr 17, 2020
14bb194
Code improvements
rodrigok Apr 17, 2020
006ecc7
Reduce meteor usage
rodrigok Apr 17, 2020
ddb610f
Remove useless condition
rodrigok Apr 17, 2020
a96a1b4
Make APN work
rodrigok Apr 17, 2020
fa64c3c
Merge remote-tracking branch 'origin/develop' into push
rodrigok Apr 17, 2020
42bbd43
Fix misspeling
rodrigok Apr 17, 2020
9de7e67
Start queue
rodrigok Apr 18, 2020
afeff3d
First implementation of notification schedule
rodrigok Apr 18, 2020
a653672
Improve logic
rodrigok Apr 18, 2020
faa43f0
Reset queue on message read
rodrigok Apr 18, 2020
6bae623
Fix unit tests
rodrigok Apr 18, 2020
e82eccb
Remove unecessary code/comments/setting
rodrigok Apr 18, 2020
d71b258
Add indexes
rodrigok Apr 18, 2020
9f16605
Init removal of _raix_push_notifications
rodrigok Apr 18, 2020
1a1132d
Use userId directly instead of query
rodrigok Apr 18, 2020
a9e0498
Merge remote-tracking branch 'origin/develop' into push
rodrigok Apr 18, 2020
cff3ed2
Handle APN errors correctly
rodrigok Apr 19, 2020
702af0f
Merge branch 'push' into notification-logic
rodrigok Apr 19, 2020
a8cb11b
Remove unused push settings
rodrigok Apr 19, 2020
61eb21b
Add TTL of 2h
rodrigok Apr 19, 2020
7f569b8
Retry expired items on next query
rodrigok Apr 19, 2020
f756cc8
Remove `Notifications_Always_Notify_Mobile` setting
rodrigok Apr 19, 2020
2307807
Migrate and drop old collection
rodrigok Apr 19, 2020
cf768d4
Organize variables
rodrigok Apr 19, 2020
3a42dc3
Merge remote-tracking branch 'origin/develop' into notification-logic
rodrigok Apr 20, 2020
fec5190
Fix lint
rodrigok Apr 20, 2020
23820c9
Merge branch 'develop' into notification-logic
sampaiodiego Apr 20, 2020
2886c3e
Free disk space on CI
sampaiodiego Apr 20, 2020
b77588b
General improvements
sampaiodiego Apr 21, 2020
ea6556b
Remove insert from BaseRaw
sampaiodiego Apr 21, 2020
e53ed51
Remove host from push items
sampaiodiego Apr 21, 2020
dca2ebb
Rename migration
sampaiodiego Apr 21, 2020
cce65b1
Merge branch 'develop' into notification-logic
sampaiodiego Apr 21, 2020
1e3ca2b
Use Fibers on migration
sampaiodiego Apr 21, 2020
47e9605
Remove sid
sampaiodiego Apr 21, 2020
d2d1d6c
Cofigured params via env vars
sampaiodiego Apr 21, 2020
1a9ddb0
Use Number
sampaiodiego Apr 21, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 11 additions & 8 deletions app/lib/server/functions/notifications/email.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ const getButtonUrl = (room, subscription, message) => {
});
};

export function sendEmail({ message, user, subscription, room, emailAddress, hasMentionToUser }) {
export function getEmailData({ message, user, subscription, room, emailAddress, hasMentionToUser }) {
const username = settings.get('UI_Use_Real_Name') ? message.u.name || message.u.username : message.u.username;
let subjectKey = 'Offline_Mention_All_Email';

Expand Down Expand Up @@ -152,12 +152,20 @@ export function sendEmail({ message, user, subscription, room, emailAddress, has
}

metrics.notificationsSent.inc({ notification_type: 'email' });
return Mailer.send(email);
return email;
}

export function sendEmailFromData(data) {
metrics.notificationsSent.inc({ notification_type: 'email' });
return Mailer.send(data);
}

export function sendEmail({ message, user, subscription, room, emailAddress, hasMentionToUser }) {
return sendEmailFromData(getEmailData({ message, user, subscription, room, emailAddress, hasMentionToUser }));
}

export function shouldNotifyEmail({
disableAllMessageNotifications,
statusConnection,
emailNotifications,
isHighlighted,
hasMentionToUser,
Expand All @@ -170,11 +178,6 @@ export function shouldNotifyEmail({
return false;
}

// use connected (don't need to send him an email)
if (statusConnection === 'online') {
return false;
}

// user/room preference to nothing
if (emailNotifications === 'nothing') {
return false;
Expand Down
24 changes: 8 additions & 16 deletions app/lib/server/functions/notifications/mobile.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,6 @@ import { PushNotification } from '../../../../push-notifications/server';
const CATEGORY_MESSAGE = 'MESSAGE';
const CATEGORY_MESSAGE_NOREPLY = 'MESSAGE_NOREPLY';

let alwaysNotifyMobileBoolean;
settings.get('Notifications_Always_Notify_Mobile', (key, value) => {
alwaysNotifyMobileBoolean = value;
});

let SubscriptionRaw;
Meteor.startup(() => {
SubscriptionRaw = Subscriptions.model.rawCollection();
Expand Down Expand Up @@ -46,13 +41,13 @@ function enableNotificationReplyButton(room, username) {
return !room.muted.includes(username);
}

export async function sendSinglePush({ room, message, userId, receiverUsername, senderUsername, senderName, notificationMessage }) {
export async function getPushData({ room, message, userId, receiverUsername, senderUsername, senderName, notificationMessage }) {
let username = '';
if (settings.get('Push_show_username_room')) {
username = settings.get('UI_Use_Real_Name') === true ? senderName : senderUsername;
}

PushNotification.send({
return {
roomId: message.rid,
payload: {
host: Meteor.absoluteUrl(),
Expand All @@ -67,11 +62,13 @@ export async function sendSinglePush({ room, message, userId, receiverUsername,
username,
message: settings.get('Push_show_message') ? notificationMessage : ' ',
badge: await getBadgeCount(userId),
usersTo: {
userId,
},
userId,
category: enableNotificationReplyButton(room, receiverUsername) ? CATEGORY_MESSAGE : CATEGORY_MESSAGE_NOREPLY,
});
};
}

export async function sendSinglePush({ room, message, userId, receiverUsername, senderUsername, senderName, notificationMessage }) {
PushNotification.send(getPushData({ room, message, userId, receiverUsername, senderUsername, senderName, notificationMessage }));
}

export function shouldNotifyMobile({
Expand All @@ -81,7 +78,6 @@ export function shouldNotifyMobile({
isHighlighted,
hasMentionToUser,
hasReplyToThread,
statusConnection,
roomType,
}) {
if (disableAllMessageNotifications && mobilePushNotifications == null && !isHighlighted && !hasMentionToUser && !hasReplyToThread) {
Expand All @@ -92,10 +88,6 @@ export function shouldNotifyMobile({
return false;
}

if (!alwaysNotifyMobileBoolean && statusConnection === 'online') {
return false;
}

if (!mobilePushNotifications) {
if (settings.get('Accounts_Default_User_Preferences_mobileNotifications') === 'all') {
return true;
Expand Down
42 changes: 29 additions & 13 deletions app/lib/server/lib/sendNotificationsOnMessage.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ import { callbacks } from '../../../callbacks/server';
import { Subscriptions, Users } from '../../../models/server';
import { roomTypes } from '../../../utils';
import { callJoinRoom, messageContainsHighlight, parseMessageTextPerUser, replaceMentionedUsernamesWithFullNames } from '../functions/notifications';
import { sendEmail, shouldNotifyEmail } from '../functions/notifications/email';
import { sendSinglePush, shouldNotifyMobile } from '../functions/notifications/mobile';
import { getEmailData, shouldNotifyEmail } from '../functions/notifications/email';
import { getPushData, shouldNotifyMobile } from '../functions/notifications/mobile';
import { notifyDesktopUser, shouldNotifyDesktop } from '../functions/notifications/desktop';
import { notifyAudioUser, shouldNotifyAudio } from '../functions/notifications/audio';
import { Notification } from '../../../notification-queue/server/NotificationQueue';

let TroubleshootDisableNotifications;

Expand Down Expand Up @@ -115,30 +116,33 @@ export const sendNotification = async ({
});
}

const queueItems = [];

if (shouldNotifyMobile({
disableAllMessageNotifications,
mobilePushNotifications,
hasMentionToAll,
isHighlighted,
hasMentionToUser,
hasReplyToThread,
statusConnection: receiver.statusConnection,
roomType,
})) {
sendSinglePush({
notificationMessage,
room,
message,
userId: subscription.u._id,
senderUsername: sender.username,
senderName: sender.name,
receiverUsername: receiver.username,
queueItems.push({
type: 'push',
data: await getPushData({
notificationMessage,
room,
message,
userId: subscription.u._id,
senderUsername: sender.username,
senderName: sender.name,
receiverUsername: receiver.username,
}),
});
}

if (receiver.emails && shouldNotifyEmail({
disableAllMessageNotifications,
statusConnection: receiver.statusConnection,
emailNotifications,
isHighlighted,
hasMentionToUser,
Expand All @@ -148,13 +152,25 @@ export const sendNotification = async ({
})) {
receiver.emails.some((email) => {
if (email.verified) {
sendEmail({ message, receiver, subscription, room, emailAddress: email.address, hasMentionToUser });
queueItems.push({
type: 'email',
data: getEmailData({ message, receiver, subscription, room, emailAddress: email.address, hasMentionToUser }),
});

return true;
}
return false;
});
}

if (queueItems.length) {
Notification.scheduleItem({
uid: subscription.u._id,
rid: room._id,
sid: subscription._id,
items: queueItems,
});
}
};

const project = {
Expand Down
34 changes: 1 addition & 33 deletions app/lib/server/startup/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -925,12 +925,6 @@ settings.addGroup('General', function() {
public: true,
i18nDescription: 'Notifications_Max_Room_Members_Description',
});

this.add('Notifications_Always_Notify_Mobile', false, {
type: 'boolean',
public: true,
i18nDescription: 'Notifications_Always_Notify_Mobile_Description',
});
});
this.section('REST API', function() {
return this.add('API_User_Limit', 500, {
Expand Down Expand Up @@ -1183,33 +1177,7 @@ settings.addGroup('Push', function() {
public: true,
alert: 'Push_Setting_Requires_Restart_Alert',
});
this.add('Push_debug', false, {
type: 'boolean',
public: true,
alert: 'Push_Setting_Requires_Restart_Alert',
enableQuery: {
_id: 'Push_enable',
value: true,
},
});
this.add('Push_send_interval', 2000, {
type: 'int',
public: true,
alert: 'Push_Setting_Requires_Restart_Alert',
enableQuery: {
_id: 'Push_enable',
value: true,
},
});
this.add('Push_send_batch_size', 100, {
type: 'int',
public: true,
alert: 'Push_Setting_Requires_Restart_Alert',
enableQuery: {
_id: 'Push_enable',
value: true,
},
});

this.add('Push_enable_gateway', true, {
type: 'boolean',
alert: 'Push_Setting_Requires_Restart_Alert',
Expand Down
13 changes: 13 additions & 0 deletions app/models/server/models/NotificationQueue.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Base } from './_Base';

export class NotificationQueue extends Base {
constructor() {
super('notification_queue');
this.tryEnsureIndex({ uid: 1 });
this.tryEnsureIndex({ ts: 1 }, { expireAfterSeconds: 2 * 60 * 60 });
this.tryEnsureIndex({ schedule: 1 }, { sparse: true });
this.tryEnsureIndex({ sending: 1 }, { sparse: true });
}
}

export default new NotificationQueue();
18 changes: 10 additions & 8 deletions app/models/server/models/Sessions.tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -245,21 +245,23 @@ describe('Sessions Aggregates', () => {
after(() => { mongoUnit.stop(); });
}

before(function() {
return MongoClient.connect(process.env.MONGO_URL)
.then((client) => { db = client.db('test'); });
});
before(async () => {
const client = await MongoClient.connect(process.env.MONGO_URL);
db = client.db('test');

after(() => {
client.close();
});

await db.dropDatabase();

before(() => db.dropDatabase().then(() => {
const sessions = db.collection('sessions');
const sessions_dates = db.collection('sessions_dates');
return Promise.all([
sessions.insertMany(DATA.sessions),
sessions_dates.insertMany(DATA.sessions_dates),
]);
}));

after(() => { db.close(); });
});

it('should have sessions_dates data saved', () => {
const collection = db.collection('sessions_dates');
Expand Down
9 changes: 7 additions & 2 deletions app/models/server/raw/BaseRaw.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { ObjectId } from 'mongodb';

export class BaseRaw {
constructor(col) {
this.col = col;
Expand All @@ -19,8 +21,11 @@ export class BaseRaw {
return this.col.find(...args);
}

insert(...args) {
return this.col.insert(...args);
insert(docs, options) {
rodrigok marked this conversation as resolved.
Show resolved Hide resolved
if (!Array.isArray(docs) && !docs._id) {
docs._id = new ObjectId().toString();
}
return this.col.insert(docs, options);
}

update(...args) {
Expand Down
5 changes: 5 additions & 0 deletions app/models/server/raw/NotificationQueue.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { BaseRaw } from './BaseRaw';

export class NotificationQueueRaw extends BaseRaw {

}
3 changes: 3 additions & 0 deletions app/models/server/raw/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ import LivechatAgentActivityModel from '../models/LivechatAgentActivity';
import { LivechatAgentActivityRaw } from './LivechatAgentActivity';
import StatisticsModel from '../models/Statistics';
import { StatisticsRaw } from './Statistics';
import NotificationQueueModel from '../models/NotificationQueue';
import { NotificationQueueRaw } from './NotificationQueue';

export const Permissions = new PermissionsRaw(PermissionsModel.model.rawCollection());
export const Roles = new RolesRaw(RolesModel.model.rawCollection());
Expand All @@ -71,3 +73,4 @@ export const CustomSounds = new CustomSoundsRaw(CustomSoundsModel.model.rawColle
export const CustomUserStatus = new CustomUserStatusRaw(CustomUserStatusModel.model.rawCollection());
export const LivechatAgentActivity = new LivechatAgentActivityRaw(LivechatAgentActivityModel.model.rawCollection());
export const Statistics = new StatisticsRaw(StatisticsModel.model.rawCollection());
export const NotificationQueue = new NotificationQueueRaw(NotificationQueueModel.model.rawCollection());
Loading