#!/usr/bin/env -S npx nodejsscript /* jshint esversion: 11,-W097, -W040, module: true, node: true, expr: true, undef: true *//* global echo, $, pipe, s, fetch, cyclicLoop */ const store= "./mastodonCzStats.json"; const instances_cz_pre= new Set(s.cat(store).xargs(JSON.parse).instances_cz.map(i=> i.instance)); const { version, description }= s.cat("package.json").xargs(JSON.parse); const timeOut= (l= 500)=> AbortSignal.timeout(l); $.api("", true) .version(version) .describe(description) .option("--git", "Commit changes to git") .action(async function main({ git: is_git }){ let stats= []; for(const instance of instances_cz_pre){ echo.use("-R", "Shromažďuji instance: " + instance); const peers= await fetch("https://"+instance+"/api/v1/instance/peers", { signal: timeOut() }) .then(res=> res.json()).catch(()=> []); if(peers && Array.isArray(peers)) peers.filter(i=> i.endsWith(".cz")).forEach(i=> instances_cz_pre.add(i)); } const instances_cz= Array.from(instances_cz_pre); const { length }= instances_cz; for await(const data of collectStats(instances_cz, 4)){ echo.use("-R", "Shromažďuji data za instance: " + instances_cz.indexOf(data.instance)+"/"+length+": "+data.instance); stats.push(data); } const out= { instances_cz: stats.sort((a, b)=> b.registrations-a.registrations), sum: stats.reduce((acc, stats) => sumStats([acc, stats]), { statuses: 0, logins: 0, registrations: 0, mau: 0, user_count: 0 }) }; echo(JSON.stringify(out, null, " ")).to(store); if(is_git) gitCommit([ store ], "cron"); $.exit(0); }) .parse(); function gitCommit(files, des= "not specified"){ if(!files.length || !s.run`git diff --numstat`.trim()) return echo("Nothig todo"); echo("Diff to save"); s.run`git config user.name "Bot"`; s.run`git config user.email "${"zc.murtnec@naj.elrdna".split("").reverse().join("")}"`; s.run`git add ${files}`; s.run`git commit -m "Updated by bot – ${des}"`; s.run`git push`; s.run`git config --remove-section user`; } async function* collectStats(instances, weeks = 1) { for(const instance of instances){ const { stats, mau, user_count }= await fetchInstanceStats(instance).catch(e=> ({})); if(!stats || !Array.isArray(stats)) continue; yield { instance, mau, user_count, ...sumStats(stats.slice(0, weeks + 1)) }; } } function sumStats(arr) { const out= Object.keys(arr[0]).reduce((acc, curr)=> (acc[curr]= 0, acc), {}); for (const key of Object.keys(out)) for (const s of arr) out[key]+= Number(s[key]); return out; } async function fetchInstanceStats(instance) { const url_main= "https://" + instance + "/api/"; const stats= await fetch(url_main+"v1/instance/activity", { signal: timeOut() }).then(res=> res.json()); const { usage: { users } }= await fetch(url_main+"v2/instance", { signal: timeOut() }).then(res=> res.json()); const { stats: { user_count } }= await fetch(url_main+"v1/instance", { signal: timeOut() }).then(res=> res.json()); return { instance, stats, mau: users.active_month, user_count }; }