Post
Topic
Board Guide (Italiano)
Merits 12 from 3 users
Topic OP
[Tool] Btctalk monthly stats v1.0
by
*Ace*
on 18/07/2025, 13:13:17 UTC
⭐ Merited by fillippone (9) ,babo (2) ,Lillominato89 (1)
Ciao ragazzi, da poco mi sono cimentato a scrivere userscript da utilizzare con Firefox o Chrome. Avevo visto che fillippone ogni mese costava un resoconto abbastanza bello e completo, e mi è venuta l'idea di creare uno script minimalista che restituisce alcuni dati importanti, come post scritti nel mese corrente o a ritroso, dove li avete scritti e quanti Merit avete ricevuto

Vi allego giusto 2 screenshot per avere una idea di quello che ho fatto, non è professionale ma è abbastanza carino





Inserisco anche il codice se qualcuno volesse provarlo e darmi feedback o consigli su cosa inserire, poi eventualmente valuterò di pubblicarlo anche sulla sezione internazionale

Code:
// ==UserScript==
// @name         Bitcointalk Monthly Stats + Merit Tracker
// @namespace    https://bitcointalk.org
// @version      1.0
// @description  Monthly post and merit statistics for Bitcointalk profile (mobile friendly)
// @author       *Ace*
// @match        https://bitcointalk.org/index.php?action=profile*
// @grant        none
// ==/UserScript==

(function () {
  'use strict';

  const uid = 'inserisci_il_tuo_UID';
  const username = 'inserisci_il_tuo_username';
  const boxId = 'monthlyStatsBox';
  const now = new Date();
  let currentMonthOffset = 0;

  function pad(n) {
    return n.toString().padStart(2, '0');
  }

  function addOneDay(dateString) {
    const d = new Date(dateString);
    d.setDate(d.getDate() + 1);
    return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
  }

  function getDateRange(monthOffset = 0) {
    const date = new Date(now.getFullYear(), now.getMonth() + monthOffset, 1);
    const year = date.getFullYear();
    const month = date.getMonth() + 1;
    const firstDay = `${year}-${pad(month)}-01`;
    const lastDay = new Date(year, month, 0).getDate();
    const lastDate = `${year}-${pad(month)}-${pad(lastDay)}`;
    const label = `${date.toLocaleString('en', { month: 'long' })} ${year}`;
    return { from: firstDay, to: lastDate, label, y: year, m: month };
  }

  async function fetchBoardData(from, to) {
    const url = `https://api.ninjastic.space/users/${username}/boards?from=${from}T00:00:00&to=${addOneDay(to)}T00:00:00`;
    try {
      const res = await fetch(url);
      const json = await res.json();
      if (json.result !== 'success') return null;
      return json.data;
    } catch {
      return null;
    }
  }

  async function fetchMeritData(y, m) {
    const from = `${y}-${pad(m)}-01`;
    const toDate = new Date(y, m, 0);
    const to = `${y}-${pad(m)}-${pad(toDate.getDate())}`;
    const url = `https://bpip.org/smerit.aspx?&to=${username}&start=${from}&end=${to}`;

    try {
      const res = await fetch(url);
      const htmlText = await res.text();
      const parser = new DOMParser();
      const doc = parser.parseFromString(htmlText, 'text/html');
      const rows = Array.from(doc.querySelectorAll('table tbody tr'));
      if (!rows.length) return null;

      const fromData = {};
      let total = 0;
      rows.forEach(tr => {
        const tds = tr.querySelectorAll('td');
        if (tds.length >= 4) {
          const name = tds[1].innerText.trim().replace(/\s*\(Summary\)$/i, '');
          const count = parseInt(tds[3].innerText.trim()) || 0;
          total += count;
          fromData[name] = (fromData[name] || 0) + count;
        }
      });
      return { total, fromData };
    } catch (e) {
      console.error(e);
      return null;
    }
  }

  function createBox() {
    let box = document.getElementById(boxId);
    if (box) return box;

    box = document.createElement('div');
    box.id = boxId;
    box.style.position = 'fixed';
    box.style.left = '5px';
    box.style.top = '460px';
    box.style.background = '#222';
    box.style.color = '#fff';
    box.style.padding = '12px';
    box.style.borderRadius = '12px';
    box.style.fontSize = '13px';
    box.style.maxWidth = '340px';
    box.style.zIndex = '9999';
    box.style.boxShadow = '0 0 8px rgba(0,0,0,0.6)';
    box.style.fontFamily = 'Arial, sans-serif';

    const content = document.createElement('div');
    content.id = `${boxId}-content`;
    content.innerHTML = 'Loading...';
    box.appendChild(content);

    const nav = document.createElement('div');
    nav.style.marginTop = '8px';
    nav.style.display = 'flex';
    nav.style.justifyContent = 'space-between';

    const prevBtn = document.createElement('button');
    prevBtn.textContent = '← Previous Month';
    prevBtn.style.flex = '1';
    prevBtn.style.marginRight = '4px';
    prevBtn.style.padding = '6px';
    prevBtn.style.border = 'none';
    prevBtn.style.borderRadius = '6px';
    prevBtn.style.background = '#444';
    prevBtn.style.color = '#fff';
    prevBtn.style.cursor = 'pointer';
    prevBtn.onclick = () => {
      currentMonthOffset--;
      renderStats();
    };

    const nextBtn = document.createElement('button');
    nextBtn.textContent = 'Next Month →';
    nextBtn.style.flex = '1';
    nextBtn.style.marginLeft = '4px';
    nextBtn.style.padding = '6px';
    nextBtn.style.border = 'none';
    nextBtn.style.borderRadius = '6px';
    nextBtn.style.background = '#444';
    nextBtn.style.color = '#fff';
    nextBtn.style.cursor = 'pointer';
    nextBtn.onclick = () => {
      if (currentMonthOffset < 0) {
        currentMonthOffset++;
        renderStats();
      }
    };

    nav.appendChild(prevBtn);
    nav.appendChild(nextBtn);
    box.appendChild(nav);

    document.body.appendChild(box);
    return box;
  }

  async function renderStats() {
    const box = createBox();
    const content = document.getElementById(`${boxId}-content`);
    content.innerHTML = '📊 Loading monthly data...';

    const { from, to, label, y, m } = getDateRange(currentMonthOffset);
    const boardData = await fetchBoardData(from, to);
    const meritData = await fetchMeritData(y, m);

    if (!boardData) {
      content.innerHTML = '❌ Error loading posts.';
      return;
    }

    let html = `🧮 <b>Statistics for ${label}</b><br><br>`;
    html += `📝 <b>Posts written:</b> ${boardData.total_results_with_board}<br>`;
    boardData.boards.forEach(b => {
      html += `• ${b.name}: ${b.count}<br>`;
    });

    if (!meritData) {
      html += `<br>⭐ <b>Merits received:</b> Loading error.`;
    } else {
      html += `<br>⭐ <b>Merits received:</b> ${meritData.total}<br>`;
      const sorted = Object.entries(meritData.fromData).sort((a, b) => b[1] - a[1]);
      sorted.forEach(([name, count]) => {
        html += `• ${name}: ${count}<br>`;
      });
    }

    content.innerHTML = html;
  }

  if (location.href.includes(`u=${uid}`)) {
    renderStats();
  }
})();

Basta inserire UserID e UserNAME e lo script si avvia, viene visualizzato soltanto nella nostra pagina profilo

https://bitcointalk.org/index.php?action=profile;u=xxxxx

A voi le opinioni
Grazie in anticipo ☺️