commit c892ebe7db2000315c9853332d3ace55451a0cd3 Author: fucksophie Date: Sat Jan 3 21:59:49 2026 +0200 grok 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 + } +}