UNPKG

auron

Version:

Interact with your ATProto labeler from your terminal

232 lines (208 loc) 7.42 kB
import EventEmitter from "events"; import { withLoader } from "../utils/loader"; import { database } from "../services/db"; import { writeFile } from "node:fs/promises"; type ActionTimeMetric = { item: number; time: number; }; type ActionTimeMetricGroup = { human: ActionTimeMetric; automated: ActionTimeMetric; unhandled: number; }; const automods = [ "did:plc:fokhsjjl5opb2hrkcmc2swfi", "did:plc:znoll2wxc7jt736ulwzlxiqk", "did:plc:ar7c4by46qjdydhdevvrndac", ]; // Define histogram buckets (time in minutes) const timeBins = [ { label: "0-10 secs", min: 0, max: 10 }, { label: "10-30 secs", min: 10, max: 30 }, { label: "30-60 secs", min: 30, max: 60 }, { label: "1-5 mins", min: 60, max: 300 }, { label: "5-10 mins", min: 300, max: 600 }, { label: "10-30 mins", min: 600, max: 1800 }, { label: "30-60 mins", min: 1800, max: 3600 }, { label: "1-3 hrs", min: 3600, max: 10800 }, { label: "3-6 hrs", min: 10800, max: 21600 }, { label: "6-12 hrs", min: 21600, max: 43200 }, { label: "12-24 hrs", min: 43200, max: 86400 }, { label: "1-2 days", min: 86400, max: 172800 }, { label: "2-3 days", min: 172800, max: 259200 }, { label: "3-4 days", min: 259200, max: 345600 }, { label: "4-5 days", min: 345600, max: 432000 }, { label: "5-7 days", min: 432000, max: 604800 }, { label: "7-10 days", min: 604800, max: 864000 }, { label: "10-15 days", min: 864000, max: 1296000 }, { label: "15-20 days", min: 1296000, max: 1728000 }, ]; export const computeActionTime = async (options: { start?: string; end?: string; file?: string; }) => { const emitter = new EventEmitter(); const allEvents = await withLoader( `Computing action time...`, async (updateMessage) => { const startedAt = new Date().getTime(); emitter.on("update", ({ message }) => { updateMessage(message); }); const events = await database.getEvents({ cursor: 0, createdAfter: options.start, createdBefore: options.end, types: [ "tools.ozone.moderation.defs#modEventReport", "tools.ozone.moderation.defs#modEventAcknowledge", "tools.ozone.moderation.defs#modEventTakeDown", "tools.ozone.moderation.defs#modEventLabel", ], }); const metrics: Record<string, ActionTimeMetricGroup> = { account: { human: { item: 0, time: 0 }, automated: { item: 0, time: 0 }, unhandled: 0, }, record: { human: { item: 0, time: 0 }, automated: { item: 0, time: 0 }, unhandled: 0, }, }; // Initialize separate histograms for human and automated actions const humanHandledRecords: Record<string, number> = {}; const automatedHandledRecords: Record<string, number> = {}; const humanHandledAccounts: Record<string, number> = {}; const automatedHandledAccounts: Record<string, number> = {}; for (const bin of timeBins) { humanHandledRecords[bin.max] = 0; automatedHandledRecords[bin.max] = 0; humanHandledAccounts[bin.max] = 0; automatedHandledAccounts[bin.max] = 0; } let i = 0; const subjectReportTime = new Map<string, string>(); const unhandledSubjects = new Set<string>(); let oldestReportTime = new Date().toISOString(); let latestReportTime = new Date(0).toISOString(); for (const event of events) { const isRecord = !!event.subjectUri; const subject = event.subjectUri || event.subjectDid; const reportTime = subjectReportTime.get(subject); if (event.action === "tools.ozone.moderation.defs#modEventReport") { if (!reportTime) { subjectReportTime.set(subject, event.createdAt); } if (new Date(event.createdAt) < new Date(oldestReportTime)) { oldestReportTime = event.createdAt; } if (new Date(event.createdAt) > new Date(latestReportTime)) { latestReportTime = event.createdAt; } unhandledSubjects.add(subject); continue; } if (!reportTime) { continue; } const isAutomated = automods.includes(event.createdBy); const timeDiffMs = new Date(event.createdAt).getTime() - new Date(reportTime).getTime(); const timeDiffMins = timeDiffMs / 1000; // Convert ms to minutes // Assign to histogram bins const targetHistogram = isRecord ? isAutomated ? automatedHandledRecords : humanHandledRecords : isAutomated ? automatedHandledAccounts : humanHandledAccounts; for (const bin of timeBins) { // if (bin.max === 10) { // console.log(event) // } if (timeDiffMins >= bin.min && timeDiffMins < bin.max) { targetHistogram[bin.max]++; break; } } // Store into metrics if (isRecord) { if (isAutomated) { metrics.record.automated.item++; metrics.record.automated.time += timeDiffMs; } else { metrics.record.human.item++; metrics.record.human.time += timeDiffMs; } } else { if (isAutomated) { metrics.account.automated.item++; metrics.account.automated.time += timeDiffMs; } else { metrics.account.human.item++; metrics.account.human.time += timeDiffMs; } } unhandledSubjects.delete(subject); subjectReportTime.delete(subject); i++; if (i % 10000 === 0) { const timeSpent = parseInt( `${(new Date().getTime() - startedAt) / 1000}` ); emitter.emit("update", { message: `Processed ${i} out of ${events.length} event chunks. Time spent: ${timeSpent}s`, }); await new Promise((resolve) => setTimeout(resolve, 1000)); } } console.log( `\nReport time range: ${oldestReportTime} - ${latestReportTime}` ); if (unhandledSubjects.size > 0) { for (const unhandled of unhandledSubjects) { if (unhandled.startsWith("did:")) { metrics.account.unhandled++; } else { metrics.record.unhandled++; } } } if (options.file) { await writeFile( options.file, JSON.stringify( { metrics, humanHandledRecords, automatedHandledRecords, humanHandledAccounts, automatedHandledAccounts, oldestReportTime, latestReportTime, }, null, 2 ), "utf-8" ); } else { console.log(`\n Human-Handled Action Time Histogram (Records):`); console.table(humanHandledRecords); console.log(`\n Automated-Handled Action Time Histogram (Records):`); console.table(automatedHandledRecords); console.log(`\n Human-Handled Action Time Histogram (Accounts):`); console.table(humanHandledAccounts); console.log(`\n Automated-Handled Action Time Histogram (Accounts):`); console.table(automatedHandledAccounts); } } ); return allEvents; };