DTUTOOL

Xem điểm học phần Duy Tân & Giải Captcha OCR & Tự động đánh giá & Giải Captcha Đăng Ký & Khai Báo Ngoại Trú

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name         DTUTOOL
// @namespace    https://mydtu.duytan.edu.vn/
// @version      0.9
// @description  Xem điểm học phần Duy Tân & Giải Captcha OCR & Tự động đánh giá & Giải Captcha Đăng Ký & Khai Báo Ngoại Trú
// @author       David Hua
// @match        *://mydtu.duytan.edu.vn/sites/index.aspx?p=home_grading_public_grade*
// @match        *://mydtu.duytan.edu.vn/Signin.aspx
// @match        *://mydtu.duytan.edu.vn/sites/index.aspx?p=home_ratingform*
// @match        *://mydtu.duytan.edu.vn/sites/index.aspx?p=home_registeredall*
// @match        *://khaibaongoaitru.duytan.edu.vn/KhaiBaoNgoaiTru/Index*
// @match        *://mydtu.duytan.edu.vn/sites/index.aspx?p=home_sar_studentaffairrating*
// @grant        GM_addStyle
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_registerMenuCommand
// @grant        GM_xmlhttpRequest
// @connect      toandev-ocr-for-captcha.hf.space
// @icon         https://mydtu.duytan.edu.vn/images/DTU.ICO
// ==/UserScript==

