Bitcointalk Board Stats

Statistiche board Bitcointalk con spinner "Dot Wave", sintesi testuale, distribuzione post, e utenti con maggior impatto

当前为 2025-09-12 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

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

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Bitcointalk Board Stats 
// @namespace    http://tampermonkey.net/
// @version      1.0.0
// @description  Statistiche board Bitcointalk con spinner "Dot Wave", sintesi testuale, distribuzione post, e utenti con maggior impatto
// @author       Ace
// @match        https://bitcointalk.org/*
// @grant        GM_xmlhttpRequest
// @grant        fetch
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // Funzione per recuperare le child boards
    async function fetchChildBoards(boardId) {
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: "GET",
                url: `https://api.ninjastic.space/boards`,
                onload: function(response) {
                    try {
                        const data = JSON.parse(response.responseText);
                        if (data.result !== "success") {
                            throw new Error("Errore API: " + (data.message || "Dati non validi"));
                        }
                        const childBoards = [];
                        const findChildren = (boards) => {
                            boards.forEach(board => {
                                if (board.parent === parseInt(boardId)) {
                                    childBoards.push(board.value);
                                }
                                if (board.children && board.children.length > 0) {
                                    findChildren(board.children);
                                }
                            });
                        };
                        findChildren(data.data);
                        resolve(childBoards);
                    } catch (err) {
                        console.error("Errore parsing child boards:", err);
                        reject(err);
                    }
                },
                onerror: function(error) {
                    console.error("Errore richiesta child boards:", error);
                    reject(error);
                }
            });
        });
    }

    // Funzione per recuperare i dati Merit da più board
    async function fetchMeritBatchData(boardIds, startDate, endDate) {
        let allSenders = {};
        let allReceivers = {};

        for (const boardId of boardIds) {
            try {
                const response = await new Promise((resolve, reject) => {
                    GM_xmlhttpRequest({
                        method: "GET",
                        url: `https://beta.ninjastic.space/trpc/posts.posts_per_day_histogram,merits.merits_per_day_histogram,merits.top_merit_users,posts.top_users_by_post_count,posts.count_unique_users?batch=1&input=%7B%220%22%3A%7B%22date_min%22%3A%22${startDate}%22%2C%22date_max%22%3A%22${endDate}%22%2C%22board_id%22%3A${boardId}%2C%22interval%22%3A%221d%22%7D%2C%221%22%3A%7B%22date_min%22%3A%22${startDate}%22%2C%22date_max%22%3A%22${endDate}%22%2C%22board_id%22%3A${boardId}%2C%22interval%22%3A%221d%22%7D%2C%222%22%3A%7B%22date_min%22%3A%22${startDate}%22%2C%22date_max%22%3A%22${endDate}%22%2C%22board_id%22%3A${boardId}%7D%2C%223%22%3A%7B%22date_min%22%3A%22${startDate}%22%2C%22date_max%22%3A%22${endDate}%22%2C%22board_id%22%3A${boardId}%7D%2C%224%22%3A%7B%22date_min%22%3A%22${startDate}%22%2C%22date_max%22%3A%22${endDate}%22%2C%22board_id%22%3A${boardId}%7D%7D`,
                        onload: function(response) {
                            try {
                                const data = JSON.parse(response.responseText);
                                resolve(data);
                            } catch (err) {
                                console.error("Errore parsing dati Merit per board " + boardId + ":", err);
                                reject(err);
                            }
                        },
                        onerror: function(error) {
                            console.error("Errore richiesta Merit per board " + boardId + ":", error);
                            reject(error);
                        }
                    });
                });

                // Estrai i dati corretti dalla risposta batch
                const topSenders = response[2].result.data.top_senders || [];
                const topReceivers = response[2].result.data.top_receivers || [];

                // Aggrega i dati Merit
                topSenders.forEach(user => {
                    if (!allSenders[user.user_uid]) {
                        allSenders[user.user_uid] = { sum: 0, user: user.user };
                    }
                    allSenders[user.user_uid].sum += user.sum;
                });
                topReceivers.forEach(user => {
                    if (!allReceivers[user.user_uid]) {
                        allReceivers[user.user_uid] = { sum: 0, user: user.user };
                    }
                    allReceivers[user.user_uid].sum += user.sum;
                });

            } catch (err) {
                console.error("Errore durante il recupero dei dati Merit per board " + boardId + ":", err);
            }
        }

        // Ordina e restituisce le top 10
        const topSenders = Object.entries(allSenders)
            .sort((a, b) => b[1].sum - a[1].sum)
            .slice(0, 10)
            .map(([uid, data]) => ({ user_uid: uid, user: data.user, sum: data.sum }));

        const topReceivers = Object.entries(allReceivers)
            .sort((a, b) => b[1].sum - a[1].sum)
            .slice(0, 10)
            .map(([uid, data]) => ({ user_uid: uid, user: data.user, sum: data.sum }));

        return { sender: topSenders, receiver: topReceivers };
    }

    // Crea la pagina fittizia delle statistiche
    function createStatPage() {
        if (document.querySelector('#fake-stat-page')) return;

        const page = document.createElement('div');
        page.id = 'fake-stat-page';
        page.style.position = 'fixed';
        page.style.top = '0';
        page.style.left = '0';
        page.style.width = '100%';
        page.style.height = '100%';
        page.style.backgroundColor = 'rgba(0,0,0,0.5)';
        page.style.zIndex = '9999';
        page.style.overflowY = 'auto';
        page.style.fontFamily = 'Verdana, Arial, sans-serif';
        page.style.fontSize = '14px';

        page.innerHTML = `
            <div style="
                max-width: 950px;
                margin: 20px auto;
                background: white;
                padding: 20px;
                border-radius: 5px;
                box-shadow: 0 0 10px rgba(0,0,0,0.2);
                border: 1px solid #ddd;
            ">
                <h2 style="text-align: center; margin-bottom: 20px; color: #2e3b4e;">Statistiche Board</h2>

                <div style="margin-bottom: 15px;">
                    <label style="display: block; margin-bottom: 5px; font-weight: bold;">Board ID o nome:</label>
                    <input type="text" id="board-id" value="28" style="width: 100%; padding: 8px; border: 1px solid #ccc; border-radius: 3px;">
                </div>

                <div style="margin-bottom: 15px;">
                    <label style="display: block; margin-bottom: 5px; font-weight: bold;">Mese corrente (inizio):</label>
                    <input type="datetime-local" id="current-start" value="2025-08-01T00:00:00" style="width: 100%; padding: 8px; border: 1px solid #ccc; border-radius: 3px;">
                </div>

                <div style="margin-bottom: 15px;">
                    <label style="display: block; margin-bottom: 5px; font-weight: bold;">Mese corrente (fine):</label>
                    <input type="datetime-local" id="current-end" value="2025-08-31T23:59:59" style="width: 100%; padding: 8px; border: 1px solid #ccc; border-radius: 3px;">
                </div>

                <div style="margin-bottom: 15px;">
                    <label style="display: block; margin-bottom: 5px; font-weight: bold;">Mese precedente (inizio):</label>
                    <input type="datetime-local" id="previous-start" value="2025-07-01T00:00:00" style="width: 100%; padding: 8px; border: 1px solid #ccc; border-radius: 3px;">
                </div>

                <div style="margin-bottom: 15px;">
                    <label style="display: block; margin-bottom: 5px; font-weight: bold;">Mese precedente (fine):</label>
                    <input type="datetime-local" id="previous-end" value="2025-07-31T23:59:59" style="width: 100%; padding: 8px; border: 1px solid #ccc; border-radius: 3px;">
                </div>

                <div style="margin-bottom: 20px;">
                    <label style="display: inline-flex; align-items: center; cursor: pointer;">
                        <input type="checkbox" id="child-boards" style="margin-right: 8px;">
                        Includi child boards
                    </label>
                </div>

                <button id="generate-stats" style="
                    width: 100%;
                    padding: 10px;
                    font-weight: bold;
                    background: #2e3b4e;
                    color: white;
                    border: none;
                    border-radius: 3px;
                    cursor: pointer;
                ">Genera Statistiche</button>

                <div id="stats-preview" style="margin-top: 20px; display: none;">
                    <h3 style="border-bottom: 1px solid #eee; padding-bottom: 5px;">Anteprima Tabella</h3>
                    <div id="preview-table" style="overflow-x: auto;"></div>
                </div>

                <div id="stats-output" style="
                    margin-top: 20px;
                    padding: 15px;
                    background: #f5f5f5;
                    border-radius: 3px;
                    border: 1px solid #ddd;
                    min-height: 100px;
                    display: none;
                ">
                    <h3 style="border-bottom: 1px solid #eee; padding-bottom: 5px;">BBCode</h3>
                    <textarea id="bbcode-output" style="
                        width: 100%;
                        height: 300px;
                        padding: 10px;
                        font-family: monospace;
                        border: 1px solid #ccc;
                        border-radius: 3px;
                        resize: vertical;
                    "></textarea>
                    <button id="copy-bbcode" style="
                        display: block;
                        margin: 10px auto 0;
                        padding: 8px 16px;
                        background: #2e3b4e;
                        color: white;
                        border: none;
                        border-radius: 3px;
                        cursor: pointer;
                    ">Copia BBCode</button>
                </div>

                <button id="close-page" style="
                    display: block;
                    margin: 20px auto 0;
                    padding: 8px 16px;
                    font-weight: bold;
                    background: #ccc;
                    border: none;
                    border-radius: 3px;
                    cursor: pointer;
                ">Chiudi</button>
            </div>
        `;

        document.body.appendChild(page);

        document.querySelector('#close-page').onclick = () => page.remove();
        document.querySelector('#generate-stats').onclick = generateStats;
        document.querySelector('#copy-bbcode').onclick = copyBBCode;
    }

    // Copia BBCode negli appunti
    function copyBBCode() {
        const textarea = document.querySelector('#bbcode-output');
        textarea.select();
        try {
            navigator.clipboard.writeText(textarea.value)
                .then(() => alert('BBCode copiato negli appunti!'))
                .catch(() => {
                    document.execCommand('copy');
                    alert('BBCode copiato negli appunti!');
                });
        } catch (err) {
            document.execCommand('copy');
            alert('BBCode copiato negli appunti!');
        }
    }

    // Ottieni il nome del mese da una data
    function getMonthName(dateString) {
        const months = ['Gen', 'Feb', 'Mar', 'Apr', 'Mag', 'Giu', 'Lug', 'Ago', 'Set', 'Ott', 'Nov', 'Dic'];
        const date = new Date(dateString);
        return months[date.getMonth()];
    }

    // Genera le statistiche con intestazione dinamica e Merit stats
    async function generateStats() {
        const boardId = document.querySelector('#board-id').value.trim();
        const currentStart = document.querySelector('#current-start').value;
        const currentEnd = document.querySelector('#current-end').value;
        const previousStart = document.querySelector('#previous-start').value;
        const previousEnd = document.querySelector('#previous-end').value;
        const childBoards = document.querySelector('#child-boards').checked;

        if (!boardId || !currentStart || !currentEnd || !previousStart || !previousEnd) {
            alert('Compila tutti i campi!');
            return;
        }

        // Nomi dei mesi per l'intestazione
        const currentMonthName = getMonthName(currentStart);
        const previousMonthName = getMonthName(previousStart);

        const previewDiv = document.querySelector('#stats-preview');
        const outputDiv = document.querySelector('#stats-output');
        previewDiv.style.display = 'none';
        outputDiv.style.display = 'none';

        // Spinner di caricamento "Dot Wave"
        const loadingMsg = document.createElement('div');
        loadingMsg.style.textAlign = 'center';
        loadingMsg.style.margin = '20px 0';
        loadingMsg.innerHTML = `
            <div class="lds-ellipsis">
                <div></div>
                <div></div>
                <div></div>
                <div></div>
            </div>
            <p style="color: #666; margin-top: 10px;">Caricamento dati in corso...</p>
            <style>
                .lds-ellipsis {
                    display: inline-block;
                    position: relative;
                    width: 64px;
                    height: 64px;
                }
                .lds-ellipsis div {
                    position: absolute;
                    top: 27px;
                    width: 11px;
                    height: 11px;
                    border-radius: 50%;
                    background: #2e3b4e;
                    animation-timing-function: cubic-bezier(0, 1, 1, 0);
                }
                .lds-ellipsis div:nth-child(1) {
                    left: 6px;
                    animation: lds-ellipsis1 0.6s infinite;
                }
                .lds-ellipsis div:nth-child(2) {
                    left: 6px;
                    animation: lds-ellipsis2 0.6s infinite;
                }
                .lds-ellipsis div:nth-child(3) {
                    left: 26px;
                    animation: lds-ellipsis2 0.6s infinite;
                }
                .lds-ellipsis div:nth-child(4) {
                    left: 45px;
                    animation: lds-ellipsis3 0.6s infinite;
                }
                @keyframes lds-ellipsis1 {
                    0% {
                        transform: scale(0);
                    }
                    100% {
                        transform: scale(1);
                    }
                }
                @keyframes lds-ellipsis3 {
                    0% {
                        transform: scale(1);
                    }
                    100% {
                        transform: scale(0);
                    }
                }
                @keyframes lds-ellipsis2 {
                    0% {
                        transform: translate(0, 0);
                    }
                    100% {
                        transform: translate(19px, 0);
                    }
                }
            </style>
        `;
        previewDiv.parentNode.insertBefore(loadingMsg, previewDiv);

        try {
            // Fetch dati mese corrente (post)
            const currentUrl = `https://api.ninjastic.space/posts/authors?board=${boardId}&child_boards=${childBoards}&after_date=${currentStart}&before_date=${currentEnd}&limit=1000`;
            const currentRes = await fetch(currentUrl);
            const currentJson = await currentRes.json();

            // Fetch dati mese precedente (post)
            const previousUrl = `https://api.ninjastic.space/posts/authors?board=${boardId}&child_boards=${childBoards}&after_date=${previousStart}&before_date=${previousEnd}&limit=1000`;
            const previousRes = await fetch(previousUrl);
            const previousJson = await previousRes.json();

            if (currentJson.result !== "success" || previousJson.result !== "success") {
                loadingMsg.innerHTML = `<p style="color: red; text-align: center;">Errore API: ${currentJson.message || previousJson.message || "Sconosciuto"}</p>`;
                return;
            }

            const currentAuthors = currentJson.data.authors;
            const previousAuthors = previousJson.data.authors;

            // Crea mappa post mese precedente (author_uid -> count)
            const previousPostsMap = {};
            previousAuthors.forEach(a => {
                previousPostsMap[a.author_uid] = a.count || 0;
            });

            // Unisci e ordina gli utenti per post del mese corrente
            const allAuthors = [...currentAuthors];
            allAuthors.sort((a, b) => (b.count || 0) - (a.count || 0));

            // Anteprima tabella HTML (post)
            let previewTable = `
                <table style="width: 100%; border-collapse: collapse; margin-bottom: 15px; font-size: 13px;">
                    <tr style="background: #f0f0f0;">
                        <th style="padding: 8px; text-align: center; border: 1px solid #ddd; width: 5%;">Pos.</th>
                        <th style="padding: 8px; text-align: left; border: 1px solid #ddd; width: 25%;">User</th>
                        <th style="padding: 8px; text-align: center; border: 1px solid #ddd; width: 10%;">Post (${currentMonthName})</th>
                        <th style="padding: 8px; text-align: center; border: 1px solid #ddd; width: 10%;">Post (${previousMonthName})</th>
                        <th style="padding: 8px; text-align: center; border: 1px solid #ddd; width: 10%;">Change</th>
                        <th style="padding: 8px; text-align: center; border: 1px solid #ddd; width: 10%;">BPIP</th>
                        <th style="padding: 8px; text-align: center; border: 1px solid #ddd; width: 10%;">Ninjastic</th>
                    </tr>
            `;
            allAuthors.forEach((a, index) => {
                const username = a.author || 'Sconosciuto';
                const userUid = a.author_uid || '';
                const currentCount = a.count || 0;
                const previousCount = previousPostsMap[userUid] || 0;
                const diff = currentCount - previousCount;
                const variationColor = diff >= 0 ? 'green' : 'red';
                const variationText = diff >= 0 ? `▲${diff}` : `▼${Math.abs(diff)}`;
                const userLink = userUid ? `<a href="https://bitcointalk.org/index.php?action=profile;u=${userUid}" target="_blank">${username}</a>` : username;

                previewTable += `
                    <tr style="border: 1px solid #ddd;">
                        <td style="padding: 8px; border: 1px solid #ddd; text-align: center;">${index + 1}.</td>
                        <td style="padding: 8px; border: 1px solid #ddd;">${userLink}</td>
                        <td style="padding: 8px; border: 1px solid #ddd; text-align: center;">${currentCount}</td>
                        <td style="padding: 8px; border: 1px solid #ddd; text-align: center;">${previousCount}</td>
                        <td style="padding: 8px; border: 1px solid #ddd; text-align: center; color: ${variationColor};">${variationText}</td>
                        <td style="padding: 8px; border: 1px solid #ddd; text-align: center;">
                            <a href="https://bpip.org/Profile?p=${username}" target="_blank">
                                <img src="https://www.talkimg.com/images/2023/08/03/GXlZb.png" width="15">
                            </a>
                        </td>
                        <td style="padding: 8px; border: 1px solid #ddd; text-align: center;">
                            <a href="https://ninjastic.space/user/${username}" target="_blank">
                                <img src="https://talkimg.com/images/2023/08/03/GwdMz.png" width="15">
                            </a>
                        </td>
                    </tr>
                `;
            });
            previewTable += `</table>`;

            // BBCode con intestazione dinamica (post)
            let bbcode = `[center][b][size=12pt]Statistiche Board[/size][/b][/center]
[center][i]Confronto: ${previousMonthName} vs ${currentMonthName}[/i][/center]

[table]
[tr]
[td][b]Pos.[/b][/td]
[td][b]User[/b][/td]
[td][b]Post (${currentMonthName})[/b][/td]
[td][b]Post (${previousMonthName})[/b][/td]
[td][b]Change[/b][/td]
[td][b]BPIP[/b][/td]
[td][b]Ninjastic[/b][/td]
[/tr]
`;
            allAuthors.forEach((a, index) => {
                const username = a.author || 'Sconosciuto';
                const userUid = a.author_uid || '';
                const currentCount = a.count || 0;
                const previousCount = previousPostsMap[userUid] || 0;
                const diff = currentCount - previousCount;
                const variationColor = diff >= 0 ? 'green' : 'red';
                const variationText = diff >= 0 ? `[color=${variationColor}]▲${diff}[/color]` : `[color=${variationColor}]▼${Math.abs(diff)}[/color]`;
                const userLink = userUid ? `[url=https://bitcointalk.org/index.php?action=profile;u=${userUid}]${username}[/url]` : username;

                bbcode += `[tr]
[td]${index + 1}.[/td]
[td]${userLink}[/td]
[td]${currentCount}[/td]
[td]${previousCount}[/td]
[td]${variationText}[/td]
[td][url=https://bpip.org/Profile?p=${username}][img width=15]https://www.talkimg.com/images/2023/08/03/GXlZb.png[/img][/url][/td]
[td][url=https://ninjastic.space/user/${username}][img width=15]https://talkimg.com/images/2023/08/03/GwdMz.png[/img][/url][/td]
[/tr]
`;
            });
            bbcode += `[/table]`;

            // Analizza la distribuzione dei post per utente
            const postDistribution = {
                "1 post": 0,
                "2-5 post": 0,
                "6-10 post": 0,
                "11-20 post": 0,
                "21-50 post": 0,
                ">50 post": 0
            };

            allAuthors.forEach(author => {
                const postCount = author.count || 0;
                if (postCount === 1) postDistribution["1 post"]++;
                else if (postCount >= 2 && postCount <= 5) postDistribution["2-5 post"]++;
                else if (postCount >= 6 && postCount <= 10) postDistribution["6-10 post"]++;
                else if (postCount >= 11 && postCount <= 20) postDistribution["11-20 post"]++;
                else if (postCount >= 21 && postCount <= 50) postDistribution["21-50 post"]++;
                else if (postCount > 50) postDistribution[">50 post"]++;
            });

            // Genera la tabella HTML per la distribuzione dei post
            let postDistributionPreview = `
                <div style="margin-top: 20px;">
                    <h4 style="text-align: center; margin-bottom: 10px;">Distribuzione Post per Utente (${currentMonthName})</h4>
                    <table style="width: 100%; border-collapse: collapse; font-size: 13px;">
                        <tr style="background: #f0f0f0;">
                            <th style="padding: 8px; text-align: center; border: 1px solid #ddd;">Intervallo Post</th>
                            <th style="padding: 8px; text-align: center; border: 1px solid #ddd;">Num. Utenti</th>
                        </tr>
            `;
            Object.entries(postDistribution).forEach(([range, count]) => {
                postDistributionPreview += `
                    <tr style="border: 1px solid #ddd;">
                        <td style="padding: 8px; border: 1px solid #ddd; text-align: center;">${range}</td>
                        <td style="padding: 8px; border: 1px solid #ddd; text-align: center;">${count}</td>
                    </tr>
                `;
            });
            postDistributionPreview += `</table></div>`;

            // Genera il BBCode per la distribuzione dei post
            let postDistributionBBCode = `
[center][b]Distribuzione Post per Utente (${currentMonthName})[/b][/center]
[table]
[tr]
[td][b]Intervallo Post[/b][/td]
[td][b]Num. Utenti[/b][/td]
[/tr]
            `;
            Object.entries(postDistribution).forEach(([range, count]) => {
                postDistributionBBCode += `
[tr]
[td]${range}[/td]
[td]${count}[/td]
[/tr]
                `;
            });
            postDistributionBBCode += `[/table]`;

            // Recupera i dati Merit
            const boardIds = childBoards ? [boardId, ...await fetchChildBoards(boardId)] : [boardId];
            const meritData = await fetchMeritBatchData(
                boardIds,
                currentStart.split('T')[0],
                currentEnd.split('T')[0]
            );

            // Anteprima tabelle Merit (affiancate)
            let meritPreview = `
                <div style="display: flex; gap: 20px; margin-top: 20px;">
                    <div style="flex: 1;">
                        <h4 style="text-align: center; margin-bottom: 10px;">Top 10 Merit Sender</h4>
                        <table style="width: 100%; border-collapse: collapse; font-size: 13px;">
                            <tr style="background: #f0f0f0;">
                                <th style="padding: 8px; text-align: center; border: 1px solid #ddd;">Pos.</th>
                                <th style="padding: 8px; text-align: left; border: 1px solid #ddd;">User</th>
                                <th style="padding: 8px; text-align: center; border: 1px solid #ddd;">Merit Inviati</th>
                                <th style="padding: 8px; text-align: center; border: 1px solid #ddd;">BPIP</th>
                                <th style="padding: 8px; text-align: center; border: 1px solid #ddd;">Ninjastic</th>
                            </tr>
            `;
            meritData.sender.slice(0, 10).forEach((user, index) => {
                meritPreview += `
                    <tr style="border: 1px solid #ddd;">
                        <td style="padding: 8px; border: 1px solid #ddd; text-align: center;">${index + 1}.</td>
                        <td style="padding: 8px; border: 1px solid #ddd;">
                            <a href="https://bitcointalk.org/index.php?action=profile;u=${user.user_uid}" target="_blank">${user.user}</a>
                        </td>
                        <td style="padding: 8px; border: 1px solid #ddd; text-align: center;">${user.sum}</td>
                        <td style="padding: 8px; border: 1px solid #ddd; text-align: center;">
                            <a href="https://bpip.org/Profile?p=${user.user}" target="_blank">
                                <img src="https://www.talkimg.com/images/2023/08/03/GXlZb.png" width="15">
                            </a>
                        </td>
                        <td style="padding: 8px; border: 1px solid #ddd; text-align: center;">
                            <a href="https://ninjastic.space/user/${user.user}" target="_blank">
                                <img src="https://talkimg.com/images/2023/08/03/GwdMz.png" width="15">
                            </a>
                        </td>
                    </tr>
                `;
            });
            meritPreview += `</table></div>`;

            // Tabella Merit Receiver
            meritPreview += `
                <div style="flex: 1;">
                    <h4 style="text-align: center; margin-bottom: 10px;">Top 10 Merit Receiver</h4>
                    <table style="width: 100%; border-collapse: collapse; font-size: 13px;">
                        <tr style="background: #f0f0f0;">
                            <th style="padding: 8px; text-align: center; border: 1px solid #ddd;">Pos.</th>
                            <th style="padding: 8px; text-align: left; border: 1px solid #ddd;">User</th>
                            <th style="padding: 8px; text-align: center; border: 1px solid #ddd;">Merit Ricevuti</th>
                            <th style="padding: 8px; text-align: center; border: 1px solid #ddd;">BPIP</th>
                            <th style="padding: 8px; text-align: center; border: 1px solid #ddd;">Ninjastic</th>
                        </tr>
            `;
            meritData.receiver.slice(0, 10).forEach((user, index) => {
                meritPreview += `
                    <tr style="border: 1px solid #ddd;">
                        <td style="padding: 8px; border: 1px solid #ddd; text-align: center;">${index + 1}.</td>
                        <td style="padding: 8px; border: 1px solid #ddd;">
                            <a href="https://bitcointalk.org/index.php?action=profile;u=${user.user_uid}" target="_blank">${user.user}</a>
                        </td>
                        <td style="padding: 8px; border: 1px solid #ddd; text-align: center;">${user.sum}</td>
                        <td style="padding: 8px; border: 1px solid #ddd; text-align: center;">
                            <a href="https://bpip.org/Profile?p=${user.user}" target="_blank">
                                <img src="https://www.talkimg.com/images/2023/08/03/GXlZb.png" width="15">
                            </a>
                        </td>
                        <td style="padding: 8px; border: 1px solid #ddd; text-align: center;">
                            <a href="https://ninjastic.space/user/${user.user}" target="_blank">
                                <img src="https://talkimg.com/images/2023/08/03/GwdMz.png" width="15">
                            </a>
                        </td>
                    </tr>
                `;
            });
            meritPreview += `</table></div></div>`;

            // BBCode per Merit (affiancate)
            let meritBBCode = `
[table]
[tr]
[td][center][b]Top 10 Merit Sender[/b][/center][/td]
[td][center][b]Top 10 Merit Receiver[/b][/center][/td]
[/tr]
[tr]
[td]
[table]
[tr]
[td][b]Pos.[/b][/td]
[td][b]User[/b][/td]
[td][b]Merit Inviati[/b][/td]
[td][b]BPIP[/b][/td]
[td][b]Ninjastic[/b][/td]
[/tr]
            `;
            meritData.sender.slice(0, 10).forEach((user, index) => {
                meritBBCode += `
[tr]
[td]${index + 1}.[/td]
[td][url=https://bitcointalk.org/index.php?action=profile;u=${user.user_uid}]${user.user}[/url][/td]
[td]${user.sum}[/td]
[td][url=https://bpip.org/Profile?p=${user.user}][img width=15]https://www.talkimg.com/images/2023/08/03/GXlZb.png[/img][/url][/td]
[td][url=https://ninjastic.space/user/${user.user}][img width=15]https://talkimg.com/images/2023/08/03/GwdMz.png[/img][/url][/td]
[/tr]
                `;
            });
            meritBBCode += `
[/table]
[/td]
[td]
[table]
[tr]
[td][b]Pos.[/b][/td]
[td][b]User[/b][/td]
[td][b]Merit Ricevuti[/b][/td]
[td][b]BPIP[/b][/td]
[td][b]Ninjastic[/b][/td]
[/tr]
            `;
            meritData.receiver.slice(0, 10).forEach((user, index) => {
                meritBBCode += `
[tr]
[td]${index + 1}.[/td]
[td][url=https://bitcointalk.org/index.php?action=profile;u=${user.user_uid}]${user.user}[/url][/td]
[td]${user.sum}[/td]
[td][url=https://bpip.org/Profile?p=${user.user}][img width=15]https://www.talkimg.com/images/2023/08/03/GXlZb.png[/img][/url][/td]
[td][url=https://ninjastic.space/user/${user.user}][img width=15]https://talkimg.com/images/2023/08/03/GwdMz.png[/img][/url][/td]
[/tr]
                `;
            });
            meritBBCode += `
[/table]
[/td]
[/tr]
[/table]
            `;

            // Unisci i dati dei post e dei merit per calcolare il rateo
            const userRateoMap = {};
            allAuthors.forEach(author => {
                const userUid = author.author_uid;
                const postCount = author.count || 0;
                const meritUser = meritData.receiver.find(u => u.user_uid == userUid);
                const meritCount = meritUser ? meritUser.sum : 0;
                const rateo = postCount > 0 ? (meritCount / postCount).toFixed(2) : 0;

                userRateoMap[userUid] = {
                    user: author.author,
                    userUid,
                    postCount,
                    meritCount,
                    rateo: parseFloat(rateo)
                };
            });

            // Ordina per rateo e prendi i primi 10 (filtra utenti con almeno 1 post)
            const topRateoUsers = Object.values(userRateoMap)
                .filter(user => user.postCount > 0)
                .sort((a, b) => b.rateo - a.rateo)
                .slice(0, 10);

            // Genera la tabella HTML per il rateo
            let rateoPreview = `
                <div style="margin-top: 20px;">
                    <h4 style="text-align: center; margin-bottom: 10px;">Top 10 Utenti per Rateo Merit/Post</h4>
                    <table style="width: 100%; border-collapse: collapse; font-size: 13px;">
                        <tr style="background: #f0f0f0;">
                            <th style="padding: 8px; text-align: center; border: 1px solid #ddd;">Pos.</th>
                            <th style="padding: 8px; text-align: left; border: 1px solid #ddd;">User</th>
                            <th style="padding: 8px; text-align: center; border: 1px solid #ddd;">Post</th>
                            <th style="padding: 8px; text-align: center; border: 1px solid #ddd;">Merit Ricevuti</th>
                            <th style="padding: 8px; text-align: center; border: 1px solid #ddd;">Rateo</th>
                            <th style="padding: 8px; text-align: center; border: 1px solid #ddd;">BPIP</th>
                            <th style="padding: 8px; text-align: center; border: 1px solid #ddd;">Ninjastic</th>
                        </tr>
            `;
            topRateoUsers.forEach((user, index) => {
                rateoPreview += `
                    <tr style="border: 1px solid #ddd;">
                        <td style="padding: 8px; border: 1px solid #ddd; text-align: center;">${index + 1}.</td>
                        <td style="padding: 8px; border: 1px solid #ddd;">
                            <a href="https://bitcointalk.org/index.php?action=profile;u=${user.userUid}" target="_blank">${user.user}</a>
                        </td>
                        <td style="padding: 8px; border: 1px solid #ddd; text-align: center;">${user.postCount}</td>
                        <td style="padding: 8px; border: 1px solid #ddd; text-align: center;">${user.meritCount}</td>
                        <td style="padding: 8px; border: 1px solid #ddd; text-align: center;">${user.rateo}</td>
                        <td style="padding: 8px; border: 1px solid #ddd; text-align: center;">
                            <a href="https://bpip.org/Profile?p=${user.user}" target="_blank">
                                <img src="https://www.talkimg.com/images/2023/08/03/GXlZb.png" width="15">
                            </a>
                        </td>
                        <td style="padding: 8px; border: 1px solid #ddd; text-align: center;">
                            <a href="https://ninjastic.space/user/${user.user}" target="_blank">
                                <img src="https://talkimg.com/images/2023/08/03/GwdMz.png" width="15">
                            </a>
                        </td>
                    </tr>
                `;
            });
            rateoPreview += `</table></div>`;

            // Genera il BBCode per il rateo
            let rateoBBCode = `
[center][b]Top 10 Utenti per Rateo Merit/Post[/b][/center]
[table]
[tr]
[td][b]Pos.[/b][/td]
[td][b]User[/b][/td]
[td][b]Post[/b][/td]
[td][b]Merit Ricevuti[/b][/td]
[td][b]Rateo[/b][/td]
[td][b]BPIP[/b][/td]
[td][b]Ninjastic[/b][/td]
[/tr]
            `;
            topRateoUsers.forEach((user, index) => {
                rateoBBCode += `
[tr]
[td]${index + 1}.[/td]
[td][url=https://bitcointalk.org/index.php?action=profile;u=${user.userUid}]${user.user}[/url][/td]
[td]${user.postCount}[/td]
[td]${user.meritCount}[/td]
[td]${user.rateo}[/td]
[td][url=https://bpip.org/Profile?p=${user.user}][img width=15]https://www.talkimg.com/images/2023/08/03/GXlZb.png[/img][/url][/td]
[td][url=https://ninjastic.space/user/${user.user}][img width=15]https://talkimg.com/images/2023/08/03/GwdMz.png[/img][/url][/td]
[/tr]
                `;
            });
            rateoBBCode += `[/table]`;

            // Calcola gli utenti con il maggior impatto (Post + Merit)
            const userImpactMap = {};
            allAuthors.forEach(author => {
                const userUid = author.author_uid;
                const postCount = author.count || 0;
                const meritUser = meritData.receiver.find(u => u.user_uid == userUid);
                const meritCount = meritUser ? meritUser.sum : 0;
                // Calcola il punteggio di impatto: (post * 0.5) + (merit * 1.5)
                const impactScore = (postCount * 0.5) + (meritCount * 1.5);

                userImpactMap[userUid] = {
                    user: author.author,
                    userUid,
                    postCount,
                    meritCount,
                    impactScore: impactScore.toFixed(2)
                };
            });

            // Ordina per impatto e prendi i primi 10
            const topImpactUsers = Object.values(userImpactMap)
                .filter(user => user.postCount > 0 || user.meritCount > 0)
                .sort((a, b) => parseFloat(b.impactScore) - parseFloat(a.impactScore))
                .slice(0, 10);

            // Genera la tabella HTML per l'impatto
            let impactPreview = `
                <div style="margin-top: 20px;">
                    <h4 style="text-align: center; margin-bottom: 10px;">Top 10 Utenti per Impatto (Post + Merit)</h4>
                    <table style="width: 100%; border-collapse: collapse; font-size: 13px;">
                        <tr style="background: #f0f0f0;">
                            <th style="padding: 8px; text-align: center; border: 1px solid #ddd;">Pos.</th>
                            <th style="padding: 8px; text-align: left; border: 1px solid #ddd;">User</th>
                            <th style="padding: 8px; text-align: center; border: 1px solid #ddd;">Post</th>
                            <th style="padding: 8px; text-align: center; border: 1px solid #ddd;">Merit Ricevuti</th>
                            <th style="padding: 8px; text-align: center; border: 1px solid #ddd;">Impatto</th>
                            <th style="padding: 8px; text-align: center; border: 1px solid #ddd;">BPIP</th>
                            <th style="padding: 8px; text-align: center; border: 1px solid #ddd;">Ninjastic</th>
                        </tr>
            `;
            topImpactUsers.forEach((user, index) => {
                impactPreview += `
                    <tr style="border: 1px solid #ddd;">
                        <td style="padding: 8px; border: 1px solid #ddd; text-align: center;">${index + 1}.</td>
                        <td style="padding: 8px; border: 1px solid #ddd;">
                            <a href="https://bitcointalk.org/index.php?action=profile;u=${user.userUid}" target="_blank">${user.user}</a>
                        </td>
                        <td style="padding: 8px; border: 1px solid #ddd; text-align: center;">${user.postCount}</td>
                        <td style="padding: 8px; border: 1px solid #ddd; text-align: center;">${user.meritCount}</td>
                        <td style="padding: 8px; border: 1px solid #ddd; text-align: center;">${user.impactScore}</td>
                        <td style="padding: 8px; border: 1px solid #ddd; text-align: center;">
                            <a href="https://bpip.org/Profile?p=${user.user}" target="_blank">
                                <img src="https://www.talkimg.com/images/2023/08/03/GXlZb.png" width="15">
                            </a>
                        </td>
                        <td style="padding: 8px; border: 1px solid #ddd; text-align: center;">
                            <a href="https://ninjastic.space/user/${user.user}" target="_blank">
                                <img src="https://talkimg.com/images/2023/08/03/GwdMz.png" width="15">
                            </a>
                        </td>
                    </tr>
                `;
            });
            impactPreview += `</table></div>`;

            // Genera il BBCode per l'impatto
            let impactBBCode = `
[center][b]Top 10 Utenti per Impatto (Post + Merit)[/b][/center]
[table]
[tr]
[td][b]Pos.[/b][/td]
[td][b]User[/b][/td]
[td][b]Post[/b][/td]
[td][b]Merit Ricevuti[/b][/td]
[td][b]Impatto[/b][/td]
[td][b]BPIP[/b][/td]
[td][b]Ninjastic[/b][/td]
[/tr]
            `;
            topImpactUsers.forEach((user, index) => {
                impactBBCode += `
[tr]
[td]${index + 1}.[/td]
[td][url=https://bitcointalk.org/index.php?action=profile;u=${user.userUid}]${user.user}[/url][/td]
[td]${user.postCount}[/td]
[td]${user.meritCount}[/td]
[td]${user.impactScore}[/td]
[td][url=https://bpip.org/Profile?p=${user.user}][img width=15]https://www.talkimg.com/images/2023/08/03/GXlZb.png[/img][/url][/td]
[td][url=https://ninjastic.space/user/${user.user}][img width=15]https://talkimg.com/images/2023/08/03/GwdMz.png[/img][/url][/td]
[/tr]
                `;
            });
            impactBBCode += `[/table]`;

            // Genera una sintesi testuale dei dati
            let summaryText = `
            <div style="margin-top: 20px; padding: 15px; background: #f9f9f9; border-radius: 5px; border: 1px solid #ddd;">
                <h4 style="text-align: center; margin-bottom: 15px; color: #2e3b4e;">Sintesi degli Andamenti</h4>
                <p style="margin-bottom: 10px;"><strong>Periodo analizzato:</strong> ${currentMonthName} (vs ${previousMonthName})</p>
                <p style="margin-bottom: 10px;"><strong>Board:</strong> ${boardId} ${childBoards ? "(incluse child boards)" : ""}</p>
                <p style="margin-bottom: 10px;"><strong>Utenti attivi:</strong> ${allAuthors.length} (di cui ${allAuthors.filter(a => a.count > 0).length} con almeno 1 post in ${currentMonthName})</p>

                <h5 style="margin: 15px 0 10px 0; color: #2e3b4e;">Top Poster</h5>
                <ul style="margin-bottom: 15px; padding-left: 20px;">
                    <li><strong>1°:</strong> ${allAuthors[0]?.author || "Nessuno"} (${allAuthors[0]?.count || 0} post in ${currentMonthName})</li>
                    <li><strong>2°:</strong> ${allAuthors[1]?.author || "Nessuno"} (${allAuthors[1]?.count || 0} post)</li>
                    <li><strong>3°:</strong> ${allAuthors[2]?.author || "Nessuno"} (${allAuthors[2]?.count || 0} post)</li>
                </ul>

                <h5 style="margin: 15px 0 10px 0; color: #2e3b4e;">Top Merit Receiver</h5>
                <ul style="margin-bottom: 15px; padding-left: 20px;">
                    <li><strong>1°:</strong> ${meritData.receiver[0]?.user || "Nessuno"} (${meritData.receiver[0]?.sum || 0} merit)</li>
                    <li><strong>2°:</strong> ${meritData.receiver[1]?.user || "Nessuno"} (${meritData.receiver[1]?.sum || 0} merit)</li>
                    <li><strong>3°:</strong> ${meritData.receiver[2]?.user || "Nessuno"} (${meritData.receiver[2]?.sum || 0} merit)</li>
                </ul>

                <h5 style="margin: 15px 0 10px 0; color: #2e3b4e;">Distribuzione Post</h5>
                <p style="margin-bottom: 10px;">
                    ${postDistribution["1 post"]} utenti con 1 post,<br>
                    ${postDistribution["2-5 post"]} utenti con 2-5 post,<br>
                    ${postDistribution["6-10 post"]} utenti con 6-10 post,<br>
                    ${postDistribution[">50 post"]} utenti con più di 50 post.
                </p>

                <h5 style="margin: 15px 0 10px 0; color: #2e3b4e;">Utenti con Maggior Impatto</h5>
                <ul style="margin-bottom: 15px; padding-left: 20px;">
                    <li><strong>1°:</strong> ${topImpactUsers[0]?.user || "Nessuno"} (Impatto: ${topImpactUsers[0]?.impactScore || 0})</li>
                    <li><strong>2°:</strong> ${topImpactUsers[1]?.user || "Nessuno"} (Impatto: ${topImpactUsers[1]?.impactScore || 0})</li>
                    <li><strong>3°:</strong> ${topImpactUsers[2]?.user || "Nessuno"} (Impatto: ${topImpactUsers[2]?.impactScore || 0})</li>
                </ul>

                <h5 style="margin: 15px 0 10px 0; color: #2e3b4e;">Rateo Merit/Post</h5>
                <p style="margin-bottom: 10px;">
                    ${topRateoUsers[0]?.user || "Nessuno"} ha il rateo più alto: ${topRateoUsers[0]?.rateo || 0} merit per post.
                </p>
            </div>
            `;

            // Genera il BBCode per la sintesi
            let summaryBBCode = `
[center][b][size=12pt]Sintesi degli Andamenti[/size][/b][/center]
[b]Periodo analizzato:[/b] ${currentMonthName} (vs ${previousMonthName})
[b]Board:[/b] ${boardId} ${childBoards ? "(incluse child boards)" : ""}
[b]Utenti attivi:[/b] ${allAuthors.length} (di cui ${allAuthors.filter(a => a.count > 0).length} con almeno 1 post in ${currentMonthName})

[center][b]Top Poster[/b][/center]
[list]
[*]1°: ${allAuthors[0]?.author || "Nessuno"} (${allAuthors[0]?.count || 0} post in ${currentMonthName})
[*]2°: ${allAuthors[1]?.author || "Nessuno"} (${allAuthors[1]?.count || 0} post)
[*]3°: ${allAuthors[2]?.author || "Nessuno"} (${allAuthors[2]?.count || 0} post)
[/list]

[center][b]Top Merit Receiver[/b][/center]
[list]
[*]1°: ${meritData.receiver[0]?.user || "Nessuno"} (${meritData.receiver[0]?.sum || 0} merit)
[*]2°: ${meritData.receiver[1]?.user || "Nessuno"} (${meritData.receiver[1]?.sum || 0} merit)
[*]3°: ${meritData.receiver[2]?.user || "Nessuno"} (${meritData.receiver[2]?.sum || 0} merit)
[/list]

[center][b]Distribuzione Post[/b][/center]
${postDistribution["1 post"]} utenti con 1 post
${postDistribution["2-5 post"]} utenti con 2-5 post
${postDistribution["6-10 post"]} utenti con 6-10 post
${postDistribution[">50 post"]} utenti con più di 50 post

[center][b]Utenti con Maggior Impatto[/b][/center]
[list]
[*]1°: ${topImpactUsers[0]?.user || "Nessuno"} (Impatto: ${topImpactUsers[0]?.impactScore || 0})
[*]2°: ${topImpactUsers[1]?.user || "Nessuno"} (Impatto: ${topImpactUsers[1]?.impactScore || 0})
[*]3°: ${topImpactUsers[2]?.user || "Nessuno"} (Impatto: ${topImpactUsers[2]?.impactScore || 0})
[/list]

[center][b]Rateo Merit/Post[/b][/center]
${topRateoUsers[0]?.user || "Nessuno"} ha il rateo più alto: ${topRateoUsers[0]?.rateo || 0} merit per post
`;

            // Aggiungi la sintesi all'anteprima e al BBCode
            previewTable = summaryText + previewTable;
            bbcode = summaryBBCode + `\n\n` + bbcode;

            // Mostra anteprima e BBCode
            loadingMsg.remove();
            previewDiv.style.display = 'block';
            outputDiv.style.display = 'block';
            document.querySelector('#preview-table').innerHTML = previewTable + postDistributionPreview + meritPreview + rateoPreview + impactPreview;
            document.querySelector('#bbcode-output').value = bbcode + `\n\n${postDistributionBBCode}\n\n${meritBBCode}\n\n${rateoBBCode}\n\n${impactBBCode}`;

        } catch (err) {
            console.error("Errore generale durante il recupero dei dati:", err);
            loadingMsg.innerHTML = '<p style="color: red; text-align: center;">Errore durante il caricamento dati. Vedi console per dettagli.</p>';
        }
    }

    // Aggiunge pulsante Stat nella navbar
    function addStatButton() {
        const navbar = document.querySelector('table[style*="margin-left: 10px;"]');
        if (!navbar) {
            setTimeout(addStatButton, 1000);
            return;
        }

        const lastCell = navbar.querySelector('td.maintab_last');
        if (lastCell.querySelector('#stat-button')) return;

        const statButton = document.createElement('td');
        statButton.id = 'stat-button';
        statButton.className = 'maintab_back';
        statButton.innerHTML = `<a href="javascript:void(0)" class="maintab_link" style="padding: 0 10px;">Stats</a>`;
        statButton.querySelector('a').onclick = createStatPage;

        navbar.querySelector('tr').insertBefore(statButton, lastCell);
    }

    window.addEventListener('load', () => setTimeout(addStatButton, 1000));
    window.copyBBCode = copyBBCode;
    window.createStatPage = createStatPage;
})();