diff --git a/functions/upcomingRoom-Message-Notification/src/main.js b/functions/upcomingRoom-Message-Notification/src/main.js index 9b7c3c9..015c683 100644 --- a/functions/upcomingRoom-Message-Notification/src/main.js +++ b/functions/upcomingRoom-Message-Notification/src/main.js @@ -1,61 +1,108 @@ const sdk = require("node-appwrite"); - const admin = require('firebase-admin'); const { getMessaging } = require('firebase-admin/messaging'); const serviceAccount = require("./resonate-service-account.json"); + const app = admin.initializeApp({ credential: admin.credential.cert(serviceAccount) }); module.exports = async function ({ req, res, log, error }) { - var subscribersTokens = []; + const subscribersTokens = []; const client = new sdk.Client(); const database = new sdk.Databases(client); const query = sdk.Query; - const { roomId, payload } = JSON.parse(req.body); client.setEndpoint( process.env.APPWRITE_ENDPOINT ?? 'https://cloud.appwrite.io/v1' ) .setProject(process.env.APPWRITE_FUNCTION_PROJECT_ID) .setKey(process.env.APPWRITE_API_KEY); - log(process.env.APPWRITE_FUNCTION_PROJECT_ID); - - log("Send Notification"); - log(roomId); - log(payload); - let subscriberList = await database.listDocuments(process.env.UpcomingRoomsDataBaseID, process.env.SubscriberCollectionID, [query.equal('upcomingRoomId', [roomId])]); - log("here as well") - subscriberList.documents.forEach(subscriber => { - for (const token of subscriber["registrationTokens"]) { - subscribersTokens.push(token); + + try { + const body = typeof req.body === 'string' ? JSON.parse(req.body) : req.body; + if (!roomId || typeof roomId !== 'string') { + log('Invalid roomId: must be a non-empty string'); + return res.json({ message: 'Invalid roomId' }, 400); } - }); - let document = await database.getDocument(process.env.UpcomingRoomsDataBaseID, process.env.UpcomingRoomsCollectionID, roomId); - for (const creator_token of document["creator_fcm_tokens"]) { - subscribersTokens.push(creator_token); - } - log(subscribersTokens); - const message = { - notification: payload, - tokens: subscribersTokens, - priority: "high", - android: { - priority: "high" + + if (!payload || typeof payload !== 'object' || !payload.title || !payload.body) { + log('Invalid payload: must be an object with title and body'); + return res.json({ message: 'Invalid payload' }, 400); + } + + if (!roomId || !payload) { + return res.json({ + message: 'Missing required fields', + error: 'roomId and payload are required' + }, 400); } - }; - getMessaging(app).sendEachForMulticast(message) - .then((response) => { + + log(`Sending notification for room: ${roomId}`); + + const subscriberList = await database.listDocuments( + process.env.UpcomingRoomsDataBaseID, + process.env.SubscriberCollectionID, + [query.equal('upcomingRoomId', [roomId])] + ); + + subscriberList.documents.forEach(subscriber => { + if (subscriber["registrationTokens"] && Array.isArray(subscriber["registrationTokens"])) { + subscribersTokens.push(...subscriber["registrationTokens"]); + } + }); + + const roomDocument = await database.getDocument( + process.env.UpcomingRoomsDataBaseID, + process.env.UpcomingRoomsCollectionID, + roomId + ); + + if (roomDocument["creator_fcm_tokens"] && Array.isArray(roomDocument["creator_fcm_tokens"])) { + subscribersTokens.push(...roomDocument["creator_fcm_tokens"]); + } + + // Deduplicate and validate tokens (must be non-empty strings) + const uniqueTokens = [...new Set(subscribersTokens)].filter(token => typeof token === 'string' && token.trim().length > 0); + + if (uniqueTokens.length > 0) { + const message = { + notification: payload, + tokens: uniqueTokens, + android: { + priority: "high" + }, + apns: { + headers: { + "apns-priority": "10" + } + } + }; + + const response = await getMessaging(app).sendEachForMulticast(message); if (response.failureCount > 0) { - log('Failed'); + log(`Failed to send ${response.failureCount} notifications`); + response.responses.forEach((resp, idx) => { + if (!resp.success) { + log(`Failure for token ${uniqueTokens[idx]}: ${resp.error}`); + } + }); } else { log('Notifications were sent successfully'); } + } else { + log('No valid tokens found to send notifications.'); + } + + return res.json({ + message: 'Notification process completed' }); - return res.json({ - message: 'Notification sent' - }); + } catch (err) { + error(`Error in upcomingRoom-Message-Notification: ${err.message}`); + return res.json({ + message: 'Error occurred', + error: err.message + }, 500); + } } - - diff --git a/functions/upcomingRoom-isTime-checker/src/main.js b/functions/upcomingRoom-isTime-checker/src/main.js index 42ac55f..7662419 100644 --- a/functions/upcomingRoom-isTime-checker/src/main.js +++ b/functions/upcomingRoom-isTime-checker/src/main.js @@ -1,88 +1,81 @@ const sdk = require("node-appwrite"); +// const admin = require('firebase-admin'); -const admin = require('firebase-admin'); // const { getMessaging } = require('firebase-admin/messaging'); // const serviceAccount = require("./resonate-service-account.json"); // const app = admin.initializeApp({ // credential: admin.credential.cert(serviceAccount) // }); -module.exports = async function ({ req, res, log }) { - var subscribersTokens = []; +module.exports = async function ({ req, res, log, error }) { const client = new sdk.Client(); const database = new sdk.Databases(client); - const query = sdk.Query; - log("here"); + client.setEndpoint( process.env.APPWRITE_ENDPOINT ?? 'https://cloud.appwrite.io/v1' ) .setProject(process.env.APPWRITE_FUNCTION_PROJECT_ID) .setKey(process.env.APPWRITE_API_KEY); - log(process.env.APPWRITE_FUNCTION_PROJECT_ID); - log("here also"); - let upcomingRoomsList = await database.listDocuments(process.env.UpcomingRoomsDataBaseID, process.env.UpcomingRoomsCollectionID); - log("here as well"); - for (const document of upcomingRoomsList.documents) { - log("now here"); - var scheduledDateTime = document["scheduledDateTime"]; - log(scheduledDateTime); - var splittedDateTime = scheduledDateTime.split('T'); - var extractedDate = splittedDateTime[0]; - var extractedTime = splittedDateTime[1]; - var SplittingDate = extractedDate.split("-"); - const year = Number(SplittingDate[0]); - const month = Number(SplittingDate[1]); - const day = Number(SplittingDate[2]); - var SplittingTime = extractedTime.split(":"); - const hour = Number(SplittingTime[0]); - const minutes = Number(SplittingTime[1]); - const upcomingRoomDate = Date.UTC(year, month - 1, day, hour, minutes); - log(upcomingRoomDate); - const now = new Date(); - const nowTime = now.getTime(); - log(nowTime); - var timeLeft = upcomingRoomDate - nowTime; - var timeLeftInMinutes = timeLeft / (1000 * 60); - log(timeLeftInMinutes); - if (timeLeftInMinutes <= 5 && timeLeftInMinutes >= -5 && document["isTime"] == false) { - await database.updateDocument(process.env.UpcomingRoomsDataBaseID, process.env.UpcomingRoomsCollectionID, document.$id, { - "isTime": true - }) - // log("Send Notification"); - // let subscriberList = await database.listDocuments(process.env.UpcomingRoomsDataBaseID, process.env.SubscriberCollectionID, [query.equal('upcomingRoomId', [document.$id])]); - // log("here as well") - // subscriberList.documents.forEach(subscriber => { - // for (const token of subscriber["registrationTokens"]) { - // subscribersTokens.push(token); - // } - // }); - // for (const creator_token of document["creator_fcm_tokens"]) { - // subscribersTokens.push(creator_token); - // } - // log(subscribersTokens); - // const message = { - // notification: { - // title: 'Room Reminder', - // body: `The room ${document["name"]} will Start Soon` - // }, - // tokens: subscribersTokens, - // priority: "high", - // android: { - // priority: "high" - // } - // }; - // getMessaging(app).sendEachForMulticast(message) - // .then((response) => { - // if (response.failureCount > 0) { - // log('Failed'); - // } else { - // log('Notifications were sent successfully'); - // } - // }); + try { + const upcomingRoomsList = await database.listDocuments( + process.env.UpcomingRoomsDataBaseID, + process.env.UpcomingRoomsCollectionID + ); + + for (const document of upcomingRoomsList.documents) { + const scheduledDateTime = document["scheduledDateTime"]; + + if (!scheduledDateTime) { + log(`Skipping document ${document.$id}: missing scheduledDateTime`); + continue; + } + + // Use standard Date parsing (handles ISO 8601 correctly) + const upcomingRoomDate = new Date(scheduledDateTime).getTime(); + + if (isNaN(upcomingRoomDate)) { + log(`Skipping document ${document.$id}: invalid date format "${scheduledDateTime}"`); + continue; + } + + const nowTime = new Date().getTime(); + + const timeLeft = upcomingRoomDate - nowTime; + const timeLeftInMinutes = timeLeft / (1000 * 60); + + // Check if time is within +/- 5 minutes and not yet marked + if (timeLeftInMinutes <= 5 && timeLeftInMinutes >= -5 && document["isTime"] === false) { + await database.updateDocument( + process.env.UpcomingRoomsDataBaseID, + process.env.UpcomingRoomsCollectionID, + document.$id, + { + "isTime": true + } + ); + + // Log explicitly when an action is taken + log(`Activated room: ${document.$id}`); + + /* + // Notification logic (legacy) + var subscribersTokens = []; + // ... (rest of the commented notification logic kept if needed for reference, + // essentially user logic seems to be to keep it commented for now as they didn't ask to implement it) + */ + } } + + return res.json({ + message: 'Time check completed successfully' + }); + + } catch (err) { + error(`Error in upcomingRoom-isTime-checker: ${err.message}`); + return res.json({ + message: 'Error occurred during time check', + error: err.message + }, 500); } - return res.json({ - message: 'set verified' - }); };