From 3ffc54fe37684330e80e65de318c3c3e4d1a4277 Mon Sep 17 00:00:00 2001 From: Bing Wen Tan Date: Sun, 7 Apr 2024 09:04:05 +0800 Subject: [PATCH 1/5] fixed response utils ignoring error response --- functions/src/definitions/common/counters.ts | 56 +++++++++++- .../src/definitions/common/responseUtils.ts | 87 +++++++------------ .../eventHandlers/onVoteRequestUpdate.ts | 78 +++++------------ 3 files changed, 104 insertions(+), 117 deletions(-) diff --git a/functions/src/definitions/common/counters.ts b/functions/src/definitions/common/counters.ts index c10a8340..820b1a9b 100644 --- a/functions/src/definitions/common/counters.ts +++ b/functions/src/definitions/common/counters.ts @@ -28,4 +28,58 @@ const getCount = async function (docRef: DocumentReference, type: string) { return count } -export { getCount, incrementCounter } +const getVoteCounts = async function (messageRef: DocumentReference) { + const totalVoteRequestQuery = messageRef + .collection("voteRequests") + .count() + .get() + const [ + responsesCount, + errorCount, + irrelevantCount, + scamCount, + illicitCount, + infoCount, + spamCount, + legitimateCount, + unsureCount, + satireCount, + voteTotal, + voteRequestCountSnapshot, + ] = await Promise.all([ + getCount(messageRef, "responses"), + getCount(messageRef, "error"), + getCount(messageRef, "irrelevant"), + getCount(messageRef, "scam"), + getCount(messageRef, "illicit"), + getCount(messageRef, "info"), + getCount(messageRef, "spam"), + getCount(messageRef, "legitimate"), + getCount(messageRef, "unsure"), + getCount(messageRef, "satire"), + getCount(messageRef, "totalVoteScore"), + totalVoteRequestQuery, + ]) + const totalVoteRequestsCount = voteRequestCountSnapshot.data().count ?? 0 + const factCheckerCount = totalVoteRequestsCount - errorCount //don't count "error" votes in number of fact checkers, as this will slow the replies unnecessarily. + const validResponsesCount = responsesCount - errorCount //can remove in future and replace with nonErrorCount + const susCount = scamCount + illicitCount + return { + responsesCount, + errorCount, + irrelevantCount, + scamCount, + illicitCount, + infoCount, + spamCount, + legitimateCount, + unsureCount, + satireCount, + voteTotal, + validResponsesCount, + susCount, + factCheckerCount, + } +} + +export { getCount, incrementCounter, getVoteCounts } diff --git a/functions/src/definitions/common/responseUtils.ts b/functions/src/definitions/common/responseUtils.ts index 4218afa4..381d42a8 100644 --- a/functions/src/definitions/common/responseUtils.ts +++ b/functions/src/definitions/common/responseUtils.ts @@ -12,7 +12,7 @@ import { DocumentSnapshot, Timestamp } from "firebase-admin/firestore" import { getThresholds, sleep } from "./utils" import { getSignedUrl } from "./mediaUtils" import { sendTextMessage } from "./sendMessage" -import { getCount } from "./counters" +import { getVoteCounts } from "./counters" const db = admin.firestore() @@ -74,7 +74,7 @@ async function getResponsesObj( function getInfoLiner(truthScore: null | number, infoPlaceholder: string) { return infoPlaceholder.replace( "{{score}}", - typeof truthScore === "number" ? truthScore.toFixed(2) : "NA" + typeof truthScore === "number" ? truthScore.toFixed(1) : "NA" ) } @@ -299,23 +299,26 @@ async function sendVotingStats(instancePath: string) { } const messageSnap = await messageRef.get() const instanceSnap = await db.doc(instancePath).get() - const responseCount = await getCount(messageRef, "responses") - const irrelevantCount = await getCount(messageRef, "irrelevant") - const scamCount = await getCount(messageRef, "scam") - const illicitCount = await getCount(messageRef, "illicit") - const infoCount = await getCount(messageRef, "info") - const satireCount = await getCount(messageRef, "satire") - const spamCount = await getCount(messageRef, "spam") - const legitimateCount = await getCount(messageRef, "legitimate") - const unsureCount = await getCount(messageRef, "unsure") - const susCount = scamCount + illicitCount + const { + irrelevantCount, + scamCount, + illicitCount, + infoCount, + spamCount, + legitimateCount, + unsureCount, + satireCount, + validResponsesCount, + susCount, + factCheckerCount, + } = await getVoteCounts(messageRef) const truthScore = messageSnap.get("truthScore") const thresholds = await getThresholds() const from = instanceSnap.get("from") const responses = await getResponsesObj("user", from) let truthCategory - if (responseCount <= 0) { + if (validResponsesCount <= 0) { functions.logger.error( `Stats requested for instance ${instancePath} with 0 votes` ) @@ -366,61 +369,28 @@ async function sendVotingStats(instancePath: string) { categories.sort((a, b) => b.count - a.count) // sort in descending order const highestCategory = categories[0].name const secondCategory = categories[1].name - const highestPercentage = (categories[0].count / responseCount) * 100 - const secondPercentage = (categories[1].count / responseCount) * 100 + const highestPercentage = (categories[0].count / validResponsesCount) * 100 + const secondPercentage = (categories[1].count / validResponsesCount) * 100 const isHighestInfo = categories[0].isInfo const isSecondInfo = categories[1].isInfo const infoLiner = getInfoLiner(truthScore, responses.INFO_PLACEHOLDER) let response = responses.STATS_TEMPLATE_1.replace( "{{top}}", - `${highestPercentage.toFixed(2)}` + `${highestPercentage.toFixed(1)}` ) .replace("{{category}}", highestCategory) .replace("{{info_placeholder}}", isHighestInfo ? infoLiner : "") - // let response = `${highestPercentage.toFixed(2)}% of our CheckMates ${ - // isHighestInfo ? "collectively " : "" - // }thought this was *${highestCategory}*${isHighestInfo ? infoLiner : ""}.` if (secondPercentage > 0) { response += responses.STATS_TEMPLATE_2.replace( "{{second}}", - `${secondPercentage.toFixed(2)}` + `${secondPercentage.toFixed(1)}` ) .replace("{{category}}", secondCategory) .replace("{{info_placeholder}}", isSecondInfo ? infoLiner : "") - // response += ` ${secondPercentage.toFixed(2)}% ${ - // isSecondInfo ? "collectively " : "" - // } thought this was *${secondCategory}*${isSecondInfo ? infoLiner : ""}.` } await sendTextMessage("user", from, response, instanceSnap.get("id")) - - // if (triggerScamShieldConsent) { - // await sleep(2000) - // const buttons = [ - // { - // type: "reply", - // reply: { - // id: `scamshieldConsent_${instancePath}_consent`, - // title: "Yes", - // }, - // }, - // { - // type: "reply", - // reply: { - // id: `scamshieldConsent_${instancePath}_decline`, - // title: "No", - // }, - // }, - // ] - // await sendWhatsappButtonMessage( - // "user", - // from, - // responses.SCAMSHIELD_SEEK_CONSENT, - // buttons, - // instanceSnap.get("id") - // ) - // } } async function sendRationalisation(instancePath: string) { @@ -515,12 +485,13 @@ async function sendInterimUpdate(instancePath: string) { const parentMessageSnap = await parentMessageRef.get() const primaryCategory = parentMessageSnap.get("primaryCategory") const truthScore = parentMessageSnap.get("truthScore") - const voteRequestQuerySnapshot = await parentMessageRef - .collection("voteRequests") - .get() - const numFactCheckers = voteRequestQuerySnapshot.size - const voteCount = await getCount(parentMessageRef, "responses") - const percentageVoted = ((voteCount / numFactCheckers) * 100).toFixed(2) + const { validResponsesCount, factCheckerCount } = await getVoteCounts( + parentMessageRef + ) + const percentageVoted = ( + (validResponsesCount / factCheckerCount) * + 100 + ).toFixed(1) let prelimAssessment let infoPlaceholder = "" const infoLiner = getInfoLiner(truthScore, responses.INFO_PLACEHOLDER) @@ -651,7 +622,7 @@ async function respondToInstance( const isAssessed = parentMessageSnap.get("isAssessed") const isMachineCategorised = parentMessageSnap.get("isMachineCategorised") const instanceCount = parentMessageSnap.get("instanceCount") - const responseCount = await getCount(parentMessageRef, "responses") + const { validResponsesCount } = await getVoteCounts(parentMessageRef) const isImage = data?.type === "image" const isMatched = data?.isMatched ?? false const primaryCategory = parentMessageSnap.get("primaryCategory") @@ -774,7 +745,7 @@ async function respondToInstance( responses[category.toUpperCase() as keyof typeof responses] ) - if (!(isMachineCategorised || responseCount <= 0)) { + if (!(isMachineCategorised || validResponsesCount <= 0)) { buttons.push(votingResultsButton) } diff --git a/functions/src/definitions/eventHandlers/onVoteRequestUpdate.ts b/functions/src/definitions/eventHandlers/onVoteRequestUpdate.ts index 6a6107a0..d9e1e84c 100644 --- a/functions/src/definitions/eventHandlers/onVoteRequestUpdate.ts +++ b/functions/src/definitions/eventHandlers/onVoteRequestUpdate.ts @@ -6,7 +6,7 @@ import { sendL2OthersCategorisationMessage, sendRemainingReminder, } from "../common/sendFactCheckerMessages" -import { incrementCounter, getCount } from "../common/counters" +import { incrementCounter, getVoteCounts } from "../common/counters" import { FieldValue } from "@google-cloud/firestore" import { defineInt } from "firebase-functions/params" import { onDocumentUpdated } from "firebase-functions/v2/firestore" @@ -56,35 +56,14 @@ const onVoteRequestUpdateV2 = onDocumentUpdated( const isLegacy = postChangeData.truthScore === undefined && postChangeData.vote !== undefined - const voteRequestNotErrorCountQuery = messageRef //does not match category == null as per https://firebase.google.com/docs/firestore/query-data/queries#not_equal - .collection("voteRequests") - .where("category", "!=", "error") - .count() - .get() - const voteRequestNullCountQuery = messageRef - .collection("voteRequests") - .where("category", "==", null) - .count() - .get() - const [ - voteRequestNotErrorCountSnapshot, - voteRequestNullCountSnapshot, - , - , - ] = await Promise.all([ - voteRequestNotErrorCountQuery, - voteRequestNullCountQuery, + await Promise.all([ updateCounts(messageRef, preChangeData, postChangeData), updateCheckerVoteCount(preChangeData, postChangeData), ]) - const nonErrorCount = voteRequestNotErrorCountSnapshot.data().count ?? 0 - const nullCount = voteRequestNullCountSnapshot.data().count ?? 0 - const numFactCheckers = nonErrorCount + nullCount + const thresholds = await getThresholds() - const [ - responsesCount, - errorCount, + const { irrelevantCount, scamCount, illicitCount, @@ -94,41 +73,22 @@ const onVoteRequestUpdateV2 = onDocumentUpdated( unsureCount, satireCount, voteTotal, - thresholds, - ] = await Promise.all([ - getCount(messageRef, "responses"), - getCount(messageRef, "error"), - getCount(messageRef, "irrelevant"), - getCount(messageRef, "scam"), - getCount(messageRef, "illicit"), - getCount(messageRef, "info"), - getCount(messageRef, "spam"), - getCount(messageRef, "legitimate"), - getCount(messageRef, "unsure"), - getCount(messageRef, "satire"), - getCount(messageRef, "totalVoteScore"), - getThresholds(), - ]) - - const responseCount = responsesCount - errorCount //can remove in future and replace with nonErrorCount - const susCount = scamCount + illicitCount + validResponsesCount, + susCount, + factCheckerCount, + } = await getVoteCounts(messageRef) - if (responseCount != nonErrorCount) { - functions.logger.error( - `Response count ${responseCount} does not match nonErrorCount ${nonErrorCount}, using response count` - ) - } const truthScore = computeTruthScore(infoCount, voteTotal, isLegacy) - const isSus = susCount > thresholds.isSus * responseCount + const isSus = susCount > thresholds.isSus * validResponsesCount const isScam = isSus && scamCount >= illicitCount const isIllicit = isSus && !isScam - const isInfo = infoCount > thresholds.isInfo * responseCount - const isSatire = satireCount > thresholds.isSatire * responseCount - const isSpam = spamCount > thresholds.isSpam * responseCount + const isInfo = infoCount > thresholds.isInfo * validResponsesCount + const isSatire = satireCount > thresholds.isSatire * validResponsesCount + const isSpam = spamCount > thresholds.isSpam * validResponsesCount const isLegitimate = - legitimateCount > thresholds.isLegitimate * responseCount + legitimateCount > thresholds.isLegitimate * validResponsesCount const isIrrelevant = - irrelevantCount > thresholds.isIrrelevant * responseCount + irrelevantCount > thresholds.isIrrelevant * validResponsesCount const isUnsure = (!isSus && !isInfo && @@ -136,12 +96,14 @@ const onVoteRequestUpdateV2 = onDocumentUpdated( !isLegitimate && !isIrrelevant && !isSatire) || - unsureCount > thresholds.isUnsure * responseCount + unsureCount > thresholds.isUnsure * validResponsesCount const isAssessed = (isUnsure && - responseCount > thresholds.endVoteUnsure * numFactCheckers) || - (!isUnsure && responseCount > thresholds.endVote * numFactCheckers) || - (isSus && responseCount > thresholds.endVoteSus * numFactCheckers) + validResponsesCount > thresholds.endVoteUnsure * factCheckerCount) || + (!isUnsure && + validResponsesCount > thresholds.endVote * factCheckerCount) || + (isSus && + validResponsesCount > thresholds.endVoteSus * factCheckerCount) //set primaryCategory let primaryCategory From 84ac09e3fed38e1fa7e6da0d4e14d34479fc6440 Mon Sep 17 00:00:00 2001 From: Bing Wen Tan Date: Sun, 7 Apr 2024 09:14:12 +0800 Subject: [PATCH 2/5] remove final cat unsure from acc calculations --- checkers-app/src/components/dashboard/index.tsx | 8 +++++--- functions/src/definitions/api/handlers/getChecker.ts | 8 +++++--- functions/src/definitions/api/interfaces.ts | 2 +- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/checkers-app/src/components/dashboard/index.tsx b/checkers-app/src/components/dashboard/index.tsx index 340178a3..e0da2457 100644 --- a/checkers-app/src/components/dashboard/index.tsx +++ b/checkers-app/src/components/dashboard/index.tsx @@ -12,7 +12,7 @@ export default function Dashboard() { const [isLoading, setIsLoading] = useState(false); const { checkerId, pendingCount, setPendingCount } = useUser(); const [totalVotes, setTotalVotes] = useState(0); - const [accuracyRate, setAccuracyRate] = useState(0); + const [accuracyRate, setAccuracyRate] = useState(0); const [avgResponseTime, setAvgResponseTime] = useState(0); const [peopleHelped, setPeopleHelped] = useState(0); @@ -61,12 +61,14 @@ export default function Dashboard() { { const accurateCount = results.filter( (d) => d !== null && d.isAccurate ).length - const totalAssessedCount = results.filter( - (d) => d !== null && d.isAssessed + const totalAssessedAndNonUnsureCount = results.filter( + (d) => d !== null && d.isAssessed && d.isAccurate !== null ).length const totalCount = results.filter((d) => d !== null).length //calculate people helped @@ -126,7 +126,9 @@ const getCheckerHandler = async (req: Request, res: Response) => { const averageResponseTime = totalResponseTime / (totalCount || 1) const accuracyRate = - totalAssessedCount === 0 ? 1 : accurateCount / totalAssessedCount + totalAssessedAndNonUnsureCount === 0 + ? null + : accurateCount / totalAssessedAndNonUnsureCount const returnData: Checker = { name: checkerData.name, type: checkerData.type, diff --git a/functions/src/definitions/api/interfaces.ts b/functions/src/definitions/api/interfaces.ts index 39deaa71..5f3a2cf7 100644 --- a/functions/src/definitions/api/interfaces.ts +++ b/functions/src/definitions/api/interfaces.ts @@ -86,7 +86,7 @@ interface Vote { interface last30DaysStats { totalVoted: number - accuracyRate: number + accuracyRate: number | null averageResponseTime: number peopleHelped: number } From 745c73df44327ccb4b7948fbeddbb720f7146d63 Mon Sep 17 00:00:00 2001 From: Bing Wen Tan Date: Sun, 7 Apr 2024 09:33:50 +0800 Subject: [PATCH 3/5] show different icon if msg gets assessed as unsure --- .../src/components/myvotes/MessageCard.tsx | 81 +++++++++++-------- .../api/handlers/getCheckerVotes.ts | 6 +- functions/src/definitions/api/interfaces.ts | 1 + 3 files changed, 51 insertions(+), 37 deletions(-) diff --git a/checkers-app/src/components/myvotes/MessageCard.tsx b/checkers-app/src/components/myvotes/MessageCard.tsx index c4e7a43b..67be1bbb 100644 --- a/checkers-app/src/components/myvotes/MessageCard.tsx +++ b/checkers-app/src/components/myvotes/MessageCard.tsx @@ -1,6 +1,6 @@ import { VoteSummary } from "../../types"; import { useNavigate } from "react-router-dom"; -import { PencilIcon } from "@heroicons/react/20/solid"; +import { PencilIcon, QuestionMarkCircleIcon } from "@heroicons/react/20/solid"; import "./MessageCard.css"; interface MessageCardProps { @@ -21,23 +21,6 @@ const colours: ColourMap = { WAITING: "waiting-color", }; -// function getCategory(msg: Message): string { -// if (msg.voteRequests.category == null) { -// return "PENDING"; -// } -// else if (!msg.isAssessed && msg.voteRequests.category != null) { -// return "WAITING"; -// } -// else { -// if (msg.isMatch) { -// return "CORRECT" -// } -// else { -// return "INCORRECT" -// } -// } -// } - function dateToDateString(date: Date | null): string { // Parse the ISO string into a Date object if (date === null) { @@ -84,6 +67,7 @@ export default function MessageCard(props: MessageCardProps) { //caption, needsReview, isAssessed, + isUnsure, firestorePath, } = props.voteSummary; const status = props.status; @@ -98,6 +82,49 @@ export default function MessageCard(props: MessageCardProps) { const textStyle = "font-normal"; //add bold in future + function renderStatusDot() { + // Checking if the status is 'voted' + if (status === "voted") { + if (isAssessed) { + if (isUnsure) { + // Is unsure + return ( +
+ +
+ ); + } else { + if (needsReview) { + return ( +
+
+
+ ); + } else { + return ( +
+
+
+ ); + } + } + } else { + // Not assessed + return ( +
+ +
+ ); + } + } + // If none of the conditions match, return null + return null; + } + return (
{/* Coloured dot if needs review*/} - {isAssessed && needsReview && status === "voted" && ( -
-
-
- )} - - {isAssessed && !needsReview && status === "voted" && ( -
-
-
- )} - - {!isAssessed && status === "voted" && ( -
- -
- )} + {renderStatusDot()} {/* Message content */}
diff --git a/functions/src/definitions/api/handlers/getCheckerVotes.ts b/functions/src/definitions/api/handlers/getCheckerVotes.ts index f8b60d7b..29eb1ab3 100644 --- a/functions/src/definitions/api/handlers/getCheckerVotes.ts +++ b/functions/src/definitions/api/handlers/getCheckerVotes.ts @@ -97,8 +97,9 @@ const getCheckerVotesHandler = async (req: Request, res: Response) => { const caption = latestInstanceSnap.get("caption") ?? null const isAssessed = parentMessageSnap.get("isAssessed") ?? false const firestorePath = doc.ref.path - const needsReview = - isAssessed && checkAccuracy(parentMessageSnap, doc) === false + const isCorrect = checkAccuracy(parentMessageSnap, doc) + const needsReview = isAssessed && isCorrect === false + const isUnsure = isCorrect === null //either unsure or some other type of error const returnObject: VoteSummary = { category, @@ -110,6 +111,7 @@ const getCheckerVotesHandler = async (req: Request, res: Response) => { caption, needsReview, isAssessed, + isUnsure, firestorePath, } return returnObject diff --git a/functions/src/definitions/api/interfaces.ts b/functions/src/definitions/api/interfaces.ts index 5f3a2cf7..3fcb0c55 100644 --- a/functions/src/definitions/api/interfaces.ts +++ b/functions/src/definitions/api/interfaces.ts @@ -64,6 +64,7 @@ interface VoteSummary { caption: string | null //only for type image needsReview: boolean //if the vote differs from the majority isAssessed: boolean //if the message is assessed + isUnsure: boolean //if the final assessed category ended as unsure firestorePath: string } From ae521cdf54bda0500a7654b7789b4a9c36edf787 Mon Sep 17 00:00:00 2001 From: Bing Wen Tan Date: Sun, 7 Apr 2024 10:21:23 +0800 Subject: [PATCH 4/5] updated tests for new float rounding --- .../checkmate.postman_collection.json | 81 ++++++++++++------- 1 file changed, 51 insertions(+), 30 deletions(-) diff --git a/integration-tests/checkmate.postman_collection.json b/integration-tests/checkmate.postman_collection.json index 484d551d..fb06c506 100644 --- a/integration-tests/checkmate.postman_collection.json +++ b/integration-tests/checkmate.postman_collection.json @@ -3009,7 +3009,7 @@ " const BUTTON_ANOTHER_UPDATE = pm.variables.get(\"__CONSTANTS__.USER_BOT_RESPONSES.BUTTON_ANOTHER_UPDATE.en\");\r", " const wamid = pm.variables.get(\"whatsapp_id_11\")\r", " const instanceId = pm.variables.get(\"spamInstanceId\")\r", - " const interimResponse = INTERIM_TEMPLATE.replace(\"{{%voted}}\", \"33.33\").replace(\"{{prelim_assessment}}\",\"spam🚧\").replace(\"{{info_placeholder}}\",\"\")\r", + " const interimResponse = INTERIM_TEMPLATE.replace(\"{{%voted}}\", \"33.3\").replace(\"{{prelim_assessment}}\",\"spam🚧\").replace(\"{{info_placeholder}}\",\"\")\r", " const expected = {\r", " \"hostname\": \"resultserver\",\r", " \"path\": \"/v15.0/WHATSAPP_TEST_USER_BOT_PHONE_NUMBER_ID/messages\",\r", @@ -3046,7 +3046,8 @@ " pm.expect(jsonData).to.eql(expected);\r", "});" ], - "type": "text/javascript" + "type": "text/javascript", + "packages": {} } }, { @@ -3055,7 +3056,8 @@ "exec": [ "setTimeout(() => {}, 3000);" ], - "type": "text/javascript" + "type": "text/javascript", + "packages": {} } } ], @@ -3154,7 +3156,7 @@ " const BUTTON_ANOTHER_UPDATE = pm.variables.get(\"__CONSTANTS__.USER_BOT_RESPONSES.BUTTON_ANOTHER_UPDATE.en\")\r", " const instanceId = pm.variables.get(\"spamInstanceId\")\r", " const wamid = pm.variables.get(\"whatsapp_id_11\")\r", - " const interimResponse = INTERIM_TEMPLATE.replace(\"{{%voted}}\", \"33.33\").replace(\"{{prelim_assessment}}\",\"spam🚧\").replace(\"{{info_placeholder}}\",\"\").replace(\"{{get_feedback}}\",\"\")\r", + " const interimResponse = INTERIM_TEMPLATE.replace(\"{{%voted}}\", \"33.3\").replace(\"{{prelim_assessment}}\",\"spam🚧\").replace(\"{{info_placeholder}}\",\"\").replace(\"{{get_feedback}}\",\"\")\r", " const expected = {\r", " \"hostname\": \"resultserver\",\r", " \"path\": \"/v15.0/WHATSAPP_TEST_USER_BOT_PHONE_NUMBER_ID/messages\",\r", @@ -3191,7 +3193,8 @@ " pm.expect(jsonData).to.eql(expected);\r", "});" ], - "type": "text/javascript" + "type": "text/javascript", + "packages": {} } }, { @@ -3200,7 +3203,8 @@ "exec": [ "setTimeout(() => {}, 3000);" ], - "type": "text/javascript" + "type": "text/javascript", + "packages": {} } } ], @@ -3863,7 +3867,7 @@ " const INTERIM_TEMPLATE_UNSURE = pm.variables.get(\"__CONSTANTS__.USER_BOT_RESPONSES.INTERIM_TEMPLATE_UNSURE.en\");\r", " const BUTTON_ANOTHER_UPDATE = pm.variables.get(\"__CONSTANTS__.USER_BOT_RESPONSES.BUTTON_ANOTHER_UPDATE.en\")\r", " const instanceId = pm.variables.get(\"spamInstanceId\")\r", - " const interimResponse = INTERIM_TEMPLATE_UNSURE.replace(\"{{%voted}}\", \"66.67\")\r", + " const interimResponse = INTERIM_TEMPLATE_UNSURE.replace(\"{{%voted}}\", \"66.7\")\r", " const wamid = pm.variables.get(\"whatsapp_id_11\")\r", " const expected = {\r", " \"hostname\": \"resultserver\",\r", @@ -3900,7 +3904,8 @@ " pm.expect(jsonData).to.eql(expected);\r", "});" ], - "type": "text/javascript" + "type": "text/javascript", + "packages": {} } }, { @@ -3909,7 +3914,8 @@ "exec": [ "setTimeout(() => {}, 3000);" ], - "type": "text/javascript" + "type": "text/javascript", + "packages": {} } } ], @@ -5083,7 +5089,7 @@ " \"path\": \"/v15.0/WHATSAPP_TEST_USER_BOT_PHONE_NUMBER_ID/messages\",\r", " \"body\": {\r", " \"text\": {\r", - " \"body\": \"66.67% of our CheckMates felt this was *spam🚧*. 33.33% felt this was *legitimate✅*.\",\r", + " \"body\": \"66.7% of our CheckMates felt this was *spam🚧*. 33.3% felt this was *legitimate✅*.\",\r", " \"preview_url\": false\r", " },\r", " \"to\": USER_1_NUMBER,\r", @@ -5098,7 +5104,8 @@ " pm.expect(jsonData).to.eql(expected);\r", "});" ], - "type": "text/javascript" + "type": "text/javascript", + "packages": {} } }, { @@ -5107,7 +5114,8 @@ "exec": [ "setTimeout(() => {}, 3000);" ], - "type": "text/javascript" + "type": "text/javascript", + "packages": {} } } ], @@ -5762,6 +5770,7 @@ " },\r", " \"method\": \"POST\"\r", " }\r", + " console.log(JSON.stringify(expected, null, 2))\r", " var jsonData = pm.response.json();\r", " pm.expect(jsonData).to.eql(expected);\r", "});" @@ -6225,7 +6234,7 @@ " const BUTTON_ANOTHER_UPDATE = pm.variables.get(\"__CONSTANTS__.USER_BOT_RESPONSES.BUTTON_ANOTHER_UPDATE.en\")\r", " const instanceId = pm.variables.get(\"infoInstanceId\")\r", " const wamid = pm.variables.get(\"whatsapp_id_19\")\r", - " const interimResponse = INTERIM_TEMPLATE.replace(\"{{%voted}}\", \"33.33\").replace(\"{{prelim_assessment}}\",\"untrue❌\").replace(\"{{info_placeholder}}\",INFO_PLACEHOLDER.replace(\"{{score}}\",\"1.00\"))\r", + " const interimResponse = INTERIM_TEMPLATE.replace(\"{{%voted}}\", \"33.3\").replace(\"{{prelim_assessment}}\",\"untrue❌\").replace(\"{{info_placeholder}}\",INFO_PLACEHOLDER.replace(\"{{score}}\",\"1.0\"))\r", " const expected = {\r", " \"hostname\": \"resultserver\",\r", " \"path\": \"/v15.0/WHATSAPP_TEST_USER_BOT_PHONE_NUMBER_ID/messages\",\r", @@ -6264,7 +6273,8 @@ " pm.expect(jsonData).to.eql(expected);\r", "});" ], - "type": "text/javascript" + "type": "text/javascript", + "packages": {} } }, { @@ -6273,7 +6283,8 @@ "exec": [ "setTimeout(() => {}, 3000);" ], - "type": "text/javascript" + "type": "text/javascript", + "packages": {} } } ], @@ -7191,7 +7202,7 @@ " const STATS_TEMPLATE_1 = pm.variables.get(\"__CONSTANTS__.USER_BOT_RESPONSES.STATS_TEMPLATE_1.en\")\r", " const PLACEHOLDER_MISLEADING = pm.variables.get(\"__CONSTANTS__.USER_BOT_RESPONSES.PLACEHOLDER_MISLEADING.en\")\r", " const INFO_PLACEHOLDER = pm.variables.get(\"__CONSTANTS__.USER_BOT_RESPONSES.INFO_PLACEHOLDER.en\")\r", - " const expectedBody = STATS_TEMPLATE_1.replace(\"{{top}}\", \"100.00\").replace(\"{{category}}\",PLACEHOLDER_MISLEADING).replace(\"{{info_placeholder}}\",INFO_PLACEHOLDER.replace(\"{{score}}\",\"2.33\"))\r", + " const expectedBody = STATS_TEMPLATE_1.replace(\"{{top}}\", \"100.0\").replace(\"{{category}}\",PLACEHOLDER_MISLEADING).replace(\"{{info_placeholder}}\",INFO_PLACEHOLDER.replace(\"{{score}}\",\"2.3\"))\r", " //we not using the template for this, its hardcoded.\r", " const expected = {\r", " \"hostname\": \"resultserver\",\r", @@ -7213,7 +7224,8 @@ " pm.expect(jsonData).to.eql(expected);\r", "});" ], - "type": "text/javascript" + "type": "text/javascript", + "packages": {} } }, { @@ -7222,7 +7234,8 @@ "exec": [ "setTimeout(() => {}, 3000);" ], - "type": "text/javascript" + "type": "text/javascript", + "packages": {} } } ], @@ -8557,7 +8570,7 @@ " const INTERIM_TEMPLATE_UNSURE = pm.variables.get(\"__CONSTANTS__.USER_BOT_RESPONSES.INTERIM_TEMPLATE_UNSURE.en\");\r", " const BUTTON_ANOTHER_UPDATE = pm.variables.get(\"__CONSTANTS__.USER_BOT_RESPONSES.BUTTON_ANOTHER_UPDATE.en\")\r", " const instanceId = pm.variables.get(\"unsureInstanceId\")\r", - " const interimResponse = INTERIM_TEMPLATE_UNSURE.replace(\"{{%voted}}\", \"33.33\")\r", + " const interimResponse = INTERIM_TEMPLATE_UNSURE.replace(\"{{%voted}}\", \"33.3\")\r", " const expected = {\r", " \"hostname\": \"resultserver\",\r", " \"path\": \"/v15.0/WHATSAPP_TEST_USER_BOT_PHONE_NUMBER_ID/messages\",\r", @@ -8594,7 +8607,8 @@ " pm.expect(jsonData).to.eql(expected);\r", "});" ], - "type": "text/javascript" + "type": "text/javascript", + "packages": {} } }, { @@ -8603,7 +8617,8 @@ "exec": [ "setTimeout(() => {}, 3000);" ], - "type": "text/javascript" + "type": "text/javascript", + "packages": {} } } ], @@ -8873,7 +8888,7 @@ " const INTERIM_TEMPLATE = pm.variables.get(\"__CONSTANTS__.USER_BOT_RESPONSES.INTERIM_TEMPLATE.en\");\r", " const BUTTON_ANOTHER_UPDATE = pm.variables.get(\"__CONSTANTS__.USER_BOT_RESPONSES.BUTTON_ANOTHER_UPDATE.en\")\r", " const instanceId = pm.variables.get(\"unsureInstanceId\")\r", - " const interimResponse = INTERIM_TEMPLATE.replace(\"{{%voted}}\", \"33.33\").replace(\"{{prelim_assessment}}\",\"legitimate✅\").replace(\"{{info_placeholder}}\",\"\")\r", + " const interimResponse = INTERIM_TEMPLATE.replace(\"{{%voted}}\", \"33.3\").replace(\"{{prelim_assessment}}\",\"legitimate✅\").replace(\"{{info_placeholder}}\",\"\")\r", " const expected = {\r", " \"hostname\": \"resultserver\",\r", " \"path\": \"/v15.0/WHATSAPP_TEST_USER_BOT_PHONE_NUMBER_ID/messages\",\r", @@ -8910,7 +8925,8 @@ " pm.expect(jsonData).to.eql(expected);\r", "});" ], - "type": "text/javascript" + "type": "text/javascript", + "packages": {} } }, { @@ -8919,7 +8935,8 @@ "exec": [ "setTimeout(() => {}, 3000);" ], - "type": "text/javascript" + "type": "text/javascript", + "packages": {} } } ], @@ -9573,7 +9590,7 @@ " const INTERIM_TEMPLATE_UNSURE = pm.variables.get(\"__CONSTANTS__.USER_BOT_RESPONSES.INTERIM_TEMPLATE_UNSURE.en\");\r", " const BUTTON_ANOTHER_UPDATE = pm.variables.get(\"__CONSTANTS__.USER_BOT_RESPONSES.BUTTON_ANOTHER_UPDATE.en\");\r", " const instanceId = pm.variables.get(\"unsureInstanceId\")\r", - " const interimResponse = INTERIM_TEMPLATE_UNSURE.replace(\"{{%voted}}\", \"66.67\")\r", + " const interimResponse = INTERIM_TEMPLATE_UNSURE.replace(\"{{%voted}}\", \"66.7\")\r", "\r", " const expected = {\r", " \"hostname\": \"resultserver\",\r", @@ -9611,7 +9628,8 @@ " pm.expect(jsonData).to.eql(expected);\r", "});" ], - "type": "text/javascript" + "type": "text/javascript", + "packages": {} } }, { @@ -9621,7 +9639,8 @@ "// Allow time for firestore onUpdate event to complete\r", "setTimeout(() => {}, 3000);" ], - "type": "text/javascript" + "type": "text/javascript", + "packages": {} } } ], @@ -10395,7 +10414,7 @@ " \"path\": \"/v15.0/WHATSAPP_TEST_USER_BOT_PHONE_NUMBER_ID/messages\",\r", " \"body\": {\r", " \"text\": {\r", - " \"body\": \"33.33% of our CheckMates felt this was *spam🚧*. 33.33% felt this was *misleading⚠️*, with an average score of 3.00 on a scale of 1-5 (5 = completely true).\",\r", + " \"body\": \"33.3% of our CheckMates felt this was *spam🚧*. 33.3% felt this was *misleading⚠️*, with an average score of 3.0 on a scale of 1-5 (5 = completely true).\",\r", " \"preview_url\": false\r", " },\r", " \"to\": USER_1_NUMBER,\r", @@ -10410,7 +10429,8 @@ " pm.expect(jsonData).to.eql(expected);\r", "});" ], - "type": "text/javascript" + "type": "text/javascript", + "packages": {} } }, { @@ -10419,7 +10439,8 @@ "exec": [ "setTimeout(() => {}, 3000);" ], - "type": "text/javascript" + "type": "text/javascript", + "packages": {} } } ], From 040d7cbb78d50b018c4d321403baed06a75f2e6a Mon Sep 17 00:00:00 2001 From: Bing Wen Tan Date: Sun, 7 Apr 2024 20:42:16 +0800 Subject: [PATCH 5/5] add masked sender to vote --- functions/src/definitions/api/handlers/getVote.ts | 8 ++++++++ functions/src/definitions/api/interfaces.ts | 1 + 2 files changed, 9 insertions(+) diff --git a/functions/src/definitions/api/handlers/getVote.ts b/functions/src/definitions/api/handlers/getVote.ts index 89ffd3ea..ab6ef2ff 100644 --- a/functions/src/definitions/api/handlers/getVote.ts +++ b/functions/src/definitions/api/handlers/getVote.ts @@ -45,6 +45,13 @@ const getVoteHandler = async (req: Request, res: Response) => { const latestType = latestInstanceSnap.get("type") ?? "text" + const sender = latestInstanceSnap.get("from") ?? "Unknown" + + //mask all but last 4 characters of sender + + const maskedSender = + sender != "Unknown" ? sender.replace(/.(?=.{4})/g, "*") : sender + const storageBucketUrl = latestInstanceSnap.get("storageUrl") const signedUrl = latestType === "image" ? await getSignedUrl(storageBucketUrl) : null @@ -123,6 +130,7 @@ const getVoteHandler = async (req: Request, res: Response) => { latestType === "image" ? latestInstanceSnap.get("caption") : null, signedImageUrl: signedUrl, category: voteRequestSnap.get("category"), + sender: maskedSender, truthScore: isLegacy ? voteRequestSnap.get("vote") : voteRequestSnap.get("truthScore"), diff --git a/functions/src/definitions/api/interfaces.ts b/functions/src/definitions/api/interfaces.ts index 3fcb0c55..6f52503d 100644 --- a/functions/src/definitions/api/interfaces.ts +++ b/functions/src/definitions/api/interfaces.ts @@ -77,6 +77,7 @@ interface VoteSummaryApiResponse { interface Vote { type: "image" | "text" text: string | null //only for type text + sender: string caption: string | null //only for type image signedImageUrl: string | null //only for type image category: string | null