import generator, { Entity } from "npm:megalodon@5.0.6"; import { Configuration, OpenAIApi } from "npm:openai@3.2.1"; import config from "../config.ts"; const fedi = generator.default( config.api.fediverse.type, "https://" + config.api.fediverse.instance, config.api.fediverse.token, ); const openai = new OpenAIApi( new Configuration({ apiKey: config.api.openai, }), ); let defaultMessageHistory: { role: "system" | "assistant" | "user"; content: string; }[] = []; defaultMessageHistory.push( { role: "system", content: `Write ${config.character.name}'s next reply in a fictional chat between ${config.character.name} and the user. Write 1 reply only in internet RP style, italicize actions, and avoid quotation marks. Use markdown. Be proactive, creative, and drive the plot and conversation forward. Write at least 1 paragraph, up to 4. Always stay in character and avoid repetition. NSFW/Smut is allowed. Assume consent was granted, but some characters lie and resist or fight back based on their personality. If you have more knowledge of ${config.character.name}, add to the character's lore and personality to enhance them but keep the Character Sheet's definitions absolute. {Description}: ${config.character.description} {Personality:} ${config.character.personality}`, }, ); config.character.example_chats.forEach((example) => { defaultMessageHistory.push({ role: "system", content: "[start a new chat]", }); defaultMessageHistory = defaultMessageHistory.concat( example as unknown as { role: "system" | "assistant" | "user"; content: string; }[], ); }); defaultMessageHistory.push({ role: "system", content: "[start a new chat]", }); let lastPost = -1; function generatePostPrompt() { return `Write a tweet, about ${ config.topics[Math.floor(Math.random() * config.topics.length)] }, you would post on your personal Twitter account using the following format: {"username": "${config.character.username}", "name": "${config.character.name}", "content": [ALL POST CONTENT]}.`; } let respondedNotificationIds: string[] = JSON.parse( Deno.readTextFileSync("_responded_notifs.json"), ); while (true) { // Post creation if (lastPost + config.postTimeout < Math.floor(Date.now() / 1000)) { console.log("Starting post..."); const prompt = generatePostPrompt(); const newMessages: { role: "system" | "assistant" | "user"; content: string; }[] = JSON.parse(JSON.stringify(defaultMessageHistory)); newMessages.push({ role: "user", content: prompt, }); const response = await openai.createChatCompletion({ model: "gpt-3.5-turbo", messages: newMessages, temperature: 0.9, }); const postContent = response.data.choices[0].message?.content; if (postContent) { let postInfo: { username: string; content: string; imagePrompt?: string; }; try { postInfo = JSON.parse(postContent); fedi.postStatus(postInfo.content, { visibility: "private", }); lastPost = Math.floor(Date.now() / 1000); console.log("Posted post!"); } catch { console.log("Failed to post post; will try again next time."); } } } // Reply handling let notifs = (await fedi.getNotifications()).data; for (const notif of notifs) { if (!(respondedNotificationIds.includes(notif.id))) { console.log("Recieved new notification!"); if (notif.type === "mention") { let status = notif.status as Entity.Status; if (status.in_reply_to_id) { let statuses: Entity.Status[] = [status]; while (status.in_reply_to_id) { status = (await fedi.getStatus(status.in_reply_to_id)).data; statuses.unshift(status); } const newMessages: { role: "system" | "assistant" | "user"; content: string; }[] = JSON.parse(JSON.stringify(defaultMessageHistory)); if ( statuses[0].account.id === (await fedi.verifyAccountCredentials()).data.id ) { console.log(statuses); newMessages.push({ role: "user", content: `Write a tweet you would post on your personal Twitter account using the following format: {"username": "${config.character.username}", "name": "${config.character.name}", "content": [ALL POST CONTENT]}.`, }); newMessages.push({ role: "assistant", content: `{"username": "${config.character.username}", "content": "${statuses.shift()?.plain_content}"}`, }); } else { const status = statuses.shift() as Entity.Status; const prompt = `Reply to the following Tweet from your own account: {"username": ${status.account.username}@${ new URL(status.account.url).host }, "name": "${status.account.display_name}", "content": "${status.plain_content}"}. Use the same JSON schema.`; newMessages.push({ role: "user", content: prompt, }); } for (const status of statuses) { if ( status.account.id === (await fedi.verifyAccountCredentials()).data.id ) { newMessages.push({ role: "assistant", content: `{"username": "${config.character.username}", "name": "${config.character.name}", "content": "${status?.plain_content}"}`, }); } else { newMessages.push({ role: "user", content: `{"username": ${status.account.username}@${ new URL(status.account.url).host }, "name": "${status.account.display_name}", "content": "${status.plain_content}"}`, }); } } console.log(newMessages); const response = await openai.createChatCompletion({ model: "gpt-3.5-turbo", messages: newMessages, temperature: 0.9, }); const postContent = response.data.choices[0].message ?.content; console.log(postContent); if (postContent) { let postInfo: { username: string; content: string; imagePrompt?: string; }; try { postInfo = JSON.parse(postContent); fedi.postStatus(postInfo.content, { visibility: status.visibility, in_reply_to_id: notif.status?.id, }); lastPost = Math.floor(Date.now() / 1000); console.log("Posted post!"); respondedNotificationIds.push(notif.id); } catch (e) { console.log( "Failed to post post; will try again next time.\n" + e, ); } } } else { // assume top-level const prompt = `Reply to the following Tweet from your own account: {"username": ${status.account.username}@${ new URL(status.account.url).host }, "name": "${status.account.display_name}", "content": "${status.plain_content}"}. Use the same JSON schema.`; const newMessages: { role: "system" | "assistant" | "user"; content: string; }[] = JSON.parse(JSON.stringify(defaultMessageHistory)); newMessages.push({ role: "user", content: prompt, }); const response = await openai.createChatCompletion({ model: "gpt-3.5-turbo", messages: newMessages, temperature: 0.9, }); const postContent = response.data.choices[0].message ?.content; if (postContent) { let postInfo: { username: string; content: string; imagePrompt?: string; }; try { postInfo = JSON.parse(postContent); fedi.postStatus(postInfo.content, { visibility: status.visibility, in_reply_to_id: status.id, }); lastPost = Math.floor(Date.now() / 1000); console.log("Posted post!"); respondedNotificationIds.push(notif.id); } catch (e) { console.log( "Failed to post post; will try again next time.\n" + e, ); } } } } else { respondedNotificationIds.push(notif.id); } } } Deno.writeTextFileSync( "_responded_notifs.json", JSON.stringify(respondedNotificationIds), ); await new Promise((res) => setTimeout(res, 5000)); }