Survev Quick Stats

Custom stats panel with stats + match history.

Vous devrez installer une extension telle que Tampermonkey, Greasemonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Userscripts pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension de gestionnaire de script utilisateur pour installer ce script.

(J'ai déjà un gestionnaire de scripts utilisateur, laissez-moi l'installer !)

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

(J'ai déjà un gestionnaire de style utilisateur, laissez-moi l'installer!)

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

})();