diff --git a/src/controllers/tanksController.js b/src/controllers/tanksController.js index 7141e94c..ee916bb0 100644 --- a/src/controllers/tanksController.js +++ b/src/controllers/tanksController.js @@ -1727,10 +1727,13 @@ admin.initializeApp({ // Event listener for motorStart eventEmitter.on( 'motorStart', - async (fcmTokens, tankName, blockName, startTime, motorOnType, manual_threshold_time, typeOfWater) => { + async (customerId, fcmTokens, tankName, blockName, startTime, motorOnType, manual_threshold_time, typeOfWater) => { try { + // Log the FCM tokens to check their format + console.log("FCM Tokens:", fcmTokens); + // Retrieve the user information - const users = await User.find({ fcmIds: { $in: fcmTokens } }); + const users = await User.find({ customerId: customerId, fcmIds: { $in: fcmTokens } }); console.log("users", users); const userNames = users.map(user => user.username).join(', '); console.log("userNames", userNames); @@ -1756,20 +1759,93 @@ eventEmitter.on( `Will stop after: '${manual_threshold_time}' mins`; // Send the notification - await sendNotification(fcmTokens, 'Arminta Water Management', message); + await sendNotification(customerId, fcmTokens, 'Arminta Water Management', message); } catch (error) { console.error('Error in motorStart event:', error); } } ); +// eventEmitter.on( +// "motorStart", +// async (customerId, fcmTokens, tankName, blockName, startTime, motorOnType, manual_threshold_time, typeOfWater) => { +// try { +// // Flatten and clean up FCM tokens +// const flatFcmTokens = fcmTokens.flat(); +// const validFcmTokens = flatFcmTokens.filter(token => typeof token === 'string' && token.trim() !== ''); + +// // Retrieve users with matching `customerId` and valid `fcmIds` +// const users = await User.find({ +// customerId: customerId, +// fcmIds: { $in: validFcmTokens } // Ensure fcmId exists in the database +// }); + +// console.log("Users Found:", users); + +// // Check if we found any users +// if (!users.length) { +// console.log("No users found for motorStart event."); +// return; +// } + +// // Extract usernames and their valid FCM tokens +// const matchedTokens = users.flatMap(user => user.fcmIds.filter(token => validFcmTokens.includes(token))); + +// console.log("Matched FCM Tokens:", matchedTokens); + +// // Check if there are valid tokens to send notifications +// if (matchedTokens.length === 0) { +// console.log("No valid FCM tokens found for sending notifications."); +// return; +// } + +// const startMethod = motorOnType === "Mobile APP" ? "Mobile APP" : "Manual"; +// const motorName = `${tankName}-${blockName}-${typeOfWater}`; +// // Prepare the message +// // const message = `Motor ${motorOnType} started in tank ${tankName} at ${startTime}.`; +// const message = +// `🚰 Motor Name: ${motorName}\n` + +// `🚰 Tank Name: '${tankName}'\n` + +// `🏢 Block Name: '${blockName}'\n` + +// `👤 Started by: ${userNames}\n` + +// `📱 Mode: '${startMethod}'\n` + +// `🕒 Pump started at: ${startTime} \n` + +// `Will stop after: '${manual_threshold_time}' mins`; + + +// // Send notification to the matched users +// await sendNotification(customerId, matchedTokens, "Motor Start Notification", message); + +// } catch (error) { +// console.error("Error in motorStart event:", error); +// } +// } +// ); + + + + + + +// Event listener for motorStop // Event listener for motorStop eventEmitter.on( 'motorStop', - async (fcmTokens, tankName, blockName, stopTime, motorOnType, totalWaterPumped, typeOfWater) => { + async (customerId, fcmTokens, tankName, blockName, stopTime, motorOnType, totalWaterPumped, typeOfWater) => { try { - // Retrieve the user information - const users = await User.find({ fcmIds: { $in: fcmTokens } }); + // Log the FCM tokens to check their format + console.log("FCM Tokens:", fcmTokens); + + // Retrieve the user information based on customerId + const users = await User.find({ customerId: customerId, fcmIds: { $in: fcmTokens } }); + console.log("users", users); + + // If no users are found, log a message and return + if (users.length === 0) { + console.log(`No users found for customerId: ${customerId}`); + return; + } + const userNames = users.map(user => user.username).join(', '); const stopMethod = motorOnType === "Mobile APP" ? "Mobile APP" : "Manual"; @@ -1790,16 +1866,17 @@ eventEmitter.on( `👤 Stopped by: ${userNames}\n` + `📱 Mode: '${stopMethod}'\n` + `🕒 Pump stopped at: ${stopTime}\n` + - `💧 Total water pumped: ${totalWaterPumped} liters\n`; // Ensure this line is properly terminated + `💧 Total water pumped: ${totalWaterPumped} liters\n`; // Send the notification - await sendNotification(fcmTokens, 'Arminta Water Management', message); + await sendNotification(customerId, fcmTokens, 'Arminta Water Management', message); } catch (error) { console.error('Error in motorStop event:', error); } } ); + eventEmitter.on( 'motorStartAutomatic', async (fcmTokens, tankName, blockName, startTime, motorOnType, manual_threshold_time, typeOfWater,threshold) => { @@ -2227,45 +2304,153 @@ const emitWithTimestamp = (eventName, fcmTokens, motorId, waterLevel) => { // } // }; -const sendNotification = async (fcmIds, title, body) => { +// const sendNotification = async (fcmIds, title, body) => { +// try { +// if (!fcmIds || fcmIds.length === 0) { +// throw new Error('No FCM tokens provided.'); +// } + +// const flatTokens = fcmIds.flat(); +// if (flatTokens.length === 0) { +// throw new Error('Flattened FCM token list is empty.'); +// } + +// // Iterate over each token and send individually +// const promises = flatTokens.map(async (token) => { +// try { +// const response = await admin.messaging().send({ +// notification: { title, body }, +// token, +// data: { +// 'target': 'tank_levels', +// }, +// }); +// console.log(`Notification sent successfully to token: ${token}`, response); +// } catch (error) { +// console.error(`Failed to send notification to token: ${token}`, error); +// // Check for specific error indicating an invalid token +// if (error.code === 'messaging/registration-token-not-registered') { +// // Remove the invalid token from the database +// await User.updateOne( +// { fcmIds: token }, // Ensure you're targeting the right user with the invalid token +// { $pull: { fcmIds: token } } // Remove the invalid token +// ); +// console.log(`Removed invalid token: ${token}`); +// } +// } +// }); + +// await Promise.all(promises); +// } catch (error) { +// console.error('Error sending notifications:', error); +// } +// }; + +const sendNotification = async (customerId, fcmIds, title, body) => { try { - if (!fcmIds || fcmIds.length === 0) { - throw new Error('No FCM tokens provided.'); + if (!customerId) { + throw new Error("Customer ID is required."); + } + + if (!Array.isArray(fcmIds) || fcmIds.length === 0) { + throw new Error("No FCM tokens provided or invalid format."); } - const flatTokens = fcmIds.flat(); + // Safely flatten the tokens + const flatTokens = fcmIds.flat ? fcmIds.flat() : fcmIds; // Use flat() if available if (flatTokens.length === 0) { - throw new Error('Flattened FCM token list is empty.'); + throw new Error("Flattened FCM token list is empty."); } - // Iterate over each token and send individually - const promises = flatTokens.map(async (token) => { - try { - const response = await admin.messaging().send({ - notification: { title, body }, - token, - data: { - 'target': 'tank_levels', - }, - }); - console.log(`Notification sent successfully to token: ${token}`, response); - } catch (error) { - console.error(`Failed to send notification to token: ${token}`, error); - // Check for specific error indicating an invalid token - if (error.code === 'messaging/registration-token-not-registered') { - // Remove the invalid token from the database - await User.updateOne( - { fcmIds: token }, // Ensure you're targeting the right user with the invalid token - { $pull: { fcmIds: token } } // Remove the invalid token - ); - console.log(`Removed invalid token: ${token}`); + // Fetch user notification settings + const users = await User.find({ customerId }).select("fcmIds notificationPreference lastNotificationSent"); + + // Iterate over users to send notifications based on their preferences + const promises = users.map(async (user) => { + const { fcmIds: userFcmIds, notificationPreference, lastNotificationSent } = user; + + // Check if userFcmIds is an array + if (!Array.isArray(userFcmIds)) { + console.log(`Invalid fcmIds for customer ID: ${customerId}`); + return; + } + + // Filter tokens that belong to the user + const validTokens = flatTokens.filter(token => userFcmIds.includes(token)); + + if (validTokens.length === 0) { + console.log(`No matching FCM tokens for customer ID: ${customerId}`); + return; + } + + // Handle notification preference + if (notificationPreference === "never") { + console.log(`Notifications disabled for customer ID: ${customerId}`); + return; + } + + const now = new Date(); + const lastSent = new Date(lastNotificationSent || 0); + + // If preference is not "always", check the timing + if (notificationPreference !== "always") { + let minInterval = 0; + + switch (notificationPreference) { + case "6_hours": + minInterval = 6 * 60 * 60 * 1000; // 6 hours + break; + case "8_hours": + minInterval = 8 * 60 * 60 * 1000; // 8 hours + break; + case "1_month": + minInterval = 30 * 24 * 60 * 60 * 1000; // 1 month + break; + } + + // Skip sending if the time restriction hasn't passed + if (now - lastSent < minInterval) { + console.log(`Skipping notification for customer ID: ${customerId} due to time restriction.`); + return; } } + + // Send notifications + const notificationPromises = validTokens.map(async (token) => { + try { + const response = await admin.messaging().send({ + notification: { title, body }, + token, + data: { target: "tank_levels" }, + }); + + console.log(`Notification sent successfully to token: ${token}`, response); + } catch (error) { + console.error(`Failed to send notification to token: ${token}`, error); + + // Handle token errors + if (error.code === "messaging/registration-token-not-registered") { + await User.updateOne( + { customerId }, + { $pull: { fcmIds: token } } // Remove invalid token + ); + console.log(`Removed invalid token: ${token}`); + } + } + }); + + await Promise.all(notificationPromises); + + // Update lastNotificationSent timestamp if not "always" + if (notificationPreference !== "always") { + await User.updateOne({ customerId }, { lastNotificationSent: now }); + } }); await Promise.all(promises); + } catch (error) { - console.error('Error sending notifications:', error); + console.error("Error sending notifications:", error); } }; @@ -2274,6 +2459,7 @@ const sendNotification = async (fcmIds, title, body) => { + // const sendPushNotification = async (registrationToken, title, body) => { // const message = { // notification: { @@ -2841,20 +3027,22 @@ exports.motorAction = async (req, reply) => { // await checkWaterLevelsAndNotify(customerId, tankName, receiverTank.tankLocation, fcmToken); - if (!notificationSentStatus.motorStart) { - eventEmitter.emit( - 'motorStart', - fcmToken, - tankName, - blockName, - startTime, - "Mobile APP", - manual_threshold_time, - typeOfWater - ); - - notificationSentStatus.motorStart = true; // Set flag to true to prevent duplicate notifications - } + if (!notificationSentStatus.motorStart) { + eventEmitter.emit( + 'motorStart', + customerId, + fcmToken, // Ensure this is an array of tokens + tankName, + blockName, + startTime, + "Mobile APP", + manual_threshold_time, + typeOfWater + ); + + notificationSentStatus.motorStart = true; // Prevent duplicate notifications + } + // Start checking water level every 30 minutes if (!waterLevelCheckInterval) { waterLevelCheckInterval = setInterval(async () => { @@ -2880,6 +3068,7 @@ exports.motorAction = async (req, reply) => { const totalWaterPumped = await calculateTotalPumpedWater(customerId, motorId, start_instance_id); // A function to calculate total water pumped eventEmitter.emit( 'motorStop', + customerId, fcmToken, tankName, blockName, diff --git a/src/models/User.js b/src/models/User.js index 67e8310f..59438c45 100644 --- a/src/models/User.js +++ b/src/models/User.js @@ -123,6 +123,15 @@ const userSchema = new mongoose.Schema( fcmIds: [{ type: String }], // Changed to an array of strings deviceId: { type: String, default: null }, + notificationPreference: { + type: String, + enum: ["never", "always", "6_hours", "8_hours", "1_month"], + default: "always", // Default is now "always" + }, + lastNotificationSent: { + type: Date, + default: null, // Initially, no notifications sent + }, createdAt: { type: Date, default: function () {