Skip to content

Commit

Permalink
Merge pull request #496 from bettersg/feat/revamped-checker-ops
Browse files Browse the repository at this point in the history
Improved Checker Ops
  • Loading branch information
sarge1989 authored Nov 2, 2024
2 parents c228a08 + 7cbb617 commit 964a807
Show file tree
Hide file tree
Showing 27 changed files with 696 additions and 123 deletions.
8 changes: 3 additions & 5 deletions checkers-app/src/components/dashboard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -123,9 +123,7 @@ export default function Dashboard() {
name="Voting Accuracy"
img_src="/accuracy.png"
current={
programStats.accuracy === null
? 0
: programStats.accuracy * 100
programStats.accuracy === null ? 0 : programStats.accuracy * 100
}
target={programStats.accuracyTarget * 100}
isPercentageTarget={true}
Expand Down Expand Up @@ -158,8 +156,8 @@ export default function Dashboard() {
>
WhatsApp Bot
</a>
. You need to submit at least{" "}
{programStats.numReportTarget} messages.
. You need to submit at least {programStats.numReportTarget}{" "}
messages that are not eventually marked nvc-can't tell.
</>
}
/>
Expand Down
6 changes: 5 additions & 1 deletion functions/src/definitions/api/handlers/postChecker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const postCheckerHandler = async (req: Request, res: Response) => {
// Check request body
const {
name,
telegramUsername,
type,
isActive,
isOnboardingComplete,
Expand Down Expand Up @@ -56,6 +57,7 @@ const postCheckerHandler = async (req: Request, res: Response) => {

const newChecker: CheckerData = {
name,
telegramUsername,
type,
isActive: isActive || false,
isOnboardingComplete: isOnboardingComplete || false,
Expand All @@ -81,7 +83,8 @@ const postCheckerHandler = async (req: Request, res: Response) => {
preferredPlatform: preferredPlatform || (type === "ai" ? null : "telegram"),
lastVotedTimestamp: lastVotedTimestamp || null,
getNameMessageId: null,
certificateUrl: null, // Initialize certificateUrl as an empty string
hasCompletedProgram: false,
certificateUrl: null,
leaderboardStats: {
numVoted: 0,
numCorrectVotes: 0,
Expand All @@ -107,6 +110,7 @@ const postCheckerHandler = async (req: Request, res: Response) => {
numCorrectVotesAtProgramEnd: null,
numNonUnsureVotesAtProgramEnd: null,
},
offboardingTime: null,
}

logger.info("Creating new checker", newChecker)
Expand Down
2 changes: 2 additions & 0 deletions functions/src/definitions/api/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ interface updateVoteRequest {

interface createChecker {
name: string | null
telegramUsername: string | null
type: "human" | "ai"
isActive?: boolean
isOnboardingComplete?: boolean
Expand All @@ -47,6 +48,7 @@ interface createChecker {

interface updateChecker {
name?: string
telegramUsername?: string | null
type?: "human" | "ai"
isActive?: boolean
isOnboardingComplete?: boolean
Expand Down
72 changes: 68 additions & 4 deletions functions/src/definitions/batchJobs/batchJobs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { enqueueTask } from "../common/cloudTasks"

const runtimeEnvironment = defineString(AppEnv.ENVIRONMENT)
const CHECKERS_GROUP_LINK = String(process.env.CHECKERS_GROUP_LINK)
const CHECKERS_CHAT_ID = String(process.env.CHECKERS_CHAT_ID)

if (!admin.apps.length) {
admin.initializeApp()
Expand All @@ -45,8 +46,6 @@ async function handleInactiveCheckers() {
const promisesArr = activeCheckMatesSnap.docs.map(async (doc) => {
const telegramId = doc.get("telegramId")
const preferredPlatform = doc.get("preferredPlatform") ?? "whatsapp"

console.log(`deactiviateAfter: ${deactivateAfter}`)
const deactivationCheckResponse = await checkCheckerActivity(
doc,
deactivateAfter
Expand Down Expand Up @@ -216,6 +215,54 @@ async function interimPromptHandler() {
}
}

async function welcomeNewCheckers() {
// find checkers that onboarded since last week 12pm on Tuesday
try {
const lastWeek = new Date()
lastWeek.setDate(lastWeek.getDate() - 7)
lastWeek.setHours(12, 3, 0, 0)
const lastWeekTimestamp = Timestamp.fromDate(lastWeek)
const checkersQuerySnap = await db
.collection("checkers")
.where("onboardingTime", ">=", lastWeekTimestamp)
.get()
if (checkersQuerySnap.empty) {
return
}

// Create concatenated string of names
const names = checkersQuerySnap.docs
.map((doc) => {
const name = doc.get("name")
const telegramUsername = doc.get("telegramUsername")
const username = telegramUsername ? ` @${telegramUsername}` : ""
return `${name}${username}`
})
.join("\n")

// Get responses object
const responses = await getResponsesObj("factChecker")
const welcomeMessage = responses.WELCOME.replace("{{names}}", names)

// Send single welcome message to group chat
if (!CHECKERS_CHAT_ID) {
logger.error("Missing TELEGRAM_CHECKERS_GROUP_ID env var")
return
}

await sendTelegramTextMessage(
"admin",
CHECKERS_CHAT_ID,
welcomeMessage,
null,
"HTML",
null
)
} catch (error) {
logger.error("Error occured in welcomeNewCheckers:", error)
}
}

async function resetLeaderboardHandler() {
await saveLeaderboard()
try {
Expand Down Expand Up @@ -275,6 +322,16 @@ const scheduledDeactivation = onSchedule(
handleInactiveCheckers
)

const sendCheckersWelcomeMesssage = onSchedule(
{
schedule: "3 12 * * 2",
timeZone: "Asia/Singapore",
secrets: ["TELEGRAM_ADMIN_BOT_TOKEN"],
region: "asia-southeast1",
},
welcomeNewCheckers
)

const sendInterimPrompt = onSchedule(
{
schedule: "2,22,42 * * * *",
Expand All @@ -294,11 +351,18 @@ const resetLeaderboard = onSchedule(
resetLeaderboardHandler
)

export {
handleInactiveCheckers,
// Export scheduled cloud functions
export const batchJobs = {
checkSessionExpiring,
scheduledDeactivation,
sendCheckersWelcomeMesssage,
sendInterimPrompt,
resetLeaderboard,
}

// Export utility functions
export const utils = {
handleInactiveCheckers,
welcomeNewCheckers,
interimPromptHandler,
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@
"NOT_A_REPLY": "Sorry, did you forget to reply to a message? You need to swipe right on the message to reply to it.",
"OUTSTANDING_REMINDER": "You have *{{num_outstanding}} remaining messages* to assess. Would you like to be sent the next one in line?",
"NO_OUTSTANDING": "Great, you have no further messages to assess. Keep it up!💪",
"PROGRAM_COMPLETED": "Congratulations! You have completed our CheckMate volunteers program with the following stats:\n\nNo. of messages voted on: {{num_messages}}\nAccuracy: {{accuracy}}\nNo. of new users referred: {{num_referred}}\nNo. of non-trivial messages reported: {{num_reported}}\n\nand are now a certified CheckMate!! 🎉🥳",
"REMINDER": "<b>📍 We Miss You! 👀</b>\n\nHey {{name}}, it looks like you've been inactive for the past {{num_days}} days. Just a friendly reminder: if we don''t see any activity for 7 days, your fact-checking access will be temporarily deactivated.\n\nIf you need any help or have any questions, feel free to ask in the Q&A channel of our [Telegram group]({{checkers_group_link}}) - we're here to help! 😊",
"DEACTIVATION": "<b>📍 Temporary Deactivation Notice</b>\n\nHi {{name}},\n\nYour fact-checking access has been temporarily deactivated due to inactivity. No worries though - simply press the button below to get back to checking! 🚀\n\nWe're excited to see you back in action soon 😊",
"REACTIVATION": "<b>📍 We Miss You at CheckMate! 🥺</b>\n\nHey {{name}},\n\nIt's been {{num_days}} days since we last saw you on the CheckMate bot, and we miss having you around! We'd love to have you back, so why not pop in when you have a moment? 😄\n\nSimply press the button below to resume your fact-checking journey. If you need any help, don't hesitate to reach out!\n\nLooking forward to seeing you again 🤗"
"REMINDER": "<b>📍 We Miss You! 👀</b>\n\nHey {{name}}, it looks like you've been inactive for the past {{num_days}} days. Just a friendly reminder: if we don't see any activity for 7 days, your access as a checker will be temporarily deactivated.\n\nIf you need any help or have any questions, feel free to ask in the Q&A channel of our <a href='{{checkers_group_link}}'>Checker's Telegram group</a> - we're here to help! 😊",
"DEACTIVATION": "<b>📍 Temporary Deactivation Notice</b>\n\nHi {{name}},\n\nYour access as a checker has been temporarily deactivated due to inactivity. No worries though - simply press the button below to get back to checking! 🚀\n\nWe're excited to see you back in action soon 😊",
"MANAGE_OUT": "<b>📍 CheckMate Fact-checker Programme Status</b>\n\nHi {{name}},\n\nThank you so much for your time and dedication during the CheckMate programme. While you've given it your best, it seems we haven't quite met the criteria needed to continue as a volunteer in our checking crew.\n\nWe'd love to hear about your experience - your feedback will help us improve! Please take a moment to fill out this <a href='{{survey_link}}'>quick survey</a>: \n\nAs part of this process, you'll also be removed from the Checkers' Crew Telegram chat. We appreciate your understanding and wish you all the best moving forward 💛\n\nThank you for your contribution,\n\nThe CheckMate Team",
"GRADUATION": "<b>📍 Congratulations, You've Graduated! 🎉</b>\n\nHi {{name}},\n\nHuge congratulations on completing the CheckMate checkers' programme! You've worked hard, and completed the following:\n\nNo. of messages voted on: {{num_messages}}\nAccuracy: {{accuracy}}\nNo. of new users referred: {{num_referred}}\nNo. of messages reported: {{num_reported}}\n\n. Your dedication has clearly paid off! 🎓\n\nWe'd love to hear about your experience - please take a moment to fill out <a href='{{survey_link}}'>our survey</a>\n\nTo generate your official completion certificate, just press the button below.\n\nThank you for being part of the Checkers' Crew. We're so proud of you! 💪",
"WELCOME": "<b>📍 Welcome to the Checkers' Crew! 👋</b>\n\nLet's welcome our new checkers!🎉\n\n{{names}}\n\nWe're so excited to have you join our fact-checking family. This group is your hub for connecting with fellow Checkers, sharing tips, and staying up to date with important updates.\n\nIf you ever feel unsure about anything, don't hesitate to reach out or ask in our Q&A channel. We're all here to support each other and improve our skills together 🤗\n\nLet's get started!"
}
11 changes: 11 additions & 0 deletions functions/src/definitions/common/parameters/nudges.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"REACTIVATION": {
"1": "<b>📍 We Miss You at CheckMate! 🥺</b>\n\nHey {{name}},\n\nIt's been {{num_days}} days since we last saw you on the CheckMate bot, and we miss having you around! We'd love to have you back, so why not pop in when you have a moment? 😄\n\nSimply press the button below to resume your checking journey. If you need any help, don't hesitate to reach out!\n\nLooking forward to seeing you again 🤗"
},
"ACCURACY": {
"1": "<b>📍 Let's Review Together!</b>\n\nHi {{name}},\n\nIt looks like you've had some challenges with your first {{num_messages}} messages, with over {{accuracy_threshold}} of them marked incorrect. Don't worry! This is all part of the learning process. 😊\n\nWe recommend revisiting the resources which you can assess by pressing the button below. If you're still unsure about categorization, feel free to ask any questions in the <a href='{{checkers_group_link}}'>Checker's Telegram group</a>. We're here to help you improve and succeed!\n\nKeep going, you've got this 💪"
},
"EXTENSION": {
"1": "<b>📍 You've Got Another Chance! 🎓</b>\n\nHi {{name}},\n\nAlthough you haven't met the graduation criteria just yet, we've extended your time in the programme by another month! You now have extra time to give it another go. 😊\n\nTo help you prepare, we've put together a <a href='{{revision_quiz_link}}'>revision quiz</a> for you to complete. Don't hesitate to ask questions in the group chat if you need any guidance.\n\nYou're almost there, let's finish strong! 💪"
}
}
6 changes: 5 additions & 1 deletion functions/src/definitions/common/parameters/thresholds.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,9 @@
"volunteerProgramVotesRequirement": 50,
"volunteerProgramReferralRequirement": 0,
"volunteerProgramReportRequirement": 3,
"volunteerProgramAccuracyRequirement": 0.6
"volunteerProgramAccuracyRequirement": 0.6,
"accuracyNudgeThreshold": 0.5,
"numberBeforeAccuracyNudge": 20,
"daysBeforeFirstCompletionCheck": 60,
"daysBeforeSecondCompletionCheck": 90
}
2 changes: 2 additions & 0 deletions functions/src/definitions/common/sendTelegramMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ const sendTelegramTextMessage = async function (
token = process.env.TELEGRAM_CHECKER_BOT_TOKEN
} else if (bot === "report") {
token = process.env.TELEGRAM_REPORT_BOT_TOKEN
} else if (bot === "admin") {
token = process.env.TELEGRAM_ADMIN_BOT_TOKEN
} else {
token = process.env.TELEGRAM_USER_BOT_TOKEN
}
Expand Down
7 changes: 4 additions & 3 deletions functions/src/definitions/common/statistics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { logger } from "firebase-functions/v2"
import { LeaderboardEntry } from "../../types"
import { Timestamp } from "firebase-admin/firestore"
import { TIME } from "../../utils/time"
import { CheckerData } from "../../types"
import { CheckerData, CheckerProgramStats } from "../../types"

function checkAccuracy(
parentMessageSnap: admin.firestore.DocumentSnapshot<admin.firestore.DocumentData>,
Expand Down Expand Up @@ -133,7 +133,7 @@ function tabulateVoteStats(
async function computeProgramStats(
checkerSnap: admin.firestore.DocumentSnapshot<admin.firestore.DocumentData>,
updateCompletion: boolean = false
) {
): Promise<CheckerProgramStats> {
try {
const checkerData = checkerSnap.data() as CheckerData
if (!checkerData.programData?.isOnProgram) {
Expand Down Expand Up @@ -177,6 +177,7 @@ async function computeProgramStats(
) {
timestamp = Timestamp.fromDate(new Date())
await checkerSnap.ref.update({
hasCompletedProgram: true,
"programData.programEnd": timestamp,
"programData.numVotesAtProgramEnd": checkerData.numVoted,
"programData.numReferralsAtProgramEnd": checkerData.numReferred,
Expand All @@ -195,7 +196,7 @@ async function computeProgramStats(
isProgramCompleted,
isNewlyCompleted,
completionTimestamp: timestamp,
}
} as CheckerProgramStats
} catch (error) {
logger.error(
`Error computing program stats for checker ${checkerSnap.ref.id}: ${error}`
Expand Down
Loading

0 comments on commit 964a807

Please sign in to comment.