Greasy Fork

MZ - Training Report

Checks if the training report is already out for the current day

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

// ==UserScript==
// @name         MZ - Training Report
// @namespace    douglaskampl
// @version      3.6
// @description  Checks if the training report is already out for the current day
// @author       Douglas
// @match        https://www.managerzone.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=managerzone.com
// @grant        GM_addStyle
// @grant        GM_getValue
// @grant        GM_setValue
// @license      MIT
// ==/UserScript==

GM_addStyle(`
    #training_report_modal {
        position: absolute;
        background: #002633;
        color: #ffd600;
        padding: 5px;
        border: 1px solid #374151;
        border-radius: 3px;
        font-size: 12px;
        display: none;
        z-index: 9999;
        text-align: center;
        animation-duration: 0.3s;
        transition: opacity 0.3s ease-in-out;
    }

    #training_report_modal.ready {
        color: #86efac;
    }

    #training_report_modal.not-ready {
        color: #f9fafb;
    }

    #training_report_modal.no-report {
        color: #ef4444;
    }

    @keyframes fadeIn {
        from { opacity: 0; transform: scale(0.95); }
        to { opacity: 1; transform: scale(1); }
    }

    @keyframes fadeOut {
        from { opacity: 1; transform: scale(1); }
        to { opacity: 0; transform: scale(0.95); }
    }

    .fade-in {
      animation: fadeIn 0.3s forwards;
    }

    .fade-out {
      animation: fadeOut 0.3s forwards;
    }

    .report-icon {
      display: inline-block;
      transition: transform 0.3s ease-in-out;
    }

    .report-icon:hover {
      transform: rotate(2deg) scale(1.1);
    }

    .report-icon.ready {
      color: #2c9c55;
    }

    .report-icon.not-ready {
      color: #f9fafb;
    }

    .report-icon.no-report {
      color: #ef4444;
    }
`);

