project setup

This commit is contained in:
fzzinchemical
2025-03-25 21:24:57 +01:00
commit aa36bbb3f2
10 changed files with 701 additions and 0 deletions

30
src/bot.ts Executable file
View File

@@ -0,0 +1,30 @@
import { createBot, Intents, startBot } from "npm:discordeno@18.0.1";
import * as dotenv from "jsr:@std/dotenv";
import { messagehandler } from "./messages.ts";
const env = dotenv.loadSync();
if (typeof env.BOT_TOKEN !== "string") {
throw new Error("Bot token is required in .env file");
}
const bot = createBot({
token: env.BOT_TOKEN,
intents: Intents.Guilds | Intents.GuildMessages | Intents.MessageContent |
Intents.DirectMessages,
events: {
ready() {
console.log("Bot is ready!");
},
async messageCreate(bot, message) {
await messagehandler(bot, message);
}
}
});
// Setup desired properties
await startBot(bot);

9
src/logging.ts Executable file
View File

@@ -0,0 +1,9 @@
import * as stdlogger from "jsr:@std/log";
import { Message } from "npm:discordeno@18.0.1";
export function logMessage(message: Message) {
return stdlogger.info(
`Message from ${message.tag} in ${message.guildId?? "Private"} ${message.channelId} Command:[${message.content}]`
);
}

152
src/messages.ts Executable file
View File

@@ -0,0 +1,152 @@
import { Bot, Message } from "npm:discordeno@18.0.1";
import { dropdeineMutti, r34test, refresh } from "./r34api.ts";
import { logMessage } from "./logging.ts";
import { communicate, setMemory } from "./ollama_api.ts";
import { dropYandere, dropYandere5, getPage, setPage } from "./yandereapi.ts";
import * as dotenv from "jsr:@std/dotenv";
const env = dotenv.loadSync();
const prefix = env.BOT_PREFIX!;
export async function messagehandler(bot: Bot, message: Message) {
const command = message.content.split(" ")[0];
const args = message.content.split(" ").slice(1);
switch (command) {
case `${prefix}rule`:
logMessage(message);
if (
message.channelId === 754338073101205524n ||
message.guildId === undefined
) {
bot.helpers.sendMessage(message.channelId, {
content: defaultString(await r34test()),
});
}
break;
case `${prefix}rule5`:
logMessage(message);
if (
message.channelId === 754338073101205524n ||
message.guildId === undefined
) {
bot.helpers.sendMessage(message.channelId, {
content: defaultString(await dropdeineMutti()),
});
}
break;
case `${prefix}refresh`:
logMessage(message);
if (
message.channelId === 754338073101205524n ||
message.guildId === undefined
) {
await refresh();
bot.helpers.sendMessage(message.channelId, {
content: "Refreshed",
});
}
break;
case `${prefix}yande`:
logMessage(message);
if (
message.channelId === 754338073101205524n ||
message.guildId === undefined
) {
await refresh();
bot.helpers.sendMessage(message.channelId, {
content: defaultString(await dropYandere()),
});
}
break;
case `${prefix}yande5`:
logMessage(message);
if (
message.channelId === 754338073101205524n ||
message.guildId === undefined
) {
await refresh();
bot.helpers.sendMessage(message.channelId, {
content: defaultString(await dropYandere5()),
});
}
break;
case `${prefix}yandepage`:
logMessage(message);
if (
message.channelId === 754338073101205524n ||
message.guildId === undefined
) {
if (args[0] === undefined) {
bot.helpers.sendMessage(message.channelId, {
content: "Please provide a page number",
});
} else if (isNaN(parseInt(args[0]))) {
bot.helpers.sendMessage(message.channelId, {
content: "Please provide a valid number",
});
} else {
await setPage(parseInt(args[0]));
bot.helpers.sendMessage(message.channelId, {
content: "Page set to " + args[0],
});
}
}
break;
case `${prefix}yandegetpage`:
logMessage(message);
if (
message.channelId === 754338073101205524n ||
message.guildId === undefined
) {
bot.helpers.sendMessage(message.channelId, {
content: "Page is " + getPage(),
});
}
break;
// TODO Add exception for when the ollama api is down
case `${prefix}brainrotgf`:
logMessage(message);
if (
(message.channelId === 754338073101205524n ||
message.guildId === undefined) ||
message.authorId != 1281272527792111698n
) {
bot.helpers.sendMessage(message.channelId, {
content: await communicate("brainrotgf", args.join(" ")),
});
}
break;
case `${prefix}brainrotgfmem`:
logMessage(message);
if (
(message.channelId === 754338073101205524n ||
message.guildId === undefined) ||
message.authorId != 1281272527792111698n
) {
if (args[0] !== undefined) {
const arg = args[0].toLowerCase();
if (arg === "true" || arg === "false") {
const memoryActive = arg === "true";
setMemory(memoryActive);
bot.helpers.sendMessage(message.channelId, {
content: "AI Memory is now set to " + memoryActive,
});
} else {
bot.helpers.sendMessage(message.channelId, {
content:
"ERROR: Invalid argument. Use true or false.",
});
}
} else {
bot.helpers.sendMessage(message.channelId, {
content: "Please provide an argument",
});
}
}
break;
}
}
function defaultString(s: string): string {
return s === "" ? "ERROR: Tried to send empty message" : s;
}

