i-Ready Auto Answer

Automatically answers i-Ready questions using AI

Size

12.0 KB

Version

1.0.1

Created

Mar 18, 2026

Updated

3 days ago

1// ==UserScript==
2// @name		i-Ready Auto Answer
3// @description		Automatically answers i-Ready questions using AI
4// @version		1.0.1
5// @match		https://*.login.i-ready.com/*
6// @icon		https://login.i-ready.com/favicon.ico
7// ==/UserScript==
8(function() {
9    'use strict';
10
11    console.log('i-Ready Auto Answer extension loaded');
12
13    // Debounce function to prevent excessive 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    // Get the iframe element
27    function getIframe() {
28        return document.querySelector('#html5Iframe');
29    }
30
31    // Get the iframe document
32    function getIframeDoc() {
33        const iframe = getIframe();
34        if (iframe && iframe.contentDocument) {
35            return iframe.contentDocument;
36        }
37        return null;
38    }
39
40    // Extract question text from the page
41    function extractQuestion() {
42        const iframeDoc = getIframeDoc();
43        if (!iframeDoc) return null;
44
45        // Look for question text in various possible locations
46        const questionSelectors = [
47            '[data-testid*="question"]',
48            '[class*="question"]',
49            '[class*="prompt"]',
50            '[role="heading"]',
51            '.instruction-text',
52            '.question-text'
53        ];
54
55        for (const selector of questionSelectors) {
56            const element = iframeDoc.querySelector(selector);
57            if (element && element.textContent.trim()) {
58                console.log('Found question element:', element);
59                return element.textContent.trim();
60            }
61        }
62
63        // Fallback: get all visible text
64        const body = iframeDoc.body;
65        if (body) {
66            const allText = body.textContent.trim();
67            console.log('Extracted text from body:', allText.substring(0, 200));
68            return allText;
69        }
70
71        return null;
72    }
73
74    // Find answer options
75    function findAnswerOptions() {
76        const iframeDoc = getIframeDoc();
77        if (!iframeDoc) return [];
78
79        const options = [];
80        
81        // Look for clickable answer elements
82        const answerSelectors = [
83            'button[data-testid*="answer"]',
84            'button[class*="answer"]',
85            'div[role="button"][class*="answer"]',
86            'div[class*="choice"]',
87            'button[class*="choice"]',
88            '[data-testid*="choice"]'
89        ];
90
91        for (const selector of answerSelectors) {
92            const elements = iframeDoc.querySelectorAll(selector);
93            if (elements.length > 0) {
94                console.log(`Found ${elements.length} answer options with selector: ${selector}`);
95                elements.forEach((el, index) => {
96                    options.push({
97                        element: el,
98                        text: el.textContent.trim(),
99                        index: index
100                    });
101                });
102                break;
103            }
104        }
105
106        return options;
107    }
108
109    // Find input fields for text answers
110    function findInputFields() {
111        const iframeDoc = getIframeDoc();
112        if (!iframeDoc) return [];
113
114        const inputs = iframeDoc.querySelectorAll('input[type="text"], input[type="number"], textarea');
115        console.log(`Found ${inputs.length} input fields`);
116        return Array.from(inputs);
117    }
118
119    // Use AI to answer the question
120    async function getAIAnswer(question, options) {
121        try {
122            console.log('Asking AI to answer question:', question);
123            
124            if (options && options.length > 0) {
125                // Multiple choice question
126                const optionsText = options.map((opt, idx) => `${idx + 1}. ${opt.text}`).join('\n');
127                
128                const response = await RM.aiCall(
129                    `You are helping a student with an i-Ready question. Answer this question by selecting the correct option number (1, 2, 3, or 4).\n\nQuestion: ${question}\n\nOptions:\n${optionsText}\n\nRespond with ONLY the number of the correct answer.`,
130                    {
131                        type: "json_schema",
132                        json_schema: {
133                            name: "answer_selection",
134                            schema: {
135                                type: "object",
136                                properties: {
137                                    answerNumber: { 
138                                        type: "number",
139                                        description: "The number of the correct answer option (1-4)"
140                                    },
141                                    reasoning: {
142                                        type: "string",
143                                        description: "Brief explanation of why this is correct"
144                                    }
145                                },
146                                required: ["answerNumber"]
147                            }
148                        }
149                    }
150                );
151                
152                console.log('AI response:', response);
153                return {
154                    type: 'multiple_choice',
155                    answerIndex: response.answerNumber - 1,
156                    reasoning: response.reasoning
157                };
158            } else {
159                // Text input question
160                const answer = await RM.aiCall(
161                    `You are helping a student with an i-Ready question. Provide a concise, correct answer to this question.\n\nQuestion: ${question}\n\nProvide ONLY the answer, no explanation.`
162                );
163                
164                console.log('AI answer:', answer);
165                return {
166                    type: 'text_input',
167                    answer: answer.trim()
168                };
169            }
170        } catch (error) {
171            console.error('Error getting AI answer:', error);
172            return null;
173        }
174    }
175
176    // Click the answer button
177    function clickAnswer(option) {
178        try {
179            console.log('Clicking answer:', option.text);
180            option.element.click();
181            return true;
182        } catch (error) {
183            console.error('Error clicking answer:', error);
184            return false;
185        }
186    }
187
188    // Fill in text input
189    function fillTextInput(input, answer) {
190        try {
191            console.log('Filling input with:', answer);
192            input.value = answer;
193            input.dispatchEvent(new Event('input', { bubbles: true }));
194            input.dispatchEvent(new Event('change', { bubbles: true }));
195            return true;
196        } catch (error) {
197            console.error('Error filling input:', error);
198            return false;
199        }
200    }
201
202    // Find and click submit/next button
203    function clickSubmitButton() {
204        const iframeDoc = getIframeDoc();
205        if (!iframeDoc) return false;
206
207        const submitSelectors = [
208            'button[data-testid*="submit"]',
209            'button[data-testid*="next"]',
210            'button[class*="submit"]',
211            'button[class*="next"]',
212            'button[class*="continue"]',
213            'button:contains("Submit")',
214            'button:contains("Next")',
215            'button:contains("Continue")'
216        ];
217
218        for (const selector of submitSelectors) {
219            const button = iframeDoc.querySelector(selector);
220            if (button && !button.disabled) {
221                console.log('Clicking submit button');
222                setTimeout(() => button.click(), 1000);
223                return true;
224            }
225        }
226
227        return false;
228    }
229
230    // Main auto-answer function
231    async function autoAnswer() {
232        console.log('Starting auto-answer process...');
233
234        // Wait a bit for the page to load
235        await new Promise(resolve => setTimeout(resolve, 2000));
236
237        const question = extractQuestion();
238        if (!question) {
239            console.log('No question found');
240            return;
241        }
242
243        console.log('Question found:', question);
244
245        // Check for multiple choice options
246        const options = findAnswerOptions();
247        
248        // Check for text inputs
249        const inputs = findInputFields();
250
251        if (options.length === 0 && inputs.length === 0) {
252            console.log('No answer options or input fields found');
253            return;
254        }
255
256        // Get AI answer
257        const aiResponse = await getAIAnswer(question, options.length > 0 ? options : null);
258        
259        if (!aiResponse) {
260            console.log('Failed to get AI answer');
261            return;
262        }
263
264        // Apply the answer
265        if (aiResponse.type === 'multiple_choice' && options.length > 0) {
266            const selectedOption = options[aiResponse.answerIndex];
267            if (selectedOption) {
268                console.log('AI selected option:', selectedOption.text);
269                if (aiResponse.reasoning) {
270                    console.log('Reasoning:', aiResponse.reasoning);
271                }
272                clickAnswer(selectedOption);
273                
274                // Try to click submit button after a delay
275                setTimeout(() => clickSubmitButton(), 1500);
276            }
277        } else if (aiResponse.type === 'text_input' && inputs.length > 0) {
278            inputs.forEach(input => fillTextInput(input, aiResponse.answer));
279            
280            // Try to click submit button after a delay
281            setTimeout(() => clickSubmitButton(), 1500);
282        }
283    }
284
285    // Create a control button
286    function createControlButton() {
287        const button = document.createElement('button');
288        button.textContent = '🤖 Auto Answer';
289        button.style.cssText = `
290            position: fixed;
291            top: 20px;
292            right: 20px;
293            z-index: 999999;
294            padding: 12px 20px;
295            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
296            color: white;
297            border: none;
298            border-radius: 8px;
299            font-size: 16px;
300            font-weight: bold;
301            cursor: pointer;
302            box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
303            transition: all 0.3s ease;
304        `;
305        
306        button.addEventListener('mouseenter', () => {
307            button.style.transform = 'scale(1.05)';
308            button.style.boxShadow = '0 6px 20px rgba(0, 0, 0, 0.3)';
309        });
310        
311        button.addEventListener('mouseleave', () => {
312            button.style.transform = 'scale(1)';
313            button.style.boxShadow = '0 4px 15px rgba(0, 0, 0, 0.2)';
314        });
315        
316        button.addEventListener('click', async () => {
317            button.textContent = '⏳ Processing...';
318            button.disabled = true;
319            
320            await autoAnswer();
321            
322            setTimeout(() => {
323                button.textContent = '🤖 Auto Answer';
324                button.disabled = false;
325            }, 3000);
326        });
327        
328        document.body.appendChild(button);
329        console.log('Control button created');
330    }
331
332    // Initialize the extension
333    function init() {
334        console.log('Initializing i-Ready Auto Answer extension');
335        
336        // Wait for the page to load
337        if (document.readyState === 'loading') {
338            document.addEventListener('DOMContentLoaded', () => {
339                setTimeout(createControlButton, 1000);
340            });
341        } else {
342            setTimeout(createControlButton, 1000);
343        }
344
345        // Monitor for iframe changes
346        const observer = new MutationObserver(debounce(() => {
347            const iframe = getIframe();
348            if (iframe && iframe.contentDocument) {
349                console.log('Iframe content changed');
350            }
351        }, 1000));
352
353        observer.observe(document.body, {
354            childList: true,
355            subtree: true
356        });
357    }
358
359    // Start the extension
360    init();
361})();