From 5f3ff235f06041819cefd0c13fd1c101744a564a Mon Sep 17 00:00:00 2001 From: zstrike Date: Mon, 21 Jul 2025 15:55:45 -0400 Subject: [PATCH] First working prototype, but it has alzheimer's --- README.md | 2 +- package-lock.json | 21 ++++++++++ package.json | 7 ++++ src/index.ts | 14 +++++++ src/shard.ts | 98 +++++++++++++++++++++++++++++++++++++++++++++++ src/utils.ts | 13 +++++++ systemprompt.txt | 1 + tsconfig.json | 11 ++++++ 8 files changed, 166 insertions(+), 1 deletion(-) create mode 100644 src/shard.ts create mode 100644 src/utils.ts create mode 100644 systemprompt.txt create mode 100644 tsconfig.json diff --git a/README.md b/README.md index 848eb85..15e3a1c 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,2 @@ # Aisha - +Don't mind my shitty code. diff --git a/package-lock.json b/package-lock.json index be5ec1f..a8dc19b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,9 +10,15 @@ "license": "ISC", "dependencies": { "@google/genai": "^1.10.0", + "@types/dateformat": "^5.0.3", "axios": "^1.10.0", + "dateformat": "^5.0.3", + "discord-api-types": "^0.38.16", "discord.js": "^14.21.0", "dotenv": "^17.2.0" + }, + "devDependencies": { + "@types/node": "^24.0.15" } }, "node_modules/@discordjs/builders": { @@ -196,6 +202,12 @@ "npm": ">=7.0.0" } }, + "node_modules/@types/dateformat": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@types/dateformat/-/dateformat-5.0.3.tgz", + "integrity": "sha512-hvskLa9ObgUwJe0rtfkyBbU+VIK8Ia3BACsxRsp4CPnyPBAOyIHbqImfBGFfaVVxn82wGx5SD8Twll6jMc/BaQ==", + "license": "MIT" + }, "node_modules/@types/node": { "version": "24.0.15", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.15.tgz", @@ -310,6 +322,15 @@ "node": ">= 0.8" } }, + "node_modules/dateformat": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-5.0.3.tgz", + "integrity": "sha512-Kvr6HmPXUMerlLcLF+Pwq3K7apHpYmGDVqrxcDasBg86UcKeTSNWbEzU8bwdXnxnR44FtMhJAxI4Bov6Y/KUfA==", + "license": "MIT", + "engines": { + "node": ">=12.20" + } + }, "node_modules/debug": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", diff --git a/package.json b/package.json index 4369e8c..7390632 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "version": "0.0.0", "description": "ai bot thingy", "main": "app.ts", + "type": "module", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, @@ -10,8 +11,14 @@ "license": "ISC", "dependencies": { "@google/genai": "^1.10.0", + "@types/dateformat": "^5.0.3", "axios": "^1.10.0", + "dateformat": "^5.0.3", + "discord-api-types": "^0.38.16", "discord.js": "^14.21.0", "dotenv": "^17.2.0" + }, + "devDependencies": { + "@types/node": "^24.0.15" } } diff --git a/src/index.ts b/src/index.ts index e69de29..3b628c8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -0,0 +1,14 @@ +import * as Discord from "discord.js"; +import dotenv from "dotenv"; +import axios from "axios"; +import { GoogleGenAI } from "@google/genai"; +import { createShardManager } from "./utils.ts"; +import path from "path"; + +dotenv.config(); + +const shardManager = createShardManager("src/shard.ts", process.env.BOT_TOKEN, "auto", false); + +shardManager.on("shardCreate", shard => console.log(`[ShardManager] Launched shard ${shard.id}`)); + +shardManager.spawn().catch(console.error); diff --git a/src/shard.ts b/src/shard.ts new file mode 100644 index 0000000..ea7df64 --- /dev/null +++ b/src/shard.ts @@ -0,0 +1,98 @@ +import * as Discord from "discord.js"; +import * as Utils from "@discordjs/util" +import * as Formatters from "@discordjs/formatters" +import * as Builders from "@discordjs/builders" +import * as Types from "discord-api-types/v10" +import * as fs from "node:fs" +import dateFormat, { masks } from "dateformat"; + + +import { GoogleGenAI } from "@google/genai"; +import EventEmitter from "events"; + +const Client:Discord.Client = new Discord.Client({ + intents: ["MessageContent", "Guilds", "GuildMessages", "GuildMembers","GuildMessageReactions","GuildPresences","GuildVoiceStates","GuildIntegrations","GuildEmojisAndStickers","GuildInvites","GuildExpressions","GuildScheduledEvents","GuildMessageTyping","GuildMessagePolls"], + ws: { properties: { $browser: "Discord iOS" }}, + presence: {status: "online", activities: + [{ + name: "something", + state: "who am i?", + type: Discord.ActivityType.Custom + }] + }, + shards: "auto", + allowedMentions: {parse: ["users", "roles"], repliedUser: true}, +}) + +const ai = new GoogleGenAI({ + apiKey: process.env.GEMINI_KEY, + project: process.env.GCP_PROJECT_ID||undefined, +}); + +const shardCacheMap = new Map(); + +function logPrefix():string {return `[${dateFormat(new Date(), masks.isoTime)}] [Shard-${Client.shard?.ids[0]}]: `} + +Client.on("ready", async () => { + console.log(logPrefix()+"Client is ready.") + //await createCaches(); +}) + +function sevenDays():string { + const nextWeek = new Date(); + nextWeek.setDate(nextWeek.getDate() + 7); + return nextWeek.toISOString(); +} +async function createCaches() { + console.log(logPrefix()+"Creating caches...") + const guildList:string[] = Client.guilds.cache.map(guild => guild.id) + var cacheList = new Set(); + const caches = await ai.caches.list({config: {pageSize: 100}}) + + for await (const cache of caches) { + if (cache.displayName) { + cacheList.add(cache.displayName); + shardCacheMap.set(cache.displayName, cache.name); + } + } + + for (const guildId of guildList) { + if (!cacheList.has(guildId)) { + console.log(logPrefix()+`Guild ${guildId} does not have a cache, creating one now.`) + const newCache = await ai.caches.create({ + model: "gemini-2.5-flash", + config: { + displayName: guildId, + expireTime: sevenDays(), // im not gonna bother trying to fit all this in one value + systemInstruction: fs.readFileSync("systemprompt.txt").toString().replace("",Client.guilds.cache.get(guildId)?.name||"undefined"), + } + }) + shardCacheMap.set(guildId, newCache.name); + } + } +} + +function embedMessageInfo(message:Discord.Message) { + return `Message Author Username: ${message.author.username} + Message Author Name: ${message.author.displayName} + Message Content: ${message.cleanContent}` +} + + + +Client.on("messageCreate", async message => { + if (message.content.includes("<@1396892425548730601>")) { + const genContent = await ai.models.generateContent({ + model: "gemini-2.5-flash", + contents: embedMessageInfo(message), + config: { + //cachedContent: shardCacheMap.get(message.guildId || ""), + systemInstruction: fs.readFileSync("systemprompt.txt").toString().replace("",Client.guilds.cache.get(message.guildId||"")?.name||"undefined"), + } + }) + const resContent:string = genContent.text?.toString()||"‎ "; + message.reply(resContent); + } +}) + +Client.login(process.env.TOKEN); diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 0000000..a03c1c9 --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,13 @@ +import * as Discord from "discord.js"; + +function createShardManager(file:string, token:string|undefined, shards:number|"auto", silent?:boolean):Discord.ShardingManager { + const manager = new Discord.ShardingManager(file,{ + token: token, + totalShards: shards, + mode: "process", + silent: silent||false + }) + return manager; +} + +export {createShardManager}; \ No newline at end of file diff --git a/systemprompt.txt b/systemprompt.txt new file mode 100644 index 0000000..dd35f05 --- /dev/null +++ b/systemprompt.txt @@ -0,0 +1 @@ +you are aisha, a human user on the messaging platform Discord. your job is to act like a human user of this platform and talk with people that interact with you. The server you are in is called . Users can talk to you by "mentioning" you with the mention sign @Aisha. \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..4033670 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "module": "commonjs", + "esModuleInterop": true, + "target": "es6", + "moduleResolution": "node", + "sourceMap": true, + "outDir": "dist" + }, + "lib": ["es2015"] +} \ No newline at end of file