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