Greasy Fork

MZ - Training Report Checker

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

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

// ==UserScript==
// @name         MZ - Training Report Checker
// @namespace    douglaskampl
// @version      4.3
// @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_getValue
// @grant        GM_setValue
// @grant        GM_addStyle
// @grant        GM_getResourceText
// @resource     trainingReportCheckerStyles https://u18mz.vercel.app/mz/userscript/other/trainingReportChecker.css
// @run-at       document-idle
// @license      MIT
// ==/UserScript==

(function () {
    'use strict';

    GM_addStyle(GM_getResourceText("trainingReportCheckerStyles"));

    const CONFIG = {
        CHECK_INTERVAL: 300000, /* (in milliseconds) */
        TIMEZONE_OFFSET: -3,
        FETCH_URL_REPORT: 'https://www.managerzone.com/ajax.php?p=trainingReport&sub=daily&sport=soccer&day=',
        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, // Sunday
        1: 2, // Monday
        2: 3, // Tuesday
        3: 4, // Wednesday
        4: 5, // Thursday
        5: 6, // Friday
        6: null // Saturday
    };

    class TrainingReportChecker {
        constructor() {
            this.linkId = 'shortcut_link_trainingreport';
            this.modalId = 'training_report_modal';
            this.balls = null;
            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);
        }

        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;
            } else {
                link.innerHTML = CONFIG.NOT_READY_ICON_HTML;
            }

            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();
            let content;

            if (this.isSaturday()) {
                content = "No training report on Saturdays!";
                modal.className = 'no-report';
            } else if (isReady) {
                content = `Training report is out for ${todayStr}!`;
                if (this.balls !== null && this.balls > 0) {
                    content += `<br>Training balls earned today: <br>`;
                    if (this.ballPlayers && this.ballPlayers.length > 0) {
                        content += '<ul style="list-style-type: none; padding: 0; margin: 5px 0;">';
                        this.ballPlayers.forEach(player => {
                            content += `<li style="margin: 2px 0;">${player.name} (${player.skill})</li>`;
                        });
                        content += '</ul>';
                    }
                }
                modal.className = 'ready';
            } else {
                content = `Training report is not out yet for ${todayStr}.`;
                modal.className = 'not-ready';
            }
            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';
                    }
                }, 200);
            }
        }

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

        markReportAsChecked() {
            const today = this.getBrazilianDate();
            GM_setValue(CONFIG.STORAGE_KEY, today);
            const link = document.getElementById(this.linkId);
            if (link) {
                const iconSpan = link.querySelector('.report-icon');
                if (iconSpan) {
                    iconSpan.addEventListener('animationend', () => {
                        iconSpan.classList.remove('ready-transition');
                    }, { once: true });
                    iconSpan.classList.add('ready-transition');
                }
                link.innerHTML = CONFIG.READY_ICON_HTML;
            }
            this.fetchEarnedBalls();
        }

        async fetchEarnedBalls() {
            try {
                const dayIndex = DAY_MAP[new Date().getDay()];
                if (!dayIndex) return;

                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 playerDetails = [];
                const rows = doc.querySelectorAll('tr');
                rows.forEach(row => {
                    const ballImg = row.querySelector('img[src*="gained_skill.png"]');
                    if (ballImg) {
                        const nameLink = row.querySelector('.player_link');
                        const skillCell = row.querySelector('.skillColumn .clippable');

                        if (nameLink && skillCell) {
                            const fullName = nameLink.textContent.trim();
                            const nameParts = fullName.split(' ');
                            const shortName = nameParts.length > 1 ?
                                `${nameParts[0][0]}. ${nameParts[nameParts.length-1]}` :
                                fullName;
                            const skill = skillCell.textContent.trim();

                            playerDetails.push({
                                name: shortName,
                                skill: skill
                            });
                        }
                    }
                });

                if (playerDetails.length > 0) {
                    this.balls = playerDetails.length;
                    this.ballPlayers = playerDetails;
                    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);
                if (link) {
                    link.innerHTML = CONFIG.READY_ICON_HTML;
                }
                this.fetchEarnedBalls();
                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) {
                        link.innerHTML = CONFIG.NOT_READY_ICON_HTML;
                    }
                    this.updateModalContent(false);
                    setTimeout(() => this.checkTrainingReport(), CONFIG.CHECK_INTERVAL);
                }
            } catch (_e) {
                setTimeout(() => this.checkTrainingReport(), CONFIG.CHECK_INTERVAL);
            }
        }

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