{
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "nodes": [
    {
      "id": "011057a0-718e-4c13-86da-fc4af6db18ec",
      "name": "\ud83d\udccb Flow Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -224,
        -672
      ],
      "parameters": {
        "width": 560,
        "height": 628,
        "content": "## \ud83c\udf93 WhatsApp Learning Assistant \u2013 Quiz Generator & Progress Tracker\n\n**How it works:**\n1. Student sends a topic e.g. *quiz math* on WhatsApp via WATI\n2. Switch routes message by keyword command\n3. OpenAI generates 3 MCQ questions on that topic\n4. Student replies with answers e.g. *answer 1a 2b 3c*\n5. Score calculated, saved to Google Sheets, feedback sent back\n6. Student types *progress* anytime to see score history\n\n**Credentials needed:** WATI, OpenAI API Key (Header Auth), Google Sheets OAuth2"
      },
      "typeVersion": 1
    },
    {
      "id": "d652bc1d-16a9-495a-9a32-288e97b9e4b8",
      "name": "Sticky \u2013 Trigger & Route",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        528,
        16
      ],
      "parameters": {
        "color": 7,
        "width": 370,
        "height": 510,
        "content": "### 1\ufe0f\u20e3 Trigger & Route\n**WATI Trigger** listens for incoming WhatsApp messages.\n**Route Message Switch** detects the command keyword:\n- Starts with `quiz` \u2192 generate quiz flow\n- Starts with `answer` \u2192 evaluate answers flow\n- `progress` \u2192 fetch score history\n- Anything else \u2192 send help message"
      },
      "typeVersion": 1
    },
    {
      "id": "abd16359-1519-470b-9583-c6168bae51b0",
      "name": "Sticky \u2013 Quiz Generation",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        960,
        -128
      ],
      "parameters": {
        "color": 7,
        "width": 1254,
        "height": 382,
        "content": "### 2\ufe0f\u20e3 Quiz Generation\n**Extract Topic Code** parses the topic from the message.\n**AI Agent** generates 3 MCQ questions with options A-D and correct answers.\n**Format Quiz Code** parses the AI response, formats for WhatsApp and saves quiz session to Google Sheets.\n**WATI \u2013 Send Text Message** delivers the formatted report back to the student."
      },
      "typeVersion": 1
    },
    {
      "id": "95713db8-e5cb-4f3a-98ff-06d2ba5e438c",
      "name": "Sticky \u2013 Answer Evaluation",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1024,
        304
      ],
      "parameters": {
        "color": 7,
        "width": 966,
        "height": 350,
        "content": "### 3\ufe0f\u20e3 Answer Evaluation\n**Parse Answers Code** extracts student answers from message e.g. `answer 1a 2b 3c`.\n**Sheets \u2013 Read Quiz** fetches the stored quiz for this student.\n**Evaluate & Score Code** compares answers, calculates score and builds detailed feedback.\n**Sheets \u2013 Save Score** logs result: phone, topic, score, date.\n**WATI \u2013 Send Text Message** delivers the formatted report back to the student."
      },
      "typeVersion": 1
    },
    {
      "id": "a55cbd2e-9c04-4e5a-8f13-0d2a9a50f20b",
      "name": "Sticky \u2013 Progress Report",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1056,
        720
      ],
      "parameters": {
        "color": 7,
        "width": 626,
        "height": 302,
        "content": "### 4\ufe0f\u20e3 Progress Report\n**Sheets \u2013 Read Progress** fetches all score rows for this student's phone number.\n**Build Progress Report Code** calculates total quizzes, average score, best topic and recent history with visual bar.\n**WATI \u2013 Send Text Message** delivers the formatted report back to the student."
      },
      "typeVersion": 1
    },
    {
      "id": "d29141aa-70fd-4dfa-91a7-1767ca92cc41",
      "name": "Route Message",
      "type": "n8n-nodes-base.switch",
      "position": [
        736,
        288
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "outputKey": "Quiz Request",
              "conditions": {
                "options": {
                  "version": 1,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "7b75a712-fea7-46b3-8af0-0a958c50e381",
                    "operator": {
                      "type": "string",
                      "operation": "startsWith"
                    },
                    "leftValue": "={{ $json.text }}",
                    "rightValue": "quiz "
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "Submit Answer",
              "conditions": {
                "options": {
                  "version": 1,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "34d4f348-48e4-4f4b-832f-af9c56b4f81b",
                    "operator": {
                      "type": "string",
                      "operation": "startsWith"
                    },
                    "leftValue": "={{ $json.text }}",
                    "rightValue": "answer "
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "Progress",
              "conditions": {
                "options": {
                  "version": 1,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "89f902f7-e331-4821-b8f8-e4dd476a4245",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.text.toLowerCase() }}",
                    "rightValue": "progress"
                  }
                ]
              },
              "renameOutput": true
            }
          ]
        },
        "options": {
          "fallbackOutput": "extra"
        }
      },
      "typeVersion": 3
    },
    {
      "id": "a90b94dd-7da7-4afd-8e3a-6d77ebe0a734",
      "name": "Extract Topic",
      "type": "n8n-nodes-base.code",
      "position": [
        1008,
        16
      ],
      "parameters": {
        "jsCode": "// Extract Topic from Quiz Request\n// Input: 'quiz photosynthesis' or 'quiz world war 2'\nconst text = $json.text || '';\nconst phone = $json.waId || $json.from || 'unknown';\nconst senderName = $json.senderName || 'Student';\n\nconst topic = text.replace(/^quiz\\s+/i, '').trim();\nif (!topic) throw new Error('No topic provided.');\n\nconst today = new Date().toISOString().split('T')[0];\nconst sessionKey = `${phone}_${today}`;\n\nreturn { json: { phone, senderName, topic, sessionKey, today } };"
      },
      "typeVersion": 2
    },
    {
      "id": "c9b1c12d-e189-46f1-8c62-dcac2d866169",
      "name": "Format Quiz & Build Message",
      "type": "n8n-nodes-base.code",
      "position": [
        1520,
        0
      ],
      "parameters": {
        "jsCode": "// Format Quiz & Build WhatsApp Message\n// Parses OpenAI response and formats quiz for WhatsApp display\n\nconst rawText = $json.output || '{}';\n\nlet quiz = {};\ntry {\n  quiz = JSON.parse(rawText);\n} catch (e) {\n  const match = rawText.match(/\\{[\\s\\S]*\\}/);\n  if (match) { try { quiz = JSON.parse(match[0]); } catch (e2) { quiz = {}; } }\n}\n\nconst phone = $('Extract Topic').item.json.phone;\nconst senderName = $('Extract Topic').item.json.senderName;\nconst topic = $('Extract Topic').item.json.topic;\nconst sessionKey = $('Extract Topic').item.json.sessionKey;\nconst today = $('Extract Topic').item.json.today;\nconst questions = quiz.questions || [];\n\nif (questions.length === 0) throw new Error('OpenAI did not return valid questions');\n\n// Build WhatsApp quiz message\nconst lines = [\n  `\ud83c\udf93 *Quiz Time, ${senderName}!*`,\n  `\ud83d\udcda *Topic: ${topic.toUpperCase()}*`,\n  '',\n  'Reply with: *answer 1X 2X 3X*',\n  'Example: *answer 1a 2c 3b*',\n  '\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501'\n];\n\nconst correctAnswers = {};\nfor (const q of questions) {\n  lines.push('');\n  lines.push(`*Q${q.number}.* ${q.question}`);\n  lines.push(`  \ud83c\udd50 ${q.options.a}`);\n  lines.push(`  \ud83c\udd51 ${q.options.b}`);\n  lines.push(`  \ud83c\udd52 ${q.options.c}`);\n  lines.push(`  \ud83c\udd53 ${q.options.d}`);\n  correctAnswers[q.number] = q.correct.toLowerCase();\n}\n\nlines.push('');\nlines.push('\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501');\nlines.push('\u23f1\ufe0f Reply with your answers now!');\n\n// Serialize correct answers: '1:a,2:c,3:b'\nconst answersString = Object.entries(correctAnswers).map(([n,a]) => `${n}:${a}`).join(',');\n\nreturn {\n  json: {\n    phone, senderName, topic, sessionKey, today,\n    quizMessage: lines.join('\\n'),\n    correctAnswers: answersString,\n    questionCount: questions.length\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "314bc871-8e7d-43e4-9ade-f13838691eeb",
      "name": "Google Sheets \u2013 Save Active Quiz",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1728,
        0
      ],
      "parameters": {
        "columns": {
          "value": {
            "phone": "={{ $json.phone }}",
            "today": "={{ $json.today }}",
            "topic": "={{ $json.topic }}",
            "sessionKey": "={{ $json.sessionKey }}",
            "questionCount": "={{ $json.questionCount }}",
            "correctAnswers": "={{ $json.correctAnswers }}"
          },
          "schema": [
            {
              "id": "sessionKey",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "sessionKey",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "phone",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "phone",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "topic",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "topic",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "correctAnswers",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "correctAnswers",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "questionCount",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "questionCount",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "today",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "today",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1U_qWPXSk-WfGb3lCl_Gkb17h8K0qJr_DfPxsSZ1i8hY/edit#gid=0",
          "cachedResultName": "Active Quizzes"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1U_qWPXSk-WfGb3lCl_Gkb17h8K0qJr_DfPxsSZ1i8hY",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1U_qWPXSk-WfGb3lCl_Gkb17h8K0qJr_DfPxsSZ1i8hY/edit?usp=drivesdk",
          "cachedResultName": "Untitled spreadsheet"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "d248438c-e50b-4d20-b547-f53be5b7e106",
      "name": "Parse Student Answers",
      "type": "n8n-nodes-base.code",
      "position": [
        1104,
        480
      ],
      "parameters": {
        "jsCode": "// Parse Student Answers\n// Input: 'answer 1a 2c 3b'\nconst text = ($json.text || '').trim();\nconst phone = $json.waId || $json.from || 'unknown';\nconst senderName = $json.senderName || 'Student';\n\nconst answerPart = text.replace(/^answer\\s+/i, '').trim();\nconst matches = answerPart.match(/([1-9][a-dA-D])/g);\n\nif (!matches || matches.length === 0) {\n  throw new Error('Could not parse answers. Format: answer 1a 2b 3c');\n}\n\nconst studentAnswers = {};\nfor (const match of matches) {\n  studentAnswers[parseInt(match[0])] = match[1].toLowerCase();\n}\n\nconst today = new Date().toISOString().split('T')[0];\nconst sessionKey = `${phone}_${today}`;\n\nreturn { json: { phone, senderName, sessionKey, today, studentAnswers } };"
      },
      "typeVersion": 2
    },
    {
      "id": "395a0afa-1d20-4d17-9c24-b6980519ee7d",
      "name": "Google Sheets \u2013 Read Active Quiz",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1280,
        480
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1U_qWPXSk-WfGb3lCl_Gkb17h8K0qJr_DfPxsSZ1i8hY/edit#gid=0",
          "cachedResultName": "Active Quizzes"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1U_qWPXSk-WfGb3lCl_Gkb17h8K0qJr_DfPxsSZ1i8hY",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1U_qWPXSk-WfGb3lCl_Gkb17h8K0qJr_DfPxsSZ1i8hY/edit?usp=drivesdk",
          "cachedResultName": "Untitled spreadsheet"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "4cb5e7d9-ebe6-4e0c-9e9a-5b39b41065d6",
      "name": "Evaluate & Calculate Score",
      "type": "n8n-nodes-base.code",
      "position": [
        1440,
        480
      ],
      "parameters": {
        "jsCode": "// Evaluate Answers & Calculate Score\n// Compares student answers vs correct answers from Google Sheets\n\nconst parsedData = $('Parse Student Answers').item.json;\nconst { phone, senderName, sessionKey, today, studentAnswers } = parsedData;\n\n// Find matching quiz row\nconst allRows = $input.all();\nconst quizRow = allRows.find(r => r.json.sessionKey === sessionKey);\n\nif (!quizRow) {\n  return {\n    json: {\n      phone,\n      feedbackMessage: `\u26a0\ufe0f *No active quiz found!*\\n\\nYou haven't requested a quiz today yet.\\nSend *quiz <topic>* to start one!\\nExample: *quiz solar system*`,\n      score: 0, total: 0, topic: 'N/A', today, hasQuiz: false\n    }\n  };\n}\n\nconst topic = quizRow.json.topic;\nconst questionCount = parseInt(quizRow.json.questionCount) || 3;\n\n// Parse correct answers from '1:a,2:c,3:b'\nconst correctAnswers = {};\nfor (const pair of (quizRow.json.correctAnswers || '').split(',')) {\n  const [num, ans] = pair.split(':');\n  if (num && ans) correctAnswers[parseInt(num)] = ans.toLowerCase();\n}\n\n// Score calculation\nlet score = 0;\nconst resultLines = [];\nfor (let i = 1; i <= questionCount; i++) {\n  const studentAns = (studentAnswers[i] || '?').toLowerCase();\n  const correctAns = correctAnswers[i] || '?';\n  const isCorrect = studentAns === correctAns;\n  if (isCorrect) score++;\n  resultLines.push(`Q${i}: You answered *${studentAns.toUpperCase()}* ${isCorrect ? '\u2705' : `\u274c (correct: ${correctAns.toUpperCase()})`}`);\n}\n\nconst percentage = Math.round((score / questionCount) * 100);\nlet performanceMsg = percentage === 100 ? '\ud83c\udfc6 Perfect score! Outstanding!'\n  : percentage >= 66 ? '\ud83c\udf1f Great job! Keep it up!'\n  : percentage >= 33 ? '\ud83d\udcd6 Good effort! Review and try again!'\n  : '\ud83d\udcaa Keep studying! You can do better!';\n\nconst feedbackLines = [\n  `\ud83d\udcdd *Quiz Results \u2013 ${topic.toUpperCase()}*`,\n  '',\n  ...resultLines,\n  '',\n  '\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501',\n  `\ud83c\udfaf *Score: ${score}/${questionCount} (${percentage}%)*`,\n  performanceMsg,\n  '',\n  'Type *progress* to see your full history!',\n  'Type *quiz <topic>* to try another quiz!'\n];\n\nreturn {\n  json: {\n    phone, senderName, topic, score,\n    total: questionCount, percentage, sessionKey, today,\n    feedbackMessage: feedbackLines.join('\\n'),\n    hasQuiz: true\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "24e96f7e-8fcd-4899-950b-f9dbed03eeb4",
      "name": "Google Sheets \u2013 Save Score",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1616,
        480
      ],
      "parameters": {
        "columns": {
          "value": {
            "date": "={{ $json.today }}",
            "phone": "={{ $json.phone }}",
            "score": "={{ $json.score }}",
            "topic": "={{ $json.topic }}",
            "total": "={{ $json.total }}",
            "percentage": "={{ $json.percentage }}",
            "senderName": "={{ $json.senderName }}"
          },
          "schema": [
            {
              "id": "date",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "phone",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "phone",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "senderName",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "senderName",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "topic",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "topic",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "score",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "score",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "total",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "total",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "percentage",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "percentage",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 916130242,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1U_qWPXSk-WfGb3lCl_Gkb17h8K0qJr_DfPxsSZ1i8hY/edit#gid=916130242",
          "cachedResultName": "Scores"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1U_qWPXSk-WfGb3lCl_Gkb17h8K0qJr_DfPxsSZ1i8hY",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1U_qWPXSk-WfGb3lCl_Gkb17h8K0qJr_DfPxsSZ1i8hY/edit?usp=drivesdk",
          "cachedResultName": "Untitled spreadsheet"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "f11f8afb-154e-4771-a5e8-98ee025d720d",
      "name": "Google Sheets \u2013 Read Progress",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1088,
        848
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 916130242,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1U_qWPXSk-WfGb3lCl_Gkb17h8K0qJr_DfPxsSZ1i8hY/edit#gid=916130242",
          "cachedResultName": "Scores"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1U_qWPXSk-WfGb3lCl_Gkb17h8K0qJr_DfPxsSZ1i8hY",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1U_qWPXSk-WfGb3lCl_Gkb17h8K0qJr_DfPxsSZ1i8hY/edit?usp=drivesdk",
          "cachedResultName": "Untitled spreadsheet"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "15333ca7-f728-41be-b707-7cf186e7f3ba",
      "name": "Build Progress Report",
      "type": "n8n-nodes-base.code",
      "position": [
        1296,
        848
      ],
      "parameters": {
        "jsCode": "// Build Progress Report\n// Fetches all scores for this student and builds a summary\n\nconst phone =  $('Wati Trigger1').first().json.waId|| $('Wati Trigger1').json.from;\nconst senderName =  $('Wati Trigger1').first().json.senderName|| 'Student';\nconst allRows = $input.all();\nconst myRows = allRows.filter(r => String(r.json.phone) === String(phone));\n\nif (myRows.length === 0) {\n  return [{ json: {\n    phone,\n    progressMessage: `\ud83d\udcca *No quiz history yet, ${senderName}!*\\n\\nStart your first quiz:\\n*quiz <topic>*\\nExample: *quiz photosynthesis*`\n  }}];\n}\n\nconst totalQuizzes = myRows.length;\nconst totalScore = myRows.reduce((s, r) => s + (parseFloat(r.json.score) || 0), 0);\nconst totalQuestions = myRows.reduce((s, r) => s + (parseFloat(r.json.total) || 0), 0);\nconst avgPercentage = Math.round((totalScore / totalQuestions) * 100);\n\n// Best topic calculation\nconst topicMap = {};\nfor (const r of myRows) {\n  const t = r.json.topic || 'Unknown';\n  if (!topicMap[t]) topicMap[t] = { score: 0, total: 0 };\n  topicMap[t].score += parseFloat(r.json.score) || 0;\n  topicMap[t].total += parseFloat(r.json.total) || 0;\n}\nlet bestTopic = '';\nlet bestPct = 0;\nfor (const [topic, data] of Object.entries(topicMap)) {\n  const pct = data.total > 0 ? (data.score / data.total) * 100 : 0;\n  if (pct > bestPct) { bestPct = pct; bestTopic = topic; }\n}\n\n// Recent 5 quizzes\nconst recent = myRows.slice(-5).reverse();\nconst recentLines = recent.map(r => {\n  const pct = parseFloat(r.json.percentage) || 0;\n  const emoji = pct === 100 ? '\ud83c\udfc6' : pct >= 66 ? '\ud83c\udf1f' : pct >= 33 ? '\ud83d\udcd6' : '\ud83d\udcaa';\n  return `${emoji} ${r.json.topic} \u2013 ${r.json.score}/${r.json.total} (${pct}%) on ${r.json.date}`;\n});\n\n// Progress bar\nconst filled = Math.round(avgPercentage / 10);\nconst bar = '\u2588'.repeat(filled) + '\u2591'.repeat(10 - filled);\n\nconst lines = [\n  `\ud83d\udcca *Progress Report*`,\n  `\ud83d\udc64 *${senderName}*`,\n  '',\n  `${bar} *${avgPercentage}% avg score*`,\n  '',\n  `\ud83d\udcdd *Total Quizzes Taken:* ${totalQuizzes}`,\n  `\u2705 *Total Correct:* ${totalScore}/${totalQuestions}`,\n  `\ud83c\udfc5 *Best Topic:* ${bestTopic} (${Math.round(bestPct)}%)`,\n  '',\n  '\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501',\n  '*Recent Quizzes:*',\n  ...recentLines,\n  '',\n  'Type *quiz <topic>* to keep learning! \ud83d\ude80'\n];\n\nreturn [{ json: { phone, progressMessage: lines.join('\\n') } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "cf7720eb-1a8d-4620-be7f-22d452f4180f",
      "name": "Wati Trigger1",
      "type": "n8n-nodes-wati.watiTrigger",
      "position": [
        544,
        320
      ],
      "parameters": {
        "event": "messageReceived"
      },
      "credentials": {
        "watiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1,
      "alwaysOutputData": true
    },
    {
      "id": "0dd92282-065f-47a1-8fca-a99326399b5b",
      "name": "AI Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        1168,
        0
      ],
      "parameters": {
        "text": "=Generate a 3-question MCQ quiz about the topic: {{ $json.topic }}",
        "options": {
          "systemMessage": "=\"You are a professional tutor. Generate a 3-question MCQ quiz. Return ONLY a valid JSON object with 'questions' containing 'number', 'question', 'options' (a,b,c,d), and 'correct'. Do not include any conversational text or markdown blocks like ```json.\""
        },
        "promptType": "define"
      },
      "typeVersion": 3.1
    },
    {
      "id": "acafd240-d88c-444f-98a4-3fbabaa7eb2f",
      "name": "OpenAI Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        1168,
        144
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4.1-mini",
          "cachedResultName": "gpt-4.1-mini"
        },
        "options": {},
        "builtInTools": {}
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "b16ab10c-284b-4d67-b1e3-084de2af1371",
      "name": "Send Quiz",
      "type": "n8n-nodes-wati.wati",
      "position": [
        1952,
        0
      ],
      "parameters": {
        "target": "={{ $json.phone }}",
        "messageText": "={{ $('Format Quiz & Build Message').item.json.quizMessage }}ssage Text: ```markdown\n"
      },
      "credentials": {
        "watiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "592de9a8-28d1-4a77-a481-5cdcdc15f713",
      "name": "Send Score",
      "type": "n8n-nodes-wati.wati",
      "position": [
        1792,
        480
      ],
      "parameters": {
        "target": "={{ $json.phone }}",
        "messageText": "={{ $('Evaluate & Calculate Score').item.json.feedbackMessage }}"
      },
      "credentials": {
        "watiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "029b3a0e-7e4f-414e-b023-fe48d93858ff",
      "name": "Send Score1",
      "type": "n8n-nodes-wati.wati",
      "position": [
        1504,
        848
      ],
      "parameters": {
        "target": "={{ $json.phone }}",
        "messageText": "={{ $json.progressMessage }}"
      },
      "credentials": {
        "watiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    }
  ],
  "connections": {
    "AI Agent": {
      "main": [
        [
          {
            "node": "Format Quiz & Build Message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Topic": {
      "main": [
        [
          {
            "node": "AI Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Route Message": {
      "main": [
        [
          {
            "node": "Extract Topic",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Parse Student Answers",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Google Sheets \u2013 Read Progress",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wati Trigger1": {
      "main": [
        [
          {
            "node": "Route Message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "AI Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Build Progress Report": {
      "main": [
        [
          {
            "node": "Send Score1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Student Answers": {
      "main": [
        [
          {
            "node": "Google Sheets \u2013 Read Active Quiz",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Evaluate & Calculate Score": {
      "main": [
        [
          {
            "node": "Google Sheets \u2013 Save Score",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Quiz & Build Message": {
      "main": [
        [
          {
            "node": "Google Sheets \u2013 Save Active Quiz",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Sheets \u2013 Save Score": {
      "main": [
        [
          {
            "node": "Send Score",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Sheets \u2013 Read Progress": {
      "main": [
        [
          {
            "node": "Build Progress Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Sheets \u2013 Read Active Quiz": {
      "main": [
        [
          {
            "node": "Evaluate & Calculate Score",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Sheets \u2013 Save Active Quiz": {
      "main": [
        [
          {
            "node": "Send Quiz",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}