(function () {
    'use strict';
    // Configuration removed as the new OCR API doesn't require keys.
    const css = `#txtCaptcha.solving, #ctl00_PlaceHolderContentArea_ctl00_ctl01_txtCaptcha.solving, input.txt.txt-vn.solving, #captchaText.solving, #CodeNumberTextBox.solving { background-image: url("https://mydtu.duytan.edu.vn/images/ajax-loader1.gif") !important; background-position: right 5px center !important; background-repeat: no-repeat !important; background-size: 16px 16px !important; }`;
    const style = document.createElement('style'); style.textContent = css; document.head.appendChild(style);
    let lastCaptchaUrl = '';
    async function getCaptchaImageAsBlob(url) { const response = await fetch(url); if (!response.ok) return null; return await response.blob(); }
    async function getBase64FromImage(imgElement) { const canvas = document.createElement('canvas'); canvas.width = imgElement.naturalWidth; canvas.height = imgElement.naturalHeight; const ctx = canvas.getContext('2d'); ctx.drawImage(imgElement, 0, 0); return new Promise((resolve) => { canvas.toBlob((blob) => { resolve(blob); }, 'image/jpeg'); }); }
    async function getCaptchaImage() { if (window.location.href.includes('khaibaongoaitru.duytan.edu.vn')) { const imgElement = document.querySelector('.box_col_6.col_box img'); if (imgElement) { return await getBase64FromImage(imgElement); } return null; } else if (window.location.href.includes('home_sar_studentaffairrating')) { const imgElement = document.getElementById('captchaImg'); if (imgElement) { return await getBase64FromImage(imgElement); } return null; } const captchaUrl = getCaptchaUrl(); if (!captchaUrl) return null; if (captchaUrl !== lastCaptchaUrl) { lastCaptchaUrl = captchaUrl; return await getCaptchaImageAsBlob(captchaUrl); } return null; }
    function getCaptchaUrl() { let captchaImage = document.querySelector('#imgCapt'); if (captchaImage) { return captchaImage.src; } captchaImage = document.querySelector('.floatbox img'); if (captchaImage) { return captchaImage.src; } return null; }
    async function solveCaptcha(blob, inputElement) {
        if (inputElement) { inputElement.classList.add('solving'); inputElement.value = ''; }
        try {
            // Step 1: Upload image to Gradio server
            const formData = new FormData();
            formData.append('files', blob, 'captcha.jpg');
            
            const uploadResult = await new Promise((resolve, reject) => {
                GM_xmlhttpRequest({
                    method: 'POST',
                    url: 'https://toandev-ocr-for-captcha.hf.space/gradio_api/upload',
                    data: formData,
                    onload: function(response) {
                        if (response.status === 200) {
                            try {
                                const json = JSON.parse(response.responseText);
                                resolve(json[0]); // Gradio returns an array of paths
                            } catch (e) { reject(new Error('Lỗi parse upload: ' + e.message)); }
                        } else { reject(new Error('Lỗi upload: ' + response.statusText)); }
                    },
                    onerror: (e) => reject(new Error('Network error during upload: ' + e.statusText))
                });
            });

            // Step 2: Initiate prediction call
            const eventId = await new Promise((resolve, reject) => {
                GM_xmlhttpRequest({
                    method: 'POST',
                    url: 'https://toandev-ocr-for-captcha.hf.space/gradio_api/call/predict',
                    headers: { 'Content-Type': 'application/json' },
                    data: JSON.stringify({ data: [{ path: uploadResult }] }),
                    onload: function(response) {
                        if (response.status === 200) {
                            try {
                                const json = JSON.parse(response.responseText);
                                resolve(json.event_id);
                            } catch (e) { reject(new Error('Lỗi parse predict: ' + e.message)); }
                        } else { reject(new Error('Lỗi predict call: ' + response.statusText)); }
                    },
                    onerror: (e) => reject(new Error('Network error during predict call: ' + e.statusText))
                });
            });

            // Step 3: Get result from the event stream
            return await new Promise((resolve, reject) => {
                GM_xmlhttpRequest({
                    method: 'GET',
                    url: `https://toandev-ocr-for-captcha.hf.space/gradio_api/call/predict/${eventId}`,
                    onload: function(response) {
                        if (response.status === 200) {
                            const text = response.responseText;
                            if (text.includes('event: complete')) {
                                const lines = text.split('\n');
                                for (const line of lines) {
                                    if (line.startsWith('data: ')) {
                                        try {
                                            const resultData = JSON.parse(line.substring(6));
                                            const result = resultData[0];
                                            const cleanResult = result.replace(/[^A-Z0-9]/gi, '');
                                            console.log('🎯 Captcha solved:', cleanResult);
                                            resolve(cleanResult);
                                            return;
                                        } catch (e) { console.error('Parse result error:', e); }
                                    }
                                }
                            }
                            reject(new Error('Không tìm thấy kết quả trong response'));
                        } else { reject(new Error('Lỗi lấy kết quả: ' + response.statusText)); }
                    },
                    onerror: (e) => reject(new Error('Network error while fetching result: ' + e.statusText))
                });
            });
        } catch (error) {
            console.error('❌ Captcha Error:', error);
            return null;
        } finally {
            if (inputElement) { inputElement.classList.remove('solving'); }
        }
    }
    async function handleCaptcha(inputId) { try { const captchaInput = document.getElementById(inputId); const captchaImageBlob = await getCaptchaImage(); if (!captchaImageBlob) { return; } const captchaResult = await solveCaptcha(captchaImageBlob, captchaInput); if (!captchaResult) { return; } if (captchaInput) { captchaInput.value = captchaResult; } } catch (error) { console.error('HandleCaptcha Error:', error); } }
    if (typeof F5Captcha === 'function') { const originalF5Captcha = F5Captcha; F5Captcha = function () { originalF5Captcha.apply(this, arguments); setTimeout(() => { handleCaptcha('CodeNumberTextBox'); }, 500); }; }
    if (typeof LoadCaptcha === 'function') { const originalLoadCaptcha = LoadCaptcha; LoadCaptcha = function () { originalLoadCaptcha.apply(this, arguments); const captchaInputId = 'ctl00_PlaceHolderContentArea_ctl00_ctl01_txtCaptchar'; const captchaImageSelector = '#imgCapt'; const loadCheckInterval = setInterval(() => { const captchaImage = document.querySelector(captchaImageSelector); if (captchaImage && captchaImage.src) { clearInterval(loadCheckInterval); handleCaptcha(captchaInputId); } }, 500); }; }
    function checkOption(radioId) { const radio = document.getElementById(radioId); if (radio) { radio.checked = true; } }
    function autoFillRatingForm() { for (let i = 48; i <= 51; i++) { const element = document.getElementById("R" + i); if (element) { element.value += "Không có ý kiến"; } } for (let i = 0; i <= 47; i++) { checkOption("R" + i + "A"); } checkOption('R52E'); window.scrollTo(0, document.body.scrollHeight); handleCaptcha('ctl00_PlaceHolderContentArea_ctl00_ctl01_txtCaptcha'); }
    if (window.location.href.includes('home_sar_studentaffairrating')) { const sarCaptchaInterval = setInterval(() => { const captchaImage = document.getElementById('captchaImg'); if (captchaImage && captchaImage.complete) { clearInterval(sarCaptchaInterval); handleCaptcha('CodeNumberTextBox'); } }, 500); } else if (window.location.href.includes('khaibaongoaitru.duytan.edu.vn/KhaiBaoNgoaiTru/Index')) { const captchaCheckInterval = setInterval(() => { const captchaImage = document.querySelector('.box_col_6.col_box img'); if (captchaImage && captchaImage.complete) { clearInterval(captchaCheckInterval); handleCaptcha('captchaText'); } }, 500); } else if (window.location.href === 'https://mydtu.duytan.edu.vn/Signin.aspx') { handleCaptcha('txtCaptcha'); const loginButton = document.getElementById('btnLogin1'); if (loginButton) { loginButton.addEventListener('click', function () { const loginCheckInterval = setInterval(() => { const captchaImage = document.querySelector('.floatbox img'); if (captchaImage && captchaImage.src) { clearInterval(loginCheckInterval); handleCaptcha('txtCaptcha'); } }, 500); }); } document.addEventListener('keypress', function (event) { if (event.key === 'Enter') { const enterCheckInterval = setInterval(() => { const captchaImage = document.querySelector('.floatbox img'); if (captchaImage && captchaImage.src) { clearInterval(enterCheckInterval); handleCaptcha('txtCaptcha'); } }, 500); } }); } else if (window.location.href.includes('mydtu.duytan.edu.vn/sites/index.aspx?p=home_ratingform')) { autoFillRatingForm(); } else if (window.location.href.includes('mydtu.duytan.edu.vn/sites/index.aspx?p=home_grading_public_grade')) { const csvUrl = 'https://docs.google.com/spreadsheets/d/1RL30B0GkoiJcSYeABZCtUvIoWduxegF0/export?format=csv'; async function fetchCsvData(url) { const response = await fetch(url); const text = await response.text(); return text.split('\n').map(row => row.split(',')); } async function updateTable() { const csvData = await fetchCsvData(csvUrl); const dataMap = {}; csvData.forEach(row => { const [key, value] = row; dataMap[key.trim()] = value.trim(); }); const table = document.getElementById('frmNhapDiem'); if (!table) return; const rows = table.getElementsByTagName('tbody')[0].getElementsByTagName('tr'); for (let row of rows) { const cells = row.getElementsByTagName('td'); if (cells.length > 2) { const id = cells[1].innerText.trim(); if (dataMap[id]) { cells[2].innerText = dataMap[id]; } } } } updateTable(); } else if (window.location.href.includes('mydtu.duytan.edu.vn/sites/index.aspx?p=home_registeredall')) { const captchaInputId = 'ctl00_PlaceHolderContentArea_ctl00_ctl01_txtCaptchar'; const captchaImageSelector = '#imgCapt'; const initialCheckInterval = setInterval(() => { const captchaImage = document.querySelector(captchaImageSelector); if (captchaImage && captchaImage.src) { clearInterval(initialCheckInterval); handleCaptcha(captchaInputId); } }, 500); }
})();