import { api } from "misskey-js"; import type { Note } from "misskey-js/entities.js"; import { sample } from "./util"; import type { Message } from "./llm"; export const misskey = new api.APIClient({ origin: Bun.env["MISSKEY_ORIGIN"] || "https://misskey.cannorin.net", credential: Bun.env["MISSKEY_CREDENTIAL"], }); export const me = await misskey.request("i", {}); /** check if a note is suitable as an input */ export const isSuitableAsInput = (n: Note) => !n.user.isBot && !n.replyId && (!n.mentions || n.mentions.length === 0) && n.text?.length && n.text.length > 0; /** randomly sample some notes from the timeline */ export async function getNotes() { // randomly sample N local notes const localNotes = (count: number) => misskey .request("notes/local-timeline", { limit: 100 }) .then((xs) => xs.filter(isSuitableAsInput)) .then((xs) => sample(xs, count)); // randomly sample N global notes const globalNotes = (count: number) => misskey .request("notes/global-timeline", { limit: 100 }) .then((xs) => xs.filter(isSuitableAsInput)) .then((xs) => sample(xs, count)); const notes = await Promise.all([localNotes(5), globalNotes(10)]); return sample(notes.flat()); } /** fetch the whole reply tree */ export async function expandReplyTree( note: Note, acc: Note[] = [], cutoff = 5, ) { if (!note.reply || cutoff < 1) return [...acc, note]; const reply = await misskey.request("notes/show", { noteId: note.reply.id }); return await expandReplyTree(reply, [...acc, note], cutoff - 1); } /** convert a note to a chat message */ export const noteToMessage = (note: Note): Message => ({ type: note.userId === me.id ? ("model" as const) : ("user" as const), text: note.text?.replaceAll(`@${me.username}`, "") || "", });