// ==UserScript== // @name MZ - Training Report Checker // @namespace douglaskampl // @version 4.8 // @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 // @downloadURL none // ==/UserScript== (function () { 'use strict'; GM_addStyle(GM_getResourceText('trainingReportCheckerStyles')); const CONFIG = { CHECK_INTERVAL: 300000, // [ms] TIMEZONE_OFFSET: -3, FETCH_URL_REPORT: 'https://www.managerzone.com/ajax.php?p=trainingReport&sub=daily&sport=soccer&day=', READY_ICON_HTML: '', NOT_READY_ICON_HTML: '', NO_REPORT_ICON_HTML: '', 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; } init() { this.verifySportIsSoccer(); this.addTrainingReportLink(); this.updateModalContent(false); this.checkTrainingReport(); } verifySportIsSoccer() { const sportLink = document.querySelector('#shortcut_link_thezone'); if (!sportLink) return; const sport = new URL(sportLink.href).searchParams.get('sport'); if (sport !== 'soccer') return; } 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.title = ''; // To bypass Power Tokens title inherited from parent div 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)); } updateModalContent(isReady) { const modal = document.getElementById(this.modalId); if (!modal) return; const todayStr = this.getBrDate(); let content; if (this.isExtendedFriday()) { if (isReady) { content = `Training report is out for ${todayStr} (Friday).`; if (this.balls !== null && this.balls > 0) { content += '
Training balls earned today:
'; if (this.ballPlayers && this.ballPlayers.length > 0) { content += ''; } } modal.className = 'ready'; } else { content = `Training report is not out yet for ${todayStr} (Friday).`; modal.className = 'not-ready'; } } else 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 += '
Training balls earned today:
'; if (this.ballPlayers && this.ballPlayers.length > 0) { content += ''; } } modal.className = 'ready'; } else { content = `Training report is not out yet for ${todayStr}.`; modal.className = 'not-ready'; } modal.innerHTML = content; } async checkTrainingReport() { const now = this.getBrTime(); let day = now.getDay(); if (this.isExtendedFriday()) day = 5; const dayIndex = DAY_MAP[day]; 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); } } getBrDate() { const date = new Date(); const utc = date.getTime() + (date.getTimezoneOffset() * 60000); const brTime = new Date(utc + (3600000 * CONFIG.TIMEZONE_OFFSET)); return new Intl.DateTimeFormat('en-US', { year: 'numeric', month: 'long', day: 'numeric' }).format(brTime); } getBrTime() { const date = new Date(); const utc = date.getTime() + (date.getTimezoneOffset() * 60000); return new Date(utc + (3600000 * CONFIG.TIMEZONE_OFFSET)); } isExtendedFriday() { const now = this.getBrTime(); return now.getDay() === 6 && now.getHours() < 1; } isSaturday() { return this.getBrTime().getDay() === 6 && !this.isExtendedFriday(); } isSunday() { return this.getBrTime().getDay() === 0; } isWithinCheckWindow() { const now = this.getBrTime(); const day = now.getDay(); const hour = now.getHours(); const minute = now.getMinutes(); if ((day === 5 && hour >= 19) || (day === 6 && hour < 1)) { return true; } 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; } createModal() { const modal = document.createElement('div'); modal.id = this.modalId; document.body.appendChild(modal); } 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.getBrDate(); 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 now = this.getBrTime(); let day = now.getDay(); if (this.isExtendedFriday()) day = 5; const dayIndex = DAY_MAP[day]; 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.getBrDate(); return GM_getValue(CONFIG.STORAGE_KEY) === today; } } new TrainingReportChecker().init(); })();