Size
7.2 KB
Version
1.0.1
Created
Dec 1, 2025
Updated
12 days ago
1// ==UserScript==
2// @name Learnbeat Auto Answer
3// @description Automatically answers Learnbeat questions using AI
4// @version 1.0.1
5// @match https://*.inloggen.learnbeat.nl/*
6// @icon https://static.learnbeat.nl/app_versions/learnbeat/favicon.ico
7// ==/UserScript==
8(function() {
9 'use strict';
10
11 console.log('Learnbeat Auto Answer extension loaded');
12
13 // Debounce function to prevent multiple rapid calls
14 function debounce(func, wait) {
15 let timeout;
16 return function executedFunction(...args) {
17 const later = () => {
18 clearTimeout(timeout);
19 func(...args);
20 };
21 clearTimeout(timeout);
22 timeout = setTimeout(later, wait);
23 };
24 }
25
26 // Function to extract question text and verb from a question element
27 function extractQuestionInfo(questionElement) {
28 const fullText = questionElement.textContent.trim();
29
30 // Extract the verb in parentheses
31 const verbMatch = fullText.match(/\(([^)]+)\)/);
32 const verb = verbMatch ? verbMatch[1] : null;
33
34 // Get the full sentence context
35 const sentence = fullText;
36
37 return { sentence, verb };
38 }
39
40 // Function to answer a single question using AI
41 async function answerQuestion(inputElement, questionElement) {
42 try {
43 const { sentence, verb } = extractQuestionInfo(questionElement);
44
45 if (!verb) {
46 console.error('Could not extract verb from question');
47 return;
48 }
49
50 console.log('Processing question:', sentence);
51 console.log('Verb to conjugate:', verb);
52
53 // Use AI to get the correct conjugation
54 const prompt = `You are a Dutch language expert. Given the following Dutch sentence with a verb in parentheses, provide ONLY the correctly conjugated form of the verb that should replace the parentheses. Do not include any explanation, just the conjugated verb.
55
56Sentence: ${sentence}
57Verb to conjugate: ${verb}
58
59Provide only the conjugated verb form:`;
60
61 const answer = await RM.aiCall(prompt);
62 const cleanAnswer = answer.trim();
63
64 console.log('AI answer:', cleanAnswer);
65
66 // Fill in the answer
67 inputElement.value = cleanAnswer;
68
69 // Trigger input event to ensure the page registers the change
70 inputElement.dispatchEvent(new Event('input', { bubbles: true }));
71 inputElement.dispatchEvent(new Event('change', { bubbles: true }));
72
73 return cleanAnswer;
74 } catch (error) {
75 console.error('Error answering question:', error);
76 return null;
77 }
78 }
79
80 // Function to find and answer all questions on the page
81 async function answerAllQuestions() {
82 console.log('Starting to answer all questions...');
83
84 // Find all question containers
85 const questionContainers = document.querySelectorAll('.js-content');
86
87 if (questionContainers.length === 0) {
88 console.log('No questions found on this page');
89 alert('No questions found on this page');
90 return;
91 }
92
93 console.log(`Found ${questionContainers.length} questions`);
94
95 let answeredCount = 0;
96
97 for (const container of questionContainers) {
98 // Find the input field within this question
99 const inputField = container.querySelector('input[type="text"]');
100
101 if (inputField && !inputField.value) {
102 const answer = await answerQuestion(inputField, container);
103 if (answer) {
104 answeredCount++;
105 console.log(`Answered question ${answeredCount}/${questionContainers.length}`);
106 }
107
108 // Small delay between questions to avoid overwhelming the AI API
109 await new Promise(resolve => setTimeout(resolve, 500));
110 }
111 }
112
113 console.log(`Finished! Answered ${answeredCount} questions`);
114 alert(`Successfully answered ${answeredCount} questions!`);
115 }
116
117 // Function to create and add the auto-answer button
118 function createAutoAnswerButton() {
119 // Check if button already exists
120 if (document.getElementById('learnbeat-auto-answer-btn')) {
121 return;
122 }
123
124 // Find the header buttons container
125 const headerButtons = document.querySelector('.js-page-buttons');
126
127 if (!headerButtons) {
128 console.log('Header buttons container not found, will retry...');
129 return;
130 }
131
132 // Create the auto-answer button matching Learnbeat's style
133 const button = document.createElement('button');
134 button.id = 'learnbeat-auto-answer-btn';
135 button.className = 'js-hero-button HeroButton-jU7f__hero-button--has-callback js-btn';
136 button.innerHTML = `
137 <div class="js-icon"></div>
138 <div class="js-label">
139 <span class="js-first-line">Auto Answer</span>
140 </div>
141 `;
142
143 button.style.cssText = `
144 background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
145 color: white;
146 border: none;
147 cursor: pointer;
148 transition: all 0.3s ease;
149 `;
150
151 button.addEventListener('mouseenter', () => {
152 button.style.transform = 'scale(1.05)';
153 button.style.boxShadow = '0 4px 12px rgba(102, 126, 234, 0.4)';
154 });
155
156 button.addEventListener('mouseleave', () => {
157 button.style.transform = 'scale(1)';
158 button.style.boxShadow = 'none';
159 });
160
161 button.addEventListener('click', async () => {
162 button.disabled = true;
163 button.style.opacity = '0.6';
164 button.querySelector('.js-first-line').textContent = 'Processing...';
165
166 try {
167 await answerAllQuestions();
168 } finally {
169 button.disabled = false;
170 button.style.opacity = '1';
171 button.querySelector('.js-first-line').textContent = 'Auto Answer';
172 }
173 });
174
175 // Add button to the header
176 headerButtons.appendChild(button);
177 console.log('Auto Answer button added to page');
178 }
179
180 // Initialize the extension
181 function init() {
182 console.log('Initializing Learnbeat Auto Answer extension...');
183
184 // Wait for the page to be fully loaded
185 if (document.readyState === 'loading') {
186 document.addEventListener('DOMContentLoaded', init);
187 return;
188 }
189
190 // Try to create the button immediately
191 createAutoAnswerButton();
192
193 // Also observe for dynamic content changes
194 const observer = new MutationObserver(debounce(() => {
195 createAutoAnswerButton();
196 }, 1000));
197
198 observer.observe(document.body, {
199 childList: true,
200 subtree: true
201 });
202
203 console.log('Extension initialized successfully');
204 }
205
206 // Start the extension
207 init();
208})();