@atproto/ozone
Version:
Backend service for moderating the Bluesky network.
139 lines • 5.58 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = default_1;
const lexicons_1 = require("../../lexicon/lexicons");
const util_1 = require("../../mod-service/util");
function default_1(server, ctx) {
server.tools.ozone.moderation.getAccountTimeline({
auth: ctx.authVerifier.modOrAdminToken,
handler: async ({ params }) => {
const { did } = params;
const db = ctx.db;
const modService = ctx.modService(db);
const [modEventHistory, accountHistory, plcHistory] = await Promise.allSettled([
modService.getAccountTimeline(did),
getAccountHistory(ctx, did),
getPlcHistory(ctx, did),
]);
const timelineByDay = new Map();
if (modEventHistory.status === 'fulfilled') {
for (const row of modEventHistory.value) {
const day = timelineByDay.get(row.day);
const summary = {
eventSubjectType: row.subjectUri ? 'record' : 'account',
eventType: row.action,
count: row.count,
};
if (day) {
day.push(summary);
timelineByDay.set(row.day, day);
}
else {
timelineByDay.set(row.day, [summary]);
}
}
}
else {
throw modEventHistory.reason;
}
if (accountHistory.status === 'fulfilled') {
for (const [rowDay, row] of Object.entries(accountHistory.value)) {
const day = timelineByDay.get(rowDay);
const summaries = [];
for (const [eventType, count] of Object.entries(row)) {
summaries.push({
eventSubjectType: 'account',
eventType,
count,
});
}
if (day) {
day.push(...summaries);
timelineByDay.set(rowDay, day);
}
else {
timelineByDay.set(rowDay, summaries);
}
}
}
if (plcHistory.status === 'fulfilled') {
for (const [rowDay, row] of Object.entries(plcHistory.value)) {
const day = timelineByDay.get(rowDay);
const summaries = [];
for (const [eventType, count] of Object.entries(row)) {
summaries.push({
eventSubjectType: 'account',
eventType,
count,
});
}
if (day) {
day.push(...summaries);
timelineByDay.set(rowDay, day);
}
else {
timelineByDay.set(rowDay, summaries);
}
}
}
const timeline = [];
for (const [day, summary] of timelineByDay.entries()) {
timeline.push({ day, summary: summary.flat() });
}
return {
encoding: 'application/json',
body: { timeline },
};
},
});
}
const getAccountHistory = async (ctx, did) => {
var _a, _b;
const events = {};
if (!ctx.pdsAgent) {
return events;
}
const auth = await ctx.pdsAuth(lexicons_1.ids.ToolsOzoneHostingGetAccountHistory);
let cursor = undefined;
do {
const { data } = await ctx.pdsAgent.tools.ozone.hosting.getAccountHistory({ did, cursor }, auth);
cursor = data.cursor;
for (const event of data.events) {
// This should never happen and the check is here only because typescript screams at us otherwise
if (!event.$type) {
continue;
}
const day = (0, util_1.dateFromDatetime)(new Date(event.createdAt));
events[day] ?? (events[day] = {});
(_a = events[day])[_b = event.$type] ?? (_a[_b] = 0);
events[day][event.$type]++;
}
} while (cursor);
return events;
};
const PLC_OPERATION_MAP = {
create: 'tools.ozone.moderation.defs#timelineEventPlcCreate',
plc_operation: 'tools.ozone.moderation.defs#timelineEventPlcOperation',
plc_tombstone: 'tools.ozone.moderation.defs#timelineEventPlcTombstone',
};
const getPlcHistory = async (ctx, did) => {
var _a;
const events = {};
if (!ctx.plcClient) {
return events;
}
const result = await ctx.plcClient.getAuditableLog(did);
for (const event of result) {
// Skip events that are not mapped, this means we will have to add correct mapping if/when new event types are introduced here
if (!Object.hasOwn(PLC_OPERATION_MAP, event.operation.type)) {
continue;
}
const day = (0, util_1.dateFromDatetime)(new Date(event.createdAt));
events[day] ?? (events[day] = {});
const eventType = PLC_OPERATION_MAP[event.operation.type] || event.operation.type;
(_a = events[day])[eventType] ?? (_a[eventType] = 0);
events[day][eventType]++;
}
return events;
};
//# sourceMappingURL=getAccountTimeline.js.map