GrreasyFork 用戶發佈的腳本數據視覺化

使用 Chart.js 獲取和可視化使用者腳本數據,並在圖表下方顯示總數

// ==UserScript==
// @name         GrreasyFork User Script Data Visualization
// @name:en      GrreasyFork User Script Data Visualization
// @name:es      Visualización de datos del script de usuario de GrreasyFork
// @name:fr      Visualisation des données de script utilisateur de GrreasyFork
// @name:zh      GrreasyFork 用户发布的脚本数据可视化
// @name:zh-TW   GrreasyFork 用戶發佈的腳本數據視覺化
// @namespace    http://tampermonkey.net/
// @version      0.2.2
// @description  Fetch and visualize user script data with Chart.js and display totals below the chart
// @description:en Fetch and visualize user script data with Chart.js and display totals below the chart
// @description:es Obtener y visualizar datos del script de usuario con Chart.js y mostrar totales debajo del gráfico
// @description:fr Récupérer et visualiser les données du script utilisateur avec Chart.js et afficher les totaux sous le graphique
// @description:zh-TW 使用 Chart.js 獲取和可視化使用者腳本數據,並在圖表下方顯示總數
// @description:zh    使用Chart.js获取和可视化使用者脚本数据,并在图表下方显示总数
// @locale:en    en
// @locale:es    es
// @locale:fr    fr
// @locale:zh-TW zh-TW
// @author       aspen138
// @match        *://greasyfork.org/*/users/*
// @grant        none
// @license      MIT
// @icon         https://greasyfork.org//vite/assets/blacklogo16-37ZGLlXh.png
// ==/UserScript==



