Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Professor/TA Invite Email #823

Open
wants to merge 20 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
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
8 changes: 5 additions & 3 deletions firebase.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@
"yarn --cwd \"$RESOURCE_DIR\" lint",
"yarn --cwd \"$RESOURCE_DIR\" build"
],
"source": "functions"
"source": "functions",
"runtime": "nodejs18"
},
"emulators": {
"functions": {
Expand All @@ -39,10 +40,11 @@
"port": 8080
},
"hosting": {
"port": 5000
"port": 5005
},
"ui": {
"enabled": true
}
}
}
}

3 changes: 3 additions & 0 deletions functions/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@ typings/

# Node.js dependency directory
node_modules/

# local runtime config for Twilio
.runtimeconfig.json
1 change: 1 addition & 0 deletions functions/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"firebase-admin": "^12.0.0",
"firebase-functions": "^4.8.2",
"firebase-tools": "11.18.0",
"mailtrap": "^3.3.0",
"twilio": "^3.71.3"
},
"devDependencies": {
Expand Down
131 changes: 79 additions & 52 deletions functions/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,25 @@
import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
import { Twilio } from 'twilio';
import { MailtrapClient } from 'mailtrap';

// Use admin SDK to enable writing to other parts of database
// const admin = require('firebase-admin');
admin.initializeApp();

const db = admin.firestore();

// Twilio Setup
const accountSid = functions.config().twilio.accountsid;
const authToken = functions.config().twilio.twilio_auth_token;
const twilioNumber = functions.config().twilio.twilionumber;

const client = new Twilio(accountSid, authToken);

/**
* Function that handles data and sends a text message to a requested phone number
*/
* Function that handles data and sends a text message to a requested phone number
*/
async function sendSMS (user: FireUser, message: string) {
// Twilio Setup
const accountSid = process.env.ACCOUNTSID as string;
const authToken = process.env.TWILIO_AUTH_TOKEN as string;
const twilioNumber = process.env.TWILIO_NUMBER;

const client = new Twilio(accountSid, authToken);

if(process.env.DATABASE === "staging") {
return;
}
Expand All @@ -40,10 +41,10 @@
}

/** Adds new roles to a user without them being in QMI's system
* Not inclusive: Still need to consider users that are
* already in the system. (THIS CASE IS HANDLED BY NOT INCLDUING THEM IN THE
* pendingUsers COLLECTION IN THE FIRST PLACE)
*/
* Not inclusive: Still need to consider users that are
* already in the system. (THIS CASE IS HANDLED BY NOT INCLDUING THEM IN THE
* pendingUsers COLLECTION IN THE FIRST PLACE)
*/
exports.onUserCreate = functions.firestore
.document('users/{userId}')
.onCreate(async (snap, context) => {
Expand Down Expand Up @@ -140,7 +141,7 @@
title: 'Student comment',
subtitle: "New student comment",
message: `${asker.firstName} commented \
on your assigned question`.trim(),
on your assigned question`.trim(),
createdAt: admin.firestore.Timestamp.now()
})
}).catch(() => {
Expand All @@ -149,13 +150,13 @@
title: 'Student comment',
subtitle: "New student comment",
message: `${asker.firstName} commented \
on your assigned question`.trim(),
on your assigned question`.trim(),
createdAt: admin.firestore.Timestamp.now()
}],
notifications: admin.firestore.Timestamp.now(),
productUpdates: admin.firestore.Timestamp.now(),
lastSent: admin.firestore.Timestamp.now(),})

});
} else {
db.doc(`notificationTrackers/${asker.email}`)
Expand All @@ -177,7 +178,7 @@
notifications: admin.firestore.Timestamp.now(),
productUpdates: admin.firestore.Timestamp.now(),
lastSent: admin.firestore.Timestamp.now(),})

});
}
})
Expand All @@ -201,31 +202,31 @@
const asker: FireUser = (await db.doc(`users/${topQuestion.askerId}`)
.get()).data() as FireUser;
sendSMS(asker, `Your question has reached the top of the \
${sessionName} queue. A TA will likely help you shortly.`);
${sessionName} queue. A TA will likely help you shortly.`);
db.doc(`notificationTrackers/${asker.email}`)
.update({
notificationList: admin.firestore.FieldValue.arrayUnion({
title: 'Your Question is Up!',
subtitle: `Your question has reached the top of the \
${sessionName} queue.`,
${sessionName} queue.`,
message: `Your question has reached the top of the \
${sessionName} queue. A TA will likely help you shortly.`,
${sessionName} queue. A TA will likely help you shortly.`,
createdAt: admin.firestore.Timestamp.now()
})
}).catch(() => {
db.doc(`notificationTrackers/${asker.email}`).create({id: asker.email,
notificationList: [{
title: 'Your Question is Up!',
subtitle: `Your question has reached the top of the \
${sessionName} queue.`,
${sessionName} queue.`,
message: `Your question has reached the top of the \
${sessionName} queue. A TA will likely help you shortly.`,
${sessionName} queue. A TA will likely help you shortly.`,
createdAt: admin.firestore.Timestamp.now()
}],
notifications: admin.firestore.Timestamp.now(),
productUpdates: admin.firestore.Timestamp.now(),
lastSent: admin.firestore.Timestamp.now(),})

});
db.doc(`questions/${afterQuestions[0].id}`).update({
wasNotified: true
Expand Down Expand Up @@ -266,7 +267,7 @@
notifications: admin.firestore.Timestamp.now(),
productUpdates: admin.firestore.Timestamp.now(),
lastSent: admin.firestore.Timestamp.now(),})

})
});

Expand All @@ -291,7 +292,7 @@
notifications: admin.firestore.Timestamp.now(),
productUpdates: admin.firestore.Timestamp.now(),
lastSent: admin.firestore.Timestamp.now(),})

})
});
return db.doc(`sessions/${sessionId}`).update({
Expand Down Expand Up @@ -323,8 +324,8 @@
// Derive changes in counts
const newStatus = newQuestion.status;
const prevStatus = prevQuestion.status;
const newNumbers = questionStatusNumbers.get(newStatus)!;

Check warning on line 327 in functions/src/index.ts

View workflow job for this annotation

GitHub Actions / build

Forbidden non-null assertion
const prevNumbers = questionStatusNumbers.get(prevStatus)!;

Check warning on line 328 in functions/src/index.ts

View workflow job for this annotation

GitHub Actions / build

Forbidden non-null assertion

// Grab number of changes
const numQuestionChange = newNumbers[0] - prevNumbers[0];
Expand All @@ -337,24 +338,24 @@
// Derive timing changes (changes from assigned to unassigned)
if (numAssignedChange === 1 && newQuestion.timeAssigned !== undefined) {
// Add new time addressed
waitTimeChange =
(newQuestion.timeAssigned.seconds - newQuestion.timeEntered.seconds)
/ (newQuestion.position || 1);
waitTimeChange =
(newQuestion.timeAssigned.seconds - newQuestion.timeEntered.seconds)
/ (newQuestion.position || 1);
}
else if (numAssignedChange === -1 && prevQuestion.timeAssigned !== undefined) {
// Subtract previous time addressed
waitTimeChange =
(prevQuestion.timeEntered.seconds - prevQuestion.timeAssigned.seconds)
/ (newQuestion.position || 1);
waitTimeChange =
(prevQuestion.timeEntered.seconds - prevQuestion.timeAssigned.seconds)
/ (newQuestion.position || 1);
}

// Derive timing changes (changes from assigned to resolved)
if (numResolvedChange === 1 && newQuestion.timeAssigned !== undefined) {
resolveTimeChange = newQuestion.timeAddressed!.seconds - newQuestion.timeAssigned.seconds;

Check warning on line 354 in functions/src/index.ts

View workflow job for this annotation

GitHub Actions / build

Forbidden non-null assertion
}
else if (numResolvedChange === -1
&& prevQuestion.timeAssigned !== undefined
&& prevQuestion.timeAddressed !== undefined
&& prevQuestion.timeAssigned !== undefined
&& prevQuestion.timeAddressed !== undefined
) {
resolveTimeChange = prevQuestion.timeAssigned.seconds - prevQuestion.timeAddressed.seconds;
}
Expand All @@ -364,7 +365,7 @@

if (
prevQuestion.answererId !== newQuestion.answererId &&
newQuestion.answererId !== ''
newQuestion.answererId !== ''
) {
db.doc(`notificationTrackers/${asker.email}`)
.update({
Expand All @@ -385,13 +386,13 @@
notifications: admin.firestore.Timestamp.now(),
productUpdates: admin.firestore.Timestamp.now(),
lastSent: admin.firestore.Timestamp.now(),})

});
}

if (
prevQuestion.answererId !== newQuestion.answererId &&
newQuestion.answererId === ''
newQuestion.answererId === ''
) {
const session: FireSession = (await db.doc(`sessions/${sessionId}`).get()).data() as FireSession;
db.doc(`notificationTrackers/${asker.email}`)
Expand All @@ -400,8 +401,8 @@
title: 'TA Unassigned',
subtitle: 'TA Unassigned',
message:
`A TA has been unassigned from your question and you have \
been readded to the top of the ${session.title} queue.`,
`A TA has been unassigned from your question and you have \
been readded to the top of the ${session.title} queue.`,
createdAt: admin.firestore.Timestamp.now()
})
}).catch(() => {
Expand All @@ -410,14 +411,14 @@
title: 'TA Unassigned',
subtitle: 'TA Unassigned',
message:
`A TA has been unassigned from your question and you have \
been readded to the top of the ${session.title} queue.`,
`A TA has been unassigned from your question and you have \
been readded to the top of the ${session.title} queue.`,
createdAt: admin.firestore.Timestamp.now()
}],
notifications: admin.firestore.Timestamp.now(),
productUpdates: admin.firestore.Timestamp.now(),
lastSent: admin.firestore.Timestamp.now(),})

});
}
else if (newQuestion.status === 'resolved') {
Expand All @@ -428,8 +429,8 @@
title: 'Question resolved',
subtitle: 'Question marked as resolved',
message:
`A TA has marked your question as resolved and you \
have been removed from the ${session.title} queue`,
`A TA has marked your question as resolved and you \
have been removed from the ${session.title} queue`,
createdAt: admin.firestore.Timestamp.now()
})
}).catch(() => {
Expand All @@ -438,14 +439,14 @@
title: 'Question marked no-show',
subtitle: 'Question marked as no-show',
message:
`A TA has marked your question as no-show and you \
have been removed from the ${session.title} queue`,
`A TA has marked your question as no-show and you \
have been removed from the ${session.title} queue`,
createdAt: admin.firestore.Timestamp.now()
}],
notifications: admin.firestore.Timestamp.now(),
productUpdates: admin.firestore.Timestamp.now(),
lastSent: admin.firestore.Timestamp.now(),})

});
} else if (newQuestion.status === "no-show") {
const session: FireSession = (await db.doc(`sessions/${sessionId}`).get()).data() as FireSession;
Expand All @@ -455,8 +456,8 @@
title: 'Question marked no-show',
subtitle: 'Question marked as no-show',
message:
`A TA has marked your question as no-show and you \
have been removed from the ${session.title} queue`,
`A TA has marked your question as no-show and you \
have been removed from the ${session.title} queue`,
createdAt: admin.firestore.Timestamp.now()
})
}).catch(() => {
Expand All @@ -465,14 +466,14 @@
title: 'Question marked no-show',
subtitle: 'Question marked as no-show',
message:
`A TA has marked your question as no-show and you \
have been removed from the ${session.title} queue`,
`A TA has marked your question as no-show and you \
have been removed from the ${session.title} queue`,
createdAt: admin.firestore.Timestamp.now()
}],
notifications: admin.firestore.Timestamp.now(),
productUpdates: admin.firestore.Timestamp.now(),
lastSent: admin.firestore.Timestamp.now(),})

});
}

Expand All @@ -484,4 +485,30 @@
totalWaitTime: admin.firestore.FieldValue.increment(waitTimeChange),
totalResolveTime: admin.firestore.FieldValue.increment(resolveTimeChange),
});
});
});

exports.onPendingUserCreate = functions.firestore
.document('pendingUsers/{userEmail}')
.onCreate(async (snap, context) => {
const userEmail = context.params.userEmail
const mailtrapClient = new MailtrapClient({ token: process.env.MAILTRAP_TOKEN as string });
const sender = {
email: "[email protected]",
name: "QueueMeIn Team"
}
const recipient = [{
email: userEmail
}]

try {
await mailtrapClient.send({
from: sender,
to: recipient,
subject: "You've been invited to QueueMeIn!",
text: "You've been invited to QueueMeIn! Click here to sign up: https://queueme.in",
category: "QMI Invite – Test",
});
} catch (error) {
// console.log("error sending mail: ", error)
}
});
Loading
Loading