{"id":368,"date":"2025-11-25T16:13:25","date_gmt":"2025-11-25T16:13:25","guid":{"rendered":"https:\/\/i-cte.org\/robot\/?p=368"},"modified":"2025-11-25T16:13:25","modified_gmt":"2025-11-25T16:13:25","slug":"having-fun-listening","status":"publish","type":"post","link":"https:\/\/i-cte.org\/robot\/having-fun-listening\/","title":{"rendered":"Having fun &#8211; Listening"},"content":{"rendered":"\n<p>This Robot is used for practicing English at level A2. In order to interact with this Robot smoothly, please use a laptop, desktop, or iPad. Do not use Smartphones.<\/p>\n\n\n\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>Listening \u2013 Ayden\u2019s Busy Week<\/title>\n    <style>\n        \/* Reset and Base Styles *\/\n        * {\n            box-sizing: border-box;\n        }\n\n        body {\n            font-family: Arial, sans-serif;\n            background-color: #f4f6f8;\n            margin: 0;\n            padding: 0;\n            display: flex;\n            flex-direction: column;\n            min-height: 100vh;\n        }\n\n        \/* Menu Styles *\/\n        .menu {\n            width: 100%;\n            background-color: #28a745;\n            padding: 10px;\n            text-align: center;\n            box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);\n            margin-bottom: 10px;\n            overflow-x: auto;\n            white-space: nowrap;\n        }\n        .menu a {\n            color: #fff;\n            text-decoration: none;\n            margin: 0 10px;\n            font-weight: bold;\n            font-size: 14px;\n            display: inline-block;\n        }\n        .menu a:hover,\n        .menu a:focus {\n            text-decoration: underline;\n            outline: 2px dashed #fff;\n            outline-offset: 4px;\n        }\n\n        \/* Container Styles *\/\n        .container {\n            display: flex;\n            flex-direction: column;\n            align-items: center;\n            padding: 10px;\n            box-sizing: border-box;\n            flex: 1 0 auto;\n            width: 100%;\n        }\n\n        .image-chat-wrapper {\n            display: flex;\n            flex-direction: column;\n            width: 100%;\n            max-width: 900px;\n            flex: 1;\n        }\n\n        .image-container {\n            flex: 1;\n            text-align: center;\n            margin-bottom: 15px;\n        }\n        .image-container img {\n            width: 100%;\n            height: auto;\n            max-height: 260px;\n            object-fit: cover;\n            border-radius: 15px;\n            box-shadow: 0 0 8px rgba(0, 0, 0, 0.1);\n        }\n        .sentence-text {\n            margin: 10px 0;\n            font-weight: bold;\n            color: #333;\n            font-size: 16px;\n        }\n        .word-box {\n            margin-top: 10px;\n            padding: 10px;\n            background-color: #fff;\n            border: 2px solid #28a745;\n            border-radius: 8px;\n            box-shadow: 0 0 8px rgba(0, 0, 0, 0.1);\n            font-size: 15px;\n            font-weight: bold;\n            color: #28a745;\n            text-align: left;\n            max-height: 220px;\n            overflow-y: auto;\n        }\n\n        .audio-player {\n            margin-top: 10px;\n        }\n        .audio-player audio {\n            width: 100%;\n        }\n\n        .script-button {\n            margin-top: 8px;\n            background-color: #17a2b8;\n            color: #fff;\n            border: none;\n            border-radius: 5px;\n            padding: 8px 12px;\n            font-size: 14px;\n            font-weight: bold;\n            cursor: pointer;\n        }\n        .script-button:hover,\n        .script-button:focus {\n            background-color: #138496;\n            outline: none;\n        }\n\n        .chat-container {\n            background-color: #28a745;\n            border-radius: 15px;\n            box-shadow: 0 0 8px rgba(0, 0, 0, 0.1);\n            border: 3px solid #007bff;\n            overflow: hidden;\n            display: flex;\n            flex-direction: column;\n            height: auto;\n            max-height: 560px;\n            flex: 1;\n        }\n        .chat-header {\n            background-color: #1e7e34;\n            color: #fff;\n            padding: 12px;\n            text-align: center;\n            border-bottom: 1px solid #155724;\n            position: relative;\n        }\n        .chat-header h2 {\n            margin: 0;\n            font-size: 18px;\n        }\n        .chat-header .loader {\n            position: absolute;\n            right: 20px;\n            top: 50%;\n            transform: translateY(-50%);\n            display: none;\n        }\n        .chat-messages {\n            padding: 12px;\n            overflow-y: auto;\n            flex: 1;\n            background-color: #fff;\n            max-height: 380px;\n        }\n        .message {\n            margin-bottom: 10px;\n            padding: 10px;\n            border-radius: 6px;\n            font-size: 14px;\n            line-height: 1.5;\n        }\n        .bot-message {\n            background-color: #17a2b8;\n            color: #fff;\n            text-align: left;\n        }\n\n        .chat-input {\n            display: flex;\n            align-items: center;\n            background-color: #c3e6cb;\n            padding: 10px;\n            justify-content: space-between;\n            flex-wrap: wrap;\n        }\n        .send-button, .stop-button {\n            background-color: #17a2b8;\n            color: #fff;\n            font-weight: bold;\n            border: none;\n            border-radius: 5px;\n            padding: 12px 16px;\n            cursor: pointer;\n            transition: background-color 0.3s;\n            flex: 1;\n            margin: 6px;\n            max-width: 150px;\n            font-size: 16px;\n        }\n        .stop-button {\n            background-color: #dc3545;\n        }\n        .send-button:hover,\n        .stop-button:hover,\n        .send-button:focus,\n        .stop-button:focus {\n            background-color: #218838;\n            outline: none;\n        }\n\n        .voice-selection {\n            margin: 10px 0;\n            width: 100%;\n            max-width: 300px;\n        }\n        .voice-selection label {\n            display: block;\n            margin-bottom: 5px;\n            font-weight: bold;\n            color: #333;\n        }\n        .voice-selection select {\n            width: 100%;\n            padding: 8px;\n            border-radius: 5px;\n            border: 1px solid #ccc;\n            font-size: 14px;\n        }\n\n        .loader {\n            border: 4px solid #f3f3f3;\n            border-top: 4px solid #17a2b8;\n            border-radius: 50%;\n            width: 20px;\n            height: 20px;\n            animation: spin 2s linear infinite;\n            display: inline-block;\n            margin-left: 10px;\n        }\n\n        @keyframes spin {\n            0% { transform: rotate(0deg); }\n            100% { transform: rotate(360deg); }\n        }\n\n        \/* Inputs styling *\/\n        .short-answer-input,\n        .schedule-input {\n            width: 100%;\n            max-width: 260px;\n            padding: 4px 6px;\n            font-size: 13px;\n            border-radius: 4px;\n            border: 1px solid #ccc;\n        }\n\n        .schedule-table {\n            width: 100%;\n            border-collapse: collapse;\n            margin-top: 6px;\n            font-size: 13px;\n        }\n        .schedule-table th,\n        .schedule-table td {\n            border: 1px solid #ddd;\n            padding: 4px;\n            vertical-align: top;\n        }\n        .schedule-table th {\n            background-color: #f1f1f1;\n        }\n\n        .correct {\n            background-color: #d4edda !important;\n        }\n        .incorrect {\n            background-color: #f8d7da !important;\n        }\n        .unanswered {\n            background-color: #fff3cd !important;\n        }\n\n        \/* Responsive *\/\n        @media (orientation: portrait) {\n            .image-chat-wrapper {\n                flex-direction: column;\n            }\n            .image-container, .chat-container {\n                width: 100%;\n                max-width: 100%;\n            }\n        }\n\n        @media (orientation: landscape) and (min-width: 700px) {\n            .image-chat-wrapper {\n                flex-direction: row;\n                justify-content: space-between;\n            }\n            .image-container, .chat-container {\n                width: 48%;\n                max-width: 48%;\n            }\n        }\n    <\/style>\n<\/head>\n<body>\n    <div class=\"menu\">\n            <a href=\"https:\/\/i-cte.org\/robot\/having-fun-conversation\/\">Conversation<\/a>\n            <a href=\"https:\/\/i-cte.org\/robot\/having-fun-vocabulary\/\">Vocabulary<\/a>\n            <a href=\"https:\/\/i-cte.org\/robot\/having-fun-real-life\/\">Real Life<\/a>\n            <a href=\"https:\/\/i-cte.org\/robot\/having-fun-describing-pictures\/\">Describing Pictures<\/a>\n            <a href=\"https:\/\/i-cte.org\/robot\/having-fun-reading-comprehension\/\">Reading<\/a>\n            <a href=\"https:\/\/i-cte.org\/robot\/having-fun-listening\/\">Listening<\/a>\n    <\/div>\n\n    <div class=\"container\">\n        <div class=\"image-chat-wrapper\">\n            <div class=\"image-container\">\n                <!-- Replace this with Ayden\u2019s picture on your site -->\n                <img decoding=\"async\" src=\"https:\/\/i-cte.org\/robot\/wp-content\/uploads\/2025\/11\/6.webp\"\n                     alt=\"Ayden talking about his plans for next week\">\n                <div class=\"sentence-text\">\n                    Listen to Ayden, a student at Bilkent University in Turkey. Then answer the questions and complete his schedule.\n                <\/div>\n\n                <div class=\"audio-player\">\n                    <!-- \ud83d\udd0a Change the src below to the real URL of the MP3 on your site -->\n                    <audio id=\"ayden-audio\" controls>\n                        <source src=\"\/mnt\/data\/BTP_L1_SB_067.mp3\" type=\"audio\/mpeg\">\n                        Your browser does not support the audio element.\n                    <\/audio>\n                <\/div>\n\n                <!-- Button to play the script with two robot voices -->\n                <button id=\"play-script-btn\" class=\"script-button\" type=\"button\">\n                    Play Script (Robot Voices)\n                <\/button>\n\n                <div id=\"word-box\" class=\"word-box\">\n                    PART 1<br>\n                    1. Why are the students in Ayden\u2019s class having a party on Friday night?<br>\n                    2. What sport does he love to play?<br>\n                    3. What music does he hate?<br><br>\n                    PART 2 \u2013 Complete Ayden\u2019s appointment book with one or two words in each space.\n                <\/div>\n            <\/div>\n\n            <div class=\"chat-container\">\n                <div class=\"chat-header\">\n                    <h2>Listening \u2013 Ayden\u2019s Week<\/h2>\n                    <div class=\"loader\" id=\"synthesis-loader\"><\/div>\n                <\/div>\n                <div class=\"chat-messages\" id=\"chat-messages\">\n                    <!-- Questions will appear here -->\n                <\/div>\n                <div class=\"chat-input\">\n                    <div class=\"voice-selection\">\n                        <label for=\"voice-select\">Choose Voice for Feedback:<\/label>\n                        <select id=\"voice-select\" aria-label=\"Select Voice\">\n                            <option value=\"\">Loading voices&#8230;<\/option>\n                        <\/select>\n                    <\/div>\n                    <button id=\"start-btn\" class=\"send-button\" aria-label=\"Check Answers\">Check Answers<\/button>\n                    <button id=\"stop-btn\" class=\"stop-button\" aria-label=\"Stop Voice\">Stop Voice<\/button>\n                <\/div>\n            <\/div>\n        <\/div>\n    <\/div>\n\n    <div class=\"menu\">\n            <a href=\"https:\/\/i-cte.org\/robot\/having-fun-conversation\/\">Conversation<\/a>\n            <a href=\"https:\/\/i-cte.org\/robot\/having-fun-vocabulary\/\">Vocabulary<\/a>\n            <a href=\"https:\/\/i-cte.org\/robot\/having-fun-real-life\/\">Real Life<\/a>\n            <a href=\"https:\/\/i-cte.org\/robot\/having-fun-describing-pictures\/\">Describing Pictures<\/a>\n            <a href=\"https:\/\/i-cte.org\/robot\/having-fun-reading-comprehension\/\">Reading<\/a>\n            <a href=\"https:\/\/i-cte.org\/robot\/having-fun-listening\/\">Listening<\/a>\n    <\/div>\n\n<script>\n\/* ============================\n   Listening \u2013 Ayden\u2019s Week\n   ============================ *\/\n\n\/* Script: Interviewer = I, Ayden = A *\/\nconst listeningScript = `I: So, what plans do you have for next week, Ayden?\nA: Well, I\u2019m studying on Monday and Tuesday because we have our final exams on Wednesday.\nI: Too bad.\nA: Yeah. Anyway, after the exams, in the evening I\u2019m seeing my best friend, Erdal. We\u2019re going out for dinner to celebrate.\nI: Good idea. You can relax for a while.\nA: Yeah! I\u2019m not doing anything on Thursday, but on Friday night some students on my course are having a party, because it\u2019s the end of the semester. It starts at eight o\u2019clock.\nI: That sounds like fun!\nA: Sure! Then on Saturday morning I am seeing my parents. They\u2019re coming here at twelve o\u2019clock and we\u2019re going out for lunch. Hmm \u2026 I\u2019m seeing my friend Amy in the afternoon and \u2026 oh, yes, on Sunday I\u2019m playing basketball. I\u2019m on the college team, and we practice on Sunday mornings. I love basketball. Er, oh, and in the evening I\u2019m going to a concert with Amy.\nI: A pop concert?\nA: No, I hate pop. It\u2019s classical. Here at the University Concert Hall.\nI: Well, that\u2019s a busy week, for sure. I hope you have fun, Ayden! Good luck with your exams.\nA: Thanks!`;\n\n\/* PART 1 \u2013 Short answer questions *\/\nconst part1Questions = [\n  {\n    id: 1,\n    text: \"1) Why are the students in Ayden\u2019s class having a party on Friday night?\",\n    \/\/ at least \"end\" + \"semester\" OR \"end of the semester\"\n    keywordSets: [\n      [\"end\", \"semester\"],\n      [\"end\", \"term\"],\n      [\"end of the semester\"]\n    ]\n  },\n  {\n    id: 2,\n    text: \"2) What sport does he love to play?\",\n    keywordSets: [[\"basketball\"]]\n  },\n  {\n    id: 3,\n    text: \"3) What music does he hate?\",\n    keywordSets: [[\"pop\"]]\n  }\n];\n\n\/* PART 2 \u2013 Schedule blanks *\/\nconst scheduleItems = [\n  { id: \"tue\",  label: \"TUE \u2013 (He is) ________\", answers: [\"studying\", \"study\"] },\n  { id: \"wed_exam\", label: \"WED \u2013 Final ________!\", answers: [\"exams\", \"exam\"] },\n  { id: \"wed_dinner\", label: \"WED \u2013 p.m. go out to ________ with Erdal\", answers: [\"dinner\"] },\n  { id: \"fri_time\", label: \"FRI \u2013 Party at ________ p.m.\", answers: [\"8:00\", \"8\", \"eight\"] },\n  { id: \"sat_noon\", label: \"SAT \u2013 ________ arrive at noon\", answers: [\"parents\", \"my parents\", \"his parents\"] },\n  { id: \"sun_practice\", label: \"SUN \u2013 a.m. ________ practice\", answers: [\"basketball\", \"basketball team\"] },\n  { id: \"sun_place\", label: \"SUN \u2013 p.m. concert + Amy (at ________ Concert Hall)\", answers: [\"university\", \"the university\"] }\n];\n\nconst summaryMessages = [\n  \"Nice listening! Keep going.\",\n  \"Good job. Listen again and try to improve your score.\",\n  \"You\u2019re doing well. Practice this activity again later.\"\n];\n\nconst positiveMessages = [\n  \"Excellent comprehension!\",\n  \"Great work! You understood Ayden\u2019s plans.\",\n  \"Very good! Your answers match the audio quite well.\"\n];\n\n\/* Speech synthesis *\/\nconst speechSynthesisSupported = \"speechSynthesis\" in window;\nlet voices = [];\nlet selectedVoice = null; \/\/ feedback\nlet interviewerVoice = null;\nlet studentVoice = null;\n\nconst desiredVoiceNames = [\n  \"Google US English\",\n  \"Google US English Female\",\n  \"Google UK English Male\",\n  \"Google UK English Female\"\n];\n\n\/* ========== INITIALISE ========== *\/\n\nfunction initialize() {\n  loadVoices();\n\n  if (!speechSynthesisSupported) {\n    appendMessage(\"Sorry, your browser does not support speech synthesis.\", \"bot\");\n  }\n\n  document.getElementById(\"start-btn\").onclick = checkAnswers;\n  document.getElementById(\"stop-btn\").onclick = stopSpeaking;\n  document.getElementById(\"play-script-btn\").onclick = playScript;\n\n  buildTasks();\n\n  const intro =\n    \"First, listen to Ayden talk about his plans for next week. Then listen again, answer the three questions, and complete his appointment book.\";\n  appendMessage(intro, \"bot\");\n  if (speechSynthesisSupported) {\n    showSynthesisLoader(true);\n    sayText(intro, \"en-US\").then(() => showSynthesisLoader(false));\n  }\n}\n\n\/* ========== VOICES ========== *\/\n\nfunction loadVoices() {\n  if (!speechSynthesisSupported) return;\n  voices = window.speechSynthesis.getVoices();\n  if (voices.length === 0) {\n    window.speechSynthesis.onvoiceschanged = () => {\n      voices = window.speechSynthesis.getVoices();\n      populateVoiceList();\n    };\n  } else {\n    populateVoiceList();\n  }\n}\n\nfunction chooseDialogueVoices(filteredVoices) {\n  \/\/ interviewer: prefer male, student: another voice\n  interviewerVoice =\n    filteredVoices.find(v => \/male\/i.test(v.name)) ||\n    filteredVoices.find(v => \/UK English Male\/i.test(v.name)) ||\n    filteredVoices[0];\n\n  studentVoice =\n    filteredVoices.find(v => v !== interviewerVoice && \/female\/i.test(v.name)) ||\n    filteredVoices.find(v => v !== interviewerVoice) ||\n    interviewerVoice;\n}\n\nfunction populateVoiceList() {\n  const voiceSelect = document.getElementById(\"voice-select\");\n  voiceSelect.innerHTML = \"\";\n\n  if (!speechSynthesisSupported) {\n    const option = document.createElement(\"option\");\n    option.value = \"\";\n    option.textContent = \"No voices available\";\n    voiceSelect.appendChild(option);\n    voiceSelect.disabled = true;\n    return;\n  }\n\n  const filteredVoices = voices.filter((voice) =>\n    desiredVoiceNames.includes(voice.name) || voice.lang.startsWith(\"en-\")\n  );\n\n  if (filteredVoices.length === 0) {\n    const option = document.createElement(\"option\");\n    option.value = \"\";\n    option.textContent = \"Default browser voice\";\n    voiceSelect.appendChild(option);\n    voiceSelect.disabled = true;\n    return;\n  }\n\n  filteredVoices.forEach((voice, index) => {\n    const option = document.createElement(\"option\");\n    option.value = index;\n    option.textContent = `${voice.name} (${voice.lang})`;\n    voiceSelect.appendChild(option);\n  });\n\n  voiceSelect.selectedIndex = 0;\n  selectedVoice = filteredVoices[0];\n\n  chooseDialogueVoices(filteredVoices);\n\n  voiceSelect.onchange = () => {\n    const selectedIndex = voiceSelect.value;\n    selectedVoice = filteredVoices[selectedIndex];\n  };\n}\n\n\/* ========== BUILD UI ========== *\/\n\nfunction buildTasks() {\n  const chat = document.getElementById(\"chat-messages\");\n  chat.innerHTML = \"\";\n\n  \/\/ PART 1\n  const p1Title = document.createElement(\"h3\");\n  p1Title.textContent = \"PART 1 \u2013 Answer the questions.\";\n  p1Title.style.margin = \"4px 0\";\n  p1Title.style.fontSize = \"16px\";\n  chat.appendChild(p1Title);\n\n  part1Questions.forEach(q => {\n    const wrapper = document.createElement(\"div\");\n    wrapper.style.marginBottom = \"6px\";\n    wrapper.id = `p1-q${q.id}`;\n\n    const label = document.createElement(\"label\");\n    label.textContent = q.text;\n    label.style.display = \"block\";\n    label.style.marginBottom = \"2px\";\n\n    const input = document.createElement(\"input\");\n    input.type = \"text\";\n    input.className = \"short-answer-input\";\n    input.id = `p1-input-${q.id}`;\n\n    wrapper.appendChild(label);\n    wrapper.appendChild(input);\n    chat.appendChild(wrapper);\n  });\n\n  \/\/ PART 2 \u2013 schedule table\n  const p2Title = document.createElement(\"h3\");\n  p2Title.textContent = \"PART 2 \u2013 Complete Ayden\u2019s appointment book.\";\n  p2Title.style.marginTop = \"10px\";\n  p2Title.style.fontSize = \"16px\";\n  chat.appendChild(p2Title);\n\n  const table = document.createElement(\"table\");\n  table.className = \"schedule-table\";\n\n  const headerRow = document.createElement(\"tr\");\n  [\"Day\", \"Note\"].forEach(h => {\n    const th = document.createElement(\"th\");\n    th.textContent = h;\n    headerRow.appendChild(th);\n  });\n  table.appendChild(headerRow);\n\n  scheduleItems.forEach(item => {\n    const tr = document.createElement(\"tr\");\n    tr.id = `p2-row-${item.id}`;\n\n    const tdDay = document.createElement(\"td\");\n    tdDay.textContent = item.label.split(\"\u2013\")[0].trim(); \/\/ MON, TUE, etc.\n\n    const tdNote = document.createElement(\"td\");\n    const label = document.createElement(\"div\");\n    label.textContent = item.label.split(\"\u2013\")[1].trim();\n\n    const input = document.createElement(\"input\");\n    input.type = \"text\";\n    input.className = \"schedule-input\";\n    input.id = `p2-input-${item.id}`;\n\n    tdNote.appendChild(label);\n    tdNote.appendChild(input);\n\n    tr.appendChild(tdDay);\n    tr.appendChild(tdNote);\n    table.appendChild(tr);\n  });\n\n  chat.appendChild(table);\n}\n\n\/* ========== CHECK ANSWERS ========== *\/\n\nfunction matchesKeywordSets(answer, keywordSets) {\n  const text = answer.toLowerCase();\n  if (!text) return false;\n\n  return keywordSets.some(set => {\n    return set.every(kw => text.includes(kw.toLowerCase()));\n  });\n}\n\nfunction checkAnswers() {\n  let score = 0;\n  const totalPart1 = part1Questions.length;\n  const totalPart2 = scheduleItems.length;\n  let unanswered = 0;\n\n  \/\/ Reset styles\n  part1Questions.forEach(q => {\n    const wrapper = document.getElementById(`p1-q${q.id}`);\n    if (wrapper) {\n      wrapper.classList.remove(\"correct\", \"incorrect\", \"unanswered\");\n    }\n  });\n  scheduleItems.forEach(item => {\n    const row = document.getElementById(`p2-row-${item.id}`);\n    if (row) {\n      row.classList.remove(\"correct\", \"incorrect\", \"unanswered\");\n    }\n  });\n\n  \/\/ Part 1\n  part1Questions.forEach(q => {\n    const input = document.getElementById(`p1-input-${q.id}`);\n    const wrapper = document.getElementById(`p1-q${q.id}`);\n    const value = input.value.trim();\n\n    if (!value) {\n      unanswered++;\n      wrapper.classList.add(\"unanswered\");\n      return;\n    }\n\n    if (matchesKeywordSets(value, q.keywordSets)) {\n      score++;\n      wrapper.classList.add(\"correct\");\n    } else {\n      wrapper.classList.add(\"incorrect\");\n    }\n  });\n\n  \/\/ Part 2\n  scheduleItems.forEach(item => {\n    const input = document.getElementById(`p2-input-${item.id}`);\n    const row = document.getElementById(`p2-row-${item.id}`);\n    const value = input.value.trim().toLowerCase();\n\n    if (!value) {\n      unanswered++;\n      row.classList.add(\"unanswered\");\n      return;\n    }\n\n    const correct = item.answers.some(ans => value === ans.toLowerCase());\n    if (correct) {\n      score++;\n      row.classList.add(\"correct\");\n    } else {\n      row.classList.add(\"incorrect\");\n    }\n  });\n\n  const totalItems = totalPart1 + totalPart2;\n\n  const resultMessage =\n    `You got ${score} out of ${totalItems} items correct ` +\n    `(Part 1: ${score <= totalPart1 ? score : totalPart1}\/${totalPart1}, ` +\n    `Part 2: ${Math.max(score - totalPart1, 0)}\/${totalPart2}).` +\n    (unanswered > 0 ? ` (${unanswered} item(s) not answered.)` : \"\");\n\n  appendMessage(resultMessage, \"bot\");\n\n  let extra;\n  if (score === totalItems && unanswered === 0) {\n    extra = \"Perfect score! \" + positiveMessages[Math.floor(Math.random() * positiveMessages.length)];\n  } else if (score >= Math.round(totalItems * 0.7)) {\n    extra = positiveMessages[Math.floor(Math.random() * positiveMessages.length)];\n  } else {\n    extra = summaryMessages[Math.floor(Math.random() * summaryMessages.length)];\n  }\n\n  appendMessage(extra, \"bot\");\n\n  if (speechSynthesisSupported) {\n    showSynthesisLoader(true);\n    sayText(resultMessage + \" \" + extra, \"en-US\").then(() => {\n      showSynthesisLoader(false);\n    });\n  }\n}\n\n\/* ========== SCRIPT PLAYER WITH TWO VOICES (I & A) ========== *\/\n\nfunction playScript() {\n  if (!speechSynthesisSupported) {\n    appendMessage(\"Your browser cannot play the script with robot voices.\", \"bot\");\n    return;\n  }\n  if (!interviewerVoice || !studentVoice) {\n    appendMessage(\"Voices are still loading. Please wait a moment and try again.\", \"bot\");\n    loadVoices();\n    return;\n  }\n\n  stopSpeaking();\n  showSynthesisLoader(true);\n\n  const lines = listeningScript.split(\"\\n\").map(l => l.trim()).filter(l => l.length > 0);\n\n  function speakLine(index) {\n    if (index >= lines.length) {\n      showSynthesisLoader(false);\n      return;\n    }\n\n    let line = lines[index];\n    let voiceForLine = selectedVoice || voices[0];\n    let text = line;\n\n    if (line.startsWith(\"I:\")) {\n      text = line.replace(\/^I:\\s*\/, \"\");\n      voiceForLine = interviewerVoice || voiceForLine;\n    } else if (line.startsWith(\"A:\")) {\n      text = line.replace(\/^A:\\s*\/, \"\");\n      voiceForLine = studentVoice || voiceForLine;\n    }\n\n    const utterance = new SpeechSynthesisUtterance(text);\n    utterance.lang = \"en-US\";\n    utterance.rate = 1.0;\n    utterance.pitch = 1.0;\n    utterance.voice = voiceForLine;\n\n    utterance.onend = function () {\n      speakLine(index + 1);\n    };\n    utterance.onerror = function () {\n      speakLine(index + 1);\n    };\n\n    window.speechSynthesis.speak(utterance);\n  }\n\n  speakLine(0);\n}\n\n\/* ========== GENERIC SPEECH HELPERS (feedback) ========== *\/\n\nfunction stopSpeaking() {\n  if (speechSynthesisSupported) {\n    window.speechSynthesis.cancel();\n    showSynthesisLoader(false);\n  }\n}\n\nfunction sayText(text, lang) {\n  return new Promise((resolve) => {\n    if (!speechSynthesisSupported) {\n      resolve();\n      return;\n    }\n    const utterance = new SpeechSynthesisUtterance(text);\n    utterance.lang = lang;\n    utterance.rate = 1.0;\n    utterance.pitch = 1.0;\n\n    if (selectedVoice) {\n      utterance.voice = selectedVoice;\n    }\n\n    utterance.onend = function () {\n      resolve();\n    };\n    utterance.onerror = function () {\n      resolve();\n    };\n\n    window.speechSynthesis.speak(utterance);\n  });\n}\n\n\/* ========== UI HELPERS ========== *\/\n\nfunction appendMessage(text, sender) {\n  const messageContainer = document.getElementById(\"chat-messages\");\n  const messageElement = document.createElement(\"div\");\n  messageElement.classList.add(\"message\");\n  messageElement.classList.add(\"bot-message\");\n  messageElement.innerText = text;\n  messageContainer.appendChild(messageElement);\n  messageContainer.scrollTop = messageContainer.scrollHeight;\n}\n\nfunction showSynthesisLoader(show) {\n  const loader = document.getElementById(\"synthesis-loader\");\n  loader.style.display = show ? \"inline-block\" : \"none\";\n  if (show) {\n    loader.style.borderTop = \"4px solid #17a2b8\";\n  }\n}\n\nwindow.onload = initialize;\n<\/script>\n<\/body>\n<\/html>\n\n","protected":false},"excerpt":{"rendered":"<p>This Robot is used for practicing English at level A2. In order to interact with this Robot smoothly, please use<\/p>\n","protected":false},"author":1,"featured_media":192,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"colormag_page_layout":"default_layout","footnotes":""},"categories":[18,5],"tags":[],"class_list":["post-368","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-a2","category-chatbot"],"_links":{"self":[{"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/posts\/368","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/comments?post=368"}],"version-history":[{"count":1,"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/posts\/368\/revisions"}],"predecessor-version":[{"id":369,"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/posts\/368\/revisions\/369"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/media\/192"}],"wp:attachment":[{"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/media?parent=368"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/categories?post=368"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/tags?post=368"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}