/**
* 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<string, number>} 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<string, number>} 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<Record<string, unknown>>}
*/
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<boolean>} 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;
}
}