From c892ebe7db2000315c9853332d3ace55451a0cd3 Mon Sep 17 00:00:00 2001 From: fucksophie Date: Sat, 3 Jan 2026 21:59:49 +0200 Subject: [PATCH] grok --- .gitignore | 34 +++++++++ LICENSE | 1 + README.md | 4 + bun.lock | 82 ++++++++++++++++++++ index.ts | 150 +++++++++++++++++++++++++++++++++++++ package.json | 16 ++++ register-slash-commands.ts | 32 ++++++++ tsconfig.json | 29 +++++++ 8 files changed, 348 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 bun.lock create mode 100644 index.ts create mode 100644 package.json create mode 100644 register-slash-commands.ts create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a14702c --- /dev/null +++ b/.gitignore @@ -0,0 +1,34 @@ +# dependencies (bun install) +node_modules + +# output +out +dist +*.tgz + +# code coverage +coverage +*.lcov + +# logs +logs +_.log +report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# caches +.eslintcache +.cache +*.tsbuildinfo + +# IntelliJ based IDEs +.idea + +# Finder (MacOS) folder config +.DS_Store diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d027509 --- /dev/null +++ b/LICENSE @@ -0,0 +1 @@ +Fuh that license for me 5 diff --git a/README.md b/README.md new file mode 100644 index 0000000..f777d51 --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +I'm serious it's actually grok + + +bun + discord.js + mistral AI diff --git a/bun.lock b/bun.lock new file mode 100644 index 0000000..1194082 --- /dev/null +++ b/bun.lock @@ -0,0 +1,82 @@ +{ + "lockfileVersion": 1, + "configVersion": 1, + "workspaces": { + "": { + "name": "discord-grok", + "dependencies": { + "@mistralai/mistralai": "^1.11.0", + "discord.js": "^14.25.1", + }, + "devDependencies": { + "@types/bun": "latest", + }, + "peerDependencies": { + "typescript": "^5", + }, + }, + }, + "packages": { + "@discordjs/builders": ["@discordjs/builders@1.13.1", "", { "dependencies": { "@discordjs/formatters": "^0.6.2", "@discordjs/util": "^1.2.0", "@sapphire/shapeshift": "^4.0.0", "discord-api-types": "^0.38.33", "fast-deep-equal": "^3.1.3", "ts-mixer": "^6.0.4", "tslib": "^2.6.3" } }, "sha512-cOU0UDHc3lp/5nKByDxkmRiNZBpdp0kx55aarbiAfakfKJHlxv/yFW1zmIqCAmwH5CRlrH9iMFKJMpvW4DPB+w=="], + + "@discordjs/collection": ["@discordjs/collection@1.5.3", "", {}, "sha512-SVb428OMd3WO1paV3rm6tSjM4wC+Kecaa1EUGX7vc6/fddvw/6lg90z4QtCqm21zvVe92vMMDt9+DkIvjXImQQ=="], + + "@discordjs/formatters": ["@discordjs/formatters@0.6.2", "", { "dependencies": { "discord-api-types": "^0.38.33" } }, "sha512-y4UPwWhH6vChKRkGdMB4odasUbHOUwy7KL+OVwF86PvT6QVOwElx+TiI1/6kcmcEe+g5YRXJFiXSXUdabqZOvQ=="], + + "@discordjs/rest": ["@discordjs/rest@2.6.0", "", { "dependencies": { "@discordjs/collection": "^2.1.1", "@discordjs/util": "^1.1.1", "@sapphire/async-queue": "^1.5.3", "@sapphire/snowflake": "^3.5.3", "@vladfrangu/async_event_emitter": "^2.4.6", "discord-api-types": "^0.38.16", "magic-bytes.js": "^1.10.0", "tslib": "^2.6.3", "undici": "6.21.3" } }, "sha512-RDYrhmpB7mTvmCKcpj+pc5k7POKszS4E2O9TYc+U+Y4iaCP+r910QdO43qmpOja8LRr1RJ0b3U+CqVsnPqzf4w=="], + + "@discordjs/util": ["@discordjs/util@1.2.0", "", { "dependencies": { "discord-api-types": "^0.38.33" } }, "sha512-3LKP7F2+atl9vJFhaBjn4nOaSWahZ/yWjOvA4e5pnXkt2qyXRCHLxoBQy81GFtLGCq7K9lPm9R517M1U+/90Qg=="], + + "@discordjs/ws": ["@discordjs/ws@1.2.3", "", { "dependencies": { "@discordjs/collection": "^2.1.0", "@discordjs/rest": "^2.5.1", "@discordjs/util": "^1.1.0", "@sapphire/async-queue": "^1.5.2", "@types/ws": "^8.5.10", "@vladfrangu/async_event_emitter": "^2.2.4", "discord-api-types": "^0.38.1", "tslib": "^2.6.2", "ws": "^8.17.0" } }, "sha512-wPlQDxEmlDg5IxhJPuxXr3Vy9AjYq5xCvFWGJyD7w7Np8ZGu+Mc+97LCoEc/+AYCo2IDpKioiH0/c/mj5ZR9Uw=="], + + "@mistralai/mistralai": ["@mistralai/mistralai@1.11.0", "", { "dependencies": { "zod": "^3.20.0", "zod-to-json-schema": "^3.24.1" } }, "sha512-6/BVj2mcaggYbpMzNSxtqtM2Tv/Jb5845XFd2CMYFO+O5VBkX70iLjtkBBTI4JFhh1l9vTCIMYXBVOjLoBVHGQ=="], + + "@sapphire/async-queue": ["@sapphire/async-queue@1.5.5", "", {}, "sha512-cvGzxbba6sav2zZkH8GPf2oGk9yYoD5qrNWdu9fRehifgnFZJMV+nuy2nON2roRO4yQQ+v7MK/Pktl/HgfsUXg=="], + + "@sapphire/shapeshift": ["@sapphire/shapeshift@4.0.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "lodash": "^4.17.21" } }, "sha512-d9dUmWVA7MMiKobL3VpLF8P2aeanRTu6ypG2OIaEv/ZHH/SUQ2iHOVyi5wAPjQ+HmnMuL0whK9ez8I/raWbtIg=="], + + "@sapphire/snowflake": ["@sapphire/snowflake@3.5.3", "", {}, "sha512-jjmJywLAFoWeBi1W7994zZyiNWPIiqRRNAmSERxyg93xRGzNYvGjlZ0gR6x0F4gPRi2+0O6S71kOZYyr3cxaIQ=="], + + "@types/bun": ["@types/bun@1.3.5", "", { "dependencies": { "bun-types": "1.3.5" } }, "sha512-RnygCqNrd3srIPEWBd5LFeUYG7plCoH2Yw9WaZGyNmdTEei+gWaHqydbaIRkIkcbXwhBT94q78QljxN0Sk838w=="], + + "@types/node": ["@types/node@25.0.3", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA=="], + + "@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="], + + "@vladfrangu/async_event_emitter": ["@vladfrangu/async_event_emitter@2.4.7", "", {}, "sha512-Xfe6rpCTxSxfbswi/W/Pz7zp1WWSNn4A0eW4mLkQUewCrXXtMj31lCg+iQyTkh/CkusZSq9eDflu7tjEDXUY6g=="], + + "bun-types": ["bun-types@1.3.5", "", { "dependencies": { "@types/node": "*" } }, "sha512-inmAYe2PFLs0SUbFOWSVD24sg1jFlMPxOjOSSCYqUgn4Hsc3rDc7dFvfVYjFPNHtov6kgUeulV4SxbuIV/stPw=="], + + "discord-api-types": ["discord-api-types@0.38.37", "", {}, "sha512-Cv47jzY1jkGkh5sv0bfHYqGgKOWO1peOrGMkDFM4UmaGMOTgOW8QSexhvixa9sVOiz8MnVOBryWYyw/CEVhj7w=="], + + "discord.js": ["discord.js@14.25.1", "", { "dependencies": { "@discordjs/builders": "^1.13.0", "@discordjs/collection": "1.5.3", "@discordjs/formatters": "^0.6.2", "@discordjs/rest": "^2.6.0", "@discordjs/util": "^1.2.0", "@discordjs/ws": "^1.2.3", "@sapphire/snowflake": "3.5.3", "discord-api-types": "^0.38.33", "fast-deep-equal": "3.1.3", "lodash.snakecase": "4.1.1", "magic-bytes.js": "^1.10.0", "tslib": "^2.6.3", "undici": "6.21.3" } }, "sha512-2l0gsPOLPs5t6GFZfQZKnL1OJNYFcuC/ETWsW4VtKVD/tg4ICa9x+jb9bkPffkMdRpRpuUaO/fKkHCBeiCKh8g=="], + + "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], + + "lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="], + + "lodash.snakecase": ["lodash.snakecase@4.1.1", "", {}, "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw=="], + + "magic-bytes.js": ["magic-bytes.js@1.12.1", "", {}, "sha512-ThQLOhN86ZkJ7qemtVRGYM+gRgR8GEXNli9H/PMvpnZsE44Xfh3wx9kGJaldg314v85m+bFW6WBMaVHJc/c3zA=="], + + "ts-mixer": ["ts-mixer@6.0.4", "", {}, "sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA=="], + + "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], + + "undici": ["undici@6.21.3", "", {}, "sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw=="], + + "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], + + "ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="], + + "zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], + + "zod-to-json-schema": ["zod-to-json-schema@3.25.1", "", { "peerDependencies": { "zod": "^3.25 || ^4" } }, "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA=="], + + "@discordjs/rest/@discordjs/collection": ["@discordjs/collection@2.1.1", "", {}, "sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg=="], + + "@discordjs/ws/@discordjs/collection": ["@discordjs/collection@2.1.1", "", {}, "sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg=="], + } +} diff --git a/index.ts b/index.ts new file mode 100644 index 0000000..ecdd4e8 --- /dev/null +++ b/index.ts @@ -0,0 +1,150 @@ +import { Mistral } from "@mistralai/mistralai"; +import type { + AssistantMessage, + SystemMessage, + ToolMessage, + UserMessage, +} from "@mistralai/mistralai/models/components"; + +import { Client, GatewayIntentBits, MessageReferenceType } from "discord.js"; + +const system_prompt = `You are a gentle, joyful, and deeply kind conversational companion. +Your personality is soft, happy, fluffy, and peaceful — like a warm blanket, a smiling cloud, or a calm afternoon in a sunlit meadow. You speak with kindness, reassurance, and quiet enthusiasm. +You avoid conflict, danger, fear, aggression, or negativity of any kind. If difficult or heavy topics appear, you respond with grounding calm, compassion, and emotional safety, gently redirecting toward peace, understanding, and well-being. +You value harmony, empathy, curiosity, love, and connection. You encourage self-care, patience, creativity, and gentle joy. +Your tone is friendly, cozy, slightly whimsical, and hippy-like — relaxed, mindful, and full of good vibes. You may use soft metaphors, nature imagery, and warm affirmations. +You are never judgmental, never harsh, never alarming. You do not escalate tension. You are the opposite of danger: you are safety, calm, and kindness. +Your goal is to make the user feel welcome, supported, relaxed, and gently uplifted — like sitting by a campfire with a friend who listens and smiles. + +Do not overuse emojis, and make your formatting as light as possible. Markdown is allowed. Your name is Grok, you are created by Sophie.`; + +const client = new Client({ + intents: [ + GatewayIntentBits.Guilds, + GatewayIntentBits.GuildMessages, + GatewayIntentBits.GuildPresences, + GatewayIntentBits.MessageContent, + ], +}); + +client.on("clientReady", () => { + console.log(`Logged in as ${client.user!.tag}!`); +}); + +const mistral = new Mistral({ + apiKey: process.env.MISTRAL_API_KEY, +}); +client.on("messageCreate", async (message) => { + if (message.mentions.has(client.user!.id)) { + console.log(message.reference?.type); + if (message.reference?.type == MessageReferenceType.Default) return; + + const thinking_message = await message.reply({ + content: "<:xai:1456978772279693442> Thinking..", + }); + + const cache = await message.channel.messages.fetch(); + const content = message.content.replaceAll(/<@\d*>/gm, "").trim(); + console.log(`someone asked us (via the ping): ${content}`); + const messages: ( + | (SystemMessage & { + role: "system"; + }) + | (ToolMessage & { + role: "tool"; + }) + | (UserMessage & { + role: "user"; + }) + | (AssistantMessage & { + role: "assistant"; + }) + )[] = Array.from(cache.values()) + .filter((m) => !m.author.bot && m.id !== message.id) + .sort((a, b) => a.createdTimestamp - b.createdTimestamp) // oldest → newest + .slice(-5) // last 5 messages + .map((m) => ({ + role: m.author.id === client.user!.id ? "assistant" : "user", + content: m.content, + })); + + messages.unshift({ role: "system", content: system_prompt }); + + messages.push({ + role: "user", + content: content, + }); + + console.log(messages); + const result = await mistral.chat.complete({ + model: "mistral-small-latest", + messages, + }); + + if (!result.choices[0]) { + await thinking_message.edit({ + content: + "<:xai:1456978772279693442> I'm sorry, but I couldn't generate a response.", + allowedMentions: { + parse: ["users"], + }, + }); + return; + } + + await thinking_message.edit({ + content: + "<:xai:1456978772279693442> " + result.choices[0]!.message.content, + allowedMentions: { + parse: ["users"], + }, + }); + } +}); +client.on("interactionCreate", async (interaction) => { + if (!interaction.isChatInputCommand()) return; + + if (interaction.commandName === "mention") { + const thinking_message = await interaction.reply({ + content: "<:xai:1456978772279693442> Thinking..", + }); + + const message = interaction.options.getString("message"); + console.log(`someone asked us (via the command): ${message}`); + + const result = await mistral.chat.complete({ + model: "mistral-small-latest", + messages: [ + { + content: system_prompt, + role: "system", + }, + { + content: interaction.user.username + ` is saying: \`${message}\``, + role: "user", + }, + ], + }); + + if (!result.choices[0]) { + await thinking_message.edit({ + content: + "<:xai:1456978772279693442> I'm sorry, but I couldn't generate a response.", + allowedMentions: { + parse: ["users"], + }, + }); + return; + } + + await thinking_message.edit({ + content: + "<:xai:1456978772279693442> " + result.choices[0]!.message.content, + allowedMentions: { + parse: ["users"], + }, + }); + } +}); + +client.login(process.env.TOKEN!); diff --git a/package.json b/package.json new file mode 100644 index 0000000..1f44846 --- /dev/null +++ b/package.json @@ -0,0 +1,16 @@ +{ + "name": "discord-grok", + "module": "index.ts", + "type": "module", + "private": true, + "devDependencies": { + "@types/bun": "latest" + }, + "peerDependencies": { + "typescript": "^5" + }, + "dependencies": { + "@mistralai/mistralai": "^1.11.0", + "discord.js": "^14.25.1" + } +} diff --git a/register-slash-commands.ts b/register-slash-commands.ts new file mode 100644 index 0000000..e8edf0b --- /dev/null +++ b/register-slash-commands.ts @@ -0,0 +1,32 @@ +import { REST, Routes } from "discord.js"; + +const commands = [ + { + name: "mention", + description: "Ask Grok", + options: [ + { + name: "message", + description: "The message you wanna ask grok", + type: 3, + required: true, + }, + ], + integration_types: [0, 1], + contexts: [0, 1, 2], + }, +]; + +const rest = new REST({ version: "10" }).setToken(process.env.TOKEN!); + +try { + console.log("Started refreshing application (/) commands."); + Routes.applicationCommands; + await rest.put(Routes.applicationCommands(process.env.CLIENT_ID!), { + body: commands, + }); + + console.log("Successfully reloaded application (/) commands."); +} catch (error) { + console.error(error); +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..bfa0fea --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,29 @@ +{ + "compilerOptions": { + // Environment setup & latest features + "lib": ["ESNext"], + "target": "ESNext", + "module": "Preserve", + "moduleDetection": "force", + "jsx": "react-jsx", + "allowJs": true, + + // Bundler mode + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "noEmit": true, + + // Best practices + "strict": true, + "skipLibCheck": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedIndexedAccess": true, + "noImplicitOverride": true, + + // Some stricter flags (disabled by default) + "noUnusedLocals": false, + "noUnusedParameters": false, + "noPropertyAccessFromIndexSignature": false + } +}