its called we do a little abstraction
This commit is contained in:
		
							
								
								
									
										75
									
								
								src/api.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								src/api.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,75 @@ | ||||
| import { envConfig, prisma } from "./main.js"; | ||||
| import { PleromaEmoji, Notification } from "../types.js"; | ||||
|  | ||||
| const getNotifications = async () => { | ||||
|   const { bearerToken, pleromaInstanceUrl } = envConfig; | ||||
|   try { | ||||
|     const request = await fetch( | ||||
|       `${pleromaInstanceUrl}/api/v1/notifications?types[]=mention`, | ||||
|       { | ||||
|         method: "GET", | ||||
|         headers: { | ||||
|           Authorization: `Bearer ${bearerToken}`, | ||||
|         }, | ||||
|       } | ||||
|     ); | ||||
|  | ||||
|     const notifications: Notification[] = await request.json(); | ||||
|  | ||||
|     return notifications; | ||||
|   } catch (error: any) { | ||||
|     throw new Error(error.message); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| const getInstanceEmojis = async () => { | ||||
|   const { bearerToken, pleromaInstanceUrl } = envConfig; | ||||
|   try { | ||||
|     const request = await fetch(`${pleromaInstanceUrl}/api/v1/pleroma/emoji`, { | ||||
|       method: "GET", | ||||
|       headers: { | ||||
|         Authorization: `Bearer ${bearerToken}`, | ||||
|       }, | ||||
|     }); | ||||
|     if (!request.ok) { | ||||
|       console.error(`Emoji GET failed: ${request.status}`); | ||||
|       return; | ||||
|     } | ||||
|     const emojis: PleromaEmoji[] = await request.json(); | ||||
|     return Object.keys(emojis); | ||||
|   } catch (error: any) { | ||||
|     console.error(`Could not fetch emojis: ${error.message}`); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| const deleteNotification = async (notification: Notification) => { | ||||
|   const { pleromaInstanceUrl, bearerToken } = envConfig; | ||||
|   try { | ||||
|     if (!notification.id) { | ||||
|       return; | ||||
|     } | ||||
|     await prisma.response.updateMany({ | ||||
|       // this is probably not the best way to do this, but since we may have duplicate notifications, we have to update all of them - probably won't scale (lmao) | ||||
|       where: { pleromaNotificationId: notification.id }, | ||||
|       data: { isProcessing: false }, | ||||
|     }); | ||||
|     const response = await fetch( | ||||
|       `${pleromaInstanceUrl}/api/v1/notifications/${notification.id}/dismiss`, | ||||
|       { | ||||
|         method: "POST", | ||||
|         headers: { | ||||
|           Authorization: `Bearer ${bearerToken}`, | ||||
|         }, | ||||
|       } | ||||
|     ); | ||||
|     if (!response.ok) { | ||||
|       console.error( | ||||
|         `Could not delete notification ID: ${notification.id}\nReason: ${response.status} - ${response.statusText}` | ||||
|       ); | ||||
|     } | ||||
|   } catch (error: any) { | ||||
|     throw new Error(error.message); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| export { deleteNotification, getInstanceEmojis, getNotifications }; | ||||
							
								
								
									
										187
									
								
								src/main.ts
									
									
									
									
									
								
							
							
						
						
									
										187
									
								
								src/main.ts
									
									
									
									
									
								
							| @ -4,14 +4,26 @@ import { | ||||
|   NewStatusBody, | ||||
|   Notification, | ||||
|   OllamaConfigOptions, | ||||
|   PleromaEmoji, | ||||
| } from "../types.js"; | ||||
| import striptags from "striptags"; | ||||
| import { PrismaClient } from "../generated/prisma/client.js"; | ||||
| import { | ||||
|   getInstanceEmojis, | ||||
|   deleteNotification, | ||||
|   getNotifications, | ||||
| } from "./api.js"; | ||||
| import { storeUserData, storePromptData } from "./prisma.js"; | ||||
| import { | ||||
|   isFromWhitelistedDomain, | ||||
|   alreadyRespondedTo, | ||||
|   recordPendingResponse, | ||||
|   trimInputData, | ||||
|   selectRandomEmoji, | ||||
| } from "./util.js"; | ||||
|  | ||||
| const prisma = new PrismaClient(); | ||||
| export const prisma = new PrismaClient(); | ||||
|  | ||||
| const envConfig = { | ||||
| export const envConfig = { | ||||
|   pleromaInstanceUrl: process.env.PLEROMA_INSTANCE_URL || "", | ||||
|   pleromaInstanceDomain: process.env.PLEROMA_INSTANCE_DOMAIN || "", | ||||
|   whitelistOnly: process.env.ONLY_WHITELIST === "true" ? true : false || "true", | ||||
| @ -33,143 +45,6 @@ const ollamaConfig: OllamaConfigOptions = { | ||||
|   temperature: 1.2, | ||||
| }; | ||||
|  | ||||
| const getNotifications = async () => { | ||||
|   const { bearerToken, pleromaInstanceUrl } = envConfig; | ||||
|   try { | ||||
|     const request = await fetch( | ||||
|       `${pleromaInstanceUrl}/api/v1/notifications?types[]=mention`, | ||||
|       { | ||||
|         method: "GET", | ||||
|         headers: { | ||||
|           Authorization: `Bearer ${bearerToken}`, | ||||
|         }, | ||||
|       } | ||||
|     ); | ||||
|  | ||||
|     const notifications: Notification[] = await request.json(); | ||||
|  | ||||
|     return notifications; | ||||
|   } catch (error: any) { | ||||
|     throw new Error(error.message); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| const getInstanceEmojis = async () => { | ||||
|   const { bearerToken, pleromaInstanceUrl } = envConfig; | ||||
|   try { | ||||
|     const request = await fetch(`${pleromaInstanceUrl}/api/v1/pleroma/emoji`, { | ||||
|       method: "GET", | ||||
|       headers: { | ||||
|         Authorization: `Bearer ${bearerToken}`, | ||||
|       }, | ||||
|     }); | ||||
|     if (!request.ok) { | ||||
|       console.error(`Emoji GET failed: ${request.status}`); | ||||
|       return; | ||||
|     } | ||||
|     const emojis: PleromaEmoji[] = await request.json(); | ||||
|     return Object.keys(emojis); | ||||
|   } catch (error: any) { | ||||
|     console.error(`Could not fetch emojis: ${error.message}`); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| const selectRandomEmoji = (emojiList: string[]) => { | ||||
|   return emojiList[Math.floor(Math.random() * emojiList.length)]; | ||||
| }; | ||||
|  | ||||
| const storeUserData = async (notification: Notification): Promise<void> => { | ||||
|   try { | ||||
|     await prisma.user.upsert({ | ||||
|       where: { userFqn: notification.status.account.fqn }, | ||||
|       update: { | ||||
|         lastRespondedTo: new Date(Date.now()), | ||||
|       }, | ||||
|       create: { | ||||
|         userFqn: notification.status.account.fqn, | ||||
|         lastRespondedTo: new Date(Date.now()), | ||||
|       }, | ||||
|     }); | ||||
|   } catch (error: any) { | ||||
|     throw new Error(error.message); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| const alreadyRespondedTo = async ( | ||||
|   notification: Notification | ||||
| ): Promise<boolean> => { | ||||
|   try { | ||||
|     const duplicate = await prisma.response.findFirst({ | ||||
|       where: { pleromaNotificationId: notification.id, isProcessing: true }, | ||||
|     }); | ||||
|     if (duplicate) { | ||||
|       return true; | ||||
|     } | ||||
|     return false; | ||||
|   } catch (error: any) { | ||||
|     throw new Error(error.message); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| const storePromptData = async ( | ||||
|   notification: Notification, | ||||
|   ollamaResponseBody: OllamaResponse | ||||
| ) => { | ||||
|   try { | ||||
|     await prisma.response.updateMany({ | ||||
|       where: { pleromaNotificationId: notification.id }, | ||||
|       data: { | ||||
|         response: ollamaResponseBody.response, | ||||
|         request: trimInputData(notification.status.content), | ||||
|         to: notification.account.fqn, | ||||
|         isProcessing: false, | ||||
|       }, | ||||
|     }); | ||||
|   } catch (error: any) { | ||||
|     throw new Error(error.message); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| const trimInputData = (input: string): string => { | ||||
|   const strippedInput = striptags(input); | ||||
|   const split = strippedInput.split(" "); | ||||
|   const promptStringIndex = split.indexOf("!prompt"); | ||||
|   split.splice(promptStringIndex, 1); | ||||
|   return split.join(" "); // returns everything after the !prompt | ||||
| }; | ||||
|  | ||||
| const recordPendingResponse = async (notification: Notification) => { | ||||
|   try { | ||||
|     await prisma.response.create({ | ||||
|       data: { | ||||
|         pleromaNotificationId: notification.id, | ||||
|       }, | ||||
|     }); | ||||
|   } catch (error: any) { | ||||
|     throw new Error(error.message); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| const isFromWhitelistedDomain = async ( | ||||
|   notification: Notification | ||||
| ): Promise<boolean> => { | ||||
|   try { | ||||
|     const domain = notification.status.account.fqn.split("@")[1]; | ||||
|     if (envConfig.whitelistedDomains.includes(domain)) { | ||||
|       return true; | ||||
|     } | ||||
|     console.log( | ||||
|       `Rejecting prompt request from non-whitelisted domain: ${domain}` | ||||
|     ); | ||||
|     // delete the notification so we don't keep trying to fetch it | ||||
|     await deleteNotification(notification); | ||||
|     return false; | ||||
|   } catch (error: any) { | ||||
|     console.error(`Error with domain check: ${error.message}`); | ||||
|     return false; | ||||
|   } | ||||
| }; | ||||
|  | ||||
| const generateOllamaRequest = async ( | ||||
|   notification: Notification | ||||
| ): Promise<OllamaResponse | undefined> => { | ||||
| @ -258,36 +133,6 @@ const postReplyToStatus = async ( | ||||
|   } | ||||
| }; | ||||
|  | ||||
| const deleteNotification = async (notification: Notification) => { | ||||
|   const { pleromaInstanceUrl, bearerToken } = envConfig; | ||||
|   try { | ||||
|     if (!notification.id) { | ||||
|       return; | ||||
|     } | ||||
|     await prisma.response.updateMany({ | ||||
|       // this is probably not the best way to do this, but since we may have duplicate notifications, we have to update all of them - probably won't scale (lmao) | ||||
|       where: { pleromaNotificationId: notification.id }, | ||||
|       data: { isProcessing: false }, | ||||
|     }); | ||||
|     const response = await fetch( | ||||
|       `${pleromaInstanceUrl}/api/v1/notifications/${notification.id}/dismiss`, | ||||
|       { | ||||
|         method: "POST", | ||||
|         headers: { | ||||
|           Authorization: `Bearer ${bearerToken}`, | ||||
|         }, | ||||
|       } | ||||
|     ); | ||||
|     if (!response.ok) { | ||||
|       console.error( | ||||
|         `Could not delete notification ID: ${notification.id}\nReason: ${response.status} - ${response.statusText}` | ||||
|       ); | ||||
|     } | ||||
|   } catch (error: any) { | ||||
|     throw new Error(error.message); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| let notifications = []; | ||||
| const beginFetchCycle = async () => { | ||||
|   setInterval(async () => { | ||||
| @ -315,6 +160,6 @@ console.log( | ||||
|   } seconds.` | ||||
| ); | ||||
| console.log( | ||||
|   `Accepting prompts from domains: ${envConfig.whitelistedDomains.join(", ")}` | ||||
|   `Accepting prompts from: ${envConfig.whitelistedDomains.join(", ")}` | ||||
| ); | ||||
| await beginFetchCycle(); | ||||
|  | ||||
							
								
								
									
										41
									
								
								src/prisma.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								src/prisma.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,41 @@ | ||||
| import { Notification, OllamaResponse } from "../types.js"; | ||||
| import { trimInputData } from "./util.js"; | ||||
| import { prisma } from "./main.js"; | ||||
|  | ||||
| const storePromptData = async ( | ||||
|   notification: Notification, | ||||
|   ollamaResponseBody: OllamaResponse | ||||
| ) => { | ||||
|   try { | ||||
|     await prisma.response.updateMany({ | ||||
|       where: { pleromaNotificationId: notification.id }, | ||||
|       data: { | ||||
|         response: ollamaResponseBody.response, | ||||
|         request: trimInputData(notification.status.content), | ||||
|         to: notification.account.fqn, | ||||
|         isProcessing: false, | ||||
|       }, | ||||
|     }); | ||||
|   } catch (error: any) { | ||||
|     throw new Error(error.message); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| const storeUserData = async (notification: Notification): Promise<void> => { | ||||
|   try { | ||||
|     await prisma.user.upsert({ | ||||
|       where: { userFqn: notification.status.account.fqn }, | ||||
|       update: { | ||||
|         lastRespondedTo: new Date(Date.now()), | ||||
|       }, | ||||
|       create: { | ||||
|         userFqn: notification.status.account.fqn, | ||||
|         lastRespondedTo: new Date(Date.now()), | ||||
|       }, | ||||
|     }); | ||||
|   } catch (error: any) { | ||||
|     throw new Error(error.message); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| export { storeUserData, storePromptData }; | ||||
							
								
								
									
										73
									
								
								src/util.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								src/util.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,73 @@ | ||||
| import striptags from "striptags"; | ||||
| import { prisma } from "./main.js"; | ||||
| import { envConfig } from "./main.js"; | ||||
| import { Notification } from "../types.js"; | ||||
| import { deleteNotification } from "./api.js"; | ||||
|  | ||||
| const trimInputData = (input: string): string => { | ||||
|   const strippedInput = striptags(input); | ||||
|   const split = strippedInput.split(" "); | ||||
|   const promptStringIndex = split.indexOf("!prompt"); | ||||
|   split.splice(promptStringIndex, 1); | ||||
|   return split.join(" "); // returns everything after the !prompt | ||||
| }; | ||||
|  | ||||
| const recordPendingResponse = async (notification: Notification) => { | ||||
|   try { | ||||
|     await prisma.response.create({ | ||||
|       data: { | ||||
|         pleromaNotificationId: notification.id, | ||||
|       }, | ||||
|     }); | ||||
|   } catch (error: any) { | ||||
|     throw new Error(error.message); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| const isFromWhitelistedDomain = async ( | ||||
|   notification: Notification | ||||
| ): Promise<boolean> => { | ||||
|   try { | ||||
|     const domain = notification.status.account.fqn.split("@")[1]; | ||||
|     if (envConfig.whitelistedDomains.includes(domain)) { | ||||
|       return true; | ||||
|     } | ||||
|     console.log( | ||||
|       `Rejecting prompt request from non-whitelisted domain: ${domain}` | ||||
|     ); | ||||
|     // delete the notification so we don't keep trying to fetch it | ||||
|     await deleteNotification(notification); | ||||
|     return false; | ||||
|   } catch (error: any) { | ||||
|     console.error(`Error with domain check: ${error.message}`); | ||||
|     return false; | ||||
|   } | ||||
| }; | ||||
|  | ||||
| const alreadyRespondedTo = async ( | ||||
|   notification: Notification | ||||
| ): Promise<boolean> => { | ||||
|   try { | ||||
|     const duplicate = await prisma.response.findFirst({ | ||||
|       where: { pleromaNotificationId: notification.id, isProcessing: true }, | ||||
|     }); | ||||
|     if (duplicate) { | ||||
|       return true; | ||||
|     } | ||||
|     return false; | ||||
|   } catch (error: any) { | ||||
|     throw new Error(error.message); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| const selectRandomEmoji = (emojiList: string[]) => { | ||||
|   return emojiList[Math.floor(Math.random() * emojiList.length)]; | ||||
| }; | ||||
|  | ||||
| export { | ||||
|   alreadyRespondedTo, | ||||
|   selectRandomEmoji, | ||||
|   trimInputData, | ||||
|   recordPendingResponse, | ||||
|   isFromWhitelistedDomain, | ||||
| }; | ||||
		Reference in New Issue
	
	Block a user