(function() {
    'use strict';

    const CONFIG = {
        CHECK_INTERVAL: 300000,
        TIMEZONE_OFFSET: -3,
        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',
        READY_ICON_HTML: '<span class="report-icon ready">| <i class="fa fa-unlock"></i></span>',
        NOT_READY_ICON_HTML: '<span class="report-icon not-ready">| <i class="fa fa-lock"></i></span>',
        NO_REPORT_ICON_HTML: '<span class="report-icon no-report">| <i class="fa fa-times"></i></span>',
        STORAGE_KEY: 'reportCheckedDate',
        TIMES: {
            DEFAULT: {
                START_HOUR: 19,
                START_MINUTE: 1,
                END_HOUR: 23,
                END_MINUTE: 59
            },
            SUNDAY: {
                START_HOUR: 20,
                START_MINUTE: 0,
                END_HOUR: 23,
                END_MINUTE: 59
            }
        }
    };

    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;
            this.currentIconColor = CONFIG.NOT_READY_COLOR;
            this.hovering = false;
        }

        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);
        }

        getLastReportDate() {
            const date = new Date();
            const utc = date.getTime() + (date.getTimezoneOffset() * 60000);
            const brazilTime = new Date(utc + (3600000 * CONFIG.TIMEZONE_OFFSET));

            const daysToSubtract = brazilTime.getDay() === 0 ? 2 : 1;
            brazilTime.setDate(brazilTime.getDate() - daysToSubtract);

            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));
        }

        isSaturday() {
            return this.getBrazilianTime().getDay() === 6;
        }

        isSunday() {
            return this.getBrazilianTime().getDay() === 0;
        }

        isWithinCheckWindow() {
            const now = this.getBrazilianTime();
            const hour = now.getHours();
            const minute = now.getMinutes();

            const times = this.isSunday() ? CONFIG.TIMES.SUNDAY : CONFIG.TIMES.DEFAULT;
            const startTime = times.START_HOUR * 60 + times.START_MINUTE;
            const checkEndTime = times.END_HOUR * 60 + times.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';

            if (this.isSaturday()) {
                link.innerHTML = CONFIG.NO_REPORT_ICON_HTML;
                link.style.color = CONFIG.SATURDAY_COLOR;
                this.currentIconColor = CONFIG.SATURDAY_COLOR;
            } else {
                link.innerHTML = CONFIG.NOT_READY_ICON_HTML;
                link.style.color = CONFIG.NOT_READY_COLOR;
                this.currentIconColor = CONFIG.NOT_READY_COLOR;
            }

            targetDiv.appendChild(link);

            this.createModal();

            link.addEventListener('mouseenter', () => this.showModal());
            link.addEventListener('mouseleave', (e) => this.handleMouseLeave(e));

            const modal = document.getElementById(this.modalId);
            modal.addEventListener('mouseenter', () => this.hovering = true);
            modal.addEventListener('mouseleave', (e) => this.handleMouseLeave(e));
        }

        createModal() {
            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 lastReportStr = this.getLastReportDate();

            let content;

            if (this.isSaturday()) {
                content = "No training report on Saturdays!";
                modal.style.color = CONFIG.SATURDAY_COLOR;
            } else if (this.isSunday()) {
                if (isReady) {
                    content = "Training report out for " + todayStr + "!";
                    if (this.balls !== null) {
                        content += "<br>Balls earned today: " + this.balls;
                    }
                    modal.style.color = CONFIG.READY_COLOR;
                } else {
                    content = "Training report is not out yet for " + todayStr + ".";
                    modal.style.color = CONFIG.NOT_READY_COLOR;
                }
            } else if (isReady) {
                content = "Training report is out for " + todayStr + "!";
                if (this.balls !== null) {
                    content += "<br>Balls earned today: " + this.balls;
                }
                modal.style.color = CONFIG.READY_COLOR;
            } else {
                content = "Training report is not out yet for " + todayStr + ".";
                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.hovering = true;
            this.positionModal();
            modal.style.display = 'block';
            modal.classList.remove('fade-out');
            modal.classList.add('fade-in');
        }

        handleMouseLeave(e) {
            const modal = document.getElementById(this.modalId);
            const icon = document.getElementById(this.linkId);

            if (!icon || !modal) return;

            const relatedTarget = e.relatedTarget;
            if (relatedTarget !== modal && relatedTarget !== icon) {
                this.hovering = false;
                this.hideModal();
            }
        }

        hideModal() {
            if (!this.hovering) {
                const modal = document.getElementById(this.modalId);
                if (!modal) return;
                modal.classList.remove('fade-in');
                modal.classList.add('fade-out');
                setTimeout(() => {
                    if (!this.hovering) {
                        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();
            GM_setValue(CONFIG.STORAGE_KEY, today);
            const link = document.getElementById(this.linkId);
            link.style.color = CONFIG.READY_COLOR;
            link.innerHTML = CONFIG.READY_ICON_HTML;
            this.currentIconColor = 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 GM_getValue(CONFIG.STORAGE_KEY) === today;
        }

        async checkTrainingReport() {
            const today = new Date();
            const dayIndex = DAY_MAP[today.getDay()];

            if (!dayIndex) {
                this.updateModalContent(false);
                return;
            }

            if (!this.isWithinCheckWindow()) return;

            if (this.hasReportBeenCheckedToday()) {
                const link = document.getElementById(this.linkId);
                link.style.color = CONFIG.READY_COLOR;
                link.innerHTML = CONFIG.READY_ICON_HTML;
                this.currentIconColor = 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()) {
                    const link = document.getElementById(this.linkId);
                    if (!this.isSaturday()) {
                        link.style.color = CONFIG.NOT_READY_COLOR;
                        link.innerHTML = CONFIG.NOT_READY_ICON_HTML;
                        this.currentIconColor = CONFIG.NOT_READY_COLOR;
                    }
                    this.updateModalContent(false);
                    setTimeout(() => this.checkTrainingReport(), CONFIG.CHECK_INTERVAL);
                }
            } catch (_e) {}
        }

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

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