Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
92 changes: 57 additions & 35 deletions functions/upcomingRoom-Message-Notification/src/main.js
Original file line number Diff line number Diff line change
@@ -1,61 +1,83 @@
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);
}
});
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"

try {
const { roomId, payload } = JSON.parse(req.body);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Handle cases where req.body may already be parsed or undefined.

In Appwrite Functions, req.body can be a pre-parsed object (depending on content-type headers) or undefined. Calling JSON.parse() on an object throws "Unexpected token o in JSON", which is confusing.

🔎 Proposed fix
-    const { roomId, payload } = JSON.parse(req.body);
+    const body = typeof req.body === 'string' ? JSON.parse(req.body) : req.body;
+    const { roomId, payload } = body;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const { roomId, payload } = JSON.parse(req.body);
const body = typeof req.body === 'string' ? JSON.parse(req.body) : req.body;
const { roomId, payload } = body;
🤖 Prompt for AI Agents
In functions/upcomingRoom-Message-Notification/src/main.js around line 23, avoid
blindly calling JSON.parse on req.body; instead check if req.body is undefined,
a string, or already an object. If undefined, set a safe default (e.g., {} or
return a 400); if typeof req.body === "string" parse it inside a try/catch and
handle parse errors (log and return an error response); if typeof req.body ===
"object" use it directly; finally validate that roomId and payload exist before
proceeding.


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"]);
}
};
getMessaging(app).sendEachForMulticast(message)
.then((response) => {

// Deduplicate tokens
const uniqueTokens = [...new Set(subscribersTokens)];

if (uniqueTokens.length > 0) {
const message = {
notification: payload,
tokens: uniqueTokens,
priority: "high",
android: {
priority: "high"
}
};

const response = await getMessaging(app).sendEachForMulticast(message);
if (response.failureCount > 0) {
log('Failed');
log(`Failed to send ${response.failureCount} notifications`);
} else {
log('Notifications were sent successfully');
}
} else {
log('No 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);
}
}


120 changes: 51 additions & 69 deletions functions/upcomingRoom-isTime-checker/src/main.js
Original file line number Diff line number Diff line change
@@ -1,88 +1,70 @@
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 }) {
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"];

// Use standard Date parsing (handles ISO 8601 correctly)
const upcomingRoomDate = new Date(scheduledDateTime).getTime();
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'
});
};