Greasy Fork

MZ - Training Report Checker

Checks training report status and visually tells the user if it's out or not on a daily basis (except for saturdays)

目前为 2024-12-11 提交的版本。查看 最新版本

// ==UserScript==
// @name         MZ - Training Report Checker
// @namespace    douglaskampl
// @version      2.4
// @description  Checks training report status and visually tells the user if it's out or not on a daily basis (except for saturdays)
// @author       Douglas
// @match        https://www.managerzone.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=managerzone.com
// @grant        GM_addStyle
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    const CONFIG = {
        START_HOUR: 19,
        START_MINUTE: 1,
        END_HOUR: 23,
        END_MINUTE: 59,
        CHECK_INTERVAL: 300000,
        TIMEZONE_OFFSET: -3,
        READY_COLOR: 'green',
        NOT_READY_COLOR: 'red',
        MODAL_BG: '#333',
        MODAL_TEXT_COLOR: '#fff',
        MODAL_PADDING: '5px',
        MODAL_BORDER_RADIUS: '3px',
        MODAL_FONT_SIZE: '12px',
        FETCH_URL_REPORT: 'https://www.managerzone.com/ajax.php?p=trainingReport&sub=daily&sport=soccer&day=',
        FETCH_URL_CLUBHOUSE: 'https://www.managerzone.com/?p=clubhouse',
        ICON_HTML: '| <i class="fa fa-unlock"></i>'
    };

    const DAY_MAP = {
        0: 1,
        1: 2,
        2: 3,
        3: 4,
        4: 5,
        5: 6,
        6: null
    };

    class TrainingReportChecker {
        constructor() {
            this.linkId = 'shortcut_link_trainingreport';
            this.modalId = 'training_report_modal';
            this.balls = null;
        }

        getBrazilianDate() {
            const date = new Date();
            const utc = date.getTime() + (date.getTimezoneOffset() * 60000);
            const brazilTime = new Date(utc + (3600000 * CONFIG.TIMEZONE_OFFSET));
            return new Intl.DateTimeFormat('en-US', { year: 'numeric', month: 'long', day: 'numeric' }).format(brazilTime);
        }

        getBrazilianYesterdayDate() {
            const date = new Date();
            const utc = date.getTime() + (date.getTimezoneOffset() * 60000);
            const brazilTime = new Date(utc + (3600000 * CONFIG.TIMEZONE_OFFSET));
            brazilTime.setDate(brazilTime.getDate() - 1);
            return new Intl.DateTimeFormat('en-US', { year: 'numeric', month: 'long', day: 'numeric' }).format(brazilTime);
        }

        getBrazilianTime() {
            const date = new Date();
            const utc = date.getTime() + (date.getTimezoneOffset() * 60000);
            return new Date(utc + (3600000 * CONFIG.TIMEZONE_OFFSET));
        }

        isWithinCheckWindow() {
            const now = this.getBrazilianTime();
            const hour = now.getHours();
            const minute = now.getMinutes();
            const startTime = CONFIG.START_HOUR * 60 + CONFIG.START_MINUTE;
            const checkEndTime = CONFIG.END_HOUR * 60 + CONFIG.END_MINUTE;
            const currentTime = hour * 60 + minute;
            return currentTime >= startTime && currentTime <= checkEndTime;
        }

        addTrainingReportLink() {
            const targetDiv = document.getElementById('pt-wrapper');
            if (!targetDiv) return;
            const link = document.createElement('a');
            link.id = this.linkId;
            link.href = '/?p=training_report';
            link.innerHTML = CONFIG.ICON_HTML;
            link.style.color = CONFIG.NOT_READY_COLOR;
            targetDiv.appendChild(link);
            this.createModal();
            link.addEventListener('mouseover', () => this.showModal());
            link.addEventListener('mouseout', () => this.hideModal());
        }

        createModal() {
            GM_addStyle(`#${this.modalId} {position:absolute;background:${CONFIG.MODAL_BG};color:#ffd600 !important;padding:${CONFIG.MODAL_PADDING};border:2px solid blue;border-radius:${CONFIG.MODAL_BORDER_RADIUS};font-size:14px;display:none;z-index:9999;text-align:center;animation-duration:0.3s;} @keyframes fadeIn {from {opacity:0;} to {opacity:1;}} @keyframes fadeOut {from {opacity:1;} to {opacity:0;}}`);
            const modal = document.createElement('div');
            modal.id = this.modalId;
            document.body.appendChild(modal);
        }

        updateModalContent(isReady) {
            const modal = document.getElementById(this.modalId);
            if (!modal) return;
            const todayStr = this.getBrazilianDate();
            const yesterdayStr = this.getBrazilianYesterdayDate();
            let content;
            if (isReady) {
                content = "Training report is finally out for " + todayStr + "<br>Click the icon to see it";
                if (this.balls !== null) {
                    content += "<br>New balls gained today: " + this.balls;
                }
                modal.style.color = CONFIG.READY_COLOR;
            } else {
                content = "Training report is not out yet for " + todayStr + "<br>Click the icon to see the most recent training report (" + yesterdayStr + ")";
                modal.style.color = CONFIG.NOT_READY_COLOR;
            }
            modal.innerHTML = content;
        }

        positionModal() {
            const icon = document.getElementById(this.linkId);
            const modal = document.getElementById(this.modalId);
            if (!icon || !modal) return;
            const rect = icon.getBoundingClientRect();
            modal.style.top = (window.scrollY + rect.bottom + 5) + 'px';
            modal.style.left = (window.scrollX + rect.left + 50) + 'px';
        }

        showModal() {
            const modal = document.getElementById(this.modalId);
            if (!modal) return;
            this.positionModal();
            modal.style.display = 'block';
            modal.style.animation = 'fadeIn 0.6s forwards';
        }

        hideModal() {
            const modal = document.getElementById(this.modalId);
            if (!modal) return;
            modal.style.animation = 'fadeOut 0.6s forwards';
            setTimeout(() => {
                modal.style.display = 'none';
            }, 300);
        }

        isReportReady(table) {
            return Array.from(table.querySelectorAll('tr')).some(row => row.querySelector('img[src*="training_camp.png"]'));
        }

        markReportAsChecked() {
            const today = this.getBrazilianDate();
            localStorage.setItem('reportCheckedDate', today);
            document.getElementById(this.linkId).style.color = CONFIG.READY_COLOR;
            this.fetchEarnedBalls();
        }

        async fetchEarnedBalls() {
            try {
                const response = await fetch(CONFIG.FETCH_URL_CLUBHOUSE);
                const text = await response.text();
                const parser = new DOMParser();
                const doc = parser.parseFromString(text, "text/html");
                const widget = doc.querySelector('#clubhouse-widget-training');
                if (widget) {
                    const strongElems = widget.querySelectorAll('strong');
                    if (strongElems.length > 0) {
                        const ballsText = strongElems[0].textContent.trim();
                        this.balls = ballsText;
                        this.updateModalContent(true);
                    }
                }
            } catch (e) {}
        }

        hasReportBeenCheckedToday() {
            const today = this.getBrazilianDate();
            return localStorage.getItem('reportCheckedDate') === today;
        }

        async checkTrainingReport() {
            const today = new Date();
            const dayIndex = DAY_MAP[today.getDay()];
            if (!dayIndex) return;
            if (!this.isWithinCheckWindow()) return;
            if (this.hasReportBeenCheckedToday()) {
                document.getElementById(this.linkId).style.color = CONFIG.READY_COLOR;
                this.updateModalContent(true);
                return;
            }
            try {
                const response = await fetch(CONFIG.FETCH_URL_REPORT + dayIndex + '&sort_order=desc&sort_key=modification&player_sort=all');
                const text = await response.text();
                const parser = new DOMParser();
                const doc = parser.parseFromString(text, "text/html");
                const table = doc.querySelector("body > table:nth-child(3)");
                if (table && this.isReportReady(table)) {
                    this.markReportAsChecked();
                    this.updateModalContent(true);
                } else if (this.isWithinCheckWindow()) {
                    this.updateModalContent(false);
                    setTimeout(() => this.checkTrainingReport(), CONFIG.CHECK_INTERVAL);
                }
            } catch (e) {}
        }

        init() {
            const sport = new URL(document.querySelector("#shortcut_link_thezone").href).searchParams.get("sport");
            if (sport !== "soccer") return;
            this.addTrainingReportLink();
            this.updateModalContent(false);
            this.checkTrainingReport();
        }
    }

    const checker = new TrainingReportChecker();
    checker.init();
})();