Skip to content

Commit 71d2646

Browse files
committed
feat: add quiz functionality with tracking and user interaction
1 parent 63870f0 commit 71d2646

11 files changed

Lines changed: 162 additions & 11 deletions

File tree

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
-- CreateTable
2+
CREATE TABLE `Quiz` (
3+
`id` INTEGER NOT NULL AUTO_INCREMENT,
4+
`mid` VARCHAR(191) NOT NULL,
5+
`lid` VARCHAR(191) NOT NULL,
6+
`qid` VARCHAR(191) NOT NULL,
7+
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
8+
`updatedAt` DATETIME(3) NOT NULL,
9+
10+
PRIMARY KEY (`id`)
11+
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
-- AlterTable
2+
ALTER TABLE `Quiz` ADD COLUMN `answeredAt` DATETIME(3) NULL;
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/*
2+
Warnings:
3+
4+
- A unique constraint covering the columns `[mid]` on the table `Quiz` will be added. If there are existing duplicate values, this will fail.
5+
6+
*/
7+
-- CreateIndex
8+
CREATE UNIQUE INDEX `Quiz_mid_key` ON `Quiz`(`mid`);
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
-- AlterTable
2+
ALTER TABLE `User` ADD COLUMN `quizAnswered` INTEGER NOT NULL DEFAULT 0;

prisma/schema.prisma

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ model User {
2424
mode String
2525
about String?
2626
commandCount Int @default(0)
27+
quizAnswered Int @default(0)
2728
createdAt DateTime @default(now())
2829
updatedAt DateTime @updatedAt
2930
}
@@ -70,4 +71,14 @@ model Log {
7071
output String?
7172
createdAt DateTime @default(now())
7273
updatedAt DateTime @updatedAt
73-
}
74+
}
75+
76+
model Quiz {
77+
id Int @id @default(autoincrement())
78+
mid String @unique
79+
lid String
80+
qid String
81+
answeredAt DateTime?
82+
createdAt DateTime @default(now())
83+
updatedAt DateTime @updatedAt
84+
}

src/commands/quiz.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { Message } from "whatsapp-web.js";
22
import { quiz } from "../components/utils/data";
3+
import { newQuizAttempt } from "../components/services/quiz";
4+
import log from "../components/utils/log";
35

