initial commit

master
Drake 12 months ago
commit 3ef87655d8

1
.gitignore vendored

@ -0,0 +1 @@
config.ts

@ -0,0 +1,9 @@
{
"deno.enable": true,
"deno.unstable": true,
"editor.formatOnSave": true,
"[typescript]": {
"editor.tabSize": 4,
"editor.defaultFormatter": "denoland.vscode-deno"
}
}

@ -0,0 +1 @@
I might write docs, or a config template, or anything on how to run this.. eventually. Maybe.

@ -0,0 +1,7 @@
{
"fmt": {
"options": {
"indentWidth": 4
}
}
}

@ -0,0 +1,222 @@
{
"version": "2",
"remote": {},
"npm": {
"specifiers": {
"megalodon@5.0.6": "megalodon@5.0.6",
"megalodon@5.4.1": "megalodon@5.4.1",
"openai@3.2.1": "openai@3.2.1"
},
"packages": {
"@types/node@18.14.6": {
"integrity": "sha512-93+VvleD3mXwlLI/xASjw0FzKcwzl3OdTCzm1LaRfqgS21gfFtK3zDXM5Op9TeeMsJVOaJ2VRDpT9q4Y3d0AvA==",
"dependencies": {}
},
"@types/oauth@0.9.1": {
"integrity": "sha512-a1iY62/a3yhZ7qH7cNUsxoI3U/0Fe9+RnuFrpTKr+0WVOzbKlSLojShCKe20aOD1Sppv+i8Zlq0pLDuTJnwS4A==",
"dependencies": {
"@types/node": "@types/node@18.14.6"
}
},
"@types/ws@8.5.4": {
"integrity": "sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg==",
"dependencies": {
"@types/node": "@types/node@18.14.6"
}
},
"agent-base@6.0.2": {
"integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
"dependencies": {
"debug": "debug@4.3.4"
}
},
"asynckit@0.4.0": {
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
"dependencies": {}
},
"axios@0.26.1": {
"integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==",
"dependencies": {
"follow-redirects": "follow-redirects@1.15.2"
}
},
"axios@1.2.2": {
"integrity": "sha512-bz/J4gS2S3I7mpN/YZfGFTqhXTYzRho8Ay38w2otuuDR322KzFIWm/4W2K6gIwvWaws5n+mnb7D1lN9uD+QH6Q==",
"dependencies": {
"follow-redirects": "follow-redirects@1.15.2",
"form-data": "form-data@4.0.0",
"proxy-from-env": "proxy-from-env@1.1.0"
}
},
"axios@1.3.4": {
"integrity": "sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==",
"dependencies": {
"follow-redirects": "follow-redirects@1.15.2",
"form-data": "form-data@4.0.0",
"proxy-from-env": "proxy-from-env@1.1.0"
}
},
"combined-stream@1.0.8": {
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"dependencies": {
"delayed-stream": "delayed-stream@1.0.0"
}
},
"dayjs@1.11.7": {
"integrity": "sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ==",
"dependencies": {}
},
"debug@4.3.4": {
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
"dependencies": {
"ms": "ms@2.1.2"
}
},
"delayed-stream@1.0.0": {
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"dependencies": {}
},
"follow-redirects@1.15.2": {
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
"dependencies": {}
},
"form-data@4.0.0": {
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
"dependencies": {
"asynckit": "asynckit@0.4.0",
"combined-stream": "combined-stream@1.0.8",
"mime-types": "mime-types@2.1.35"
}
},
"https-proxy-agent@5.0.1": {
"integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
"dependencies": {
"agent-base": "agent-base@6.0.2",
"debug": "debug@4.3.4"
}
},
"ip@2.0.0": {
"integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==",
"dependencies": {}
},
"megalodon@5.0.6": {
"integrity": "sha512-Tt27g71M852mw14LvCEDgOeIEoP6tHRVRDJMxl0BYVGr/IpYC0Zpd5M3ql0teV3JL8Sl5kSAI2jt0mYzpsuilg==",
"dependencies": {
"@types/oauth": "@types/oauth@0.9.1",
"@types/ws": "@types/ws@8.5.4",
"axios": "axios@1.2.2",
"dayjs": "dayjs@1.11.7",
"form-data": "form-data@4.0.0",
"https-proxy-agent": "https-proxy-agent@5.0.1",
"oauth": "oauth@0.10.0",
"object-assign-deep": "object-assign-deep@0.4.0",
"parse-link-header": "parse-link-header@2.0.0",
"socks-proxy-agent": "socks-proxy-agent@7.0.0",
"typescript": "typescript@4.9.4",
"uuid": "uuid@9.0.0",
"ws": "ws@8.5.0"
}
},
"megalodon@5.4.1": {
"integrity": "sha512-6UPbODxOFnZPChHEcoZvwTSjJaPmSMQWW40snBIT/YFxpcwH8Q8GfqYRIG46eAKl1PQCUI9gFpEvt9I+l9KKVA==",
"dependencies": {
"@types/oauth": "@types/oauth@0.9.1",
"@types/ws": "@types/ws@8.5.4",
"axios": "axios@1.3.4",
"dayjs": "dayjs@1.11.7",
"form-data": "form-data@4.0.0",
"https-proxy-agent": "https-proxy-agent@5.0.1",
"oauth": "oauth@0.10.0",
"object-assign-deep": "object-assign-deep@0.4.0",
"parse-link-header": "parse-link-header@2.0.0",
"socks-proxy-agent": "socks-proxy-agent@7.0.0",
"typescript": "typescript@4.9.5",
"uuid": "uuid@9.0.0",
"ws": "ws@8.12.1"
}
},
"mime-db@1.52.0": {
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"dependencies": {}
},
"mime-types@2.1.35": {
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"dependencies": {
"mime-db": "mime-db@1.52.0"
}
},
"ms@2.1.2": {
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dependencies": {}
},
"oauth@0.10.0": {
"integrity": "sha512-1orQ9MT1vHFGQxhuy7E/0gECD3fd2fCC+PIX+/jgmU/gI3EpRocXtmtvxCO5x3WZ443FLTLFWNDjl5MPJf9u+Q==",
"dependencies": {}
},
"object-assign-deep@0.4.0": {
"integrity": "sha512-54Uvn3s+4A/cMWx9tlRez1qtc7pN7pbQ+Yi7mjLjcBpWLlP+XbSHiHbQW6CElDiV4OvuzqnMrBdkgxI1mT8V/Q==",
"dependencies": {}
},
"openai@3.2.1": {
"integrity": "sha512-762C9BNlJPbjjlWZi4WYK9iM2tAVAv0uUp1UmI34vb0CN5T2mjB/qM6RYBmNKMh/dN9fC+bxqPwWJZUTWW052A==",
"dependencies": {
"axios": "axios@0.26.1",
"form-data": "form-data@4.0.0"
}
},
"parse-link-header@2.0.0": {
"integrity": "sha512-xjU87V0VyHZybn2RrCX5TIFGxTVZE6zqqZWMPlIKiSKuWh/X5WZdt+w1Ki1nXB+8L/KtL+nZ4iq+sfI6MrhhMw==",
"dependencies": {
"xtend": "xtend@4.0.2"
}
},
"proxy-from-env@1.1.0": {
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
"dependencies": {}
},
"smart-buffer@4.2.0": {
"integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==",
"dependencies": {}
},
"socks-proxy-agent@7.0.0": {
"integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==",
"dependencies": {
"agent-base": "agent-base@6.0.2",
"debug": "debug@4.3.4",
"socks": "socks@2.7.1"
}
},
"socks@2.7.1": {
"integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==",
"dependencies": {
"ip": "ip@2.0.0",
"smart-buffer": "smart-buffer@4.2.0"
}
},
"typescript@4.9.4": {
"integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==",
"dependencies": {}
},
"typescript@4.9.5": {
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
"dependencies": {}
},
"uuid@9.0.0": {
"integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==",
"dependencies": {}
},
"ws@8.12.1": {
"integrity": "sha512-1qo+M9Ba+xNhPB+YTWUlK6M17brTut5EXbcBaMRN5pH5dFrXz7lzz1ChFSUq3bOUl8yEvSenhHmYUNJxFzdJew==",
"dependencies": {}
},
"ws@8.5.0": {
"integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==",
"dependencies": {}
},
"xtend@4.0.2": {
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
"dependencies": {}
}
}
}
}

@ -0,0 +1,21 @@
import generator from "npm:megalodon@5.4.1";
const client = generator.default("pleroma", "https://social.ruthenic.com");
const appData = await client.registerApp("Test", {
scopes: ["read", "write", "follow"],
});
console.log(appData.url);
const code = prompt("Enter auth code:");
if (code) {
console.log(
await client.fetchAccessToken(
appData.clientId,
appData.clientSecret,
code,
),
);
}

@ -0,0 +1,252 @@
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));
}
Loading…
Cancel
Save