您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Greasy Fork is available in English.
Monitors transfers for players from a specific country
当前为
// ==UserScript== // @name MZ - Country Transfer Monitor // @namespace douglaskampl // @version 1.0 // @description Monitors transfers for players from a specific country // @author Douglas // @match https://www.managerzone.com/* // @icon https://www.google.com/s2/favicons?sz=64&domain=managerzone.com // @grant GM_setValue // @grant GM_getValue // @grant GM_addStyle // @grant GM_getResourceText // @require https://cdn.jsdelivr.net/npm/sweetalert2@11 // @resource SWEETALERT2_CSS https://cdn.jsdelivr.net/npm/sweetalert2@11/dist/sweetalert2.min.css // @run-at document-idle // @license MIT // ==/UserScript== (function () { 'use strict'; GM_addStyle(GM_getResourceText('SWEETALERT2_CSS')); GM_addStyle(` #country-monitor-wrapper { display: flex; align-items: center; margin-left: 15px; cursor: pointer; position: relative; } #country-monitor-flag { width: 22px; height: 17px; object-fit: contain; vertical-align: middle; border: 1px solid #ddd; border-radius: 2px; transition: transform 0.2s ease; } #country-monitor-flag:hover { transform: scale(1.1); } #country-monitor-name { margin-left: 5px; font-size: 12px; color: #666; display: none; } #country-selector { position: absolute; top: 100%; right: 0; z-index: 9999; background: #2c3e50; color: #fff; border: 1px solid #34495e; border-radius: 4px; box-shadow: 0 4px 15px rgba(0,0,0,0.2); width: 300px; max-height: 400px; overflow-y: auto; padding: 10px; display: none; opacity: 0; transform: translateY(-10px); transition: opacity 0.3s ease, transform 0.3s ease; } #country-selector.visible { opacity: 1; transform: translateY(0); } #country-selector::-webkit-scrollbar { width: 8px; } #country-selector::-webkit-scrollbar-track { background: #34495e; border-radius: 4px; } #country-selector::-webkit-scrollbar-thumb { background: #7f8c8d; border-radius: 4px; } #country-selector::-webkit-scrollbar-thumb:hover { background: #95a5a6; } #country-search { width: 100%; padding: 8px; margin-bottom: 10px; border: 1px solid #34495e; background-color: #34495e; color: white; border-radius: 4px; } #country-search::placeholder { color: #bdc3c7; } .country-option { display: flex; align-items: center; padding: 6px 10px; cursor: pointer; border-radius: 4px; color: #ecf0f1; } .country-option:hover { background: #34495e; } .country-option img { width: 24px; height: 16px; margin-right: 10px; object-fit: contain; border: 1px solid #34495e; } .country-option.selected { background: #3498db; } `); const CONFIG = { CHECK_INTERVAL_HOURS: 24, TOAST_DURATION: 5000, DEFAULT_COUNTRY_NAME: 'Country', DEFAULT_COUNTRY_CID: null, COUNTRIES_JSON_URL: 'https://pub-02de1c06eac643f992bb26daeae5c7a0.r2.dev/json/countries.json' }; let countries = []; let selectedCountry = { cid: GM_getValue('selectedCountryCid', CONFIG.DEFAULT_COUNTRY_CID), name: GM_getValue('selectedCountryName', CONFIG.DEFAULT_COUNTRY_NAME), code: GM_getValue('selectedCountryCode', '') }; let knownPlayers = GM_getValue('knownPlayers', {}); let lastChecked = GM_getValue('lastChecked', 0); function getFlagUrl(code) { const upperCode = (code || '').toUpperCase(); if (upperCode === 'SC') { return 'https://cdn.jsdelivr.net/gh/lipis/flag-icons/flags/4x3/gb-sct.svg'; } else if (upperCode === 'WL') { return 'https://cdn.jsdelivr.net/gh/lipis/flag-icons/flags/4x3/gb-wls.svg'; } else if (upperCode === 'NI') { return 'https://cdn.jsdelivr.net/gh/lipis/flag-icons/flags/4x3/gb-nir.svg'; } else if (upperCode === 'EN') { return 'https://cdn.jsdelivr.net/gh/lipis/flag-icons/flags/4x3/gb-eng.svg'; } else if (upperCode === 'DC') { return 'https://placehold.co/16x12/gray/white?text=MZ'; } return `https://flagcdn.com/16x12/${(code || '').toLowerCase()}.png`; } function cleanupKnownPlayers() { const now = Date.now(); const threeDaysInMs = 3 * 24 * 60 * 60 * 1000; let modified = false; Object.keys(knownPlayers).forEach(playerId => { if (now - knownPlayers[playerId].firstSeen > threeDaysInMs) { delete knownPlayers[playerId]; modified = true; } }); if (modified) { GM_setValue('knownPlayers', knownPlayers); } } function fetchCountries() { fetch(CONFIG.COUNTRIES_JSON_URL) .then(response => response.json()) .then(data => { countries = data; countries.sort((a, b) => a.name.localeCompare(b.name)); buildUI(); }) .catch(error => { console.error('Error fetching countries:', error); }); } function buildUI() { if (!isTransferPage()) return; const searchButton = document.getElementById('tds'); if (!searchButton) return; const monitorWrapper = document.createElement('div'); monitorWrapper.id = 'country-monitor-wrapper'; monitorWrapper.style.display = 'inline-block'; monitorWrapper.style.marginLeft = '10px'; const flagImg = document.createElement('img'); flagImg.id = 'country-monitor-flag'; flagImg.src = selectedCountry.code ? getFlagUrl(selectedCountry.code) : 'https://placehold.co/16x12/gray/white?text=?'; flagImg.alt = selectedCountry.name; flagImg.title = "Monitor players"; const countryName = document.createElement('span'); countryName.id = 'country-monitor-name'; countryName.textContent = selectedCountry.name; monitorWrapper.appendChild(flagImg); monitorWrapper.appendChild(countryName); const selector = document.createElement('div'); selector.id = 'country-selector'; const search = document.createElement('input'); search.id = 'country-search'; search.type = 'text'; search.placeholder = 'Search country...'; selector.appendChild(search); countries.forEach(country => { const option = document.createElement('div'); option.className = 'country-option'; if (selectedCountry.cid === country.cid) { option.classList.add('selected'); } const optionImg = document.createElement('img'); optionImg.src = getFlagUrl(country.code); optionImg.alt = country.name; const optionName = document.createElement('span'); optionName.textContent = country.name; option.appendChild(optionImg); option.appendChild(optionName); option.addEventListener('click', () => { selectCountry(country); hideSelector(selector); }); selector.appendChild(option); }); search.addEventListener('input', function() { const query = this.value.toLowerCase(); Array.from(selector.querySelectorAll('.country-option')).forEach(option => { const name = option.querySelector('span').textContent.toLowerCase(); option.style.display = name.includes(query) ? 'flex' : 'none'; }); }); monitorWrapper.appendChild(selector); monitorWrapper.addEventListener('click', (e) => { if (e.target.id !== 'country-search') { if (selector.classList.contains('visible')) { hideSelector(selector); } else { showSelector(selector); search.focus(); } } }); document.addEventListener('click', (e) => { if (!monitorWrapper.contains(e.target)) { hideSelector(selector); } }); searchButton.insertAdjacentElement('afterend', monitorWrapper); if (selectedCountry.cid) { checkTransferMarket(false); } } function showSelector(selector) { selector.style.display = 'block'; selector.offsetHeight; selector.classList.add('visible'); } function hideSelector(selector) { selector.classList.remove('visible'); setTimeout(() => { if (!selector.classList.contains('visible')) { selector.style.display = 'none'; } }, 300); } function selectCountry(country) { selectedCountry = { cid: country.cid, name: country.name, code: country.code }; GM_setValue('selectedCountryCid', country.cid); GM_setValue('selectedCountryName', country.name); GM_setValue('selectedCountryCode', country.code); const flagImg = document.getElementById('country-monitor-flag'); if (flagImg) { flagImg.src = getFlagUrl(country.code); flagImg.alt = country.name; flagImg.title = "Monitor players from: " + country.name; } const countryName = document.getElementById('country-monitor-name'); if (countryName) { countryName.textContent = country.name; } checkTransferMarket(true); } function isTransferPage() { return window.location.href.includes('p=transfer'); } function checkTransferMarket(forced = false) { if (!selectedCountry.cid) return; const now = Date.now(); const checkIntervalMs = CONFIG.CHECK_INTERVAL_HOURS * 60 * 60 * 1000; if (!forced && (now - lastChecked < checkIntervalMs)) { return; } cleanupKnownPlayers(); fetch(`https://www.managerzone.com/ajax.php?p=transfer&sub=transfer-search&sport=soccer&issearch=true&u=&nationality=${selectedCountry.cid}&deadline=0&category=&valuea=&valueb=&bida=&bidb=&agea=19&ageb=37&birth_season_low=56&birth_season_high=74&tot_low=0&tot_high=110&s0a=0&s0b=10&s1a=0&s1b=10&s2a=0&s2b=10&s3a=0&s3b=10&s4a=0&s4b=10&s5a=0&s5b=10&s6a=0&s6b=10&s7a=0&s7b=10&s8a=0&s8b=10&s9a=0&s9b=10&s10a=0&s10b=10&s11a=0&s11b=10&s12a=0&s12b=10`) .then(response => response.json()) .then(data => { lastChecked = now; GM_setValue('lastChecked', lastChecked); if (data.totalHits && parseInt(data.totalHits) > 0) { processPlayers(data); } }) .catch(error => {}); } function processPlayers(data) { if (!data.players || data.players.includes("No players found")) { return; } const playersData = data.players; const pidRegex = /href=["'].*?pid=(\d+)/g; let pidMatches = [...playersData.matchAll(pidRegex)]; const playerIdSpanRegex = /player_id_(\d+)/g; let playerIdSpanMatches = [...playersData.matchAll(playerIdSpanRegex)]; const idRegex = /id: (\d+)/g; let idMatches = [...playersData.matchAll(idRegex)]; let allPlayerIds = new Set([ ...pidMatches.map(match => match[1]), ...playerIdSpanMatches.map(match => match[1]), ...idMatches.map(match => match[1]) ]); const playerNameRegex1 = /"([^"]+)"\s+([^<]+)<\/span>/g; const playerNameRegex2 = /player_name">([^<]+)<\/span>/g; let nameMatches1 = [...playersData.matchAll(playerNameRegex1)]; let nameMatches2 = [...playersData.matchAll(playerNameRegex2)]; let players = []; if (nameMatches1.length > 0) { nameMatches1.forEach((match, index) => { if (index < allPlayerIds.size) { const playerName = match[2] ? match[2].trim() : match[1].trim(); const playerId = Array.from(allPlayerIds)[index]; players.push({ id: playerId, name: playerName }); } }); } else if (nameMatches2.length > 0) { nameMatches2.forEach((match, index) => { if (index < allPlayerIds.size) { const playerName = match[1].trim(); const playerId = Array.from(allPlayerIds)[index]; players.push({ id: playerId, name: playerName }); } }); } else if (allPlayerIds.size > 0) { Array.from(allPlayerIds).forEach(id => { players.push({ id: id, name: id }); }); } if (players.length === 0) { try { const jsonString = JSON.stringify(data); const jsonIdRegex = /\d{9}/g; const jsonIdMatches = [...new Set([...jsonString.matchAll(jsonIdRegex)].map(m => m[0]))]; jsonIdMatches.forEach(id => { players.push({ id: id, name: id }); }); } catch (e) {} } const newPlayers = players.filter(player => !knownPlayers[player.id]); if (newPlayers.length > 0) { showPlayerNotification(newPlayers); newPlayers.forEach(player => { knownPlayers[player.id] = { k: player.name, firstSeen: Date.now() }; }); GM_setValue('knownPlayers', knownPlayers); } } function showPlayerNotification(players) { const htmlContent = `<p>New players from ${selectedCountry.name} found (${players.length}). Click "View" to see them on the transfer market.</p>`; Swal.fire({ title: 'New Players!', html: htmlContent, icon: 'info', showCancelButton: true, confirmButtonText: 'View', cancelButtonText: 'Close' }).then((result) => { if (result.isConfirmed) { window.location.href = `https://www.managerzone.com/?p=transfer&sub=search&sport=soccer&nationality=${selectedCountry.cid}`; } }); } fetchCountries(); setInterval(checkTransferMarket, 30 * 60 * 1000); })();