From ec2d16f3db9923074c923c6fd48116702aca00bf Mon Sep 17 00:00:00 2001 From: aeroreyna Date: Sun, 13 Mar 2022 23:01:49 -0700 Subject: [PATCH] Notifications --- notifications.js | 125 ++++++++++++++++++++++++++++++++++++++-------- package-lock.json | 29 +++++++++++ package.json | 1 + 3 files changed, 134 insertions(+), 21 deletions(-) diff --git a/notifications.js b/notifications.js index 02b9c65..1b433e1 100644 --- a/notifications.js +++ b/notifications.js @@ -1,5 +1,84 @@ const nodemailer = require("nodemailer"); const DBGetter = require("./mongoDB.js"); +const { Expo } = require('expo-server-sdk'); + + +const sendPushNotification = async (profileToken, body, data) => { + if(!profileToken) return 0; + let expo = new Expo(); + + // Create the messages that you want to send to clients + let messages = []; + let pushToken = profileToken; + if (!Expo.isExpoPushToken(pushToken)) { + console.error(`Push token ${pushToken} is not a valid Expo push token`); + return 0; + } + // Construct a message (see https://docs.expo.io/push-notifications/sending-notifications/) + messages.push({ + to: pushToken, + sound: 'default', + body: body, + data: data, + }) + let chunks = expo.chunkPushNotifications(messages); + let tickets = []; + (async () => { + for (let chunk of chunks) { + try { + let ticketChunk = await expo.sendPushNotificationsAsync(chunk); + console.log(ticketChunk); + tickets.push(...ticketChunk); + // NOTE: If a ticket contains an error code in ticket.details.error, you + // must handle it appropriately. The error codes are listed in the Expo + // documentation: + // https://docs.expo.io/push-notifications/sending-notifications/#individual-errors + } catch (error) { + console.error(error); + } + } + })(); + + let receiptIds = []; + for (let ticket of tickets) { + // NOTE: Not all tickets have IDs; for example, tickets for notifications + // that could not be enqueued will have error information and no receipt ID. + if (ticket.id) { + receiptIds.push(ticket.id); + } + } + + let receiptIdChunks = expo.chunkPushNotificationReceiptIds(receiptIds); + (async () => { + for (let chunk of receiptIdChunks) { + try { + let receipts = await expo.getPushNotificationReceiptsAsync(chunk); + console.log(receipts); + + // The receipts specify whether Apple or Google successfully received the + // notification and information about an error, if one occurred. + for (let receiptId in receipts) { + let { status, message, details } = receipts[receiptId]; + if (status === 'ok') { + continue; + } else if (status === 'error') { + console.error( + `There was an error sending a notification: ${message}` + ); + if (details && details.error) { + // The error codes are listed in the Expo documentation: + // https://docs.expo.io/push-notifications/sending-notifications/#individual-errors + // You must handle the errors appropriately. + console.error(`The error code is ${details.error}`); + } + } + } + } catch (error) { + console.error(error); + } + } + })(); +} const sendEmail = async (to, subject, html) => { let transporter = nodemailer.createTransport({ @@ -18,7 +97,7 @@ const sendEmail = async (to, subject, html) => { subject, html, }).catch(console.error); - if(info && info.messageId) console.log("Email sent: %s", info.messageId); + if (info && info.messageId) console.log("Email sent: %s", info.messageId); }; const yourBookmarkedPostGotACommentTemplate = (post, userEmail, postProfile, senderProfile, bookedProfile, message) => { @@ -151,32 +230,32 @@ const yourGroupHasARequestTemplate = (groupProfile, ownerEmail, senderProfile) = sendEmail(ownerEmail, subject, html) } -const convertLinks = ( input ) => { +const convertLinks = (input) => { let text = input; - const linksFound = text.match( /(?:www|https?)[^\s]+/g ); + const linksFound = text.match(/(?:www|https?)[^\s]+/g); const aLink = []; - if ( linksFound != null ) { + if (linksFound != null) { - for ( let i=0; i' ) + let youtubeID = replace.split('/').slice(-1)[0]; + aLink.push('
') } - else if ( linkText.match( /vimeo/ ) ) { - let vimeoID = replace.split( '/' ).slice(-1)[0]; - aLink.push( '
' ) + else if (linkText.match(/vimeo/)) { + let vimeoID = replace.split('/').slice(-1)[0]; + aLink.push('
') } else { - aLink.push( '' + linkText + '' ); + aLink.push('' + linkText + ''); } - text = text.split( linksFound[i] ).map(item => { return aLink[i].includes('iframe') ? item.trim() : item } ).join( aLink[i] ); + text = text.split(linksFound[i]).map(item => { return aLink[i].includes('iframe') ? item.trim() : item }).join(aLink[i]); } return text; @@ -203,7 +282,7 @@ const broadcastNews = async (post, users) => {

Blessings

`; - + for (const user of users) { console.log(user, 'Sending news') await sendEmail(user.username, subject, html); @@ -226,6 +305,7 @@ const Notifications = { const bookedProfile = subscribed[index]; if (bookedProfile._id == senderProfile._id) return 0; const notifBody = `${senderProfile.profile.firstName} commented in a post you follow`; + sendPushNotification(bookedProfile.token, notifBody, {}); DB.addNotification(bookedProfile._id, notifBody, post._id, post.comments.length - 1); yourBookmarkedPostGotACommentTemplate(post, userEmail, postProfile, senderProfile, bookedProfile, message); }); @@ -241,6 +321,7 @@ const Notifications = { } if (postProfile.isCourse || senderProfile._id == postProfile._id) return 0; //Course owners do not need to receive notifs const notifBody = `${senderProfile.profile.firstName} commented in your post`; + sendPushNotification(postProfile.token, notifBody, {}); DB.addNotification(post.profileid, notifBody, postId, post.comments.length - 1); return youGotANewPostCommentTemplate(post, userEmail, postProfile, senderProfile, message); }, @@ -258,32 +339,34 @@ const Notifications = { let userProfile = subscribed[index]; //who is this email sending to if (userProfile._id == senderProfile._id) return 0; //avoid sending self notifications const notifBody = `${senderProfile.profile.firstName} post in the group ${groupProfile.profile.firstName} ${groupProfile.profile.lastName}`; + sendPushNotification(userProfile.token, notifBody, {}); DB.addNotification(userProfile._id, notifBody, post._id); yourGroupGotANewPostTemplate(groupProfile, userEmail, userProfile, senderProfile, message); }); }, async youGotANewPost(post) { - const toProfileId = post.toProfile; + const toProfileId = post.toProfile; const whoPostedId = post.profileid; const message = post.content; const DB = await DBGetter.getDB; const profile = await DB.getProfileCache(toProfileId); const user = await DB.getUserById(profile.userid); const senderProfile = await DB.getProfileCache(whoPostedId); - if(profile.isGroup) { + if (profile.isGroup) { return this.yourGroupGotANewPost(profile, senderProfile, message, post); } - if(post.nonOrganicType == 'News'){ + if (post.nonOrganicType == 'News') { const emails = await DB.getAllEmails(); return this.broadcastNews(post, emails); } const notifBody = `${senderProfile.profile.firstName} post in your profile`; + sendPushNotification(profile.token, notifBody, {}); DB.addNotification(toProfileId, notifBody, post._id); return youGotANewPostTemplate(profile, user.username, senderProfile, message); }, youHaveAnInvitation, broadcastNews, - async yourGroupHasARequest(requesterProfileId, groupId){ + async yourGroupHasARequest(requesterProfileId, groupId) { const DB = await DBGetter.getDB; const requesterProfile = await DB.getProfileCache(requesterProfileId); const groupProfile = await DB.getProfileCache(groupId); diff --git a/package-lock.json b/package-lock.json index b015614..1913281 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "cookie-parser": "^1.4.5", "cors": "^2.8.5", "dotenv": "^8.2.0", + "expo-server-sdk": "^3.6.0", "express": "^4.17.1", "mongodb": "^3.6.3", "nodemailer": "^6.6.3", @@ -378,6 +379,15 @@ "node": ">= 0.6" } }, + "node_modules/expo-server-sdk": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/expo-server-sdk/-/expo-server-sdk-3.6.0.tgz", + "integrity": "sha512-GyA0BTcFBKk/5gTEO4WOScP9hEttR+GitrcOIl7XwXwE1FHFvbluKiUc9yEjsfEYMgyd78+XhSpGVGQnutGOdA==", + "dependencies": { + "node-fetch": "^2.6.0", + "promise-limit": "^2.7.0" + } + }, "node_modules/express": { "version": "4.17.1", "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", @@ -973,6 +983,11 @@ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, + "node_modules/promise-limit": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/promise-limit/-/promise-limit-2.7.0.tgz", + "integrity": "sha512-7nJ6v5lnJsXwGprnGXga4wx6d1POjvi5Qmf1ivTRxTjH4Z/9Czja/UCMLVmB9N93GeWOU93XaFaEt6jbuoagNw==" + }, "node_modules/proper-lockfile": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-2.0.1.tgz", @@ -1627,6 +1642,15 @@ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" }, + "expo-server-sdk": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/expo-server-sdk/-/expo-server-sdk-3.6.0.tgz", + "integrity": "sha512-GyA0BTcFBKk/5gTEO4WOScP9hEttR+GitrcOIl7XwXwE1FHFvbluKiUc9yEjsfEYMgyd78+XhSpGVGQnutGOdA==", + "requires": { + "node-fetch": "^2.6.0", + "promise-limit": "^2.7.0" + } + }, "express": { "version": "4.17.1", "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", @@ -2079,6 +2103,11 @@ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, + "promise-limit": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/promise-limit/-/promise-limit-2.7.0.tgz", + "integrity": "sha512-7nJ6v5lnJsXwGprnGXga4wx6d1POjvi5Qmf1ivTRxTjH4Z/9Czja/UCMLVmB9N93GeWOU93XaFaEt6jbuoagNw==" + }, "proper-lockfile": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-2.0.1.tgz", diff --git a/package.json b/package.json index bcc93af..fbae892 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "cookie-parser": "^1.4.5", "cors": "^2.8.5", "dotenv": "^8.2.0", + "expo-server-sdk": "^3.6.0", "express": "^4.17.1", "mongodb": "^3.6.3", "nodemailer": "^6.6.3",