Survev Quick Stats

Custom stats panel with stats + match history.

Você precisará instalar uma extensão como Tampermonkey, Greasemonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Userscripts para instalar este script.

Você precisará instalar uma extensão como o Tampermonkey para instalar este script.

Você precisará instalar um gerenciador de scripts de usuário para instalar este script.

(Eu já tenho um gerenciador de scripts de usuário, me deixe instalá-lo!)

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

(Eu já possuo um gerenciador de estilos de usuário, me deixar fazer a instalação!)

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

})();