// ==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 // @downloadURL none // ==/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: '| ' }; 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 + "
Click the icon to see it"; if (this.balls !== null) { content += "
New balls gained today: " + this.balls; } modal.style.color = CONFIG.READY_COLOR; } else { content = "Training report is not out yet for " + todayStr + "
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(); })();