您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Sends NT challenges and notifies about incoming challenges
// ==UserScript== // @name MZ - NT Challenges // @namespace douglaskampl // @version 2.0 // @description Sends NT challenges and notifies about incoming challenges // @author Douglas // @match https://www.managerzone.com/* // @icon https://www.google.com/s2/favicons?sz=64&domain=managerzone.com // @connect pub-02de1c06eac643f992bb26daeae5c7a0.r2.dev // @connect www.managerzone.com // @grant GM_getValue // @grant GM_setValue // @grant GM_addStyle // @grant GM_getResourceText // @grant GM_xmlhttpRequest // @require https://cdn.jsdelivr.net/npm/[email protected]/src/toastify.min.js // @resource TOASTIFY_CSS https://cdn.jsdelivr.net/npm/[email protected]/src/toastify.min.css // @license MIT // ==/UserScript== (function () { 'use strict'; const SCRIPT_PREFIX = '[NT-CHALLENGER]'; const dataManager = { cachedCountries: null, endpoints: { countries: 'https://pub-02de1c06eac643f992bb26daeae5c7a0.r2.dev/json/countries.json', userData: u => `https://www.managerzone.com/xml/manager_data.php?sport_id=1&username=${u}`, nationalTeam: (nt, cid, type) => `https://www.managerzone.com/ajax.php?p=nationalTeams&sub=team&ntid=${nt}&cid=${cid}&type=${type}&sport=soccer`, challenges: 'https://www.managerzone.com/ajax.php?p=nationalTeams&sub=challenge', createChallenge: 'https://www.managerzone.com/ajax.php?p=challenge&sub=handle-challenge&action=create' }, storageKeys: { countriesCache: 'mz_nt_challenger_countries_cache', defaultCoached: 'mz_nt_default_coached_list', defaultUncoached: 'mz_nt_default_uncoached_list', exclusionList: 'mz_nt_challenge_exclusion_list' }, initializeDefaultLists() { if (!GM_getValue(this.storageKeys.defaultCoached, null)) { const defaultCoached = [ "Albania", "Argentina", "Australia", "Austria", "Belgium", "Brazil", "Canada", "Chile", "China", "Colombia", "Croatia", "Denmark", "England", "Finland", "France", "Germany", "Greece", "Hungary", "Iceland", "Italy", "Mexico", "Netherlands", "Poland", "Portugal", "Russia", "Scotland", "Spain", "Sweden", "Switzerland", "Turkey", "United States", "Uruguay", "Wales" ]; GM_setValue(this.storageKeys.defaultCoached, defaultCoached); } if (!GM_getValue(this.storageKeys.defaultUncoached, null)) { const defaultUncoached = [ "Algeria", "Faroe Islands", "Iran", "Jordan", "Kenya", "Kuwait", "Northern Ireland", "Norway", "Pakistan", "Saudi Arabia", "Senegal", "Vietnam" ]; GM_setValue(this.storageKeys.defaultUncoached, defaultUncoached); } }, async fetch(details) { return new Promise((resolve, reject) => { const requestDetails = { ...details, timeout: 20000, onload: response => resolve(response), onerror: (err) => { console.error(`${SCRIPT_PREFIX} GM_xmlhttpRequest error:`, err); reject(new Error('Network Error')); }, ontimeout: () => { console.error(`${SCRIPT_PREFIX} GM_xmlhttpRequest timeout for URL: ${details.url}`); reject(new Error('Request Timed Out')); }, }; GM_xmlhttpRequest(requestDetails); }); }, async getCountries(force = false) { if (force) { this.cachedCountries = null; GM_setValue(this.storageKeys.countriesCache, null); console.log(`${SCRIPT_PREFIX} Force refresh: Cleared countries cache.`); } if (this.cachedCountries) { return this.cachedCountries; } const persistentCache = GM_getValue(this.storageKeys.countriesCache, null); if (persistentCache) { this.cachedCountries = JSON.parse(persistentCache); return this.cachedCountries; } try { const response = await this.fetch({ method: 'GET', url: this.endpoints.countries }); this.cachedCountries = JSON.parse(response.responseText); GM_setValue(this.storageKeys.countriesCache, response.responseText); return this.cachedCountries; } catch (error) { console.error(`${SCRIPT_PREFIX} Failed to fetch and parse countries list.`, error); return null; } }, async getTargetableNTs() { const allTeams = await this.getCountries(); if (!allTeams) { throw new Error("Failed to fetch base country list."); } const defaultExclusions = GM_getValue(this.storageKeys.defaultUncoached, []); const excludedTeams = GM_getValue(this.storageKeys.exclusionList, defaultExclusions); const targetable = allTeams.filter(team => !excludedTeams.includes(team.name)); return targetable; } }; const uiManager = { triggerId: 'mz-challenger', modalId: 'mz-master-modal', logPanelId: 'mz-master-log-panel', progressTrackerId: 'mz-master-progress-tracker', teamTypeSelectorName: 'team-type-selector', injectStyles() { GM_addStyle(` @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;700&display=swap'); :root { --mz-master-navy: #0D253F; --mz-master-black: #010101; --mz-master-pink: #EC407A; --mz-master-text: #E0E0E0; --mz-master-text-dark: #a0a0a0; --mz-master-bg: #151821; --mz-master-bg-dark: #111; } .mz-master-radio-group { display: flex; justify-content: center; gap: 10px; margin: 25px 0; } .mz-master-radio-group input { display: none; } .mz-master-radio-group label { font-size: 14px; font-weight: 500; color: var(--mz-master-text-dark); background-color: #2c2c2c; padding: 10px 20px; border-radius: 8px; border: 2px solid #444; cursor: pointer; transition: all 0.2s ease; } .mz-master-radio-group input:checked + label { color: var(--mz-master-text); border-color: var(--mz-master-pink); box-shadow: 0 0 10px rgba(236, 64, 122, 0.5); } .mz-master-modal-button, #${this.triggerId} { font-family: 'Inter', sans-serif; font-weight: 500; color: var(--mz-master-text); background-image: linear-gradient(to right, var(--mz-master-navy), var(--mz-master-black)); border-radius: 6px; border: none; cursor: pointer; transition: all 0.2s ease; box-shadow: 0 2px 5px rgba(236, 64, 122, 0.3); } #${this.triggerId} { font-size: 12px; padding: 6px 12px; margin-left: 20px; } .mz-master-modal-button { font-size: 13px; padding: 10px 20px; } .mz-master-modal-button.cancel { background-image: linear-gradient(to right, #555, #333); } .mz-master-modal-button.close-btn { margin-top: 20px; } .mz-master-modal-button:hover, #${this.triggerId}:hover { transform: scale(1.05); box-shadow: 0 4px 10px rgba(236, 64, 122, 0.5); } .mz-master-modal-button.cancel:hover { box-shadow: 0 4px 10px rgba(255, 255, 255, 0.2); } #${this.triggerId}:disabled { cursor: not-allowed; background-image: linear-gradient(to right, #333, #222); color: #666; transform: none; box-shadow: none; } .${this.modalId}-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.7); z-index: 9998; display: flex; justify-content: center; align-items: center; opacity: 0; transition: opacity 0.3s ease; } .${this.modalId}-content { position: relative; font-family: 'Inter', sans-serif; background-color: var(--mz-master-navy); color: var(--mz-master-text); padding: 25px; border-radius: 8px; width: 90%; max-width: 600px; box-shadow: 0 0 20px rgba(236, 64, 122, 0.2); border: 1px solid var(--mz-master-pink); transform: scale(0.9); transition: transform 0.3s ease; text-align: center; } .${this.modalId}-content h2 { margin: 0 25px 15px; color: var(--mz-master-pink); font-weight: 700; } .modal-close-btn { position: absolute; top: 10px; right: 15px; background: none; border: none; font-size: 24px; color: var(--mz-master-text-dark); cursor: pointer; transition: color 0.2s, transform 0.2s; } .modal-close-btn:hover { color: var(--mz-master-text); transform: scale(1.2); } .modal-settings-btn { position: absolute; top: 12px; left: 15px; background: none; border: none; font-size: 18px; color: var(--mz-master-text-dark); cursor: pointer; transition: color 0.2s, transform 0.2s; } .modal-settings-btn:hover { color: var(--mz-master-text); transform: rotate(45deg); } .modal-button-container { display: flex; justify-content: center; gap: 15px; margin-top: 25px; } .${this.modalId}-spinner { border: 4px solid #444; border-top: 4px solid var(--mz-master-pink); border-radius: 50%; width: 40px; height: 40px; animation: spin 1s linear infinite; margin: 20px auto; } .modal-plan-info { font-size: 13px; color: var(--mz-master-text-dark); margin-bottom: 15px; border-bottom: 1px solid #444; padding-bottom: 15px; } .settings-search-input { width: 100%; padding: 10px; margin-bottom: 15px; box-sizing: border-box; background-color: var(--mz-master-bg-dark); border: 1px solid #444; border-radius: 4px; color: var(--mz-master-text); font-family: 'Inter', sans-serif; } #${this.progressTrackerId} { font-size: 14px; color: var(--mz-master-text); margin: 20px 0; font-weight: 500; } #${this.logPanelId}, .modal-plan-details, .settings-list { height: 250px; overflow-y: auto; background-color: var(--mz-master-bg-dark); border: 1px solid #333; border-radius: 4px; margin-top: 20px; padding: 10px; text-align: left; font-size: 12px; line-height: 1.6; font-family: 'Menlo', 'Consolas', monospace; } .settings-list-item { display: block; margin-bottom: 5px; cursor: pointer; padding: 2px 5px; } .log-entry { margin-bottom: 3px; } .log-entry-ok { color: #81C784; } .log-entry-error { color: #E57373; font-weight: bold; } .log-entry-info { color: #90A4AE; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } `); }, appendToLog(message, type = 'info') { const logPanel = document.getElementById(this.logPanelId); if (!logPanel) { return; } const entry = document.createElement('div'); entry.textContent = message; entry.className = `log-entry log-entry-${type}`; logPanel.appendChild(entry); logPanel.scrollTop = logPanel.scrollHeight; }, updateProgress(sent, total) { const tracker = document.getElementById(this.progressTrackerId); if (tracker) { tracker.textContent = `Processing challenge ${sent} of ${total}...`; } }, showModal(content, options = {}) { this.closeModal(true); const { title, showSettings, closeOnClickOutside } = options; const overlay = document.createElement('div'); overlay.className = `${this.modalId}-overlay`; if (closeOnClickOutside) { overlay.addEventListener('click', (e) => { if (e.target === overlay) { this.closeModal(); } }); } const contentDiv = document.createElement('div'); contentDiv.className = `${this.modalId}-content`; contentDiv.innerHTML = ` <button title="Close" class="modal-close-btn">×</button> ${showSettings ? `<button title="Settings" class="modal-settings-btn"><i class="fa fa-cog"></i></button>` : ''} ${title ? `<h2>${title}</h2>` : ''} ${content} `; overlay.appendChild(contentDiv); document.body.appendChild(overlay); contentDiv.querySelector('.modal-close-btn').onclick = () => this.closeModal(); if (showSettings) { const settingsBtn = contentDiv.querySelector('.modal-settings-btn'); if (settingsBtn) { settingsBtn.onclick = () => massChallengeSender.showSettings(); } } setTimeout(() => { overlay.style.opacity = '1'; contentDiv.style.transform = 'scale(1)'; }, 10); return contentDiv; }, updateModal(content, options = {}) { const contentDiv = document.querySelector(`.${this.modalId}-content`); if (contentDiv) { const { title, showSettings } = options; const closeBtnHTML = contentDiv.querySelector('.modal-close-btn')?.outerHTML || ''; const titleHTML = title ? `<h2>${title}</h2>` : ''; const settingsBtnHTML = showSettings ? `<button title="Settings" class="modal-settings-btn"><i class="fa fa-cog"></i></button>` : ''; contentDiv.innerHTML = `${closeBtnHTML}${settingsBtnHTML}${titleHTML}${content}`; contentDiv.querySelector('.modal-close-btn').onclick = () => this.closeModal(); if (showSettings) { const settingsBtn = contentDiv.querySelector('.modal-settings-btn'); if (settingsBtn) { settingsBtn.onclick = () => massChallengeSender.showSettings(); } } } }, closeModal(isSilent = false) { const overlay = document.querySelector(`.${this.modalId}-overlay`); if (!overlay) { return; } if (isSilent) { overlay.remove(); } else { overlay.style.opacity = '0'; overlay.querySelector(`.${this.modalId}-content`).style.transform = 'scale(0.9)'; setTimeout(() => { overlay.remove(); this.setButtonState('idle'); }, 300); } }, renderInitialUI(container) { if (document.getElementById(this.triggerId)) { return null; } const button = document.createElement('button'); button.id = this.triggerId; button.textContent = 'Send Lots of Challenges'; const parent = container.parentElement; parent.style.display = 'flex'; parent.style.alignItems = 'center'; parent.appendChild(button); return button; }, setButtonState(state) { const button = document.getElementById(this.triggerId); if (!button) { return; } if (state === 'processing') { button.disabled = true; button.textContent = 'Processing...'; } else { button.disabled = false; button.textContent = 'Send Challenges to Everyone'; } } }; const challengeScheduler = { CHALLENGE_WINDOW_DAYS: 12, generateChallengeDates() { const dates = []; const now = new Date(); for (let i = 1; i <= this.CHALLENGE_WINDOW_DAYS; i++) { const futureDate = new Date(); futureDate.setDate(now.getDate() + i); futureDate.setUTCHours(0, 0, 0, 0); const timestamp = futureDate.getTime() / 1000; dates.push(timestamp); } return dates; }, shuffleArray(array) { let currentIndex = array.length, randomIndex; while (currentIndex !== 0) { randomIndex = Math.floor(Math.random() * currentIndex); currentIndex--; [array[currentIndex], array[randomIndex]] = [array[randomIndex], array[currentIndex]]; } return array; }, createTeamSubgroups(teams, groupCount) { const groups = []; if (groupCount === 0 || teams.length === 0) { return groups; } let startIndex = 0; const baseSize = Math.floor(teams.length / groupCount); const remainder = teams.length % groupCount; for (let i = 0; i < groupCount; i++) { const size = baseSize + (i < remainder ? 1 : 0); groups.push(teams.slice(startIndex, startIndex + size)); startIndex += size; } return groups.filter(g => g.length > 0); }, prepareChallengeMatrix(teams, dates, teamType) { const matrix = []; const GROUPS_PER_DATE_BATCH = 6; const challengeTypes = []; if (teamType === 'senior' || teamType === 'both') { challengeTypes.push('senior'); } if (teamType === 'u21' || teamType === 'both') { challengeTypes.push('u21'); } if (challengeTypes.length === 0) { return []; } const shuffledDates = this.shuffleArray([...dates]); for (let i = 0; i < shuffledDates.length; i += GROUPS_PER_DATE_BATCH) { const dateBatch = shuffledDates.slice(i, i + GROUPS_PER_DATE_BATCH); const shuffledTeams = this.shuffleArray([...teams]); const teamGroups = this.createTeamSubgroups(shuffledTeams, dateBatch.length); dateBatch.forEach((date, j) => { const currentGroup = teamGroups[j]; if (!currentGroup) { return; } currentGroup.forEach(team => { challengeTypes.forEach(type => { matrix.push({ team, type, date, hour: 13, home_away: Math.random() < 0.5 ? 'home' : 'away' }); }); }); }); } return this.shuffleArray(matrix); }, async runRealChallenges(matrix) { const modalContent = uiManager.showModal( `<div class="${uiManager.modalId}-spinner"></div><div id="${uiManager.progressTrackerId}"></div><div id="${uiManager.logPanelId}"></div>`, { closeOnClickOutside: false } ); let sentCount = 0; const totalChallenges = matrix.length; uiManager.updateProgress(sentCount, totalChallenges); const challengePromises = matrix.map(challenge => { const { team, type, date, hour, home_away } = challenge; const formattedDate = new Date(date * 1000).toLocaleDateString("en-CA", { timeZone: 'UTC' }); const formData = new URLSearchParams(); formData.append(home_away === 'home' ? 'date_home' : 'date_away', `${date},${hour}`); formData.append('tactic_home', 'a'); formData.append('tactic_away', 'a'); const url = new URL(dataManager.endpoints.createChallenge); const teamId = type === 'senior' ? team.ntid : team.u21ntid; url.searchParams.append('tid', teamId); url.searchParams.append('national', type); url.searchParams.append('type', type); url.searchParams.append('sport', 'soccer'); return dataManager.fetch({ method: 'POST', url: url.toString(), data: formData.toString(), headers: { "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8" } }).then(response => { if (response.status >= 200 && response.status < 300) { uiManager.appendToLog(`[SUCCESS] Sent [${type.toUpperCase()}] challenge to ${team.name} on ${formattedDate}.`, 'ok'); return { status: 'fulfilled' }; } else { throw new Error(`Status ${response.status}`); } }).catch(error => { uiManager.appendToLog(`[FAILED] Sending [${type.toUpperCase()}] challenge to ${team.name}. Reason: ${error.message || 'Network Error'}`, 'error'); return { status: 'rejected' }; }).finally(() => { sentCount++; uiManager.updateProgress(sentCount, totalChallenges); }); }); await Promise.all(challengePromises); modalContent.querySelector(`.${uiManager.modalId}-spinner`).style.display = 'none'; uiManager.appendToLog('Done.', 'info'); const closeButton = document.createElement('button'); closeButton.textContent = 'Close'; closeButton.className = 'mz-master-modal-button close-btn'; closeButton.onclick = () => location.reload(); modalContent.appendChild(closeButton); } }; const massChallengeSender = { challengeTabSelector: 'a[href*="sub=challenge"]', dropdownSelector: 'select[name="tid"]', showSettings() { const coachedTeams = GM_getValue(dataManager.storageKeys.defaultCoached, []); const uncoachedTeams = GM_getValue(dataManager.storageKeys.defaultUncoached, []); const allKnownTeams = [...coachedTeams, ...uncoachedTeams]; const excludedTeams = GM_getValue(dataManager.storageKeys.exclusionList, uncoachedTeams); const searchInputHTML = `<input type="text" id="settings-search" class="settings-search-input" placeholder="Filter...">`; const settingsListHTML = `<div class="settings-list">${allKnownTeams.sort().map(teamName => ` <label class="settings-list-item"> <input type="checkbox" name="nt-exclusion" value="${teamName}" ${!excludedTeams.includes(teamName) ? 'checked' : ''}> ${teamName} </label> `).join('')}</div>`; const buttonsHTML = ` <div class="modal-button-container"> <button id="modal-save-settings-btn" class="mz-master-modal-button">Save</button> </div> `; uiManager.showModal(`${searchInputHTML}${settingsListHTML}${buttonsHTML}`, { title: "Manage Targetable Countries (Uncheck Undesirable Countries)", closeOnClickOutside: true, showSettings: true }); const searchInput = document.getElementById('settings-search'); searchInput.oninput = () => { const filter = searchInput.value.toLowerCase(); document.querySelectorAll('.settings-list-item').forEach(item => { const text = item.textContent.toLowerCase(); item.style.display = text.includes(filter) ? '' : 'none'; }); }; document.getElementById('modal-save-settings-btn').onclick = () => { const newExcluded = [...document.querySelectorAll('input[name="nt-exclusion"]:not(:checked)')].map(cb => cb.value); GM_setValue(dataManager.storageKeys.exclusionList, newExcluded); this.startW(); }; }, async generateAndShowChallengePlan(teamType) { uiManager.setButtonState('processing'); uiManager.showModal(`<div class="${uiManager.modalId}-spinner"></div><p>Please wait...</p>`, { showSettings: true }); try { const loadingDelay = new Promise(resolve => setTimeout(resolve, 1000)); const dataFetch = dataManager.getTargetableNTs(); const [_, teams] = await Promise.all([loadingDelay, dataFetch]); const dates = challengeScheduler.generateChallengeDates(); if (!teams || teams.length === 0) { throw new Error("No targetable NTs found. Check settings."); } if (!dates || dates.length === 0) { throw new Error("No available challenge dates found."); } const nowString = new Date().toLocaleString('en-GB', { timeZone: 'Europe/Stockholm', year: 'numeric', month: 'long', day: 'numeric', hour: '2-digit', minute: '2-digit', second: '2-digit' }).replace(' at', ','); const matrix = challengeScheduler.prepareChallengeMatrix(teams, dates, teamType); const summary = `Reference Time (Sweden): <b>${nowString}</b>.<br><b>${matrix.length} challenges</b> will be sent to <b>${teams.length}</b> targetable NTs.`; const planDetails = `<div class="modal-plan-details">${matrix.map(c => `<div>[${c.type.toUpperCase()}] to <b>${c.team.name}</b> on ${new Date(c.date*1000).toLocaleDateString("en-CA",{timeZone: 'UTC'})} at ${c.hour}:00</div>`).join('')}</div>`; const buttonsHTML = ` <div class="modal-button-container"> <button id="modal-cancel-btn" class="mz-master-modal-button cancel">Cancel</button> <button id="modal-confirm-btn" class="mz-master-modal-button">Confirm & Send</button> </div> `; uiManager.updateModal(`<div class="modal-plan-info">${summary}</div>${planDetails}${buttonsHTML}`, { title: "", showSettings: true }); document.getElementById('modal-cancel-btn').onclick = () => uiManager.closeModal(); document.getElementById('modal-confirm-btn').onclick = () => challengeScheduler.runRealChallenges(matrix); } catch (error) { console.error(`${SCRIPT_PREFIX} Error during plan generation:`, error); uiManager.showModal(`<p>${error.message}</p>`, { title: 'Error' }); uiManager.setButtonState('idle'); } }, startW() { const types = [{ value: 'senior', text: 'Senior' }, { value: 'u21', text: 'U21' }, { value: 'both', text: 'Both' }]; const radioHTML = types.map((type) => ` <input type="radio" id="radio-${type.value}" name="${uiManager.teamTypeSelectorName}" value="${type.value}" ${type.value === 'senior' ? 'checked' : ''}> <label for="radio-${type.value}">${type.text}</label> `).join(''); const modalContent = `<div class="mz-master-radio-group">${radioHTML}</div><button id="modal-continue-btn" class="mz-master-modal-button">Continue</button>`; uiManager.showModal(modalContent, { showSettings: true, closeOnClickOutside: true, title: 'Select Type' }); document.getElementById('modal-continue-btn').addEventListener('click', () => { const selectedType = document.querySelector(`input[name="${uiManager.teamTypeSelectorName}"]:checked`).value; this.generateAndShowChallengePlan(selectedType); }); }, watchTab() { const persistentObserver = new MutationObserver(() => { const dropdown = document.querySelector(this.dropdownSelector); if (dropdown && dropdown.parentElement && !document.getElementById(uiManager.triggerId)) { const button = uiManager.renderInitialUI(dropdown.parentElement); if (button) { button.addEventListener('click', () => this.startW()); } } }); persistentObserver.observe(document.body, { childList: true, subtree: true }); }, initialize() { console.log(`${SCRIPT_PREFIX} Initializing...`); dataManager.initializeDefaultLists(); uiManager.injectStyles(); this.watchTab(); } }; const incomingChallengeNotifier = { CONFIG: { USERNAME: '', COUNTRY_NAME: '', COUNTRY_ID: null, SENIOR_NT_ID: null, U21_NT_ID: null }, NOTIFICATION_SETTINGS: { duration: 10000, close: true, gravity: "bottom", position: "left", style: { background: "#0D0D0D", color: "#00FF41", border: "1px solid #00FF41", borderRadius: "0px", boxShadow: "0 0 15px #00FF41", fontFamily: "monospace" } }, async initializeConfig() { const u = this.CONFIG.USERNAME || this.getCurrentUsername(); if (!u) { return false; } try { const userResponse = await dataManager.fetch({ method: 'GET', url: dataManager.endpoints.userData(u) }); const xml = new DOMParser().parseFromString(userResponse.responseText, 'text/xml'); const countryCode = xml.querySelector('UserData')?.getAttribute('countryShortname'); if (!countryCode) { return false; } const countries = await dataManager.getCountries(); if (!countries) { return false; } const countryData = countries.find(c => c.code === countryCode); if (!countryData) { return false; } Object.assign(this.CONFIG, { USERNAME: u, COUNTRY_NAME: countryData.name, COUNTRY_ID: countryData.cid, SENIOR_NT_ID: countryData.ntid, U21_NT_ID: countryData.u21ntid }); return true; } catch (e) { return false; } }, getCurrentUsername() { const el = document.querySelector('#header-username'); return el ? el.textContent.trim() : ''; }, async verifyNCStatus() { try { const url = dataManager.endpoints.nationalTeam(this.CONFIG.SENIOR_NT_ID, this.CONFIG.COUNTRY_ID, 'national_team'); const response = await dataManager.fetch({ method: 'GET', url: url }); const doc = new DOMParser().parseFromString(response.responseText, 'text/html'); return Array.from(doc.querySelectorAll('a[href*="/?p=profile"]')).some(link => link.textContent.trim() === this.CONFIG.USERNAME); } catch (e) { return false; } }, async processIncomingChallenges(cat, tid) { const p = new URLSearchParams({ ntid: tid, cid: this.CONFIG.COUNTRY_ID, type: cat === 'U21' ? 'national_team_u21' : 'national_team', sport: 'soccer' }); try { const response = await dataManager.fetch({ method: 'GET', url: `${dataManager.endpoints.challenges}&${p.toString()}` }); const doc = new DOMParser().parseFromString(response.responseText, 'text/html'); const tab = doc.querySelector('#matches_in'); if (!tab) { return; } tab.querySelectorAll('tbody tr').forEach(row => { const opp = row.querySelector('td:nth-child(1) a'); const tim = row.querySelector('td:nth-child(3)'); if (!opp || !tim) { return; } const oppName = opp.textContent.trim(); const timeText = tim.textContent.trim(); const sk = `challenge_${oppName}_${cat}_${timeText}`; if (!GM_getValue(sk, false)) { Toastify({ text: `Incoming NT challenge from ${oppName} (${cat})!`, ...this.NOTIFICATION_SETTINGS }).showToast(); GM_setValue(sk, true); } }); } catch (e) { return; } }, async initialize() { GM_addStyle(GM_getResourceText('TOASTIFY_CSS')); const ok = await this.initializeConfig(); if (!ok) { return; } const isNC = await this.verifyNCStatus(); if (!isNC) { return; } if (this.CONFIG.SENIOR_NT_ID) { this.processIncomingChallenges('SENIOR', this.CONFIG.SENIOR_NT_ID); } if (this.CONFIG.U21_NT_ID) { this.processIncomingChallenges('U21', this.CONFIG.U21_NT_ID); } } }; massChallengeSender.initialize(); incomingChallengeNotifier.initialize(); })();