Hack olm

Hack olm by Bảo Ngọc

当前为 2025-11-01 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Hack olm
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  Hack olm by Bảo Ngọc
// @author       Trần Bảo Ngọc
// @match        https://olm.vn/chu-de/*
// @grant        unsafeWindow
// @run-at       document-start
// ==/UserScript==

(function() {
    'use strict';

    const TARGET_URL_KEYWORD = 'get-question-of-ids';

    function decodeBase64Utf8(base64) {
        try {
            const binaryString = atob(base64);
            const bytes = new Uint8Array(binaryString.length);
            for (let i = 0; i < binaryString.length; i++) {
                bytes[i] = binaryString.charCodeAt(i);
            }
            return new TextDecoder('utf-8').decode(bytes);
        } catch (e) {
            console.error("Lỗi giải mã Base64:", e);
            return 'Lỗi giải mã nội dung!';
        }
    }

    class AnswerDisplay {
        constructor() {
            this.isVisible = true;
            this.dragState = { isDragging: false, startX: 0, startY: 0, initialX: 0, initialY: 0 };
            this.onMouseDown = this.onMouseDown.bind(this);
            this.onMouseMove = this.onMouseMove.bind(this);
            this.onMouseUp = this.onMouseUp.bind(this);
            this.onKeyDown = this.onKeyDown.bind(this);
            this.exportToTxt = this.exportToTxt.bind(this);
        }

        init() {
            this.injectCSS();
            this.createUI();
            this.addEventListeners();
        }

        injectCSS() {
            const styles = `
                @keyframes gradient-animation {
                    0% { background-position: 0% 50%; } 50% { background-position: 100% 50%; } 100% { background-position: 0% 50%; }
                }
                #olm-answers-container {
                    position: fixed; top: 10px; right: 10px; width: 450px; max-height: 90vh;
                    border: 1px solid #cccccc; border-radius: 8px; box-shadow: 0 6px 20px rgba(0, 0, 0, 0.2);
                    z-index: 10000; display: flex; flex-direction: column; font-size: 14px; resize: both;
                    overflow: hidden; transition: opacity 0.2s, transform 0.2s; color: #333;
                    background: linear-gradient(45deg, #e0f7fa, #d1c4e9, #fce4ec, #e0f7fa);
                    background-size: 400% 400%; animation: gradient-animation 20s ease infinite;
                }
                #olm-answers-container.hidden { opacity: 0; transform: scale(0.95); pointer-events: none; }
                .olm-answers-header {
                    padding: 10px 15px; background-color: rgba(255, 255, 255, 0.4); border-bottom: 1px solid #cccccc;
                    cursor: move; user-select: none; font-weight: bold; text-align: center;
                }
                #olm-answers-content {
                    padding: 10px; margin: 0; flex-grow: 1; overflow-y: auto; background-color: rgba(255, 255, 255, 0.8);
                }
                #olm-answers-footer {
                    padding: 8px; background-color: rgba(255, 255, 255, 0.4); border-top: 1px solid #cccccc; text-align: center;
                }
                #export-btn {
                    padding: 6px 12px; border: 1px solid #007bff; background-color: #007bff; color: white;
                    border-radius: 5px; cursor: pointer; font-weight: bold; transition: background-color 0.2s;
                }
                #export-btn:hover { background-color: #0056b3; }
                #olm-answers-content::-webkit-scrollbar { width: 8px; }
                #olm-answers-content::-webkit-scrollbar-track { background: #f1f1f1; }
                #olm-answers-content::-webkit-scrollbar-thumb { background: #888; border-radius: 4px; }
                #olm-answers-content::-webkit-scrollbar-thumb:hover { background: #555; }
            `;
            const styleSheet = document.createElement("style");
            styleSheet.innerText = styles;
            document.head.appendChild(styleSheet);
        }

        createUI() {
            this.container = document.createElement('div');
            this.container.id = 'olm-answers-container';
            this.header = document.createElement('div');
            this.header.className = 'olm-answers-header';
            this.header.innerHTML = 'Code by Trần Bảo Ngọc (Nhấn Shift phải để Ẩn/Hiện)';
            this.contentArea = document.createElement('div');
            this.contentArea.id = 'olm-answers-content';
            const footer = document.createElement('div');
            footer.id = 'olm-answers-footer';
            const exportButton = document.createElement('button');
            exportButton.id = 'export-btn';
            exportButton.textContent = 'Xuất ra file TXT';
            footer.appendChild(exportButton);
            this.container.append(this.header, this.contentArea, footer);
            const appendToBody = () => document.body.appendChild(this.container);
            if (document.body) appendToBody();
            else window.addEventListener('DOMContentLoaded', appendToBody);
        }

        addEventListeners() {
            setTimeout(() => {
                this.header.addEventListener('mousedown', this.onMouseDown);
                window.addEventListener('keydown', this.onKeyDown);
                document.getElementById('export-btn').addEventListener('click', this.exportToTxt);
            }, 500);
        }

        exportToTxt() {
            let fullText = `Đáp án OLM by Trần Bảo Ngọc - ${new Date().toLocaleString('vi-VN')}\n`;
            const questionBlocks = this.contentArea.querySelectorAll('.qa-block');
            questionBlocks.forEach((block, index) => {
                const questionElement = block.querySelector('.question-content');
                const contentElement = block.querySelector('.content-container');
                if (questionElement && contentElement) {
                    const type = contentElement.dataset.type || 'answer';
                    let label = '--> Đáp án:';
                    if (type === 'solution') {
                        label = '--> Hướng dẫn giải:';
                    } else if (type === 'not-found') {
                        label = '--> ';
                    }
                    const questionText = questionElement.textContent.trim().replace(/\s\s+/g, ' ');
                    const contentText = contentElement.textContent.trim().replace(/\s\s+/g, ' ');
                    fullText += `Câu hỏi ${index + 1}: ${questionText}\n`;
                    fullText += `${label} ${contentText}\n\n`;
                }
            });
            const blob = new Blob([fullText], { type: 'text/plain;charset=utf-8' });
            const link = document.createElement('a');
            link.href = URL.createObjectURL(blob);
            link.download = `dap-an-olm-${Date.now()}.txt`;
            document.body.appendChild(link);
            link.click();
            document.body.removeChild(link);
        }

        onMouseDown(event) {
            this.dragState.isDragging = true; const rect = this.container.getBoundingClientRect(); this.container.style.right = 'auto'; this.container.style.bottom = 'auto'; this.container.style.left = `${rect.left}px`; this.container.style.top = `${rect.top}px`; this.dragState.initialX = rect.left; this.dragState.initialY = rect.top; this.dragState.startX = event.clientX; this.dragState.startY = event.clientY; window.addEventListener('mousemove', this.onMouseMove); window.addEventListener('mouseup', this.onMouseUp);
        }
        onMouseMove(event) {
            if (!this.dragState.isDragging) return; event.preventDefault(); const dx = event.clientX - this.dragState.startX; const dy = event.clientY - this.dragState.startY; this.container.style.left = `${this.dragState.initialX + dx}px`; this.container.style.top = `${this.dragState.initialY + dy}px`;
        }
        onMouseUp() {
            this.dragState.isDragging = false; window.removeEventListener('mousemove', this.onMouseMove); window.removeEventListener('mouseup', this.onMouseUp);
        }
        onKeyDown(event) { if (event.code === 'ShiftRight') this.toggleVisibility(); }
        toggleVisibility() { this.isVisible = !this.isVisible; this.container.classList.toggle('hidden', !this.isVisible); }

        getAnswersAsDOM(question) {
            const listElement = document.createElement('ul');
            if (question.json_content) {
                try {
                    const jsonData = JSON.parse(question.json_content);
                    const findCorrect = (node) => { if (!node) return null; if (node.correct === true) return node; if (node.children) { for (const child of node.children) { const found = findCorrect(child); if (found) return found; } } return null; };
                    const extractText = (node) => (node ? (node.text || '') + (node.children ? node.children.map(extractText).join('') : '') : '');
                    const correctAnswerNode = findCorrect(jsonData.root);
                    if (correctAnswerNode) { const answerText = extractText(correctAnswerNode).trim(); if (answerText) { const li = document.createElement('li'); li.innerHTML = answerText; listElement.appendChild(li); return listElement; } }
                } catch (e) { console.error("Lỗi phân tích JSON:", e); }
            }
            const tempDiv = document.createElement('div'); tempDiv.innerHTML = decodeBase64Utf8(question.content || '');
            const correctAnswers = tempDiv.querySelectorAll('.correctAnswer');
            if (correctAnswers.length > 0) { correctAnswers.forEach(ans => { const li = document.createElement('li'); while (ans.firstChild) { li.appendChild(ans.firstChild.cloneNode(true)); } listElement.appendChild(li); }); return listElement; }
            const fillInInput = tempDiv.querySelector('input[data-accept]');
            if (fillInInput) { fillInInput.getAttribute('data-accept').split('|').forEach(a => { const li = document.createElement('li'); li.textContent = a.trim(); listElement.appendChild(li); }); return listElement; }
            return null;
        }
        getSolutionAsDOM(decodedContent) {
            const tempDiv = document.createElement('div');
            tempDiv.innerHTML = decodedContent;
            const solutionNode = tempDiv.querySelector('.loigiai, .huong-dan-giai, .explain, .solution, #solution, .guide, .exp, .exp-in');
            return solutionNode ? solutionNode.cloneNode(true) : null;
        }

        renderContentWithOLM(element) {
            setTimeout(() => {
                const renderFunc = unsafeWindow.renderKatex || (unsafeWindow.MathJax && (unsafeWindow.MathJax.typeset || (unsafeWindow.MathJax.Hub && unsafeWindow.MathJax.Hub.Queue)));
                if (typeof renderFunc === 'function') {
                    try {
                        if (unsafeWindow.MathJax && unsafeWindow.MathJax.typeset) unsafeWindow.MathJax.typeset([element]);
                        else if (unsafeWindow.MathJax && unsafeWindow.MathJax.Hub) unsafeWindow.MathJax.Hub.Queue(["Typeset", unsafeWindow.MathJax.Hub, element]);
                        else renderFunc(element);
                    } catch (e) { console.error("Lỗi khi thực thi hàm render của OLM:", e); }
                }
            }, 500);
        }

        renderData(data) {
            if (!Array.isArray(data)) return;
            const responseContainer = document.createElement('div');
            const timestamp = new Date().toLocaleTimeString();
            responseContainer.innerHTML = `<p style="font-family: monospace; font-size: 12px; background: rgba(0,0,0,0.05); padding: 5px; border-radius: 4px;"><b>Time:</b> ${timestamp}</p>`;
            data.forEach(question => {
                const decodedContent = decodeBase64Utf8(question.content || '');
                if (!decodedContent) return;
                const answersElement = this.getAnswersAsDOM(question);
                // **MỚI**: Gọi hàm lấy lời giải
                const solutionElement = this.getSolutionAsDOM(decodedContent);
                const tempDiv = document.createElement('div');
                tempDiv.innerHTML = decodedContent;
                tempDiv.querySelectorAll('ol.quiz-list, ul.quiz-list, .interaction, .form-group, .loigiai, .huong-dan-giai, .explain, .solution, #solution, .guide, .exp').forEach(el => el.remove());
                const questionDiv = document.createElement('div');
                questionDiv.className = 'qa-block';
                questionDiv.style.cssText = 'padding: 8px; border-left: 3px solid #007bff; margin-bottom: 12px; background: rgba(255,255,255,0.6); border-radius: 0 4px 4px 0;';
                const questionDisplayContainer = document.createElement('div');
                questionDisplayContainer.className = 'question-content';
                questionDisplayContainer.style.cssText = 'font-weight: bold; color: #0056b3; margin-bottom: 5px;';
                while (tempDiv.firstChild) {
                    questionDisplayContainer.appendChild(tempDiv.firstChild);
                }
                if (!questionDisplayContainer.hasChildNodes() && question.title) {
                    questionDisplayContainer.innerHTML = question.title;
                }
                const contentContainer = document.createElement('div');
                contentContainer.className = 'content-container';
                if (answersElement) {
                    contentContainer.dataset.type = 'answer';
                    contentContainer.style.cssText = 'font-weight: bold; color: #dc3545; padding: 5px 0 0 10px;';
                    contentContainer.appendChild(answersElement);
                } else if (solutionElement) {
                    contentContainer.dataset.type = 'solution';
                    contentContainer.style.cssText = 'color: #28a745; padding: 5px 0 0 10px; border-top: 1px dashed #ccc; margin-top: 5px;';
                    const h3 = solutionElement.querySelector('h3');
                    if(h3) h3.remove();
                    contentContainer.appendChild(solutionElement);
                } else {
                    contentContainer.dataset.type = 'not-found';
                    contentContainer.style.cssText = 'padding: 5px 0 0 10px;';
                    contentContainer.innerHTML = '<p style="margin:0; font-style: italic; color: #777;">Không tìm thấy đáp án hay lời giải.</p>';
                }
                questionDiv.append(questionDisplayContainer, contentContainer);
                responseContainer.appendChild(questionDiv);
            });
            this.contentArea.prepend(responseContainer);
            this.renderContentWithOLM(this.contentArea);
        }
    }
    const answerUI = new AnswerDisplay();
    answerUI.init();
    
    const originalFetch = unsafeWindow.fetch;
    unsafeWindow.fetch = function(...args) { const requestUrl = args[0] instanceof Request ? args[0].url : args[0]; const promise = originalFetch.apply(this, args); if (requestUrl.includes(TARGET_URL_KEYWORD)) { promise.then(response => { if (response.ok) { response.clone().json().then(data => answerUI.renderData(data)).catch(err => console.error(err)); } }); } return promise; };
    const originalSend = XMLHttpRequest.prototype.send;
    XMLHttpRequest.prototype.send = function(...args) { this.addEventListener('load', () => { if (this.responseURL?.includes(TARGET_URL_KEYWORD) && this.status === 200) { try { const data = JSON.parse(this.responseText); answerUI.renderData(data); } catch (e) { console.error(e) } } }); return originalSend.apply(this, args); };
})();