// ==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
// @downloadURL none
// ==/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: '',
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;
}
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 += `
Training balls earned today:
`;
if (this.ballPlayers && this.ballPlayers.length > 0) {
content += '
';
this.ballPlayers.forEach(player => {
content += `- ${player.name} (${player.skill})
`;
});
content += '
';
}
}
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();
})();