auron
Version:
Interact with your ATProto labeler from your terminal
167 lines (145 loc) • 4.96 kB
text/typescript
import EventEmitter from "events";
import { withLoader } from "../utils/loader";
import { getQueueItems, getRecords, getRepos } from "../api/atproto";
import { database } from "../services/db";
import { ToolsOzoneModerationDefs } from "@atproto/api";
import {
transformRecordViewToRecord,
transformRepoViewToRepo,
transformStatusToSubject,
} from "../services/subject/transformers";
import { chunkArray } from "@atproto/common";
export const fetchQueueItems = async (options: {
count: number;
cursor?: string;
}) => {
const emitter = new EventEmitter();
const subjects = await withLoader(
`Fetching queue items...`,
async (updateMessage) => {
emitter.on("update", ({ nextCursor, subjectCount, maxCount }) => {
updateMessage(
`Fetched page with cursor: ${nextCursor}, total subjects: ${subjectCount} out of ${maxCount}`
);
});
const { cursor, subjectStatuses } = await getQueueItems(
{
maxCount: options.count ? Number(options.count) : undefined,
cursor: options.cursor,
},
emitter
);
updateMessage(
`Fetched ${subjectStatuses.length} queue items. Cursor: ${cursor}`
);
return subjectStatuses;
}
);
return subjects;
};
export const saveQueueItems = async (
subjects: ToolsOzoneModerationDefs.SubjectStatusView[]
) => {
await withLoader(
`Storing ${subjects.length} queue items...`,
async (updateMessage) => {
if (subjects.length === 0) {
updateMessage("No queue items to store");
throw new Error("No queue items to store");
}
for (const chunk of chunkArray(subjects, 500)) {
await database.insertSubjects(chunk.map(transformStatusToSubject));
updateMessage(
`Stored ${chunk.length} out of ${subjects.length} queue items in local database`
);
}
updateMessage(`Stored ${subjects.length} queue items in local database`);
}
);
};
export const fetchReposForSubjects = async () => {
const emitter = new EventEmitter();
const subjects = await withLoader(
`Fetching repos for subjects...`,
async (updateMessage) => {
emitter.on("update", ({ total, repoCount }) => {
updateMessage(`Fetched ${repoCount} repos out of ${total}`);
});
const dids = await database.getMissingRepoDids();
updateMessage(`Found ${dids.length} missing repos to be fetched`);
if (dids.length === 0) {
return;
}
const repos = await getRepos({ dids }, emitter);
updateMessage(
`Fetched ${repos.length} repos. Saving in local database now`
);
for (const chunk of chunkArray(repos, 500)) {
await database.saveRepos(chunk.map(transformRepoViewToRepo));
updateMessage(
`Saved ${chunk.length} out of ${repos.length} repos in local database now`
);
}
updateMessage(`Saved ${repos.length} repos in local database`);
return;
}
);
return subjects;
};
export const fetchRecordsForSubjects = async () => {
const emitter = new EventEmitter();
const subjects = await withLoader(
`Fetching records for subjects...`,
async (updateMessage) => {
emitter.on("update", ({ total, recordCount }) => {
updateMessage(`Fetched ${recordCount} records out of ${total}`);
});
const uris = await database.getMissingRecordUris();
updateMessage(`Found ${uris.length} missing records to be fetched`);
if (uris.length === 0) {
return;
}
const records = await getRecords({ uris }, emitter);
updateMessage(
`Fetched ${records.length} records. Saving in local database now`
);
for (const chunk of chunkArray(records, 500)) {
await database.saveRecords(chunk.map(transformRecordViewToRecord));
updateMessage(
`Saved ${chunk.length} out of ${records.length} records in local database`
);
}
updateMessage(`Saved ${records.length} records in local database`);
return;
}
);
return subjects;
};
export const processSubjects = async (options: {
type?: string;
cursor?: string;
count?: number;
bio?: string;
keyword?: string;
}) => {
let subjects = await database.listSubjects({
subjectType: options.type,
cursor: options.cursor,
limit: options.count,
});
if (options.bio || options.keyword) {
const keyword = `${options.bio || options.keyword}`.toLowerCase();
subjects = subjects.filter((subject) => {
const profile = JSON.parse(subject.profile || "{}");
return profile.description?.toLowerCase().includes(keyword);
});
}
if (options.keyword) {
const keyword = options.keyword.toLowerCase();
subjects = subjects.filter((subject) => {
const recordValue = JSON.parse(subject.value || "{}");
return recordValue.text?.toLowerCase().includes(keyword);
});
}
return subjects;
};