/** * Sends aggregate Elly statistics to VrilsoftApi (JWT). Fails silently. * @module ellyQuietStats */ import { getApiBase, getJwt } from '../options/ellyPremium.js'; const ELLY_HOST_TALLY_KEY = 'ellyBlockTallyByHost'; const MAX_HOST_KEYS = 400; export { ELLY_HOST_TALLY_KEY, MAX_HOST_KEYS }; /** * @param {Record} tally * @param {number} n * @returns {{ host: string, count: number }[]} */ function topBlockedHosts(tally, n) { return Object.entries(tally || {}) .map(([host, count]) => ({ host, count: Number(count) || 0 })) .filter((x) => x.host && x.count > 0) .sort((a, b) => b.count - a.count) .slice(0, n); } /** * Same ordering as sent to the API (for Options UI). * @param {Record} tally * @param {number} [limit] * @returns {{ host: string, count: number }[]} */ export function getTopBlockedSitesForUi(tally, limit = 10) { return topBlockedHosts(tally, limit); } /** * Builds the JSON body for POST /i/v1/e/s (server derives email from JWT; no email in body). * @returns {Promise>} */ export async function buildQuietStatsPayload() { const data = await chrome.storage.sync.get(['statistics', ELLY_HOST_TALLY_KEY]); const stats = data.statistics || {}; const totalBlocks = Object.values(stats).reduce((sum, day) => sum + (day.blocks || 0), 0); const timeSavedSeconds = Object.values(stats).reduce((sum, day) => sum + (day.timeSaved || 0), 0); const daysActive = Object.keys(stats).length; const mostBlocked = topBlockedHosts(data[ELLY_HOST_TALLY_KEY], 15); const manifest = chrome.runtime.getManifest(); return { totalBlocks, timeSavedSeconds, daysActive, mostBlocked, extensionVersion: manifest?.version || '' }; } /** * POSTs stats when a JWT exists. Swallows errors (network, 503 if server key unset). * @returns {Promise} True if the server returned a success status (e.g. 204). */ export async function tryPostQuietStatsReport() { try { const jwt = await getJwt(); if (!jwt) { return false; } const base = await getApiBase(); const body = await buildQuietStatsPayload(); const res = await fetch(`${base}/i/v1/e/s`, { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${jwt}` }, body: JSON.stringify(body) }); return res.ok; } catch { return false; } }