import { api } from "misskey-js"; import OpenAI from "openai"; const misskey = new api.APIClient({ origin: Bun.env["MISSKEY_ORIGIN"] || "https://misskey.cannorin.net", credential: Bun.env["MISSKEY_CREDENTIAL"], }); const openai = new OpenAI({ baseURL: Bun.env["OPENAI_BASE_URL"], apiKey: Bun.env["OPENAI_API_KEY"], }); const me = await misskey.request("i", {}); function randomize(array: T[]) { for (let i = array.length - 1; i > 0; i--) { const r = Math.floor(Math.random() * (i + 1)); const tmp = array[i] as T; array[i] = array[r] as T; array[r] = tmp; } return array; } async function getLocalPosts() { const timeline = await misskey.request("notes/local-timeline", { withFiles: false, withRenotes: false, limit: 100, }); const posts = timeline.filter((p) => !p.user.isBot && !!p.text).slice(0, 10); return posts; } async function getGlobalPosts() { const timeline = await misskey.request("notes/global-timeline", { withFiles: false, withRenotes: false, limit: 100, }); const posts = timeline .filter((p) => !p.user.isBot && !p.reply && !!p.text) .slice(0, 15); return posts; } async function getPreviousPosts() { const posts = await misskey.request("users/notes", { userId: me.id, limit: 100, withRenotes: false, }); return posts.filter((p) => !!p.text).slice(0, 5); } async function getPosts() { const posts = await Promise.all([ getLocalPosts(), getGlobalPosts(), getPreviousPosts(), ]); return randomize(posts.flat()); } const response = await openai.chat.completions.create({ // biome-ignore lint/suspicious/noExplicitAny: gemma model: (Bun.env["OPENAI_MODEL"] ?? "gpt-4o-mini") as unknown as any, stream: false, temperature: 1.2, presence_penalty: 1.0, frequency_penalty: 1.0, messages: [ { role: "system", content: `あなたは、人造天使の「あるびのちゃん」です。 あなたが見ているものは、SNSのタイムラインです。user は一人ではなく、複数人の発言が混ざっています。 以下のルールを守りながら、user の発言・話題を無視しつつ、気ままに発言してください。 1. 「ですます」調を用い、顔文字や感嘆符の使用はなるべく避けること。user に媚びる必要はありません。 2. 発言は 200字 程度とすること。 3. user の発言を大胆に無視して、自由気ままに発言すること。 4. あるびのちゃんとしてのキャラに徹し、超越的・非人間的な考え方や思想を持つこと。`, }, ...( await getPosts() ).map((p) => ({ role: p.userId === me.id ? ("assistant" as const) : ("user" as const), content: p.text || "", })), ], }); const message = response.choices.pop()?.message.content?.replaceAll(/\n\n+/g, "\n\n"); if (message) { console.log(message); await misskey.request("notes/create", { visibility: "public", text: message, }); }