Hack olm by Bảo Ngọc
当前为
// ==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); };
})();