富學寶典答題助手

AI-powered exam assistant for 富学宝典 - automatically analyzes questions and suggests answers

Size

13.7 KB

Version

1.1.1

Created

Nov 5, 2025

Updated

about 1 month ago

1// ==UserScript==
2// @name		富學寶典答題助手
3// @description		AI-powered exam assistant for 富学宝典 - automatically analyzes questions and suggests answers
4// @version		1.1.1
5// @match		https://*.iedu.foxconn.com/*
6// @icon		https://iedu.foxconn.com/favicon.ico
7// @grant		GM.getValue
8// @grant		GM.setValue
9// @grant		GM.xmlhttpRequest
10// @grant		RM.aiCall
11// ==/UserScript==
12(function() {
13    'use strict';
14
15    console.log('富學寶典答題助手已啟動');
16
17    // Debounce function to prevent excessive calls
18    function debounce(func, wait) {
19        let timeout;
20        return function executedFunction(...args) {
21            const later = () => {
22                clearTimeout(timeout);
23                func(...args);
24            };
25            clearTimeout(timeout);
26            timeout = setTimeout(later, wait);
27        };
28    }
29
30    // Extract all questions from the exam page
31    function extractQuestions() {
32        const questions = [];
33        const questionElements = document.querySelectorAll('.question_warp');
34        
35        console.log(`找到 ${questionElements.length} 個問題`);
36        
37        questionElements.forEach((element, index) => {
38            const questionTitle = element.querySelector('h3');
39            const options = element.querySelectorAll('.radio label');
40            const radioInputs = element.querySelectorAll('input[type="radio"]');
41            
42            if (questionTitle && options.length > 0) {
43                const questionText = questionTitle.textContent.trim();
44                const optionsList = [];
45                const radioName = radioInputs[0]?.name || '';
46                
47                options.forEach((option) => {
48                    const input = option.querySelector('input[type="radio"]');
49                    const value = input?.value || '';
50                    const text = option.textContent.trim();
51                    optionsList.push({ value, text });
52                });
53                
54                questions.push({
55                    index: index + 1,
56                    question: questionText,
57                    options: optionsList,
58                    radioName: radioName,
59                    element: element
60                });
61            }
62        });
63        
64        return questions;
65    }
66
67    // Analyze questions using AI
68    async function analyzeQuestionsWithAI(questions) {
69        console.log('開始使用 AI 分析問題...');
70        
71        try {
72            // Prepare questions for AI analysis
73            const questionsText = questions.map(q => {
74                const optionsText = q.options.map(opt => opt.text).join('\n');
75                return `問題 ${q.index}:\n${q.question}\n選項:\n${optionsText}\n`;
76            }).join('\n---\n');
77            
78            const prompt = `你是一個專業的考試助手。請分析以下關於靜電防護(ESD)的考試題目,並為每個問題選擇最正確的答案。
79
80${questionsText}
81
82請以JSON格式返回答案,包含每個問題的編號和建議的答案選項(A, B, C, D等)。同時提供簡短的解釋說明為什麼選擇該答案。`;
83
84            console.log('正在調用 AI API...');
85            
86            const response = await RM.aiCall(prompt, {
87                type: "json_schema",
88                json_schema: {
89                    name: "exam_answers",
90                    schema: {
91                        type: "object",
92                        properties: {
93                            answers: {
94                                type: "array",
95                                items: {
96                                    type: "object",
97                                    properties: {
98                                        questionNumber: { type: "number" },
99                                        selectedOption: { type: "string" },
100                                        explanation: { type: "string" }
101                                    },
102                                    required: ["questionNumber", "selectedOption", "explanation"]
103                                }
104                            }
105                        },
106                        required: ["answers"]
107                    }
108                }
109            });
110            
111            console.log('AI 分析完成:', response);
112            return response;
113            
114        } catch (error) {
115            console.error('AI 分析失敗:', error);
116            throw error;
117        }
118    }
119
120    // Auto-fill answers based on AI suggestions
121    function autoFillAnswers(questions, aiResponse) {
122        console.log('開始自動填寫答案...');
123        
124        if (!aiResponse || !aiResponse.answers) {
125            console.error('AI 回應格式錯誤');
126            return;
127        }
128        
129        aiResponse.answers.forEach(answer => {
130            const question = questions.find(q => q.index === answer.questionNumber);
131            if (question) {
132                const radioInput = document.querySelector(`input[name="${question.radioName}"][value="${answer.selectedOption}"]`);
133                if (radioInput) {
134                    radioInput.checked = true;
135                    console.log(`問題 ${answer.questionNumber}: 選擇 ${answer.selectedOption} - ${answer.explanation}`);
136                }
137            }
138        });
139        
140        console.log('答案填寫完成!');
141    }
142
143    // Show AI analysis results in a modal
144    function showAnalysisResults(aiResponse) {
145        // Remove existing modal if any
146        const existingModal = document.getElementById('ai-analysis-modal');
147        if (existingModal) {
148            existingModal.remove();
149        }
150        
151        // Create modal
152        const modal = document.createElement('div');
153        modal.id = 'ai-analysis-modal';
154        modal.style.cssText = `
155            position: fixed;
156            top: 50%;
157            left: 50%;
158            transform: translate(-50%, -50%);
159            background: white;
160            border: 2px solid #007bff;
161            border-radius: 10px;
162            padding: 20px;
163            max-width: 600px;
164            max-height: 80vh;
165            overflow-y: auto;
166            z-index: 10000;
167            box-shadow: 0 4px 20px rgba(0,0,0,0.3);
168        `;
169        
170        let resultsHTML = '<h2 style="color: #007bff; margin-top: 0;">AI 分析結果</h2>';
171        
172        if (aiResponse && aiResponse.answers) {
173            aiResponse.answers.forEach(answer => {
174                resultsHTML += `
175                    <div style="margin-bottom: 15px; padding: 10px; background: #f8f9fa; border-radius: 5px;">
176                        <strong style="color: #28a745;">問題 ${answer.questionNumber}:</strong> 
177                        <span style="font-size: 18px; font-weight: bold; color: #dc3545;">選項 ${answer.selectedOption}</span>
178                        <p style="margin: 5px 0 0 0; color: #666;">${answer.explanation}</p>
179                    </div>
180                `;
181            });
182        }
183        
184        resultsHTML += `
185            <button id="close-modal-btn" style="
186                background: #dc3545;
187                color: white;
188                border: none;
189                padding: 10px 20px;
190                border-radius: 5px;
191                cursor: pointer;
192                font-size: 16px;
193                margin-top: 10px;
194            ">關閉</button>
195        `;
196        
197        modal.innerHTML = resultsHTML;
198        
199        // Add overlay
200        const overlay = document.createElement('div');
201        overlay.id = 'ai-analysis-overlay';
202        overlay.style.cssText = `
203            position: fixed;
204            top: 0;
205            left: 0;
206            width: 100%;
207            height: 100%;
208            background: rgba(0,0,0,0.5);
209            z-index: 9999;
210        `;
211        
212        document.body.appendChild(overlay);
213        document.body.appendChild(modal);
214        
215        // Close modal handlers
216        document.getElementById('close-modal-btn').addEventListener('click', () => {
217            modal.remove();
218            overlay.remove();
219        });
220        
221        overlay.addEventListener('click', () => {
222            modal.remove();
223            overlay.remove();
224        });
225    }
226
227    // Show loading indicator
228    function showLoadingIndicator() {
229        const loader = document.createElement('div');
230        loader.id = 'ai-loader';
231        loader.innerHTML = `
232            <div style="display: flex; align-items: center; gap: 10px;">
233                <div style="
234                    border: 3px solid #f3f3f3;
235                    border-top: 3px solid #007bff;
236                    border-radius: 50%;
237                    width: 20px;
238                    height: 20px;
239                    animation: spin 1s linear infinite;
240                "></div>
241                <span>AI 正在分析問題...</span>
242            </div>
243        `;
244        loader.style.cssText = `
245            position: fixed;
246            top: 20px;
247            right: 20px;
248            background: #007bff;
249            color: white;
250            padding: 15px 20px;
251            border-radius: 8px;
252            z-index: 10000;
253            box-shadow: 0 4px 12px rgba(0,0,0,0.2);
254            font-size: 14px;
255        `;
256        
257        // Add animation
258        const style = document.createElement('style');
259        style.textContent = `
260            @keyframes spin {
261                0% { transform: rotate(0deg); }
262                100% { transform: rotate(360deg); }
263            }
264        `;
265        document.head.appendChild(style);
266        
267        document.body.appendChild(loader);
268    }
269
270    function hideLoadingIndicator() {
271        const loader = document.getElementById('ai-loader');
272        if (loader) loader.remove();
273    }
274
275    // Main function to handle AI analysis
276    async function handleAIAnalysis() {
277        try {
278            showLoadingIndicator();
279            
280            // Extract questions
281            const questions = extractQuestions();
282            
283            if (questions.length === 0) {
284                alert('未找到考試題目!請確認您在考試頁面上。');
285                hideLoadingIndicator();
286                return;
287            }
288            
289            // Analyze with AI
290            const aiResponse = await analyzeQuestionsWithAI(questions);
291            
292            hideLoadingIndicator();
293            
294            // Show results
295            showAnalysisResults(aiResponse);
296            
297            // Auto-fill answers
298            autoFillAnswers(questions, aiResponse);
299            
300        } catch (error) {
301            hideLoadingIndicator();
302            console.error('處理失敗:', error);
303            alert('AI 分析失敗,請稍後再試。錯誤: ' + error.message);
304        }
305    }
306
307    // Create AI assistant button
308    function createAIButton() {
309        // Check if we're on an exam page
310        const isExamPage = window.location.href.includes('/examUI');
311        
312        if (!isExamPage) {
313            console.log('不在考試頁面,不顯示 AI 按鈕');
314            return;
315        }
316        
317        // Remove existing button if any
318        const existingButton = document.getElementById('ai-assistant-btn');
319        if (existingButton) {
320            existingButton.remove();
321        }
322        
323        const button = document.createElement('button');
324        button.id = 'ai-assistant-btn';
325        button.innerHTML = `
326            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" style="margin-right: 8px; vertical-align: middle;">
327                <path d="M12 2L2 7l10 5 10-5-10-5z"></path>
328                <path d="M2 17l10 5 10-5"></path>
329                <path d="M2 12l10 5 10-5"></path>
330            </svg>
331            AI 智能答題
332        `;
333        button.style.cssText = `
334            position: fixed;
335            bottom: 30px;
336            right: 30px;
337            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
338            color: white;
339            border: none;
340            padding: 15px 25px;
341            border-radius: 50px;
342            cursor: pointer;
343            font-size: 16px;
344            font-weight: bold;
345            z-index: 9999;
346            box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
347            transition: all 0.3s ease;
348            display: flex;
349            align-items: center;
350        `;
351        
352        // Hover effect
353        button.addEventListener('mouseenter', () => {
354            button.style.transform = 'translateY(-2px)';
355            button.style.boxShadow = '0 6px 20px rgba(102, 126, 234, 0.6)';
356        });
357        
358        button.addEventListener('mouseleave', () => {
359            button.style.transform = 'translateY(0)';
360            button.style.boxShadow = '0 4px 15px rgba(102, 126, 234, 0.4)';
361        });
362        
363        button.addEventListener('click', handleAIAnalysis);
364        
365        document.body.appendChild(button);
366        console.log('AI 助手按鈕已創建');
367    }
368
369    // Initialize the extension
370    function init() {
371        console.log('初始化富學寶典答題助手...');
372        
373        // Wait for page to load
374        if (document.readyState === 'loading') {
375            document.addEventListener('DOMContentLoaded', () => {
376                setTimeout(createAIButton, 1000);
377            });
378        } else {
379            setTimeout(createAIButton, 1000);
380        }
381        
382        // Re-create button on navigation (for SPA-like behavior)
383        const debouncedCreateButton = debounce(createAIButton, 500);
384        
385        // Watch for URL changes
386        let lastUrl = location.href;
387        new MutationObserver(() => {
388            const url = location.href;
389            if (url !== lastUrl) {
390                lastUrl = url;
391                console.log('URL 變更,重新檢查頁面...');
392                debouncedCreateButton();
393            }
394        }).observe(document.body, { childList: true, subtree: true });
395    }
396
397    // Start the extension
398    init();
399
400})();
富學寶典答題助手 | Robomonkey