Survev Quick Stats

Custom stats panel with stats + match history.

Dovrai installare un'estensione come Tampermonkey, Greasemonkey o Violentmonkey per installare questo script.

You will need to install an extension such as Tampermonkey to install this script.

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Userscripts per installare questo script.

Dovrai installare un'estensione come ad esempio Tampermonkey per installare questo script.

Dovrai installare un gestore di script utente per installare questo script.

(Ho già un gestore di script utente, lasciamelo installare!)

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

(Ho già un gestore di stile utente, lasciamelo installare!)

// ==UserScript==
// @name         Survev Quick Stats
// @namespace    https://survev.io/
// @author       piesimp
// @version      1.0
// @description  Custom stats panel with stats + match history.
// @match        https://survev.io/*
// @run-at       document-end
// @grant        GM_xmlhttpRequest
// @connect      api.survev.io
// ==/UserScript==

(function () {
    'use strict';


    function modifyLeftColumn() {
        const socialBlock = document.getElementById("social-share-block");
        if (socialBlock) socialBlock.remove();

        const kofiLink = document.querySelector('#ad-block-left a[href="https://ko-fi.com/survev"]');
        if (kofiLink) kofiLink.remove();

        const survivShirts = document.querySelector('#ad-block-left .surviv-shirts');
        if (survivShirts) survivShirts.remove();
    }


    function buildEmptyStatsPanel() {

        const adBlock = document.getElementById("ad-block-left");
        const newsBlock = document.getElementById("news-block");
        if (!adBlock) return;

        adBlock.innerHTML = "";

        const container = document.createElement("div");
        container.id = "custom-stats-panel";

        container.innerHTML = `
            <div class="stats-header">
    <div class="stats-title">Your Stats</div>
    <div class="refresh-btn" title="Refresh Stats">⟳</div>
</div>

            <div class="stats-row">
                <div class="stat">
                    <div class="stat-label">Wins</div>
                    <div class="stat-value" id="stat-wins"></div>
                </div>
                <div class="stat">
                    <div class="stat-label">Kills</div>
                    <div class="stat-value" id="stat-kills"></div>
                </div>
                <div class="stat">
                    <div class="stat-label">Games</div>
                    <div class="stat-value" id="stat-games"></div>
                </div>
                <div class="stat">
                    <div class="stat-label">K/G</div>
                    <div class="stat-value" id="stat-kg"></div>
                </div>
            </div>

            <div class="recent-title">Recent Matches</div>

            <div class="recent-list">
                <div class="recent-item">
                    <div class="recent-mode"></div>
                    <div class="recent-time"></div>
                </div>
                <div class="recent-item">
                    <div class="recent-mode"></div>
                    <div class="recent-time"></div>
                </div>
                <div class="recent-item">
                    <div class="recent-mode"></div>
                    <div class="recent-time"></div>
                </div>
            </div>

            <div class="leaderboard-link">Global Leaderboard →</div>
        `;

        adBlock.appendChild(container);
        const refreshBtn = container.querySelector(".refresh-btn");

refreshBtn.onclick = () => {
    refreshBtn.classList.add("spinning");

    const username = window.currentSurvevPlayer;
    if (!username) return;

    fetchAndFillStats(username);
    fetchMatchHistory(username);

    setTimeout(() => {
        refreshBtn.classList.remove("spinning");
    }, 800);
};

        if (newsBlock) {
            const h = newsBlock.offsetHeight;
            adBlock.style.minHeight = (h + 3.8) + "px";
            container.style.minHeight = (h + 140) + "px";
        }

        container.querySelector(".leaderboard-link").onclick = () => {
            window.location.href = "https://survev.io/stats/";
        };
    }


    function fetchAndFillStats(username) {

        GM_xmlhttpRequest({
            method: "POST",
            url: "https://api.survev.io/api/user_stats",
            headers: {
                "Content-Type": "application/json; charset=UTF-8",
                "Accept": "*/*",
                "Origin": "https://survev.io",
                "Referer": "https://survev.io/"
            },
            data: JSON.stringify({
                slug: username,
                interval: "alltime",
                mapIdFilter: "-1"
            }),
            onload: function (response) {
                const data = JSON.parse(response.responseText);
                if (!data || data.banned) return;

                document.getElementById("stat-wins").textContent = data.wins || "";
                document.getElementById("stat-kills").textContent = data.kills || "";
                document.getElementById("stat-games").textContent = data.games || "";
                document.getElementById("stat-kg").textContent = data.kpg || "";
            }
        });
    }



    function fetchMatchHistory(username) {

        GM_xmlhttpRequest({
            method: "POST",
            url: "https://api.survev.io/api/match_history",
            headers: {
                "Content-Type": "application/json; charset=UTF-8",
                "Accept": "*/*",
                "Origin": "https://survev.io",
                "Referer": "https://survev.io/"
            },
            data: JSON.stringify({
                slug: username,
                offset: 0,
                count: 3,
                teamModeFilter: 7
            }),
            onload: function (response) {
                const matches = JSON.parse(response.responseText);
                if (!Array.isArray(matches)) return;

                const items = document.querySelectorAll(".recent-item");

                matches.slice(0, 3).forEach((match, i) => {

                    const modeMap = {
                        1: "Solo",
                        2: "Duo",
                        3: "Trio",
                        4: "Squad"
                    };

                    const mode = modeMap[match.team_count] || "Mode";
                    const placement = `#${match.rank}`;

                    const endTime = new Date(match.end_time);
                    const now = new Date();
                    const diff = Math.floor((now - endTime) / 1000);

                    let timeAgo;
                    if (diff < 60) timeAgo = `${diff}s ago`;
                    else if (diff < 3600) timeAgo = `${Math.floor(diff / 60)}m ago`;
                    else if (diff < 86400) timeAgo = `${Math.floor(diff / 3600)}h ago`;
                    else timeAgo = `${Math.floor(diff / 86400)}d ago`;

                    const modeDiv = items[i].querySelector(".recent-mode");
                    const timeDiv = items[i].querySelector(".recent-time");

                    modeDiv.textContent = `${mode} ${placement}`;
                    timeDiv.textContent = timeAgo;
                });
            }
        });
    }


    function detectAccountAndLoadStats() {

        const nameElement = document.getElementById("account-player-name");
        const adBlock = document.getElementById("ad-block-left");
        if (!nameElement || !adBlock) return;

        const rawName = nameElement.textContent.trim().toLowerCase();
const playerName = rawName.replace(/\s+/g, "-");

        const notLoggedInTexts = ["log in", "create account", "sign in", "guest"];
        const isLoggedOut = notLoggedInTexts.some(text =>
            playerName.includes(text)
        );

        if (isLoggedOut) {
            adBlock.innerHTML = `
                <div style="color:red; font-weight:bold; padding:15px; text-align:center;">
                    You must be logged in!
                </div>
            `;
            return;
        }
        window.currentSurvevPlayer = playerName;

        buildEmptyStatsPanel();
        fetchAndFillStats(playerName);
        fetchMatchHistory(playerName);
    }

    function injectStyles() {
        const style = document.createElement("style");
        style.textContent = `
        #custom-stats-panel {
            border-radius: 10px;
            padding: 15px;
            color: white;
            font-family: Arial, sans-serif;
            width: 100%;
            box-sizing: border-box;
        }

        .stats-title {
            font-weight: bold;
            color: #f7c948;
            margin-bottom: 10px;
        }

        .stats-row {
            display: flex;
            justify-content: space-between;
            margin-bottom: 15px;
        }

        .stat { text-align: center; flex: 1; }

        .stat-label { font-size: 12px; opacity: 0.8; }

        .stat-value {
            font-size: 18px;
            color: #00e5ff;
            margin-top: 4px;
            min-height: 20px;
        }

        .recent-title {
            margin-top: 10px;
            margin-bottom: 8px;
            font-weight: bold;
        }

        .recent-item {
            display: flex;
            justify-content: space-between;
            background: rgba(0,0,0,0.3);
            padding: 6px 8px;
            border-radius: 6px;
            margin-bottom: 6px;
            font-size: 13px;
        }

        .recent-mode { font-weight: bold; }
        .recent-time { opacity: 0.7; }

        .leaderboard-link {
            text-align: center;
            color: #00e5ff;
            cursor: pointer;
            font-size: 13px;
        }
        .stats-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 10px;
}

.refresh-btn {
    cursor: pointer;
    font-size: 16px;
    opacity: 0.7;
    transition: 0.2s ease;
}

.refresh-btn:hover {
    opacity: 1;
    transform: scale(1.1);
}

.refresh-btn.spinning {
    animation: spin 0.6s linear infinite;
}

@keyframes spin {
    from { transform: rotate(0deg); }
    to { transform: rotate(360deg); }
}
        `;
        document.head.appendChild(style);
    }



    window.addEventListener("load", function () {
        setTimeout(() => {
            modifyLeftColumn();
            injectStyles();
            detectAccountAndLoadStats();
        }, 1);
    });

})();