// ==UserScript== // @name MZ - Training Report Checker // @namespace douglaskampl // @version 4.9 // @description Checks (periodically) 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://br18.org/mz/userscript/other/trainingReport.css // @run-at document-idle // @license MIT // @downloadURL https://update.greasyfork.cloud/scripts/494191/MZ%20-%20Training%20Report%20Checker.user.js // @updateURL https://update.greasyfork.cloud/scripts/494191/MZ%20-%20Training%20Report%20Checker.meta.js // ==/UserScript== (function () { 'use strict'; GM_addStyle(GM_getResourceText('trainingReportCheckerStyles')); const CONFIG = { CHECK_INTERVAL: 200000, /* [in 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: '', STORAGE_KEY: 'reportCheckedDate', }; 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.ballPlayers = []; this.hovering = false; this.isSoccer = false; } init() { this.verifySportIsSoccer(); if (!this.isSoccer) return; if (this.isSaturday()) return; this.addTrainingReportLink(); this.updateModalContent(false); this.checkTrainingReport(); } verifySportIsSoccer() { const sportLink = document.querySelector('#shortcut_link_thezone'); if (!sportLink) { const pageSportMeta = document.querySelector('meta[name="mz-sport"]'); if(pageSportMeta && pageSportMeta.content === 'soccer') { this.isSoccer = true; } return; } const sport = new URL(sportLink.href).searchParams.get('sport'); if (sport === 'soccer') { this.isSoccer = true; } } isSwedishDSTActive() { const now = new Date(); const year = now.getUTCFullYear(); const getLastSundayOfMonthUTC = (month) => { const lastDay = new Date(Date.UTC(year, month + 1, 0)); const dayOfWeek = lastDay.getUTCDay(); // 0 = Sunday, 1 = Monday, ..., 6 = Saturday const date = lastDay.getUTCDate() - dayOfWeek; return new Date(Date.UTC(year, month, date, 1, 0, 0)); // DST change occurs at 1:00 UTC }; const dstStart = getLastSundayOfMonthUTC(2); // March (Month is 0-indexed) const dstEnd = getLastSundayOfMonthUTC(9); // October const nowUTC = Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate(), now.getUTCHours(), now.getUTCMinutes(), now.getUTCSeconds()); return nowUTC >= dstStart.getTime() && nowUTC < dstEnd.getTime(); } 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 = ''; 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; const currentDayIsSaturday = this.getBrTime().getDay() === 6 && !this.isExtendedFriday(); if (currentDayIsSaturday) { return; } if (this.isExtendedFriday()) { if (isReady) { content = `Training report is out for ${todayStr} (Friday).`; if (this.balls !== null && this.balls > 0 && this.ballPlayers.length > 0) { content += '
Training balls earned today:
'; content += ''; } modal.className = 'ready'; } else { content = `Training report is not out yet for ${todayStr} (Friday).`; modal.className = 'not-ready'; } } else if (isReady) { content = `Training report is out for ${todayStr}!`; if (this.balls !== null && this.balls > 0 && this.ballPlayers.length > 0) { content += '
Training balls earned today:
'; content += ''; } modal.className = 'ready'; } else { content = `Training report is not out yet for ${todayStr}.`; modal.className = 'not-ready'; } modal.innerHTML = content; } async checkTrainingReport() { if (!this.isWithinCheckWindow()) { if(!this.hasReportBeenCheckedToday()){ setTimeout(() => this.checkTrainingReport(), CONFIG.CHECK_INTERVAL); } return; } 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.hasReportBeenCheckedToday()) { const link = document.getElementById(this.linkId); if (link) { link.innerHTML = CONFIG.READY_ICON_HTML; } await 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 { const link = document.getElementById(this.linkId); if (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() { const now = this.getBrTime(); return now.getDay() === 6 && !this.isExtendedFriday(); } isWithinCheckWindow() { const now = this.getBrTime(); const hour = now.getHours(); const minute = now.getMinutes(); const currentDay = now.getDay(); if (this.isSaturday()) return false; if (this.isExtendedFriday()) return true; let startHour, startMinute; const endHour = 23; const endMinute = 59; if (this.isSwedishDSTActive()) { startHour = 19; startMinute = 0; } else { startHour = 20; startMinute = 0; } const startTime = startHour * 60 + startMinute; const checkEndTime = endHour * 60 + endMinute; 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) { link.innerHTML = CONFIG.READY_ICON_HTML; const iconSpan = link.querySelector('.report-icon'); if (iconSpan) { iconSpan.addEventListener('animationend', () => { iconSpan.classList.remove('ready-transition'); }, { once: true }); iconSpan.classList.add('ready-transition'); } } this.fetchEarnedBalls(); } async fetchEarnedBalls() { this.balls = 0; this.ballPlayers = []; 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); } else { this.updateModalContent(this.hasReportBeenCheckedToday()); } } catch (_e) { this.updateModalContent(this.hasReportBeenCheckedToday()); } } hasReportBeenCheckedToday() { const today = this.getBrDate(); return GM_getValue(CONFIG.STORAGE_KEY) === today; } } const checker = new TrainingReportChecker(); if (document.readyState === 'complete' || document.readyState === 'interactive') { checker.init(); } else { document.addEventListener('DOMContentLoaded', () => checker.init()); } })();