46
export const info = {
57
command: "quiz",
@@ -13,9 +15,12 @@ export const info = {
1315
export default async function (msg: Message) {
1416
if (!/^quiz$/i.test(msg.body)) return;
1517

16-
const response = quiz[Math.floor(Math.random() * quiz.length)];
18+
const id = Math.floor(Math.random() * quiz.length);
19+
const response = quiz[id];
20+
1721
if (response.length === 0)
1822
return await msg.reply("I don't have any quiz questions right now...");
23+
1924
let text = `
2025
\`${response.question}\`
2126
`;
@@ -29,5 +34,9 @@ export default async function (msg: Message) {
2934
`;
3035
}
3136

32-
await msg.reply(text);
37+
const messageReturn = await msg.reply(text);
38+
await Promise.all([
39+
newQuizAttempt(messageReturn, id.toString()),
40+
log.info("quiz", `Quiz question sent: ${response.question}`),
41+
]);
3342
}

src/commands/top.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export default async function (msg: Message) {
2828
.map((u, index) => {
2929
const displayName =
3030
u.name.length > 12 ? u.name.slice(0, 12) + "..." : u.name;
31-
return `${index + 1}. ${displayName} - ${u.commandCount} Points`;
31+
return `${index + 1}. ${displayName} - ${u.commandCount + u.quizAnswered} Points`;
3232
})
3333
.join("\n ")}
3434
`;

src/components/events/message.ts

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import sleep from "../utils/sleep";
1212
import { findOrCreateUser, isBlocked } from "../services/user";
1313
import { client } from "../client";
1414
import Font from "../utils/font";
15+
import quiz from "./quiz";
1516

1617
const commandPrefix = process.env.COMMAND_PREFIX || "!";
1718
const commandPrefixLess = process.env.COMMAND_PREFIX_LESS === "true";
@@ -30,6 +31,15 @@ export default async function message(msg: Message) {
3031
)
3132
return; // ignore them all
3233

34+
/*
35+
*
36+
* Quiz command validation
37+
*/
38+
if (msg.hasQuotedMsg) {
39+
const quoted = await msg.getQuotedMessage();
40+
if (await quiz(msg, quoted)) return;
41+
}
42+
3343
// process normalization
3444
msg.body = msg.body
3545
.normalize("NFKC")
@@ -129,13 +139,11 @@ export default async function message(msg: Message) {
129139
await Promise.all([
130140
handler.exec(msg),
131141
(async () => {
132-
if (!msg.fromMe) {
133-
const user = await findOrCreateUser(msg);
142+
const user = await findOrCreateUser(msg);
134143

135-
if (user) {
136-
await sleep(2000); // Prevent rate limiting issues
137-
await msg.react("✅");
138-
}
144+
if (user) {
145+
await sleep(2000); // Prevent rate limiting issues
146+
await msg.react("✅");
139147
}
140148

141149
return Promise.resolve();

src/components/events/quiz.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { Message } from "whatsapp-web.js";
2+
import { getQuizAttempts, setQuizAttemptAnswered } from "../services/quiz";
3+
import { quiz } from "../utils/data";
4+
5+
const done = [
6+
"Correct! 🎉",
7+
"Well done! 👍",
8+
"Nice job! ✅",
9+
"You got it! 🥳",
10+
"That's right! 👏",
11+
"Excellent! 🌟",
12+
"Great answer! 💡",
13+
"Spot on! 🎯",
14+
"Perfect! 🏆",
15+
"You nailed it! 🔥",
16+
];
17+
18+
export default async function (
19+
msg: Message,
20+
quoted: Message
21+
): Promise<boolean> {
22+
if (!quoted.body) return false;
23+
24+
const quizAttempts = await getQuizAttempts(quoted);
25+
if (quizAttempts) {
26+
const question = quiz[parseInt(quizAttempts.qid)];
27+
28+
if (msg.body.trim().toLowerCase() === question.answer.toLowerCase()) {
29+
await Promise.all([
30+
msg.reply(done[Math.floor(Math.random() * done.length)]),
31+
setQuizAttemptAnswered(msg, quoted),
32+
]);
33+
return true;
34+
}
35+
}
36+
return false;
37+
}

src/components/services/quiz.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { prisma } from "../prisma";
2+
import log from "../utils/log";
3+
import { Message } from "whatsapp-web.js";
4+
5+
export async function newQuizAttempt(msg: Message, qid: string): Promise<void> {
6+
try {
7+
const lid = msg.id.remote.split("@")[0];
8+
await prisma.quiz.create({
9+
data: {
10+
mid: msg.id.id,
11+
lid,
12+
qid,
13+
},
14+
});
15+
} catch (error) {
16+
log.error("Database", "Failed to add quiz.", error);
17+
}
18+
}
19+
20+
export async function getQuizAttempts(msg: Message): Promise<any | null> {
21+
try {
22+
if (!msg.id || !msg.id.remote) return [];
23+
24+
const quiz = await prisma.quiz.findUnique({
25+
where: { mid: msg.id.id, answeredAt: null },
26+
});
27+
28+
return quiz;
29+
} catch (error) {
30+
log.error("Database", `Failed to get quiz attempts.`, error);
31+
}
32+
return null;
33+
}
34+
35+
export async function setQuizAttemptAnswered(
36+
msg: Message,
37+
quoted: Message
38+
): Promise<void> {
39+
try {
40+
if (!quoted.id || !quoted.id.remote) return;
41+
42+
await Promise.all([
43+
prisma.quiz.update({
44+
where: { mid: quoted.id.id },
45+
data: { answeredAt: new Date() },
46+
}),
47+
prisma.user.update({
48+
where: {
49+
lid: msg.author ? msg.author.split("@")[0] : msg.from.split("@")[0],
50+
},
51+
data: {
52+
quizAnswered: {
53+
increment: 1,
54+
},
55+
},
56+
}),
57+
]);
58+
} catch (error) {
59+
log.error("Database", `Failed to set quiz attempt as answered.`, error);
60+
}
61+
}

0 commit comments

Comments
 (0)