(function() {
    'use strict';

    // Function to inject Chart.js from a CDN if the target element exists
    const injectChartJs = () => {
        const userHeader = document.querySelector('#about-user h2');
        if (!userHeader) return;

        const script = document.createElement('script');
        script.src = 'https://cdn.jsdelivr.net/npm/chart.js';
        script.onload = () => fetchDataAndPlot(); // Fetch data and plot chart once Chart.js is loaded
        document.head.appendChild(script);
    };

    // Function to fetch user data
    const getUserData = (userID) => {
        return fetch(`https://${window.location.hostname}/users/${userID}.json`)
            .then((response) => {
            console.log(`${response.status}: ${response.url}`);
            return response.json();
        })
            .then((data) => {
            data.scripts.sort((a, b) => b.total_installs - a.total_installs);
            return data;
        });
    };

    // Function to plot the chart
    const plotDistribution = (labels, totalInstalls, dailyInstalls) => {
        const canvasHtml = '<canvas id="installDistributionCanvas" width="100" height="50"></canvas>';
        const userHeader = document.querySelector('#about-user h2');
        if (userHeader) {
            userHeader.insertAdjacentHTML('afterend', canvasHtml);
            const ctx = document.getElementById('installDistributionCanvas').getContext('2d');

            // Plot chart
            new Chart(ctx, {
                type: 'bar', // Change this to 'line', 'bar', etc. as needed
                data: {
                    labels: labels, // X-axis labels
                    datasets: [{
                            label: 'Total Installs',
                            data: totalInstalls, // Y-axis data for Total Installs
                            backgroundColor: 'rgba(54, 162, 235, 0.2)',
                            borderColor: 'rgba(54, 162, 235, 1)',
                            borderWidth: 1,
                            yAxisID: 'y-axis-1', // Associate this dataset with the first y-axis
                        },
                        {
                            label: 'Daily Installs',
                            data: dailyInstalls, // Y-axis data for Daily Installs
                            backgroundColor: 'rgba(255, 99, 132, 0.2)',
                            borderColor: 'rgba(255, 99, 132, 1)',
                            borderWidth: 1,
                            yAxisID: 'y-axis-2', // Associate this dataset with the second y-axis
                        }
                    ]
                },
                options: {
                    scales: {
                        yAxes: [{
                                id: 'y-axis-1',
                                type: 'linear',
                                position: 'left', // This places the first y-axis on the left
                                beginAtZero: true,
                            },
                            {
                                id: 'y-axis-2',
                                type: 'linear',
                                position: 'right', // This places the second y-axis on the right
                                beginAtZero: true,
                                grid: {
                                    drawOnChartArea: false, // Ensures grid lines from this axis do not overlap with the first axis
                                },
                            }
                        ]
                    }
                }
            });
        }
    };

    // Function to display totals
    const displayTotals = (daily, total, publishedScriptsNumber) => {
        const userHeader = document.querySelector('#about-user h2');
        const language = document.documentElement.lang; // Get the current language of the document

        let dailyInstallsText = '';
        let totalInstallsText = '';

        // Determine the text based on the current language
        switch (language) {
            case 'zh-CN':
                publishedScriptsNumber = `已发布脚本总数:${publishedScriptsNumber}`;
                dailyInstallsText = `该用户所有脚本的今日总安装次数:${daily}`;
                totalInstallsText = `该用户所有脚本的迄今总安装次数:${total}`;
                break;
            case 'zh-TW':
                publishedScriptsNumber = `已發布腳本總數:${publishedScriptsNumber}`;
                dailyInstallsText = `該用戶所有腳本的今日總安裝次數:${daily}`;
                totalInstallsText = `該用戶所有腳本的迄今總安裝次數:${total}`;
                break;
            case 'ja':
                publishedScriptsNumber = `公開されたスクリプトの合計:${publishedScriptsNumber}`;
                dailyInstallsText = `本日の全スクリプトの合計インストール回数:${daily}`;
                totalInstallsText = `全スクリプトの累計インストール回数:${total}`;
                break;
            case 'ko':
                publishedScriptsNumber = `게시된 스크립트 총 수: ${publishedScriptsNumber}`;
                dailyInstallsText = `해당 사용자의 모든 스크립트에 대한 오늘의 총 설치 횟수: ${daily}`;
                totalInstallsText = `해당 사용자의 모든 스크립트에 대한 총 설치 횟수: ${total}`;
                break;
            case 'es':
                publishedScriptsNumber = `Número de scripts publicados: ${publishedScriptsNumber}`;
                dailyInstallsText = `Instalaciones diarias totales para todos los scripts: ${daily}`;
                totalInstallsText = `Instalaciones totales hasta la fecha para todos los scripts: ${total}`;
                break;
            case 'fr':
                publishedScriptsNumber = `Nombre de scripts publiés : ${publishedScriptsNumber}`;
                dailyInstallsText = `Installations quotidiennes totales pour tous les scripts : ${daily}`;
                totalInstallsText = `Installations totales à ce jour pour tous les scripts : ${total}`;
                break;
            case 'de':
                publishedScriptsNumber = `Anzahl der veröffentlichten Skripte: ${publishedScriptsNumber}`;
                dailyInstallsText = `Tägliche Gesamtinstallationen für alle Skripte: ${daily}`;
                totalInstallsText = `Gesamtinstallationen bis heute für alle Skripte: ${total}`;
                break;
            case 'it':
                publishedScriptsNumber = `Numero di script pubblicati: ${publishedScriptsNumber}`;
                dailyInstallsText = `Installazioni giornaliere totali per tutti gli script: ${daily}`;
                totalInstallsText = `Installazioni totali ad oggi per tutti gli script: ${total}`;
                break;
            case 'pt':
                publishedScriptsNumber = `Número de scripts publicados: ${publishedScriptsNumber}`;
                dailyInstallsText = `Total de instalações diárias para todos os scripts: ${daily}`;
                totalInstallsText = `Total de instalações até à data para todos os scripts: ${total}`;
                break;
            case 'ru':
                publishedScriptsNumber = `Количество опубликованных скриптов: ${publishedScriptsNumber}`;
                dailyInstallsText = `Общее количество ежедневных установок для всех скриптов: ${daily}`;
                totalInstallsText = `Общее количество установок на сегодняшний день для всех скриптов: ${total}`;
                break;
            default:
                publishedScriptsNumber = `Number of published scripts: ${publishedScriptsNumber}`;
                dailyInstallsText = `Total daily installations for all scripts: ${daily}`;
                totalInstallsText = `Total installations to date for all scripts: ${total}`;
        }

        if (userHeader) {
            userHeader.insertAdjacentHTML('afterend', `
                <div>${publishedScriptsNumber}</div>
                <div>${dailyInstallsText}</div>
                <div>${totalInstallsText}</div>
            `);
        }
    };

    // Function to fetch data and plot the chart
    const fetchDataAndPlot = () => {
        const currentURL = window.location.href;
        const userIDMatch = currentURL.match(/users\/(\d+)/);   // if you do concern hyphen  `-`, you should use     const userIDMatch = currentURL.match(/users\/(\d+)-/);
        const userID = userIDMatch ? userIDMatch[1] : null;

        getUserData(userID)
            .then((data) => {
                //console.log("data=", data);
                const scripts = data.all_listable_scripts || data.scripts || [];
                const filteredScripts = scripts.filter(script => !script.deleted);
                const labels = filteredScripts.map(script => script.name);
                const totalInstalls = filteredScripts.map(script => script.total_installs);
                const dailyInstalls = filteredScripts.map(script => script.daily_installs);
                const totalDailyInstalls = dailyInstalls.reduce((sum, value) => sum + value, 0);
                const totalTotalInstalls = totalInstalls.reduce((sum, value) => sum + value, 0);
                const publishedScriptsNumber = filteredScripts.length;

                plotDistribution(labels, totalInstalls, dailyInstalls);
                displayTotals(totalDailyInstalls, totalTotalInstalls, publishedScriptsNumber);
            })
            .catch((error) => console.error('Error fetching user data:', error));
    };

    // Inject Chart.js and initiate data fetching and plotting
    injectChartJs();
})();