This commit is contained in:
2025-07-05 13:34:24 +00:00
parent 5c51acc8d1
commit ff5c7506ff

View File

@ -10,14 +10,35 @@ import { PrismaClient } from "../generated/prisma/client.js";
const prisma = new PrismaClient(); const prisma = new PrismaClient();
const envConfig = {
pleromaInstanceUrl: process.env.PLEROMA_INSTANCE_URL || "",
pleromaInstanceDomain: process.env.PLEROMA_INSTANCE_DOMAIN || "",
onlyLocalReplies: process.env.ONLY_LOCAL_REPLIES === "true" ? true : false,
ollamaUrl: process.env.OLLAMA_URL || "",
ollamaSystemPrompt:
process.env.OLLAMA_SYSTEM_PROMPT ||
"You are a helpful AI assistant. Answer all questions concisely.",
ollamaModel: process.env.OLLAMA_MODEL || "",
fetchInterval: process.env.FETCH_INTERVAL
? parseInt(process.env.FETCH_INTERVAL)
: 15000,
bearerToken: process.env.INSTANCE_BEARER_TOKEN || "",
};
const ollamaConfig: OllamaConfigOptions = {
temperature: 0.3,
num_predict: 400,
};
const getNotifications = async () => { const getNotifications = async () => {
const { bearerToken, pleromaInstanceUrl } = envConfig;
try { try {
const request = await fetch( const request = await fetch(
`${process.env.PLEROMA_INSTANCE_URL}/api/v1/notifications?types[]=mention`, `${pleromaInstanceUrl}/api/v1/notifications?types[]=mention`,
{ {
method: "GET", method: "GET",
headers: { headers: {
Authorization: `Bearer ${process.env.INSTANCE_BEARER_TOKEN}`, Authorization: `Bearer ${bearerToken}`,
}, },
} }
); );
@ -82,11 +103,12 @@ const storePromptData = async (
} }
}; };
const trimInputData = (input: string) => { const trimInputData = (input: string): string => {
const strippedInput = striptags(input); const strippedInput = striptags(input);
const split = strippedInput.split(" "); const split = strippedInput.split(" ");
const promptStringIndex = split.indexOf("!prompt"); const promptStringIndex = split.indexOf("!prompt");
return split.slice(promptStringIndex + 1).join(" "); // returns everything after the !prompt split.splice(promptStringIndex, 1);
return split.join(" "); // returns everything after the !prompt
}; };
const recordPendingResponse = async (notification: Notification) => { const recordPendingResponse = async (notification: Notification) => {
@ -104,6 +126,13 @@ const recordPendingResponse = async (notification: Notification) => {
const generateOllamaRequest = async ( const generateOllamaRequest = async (
notification: Notification notification: Notification
): Promise<OllamaResponse | undefined> => { ): Promise<OllamaResponse | undefined> => {
const {
onlyLocalReplies,
pleromaInstanceDomain,
ollamaModel,
ollamaSystemPrompt,
ollamaUrl,
} = envConfig;
try { try {
if ( if (
striptags(notification.status.content).includes("!prompt") && striptags(notification.status.content).includes("!prompt") &&
@ -111,10 +140,8 @@ const generateOllamaRequest = async (
notification.type === "mention" notification.type === "mention"
) { ) {
if ( if (
process.env.ONLY_LOCAL_REPLIES === "true" && onlyLocalReplies &&
!notification.status.account.fqn.includes( !notification.status.account.fqn.includes(`@${pleromaInstanceDomain}`)
`@${process.env.PLEROMA_INSTANCE_DOMAIN}`
)
) { ) {
return; return;
} }
@ -123,20 +150,16 @@ const generateOllamaRequest = async (
} }
await recordPendingResponse(notification); await recordPendingResponse(notification);
await storeUserData(notification); await storeUserData(notification);
const ollamaConfig: OllamaConfigOptions = {
temperature: 1.2,
num_predict: 400,
};
const ollamaRequestBody: OllamaRequest = { const ollamaRequestBody: OllamaRequest = {
model: process.env.OLLAMA_MODEL as string, model: ollamaModel,
system: process.env.OLLAMA_SYSTEM_PROMPT as string, system: ollamaSystemPrompt,
prompt: `@${notification.status.account.fqn} says: ${trimInputData( prompt: `@${notification.status.account.fqn} says: ${trimInputData(
notification.status.content notification.status.content
)}`, )}`,
stream: false, stream: false,
options: ollamaConfig, options: ollamaConfig,
}; };
const response = await fetch(`${process.env.OLLAMA_URL}/api/generate`, { const response = await fetch(`${ollamaUrl}/api/generate`, {
method: "POST", method: "POST",
body: JSON.stringify(ollamaRequestBody), body: JSON.stringify(ollamaRequestBody),
}); });
@ -153,6 +176,7 @@ const postReplyToStatus = async (
notification: Notification, notification: Notification,
ollamaResponseBody: OllamaResponse ollamaResponseBody: OllamaResponse
) => { ) => {
const { pleromaInstanceUrl, bearerToken } = envConfig;
try { try {
let mentions: string[]; let mentions: string[];
const statusBody: NewStatusBody = { const statusBody: NewStatusBody = {
@ -170,17 +194,14 @@ const postReplyToStatus = async (
statusBody.to = mentions; statusBody.to = mentions;
} }
const response = await fetch( const response = await fetch(`${pleromaInstanceUrl}/api/v1/statuses`, {
`${process.env.PLEROMA_INSTANCE_URL}/api/v1/statuses`,
{
method: "POST", method: "POST",
headers: { headers: {
Authorization: `Bearer ${process.env.INSTANCE_BEARER_TOKEN}`, Authorization: `Bearer ${bearerToken}`,
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
body: JSON.stringify(statusBody), body: JSON.stringify(statusBody),
} });
);
if (!response.ok) { if (!response.ok) {
throw new Error(`New status request failed: ${response.statusText}`); throw new Error(`New status request failed: ${response.statusText}`);
@ -193,6 +214,7 @@ const postReplyToStatus = async (
}; };
const deleteNotification = async (notification: Notification) => { const deleteNotification = async (notification: Notification) => {
const { pleromaInstanceUrl, bearerToken } = envConfig;
try { try {
if (!notification.id) { if (!notification.id) {
return; return;
@ -203,11 +225,11 @@ const deleteNotification = async (notification: Notification) => {
data: { isProcessing: false }, data: { isProcessing: false },
}); });
const response = await fetch( const response = await fetch(
`${process.env.PLEROMA_INSTANCE_URL}/api/v1/notifications/${notification.id}/dismiss`, `${pleromaInstanceUrl}/api/v1/notifications/${notification.id}/dismiss`,
{ {
method: "POST", method: "POST",
headers: { headers: {
Authorization: `Bearer ${process.env.INSTANCE_BEARER_TOKEN}`, Authorization: `Bearer ${bearerToken}`,
}, },
} }
); );
@ -221,10 +243,6 @@ const deleteNotification = async (notification: Notification) => {
} }
}; };
const fetchInterval = process.env.FETCH_INTERVAL
? parseInt(process.env.FETCH_INTERVAL)
: 15000;
let notifications = []; let notifications = [];
const beginFetchCycle = async () => { const beginFetchCycle = async () => {
setInterval(async () => { setInterval(async () => {
@ -243,12 +261,12 @@ const beginFetchCycle = async () => {
}) })
); );
} }
}, fetchInterval); // lower intervals may cause the bot to respond multiple times to the same message, but we try to mitigate this with the deleteNotification function }, envConfig.fetchInterval); // lower intervals may cause the bot to respond multiple times to the same message, but we try to mitigate this with the deleteNotification function
}; };
console.log( console.log(
`Fetching notifications from ${process.env.PLEROMA_INSTANCE_DOMAIN}, every ${ `Fetching notifications from ${envConfig.pleromaInstanceDomain}, every ${
fetchInterval / 1000 envConfig.fetchInterval / 1000
} seconds.` } seconds.`
); );
await beginFetchCycle(); await beginFetchCycle();