Push Notifications Fixes - Part 2 (#12092)

* push notifications: handle some more error codes and when the user is loaded using .lean()

* fix lint

* do not send push notification if message is missing
This commit is contained in:
Matteo Pagliazzi
2020-04-15 21:36:53 +02:00
committed by GitHub
parent cc04761c24
commit c5aeab652d
8 changed files with 43 additions and 20 deletions

View File

@@ -301,7 +301,7 @@
<h4 class="popover-content-title">{{ item.text }}</h4> <h4 class="popover-content-title">{{ item.text }}</h4>
<questInfo <questInfo
:quest="item" :quest="item"
:popoverVersion="true" :popover-version="true"
/> />
</div> </div>
</span> </span>

View File

@@ -1,6 +1,6 @@
import { model as User } from '../models/user'; // eslint-disable-line import/no-cycle import { model as User } from '../models/user'; // eslint-disable-line import/no-cycle
import { getUserInfo } from './email'; // eslint-disable-line import/no-cycle import { getUserInfo } from './email'; // eslint-disable-line import/no-cycle
import { sendNotification as sendPushNotification } from './pushNotifications'; import { sendNotification as sendPushNotification } from './pushNotifications'; // eslint-disable-line import/no-cycle
export async function getAuthorEmailFromMessage (message) { export async function getAuthorEmailFromMessage (message) {
const authorId = message.uuid; const authorId = message.uuid;
@@ -30,6 +30,9 @@ export async function sendChatPushNotifications (user, group, message, mentions,
if (mentions && mentions.includes(`@${member.auth.local.username}`) && member.preferences.pushNotifications.mentionParty !== false) { if (mentions && mentions.includes(`@${member.auth.local.username}`) && member.preferences.pushNotifications.mentionParty !== false) {
return; return;
} }
if (!message.unformattedText) return;
sendPushNotification( sendPushNotification(
member, member,
{ {

View File

@@ -1,6 +1,6 @@
import { mapInboxMessage, inboxModel as Inbox } from '../../models/message'; import { mapInboxMessage, inboxModel as Inbox } from '../../models/message';
import { getUserInfo, sendTxn as sendTxnEmail } from '../email'; // eslint-disable-line import/no-cycle import { getUserInfo, sendTxn as sendTxnEmail } from '../email'; // eslint-disable-line import/no-cycle
import { sendNotification as sendPushNotification } from '../pushNotifications'; import { sendNotification as sendPushNotification } from '../pushNotifications'; // eslint-disable-line import/no-cycle
const PM_PER_PAGE = 10; const PM_PER_PAGE = 10;
@@ -14,7 +14,7 @@ export async function sentMessage (sender, receiver, message, translate) {
]); ]);
} }
if (receiver.preferences.pushNotifications.newPM !== false) { if (receiver.preferences.pushNotifications.newPM !== false && messageSent.unformattedText) {
sendPushNotification( sendPushNotification(
receiver, receiver,
{ {

View File

@@ -3,7 +3,7 @@ import { // eslint-disable-line import/no-cycle
getUserInfo, getUserInfo,
sendTxn as txnEmail, sendTxn as txnEmail,
} from '../email'; } from '../email';
import { sendNotification as sendPushNotification } from '../pushNotifications'; import { sendNotification as sendPushNotification } from '../pushNotifications'; // eslint-disable-line import/no-cycle
import shared from '../../../common'; import shared from '../../../common';
function getGiftMessage (data, byUsername, gemAmount, language) { function getGiftMessage (data, byUsername, gemAmount, language) {

View File

@@ -16,7 +16,7 @@ import {
NotFound, NotFound,
} from '../errors'; } from '../errors';
import shared from '../../../common'; import shared from '../../../common';
import { sendNotification as sendPushNotification } from '../pushNotifications'; import { sendNotification as sendPushNotification } from '../pushNotifications'; // eslint-disable-line import/no-cycle
// @TODO: Abstract to shared/constant // @TODO: Abstract to shared/constant
const JOINED_GROUP_PLAN = 'joined group plan'; const JOINED_GROUP_PLAN = 'joined group plan';

View File

@@ -3,6 +3,9 @@ import nconf from 'nconf';
import apn from 'apn'; import apn from 'apn';
import gcmLib from 'node-gcm'; // works with FCM notifications too import gcmLib from 'node-gcm'; // works with FCM notifications too
import logger from './logger'; import logger from './logger';
import { // eslint-disable-line import/no-cycle
model as User,
} from '../models/user';
const FCM_API_KEY = nconf.get('PUSH_CONFIGS_FCM_SERVER_API_KEY'); const FCM_API_KEY = nconf.get('PUSH_CONFIGS_FCM_SERVER_API_KEY');
const fcmSender = FCM_API_KEY ? new gcmLib.Sender(FCM_API_KEY) : undefined; const fcmSender = FCM_API_KEY ? new gcmLib.Sender(FCM_API_KEY) : undefined;
@@ -18,7 +21,7 @@ const apnProvider = APN_ENABLED ? new apn.Provider({
}) : undefined; }) : undefined;
function removePushDevice (user, pushDevice) { function removePushDevice (user, pushDevice) {
return user.update({ return User.update({ _id: user._id }, {
$pull: { pushDevices: { regId: pushDevice.regId } }, $pull: { pushDevices: { regId: pushDevice.regId } },
}).exec().catch(err => { }).exec().catch(err => {
logger.error(err, `Error removing pushDevice ${pushDevice.regId} for user ${user._id}`); logger.error(err, `Error removing pushDevice ${pushDevice.regId} for user ${user._id}`);
@@ -80,10 +83,18 @@ export function sendNotification (user, details = {}) {
// The regId is not valid anymore, remove it // The regId is not valid anymore, remove it
if (failed === 'NotRegistered') { if (failed === 'NotRegistered') {
removePushDevice(user, pushDevice); removePushDevice(user, pushDevice);
logger.error(new Error('FCM error, invalid pushDevice'), { logger.error(new Error('FCM error, unregistered pushDevice'), {
response, regId: pushDevice.regId, userId: user._id, regId: pushDevice.regId, userId: user._id,
}); });
} else { } else {
// An invalid token was registered by mistake
// Remove it but log the error differently so that it can be distinguished
// from when failed === NotRegistered
// Blacklisted can happen in some rare cases,
// see https://stackoverflow.com/questions/42136122/why-does-firebase-push-token-return-blacklisted
if (failed === 'InvalidRegistration' || pushDevice.regId === 'BLACKLISTED') {
removePushDevice(user, pushDevice);
}
logger.error(new Error('FCM error'), { logger.error(new Error('FCM error'), {
response, regId: pushDevice.regId, userId: user._id, response, regId: pushDevice.regId, userId: user._id,
}); });
@@ -110,7 +121,7 @@ export function sendNotification (user, details = {}) {
// Handle failed push notifications deliveries // Handle failed push notifications deliveries
response.failed.forEach(failure => { response.failed.forEach(failure => {
if (failure.error) { // generic error if (failure.error) { // generic error
logger.error(new Error('APN error'), { logger.error(new Error('Unhandled APN error'), {
response, regId: pushDevice.regId, userId: user._id, response, regId: pushDevice.regId, userId: user._id,
}); });
} else { // rejected } else { // rejected
@@ -119,10 +130,16 @@ export function sendNotification (user, details = {}) {
const { reason } = failure.response; const { reason } = failure.response;
if (reason === 'Unregistered') { if (reason === 'Unregistered') {
removePushDevice(user, pushDevice); removePushDevice(user, pushDevice);
logger.error(new Error('APN error, invalid pushDevice'), { logger.error(new Error('APN error, unregistered pushDevice'), {
response, regId: pushDevice.regId, userId: user._id, regId: pushDevice.regId, userId: user._id,
}); });
} else { } else {
if (reason === 'BadDeviceToken') {
// An invalid token was registered by mistake
// Remove it but log the error differently so that it can be distinguished
// from when reason === Unregistered
removePushDevice(user, pushDevice);
}
logger.error(new Error('APN error'), { logger.error(new Error('APN error'), {
response, regId: pushDevice.regId, userId: user._id, response, regId: pushDevice.regId, userId: user._id,
}); });

View File

@@ -11,7 +11,7 @@ import { // eslint-disable-line import/no-cycle
import { removeFromArray } from '../libs/collectionManipulators'; import { removeFromArray } from '../libs/collectionManipulators';
import shared from '../../common'; import shared from '../../common';
import { sendTxn as txnEmail } from '../libs/email'; // eslint-disable-line import/no-cycle import { sendTxn as txnEmail } from '../libs/email'; // eslint-disable-line import/no-cycle
import { sendNotification as sendPushNotification } from '../libs/pushNotifications'; import { sendNotification as sendPushNotification } from '../libs/pushNotifications'; // eslint-disable-line import/no-cycle
import { syncableAttrs, setNextDue } from '../libs/taskManager'; import { syncableAttrs, setNextDue } from '../libs/taskManager';
const { Schema } = mongoose; const { Schema } = mongoose;

View File

@@ -28,7 +28,7 @@ import {
} from '../libs/errors'; } from '../libs/errors';
import baseModel from '../libs/baseModel'; import baseModel from '../libs/baseModel';
import { sendTxn as sendTxnEmail } from '../libs/email'; // eslint-disable-line import/no-cycle import { sendTxn as sendTxnEmail } from '../libs/email'; // eslint-disable-line import/no-cycle
import { sendNotification as sendPushNotification } from '../libs/pushNotifications'; import { sendNotification as sendPushNotification } from '../libs/pushNotifications'; // eslint-disable-line import/no-cycle
import { import {
syncableAttrs, syncableAttrs,
} from '../libs/taskManager'; } from '../libs/taskManager';
@@ -637,12 +637,15 @@ schema.methods.sendChat = function sendChat (options = {}) {
return; return;
} }
} }
sendPushNotification(member, {
identifier: 'chatMention', if (newChatMessage.unformattedText) {
title: `${user.profile.name} mentioned you in ${this.name}`, sendPushNotification(member, {
message: newChatMessage.unformattedText, identifier: 'chatMention',
payload: { type: this.type }, title: `${user.profile.name} mentioned you in ${this.name}`,
}); message: newChatMessage.unformattedText,
payload: { type: this.type },
});
}
}); });
} }
return newChatMessage; return newChatMessage;