// ==UserScript== // @name K-Bot | Answer Viewer & Auto-Answer Cheat Mod Menu (Working 2026) // @version 2.1 // @namespace juanbolsa // @description A minimalist Kahoot mod menu. Features a live answer viewer and automated responding. // @author juanbolsa // @match https://kahoot.it/* // @icon https://www.google.com/s2/favicons?sz=64&domain=kahoot.it // @license MIT // @grant none // @downloadURL none // ==/UserScript== (function() { 'use strict'; // ─── Config ─────────────────────────────────────────────────────────────────── const VERSION = '2.0'; const MULTI_SELECT_STAGGER_MS = 60; // ─── State ──────────────────────────────────────────────────────────────────── const state = { questions: [], numQuestions: 0, questionNum: -1, lastAnsweredQuestion: -1, baseDelay: 0, randomDelay: 0, autoAnswer: false, showAnswers: false, }; // Keep track of DOM values to avoid redundant updates let lastProcessedQuestionText = ""; // ─── DOM Helpers ────────────────────────────────────────────────────────────── function queryBySelector(value, tag = '*') { return document.querySelector(`${tag}[data-functional-selector="${value}"]`); } function createElement(tag, { styles = {}, className, text } = {}) { const el = document.createElement(tag); if (className) el.className = className; if (text) el.textContent = text; Object.assign(el.style, styles); return el; } // ─── Inject Assets ──────────────────────────────────────────────────────────── const fontLink = document.createElement('link'); fontLink.rel = 'stylesheet'; fontLink.href = 'https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap'; document.head.appendChild(fontLink); const globalStyle = document.createElement('style'); globalStyle.textContent = ` .kb-ui * { box-sizing: border-box; font-family: 'Inter', system-ui, -apple-system, sans-serif; } .kb-ui button, .kb-input, .kb-switch-track::before { transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); } .kb-ui { /* 85% Opacity Backgrounds */ --bg: rgba(15, 15, 15, 0.75); --surface: rgba(24, 24, 24, 0.75); --border: rgba(255, 255, 255, 0.1); --accent: #f0f0f0; --muted: #888; --green: #4caf74; --red: #c0392b; --text: #d8d8d8; --label: #aaa; /* Frosted Glass Effect */ backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); box-shadow: 0 20px 50px rgba(0, 0, 0, 0.5), 0 0 1px rgba(255, 255, 255, 0.1); } .kb-handle { cursor: grab; user-select: none; background: var(--surface); border-bottom: 1px solid var(--border); } .kb-handle:active { cursor: grabbing; } .kb-input { width: 100% !important; background-color: rgba(0, 0, 0, 0.3) !important; border: 1px solid var(--border) !important; color: #ffffff !important; border-radius: 4px; font-size: 13px; font-weight: 600; padding: 5px 8px; outline: none; transition: all 0.2s ease; margin-top: 8px; } .kb-input:focus { border-color: rgba(255, 255, 255, 0.4) !important; } .kb-input.ok { border-color: var(--green) !important; background: rgba(13, 46, 26, 0.6) !important; } .kb-input.err { border-color: var(--red) !important; background: rgba(46, 13, 13, 0.6) !important; } .kb-slider-wrap { display: flex; flex-direction: column; gap: 3px; } .kb-slider-label { display: flex; justify-content: space-between; margin-bottom: 2px; } .kb-slider-label span { font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; } .kb-range { -webkit-appearance: none; width: 100%; height: 4px; background: rgba(255, 255, 255, 0.1); border-radius: 2px; outline: none; } .kb-range::-webkit-slider-thumb { -webkit-appearance: none; width: 14px; height: 14px; background: var(--accent); border-radius: 50%; cursor: pointer; box-shadow: 0 0 10px rgba(0,0,0,0.5); } .kb-toggle-row { display: flex; align-items: center; justify-content: space-between; padding: 6px 0; border-bottom: 1px solid var(--border); } .kb-toggle-label { color: var(--text); font-size: 13px; font-weight: 600; } .kb-switch { position: relative; width: 34px; height: 18px; } .kb-switch input { opacity: 0; width: 0; height: 0; } .kb-switch-track { position: absolute; inset: 0; background: rgba(255, 255, 255, 0.1); border-radius: 20px; cursor: pointer; transition: 0.3s; } .kb-switch-track::before { content: ''; position: absolute; width: 12px; height: 12px; left: 3px; top: 3px; background: #777; border-radius: 50%; transition: 0.25s cubic-bezier(0.4, 0, 0.2, 1); } .kb-switch input:checked + .kb-switch-track { background: rgba(76, 175, 116, 0.3); } .kb-switch input:checked + .kb-switch-track::before { transform: translateX(16px); background: var(--green); box-shadow: 0 0 8px var(--green); } .kb-divider { border: none; border-top: 1px solid var(--border); margin: 8px 0; } .kb-section-title { color: var(--muted); font-size: 12px; font-weight: 800; text-transform: uppercase; letter-spacing: 0.1em; margin-bottom: 3px; text-align: center; } .kb-stat { color: var(--accent); font-size: 18px; font-weight: 700; text-align: center; margin-top: 15px;} .kb-credit { color: #666; font-size: 13px; text-align: center; margin-top: 2px; font-weight: 600; } `; document.head.appendChild(globalStyle); // ─── UI Construction ────────────────────────────────────────────────────────── const uiElement = createElement('div', { className: 'kb-ui', styles: { position: 'fixed', top: '5%', left: '5%', width: '240px', background: 'var(--bg)', border: '1px solid var(--border)', borderRadius: '6px', boxShadow: '0 8px 32px rgba(0,0,0,0.7)', zIndex: '9999' } }); const handle = createElement('div', { className: 'kb-handle', styles: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '6px 8px', background: 'var(--surface)', borderBottom: '1px solid var(--border)' } }); handle.append(createElement('span', { text: 'K-Bot v'+VERSION, styles: { color: 'var(--accent)', fontSize: '13px', fontWeight: '700' } })); const titleControls = createElement('div', { styles: { display: 'flex', gap: '5px' } }); const minimizeButton = createElement('button', { text: '-', styles: { background: '#888', border: 'none', width: '22px', height: '20px', cursor: 'pointer', borderRadius: '3px', color: '#fff', fontWeight: '800',} }); const closeButton = createElement('button', { text: '✕', styles: { background: '#c0392b', color: '#fff', border: 'none', width: '22px', height: '20px', cursor: 'pointer', borderRadius: '3px', fontWeight: '800',} }); titleControls.append(minimizeButton, closeButton); handle.append(titleControls); uiElement.appendChild(handle); const body = createElement('div', { styles: { padding: '15px', display: 'flex', flexDirection: 'column', gap: '6px' } }); uiElement.appendChild(body); // Quiz ID Input const quizSection = document.createElement('div'); quizSection.append(createElement('div', { className: 'kb-section-title', text: 'Quiz ID' })); const inputBox = createElement('input', { className: 'kb-input' }); inputBox.placeholder = 'Enter Quiz ID (NOT PIN)'; quizSection.appendChild(inputBox); body.appendChild(quizSection); body.appendChild(createElement('hr', { className: 'kb-divider' })); // Sliders function makeSlider(labelText, { min, max, step, value }, onInput) { const wrap = createElement('div', { className: 'kb-slider-wrap' }); const labelRow = createElement('div', { className: 'kb-slider-label' }); const valSpan = createElement('span', { text: `${value} ms`, styles: { color: 'var(--accent)' } }); labelRow.append(createElement('span', { text: labelText, styles: { color: 'var(--label)' } }), valSpan); const input = createElement('input', { className: 'kb-range' }); input.type = 'range'; Object.assign(input, { min, max, step, value }); input.oninput = () => { valSpan.textContent = `${input.value} ms`; onInput(+input.value); }; wrap.append(labelRow, input); return wrap; } body.appendChild(createElement('div', { className: 'kb-section-title', text: 'Delay' })); body.appendChild(makeSlider('Base', { min: 0, max: 10000, step: 100, value: state.baseDelay }, v => state.baseDelay = v)); body.appendChild(makeSlider('Random ±', { min: 0, max: 5000, step: 100, value: state.randomDelay }, v => state.randomDelay = v)); body.appendChild(createElement('hr', { className: 'kb-divider' })); // Toggles function makeToggle(label, key) { const row = createElement('div', { className: 'kb-toggle-row' }); row.append(createElement('span', { className: 'kb-toggle-label', text: label })); const sw = createElement('label', { className: 'kb-switch' }); const input = document.createElement('input'); input.type = 'checkbox'; input.onchange = () => state[key] = input.checked; sw.append(input, createElement('span', { className: 'kb-switch-track' })); row.append(sw); return row; } body.appendChild(createElement('div', { className: 'kb-section-title', text: 'Answering' })); body.appendChild(makeToggle('Auto Answer', 'autoAnswer')); body.appendChild(makeToggle('Show Answers', 'showAnswers')); const questionsLabel = createElement('div', { className: 'kb-stat', text: 'Question 0 / 0' }); body.appendChild(questionsLabel); body.appendChild(createElement('div', { className: 'kb-credit', text: `v${VERSION} · juanbolsa` })); document.body.appendChild(uiElement); // ─── Logic ──────────────────────────────────────────────────────────────────── function triggerReactClick(btn) { const fiberKey = Object.keys(btn).find((k) => k.startsWith('__reactFiber')); if (!fiberKey) return; let fiber = btn[fiberKey]; while (fiber) { const onClick = fiber.memoizedProps?.onClick; if (onClick) { const nativeEvent = new MouseEvent('click', { bubbles: true, cancelable: true }); const trusted = new Proxy(nativeEvent, { get(target, prop) { if (prop === 'isTrusted') return true; const val = target[prop]; return typeof val === 'function' ? val.bind(target) : val; }, }); onClick(trusted); return; } fiber = fiber.return; } } function answer(question, delay) { const performClick = (idx) => { const btn = queryBySelector(`answer-${idx}`, 'button'); if (btn) triggerReactClick(btn); }; if (question.type === 'quiz') { setTimeout(() => performClick(question.answers[0]), delay); } else if (question.type === 'multiple_select_quiz') { question.answers.forEach((ans, i) => { setTimeout(() => performClick(ans), delay + (i * MULTI_SELECT_STAGGER_MS)); }); setTimeout(() => { const submit = queryBySelector('multi-select-submit-button', 'button'); if (submit) triggerReactClick(submit); }, delay + (question.answers.length * MULTI_SELECT_STAGGER_MS)); } } function highlightAnswers(question) { question.answers.forEach(i => { const btn = queryBySelector(`answer-${i}`, 'button'); if (btn) btn.style.backgroundColor = 'rgb(0,255,0)'; }); question.incorrectAnswers?.forEach(i => { const btn = queryBySelector(`answer-${i}`, 'button'); if (btn) btn.style.backgroundColor = 'rgb(255,0,0)'; }); } function parseQuestions(raw) { return raw.map(q => { const p = { type: q.type, time: q.time, answers: [] }; if (q.choices) { p.incorrectAnswers = []; q.choices.forEach((c, i) => (c.correct ? p.answers : p.incorrectAnswers).push(i)); } return p; }); } // ─── The Main Loop (Optimized) ──────────────────────────────────────────────── function pollFrame() { const counterEl = queryBySelector('question-index-counter', 'div'); const currentText = counterEl ? counterEl.textContent : ""; // DIRTY CHECK: Only run logic if the question number text actually changed if (currentText !== lastProcessedQuestionText) { lastProcessedQuestionText = currentText; if (currentText) { state.questionNum = parseInt(currentText) - 1; questionsLabel.textContent = `Question ${state.questionNum + 1} / ${state.numQuestions}`; } } // Check if buttons are ready and we haven't answered this specific question index yet const firstBtn = queryBySelector('answer-0', 'button'); if (firstBtn && state.lastAnsweredQuestion !== state.questionNum) { state.lastAnsweredQuestion = state.questionNum; const qData = state.questions[state.questionNum]; if (qData) { if (state.showAnswers) highlightAnswers(qData); if (state.autoAnswer) answer(qData, state.baseDelay + Math.floor(Math.random() * state.randomDelay)); } } requestAnimationFrame(pollFrame); } // ─── Event Handlers ────────────────────────────────────────────────────────── inputBox.oninput = async () => { const id = inputBox.value.trim(); inputBox.className = 'kb-input'; if (!id) return; try { const res = await fetch(`https://kahoot.it/rest/kahoots/${id}`); if (!res.ok) throw 1; const data = await res.json(); state.questions = parseQuestions(data.questions); state.numQuestions = state.questions.length; inputBox.classList.add('ok'); questionsLabel.textContent = `Question 1 / ${state.numQuestions}`; } catch(e) { inputBox.classList.add('err'); } }; minimizeButton.onclick = () => { const isMin = body.style.display === 'none'; body.style.display = isMin ? 'flex' : 'none'; minimizeButton.textContent = isMin ? '-' : '+'; }; closeButton.onclick = () => uiElement.remove(); // Draggable Logic let dragging = false, offset = [0,0]; handle.addEventListener('mousedown', (e) => { if (e.target.tagName === 'BUTTON') return; dragging = true; const rect = uiElement.getBoundingClientRect(); offset = [e.clientX - rect.left, e.clientY - rect.top]; }); document.addEventListener('mousemove', (e) => { if (!dragging) return; uiElement.style.left = `${e.clientX - offset[0]}px`; uiElement.style.top = `${e.clientY - offset[1]}px`; }); document.addEventListener('mouseup', () => dragging = false); // Start requestAnimationFrame(pollFrame); })();