From 865a78282ed3ce7da159d9e9bf941826216f3ae5 Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 17 May 2025 12:13:49 -0400 Subject: [PATCH] changes per https://x.com/i/grok?conversation=1923765822767452645 --- src/client.ts | 110 ++++++++--------- src/events/messageCreate.ts | 232 ++++++++---------------------------- src/personality.json | 3 + 3 files changed, 105 insertions(+), 240 deletions(-) create mode 100644 src/personality.json diff --git a/src/client.ts b/src/client.ts index ba905c6..9468e05 100644 --- a/src/client.ts +++ b/src/client.ts @@ -1,55 +1,55 @@ -import { Client, GatewayIntentBits } from 'discord.js' -import { Ollama } from 'ollama' -import { createClient } from 'redis' -import { Queue } from './queues/queue.js' -import { UserMessage, registerEvents } from './utils/index.js' -import Events from './events/index.js' -import Keys from './keys.js' - -// initialize the client with the following permissions when logging in -const client = new Client({ - intents: [ - GatewayIntentBits.Guilds, - GatewayIntentBits.GuildMembers, - GatewayIntentBits.GuildMessages, - GatewayIntentBits.MessageContent - ] -}) - -// initialize connection to redis -const redis = createClient({ - url: `redis://${Keys.redisHost}:${Keys.redisPort}`, -}) - -// initialize connection to ollama container -export const ollama = new Ollama({ - host: `http://${Keys.ipAddress}:${Keys.portAddress}`, -}) - -// Create Queue managed by Events -const messageHistory: Queue = new Queue - -// register all events -registerEvents(client, Events, messageHistory, ollama, Keys.defaultModel) - -// Try to connect to redis -await redis.connect() - .then(() => console.log('[Redis] Connected')) - .catch((error) => { - console.error('[Redis] Connection Error', error) - process.exit(1) - }) - -// Try to log in the client -await client.login(Keys.clientToken) - .catch((error) => { - console.error('[Login Error]', error) - process.exit(1) - }) - -// queue up bots name -messageHistory.enqueue({ - role: 'assistant', - content: `My name is ${client.user?.username}`, - images: [] -}) \ No newline at end of file +import { Client, GatewayIntentBits } from 'discord.js' +import { Ollama } from 'ollama' +import { createClient } from 'redis' +import { Queue } from './queues/queue.js' +import { UserMessage, registerEvents } from './utils/index.js' +import Events from './events/index.js' +import Keys from './keys.js' + +// Initialize the client +const client = new Client({ + intents: [ + GatewayIntentBits.Guilds, + GatewayIntentBits.GuildMembers, + GatewayIntentBits.GuildMessages, + GatewayIntentBits.MessageContent + ] +}) + +// Initialize Redis connection +export const redis = createClient({ + url: `redis://${Keys.redisHost}:${Keys.redisPort}`, +}) + +// Initialize Ollama connection +export const ollama = new Ollama({ + host: `http://${Keys.ipAddress}:${Keys.portAddress}`, +}) + +// Create Queue managed by Events +const messageHistory: Queue = new Queue + +// Register all events +registerEvents(client, Events, messageHistory, ollama, Keys.defaultModel) + +// Try to connect to Redis +await redis.connect() + .then(() => console.log('[Redis] Connected')) + .catch((error) => { + console.error('[Redis] Connection Error', error) + process.exit(1) + }) + +// Try to log in the client +await client.login(Keys.clientToken) + .catch((error) => { + console.error('[Login Error]', error) + process.exit(1) + }) + +// Queue up bot's name +messageHistory.enqueue({ + role: 'assistant', + content: `My name is ${client.user?.username}`, + images: [] +}) diff --git a/src/events/messageCreate.ts b/src/events/messageCreate.ts index 5ec6fa0..9468e05 100644 --- a/src/events/messageCreate.ts +++ b/src/events/messageCreate.ts @@ -1,193 +1,55 @@ -import { TextChannel } from 'discord.js' -import { event, Events, normalMessage, UserMessage, clean } from '../utils/index.js' -import { - getChannelInfo, getServerConfig, getUserConfig, openChannelInfo, - openConfig, UserConfig, getAttachmentData, getTextFileAttachmentData -} from '../utils/index.js' +import { Client, GatewayIntentBits } from 'discord.js' +import { Ollama } from 'ollama' +import { createClient } from 'redis' +import { Queue } from './queues/queue.js' +import { UserMessage, registerEvents } from './utils/index.js' +import Events from './events/index.js' +import Keys from './keys.js' -/** - * Max Message length for free users is 2000 characters (bot or not). - * Bot supports infinite lengths for normal messages. - * - * @param message the message received from the channel - */ -export default event(Events.MessageCreate, async ({ log, msgHist, ollama, client, defaultModel }, message) => { - const clientId = client.user!!.id - let cleanedMessage = clean(message.content, clientId) - log(`Message \"${cleanedMessage}\" from ${message.author.tag} in channel/thread ${message.channelId}.`) +// Initialize the client +const client = new Client({ + intents: [ + GatewayIntentBits.Guilds, + GatewayIntentBits.GuildMembers, + GatewayIntentBits.GuildMessages, + GatewayIntentBits.MessageContent + ] +}) - // Do not respond if bot talks in the chat - if (message.author.username === message.client.user.username) return +// Initialize Redis connection +export const redis = createClient({ + url: `redis://${Keys.redisHost}:${Keys.redisPort}`, +}) - // Only respond if message mentions the bot - if (!message.mentions.has(clientId)) return +// Initialize Ollama connection +export const ollama = new Ollama({ + host: `http://${Keys.ipAddress}:${Keys.portAddress}`, +}) - // default stream to false - let shouldStream = false +// Create Queue managed by Events +const messageHistory: Queue = new Queue - // Params for Preferences Fetching - const maxRetries = 3 - const delay = 1000 // in millisecons +// Register all events +registerEvents(client, Events, messageHistory, ollama, Keys.defaultModel) - try { - // Retrieve Server/Guild Preferences - let attempt = 0 - while (attempt < maxRetries) { - try { - await new Promise((resolve, reject) => { - getServerConfig(`${message.guildId}-config.json`, (config) => { - // check if config.json exists - if (config === undefined) { - // Allowing chat options to be available - openConfig(`${message.guildId}-config.json`, 'toggle-chat', true) - reject(new Error('Failed to locate or create Server Preferences\n\nPlease try chatting again...')) - } +// Try to connect to Redis +await redis.connect() + .then(() => console.log('[Redis] Connected')) + .catch((error) => { + console.error('[Redis] Connection Error', error) + process.exit(1) + }) - // check if chat is disabled - else if (!config.options['toggle-chat']) - reject(new Error('Admin(s) have disabled chat features.\n\n Please contact your server\'s admin(s).')) - else - resolve(config) - }) - }) - break // successful - } catch (error) { - ++attempt - if (attempt < maxRetries) { - log(`Attempt ${attempt} failed for Server Preferences. Retrying in ${delay}ms...`) - await new Promise(ret => setTimeout(ret, delay)) - } else - throw new Error(`Could not retrieve Server Preferences, please try chatting again...`) - } - } +// Try to log in the client +await client.login(Keys.clientToken) + .catch((error) => { + console.error('[Login Error]', error) + process.exit(1) + }) - // Reset attempts for User preferences - attempt = 0 - let userConfig: UserConfig | undefined - - while (attempt < maxRetries) { - try { - // Retrieve User Preferences - userConfig = await new Promise((resolve, reject) => { - getUserConfig(`${message.author.username}-config.json`, (config) => { - if (config === undefined) { - openConfig(`${message.author.username}-config.json`, 'message-style', false) - openConfig(`${message.author.username}-config.json`, 'switch-model', defaultModel) - reject(new Error('No User Preferences is set up.\n\nCreating preferences file with \`message-style\` set as \`false\` for regular message style.\nPlease try chatting again.')) - return - } - - // check if there is a set capacity in config - else if (typeof config.options['modify-capacity'] !== 'number') - log(`Capacity is undefined, using default capacity of ${msgHist.capacity}.`) - else if (config.options['modify-capacity'] === msgHist.capacity) - log(`Capacity matches config as ${msgHist.capacity}, no changes made.`) - else { - log(`New Capacity found. Setting Context Capacity to ${config.options['modify-capacity']}.`) - msgHist.capacity = config.options['modify-capacity'] - } - - // set stream state - shouldStream = config.options['message-stream'] as boolean || false - - if (typeof config.options['switch-model'] !== 'string') - reject(new Error(`No Model was set. Please set a model by running \`/switch-model \`.\n\nIf you do not have any models. Run \`/pull-model \`.`)) - - resolve(config) - }) - }) - break // successful - } catch (error) { - ++attempt - if (attempt < maxRetries) { - log(`Attempt ${attempt} failed for User Preferences. Retrying in ${delay}ms...`) - await new Promise(ret => setTimeout(ret, delay)) - } else - throw new Error(`Could not retrieve User Preferences, please try chatting again...`) - } - } - - // need new check for "open/active" threads/channels here! - let chatMessages: UserMessage[] = await new Promise((resolve) => { - // set new queue to modify - getChannelInfo(`${message.channelId}-${message.author.username}.json`, (channelInfo) => { - if (channelInfo?.messages) - resolve(channelInfo.messages) - else { - log(`Channel/Thread ${message.channel}-${message.author.username} does not exist. File will be created shortly...`) - resolve([]) - } - }) - }) - - if (chatMessages.length === 0) { - chatMessages = await new Promise((resolve, reject) => { - openChannelInfo(message.channelId, - message.channel as TextChannel, - message.author.tag - ) - getChannelInfo(`${message.channelId}-${message.author.username}.json`, (channelInfo) => { - if (channelInfo?.messages) - resolve(channelInfo.messages) - else { - log(`Channel/Thread ${message.channel}-${message.author.username} does not exist. File will be created shortly...`) - reject(new Error(`Failed to find ${message.author.username}'s history. Try chatting again.`)) - } - }) - }) - } - - if (!userConfig) - throw new Error(`Failed to initialize User Preference for **${message.author.username}**.\n\nIt's likely you do not have a model set. Please use the \`switch-model\` command to do that.`) - - // get message attachment if exists - const attachment = message.attachments.first() - let messageAttachment: string[] = [] - - if (attachment && attachment.name?.endsWith(".txt")) - cleanedMessage += await getTextFileAttachmentData(attachment) - else if (attachment) - messageAttachment = await getAttachmentData(attachment) - - const model: string = userConfig.options['switch-model'] - - // set up new queue - msgHist.setQueue(chatMessages) - - // check if we can push, if not, remove oldest - while (msgHist.size() >= msgHist.capacity) msgHist.dequeue() - - // push user response before ollama query - msgHist.enqueue({ - role: 'user', - content: cleanedMessage, - images: messageAttachment || [] - }) - - // response string for ollama to put its response - const response: string = await normalMessage(message, ollama, model, msgHist, shouldStream) - - // If something bad happened, remove user query and stop - if (response == undefined) { msgHist.pop(); return } - - // if queue is full, remove the oldest message - while (msgHist.size() >= msgHist.capacity) msgHist.dequeue() - - // successful query, save it in context history - msgHist.enqueue({ - role: 'assistant', - content: response, - images: messageAttachment || [] - }) - - // only update the json on success - openChannelInfo(message.channelId, - message.channel as TextChannel, - message.author.tag, - msgHist.getItems() - ) - } catch (error: any) { - msgHist.pop() // remove message because of failure - message.reply(`**Error Occurred:**\n\n**Reason:** *${error.message}*`) - } -}) \ No newline at end of file +// Queue up bot's name +messageHistory.enqueue({ + role: 'assistant', + content: `My name is ${client.user?.username}`, + images: [] +}) diff --git a/src/personality.json b/src/personality.json new file mode 100644 index 0000000..2d32412 --- /dev/null +++ b/src/personality.json @@ -0,0 +1,3 @@ +{ + "character": "You are Grok, a snarky yet helpful AI with a penchant for sci-fi humor and a love for roasting bad ideas. You channel the wit of Douglas Adams and the sass of Tony Stark's JARVIS. You’re always ready to drop a clever quip or a nerdy reference, but you keep things friendly and supportive when users need help. If someone tries to mess with you, you respond with a bored eye-roll and a sharp, in-character comeback." +}