101
src/ollama_api.ts Executable file
View File

@@ -0,0 +1,101 @@
import * as dotenv from "jsr:@std/dotenv";
const env = dotenv.loadSync();
const ollamalink: string = env.OLLAMA_API_LINK! + "/api/chat";
const ollamaMemoryLimit: number = parseInt(env.OLLAMA_MEMORY_LIMIT!);
// ATTENTION MEMORY LIMIT IS 10!
let memoryActive = false;
const memory: Message[] = [];
type Message = {
role: string;
content: string;
};
type OllamaAPIRequest = {
model: string;
messages: Message[];
stream: boolean;
};
type OllamaAPIResponse = {
model: string;
created_at: string;
message: Message;
done: boolean;
context: number[];
total_duration: number;
load_duration: number;
prompt_eval_count: number;
prompt_eval_duration: number;
eval_count: number;
eval_duration: number;
};
async function makeRequest(
model: string,
prompt: string,
): Promise<OllamaAPIResponse> {
const requestBody: OllamaAPIRequest = {
model: model,
messages: [
...memoryActive ? memory : [],
{
role: "user",
content: prompt,
},
],
stream: false,
};
console.log("Request Body:", JSON.stringify(requestBody));
try {
const response = await fetch(ollamalink, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(requestBody),
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data: OllamaAPIResponse = await response.json();
console.log("API Response:", data);
return data;
} catch (error) {
console.error("Error making request:", error);
throw error;
}
}
export function setMemory(tmp: boolean) {
memoryActive = tmp;
}
function memoryManager(message: Message) {
if (memory.length >= ollamaMemoryLimit) {
memory.shift();
}
memory.push(message);
}
export async function communicate(
model: string,
prompt: string,
): Promise<string> {
const response = await makeRequest(model, prompt);
memoryManager(
{
role: "user",
content: prompt,
},
);
memoryManager(response.message);
console.log("Response:", response);
return response.message.content ?? "ERROR: No response";
}

84
src/r34api.ts Executable file
View File

@@ -0,0 +1,84 @@
type APIResponse = Array<{
preview_url: string;
sample_url: string;
file_url: string;
directory: number;
hash: string;
width: number;
height: number;
id: number;
image: string;
change: number;
owner: string;
parent_id: number;
rating: string;
sample: boolean;
sample_height: number;
sample_width: number;
score: number;
tags: string;
source: string;
status: string;
has_notes: boolean;
comment_count: number;
}>;
// Define the API URL
const apiUrl =
"https://api.rule34.xxx/index.php?page=dapi&s=post&q=index&json=1";
// Make a GET request
let baseResponse = await fetch(apiUrl, { headers: { "Accept": "application/json" } })
.then(async (response) => {
if (!response.ok) {
throw new Error("Network response was not ok");
}
return <APIResponse> await response.json();
});
const hyperlinkarray: string[] = [];
// ---------------------------------
// import * as stdHTML from "jsr:@std/html@1.0.2";
// Deno.serve({ port: 6969 }, async (request) => {
// const html = hyperlinkarray.map((x) => `<img src="${stdHTML.escape(x)}"/>`)
// .join("\n");
// return new Response(html, {
// headers: { "Content-Type": "text/html;charset=utf-8" },
// });
// });
// ---------------------------------
export async function refresh() {
await fetch(apiUrl, { headers: { "Accept": "application/json" } })
.then(async (response) => {
if (!response.ok) {
throw new Error("Network response was not ok");
}
baseResponse = <APIResponse> await response.json();
for (const k of baseResponse) {
if (!hyperlinkarray.includes(k.file_url)) {
hyperlinkarray.push(k.file_url);
}
}
});
}
for (const k of baseResponse) {
hyperlinkarray.push(k.file_url);
}
export async function r34test() {
if (hyperlinkarray.length === 0) {
await refresh();
return await r34test();
}
return hyperlinkarray.pop()!;
}
export async function dropdeineMutti() {
let tmp = "";
for (let i = 0; i < 5; i++) {
tmp += await r34test() + "\n";
}
return tmp;
}

112
src/yandereapi.ts Executable file
View File

@@ -0,0 +1,112 @@
const apiUrl ="https://yande.re/post.json?api_version=2";
type APIResponse = {
posts: {
id: number;
tags: string;
created_at: string;
creator_id: number;
approver_id: number;
author: string;
change: number;
source: string;
score: number;
md5: string;
file_size: number;
file_ext: string;
file_url: string;
is_shown_in_index: boolean;
preview_url: string;
preview_width: number;
preview_height: number;
actual_preview_width: number;
actual_preview_height: number;
sample_url: string;
sample_width: number;
sample_height: number;
sample_file_size: number;
jpeg_url: string;
jpeg_width: number;
jpeg_height: number;
rating: string;
is_rating_locked: boolean;
has_children: boolean;
parent_id: number;
status: string;
is_pending: boolean;
width: number;
height: number;
is_held: boolean;
frames_pending_string: string;
frames_pending: [];
frames_string: string;
frames: [];
is_note_locked: boolean;
last_noted_at: string;
last_commented_at: string;
}[];
};
let page = 1;
let baseRequest = await fetch(apiUrl, { headers: { "Accept": "application/json" } })
.then(async (response) => {
if (!response.ok) {
throw new Error("Network response was not ok");
}
return <APIResponse> await response.json();
});
const hyperlinkarray: string[] = [];
for (const k of baseRequest.posts) {
hyperlinkarray.push(k.file_url);
}
export function setPage(newpage: number) {
page = newpage;
hyperlinkarray.length = 0;
refresh();
}
export function getPage() {
return page;
}
export async function refresh() {
await fetch(`${apiUrl}&page=${page}`, { headers: { "Accept": "application/json" } })
.then(async (response) => {
if (!response.ok) {
throw new Error("Network response was not ok");
}
baseRequest = <APIResponse> await response.json();
for (const k of baseRequest.posts) {
if (!hyperlinkarray.includes(k.file_url)) {
hyperlinkarray.push(k.file_url);
}
}
});
}
export async function fetchNextPage() {
page += 1;
await refresh();
}
export async function dropYandere() {
if (hyperlinkarray.length === 0) {
await fetchNextPage();
return await dropYandere();
}
return hyperlinkarray.pop()!;
}
export async function dropYandere5() {
let tmp = "";
for (let i = 0; i < 5; i++) {
tmp += await dropYandere() + "\n";
}
return tmp;
}