MZ - Country Transfer Monitor

Monitors transfers for players from a specific country

目前為 2025-02-25 提交的版本,檢視 最新版本

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

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址